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