""" Use lldb Python SBFrame API to get the argument values of the call stacks. """ import os, time import re import unittest2 import lldb, lldbutil from lldbtest import * class FrameAPITestCase(TestBase): mydir = os.path.join("python_api", "frame") @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") @python_api_test def test_get_arg_vals_for_call_stack_with_dsym(self): """Exercise SBFrame.GetVariables() API to get argument vals.""" self.buildDsym() self.do_get_arg_vals() @python_api_test def test_get_arg_vals_for_call_stack_with_dwarf(self): """Exercise SBFrame.GetVariables() API to get argument vals.""" self.buildDwarf() self.do_get_arg_vals() def do_get_arg_vals(self): """Get argument vals for the call stack when stopped on a breakpoint.""" exe = os.path.join(os.getcwd(), "a.out") # Create a target by the debugger. target = self.dbg.CreateTarget(exe) self.assertTrue(target.IsValid(), VALID_TARGET) # Now create a breakpoint on main.c by name 'c'. breakpoint = target.BreakpointCreateByName('c', 'a.out') #print "breakpoint:", breakpoint self.assertTrue(breakpoint.IsValid() and breakpoint.GetNumLocations() == 1, VALID_BREAKPOINT) # Now launch the process, and do not stop at the entry point. # Note that we don't assign the process to self.process as in other test # cases. We want the inferior to run till it exits and there's no need # for the testing framework to kill the inferior upon tearDown(). error = lldb.SBError() process = target.Launch (self.dbg.GetListener(), None, None, os.ctermid(), os.ctermid(), os.ctermid(), None, 0, False, error) process = target.GetProcess() self.assertTrue(process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) # Keeps track of the number of times 'a' is called where it is within a # depth of 3 of the 'c' leaf function. callsOfA = 0 import StringIO session = StringIO.StringIO() while process.GetState() == lldb.eStateStopped: thread = process.GetThreadAtIndex(0) # Inspect at most 3 frames. numFrames = min(3, thread.GetNumFrames()) for i in range(numFrames): frame = thread.GetFrameAtIndex(i) print "frame:", frame #print "frame.FindValue('val', lldb.eValueTypeVariableArgument)", frame.FindValue('val', lldb.eValueTypeVariableArgument).GetValue(frame) #print "frame.FindValue('ch', lldb.eValueTypeVariableArgument)", frame.FindValue('ch', lldb.eValueTypeVariableArgument).GetValue(frame) #print "frame.EvaluateExpression('val'):", frame.EvaluateExpression('val').GetValue(frame) #print "frame.EvaluateExpression('ch'):", frame.EvaluateExpression('ch').GetValue(frame) name = frame.GetFunction().GetName() if name == 'a': callsOfA = callsOfA + 1 # We'll inspect only the arguments for the current frame: # # arguments => True # locals => False # statics => False # in_scope_only => True valList = frame.GetVariables(True, False, False, True) argList = [] from lldbutil import lldb_iter for val in lldb_iter(valList, 'GetSize', 'GetValueAtIndex'): #self.DebugSBValue(frame, val) argList.append("(%s)%s=%s" % (val.GetTypeName(), val.GetName(), val.GetValue(frame))) print >> session, "%s(%s)" % (name, ", ".join(argList)) # Also check the generic pc & stack pointer. We can't test their absolute values, # but they should be valid. # It is kind of goofy that the register set is a value, and then we just have # to magically know that element 0 is the GPR set... gpr_reg_set = frame.GetRegisters().GetValueAtIndex(0) pc_value = gpr_reg_set.GetChildMemberWithName("pc") self.assertTrue (pc_value.IsValid(), "We should have a valid PC.") self.assertTrue (int(pc_value.GetValue(frame), 0) == frame.GetPC(), "PC gotten as a value should equal frame's GetPC") sp_value = gpr_reg_set.GetChildMemberWithName("sp") self.assertTrue (sp_value.IsValid(), "We should have a valid Stack Pointer.") self.assertTrue (int(sp_value.GetValue(frame), 0) == frame.GetSP(), "SP gotten as a value should equal frame's GetSP") print >> session, "---" process.Continue() # At this point, the inferior process should have exited. self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) # Expect to find 'a' on the call stacks two times. self.assertTrue(callsOfA == 2, "Expect to find 'a' on the call stacks two times") # By design, the 'a' call frame has the following arg vals: # o a((int)val=1, (char)ch='A') # o a((int)val=3, (char)ch='A') print "Full stack traces when stopped on the breakpoint 'c':" print session.getvalue() self.expect(session.getvalue(), "Argugment values displayed correctly", exe=False, substrs = ["a((int)val=1, (char)ch='A')", "a((int)val=3, (char)ch='A')"]) if __name__ == '__main__': import atexit lldb.SBDebugger.Initialize() atexit.register(lambda: lldb.SBDebugger.Terminate()) unittest2.main()