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