blob: 908a04919b8fa05c1695f91d49af8839c21071ec [file] [log] [blame]
"""
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))
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()