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