1"""
2Test breakpoint names.
3"""
4
5
6
7import os
8import lldb
9from lldbsuite.test.decorators import *
10from lldbsuite.test.lldbtest import *
11from lldbsuite.test import lldbutil
12
13
14class BreakpointNames(TestBase):
15    NO_DEBUG_INFO_TESTCASE = True
16
17    @add_test_categories(['pyapi'])
18    def test_setting_names(self):
19        """Use Python APIs to test that we can set breakpoint names."""
20        self.build()
21        self.setup_target()
22        self.do_check_names()
23
24    def test_illegal_names(self):
25        """Use Python APIs to test that we don't allow illegal names."""
26        self.build()
27        self.setup_target()
28        self.do_check_illegal_names()
29
30    def test_using_names(self):
31        """Use Python APIs to test that operations on names works correctly."""
32        self.build()
33        self.setup_target()
34        self.do_check_using_names()
35
36    def test_configuring_names(self):
37        """Use Python APIs to test that configuring options on breakpoint names works correctly."""
38        self.build()
39        self.make_a_dummy_name()
40        self.setup_target()
41        self.do_check_configuring_names()
42
43    def test_configuring_permissions_sb(self):
44        """Use Python APIs to test that configuring permissions on names works correctly."""
45        self.build()
46        self.setup_target()
47        self.do_check_configuring_permissions_sb()
48
49    def test_configuring_permissions_cli(self):
50        """Use Python APIs to test that configuring permissions on names works correctly."""
51        self.build()
52        self.setup_target()
53        self.do_check_configuring_permissions_cli()
54
55    def setup_target(self):
56        exe = self.getBuildArtifact("a.out")
57
58        # Create a targets we are making breakpoint in and copying to:
59        self.target = self.dbg.CreateTarget(exe)
60        self.assertTrue(self.target, VALID_TARGET)
61        self.main_file_spec = lldb.SBFileSpec(os.path.join(self.getSourceDir(), "main.c"))
62
63    def check_name_in_target(self, bkpt_name):
64        name_list = lldb.SBStringList()
65        self.target.GetBreakpointNames(name_list)
66        found_it = False
67        for name in name_list:
68            if name == bkpt_name:
69                found_it = True
70                break
71        self.assertTrue(found_it, "Didn't find the name %s in the target's name list:"%(bkpt_name))
72
73    def setUp(self):
74        # Call super's setUp().
75        TestBase.setUp(self)
76
77        # These are the settings we're going to be putting into names & breakpoints:
78        self.bp_name_string = "ABreakpoint"
79        self.is_one_shot = True
80        self.ignore_count = 1000
81        self.condition = "1 == 2"
82        self.auto_continue = True
83        self.tid = 0xaaaa
84        self.tidx = 10
85        self.thread_name = "Fooey"
86        self.queue_name = "Blooey"
87        self.cmd_list = lldb.SBStringList()
88        self.cmd_list.AppendString("frame var")
89        self.cmd_list.AppendString("bt")
90        self.help_string = "I do something interesting"
91
92
93    def do_check_names(self):
94        """Use Python APIs to check that we can set & retrieve breakpoint names"""
95        bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10)
96        bkpt_name = "ABreakpoint"
97        other_bkpt_name = "_AnotherBreakpoint"
98
99        # Add a name and make sure we match it:
100        success = bkpt.AddNameWithErrorHandling(bkpt_name)
101        self.assertSuccess(success, "We couldn't add a legal name to a breakpoint.")
102
103        matches = bkpt.MatchesName(bkpt_name)
104        self.assertTrue(matches, "We didn't match the name we just set")
105
106        # Make sure we don't match irrelevant names:
107        matches = bkpt.MatchesName("NotABreakpoint")
108        self.assertTrue(not matches, "We matched a name we didn't set.")
109
110        # Make sure the name is also in the target:
111        self.check_name_in_target(bkpt_name)
112
113        # Add another name, make sure that works too:
114        bkpt.AddNameWithErrorHandling(other_bkpt_name)
115
116        matches = bkpt.MatchesName(bkpt_name)
117        self.assertTrue(matches, "Adding a name means we didn't match the name we just set")
118        self.check_name_in_target(other_bkpt_name)
119
120        # Remove the name and make sure we no longer match it:
121        bkpt.RemoveName(bkpt_name)
122        matches = bkpt.MatchesName(bkpt_name)
123        self.assertTrue(not matches,"We still match a name after removing it.")
124
125        # Make sure the name list has the remaining name:
126        name_list = lldb.SBStringList()
127        bkpt.GetNames(name_list)
128        num_names = name_list.GetSize()
129        self.assertEquals(num_names, 1, "Name list has %d items, expected 1."%(num_names))
130
131        name = name_list.GetStringAtIndex(0)
132        self.assertEquals(name, other_bkpt_name, "Remaining name was: %s expected %s."%(name, other_bkpt_name))
133
134    def do_check_illegal_names(self):
135        """Use Python APIs to check that we reject illegal names."""
136        bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10)
137        bad_names = ["-CantStartWithADash",
138                     "1CantStartWithANumber",
139                     "^CantStartWithNonAlpha",
140                     "CantHave-ADash",
141                     "Cant Have Spaces"]
142        for bad_name in bad_names:
143            success = bkpt.AddNameWithErrorHandling(bad_name)
144            self.assertTrue(success.Fail(), "We allowed an illegal name: %s"%(bad_name))
145            bp_name = lldb.SBBreakpointName(self.target, bad_name)
146            self.assertFalse(bp_name.IsValid(), "We made a breakpoint name with an illegal name: %s"%(bad_name));
147
148            retval =lldb.SBCommandReturnObject()
149            self.dbg.GetCommandInterpreter().HandleCommand("break set -n whatever -N '%s'"%(bad_name), retval)
150            self.assertTrue(not retval.Succeeded(), "break set succeeded with: illegal name: %s"%(bad_name))
151
152    def do_check_using_names(self):
153        """Use Python APIs to check names work in place of breakpoint ID's."""
154
155        # Create a dummy breakpoint to use up ID 1
156        _ = self.target.BreakpointCreateByLocation(self.main_file_spec, 30)
157
158        # Create a breakpoint to test with
159        bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10)
160        bkpt_name = "ABreakpoint"
161        bkpt_id = bkpt.GetID()
162        other_bkpt_name= "_AnotherBreakpoint"
163
164        # Add a name and make sure we match it:
165        success = bkpt.AddNameWithErrorHandling(bkpt_name)
166        self.assertSuccess(success, "We couldn't add a legal name to a breakpoint.")
167
168        bkpts = lldb.SBBreakpointList(self.target)
169        self.target.FindBreakpointsByName(bkpt_name, bkpts)
170
171        self.assertEquals(bkpts.GetSize(), 1, "One breakpoint matched.")
172        found_bkpt = bkpts.GetBreakpointAtIndex(0)
173        self.assertEquals(bkpt.GetID(), found_bkpt.GetID(),"The right breakpoint.")
174        self.assertEquals(bkpt.GetID(), bkpt_id,"With the same ID as before.")
175
176        retval = lldb.SBCommandReturnObject()
177        self.dbg.GetCommandInterpreter().HandleCommand("break disable %s"%(bkpt_name), retval)
178        self.assertTrue(retval.Succeeded(), "break disable failed with: %s."%(retval.GetError()))
179        self.assertTrue(not bkpt.IsEnabled(), "We didn't disable the breakpoint.")
180
181        # Also make sure we don't apply commands to non-matching names:
182        self.dbg.GetCommandInterpreter().HandleCommand("break modify --one-shot 1 %s"%(other_bkpt_name), retval)
183        self.assertTrue(retval.Succeeded(), "break modify failed with: %s."%(retval.GetError()))
184        self.assertTrue(not bkpt.IsOneShot(), "We applied one-shot to the wrong breakpoint.")
185
186    def check_option_values(self, bp_object):
187        self.assertEqual(bp_object.IsOneShot(), self.is_one_shot, "IsOneShot")
188        self.assertEqual(bp_object.GetIgnoreCount(), self.ignore_count, "IgnoreCount")
189        self.assertEqual(bp_object.GetCondition(), self.condition, "Condition")
190        self.assertEqual(bp_object.GetAutoContinue(), self.auto_continue, "AutoContinue")
191        self.assertEqual(bp_object.GetThreadID(), self.tid, "Thread ID")
192        self.assertEqual(bp_object.GetThreadIndex(), self.tidx, "Thread Index")
193        self.assertEqual(bp_object.GetThreadName(), self.thread_name, "Thread Name")
194        self.assertEqual(bp_object.GetQueueName(), self.queue_name, "Queue Name")
195        set_cmds = lldb.SBStringList()
196        bp_object.GetCommandLineCommands(set_cmds)
197        self.assertEqual(set_cmds.GetSize(), self.cmd_list.GetSize(), "Size of command line commands")
198        for idx in range(0, set_cmds.GetSize()):
199            self.assertEqual(self.cmd_list.GetStringAtIndex(idx), set_cmds.GetStringAtIndex(idx), "Command %d"%(idx))
200
201    def make_a_dummy_name(self):
202        "This makes a breakpoint name in the dummy target to make sure it gets copied over"
203
204        dummy_target = self.dbg.GetDummyTarget()
205        self.assertTrue(dummy_target.IsValid(), "Dummy target was not valid.")
206
207        def cleanup ():
208            self.dbg.GetDummyTarget().DeleteBreakpointName(self.bp_name_string)
209
210        # Execute the cleanup function during test case tear down.
211        self.addTearDownHook(cleanup)
212
213        # Now find it in the dummy target, and make sure these settings took:
214        bp_name = lldb.SBBreakpointName(dummy_target, self.bp_name_string)
215        # Make sure the name is right:
216        self.assertEqual(bp_name.GetName(), self.bp_name_string, "Wrong bp_name: %s"%(bp_name.GetName()))
217        bp_name.SetOneShot(self.is_one_shot)
218        bp_name.SetIgnoreCount(self.ignore_count)
219        bp_name.SetCondition(self.condition)
220        bp_name.SetAutoContinue(self.auto_continue)
221        bp_name.SetThreadID(self.tid)
222        bp_name.SetThreadIndex(self.tidx)
223        bp_name.SetThreadName(self.thread_name)
224        bp_name.SetQueueName(self.queue_name)
225        bp_name.SetCommandLineCommands(self.cmd_list)
226
227        # Now look it up again, and make sure it got set correctly.
228        bp_name = lldb.SBBreakpointName(dummy_target, self.bp_name_string)
229        self.assertTrue(bp_name.IsValid(), "Failed to make breakpoint name.")
230        self.check_option_values(bp_name)
231
232    def do_check_configuring_names(self):
233        """Use Python APIs to check that configuring breakpoint names works correctly."""
234        other_bp_name_string = "AnotherBreakpointName"
235        cl_bp_name_string = "CLBreakpointName"
236
237        # Now find the version copied in from the dummy target, and make sure these settings took:
238        bp_name = lldb.SBBreakpointName(self.target, self.bp_name_string)
239        self.assertTrue(bp_name.IsValid(), "Failed to make breakpoint name.")
240        self.check_option_values(bp_name)
241
242        # Now add this name to a breakpoint, and make sure it gets configured properly
243        bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10)
244        success = bkpt.AddNameWithErrorHandling(self.bp_name_string)
245        self.assertSuccess(success, "Couldn't add this name to the breakpoint")
246        self.check_option_values(bkpt)
247
248        # Now make a name from this breakpoint, and make sure the new name is properly configured:
249        new_name = lldb.SBBreakpointName(bkpt, other_bp_name_string)
250        self.assertTrue(new_name.IsValid(), "Couldn't make a valid bp_name from a breakpoint.")
251        self.check_option_values(bkpt)
252
253        # Now change the name's option and make sure it gets propagated to
254        # the breakpoint:
255        new_auto_continue = not self.auto_continue
256        bp_name.SetAutoContinue(new_auto_continue)
257        self.assertEqual(bp_name.GetAutoContinue(), new_auto_continue, "Couldn't change auto-continue on the name")
258        self.assertEqual(bkpt.GetAutoContinue(), new_auto_continue, "Option didn't propagate to the breakpoint.")
259
260        # Now make this same breakpoint name - but from the command line
261        cmd_str = "breakpoint name configure %s -o %d -i %d -c '%s' -G %d -t %d -x %d -T '%s' -q '%s' -H '%s'"%(cl_bp_name_string,
262                                                                             self.is_one_shot,
263                                                                             self.ignore_count,
264                                                                             self.condition,
265                                                                             self.auto_continue,
266                                                                             self.tid,
267                                                                             self.tidx,
268                                                                             self.thread_name,
269                                                                             self.queue_name,
270                                                                             self.help_string)
271        for cmd in self.cmd_list:
272            cmd_str += " -C '%s'"%(cmd)
273
274        self.runCmd(cmd_str, check=True)
275        # Now look up this name again and check its options:
276        cl_name = lldb.SBBreakpointName(self.target, cl_bp_name_string)
277        self.check_option_values(cl_name)
278        # Also check the help string:
279        self.assertEqual(self.help_string, cl_name.GetHelpString(), "Help string didn't match")
280        # Change the name and make sure that works:
281        new_help = "I do something even more interesting"
282        cl_name.SetHelpString(new_help)
283        self.assertEqual(new_help, cl_name.GetHelpString(), "SetHelpString didn't")
284
285        # We should have three names now, make sure the target can list them:
286        name_list = lldb.SBStringList()
287        self.target.GetBreakpointNames(name_list)
288        for name_string in [self.bp_name_string, other_bp_name_string, cl_bp_name_string]:
289            self.assertIn(name_string, name_list, "Didn't find %s in names"%(name_string))
290
291        # Delete the name from the current target.  Make sure that works and deletes the
292        # name from the breakpoint as well:
293        self.target.DeleteBreakpointName(self.bp_name_string)
294        name_list.Clear()
295        self.target.GetBreakpointNames(name_list)
296        self.assertNotIn(self.bp_name_string, name_list, "Didn't delete %s from a real target"%(self.bp_name_string))
297        # Also make sure the name got removed from breakpoints holding it:
298        self.assertFalse(bkpt.MatchesName(self.bp_name_string), "Didn't remove the name from the breakpoint.")
299
300        # Test that deleting the name we injected into the dummy target works (there's also a
301        # cleanup that will do this, but that won't test the result...
302        dummy_target = self.dbg.GetDummyTarget()
303        dummy_target.DeleteBreakpointName(self.bp_name_string)
304        name_list.Clear()
305        dummy_target.GetBreakpointNames(name_list)
306        self.assertNotIn(self.bp_name_string, name_list, "Didn't delete %s from the dummy target"%(self.bp_name_string))
307        # Also make sure the name got removed from breakpoints holding it:
308        self.assertFalse(bkpt.MatchesName(self.bp_name_string), "Didn't remove the name from the breakpoint.")
309
310    def check_permission_results(self, bp_name):
311        self.assertEqual(bp_name.GetAllowDelete(), False, "Didn't set allow delete.")
312        protected_bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10)
313        protected_id = protected_bkpt.GetID()
314
315        unprotected_bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10)
316        unprotected_id = unprotected_bkpt.GetID()
317
318        success = protected_bkpt.AddNameWithErrorHandling(self.bp_name_string)
319        self.assertSuccess(success, "Couldn't add this name to the breakpoint")
320
321        self.target.DisableAllBreakpoints()
322        self.assertEqual(protected_bkpt.IsEnabled(), True, "Didnt' keep breakpoint from being disabled")
323        self.assertEqual(unprotected_bkpt.IsEnabled(), False, "Protected too many breakpoints from disabling.")
324
325        # Try from the command line too:
326        unprotected_bkpt.SetEnabled(True)
327        result = lldb.SBCommandReturnObject()
328        self.dbg.GetCommandInterpreter().HandleCommand("break disable", result)
329        self.assertTrue(result.Succeeded())
330        self.assertEqual(protected_bkpt.IsEnabled(), True, "Didnt' keep breakpoint from being disabled")
331        self.assertEqual(unprotected_bkpt.IsEnabled(), False, "Protected too many breakpoints from disabling.")
332
333        self.target.DeleteAllBreakpoints()
334        bkpt = self.target.FindBreakpointByID(protected_id)
335        self.assertTrue(bkpt.IsValid(), "Didn't keep the breakpoint from being deleted.")
336        bkpt = self.target.FindBreakpointByID(unprotected_id)
337        self.assertFalse(bkpt.IsValid(), "Protected too many breakpoints from deletion.")
338
339        # Remake the unprotected breakpoint and try again from the command line:
340        unprotected_bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10)
341        unprotected_id = unprotected_bkpt.GetID()
342
343        self.dbg.GetCommandInterpreter().HandleCommand("break delete -f", result)
344        self.assertTrue(result.Succeeded())
345        bkpt = self.target.FindBreakpointByID(protected_id)
346        self.assertTrue(bkpt.IsValid(), "Didn't keep the breakpoint from being deleted.")
347        bkpt = self.target.FindBreakpointByID(unprotected_id)
348        self.assertFalse(bkpt.IsValid(), "Protected too many breakpoints from deletion.")
349
350    def do_check_configuring_permissions_sb(self):
351        bp_name = lldb.SBBreakpointName(self.target, self.bp_name_string)
352
353        # Make a breakpoint name with delete disallowed:
354        bp_name = lldb.SBBreakpointName(self.target, self.bp_name_string)
355        self.assertTrue(bp_name.IsValid(), "Failed to make breakpoint name for valid name.")
356
357        bp_name.SetAllowDelete(False)
358        bp_name.SetAllowDisable(False)
359        bp_name.SetAllowList(False)
360        self.check_permission_results(bp_name)
361
362    def do_check_configuring_permissions_cli(self):
363        # Make the name with the right options using the command line:
364        self.runCmd("breakpoint name configure -L 0 -D 0 -A 0 %s"%(self.bp_name_string), check=True)
365        # Now look up the breakpoint we made, and check that it works.
366        bp_name = lldb.SBBreakpointName(self.target, self.bp_name_string)
367        self.assertTrue(bp_name.IsValid(), "Didn't make a breakpoint name we could find.")
368        self.check_permission_results(bp_name)
369