1"""
2Test breakpoint serialization.
3"""
4
5import os
6import lldb
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbutil
10
11
12class BreakpointSerialization(TestBase):
13
14    mydir = TestBase.compute_mydir(__file__)
15    NO_DEBUG_INFO_TESTCASE = True
16
17    @add_test_categories(['pyapi'])
18    @skipIfReproducer # side_effect bypasses reproducer
19    def test_resolvers(self):
20        """Use Python APIs to test that we serialize resolvers."""
21        self.build()
22        self.setup_targets_and_cleanup()
23        self.do_check_resolvers()
24
25    @skipIfReproducer # side_effect bypasses reproducer
26    def test_filters(self):
27        """Use Python APIs to test that we serialize search filters correctly."""
28        self.build()
29        self.setup_targets_and_cleanup()
30        self.do_check_filters()
31
32    @skipIfReproducer # side_effect bypasses reproducer
33    def test_options(self):
34        """Use Python APIs to test that we serialize breakpoint options correctly."""
35        self.build()
36        self.setup_targets_and_cleanup()
37        self.do_check_options()
38
39    @skipIfReproducer # side_effect bypasses reproducer
40    def test_appending(self):
41        """Use Python APIs to test that we serialize breakpoint options correctly."""
42        self.build()
43        self.setup_targets_and_cleanup()
44        self.do_check_appending()
45
46    @skipIfReproducer # side_effect bypasses reproducer
47    def test_name_filters(self):
48        """Use python APIs to test that reading in by name works correctly."""
49        self.build()
50        self.setup_targets_and_cleanup()
51        self.do_check_names()
52
53    @skipIfReproducer # side_effect bypasses reproducer
54    def test_scripted_extra_args(self):
55        self.build()
56        self.setup_targets_and_cleanup()
57        self.do_check_extra_args()
58
59    def setup_targets_and_cleanup(self):
60        def cleanup ():
61            self.RemoveTempFile(self.bkpts_file_path)
62
63            if self.orig_target.IsValid():
64                self.dbg.DeleteTarget(self.orig_target)
65                self.dbg.DeleteTarget(self.copy_target)
66
67        self.addTearDownHook(cleanup)
68        self.RemoveTempFile(self.bkpts_file_path)
69
70        exe = self.getBuildArtifact("a.out")
71
72        # Create the targets we are making breakpoints in and copying them to:
73        self.orig_target = self.dbg.CreateTarget(exe)
74        self.assertTrue(self.orig_target, VALID_TARGET)
75
76        self.copy_target = self.dbg.CreateTarget(exe)
77        self.assertTrue(self.copy_target, VALID_TARGET)
78
79    def setUp(self):
80        # Call super's setUp().
81        TestBase.setUp(self)
82
83        self.bkpts_file_path = self.getBuildArtifact("breakpoints.json")
84        self.bkpts_file_spec = lldb.SBFileSpec(self.bkpts_file_path)
85
86    def check_equivalence(self, source_bps, do_write = True):
87
88        error = lldb.SBError()
89
90        if (do_write):
91            error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, source_bps)
92            self.assertTrue(error.Success(), "Failed writing breakpoints to file: %s."%(error.GetCString()))
93
94        copy_bps = lldb.SBBreakpointList(self.copy_target)
95        error = self.copy_target.BreakpointsCreateFromFile(self.bkpts_file_spec, copy_bps)
96        self.assertTrue(error.Success(), "Failed reading breakpoints from file: %s"%(error.GetCString()))
97
98        num_source_bps = source_bps.GetSize()
99        num_copy_bps = copy_bps.GetSize()
100        self.assertTrue(num_source_bps == num_copy_bps, "Didn't get same number of input and output breakpoints - orig: %d copy: %d"%(num_source_bps, num_copy_bps))
101
102        for i in range(0, num_source_bps):
103            source_bp = source_bps.GetBreakpointAtIndex(i)
104            source_desc = lldb.SBStream()
105            source_bp.GetDescription(source_desc, False)
106            source_text = source_desc.GetData()
107
108            # I am assuming here that the breakpoints will get written out in breakpoint ID order, and
109            # read back in ditto.  That is true right now, and I can't see any reason to do it differently
110            # but if we do we can go to writing the breakpoints one by one, or sniffing the descriptions to
111            # see which one is which.
112            copy_id = source_bp.GetID()
113            copy_bp = copy_bps.FindBreakpointByID(copy_id)
114            self.assertTrue(copy_bp.IsValid(), "Could not find copy breakpoint %d."%(copy_id))
115
116            copy_desc = lldb.SBStream()
117            copy_bp.GetDescription(copy_desc, False)
118            copy_text = copy_desc.GetData()
119
120            # These two should be identical.
121            # print ("Source text for %d is %s."%(i, source_text))
122            self.assertTrue (source_text == copy_text, "Source and dest breakpoints are not identical: \nsource: %s\ndest: %s"%(source_text, copy_text))
123
124    def do_check_resolvers(self):
125        """Use Python APIs to check serialization of breakpoint resolvers"""
126
127        empty_module_list = lldb.SBFileSpecList()
128        empty_cu_list = lldb.SBFileSpecList()
129        blubby_file_spec = lldb.SBFileSpec(os.path.join(self.getSourceDir(), "blubby.c"))
130
131        # It isn't actually important for these purposes that these breakpoint
132        # actually have locations.
133        source_bps = lldb.SBBreakpointList(self.orig_target)
134        source_bps.Append(self.orig_target.BreakpointCreateByLocation("blubby.c", 666))
135        # Make sure we do one breakpoint right:
136        self.check_equivalence(source_bps)
137        source_bps.Clear()
138
139        source_bps.Append(self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list))
140        source_bps.Append(self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeFull, empty_module_list,empty_cu_list))
141        source_bps.Append(self.orig_target.BreakpointCreateBySourceRegex("dont really care", blubby_file_spec))
142
143        # And some number greater than one:
144        self.check_equivalence(source_bps)
145
146    def do_check_filters(self):
147        """Use Python APIs to check serialization of breakpoint filters."""
148        module_list = lldb.SBFileSpecList()
149        module_list.Append(lldb.SBFileSpec("SomeBinary"))
150        module_list.Append(lldb.SBFileSpec("SomeOtherBinary"))
151
152        cu_list = lldb.SBFileSpecList()
153        cu_list.Append(lldb.SBFileSpec("SomeCU.c"))
154        cu_list.Append(lldb.SBFileSpec("AnotherCU.c"))
155        cu_list.Append(lldb.SBFileSpec("ThirdCU.c"))
156
157        blubby_file_spec = lldb.SBFileSpec(os.path.join(self.getSourceDir(), "blubby.c"))
158
159        # It isn't actually important for these purposes that these breakpoint
160        # actually have locations.
161        source_bps = lldb.SBBreakpointList(self.orig_target)
162        bkpt = self.orig_target.BreakpointCreateByLocation(blubby_file_spec, 666, 0, module_list)
163        source_bps.Append(bkpt)
164
165        # Make sure we do one right:
166        self.check_equivalence(source_bps)
167        source_bps.Clear()
168
169        bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeAuto, module_list, cu_list)
170        source_bps.Append(bkpt)
171        bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeFull, module_list, cu_list)
172        source_bps.Append(bkpt)
173        bkpt = self.orig_target.BreakpointCreateBySourceRegex("dont really care", blubby_file_spec)
174        source_bps.Append(bkpt)
175
176        # And some number greater than one:
177        self.check_equivalence(source_bps)
178
179    def do_check_options(self):
180        """Use Python APIs to check serialization of breakpoint options."""
181
182        empty_module_list = lldb.SBFileSpecList()
183        empty_cu_list = lldb.SBFileSpecList()
184        blubby_file_spec = lldb.SBFileSpec(os.path.join(self.getSourceDir(), "blubby.c"))
185
186        # It isn't actually important for these purposes that these breakpoint
187        # actually have locations.
188        source_bps = lldb.SBBreakpointList(self.orig_target)
189
190        bkpt = self.orig_target.BreakpointCreateByLocation(
191            lldb.SBFileSpec("blubby.c"), 666, 333, 0, lldb.SBFileSpecList())
192        bkpt.SetEnabled(False)
193        bkpt.SetOneShot(True)
194        bkpt.SetThreadID(10)
195        source_bps.Append(bkpt)
196
197        # Make sure we get one right:
198        self.check_equivalence(source_bps)
199        source_bps.Clear()
200
201        bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list)
202        bkpt.SetIgnoreCount(10)
203        bkpt.SetThreadName("grubby")
204        source_bps.Append(bkpt)
205
206        bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list)
207        bkpt.SetCondition("gonna remove this")
208        bkpt.SetCondition("")
209        source_bps.Append(bkpt)
210
211        bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeFull, empty_module_list,empty_cu_list)
212        bkpt.SetCondition("something != something_else")
213        bkpt.SetQueueName("grubby")
214        bkpt.AddName("FirstName")
215        bkpt.AddName("SecondName")
216        bkpt.SetScriptCallbackBody('\tprint("I am a function that prints.")\n\tprint("I don\'t do anything else")\n')
217        source_bps.Append(bkpt)
218
219        bkpt = self.orig_target.BreakpointCreateBySourceRegex("dont really care", blubby_file_spec)
220        cmd_list = lldb.SBStringList()
221        cmd_list.AppendString("frame var")
222        cmd_list.AppendString("thread backtrace")
223
224        bkpt.SetCommandLineCommands(cmd_list)
225        source_bps.Append(bkpt)
226
227        self.check_equivalence(source_bps)
228
229    def do_check_appending(self):
230        """Use Python APIs to check appending to already serialized options."""
231
232        empty_module_list = lldb.SBFileSpecList()
233        empty_cu_list = lldb.SBFileSpecList()
234        blubby_file_spec = lldb.SBFileSpec(os.path.join(self.getSourceDir(), "blubby.c"))
235
236        # It isn't actually important for these purposes that these breakpoint
237        # actually have locations.
238
239        all_bps = lldb.SBBreakpointList(self.orig_target)
240        source_bps = lldb.SBBreakpointList(self.orig_target)
241
242        bkpt = self.orig_target.BreakpointCreateByLocation(
243            lldb.SBFileSpec("blubby.c"), 666, 333, 0, lldb.SBFileSpecList())
244        bkpt.SetEnabled(False)
245        bkpt.SetOneShot(True)
246        bkpt.SetThreadID(10)
247        source_bps.Append(bkpt)
248        all_bps.Append(bkpt)
249
250        error = lldb.SBError()
251        error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, source_bps)
252        self.assertTrue(error.Success(), "Failed writing breakpoints to file: %s."%(error.GetCString()))
253
254        source_bps.Clear()
255
256        bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list)
257        bkpt.SetIgnoreCount(10)
258        bkpt.SetThreadName("grubby")
259        source_bps.Append(bkpt)
260        all_bps.Append(bkpt)
261
262        bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeFull, empty_module_list,empty_cu_list)
263        bkpt.SetCondition("something != something_else")
264        bkpt.SetQueueName("grubby")
265        bkpt.AddName("FirstName")
266        bkpt.AddName("SecondName")
267
268        source_bps.Append(bkpt)
269        all_bps.Append(bkpt)
270
271        error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, source_bps, True)
272        self.assertTrue(error.Success(), "Failed appending breakpoints to file: %s."%(error.GetCString()))
273
274        self.check_equivalence(all_bps)
275
276    def do_check_names(self):
277        bkpt = self.orig_target.BreakpointCreateByLocation(
278            lldb.SBFileSpec("blubby.c"), 666, 333, 0, lldb.SBFileSpecList())
279        good_bkpt_name = "GoodBreakpoint"
280        write_bps = lldb.SBBreakpointList(self.orig_target)
281        bkpt.AddName(good_bkpt_name)
282        write_bps.Append(bkpt)
283
284        error = lldb.SBError()
285        error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, write_bps)
286        self.assertTrue(error.Success(), "Failed writing breakpoints to file: %s."%(error.GetCString()))
287
288        copy_bps = lldb.SBBreakpointList(self.copy_target)
289        names_list = lldb.SBStringList()
290        names_list.AppendString("NoSuchName")
291
292        error = self.copy_target.BreakpointsCreateFromFile(self.bkpts_file_spec, names_list, copy_bps)
293        self.assertTrue(error.Success(), "Failed reading breakpoints from file: %s"%(error.GetCString()))
294        self.assertTrue(copy_bps.GetSize() == 0, "Found breakpoints with a nonexistent name.")
295
296        names_list.AppendString(good_bkpt_name)
297        error = self.copy_target.BreakpointsCreateFromFile(self.bkpts_file_spec, names_list, copy_bps)
298        self.assertTrue(error.Success(), "Failed reading breakpoints from file: %s"%(error.GetCString()))
299        self.assertTrue(copy_bps.GetSize() == 1, "Found the matching breakpoint.")
300
301    def do_check_extra_args(self):
302
303        import side_effect
304        interp = self.dbg.GetCommandInterpreter()
305        error = lldb.SBError()
306
307        script_name = os.path.join(self.getSourceDir(), "resolver.py")
308
309        command = "command script import " + script_name
310        result = lldb.SBCommandReturnObject()
311        interp.HandleCommand(command, result)
312        self.assertTrue(result.Succeeded(), "com scr imp failed: %s"%(result.GetError()))
313
314        # First make sure a scripted breakpoint with no args works:
315        bkpt = self.orig_target.BreakpointCreateFromScript("resolver.Resolver", lldb.SBStructuredData(),
316                                                           lldb.SBFileSpecList(), lldb.SBFileSpecList())
317        self.assertTrue(bkpt.IsValid(), "Bkpt is valid")
318        write_bps = lldb.SBBreakpointList(self.orig_target)
319
320        error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, write_bps)
321        self.assertTrue(error.Success(), "Failed writing breakpoints: %s"%(error.GetCString()))
322
323        side_effect.g_extra_args = None
324        copy_bps = lldb.SBBreakpointList(self.copy_target)
325        error = self.copy_target.BreakpointsCreateFromFile(self.bkpts_file_spec, copy_bps)
326        self.assertTrue(error.Success(), "Failed reading breakpoints: %s"%(error.GetCString()))
327
328        self.assertEqual(copy_bps.GetSize(), 1, "Got one breakpoint from file.")
329        no_keys = lldb.SBStringList()
330        side_effect.g_extra_args.GetKeys(no_keys)
331        self.assertEqual(no_keys.GetSize(), 0, "Should have no keys")
332
333        self.orig_target.DeleteAllBreakpoints()
334        self.copy_target.DeleteAllBreakpoints()
335
336        # Now try one with extra args:
337
338        extra_args = lldb.SBStructuredData()
339        stream = lldb.SBStream()
340        stream.Print('{"first_arg" : "first_value", "second_arg" : "second_value"}')
341        extra_args.SetFromJSON(stream)
342        self.assertTrue(extra_args.IsValid(), "SBStructuredData is valid.")
343
344        bkpt = self.orig_target.BreakpointCreateFromScript("resolver.Resolver",
345                                                           extra_args, lldb.SBFileSpecList(), lldb.SBFileSpecList())
346        self.assertTrue(bkpt.IsValid(), "Bkpt is valid")
347        write_bps = lldb.SBBreakpointList(self.orig_target)
348
349        error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, write_bps)
350        self.assertTrue(error.Success(), "Failed writing breakpoints: %s"%(error.GetCString()))
351
352        orig_extra_args = side_effect.g_extra_args
353        self.assertTrue(orig_extra_args.IsValid(), "Extra args originally valid")
354
355        orig_keys = lldb.SBStringList()
356        orig_extra_args.GetKeys(orig_keys)
357        self.assertEqual(2, orig_keys.GetSize(), "Should have two keys")
358
359        side_effect.g_extra_args = None
360
361        copy_bps = lldb.SBBreakpointList(self.copy_target)
362        error = self.copy_target.BreakpointsCreateFromFile(self.bkpts_file_spec, copy_bps)
363        self.assertTrue(error.Success(), "Failed reading breakpoints: %s"%(error.GetCString()))
364
365        self.assertEqual(copy_bps.GetSize(), 1, "Got one breakpoint from file.")
366
367        copy_extra_args = side_effect.g_extra_args
368        copy_keys = lldb.SBStringList()
369        copy_extra_args.GetKeys(copy_keys)
370        self.assertEqual(2, copy_keys.GetSize(), "Copy should have two keys")
371
372        for idx in range(0, orig_keys.GetSize()):
373            key = orig_keys.GetStringAtIndex(idx)
374            copy_value = copy_extra_args.GetValueForKey(key).GetStringValue(100)
375
376            if key == "first_arg":
377                self.assertEqual(copy_value, "first_value")
378            elif key == "second_arg":
379                self.assertEqual(copy_value, "second_value")
380            else:
381                self.Fail("Unknown key: %s"%(key))
382