1"""
2Test lldb breakpoint command add/list/delete.
3"""
4
5
6
7import lldb
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10from lldbsuite.test import lldbutil
11import side_effect
12
13
14class BreakpointCommandTestCase(TestBase):
15
16    NO_DEBUG_INFO_TESTCASE = True
17    mydir = TestBase.compute_mydir(__file__)
18
19    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528")
20    @skipIfReproducer # side_effect bypasses reproducer
21    def not_test_breakpoint_command_sequence(self):
22        """Test a sequence of breakpoint command add, list, and delete."""
23        self.build()
24        self.breakpoint_command_sequence()
25
26    @skipIf(oslist=["windows"], bugnumber="llvm.org/pr44431")
27    @skipIfReproducer # side_effect bypasses reproducer
28    def test_script_parameters(self):
29        """Test a sequence of breakpoint command add, list, and delete."""
30        self.build()
31        self.breakpoint_command_script_parameters()
32
33    def test_commands_on_creation(self):
34        self.build()
35        self.breakpoint_commands_on_creation()
36
37    def setUp(self):
38        # Call super's setUp().
39        TestBase.setUp(self)
40        # Find the line number to break inside main().
41        self.line = line_number('main.c', '// Set break point at this line.')
42        # disable "There is a running process, kill it and restart?" prompt
43        self.runCmd("settings set auto-confirm true")
44        self.addTearDownHook(
45            lambda: self.runCmd("settings clear auto-confirm"))
46
47    def test_delete_all_breakpoints(self):
48        """Test that deleting all breakpoints works."""
49        self.build()
50        exe = self.getBuildArtifact("a.out")
51        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
52
53        lldbutil.run_break_set_by_symbol(self, "main")
54        lldbutil.run_break_set_by_file_and_line(
55            self, "main.c", self.line, num_expected_locations=1, loc_exact=True)
56
57        self.runCmd("run", RUN_SUCCEEDED)
58
59        self.runCmd("breakpoint delete")
60        self.runCmd("process continue")
61        self.expect("process status", PROCESS_STOPPED,
62                    patterns=['Process .* exited with status = 0'])
63
64
65    def breakpoint_command_sequence(self):
66        """Test a sequence of breakpoint command add, list, and delete."""
67        exe = self.getBuildArtifact("a.out")
68        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
69
70        # Add three breakpoints on the same line.  The first time we don't specify the file,
71        # since the default file is the one containing main:
72        lldbutil.run_break_set_by_file_and_line(
73            self, None, self.line, num_expected_locations=1, loc_exact=True)
74        lldbutil.run_break_set_by_file_and_line(
75            self, "main.c", self.line, num_expected_locations=1, loc_exact=True)
76        lldbutil.run_break_set_by_file_and_line(
77            self, "main.c", self.line, num_expected_locations=1, loc_exact=True)
78        # Breakpoint 4 - set at the same location as breakpoint 1 to test
79        # setting breakpoint commands on two breakpoints at a time
80        lldbutil.run_break_set_by_file_and_line(
81            self, None, self.line, num_expected_locations=1, loc_exact=True)
82        # Make sure relative path source breakpoints work as expected. We test
83        # with partial paths with and without "./" prefixes.
84        lldbutil.run_break_set_by_file_and_line(
85            self, "./main.c", self.line,
86            num_expected_locations=1, loc_exact=True)
87        lldbutil.run_break_set_by_file_and_line(
88            self, "breakpoint_command/main.c", self.line,
89            num_expected_locations=1, loc_exact=True)
90        lldbutil.run_break_set_by_file_and_line(
91            self, "./breakpoint_command/main.c", self.line,
92            num_expected_locations=1, loc_exact=True)
93        lldbutil.run_break_set_by_file_and_line(
94            self, "breakpoint/breakpoint_command/main.c", self.line,
95            num_expected_locations=1, loc_exact=True)
96        lldbutil.run_break_set_by_file_and_line(
97            self, "./breakpoint/breakpoint_command/main.c", self.line,
98            num_expected_locations=1, loc_exact=True)
99        # Test relative breakpoints with incorrect paths and make sure we get
100        # no breakpoint locations
101        lldbutil.run_break_set_by_file_and_line(
102            self, "invalid/main.c", self.line,
103            num_expected_locations=0, loc_exact=True)
104        lldbutil.run_break_set_by_file_and_line(
105            self, "./invalid/main.c", self.line,
106            num_expected_locations=0, loc_exact=True)
107        # Now add callbacks for the breakpoints just created.
108        self.runCmd(
109            "breakpoint command add -s command -o 'frame variable --show-types --scope' 1 4")
110        self.runCmd(
111            "breakpoint command add -s python -o 'import side_effect; side_effect.one_liner = \"one liner was here\"' 2")
112
113        import side_effect
114        self.runCmd("command script import --allow-reload ./bktptcmd.py")
115
116        self.runCmd(
117            "breakpoint command add --python-function bktptcmd.function 3")
118
119        # Check that the breakpoint commands are correctly set.
120
121        # The breakpoint list now only contains breakpoint 1.
122        self.expect(
123            "breakpoint list", "Breakpoints 1 & 2 created", substrs=[
124                "2: file = 'main.c', line = %d, exact_match = 0, locations = 1" %
125                self.line], patterns=[
126                "1: file = '.*main.c', line = %d, exact_match = 0, locations = 1" %
127                self.line])
128
129        self.expect(
130            "breakpoint list -f",
131            "Breakpoints 1 & 2 created",
132            substrs=[
133                "2: file = 'main.c', line = %d, exact_match = 0, locations = 1" %
134                self.line],
135            patterns=[
136                "1: file = '.*main.c', line = %d, exact_match = 0, locations = 1" %
137                self.line,
138                "1.1: .+at main.c:%d:?[0-9]*, .+unresolved, hit count = 0" %
139                self.line,
140                "2.1: .+at main.c:%d:?[0-9]*, .+unresolved, hit count = 0" %
141                self.line])
142
143        self.expect("breakpoint command list 1", "Breakpoint 1 command ok",
144                    substrs=["Breakpoint commands:",
145                             "frame variable --show-types --scope"])
146        self.expect("breakpoint command list 2", "Breakpoint 2 command ok",
147                    substrs=["Breakpoint commands (Python):",
148                             "import side_effect",
149                             "side_effect.one_liner"])
150        self.expect("breakpoint command list 3", "Breakpoint 3 command ok",
151                    substrs=["Breakpoint commands (Python):",
152                             "bktptcmd.function(frame, bp_loc, internal_dict)"])
153
154        self.expect("breakpoint command list 4", "Breakpoint 4 command ok",
155                    substrs=["Breakpoint commands:",
156                             "frame variable --show-types --scope"])
157
158        self.runCmd("breakpoint delete 4")
159
160        # Next lets try some other breakpoint kinds.  First break with a regular expression
161        # and then specify only one file.  The first time we should get two locations,
162        # the second time only one:
163
164        lldbutil.run_break_set_by_regexp(
165            self, r"._MyFunction", num_expected_locations=2)
166
167        lldbutil.run_break_set_by_regexp(
168            self,
169            r"._MyFunction",
170            extra_options="-f a.c",
171            num_expected_locations=1)
172
173        lldbutil.run_break_set_by_regexp(
174            self,
175            r"._MyFunction",
176            extra_options="-f a.c -f b.c",
177            num_expected_locations=2)
178
179        # Now try a source regex breakpoint:
180        lldbutil.run_break_set_by_source_regexp(
181            self,
182            r"is about to return [12]0",
183            extra_options="-f a.c -f b.c",
184            num_expected_locations=2)
185
186        lldbutil.run_break_set_by_source_regexp(
187            self,
188            r"is about to return [12]0",
189            extra_options="-f a.c",
190            num_expected_locations=1)
191
192        # Reset our canary variables and run the program.
193        side_effect.one_liner = None
194        side_effect.bktptcmd = None
195        self.runCmd("run", RUN_SUCCEEDED)
196
197        # Check the value of canary variables.
198        self.assertEquals("one liner was here", side_effect.one_liner)
199        self.assertEquals("function was here", side_effect.bktptcmd)
200
201        # Finish the program.
202        self.runCmd("process continue")
203
204        # Remove the breakpoint command associated with breakpoint 1.
205        self.runCmd("breakpoint command delete 1")
206
207        # Remove breakpoint 2.
208        self.runCmd("breakpoint delete 2")
209
210        self.expect(
211            "breakpoint command list 1",
212            startstr="Breakpoint 1 does not have an associated command.")
213        self.expect(
214            "breakpoint command list 2",
215            error=True,
216            startstr="error: '2' is not a currently valid breakpoint ID.")
217
218        # The breakpoint list now only contains breakpoint 1.
219        self.expect(
220            "breakpoint list -f",
221            "Breakpoint 1 exists",
222            patterns=[
223                "1: file = '.*main.c', line = %d, exact_match = 0, locations = 1, resolved = 1" %
224                self.line,
225                "hit count = 1"])
226
227        # Not breakpoint 2.
228        self.expect(
229            "breakpoint list -f",
230            "No more breakpoint 2",
231            matching=False,
232            substrs=[
233                "2: file = 'main.c', line = %d, exact_match = 0, locations = 1, resolved = 1" %
234                self.line])
235
236        # Run the program again, with breakpoint 1 remaining.
237        self.runCmd("run", RUN_SUCCEEDED)
238
239        # We should be stopped again due to breakpoint 1.
240
241        # The stop reason of the thread should be breakpoint.
242        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
243                    substrs=['stopped',
244                             'stop reason = breakpoint'])
245
246        # The breakpoint should have a hit count of 2.
247        self.expect("breakpoint list -f", BREAKPOINT_HIT_TWICE,
248                    substrs=['resolved, hit count = 2'])
249
250    def breakpoint_command_script_parameters(self):
251        """Test that the frame and breakpoint location are being properly passed to the script breakpoint command function."""
252        exe = self.getBuildArtifact("a.out")
253        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
254
255        # Add a breakpoint.
256        lldbutil.run_break_set_by_file_and_line(
257            self, "main.c", self.line, num_expected_locations=1, loc_exact=True)
258
259        # Now add callbacks for the breakpoints just created.
260        self.runCmd("breakpoint command add -s python -o 'import side_effect; side_effect.frame = str(frame); side_effect.bp_loc = str(bp_loc)' 1")
261
262        # Reset canary variables and run.
263        side_effect.frame = None
264        side_effect.bp_loc = None
265        self.runCmd("run", RUN_SUCCEEDED)
266
267        self.expect(side_effect.frame, exe=False, startstr="frame #0:")
268        self.expect(side_effect.bp_loc, exe=False,
269                patterns=["1.* where = .*main .* resolved, hit count = 1"])
270
271    def breakpoint_commands_on_creation(self):
272        """Test that setting breakpoint commands when creating the breakpoint works"""
273        target = self.createTestTarget()
274
275        # Add a breakpoint.
276        lldbutil.run_break_set_by_file_and_line(
277            self, "main.c", self.line, num_expected_locations=1, loc_exact=True,
278            extra_options='-C bt -C "thread list" -C continue')
279
280        bkpt = target.FindBreakpointByID(1)
281        self.assertTrue(bkpt.IsValid(), "Couldn't find breakpoint 1")
282        com_list = lldb.SBStringList()
283        bkpt.GetCommandLineCommands(com_list)
284        self.assertEqual(com_list.GetSize(), 3, "Got the wrong number of commands")
285        self.assertEqual(com_list.GetStringAtIndex(0), "bt", "First bt")
286        self.assertEqual(com_list.GetStringAtIndex(1), "thread list", "Next thread list")
287        self.assertEqual(com_list.GetStringAtIndex(2), "continue", "Last continue")
288
289    def test_breakpoint_delete_disabled(self):
290        """Test 'break delete --disabled' works"""
291        self.build()
292        target = self.createTestTarget()
293
294        bp_1 = target.BreakpointCreateByName("main")
295        bp_2 = target.BreakpointCreateByName("not_here")
296        bp_3 = target.BreakpointCreateByName("main")
297        bp_3.AddName("DeleteMeNot")
298
299        bp_1.SetEnabled(False)
300        bp_3.SetEnabled(False)
301
302        bp_id_1 = bp_1.GetID()
303        bp_id_2 = bp_2.GetID()
304        bp_id_3 = bp_3.GetID()
305
306        self.runCmd("breakpoint delete --disabled DeleteMeNot")
307
308        bp_1 = target.FindBreakpointByID(bp_id_1)
309        self.assertFalse(bp_1.IsValid(), "Didn't delete disabled breakpoint 1")
310
311        bp_2 = target.FindBreakpointByID(bp_id_2)
312        self.assertTrue(bp_2.IsValid(), "Deleted enabled breakpoint 2")
313
314        bp_3 = target.FindBreakpointByID(bp_id_3)
315        self.assertTrue(bp_3.IsValid(), "DeleteMeNot didn't protect disabled breakpoint 3")
316
317        # Reset the first breakpoint, disable it, and do this again with no protected name:
318        bp_1 = target.BreakpointCreateByName("main")
319
320        bp_1.SetEnabled(False)
321
322        bp_id_1 = bp_1.GetID()
323
324        self.runCmd("breakpoint delete --disabled")
325
326        bp_1 = target.FindBreakpointByID(bp_id_1)
327        self.assertFalse(bp_1.IsValid(), "Didn't delete disabled breakpoint 1")
328
329        bp_2 = target.FindBreakpointByID(bp_id_2)
330        self.assertTrue(bp_2.IsValid(), "Deleted enabled breakpoint 2")
331
332        bp_3 = target.FindBreakpointByID(bp_id_3)
333        self.assertFalse(bp_3.IsValid(), "Didn't delete disabled breakpoint 3")
334
335