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