1"""
2Test setting breakpoints using a scripted resolver
3"""
4
5import os
6import lldb
7import lldbsuite.test.lldbutil as lldbutil
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10
11
12class TestScriptedResolver(TestBase):
13
14    mydir = TestBase.compute_mydir(__file__)
15
16    NO_DEBUG_INFO_TESTCASE = True
17
18    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528")
19    @skipIfReproducer # FIXME: Unexpected packet during (active) replay
20    def test_scripted_resolver(self):
21        """Use a scripted resolver to set a by symbol name breakpoint"""
22        self.build()
23        self.do_test()
24
25    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528")
26    def test_search_depths(self):
27        """ Make sure we are called at the right depths depending on what we return
28            from __get_depth__"""
29        self.build()
30        self.do_test_depths()
31
32    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528")
33    def test_command_line(self):
34        """ Test setting a resolver breakpoint from the command line """
35        self.build()
36        self.do_test_cli()
37
38    def test_bad_command_lines(self):
39        """Make sure we get appropriate errors when we give invalid key/value
40           options"""
41        self.build()
42        self.do_test_bad_options()
43
44    def test_copy_from_dummy_target(self):
45        """Make sure we don't crash during scripted breakpoint copy from dummy target"""
46        self.build()
47        self.do_test_copy_from_dummy_target()
48
49    def make_target_and_import(self):
50        target = self.make_target()
51        self.import_resolver_script()
52        return target
53
54    def make_target(self):
55        return lldbutil.run_to_breakpoint_make_target(self)
56
57    def import_resolver_script(self):
58        interp = self.dbg.GetCommandInterpreter()
59        error = lldb.SBError()
60
61        script_name = os.path.join(self.getSourceDir(), "resolver.py")
62        source_name = os.path.join(self.getSourceDir(), "main.c")
63
64        command = "command script import " + script_name
65        result = lldb.SBCommandReturnObject()
66        interp.HandleCommand(command, result)
67        self.assertTrue(result.Succeeded(), "com scr imp failed: %s"%(result.GetError()))
68
69    def make_extra_args(self):
70        json_string = '{"symbol":"break_on_me", "test1": "value1"}'
71        json_stream = lldb.SBStream()
72        json_stream.Print(json_string)
73        extra_args = lldb.SBStructuredData()
74        error = extra_args.SetFromJSON(json_stream)
75        self.assertTrue(error.Success(), "Error making SBStructuredData: %s"%(error.GetCString()))
76        return extra_args
77
78    def do_test(self):
79        """This reads in a python file and sets a breakpoint using it."""
80
81        target = self.make_target_and_import()
82        extra_args = self.make_extra_args()
83
84        file_list = lldb.SBFileSpecList()
85        module_list = lldb.SBFileSpecList()
86
87        # Make breakpoints with this resolver using different filters, first ones that will take:
88        right = []
89        # one with no file or module spec - this one should fire:
90        right.append(target.BreakpointCreateFromScript("resolver.Resolver", extra_args, module_list, file_list))
91
92        # one with the right source file and no module - should also fire:
93        file_list.Append(lldb.SBFileSpec("main.c"))
94        right.append(target.BreakpointCreateFromScript("resolver.Resolver", extra_args, module_list, file_list))
95        # Make sure the help text shows up in the "break list" output:
96        self.expect("break list", substrs=["I am a python breakpoint resolver"], msg="Help is listed in break list")
97
98        # one with the right source file and right module - should also fire:
99        module_list.Append(lldb.SBFileSpec("a.out"))
100        right.append(target.BreakpointCreateFromScript("resolver.Resolver", extra_args, module_list, file_list))
101
102        # And one with no source file but the right module:
103        file_list.Clear()
104        right.append(target.BreakpointCreateFromScript("resolver.Resolver", extra_args, module_list, file_list))
105
106        # Make sure these all got locations:
107        for i in range (0, len(right)):
108            self.assertTrue(right[i].GetNumLocations() >= 1, "Breakpoint %d has no locations."%(i))
109
110        # Now some ones that won't take:
111
112        module_list.Clear()
113        file_list.Clear()
114        wrong = []
115
116        # one with the wrong module - should not fire:
117        module_list.Append(lldb.SBFileSpec("noSuchModule"))
118        wrong.append(target.BreakpointCreateFromScript("resolver.Resolver", extra_args, module_list, file_list))
119
120        # one with the wrong file - also should not fire:
121        file_list.Clear()
122        module_list.Clear()
123        file_list.Append(lldb.SBFileSpec("noFileOfThisName.xxx"))
124        wrong.append(target.BreakpointCreateFromScript("resolver.Resolver", extra_args, module_list, file_list))
125
126        # Now make sure the CU level iteration obeys the file filters:
127        file_list.Clear()
128        module_list.Clear()
129        file_list.Append(lldb.SBFileSpec("no_such_file.xxx"))
130        wrong.append(target.BreakpointCreateFromScript("resolver.ResolverCUDepth", extra_args, module_list, file_list))
131
132        # And the Module filters:
133        file_list.Clear()
134        module_list.Clear()
135        module_list.Append(lldb.SBFileSpec("NoSuchModule.dylib"))
136        wrong.append(target.BreakpointCreateFromScript("resolver.ResolverCUDepth", extra_args, module_list, file_list))
137
138        # Now make sure the Function level iteration obeys the file filters:
139        file_list.Clear()
140        module_list.Clear()
141        file_list.Append(lldb.SBFileSpec("no_such_file.xxx"))
142        wrong.append(target.BreakpointCreateFromScript("resolver.ResolverFuncDepth", extra_args, module_list, file_list))
143
144        # And the Module filters:
145        file_list.Clear()
146        module_list.Clear()
147        module_list.Append(lldb.SBFileSpec("NoSuchModule.dylib"))
148        wrong.append(target.BreakpointCreateFromScript("resolver.ResolverFuncDepth", extra_args, module_list, file_list))
149
150        # Make sure these didn't get locations:
151        for i in range(0, len(wrong)):
152            self.assertEqual(wrong[i].GetNumLocations(), 0, "Breakpoint %d has locations."%(i))
153
154        # Now run to main and ensure we hit the breakpoints we should have:
155
156        lldbutil.run_to_breakpoint_do_run(self, target, right[0])
157
158        # Test the hit counts:
159        for i in range(0, len(right)):
160            self.assertEqual(right[i].GetHitCount(), 1, "Breakpoint %d has the wrong hit count"%(i))
161
162        for i in range(0, len(wrong)):
163            self.assertEqual(wrong[i].GetHitCount(), 0, "Breakpoint %d has the wrong hit count"%(i))
164
165    def do_test_depths(self):
166        """This test uses a class variable in resolver.Resolver which gets set to 1 if we saw
167           compile unit and 2 if we only saw modules.  If the search depth is module, you get passed just
168           the modules with no comp_unit.  If the depth is comp_unit you get comp_units.  So we can use
169           this to test that our callback gets called at the right depth."""
170
171        target = self.make_target_and_import()
172        extra_args = self.make_extra_args()
173
174        file_list = lldb.SBFileSpecList()
175        module_list = lldb.SBFileSpecList()
176        module_list.Append(lldb.SBFileSpec("a.out"))
177
178        # Make a breakpoint that has no __get_depth__, check that that is converted to eSearchDepthModule:
179        bkpt = target.BreakpointCreateFromScript("resolver.Resolver", extra_args, module_list, file_list)
180        self.assertTrue(bkpt.GetNumLocations() > 0, "Resolver got no locations.")
181        self.expect("script print(resolver.Resolver.got_files)", substrs=["2"], msg="Was only passed modules")
182
183        # Make a breakpoint that asks for modules, check that we didn't get any files:
184        bkpt = target.BreakpointCreateFromScript("resolver.ResolverModuleDepth", extra_args, module_list, file_list)
185        self.assertTrue(bkpt.GetNumLocations() > 0, "ResolverModuleDepth got no locations.")
186        self.expect("script print(resolver.Resolver.got_files)", substrs=["2"], msg="Was only passed modules")
187
188        # Make a breakpoint that asks for compile units, check that we didn't get any files:
189        bkpt = target.BreakpointCreateFromScript("resolver.ResolverCUDepth", extra_args, module_list, file_list)
190        self.assertTrue(bkpt.GetNumLocations() > 0, "ResolverCUDepth got no locations.")
191        self.expect("script print(resolver.Resolver.got_files)", substrs=["1"], msg="Was passed compile units")
192
193        # Make a breakpoint that returns a bad value - we should convert that to "modules" so check that:
194        bkpt = target.BreakpointCreateFromScript("resolver.ResolverBadDepth", extra_args, module_list, file_list)
195        self.assertTrue(bkpt.GetNumLocations() > 0, "ResolverBadDepth got no locations.")
196        self.expect("script print(resolver.Resolver.got_files)", substrs=["2"], msg="Was only passed modules")
197
198        # Make a breakpoint that searches at function depth:
199        bkpt = target.BreakpointCreateFromScript("resolver.ResolverFuncDepth", extra_args, module_list, file_list)
200        self.assertTrue(bkpt.GetNumLocations() > 0, "ResolverFuncDepth got no locations.")
201        self.expect("script print(resolver.Resolver.got_files)", substrs=["3"], msg="Was only passed modules")
202        self.expect("script print(resolver.Resolver.func_list)", substrs=['test_func', 'break_on_me', 'main'], msg="Saw all the functions")
203
204    def do_test_cli(self):
205        target = self.make_target_and_import()
206
207        lldbutil.run_break_set_by_script(self, "resolver.Resolver", extra_options="-k symbol -v break_on_me")
208
209        # Make sure setting a resolver breakpoint doesn't pollute further breakpoint setting
210        # by checking the description of a regular file & line breakpoint to make sure it
211        # doesn't mention the Python Resolver function:
212        bkpt_no = lldbutil.run_break_set_by_file_and_line(self, "main.c", 12)
213        bkpt = target.FindBreakpointByID(bkpt_no)
214        strm = lldb.SBStream()
215        bkpt.GetDescription(strm, False)
216        used_resolver = "I am a python breakpoint resolver" in strm.GetData()
217        self.assertFalse(used_resolver, "Found the resolver description in the file & line breakpoint description.")
218
219        # Also make sure the breakpoint was where we expected:
220        bp_loc = bkpt.GetLocationAtIndex(0)
221        bp_sc = bp_loc.GetAddress().GetSymbolContext(lldb.eSymbolContextEverything)
222        bp_se = bp_sc.GetLineEntry()
223        self.assertEqual(bp_se.GetLine(), 12, "Got the right line number")
224        self.assertEqual(bp_se.GetFileSpec().GetFilename(), "main.c", "Got the right filename")
225
226    def do_test_bad_options(self):
227        target = self.make_target_and_import()
228
229        self.expect("break set -P resolver.Resolver -k a_key", error = True, msg="Missing value at end",
230           substrs=['Key: "a_key" missing value'])
231        self.expect("break set -P resolver.Resolver -v a_value", error = True, msg="Missing key at end",
232           substrs=['Value: "a_value" missing matching key'])
233        self.expect("break set -P resolver.Resolver -v a_value -k a_key -v another_value", error = True, msg="Missing key among args",
234           substrs=['Value: "a_value" missing matching key'])
235        self.expect("break set -P resolver.Resolver -k a_key -k a_key -v another_value", error = True, msg="Missing value among args",
236           substrs=['Key: "a_key" missing value'])
237
238    def do_test_copy_from_dummy_target(self):
239        # Import breakpoint scripted resolver.
240        self.import_resolver_script()
241
242        # Create a scripted breakpoint.
243        self.runCmd("breakpoint set -P resolver.Resolver -k symbol -v break_on_me",
244                    BREAKPOINT_CREATED)
245
246        # This is the function to remove breakpoints from the dummy target
247        # to get a clean state for the next test case.
248        def cleanup():
249            self.runCmd('breakpoint delete -D -f', check=False)
250            self.runCmd('breakpoint list', check=False)
251
252        # Execute the cleanup function during test case tear down.
253        self.addTearDownHook(cleanup)
254
255        # Check that target creating doesn't crash.
256        target = self.make_target()
257