| """ |
| Test conditionally break on a function and inspect its variables. |
| """ |
| |
| from __future__ import print_function |
| |
| |
| import os |
| import time |
| import re |
| import lldb |
| from lldbsuite.test.decorators import * |
| from lldbsuite.test.lldbtest import * |
| from lldbsuite.test import lldbutil |
| |
| # rdar://problem/8532131 |
| # lldb not able to digest the clang-generated debug info correctly with respect to function name |
| # |
| # This class currently fails for clang as well as llvm-gcc. |
| |
| |
| class ConditionalBreakTestCase(TestBase): |
| |
| mydir = TestBase.compute_mydir(__file__) |
| |
| @add_test_categories(['pyapi']) |
| def test_with_python(self): |
| """Exercise some thread and frame APIs to break if c() is called by a().""" |
| self.build() |
| self.do_conditional_break() |
| |
| def test_with_command(self): |
| """Simulate a user using lldb commands to break on c() if called from a().""" |
| self.build() |
| self.simulate_conditional_break_by_user() |
| |
| @expectedFailureAll( |
| oslist=["windows"], |
| bugnumber="llvm.org/pr26265: args in frames other than #0 are not evaluated correctly") |
| def do_conditional_break(self): |
| """Exercise some thread and frame APIs to break if c() is called by a().""" |
| exe = self.getBuildArtifact("a.out") |
| |
| target = self.dbg.CreateTarget(exe) |
| self.assertTrue(target, VALID_TARGET) |
| |
| breakpoint = target.BreakpointCreateByName("c", exe) |
| self.assertTrue(breakpoint, VALID_BREAKPOINT) |
| |
| # Now launch the process, and do not stop at entry point. |
| process = target.LaunchSimple( |
| None, None, self.get_process_working_directory()) |
| |
| self.assertTrue(process, PROCESS_IS_VALID) |
| |
| # The stop reason of the thread should be breakpoint. |
| self.assertTrue(process.GetState() == lldb.eStateStopped, |
| STOPPED_DUE_TO_BREAKPOINT) |
| |
| # Find the line number where a's parent frame function is c. |
| line = line_number( |
| 'main.c', |
| "// Find the line number where c's parent frame is a here.") |
| |
| # Suppose we are only interested in the call scenario where c()'s |
| # immediate caller is a() and we want to find out the value passed from |
| # a(). |
| # |
| # The 10 in range(10) is just an arbitrary number, which means we would |
| # like to try for at most 10 times. |
| for j in range(10): |
| if self.TraceOn(): |
| print("j is: ", j) |
| thread = lldbutil.get_one_thread_stopped_at_breakpoint( |
| process, breakpoint) |
| self.assertIsNotNone( |
| thread, "Expected one thread to be stopped at the breakpoint") |
| |
| if thread.GetNumFrames() >= 2: |
| frame0 = thread.GetFrameAtIndex(0) |
| name0 = frame0.GetFunction().GetName() |
| frame1 = thread.GetFrameAtIndex(1) |
| name1 = frame1.GetFunction().GetName() |
| # lldbutil.print_stacktrace(thread) |
| self.assertTrue(name0 == "c", "Break on function c()") |
| if (name1 == "a"): |
| # By design, we know that a() calls c() only from main.c:27. |
| # In reality, similar logic can be used to find out the call |
| # site. |
| self.assertTrue(frame1.GetLineEntry().GetLine() == line, |
| "Immediate caller a() at main.c:%d" % line) |
| |
| # And the local variable 'val' should have a value of (int) |
| # 3. |
| val = frame1.FindVariable("val") |
| self.assertEqual("int", val.GetTypeName()) |
| self.assertEqual("3", val.GetValue()) |
| break |
| |
| process.Continue() |
| |
| def simulate_conditional_break_by_user(self): |
| """Simulate a user using lldb commands to break on c() if called from a().""" |
| |
| # Sourcing .lldb in the current working directory, which sets the main |
| # executable, sets the breakpoint on c(), and adds the callback for the |
| # breakpoint such that lldb only stops when the caller of c() is a(). |
| # the "my" package that defines the date() function. |
| if self.TraceOn(): |
| print("About to source .lldb") |
| |
| if not self.TraceOn(): |
| self.HideStdout() |
| |
| # Separate out the "file " + self.getBuildArtifact("a.out") command from .lldb file, for the sake of |
| # remote testsuite. |
| self.runCmd("file " + self.getBuildArtifact("a.out")) |
| self.runCmd("command source .lldb") |
| |
| self.runCmd("break list") |
| |
| if self.TraceOn(): |
| print("About to run.") |
| self.runCmd("run", RUN_SUCCEEDED) |
| |
| self.runCmd("break list") |
| |
| if self.TraceOn(): |
| print("Done running") |
| |
| # The stop reason of the thread should be breakpoint. |
| self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, |
| substrs=['stopped', 'stop reason = breakpoint']) |
| |
| # The frame info for frame #0 points to a.out`c and its immediate caller |
| # (frame #1) points to a.out`a. |
| |
| self.expect("frame info", "We should stop at c()", |
| substrs=["a.out`c"]) |
| |
| # Select our parent frame as the current frame. |
| self.runCmd("frame select 1") |
| self.expect("frame info", "The immediate caller should be a()", |
| substrs=["a.out`a"]) |