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