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