1"""
2Test breakpoint commands for a breakpoint ID with multiple locations.
3"""
4
5
6import lldb
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbutil
10
11
12class BreakpointLocationsTestCase(TestBase):
13    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528")
14    def test_enable(self):
15        """Test breakpoint enable/disable for a breakpoint ID with multiple locations."""
16        self.build()
17        self.breakpoint_locations_test()
18
19    def test_shadowed_cond_options(self):
20        """Test that options set on the breakpoint and location behave correctly."""
21        self.build()
22        self.shadowed_bkpt_cond_test()
23
24    def test_shadowed_command_options(self):
25        """Test that options set on the breakpoint and location behave correctly."""
26        self.build()
27        self.shadowed_bkpt_command_test()
28
29    def setUp(self):
30        # Call super's setUp().
31        TestBase.setUp(self)
32        # Find the line number to break inside main().
33        self.line = line_number("main.c", "// Set break point at this line.")
34
35    def set_breakpoint(self):
36        exe = self.getBuildArtifact("a.out")
37        target = self.dbg.CreateTarget(exe)
38        self.assertTrue(target, "Target %s is not valid" % (exe))
39
40        # This should create a breakpoint with 3 locations.
41
42        bkpt = target.BreakpointCreateByLocation("main.c", self.line)
43
44        # The breakpoint list should show 3 locations.
45        self.assertEqual(bkpt.GetNumLocations(), 3, "Wrong number of locations")
46
47        self.expect(
48            "breakpoint list -f",
49            "Breakpoint locations shown correctly",
50            substrs=[
51                "1: file = 'main.c', line = %d, exact_match = 0, locations = 3"
52                % self.line
53            ],
54            patterns=[
55                "where = a.out`func_inlined .+unresolved, hit count = 0",
56                "where = a.out`main .+\[inlined\].+unresolved, hit count = 0",
57            ],
58        )
59
60        return bkpt
61
62    def shadowed_bkpt_cond_test(self):
63        """Test that options set on the breakpoint and location behave correctly."""
64        # Breakpoint option propagation from bkpt to loc used to be done the first time
65        # a breakpoint location option was specifically set.  After that the other options
66        # on that location would stop tracking the breakpoint.  That got fixed, and this test
67        # makes sure only the option touched is affected.
68
69        bkpt = self.set_breakpoint()
70        bkpt_cond = "1 == 0"
71        bkpt.SetCondition(bkpt_cond)
72        self.assertEqual(bkpt.GetCondition(), bkpt_cond, "Successfully set condition")
73        self.assertEqual(
74            bkpt.location[0].GetCondition(),
75            bkpt.GetCondition(),
76            "Conditions are the same",
77        )
78
79        # Now set a condition on the locations, make sure that this doesn't effect the bkpt:
80        bkpt_loc_1_cond = "1 == 1"
81        bkpt.location[0].SetCondition(bkpt_loc_1_cond)
82        self.assertEqual(
83            bkpt.location[0].GetCondition(),
84            bkpt_loc_1_cond,
85            "Successfully changed location condition",
86        )
87        self.assertNotEqual(
88            bkpt.GetCondition(),
89            bkpt_loc_1_cond,
90            "Changed location changed Breakpoint condition",
91        )
92        self.assertEqual(
93            bkpt.location[1].GetCondition(),
94            bkpt_cond,
95            "Changed another location's condition",
96        )
97
98        # Now make sure that setting one options doesn't fix the value of another:
99        bkpt.SetIgnoreCount(10)
100        self.assertEqual(bkpt.GetIgnoreCount(), 10, "Set the ignore count successfully")
101        self.assertEqual(
102            bkpt.location[0].GetIgnoreCount(),
103            10,
104            "Location doesn't track top-level bkpt.",
105        )
106
107        # Now make sure resetting the condition to "" resets the tracking:
108        bkpt.location[0].SetCondition("")
109        bkpt_new_cond = "1 == 3"
110        bkpt.SetCondition(bkpt_new_cond)
111        self.assertEqual(
112            bkpt.location[0].GetCondition(),
113            bkpt_new_cond,
114            "Didn't go back to tracking condition",
115        )
116
117        # Test that set/get accessor methods on BreakpointLocation behave correctly.
118        bkpt_loc = bkpt.GetLocationAtIndex(0)
119
120        value = "MyQueue"
121        bkpt_loc.SetQueueName(value)
122        self.assertEqual(
123            bkpt_loc.GetQueueName(), value, "Successfully set/get bp location QueueName"
124        )
125
126        value = 5
127        bkpt_loc.SetThreadID(value)
128        self.assertEqual(
129            bkpt_loc.GetThreadID(), value, "Successfully set/get bp location ThreadID"
130        )
131
132        value = "1 == 0"
133        bkpt_loc.SetCondition(value)
134        self.assertEqual(
135            bkpt_loc.GetCondition(), value, "Successfully set/get bp location Condition"
136        )
137
138        value = 6
139        bkpt_loc.SetThreadIndex(value)
140        self.assertEqual(
141            bkpt_loc.GetThreadIndex(),
142            value,
143            "Successfully set/get bp location ThreadIndex",
144        )
145
146        value = "MyThread"
147        bkpt_loc.SetThreadName(value)
148        self.assertEqual(
149            bkpt_loc.GetThreadName(),
150            value,
151            "Successfully set/get bp location ThreadName",
152        )
153
154        value = 5
155        bkpt_loc.SetIgnoreCount(value)
156        self.assertEqual(
157            bkpt_loc.GetIgnoreCount(),
158            value,
159            "Successfully set/get bp location IgnoreCount",
160        )
161
162        for value in [True, False]:
163            bkpt_loc.SetAutoContinue(value)
164            self.assertEqual(
165                bkpt_loc.GetAutoContinue(),
166                value,
167                "Successfully set/get bp location AutoContinue",
168            )
169
170        for value in [True, False]:
171            bkpt_loc.SetEnabled(value)
172            self.assertEqual(
173                bkpt_loc.IsEnabled(),
174                value,
175                "Successfully set/get bp location SetEnabled",
176            )
177
178        # test set/get CommandLineCommands
179        set_cmds = lldb.SBStringList()
180        set_cmds.AppendString("frame var")
181        set_cmds.AppendString("bt")
182        bkpt_loc.SetCommandLineCommands(set_cmds)
183
184        get_cmds = lldb.SBStringList()
185        bkpt_loc.GetCommandLineCommands(get_cmds)
186        self.assertEqual(
187            set_cmds.GetSize(), get_cmds.GetSize(), "Size of command line commands"
188        )
189        for idx, _ in enumerate(set_cmds):
190            self.assertEqual(
191                set_cmds.GetStringAtIndex(idx),
192                get_cmds.GetStringAtIndex(idx),
193                "Command %d" % (idx),
194            )
195
196    def shadowed_bkpt_command_test(self):
197        """Test that options set on the breakpoint and location behave correctly."""
198        # Breakpoint option propagation from bkpt to loc used to be done the first time
199        # a breakpoint location option was specifically set.  After that the other options
200        # on that location would stop tracking the breakpoint.  That got fixed, and this test
201        # makes sure only the option touched is affected.
202
203        bkpt = self.set_breakpoint()
204        commands = ["AAAAAA", "BBBBBB", "CCCCCC"]
205        str_list = lldb.SBStringList()
206        str_list.AppendList(commands, len(commands))
207
208        bkpt.SetCommandLineCommands(str_list)
209        cmd_list = lldb.SBStringList()
210        bkpt.GetCommandLineCommands(cmd_list)
211        list_size = str_list.GetSize()
212        self.assertEqual(
213            cmd_list.GetSize(), list_size, "Added the right number of commands"
214        )
215        for i in range(0, list_size):
216            self.assertEqual(
217                str_list.GetStringAtIndex(i),
218                cmd_list.GetStringAtIndex(i),
219                "Mismatched commands.",
220            )
221
222        commands = ["DDDDDD", "EEEEEE", "FFFFFF", "GGGGGG"]
223        loc_list = lldb.SBStringList()
224        loc_list.AppendList(commands, len(commands))
225        bkpt.location[1].SetCommandLineCommands(loc_list)
226        loc_cmd_list = lldb.SBStringList()
227        bkpt.location[1].GetCommandLineCommands(loc_cmd_list)
228
229        loc_list_size = loc_list.GetSize()
230
231        # Check that the location has the right commands:
232        self.assertEqual(
233            loc_cmd_list.GetSize(),
234            loc_list_size,
235            "Added the right number of commands to location",
236        )
237        for i in range(0, loc_list_size):
238            self.assertEqual(
239                loc_list.GetStringAtIndex(i),
240                loc_cmd_list.GetStringAtIndex(i),
241                "Mismatched commands.",
242            )
243
244        # Check that we didn't mess up the breakpoint level commands:
245        self.assertEqual(
246            cmd_list.GetSize(), list_size, "Added the right number of commands"
247        )
248        for i in range(0, list_size):
249            self.assertEqual(
250                str_list.GetStringAtIndex(i),
251                cmd_list.GetStringAtIndex(i),
252                "Mismatched commands.",
253            )
254
255        # And check we didn't mess up another location:
256        untouched_loc_cmds = lldb.SBStringList()
257        bkpt.location[0].GetCommandLineCommands(untouched_loc_cmds)
258        self.assertEqual(untouched_loc_cmds.GetSize(), 0, "Changed the wrong location")
259
260    def breakpoint_locations_test(self):
261        """Test breakpoint enable/disable for a breakpoint ID with multiple locations."""
262        self.set_breakpoint()
263
264        # The 'breakpoint disable 3.*' command should fail gracefully.
265        self.expect(
266            "breakpoint disable 3.*",
267            "Disabling an invalid breakpoint should fail gracefully",
268            error=True,
269            startstr="error: '3' is not a valid breakpoint ID.",
270        )
271
272        # The 'breakpoint disable 1.*' command should disable all 3 locations.
273        self.expect(
274            "breakpoint disable 1.*",
275            "All 3 breakpoint locatons disabled correctly",
276            startstr="3 breakpoints disabled.",
277        )
278
279        # Run the program.
280        self.runCmd("run", RUN_SUCCEEDED)
281
282        # We should not stopped on any breakpoint at all.
283        self.expect(
284            "process status",
285            "No stopping on any disabled breakpoint",
286            patterns=["^Process [0-9]+ exited with status = 0"],
287        )
288
289        # The 'breakpoint enable 1.*' command should enable all 3 breakpoints.
290        self.expect(
291            "breakpoint enable 1.*",
292            "All 3 breakpoint locatons enabled correctly",
293            startstr="3 breakpoints enabled.",
294        )
295
296        # The 'breakpoint enable 1.' command should not crash.
297        self.expect(
298            "breakpoint enable 1.",
299            startstr="0 breakpoints enabled.",
300        )
301
302        # The 'breakpoint disable 1.1' command should disable 1 location.
303        self.expect(
304            "breakpoint disable 1.1",
305            "1 breakpoint locatons disabled correctly",
306            startstr="1 breakpoints disabled.",
307        )
308
309        # Run the program again.  We should stop on the two breakpoint
310        # locations.
311        self.runCmd("run", RUN_SUCCEEDED)
312
313        # Stopped once.
314        self.expect(
315            "thread backtrace",
316            STOPPED_DUE_TO_BREAKPOINT,
317            substrs=["stop reason = breakpoint 1."],
318        )
319
320        # Continue the program, there should be another stop.
321        self.runCmd("process continue")
322
323        # Stopped again.
324        self.expect(
325            "thread backtrace",
326            STOPPED_DUE_TO_BREAKPOINT,
327            substrs=["stop reason = breakpoint 1."],
328        )
329
330        # At this point, 1.1 has a hit count of 0 and the other a hit count of
331        # 1".
332        lldbutil.check_breakpoint(
333            self,
334            bpno=1,
335            expected_locations=3,
336            expected_resolved_count=2,
337            expected_hit_count=2,
338        )
339        lldbutil.check_breakpoint(
340            self,
341            bpno=1,
342            location_id=1,
343            expected_location_resolved=False,
344            expected_location_hit_count=0,
345        )
346        lldbutil.check_breakpoint(
347            self,
348            bpno=1,
349            location_id=2,
350            expected_location_resolved=True,
351            expected_location_hit_count=1,
352        )
353        lldbutil.check_breakpoint(
354            self,
355            bpno=1,
356            location_id=3,
357            expected_location_resolved=True,
358            expected_location_hit_count=1,
359        )
360