1""" 2Test conditionally break on a function and inspect its variables. 3""" 4 5from __future__ import print_function 6 7 8import lldb 9from lldbsuite.test.decorators import * 10from lldbsuite.test.lldbtest import * 11from lldbsuite.test import lldbutil 12 13# rdar://problem/8532131 14# lldb not able to digest the clang-generated debug info correctly with respect to function name 15# 16# This class currently fails for clang as well as llvm-gcc. 17 18 19class ConditionalBreakTestCase(TestBase): 20 21 mydir = TestBase.compute_mydir(__file__) 22 23 @add_test_categories(['pyapi']) 24 def test_with_python(self): 25 """Exercise some thread and frame APIs to break if c() is called by a().""" 26 self.build() 27 self.do_conditional_break() 28 29 def test_with_command(self): 30 """Simulate a user using lldb commands to break on c() if called from a().""" 31 self.build() 32 self.simulate_conditional_break_by_user() 33 34 def do_conditional_break(self): 35 """Exercise some thread and frame APIs to break if c() is called by a().""" 36 exe = self.getBuildArtifact("a.out") 37 38 target = self.dbg.CreateTarget(exe) 39 self.assertTrue(target, VALID_TARGET) 40 41 breakpoint = target.BreakpointCreateByName("c", exe) 42 self.assertTrue(breakpoint, VALID_BREAKPOINT) 43 44 # Now launch the process, and do not stop at entry point. 45 process = target.LaunchSimple( 46 None, None, self.get_process_working_directory()) 47 48 self.assertTrue(process, PROCESS_IS_VALID) 49 50 # The stop reason of the thread should be breakpoint. 51 self.assertState(process.GetState(), lldb.eStateStopped, 52 STOPPED_DUE_TO_BREAKPOINT) 53 54 # Find the line number where a's parent frame function is c. 55 line = line_number( 56 'main.c', 57 "// Find the line number where c's parent frame is a here.") 58 59 # Suppose we are only interested in the call scenario where c()'s 60 # immediate caller is a() and we want to find out the value passed from 61 # a(). 62 # 63 # The 10 in range(10) is just an arbitrary number, which means we would 64 # like to try for at most 10 times. 65 for j in range(10): 66 if self.TraceOn(): 67 print("j is: ", j) 68 thread = lldbutil.get_one_thread_stopped_at_breakpoint( 69 process, breakpoint) 70 self.assertIsNotNone( 71 thread, "Expected one thread to be stopped at the breakpoint") 72 73 if thread.GetNumFrames() >= 2: 74 frame0 = thread.GetFrameAtIndex(0) 75 name0 = frame0.GetFunction().GetName() 76 frame1 = thread.GetFrameAtIndex(1) 77 name1 = frame1.GetFunction().GetName() 78 # lldbutil.print_stacktrace(thread) 79 self.assertEqual(name0, "c", "Break on function c()") 80 if (name1 == "a"): 81 # By design, we know that a() calls c() only from main.c:27. 82 # In reality, similar logic can be used to find out the call 83 # site. 84 self.assertEqual(frame1.GetLineEntry().GetLine(), line, 85 "Immediate caller a() at main.c:%d" % line) 86 87 # And the local variable 'val' should have a value of (int) 88 # 3. 89 val = frame1.FindVariable("val") 90 self.assertEqual("int", val.GetTypeName()) 91 self.assertEqual("3", val.GetValue()) 92 break 93 94 process.Continue() 95 96 def simulate_conditional_break_by_user(self): 97 """Simulate a user using lldb commands to break on c() if called from a().""" 98 99 # Sourcing .lldb in the current working directory, which sets the main 100 # executable, sets the breakpoint on c(), and adds the callback for the 101 # breakpoint such that lldb only stops when the caller of c() is a(). 102 # the "my" package that defines the date() function. 103 if self.TraceOn(): 104 print("About to source .lldb") 105 106 if not self.TraceOn(): 107 self.HideStdout() 108 109 # Separate out the "file " + self.getBuildArtifact("a.out") command from .lldb file, for the sake of 110 # remote testsuite. 111 self.runCmd("file " + self.getBuildArtifact("a.out")) 112 self.runCmd("command source .lldb") 113 114 self.runCmd("break list") 115 116 if self.TraceOn(): 117 print("About to run.") 118 self.runCmd("run", RUN_SUCCEEDED) 119 120 self.runCmd("break list") 121 122 if self.TraceOn(): 123 print("Done running") 124 125 # The stop reason of the thread should be breakpoint. 126 self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, 127 substrs=['stopped', 'stop reason = breakpoint']) 128 129 # The frame info for frame #0 points to a.out`c and its immediate caller 130 # (frame #1) points to a.out`a. 131 132 self.expect("frame info", "We should stop at c()", 133 substrs=["a.out`c"]) 134 135 # Select our parent frame as the current frame. 136 self.runCmd("frame select 1") 137 self.expect("frame info", "The immediate caller should be a()", 138 substrs=["a.out`a"]) 139