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