xref: /llvm-project/lldb/test/API/functionalities/conditional_break/TestConditionalBreak.py (revision e0a79eeca27b894bca6aa3f5dfdd8f1ac6644381)
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