1#!/usr/bin/env python
2from __future__ import print_function
3
4import lldb
5import shlex
6import sys
7try:
8    from tkinter import *
9    import tkinter.ttk as ttk
10except ImportError:
11    from Tkinter import *
12    import ttk
13
14
15class ValueTreeItemDelegate(object):
16
17    def __init__(self, value):
18        self.value = value
19
20    def get_item_dictionary(self):
21        name = self.value.name
22        if name is None:
23            name = ''
24        typename = self.value.type
25        if typename is None:
26            typename = ''
27        value = self.value.value
28        if value is None:
29            value = ''
30        summary = self.value.summary
31        if summary is None:
32            summary = ''
33        has_children = self.value.MightHaveChildren()
34        return {'#0': name,
35                'typename': typename,
36                'value': value,
37                'summary': summary,
38                'children': has_children,
39                'tree-item-delegate': self}
40
41    def get_child_item_dictionaries(self):
42        item_dicts = list()
43        for i in range(self.value.num_children):
44            item_delegate = ValueTreeItemDelegate(
45                self.value.GetChildAtIndex(i))
46            item_dicts.append(item_delegate.get_item_dictionary())
47        return item_dicts
48
49
50class FrameTreeItemDelegate(object):
51
52    def __init__(self, frame):
53        self.frame = frame
54
55    def get_item_dictionary(self):
56        id = self.frame.GetFrameID()
57        name = 'frame #%u' % (id)
58        value = '0x%16.16x' % (self.frame.GetPC())
59        stream = lldb.SBStream()
60        self.frame.GetDescription(stream)
61        summary = stream.GetData().split("`")[1]
62        return {
63            '#0': name,
64            'value': value,
65            'summary': summary,
66            'children': self.frame.GetVariables(
67                True,
68                True,
69                True,
70                True).GetSize() > 0,
71            'tree-item-delegate': self}
72
73    def get_child_item_dictionaries(self):
74        item_dicts = list()
75        variables = self.frame.GetVariables(True, True, True, True)
76        n = variables.GetSize()
77        for i in range(n):
78            item_delegate = ValueTreeItemDelegate(variables[i])
79            item_dicts.append(item_delegate.get_item_dictionary())
80        return item_dicts
81
82
83class ThreadTreeItemDelegate(object):
84
85    def __init__(self, thread):
86        self.thread = thread
87
88    def get_item_dictionary(self):
89        num_frames = self.thread.GetNumFrames()
90        name = 'thread #%u' % (self.thread.GetIndexID())
91        value = '0x%x' % (self.thread.GetThreadID())
92        summary = '%u frames' % (num_frames)
93        return {'#0': name,
94                'value': value,
95                'summary': summary,
96                'children': num_frames > 0,
97                'tree-item-delegate': self}
98
99    def get_child_item_dictionaries(self):
100        item_dicts = list()
101        for frame in self.thread:
102            item_delegate = FrameTreeItemDelegate(frame)
103            item_dicts.append(item_delegate.get_item_dictionary())
104        return item_dicts
105
106
107class ProcessTreeItemDelegate(object):
108
109    def __init__(self, process):
110        self.process = process
111
112    def get_item_dictionary(self):
113        id = self.process.GetProcessID()
114        num_threads = self.process.GetNumThreads()
115        value = str(self.process.GetProcessID())
116        summary = self.process.target.executable.fullpath
117        return {'#0': 'process',
118                'value': value,
119                'summary': summary,
120                'children': num_threads > 0,
121                'tree-item-delegate': self}
122
123    def get_child_item_dictionaries(self):
124        item_dicts = list()
125        for thread in self.process:
126            item_delegate = ThreadTreeItemDelegate(thread)
127            item_dicts.append(item_delegate.get_item_dictionary())
128        return item_dicts
129
130
131class TargetTreeItemDelegate(object):
132
133    def __init__(self, target):
134        self.target = target
135
136    def get_item_dictionary(self):
137        value = str(self.target.triple)
138        summary = self.target.executable.fullpath
139        return {'#0': 'target',
140                'value': value,
141                'summary': summary,
142                'children': True,
143                'tree-item-delegate': self}
144
145    def get_child_item_dictionaries(self):
146        item_dicts = list()
147        image_item_delegate = TargetImagesTreeItemDelegate(self.target)
148        item_dicts.append(image_item_delegate.get_item_dictionary())
149        return item_dicts
150
151
152class TargetImagesTreeItemDelegate(object):
153
154    def __init__(self, target):
155        self.target = target
156
157    def get_item_dictionary(self):
158        value = str(self.target.triple)
159        summary = self.target.executable.fullpath
160        num_modules = self.target.GetNumModules()
161        return {'#0': 'images',
162                'value': '',
163                'summary': '%u images' % num_modules,
164                'children': num_modules > 0,
165                'tree-item-delegate': self}
166
167    def get_child_item_dictionaries(self):
168        item_dicts = list()
169        for i in range(self.target.GetNumModules()):
170            module = self.target.GetModuleAtIndex(i)
171            image_item_delegate = ModuleTreeItemDelegate(
172                self.target, module, i)
173            item_dicts.append(image_item_delegate.get_item_dictionary())
174        return item_dicts
175
176
177class ModuleTreeItemDelegate(object):
178
179    def __init__(self, target, module, index):
180        self.target = target
181        self.module = module
182        self.index = index
183
184    def get_item_dictionary(self):
185        name = 'module %u' % (self.index)
186        value = self.module.file.basename
187        summary = self.module.file.dirname
188        return {'#0': name,
189                'value': value,
190                'summary': summary,
191                'children': True,
192                'tree-item-delegate': self}
193
194    def get_child_item_dictionaries(self):
195        item_dicts = list()
196        sections_item_delegate = ModuleSectionsTreeItemDelegate(
197            self.target, self.module)
198        item_dicts.append(sections_item_delegate.get_item_dictionary())
199
200        symbols_item_delegate = ModuleSymbolsTreeItemDelegate(
201            self.target, self.module)
202        item_dicts.append(symbols_item_delegate.get_item_dictionary())
203
204        comp_units_item_delegate = ModuleCompileUnitsTreeItemDelegate(
205            self.target, self.module)
206        item_dicts.append(comp_units_item_delegate.get_item_dictionary())
207        return item_dicts
208
209
210class ModuleSectionsTreeItemDelegate(object):
211
212    def __init__(self, target, module):
213        self.target = target
214        self.module = module
215
216    def get_item_dictionary(self):
217        name = 'sections'
218        value = ''
219        summary = '%u sections' % (self.module.GetNumSections())
220        return {'#0': name,
221                'value': value,
222                'summary': summary,
223                'children': True,
224                'tree-item-delegate': self}
225
226    def get_child_item_dictionaries(self):
227        item_dicts = list()
228        num_sections = self.module.GetNumSections()
229        for i in range(num_sections):
230            section = self.module.GetSectionAtIndex(i)
231            image_item_delegate = SectionTreeItemDelegate(self.target, section)
232            item_dicts.append(image_item_delegate.get_item_dictionary())
233        return item_dicts
234
235
236class SectionTreeItemDelegate(object):
237
238    def __init__(self, target, section):
239        self.target = target
240        self.section = section
241
242    def get_item_dictionary(self):
243        name = self.section.name
244        section_load_addr = self.section.GetLoadAddress(self.target)
245        if section_load_addr != lldb.LLDB_INVALID_ADDRESS:
246            value = '0x%16.16x' % (section_load_addr)
247        else:
248            value = '0x%16.16x *' % (self.section.file_addr)
249        summary = ''
250        return {'#0': name,
251                'value': value,
252                'summary': summary,
253                'children': self.section.GetNumSubSections() > 0,
254                'tree-item-delegate': self}
255
256    def get_child_item_dictionaries(self):
257        item_dicts = list()
258        num_sections = self.section.GetNumSubSections()
259        for i in range(num_sections):
260            section = self.section.GetSubSectionAtIndex(i)
261            image_item_delegate = SectionTreeItemDelegate(self.target, section)
262            item_dicts.append(image_item_delegate.get_item_dictionary())
263        return item_dicts
264
265
266class ModuleCompileUnitsTreeItemDelegate(object):
267
268    def __init__(self, target, module):
269        self.target = target
270        self.module = module
271
272    def get_item_dictionary(self):
273        name = 'compile units'
274        value = ''
275        summary = '%u compile units' % (self.module.GetNumSections())
276        return {'#0': name,
277                'value': value,
278                'summary': summary,
279                'children': self.module.GetNumCompileUnits() > 0,
280                'tree-item-delegate': self}
281
282    def get_child_item_dictionaries(self):
283        item_dicts = list()
284        num_cus = self.module.GetNumCompileUnits()
285        for i in range(num_cus):
286            cu = self.module.GetCompileUnitAtIndex(i)
287            image_item_delegate = CompileUnitTreeItemDelegate(self.target, cu)
288            item_dicts.append(image_item_delegate.get_item_dictionary())
289        return item_dicts
290
291
292class CompileUnitTreeItemDelegate(object):
293
294    def __init__(self, target, cu):
295        self.target = target
296        self.cu = cu
297
298    def get_item_dictionary(self):
299        name = self.cu.GetFileSpec().basename
300        value = ''
301        num_lines = self.cu.GetNumLineEntries()
302        summary = ''
303        return {'#0': name,
304                'value': value,
305                'summary': summary,
306                'children': num_lines > 0,
307                'tree-item-delegate': self}
308
309    def get_child_item_dictionaries(self):
310        item_dicts = list()
311        item_delegate = LineTableTreeItemDelegate(self.target, self.cu)
312        item_dicts.append(item_delegate.get_item_dictionary())
313        return item_dicts
314
315
316class LineTableTreeItemDelegate(object):
317
318    def __init__(self, target, cu):
319        self.target = target
320        self.cu = cu
321
322    def get_item_dictionary(self):
323        name = 'line table'
324        value = ''
325        num_lines = self.cu.GetNumLineEntries()
326        summary = '%u line entries' % (num_lines)
327        return {'#0': name,
328                'value': value,
329                'summary': summary,
330                'children': num_lines > 0,
331                'tree-item-delegate': self}
332
333    def get_child_item_dictionaries(self):
334        item_dicts = list()
335        num_lines = self.cu.GetNumLineEntries()
336        for i in range(num_lines):
337            line_entry = self.cu.GetLineEntryAtIndex(i)
338            item_delegate = LineEntryTreeItemDelegate(
339                self.target, line_entry, i)
340            item_dicts.append(item_delegate.get_item_dictionary())
341        return item_dicts
342
343
344class LineEntryTreeItemDelegate(object):
345
346    def __init__(self, target, line_entry, index):
347        self.target = target
348        self.line_entry = line_entry
349        self.index = index
350
351    def get_item_dictionary(self):
352        name = str(self.index)
353        address = self.line_entry.GetStartAddress()
354        load_addr = address.GetLoadAddress(self.target)
355        if load_addr != lldb.LLDB_INVALID_ADDRESS:
356            value = '0x%16.16x' % (load_addr)
357        else:
358            value = '0x%16.16x *' % (address.file_addr)
359        summary = self.line_entry.GetFileSpec().fullpath + ':' + \
360            str(self.line_entry.line)
361        return {'#0': name,
362                'value': value,
363                'summary': summary,
364                'children': False,
365                'tree-item-delegate': self}
366
367    def get_child_item_dictionaries(self):
368        item_dicts = list()
369        return item_dicts
370
371
372class InstructionTreeItemDelegate(object):
373
374    def __init__(self, target, instr):
375        self.target = target
376        self.instr = instr
377
378    def get_item_dictionary(self):
379        address = self.instr.GetAddress()
380        load_addr = address.GetLoadAddress(self.target)
381        if load_addr != lldb.LLDB_INVALID_ADDRESS:
382            name = '0x%16.16x' % (load_addr)
383        else:
384            name = '0x%16.16x *' % (address.file_addr)
385        value = self.instr.GetMnemonic(
386            self.target) + ' ' + self.instr.GetOperands(self.target)
387        summary = self.instr.GetComment(self.target)
388        return {'#0': name,
389                'value': value,
390                'summary': summary,
391                'children': False,
392                'tree-item-delegate': self}
393
394
395class ModuleSymbolsTreeItemDelegate(object):
396
397    def __init__(self, target, module):
398        self.target = target
399        self.module = module
400
401    def get_item_dictionary(self):
402        name = 'symbols'
403        value = ''
404        summary = '%u symbols' % (self.module.GetNumSymbols())
405        return {'#0': name,
406                'value': value,
407                'summary': summary,
408                'children': True,
409                'tree-item-delegate': self}
410
411    def get_child_item_dictionaries(self):
412        item_dicts = list()
413        num_symbols = self.module.GetNumSymbols()
414        for i in range(num_symbols):
415            symbol = self.module.GetSymbolAtIndex(i)
416            image_item_delegate = SymbolTreeItemDelegate(
417                self.target, symbol, i)
418            item_dicts.append(image_item_delegate.get_item_dictionary())
419        return item_dicts
420
421
422class SymbolTreeItemDelegate(object):
423
424    def __init__(self, target, symbol, index):
425        self.target = target
426        self.symbol = symbol
427        self.index = index
428
429    def get_item_dictionary(self):
430        address = self.symbol.GetStartAddress()
431        name = '[%u]' % self.index
432        symbol_load_addr = address.GetLoadAddress(self.target)
433        if symbol_load_addr != lldb.LLDB_INVALID_ADDRESS:
434            value = '0x%16.16x' % (symbol_load_addr)
435        else:
436            value = '0x%16.16x *' % (address.file_addr)
437        summary = self.symbol.name
438        return {'#0': name,
439                'value': value,
440                'summary': summary,
441                'children': False,
442                'tree-item-delegate': self}
443
444    def get_child_item_dictionaries(self):
445        item_dicts = list()
446        return item_dicts
447
448
449class DelegateTree(ttk.Frame):
450
451    def __init__(self, column_dicts, delegate, title, name):
452        ttk.Frame.__init__(self, name=name)
453        self.pack(expand=Y, fill=BOTH)
454        self.master.title(title)
455        self.delegate = delegate
456        self.columns_dicts = column_dicts
457        self.item_id_to_item_dict = dict()
458        frame = Frame(self)
459        frame.pack(side=TOP, fill=BOTH, expand=Y)
460        self._create_treeview(frame)
461        self._populate_root()
462
463    def _create_treeview(self, parent):
464        frame = ttk.Frame(parent)
465        frame.pack(side=TOP, fill=BOTH, expand=Y)
466
467        column_ids = list()
468        for i in range(1, len(self.columns_dicts)):
469            column_ids.append(self.columns_dicts[i]['id'])
470        # create the tree and scrollbars
471        self.tree = ttk.Treeview(columns=column_ids)
472
473        scroll_bar_v = ttk.Scrollbar(orient=VERTICAL, command=self.tree.yview)
474        scroll_bar_h = ttk.Scrollbar(
475            orient=HORIZONTAL, command=self.tree.xview)
476        self.tree['yscroll'] = scroll_bar_v.set
477        self.tree['xscroll'] = scroll_bar_h.set
478
479        # setup column headings and columns properties
480        for columns_dict in self.columns_dicts:
481            self.tree.heading(
482                columns_dict['id'],
483                text=columns_dict['text'],
484                anchor=columns_dict['anchor'])
485            self.tree.column(
486                columns_dict['id'],
487                stretch=columns_dict['stretch'])
488
489        # add tree and scrollbars to frame
490        self.tree.grid(in_=frame, row=0, column=0, sticky=NSEW)
491        scroll_bar_v.grid(in_=frame, row=0, column=1, sticky=NS)
492        scroll_bar_h.grid(in_=frame, row=1, column=0, sticky=EW)
493
494        # set frame resizing priorities
495        frame.rowconfigure(0, weight=1)
496        frame.columnconfigure(0, weight=1)
497
498        # action to perform when a node is expanded
499        self.tree.bind('<<TreeviewOpen>>', self._update_tree)
500
501    def insert_items(self, parent_id, item_dicts):
502        for item_dict in item_dicts:
503            name = None
504            values = list()
505            first = True
506            for columns_dict in self.columns_dicts:
507                if first:
508                    name = item_dict[columns_dict['id']]
509                    first = False
510                else:
511                    values.append(item_dict[columns_dict['id']])
512            item_id = self.tree.insert(parent_id,  # root item has an empty name
513                                       END,
514                                       text=name,
515                                       values=values)
516            self.item_id_to_item_dict[item_id] = item_dict
517            if item_dict['children']:
518                self.tree.insert(item_id, END, text='dummy')
519
520    def _populate_root(self):
521        # use current directory as root node
522        self.insert_items('', self.delegate.get_child_item_dictionaries())
523
524    def _update_tree(self, event):
525        # user expanded a node - build the related directory
526        item_id = self.tree.focus()      # the id of the expanded node
527        children = self.tree.get_children(item_id)
528        if len(children):
529            first_child = children[0]
530            # if the node only has a 'dummy' child, remove it and
531            # build new directory; skip if the node is already
532            # populated
533            if self.tree.item(first_child, option='text') == 'dummy':
534                self.tree.delete(first_child)
535                item_dict = self.item_id_to_item_dict[item_id]
536                item_dicts = item_dict[
537                    'tree-item-delegate'].get_child_item_dictionaries()
538                self.insert_items(item_id, item_dicts)
539
540
541@lldb.command("tk-variables")
542def tk_variable_display(debugger, command, result, dict):
543    # needed for tree creation in TK library as it uses sys.argv...
544    sys.argv = ['tk-variables']
545    target = debugger.GetSelectedTarget()
546    if not target:
547        print("invalid target", file=result)
548        return
549    process = target.GetProcess()
550    if not process:
551        print("invalid process", file=result)
552        return
553    thread = process.GetSelectedThread()
554    if not thread:
555        print("invalid thread", file=result)
556        return
557    frame = thread.GetSelectedFrame()
558    if not frame:
559        print("invalid frame", file=result)
560        return
561    # Parse command line args
562    command_args = shlex.split(command)
563    column_dicts = [{'id': '#0', 'text': 'Name', 'anchor': W, 'stretch': 0},
564                    {'id': 'typename', 'text': 'Type', 'anchor': W, 'stretch': 0},
565                    {'id': 'value', 'text': 'Value', 'anchor': W, 'stretch': 0},
566                    {'id': 'summary', 'text': 'Summary', 'anchor': W, 'stretch': 1}]
567    tree = DelegateTree(
568        column_dicts,
569        FrameTreeItemDelegate(frame),
570        'Variables',
571        'lldb-tk-variables')
572    tree.mainloop()
573
574
575@lldb.command("tk-process")
576def tk_process_display(debugger, command, result, dict):
577    # needed for tree creation in TK library as it uses sys.argv...
578    sys.argv = ['tk-process']
579    target = debugger.GetSelectedTarget()
580    if not target:
581        print("invalid target", file=result)
582        return
583    process = target.GetProcess()
584    if not process:
585        print("invalid process", file=result)
586        return
587    # Parse command line args
588    columnd_dicts = [{'id': '#0', 'text': 'Name', 'anchor': W, 'stretch': 0},
589                     {'id': 'value', 'text': 'Value', 'anchor': W, 'stretch': 0},
590                     {'id': 'summary', 'text': 'Summary', 'anchor': W, 'stretch': 1}]
591    command_args = shlex.split(command)
592    tree = DelegateTree(
593        columnd_dicts,
594        ProcessTreeItemDelegate(process),
595        'Process',
596        'lldb-tk-process')
597    tree.mainloop()
598
599
600@lldb.command("tk-target")
601def tk_target_display(debugger, command, result, dict):
602    # needed for tree creation in TK library as it uses sys.argv...
603    sys.argv = ['tk-target']
604    target = debugger.GetSelectedTarget()
605    if not target:
606        print("invalid target", file=result)
607        return
608    # Parse command line args
609    columnd_dicts = [{'id': '#0', 'text': 'Name', 'anchor': W, 'stretch': 0},
610                     {'id': 'value', 'text': 'Value', 'anchor': W, 'stretch': 0},
611                     {'id': 'summary', 'text': 'Summary', 'anchor': W, 'stretch': 1}]
612    command_args = shlex.split(command)
613    tree = DelegateTree(
614        columnd_dicts,
615        TargetTreeItemDelegate(target),
616        'Target',
617        'lldb-tk-target')
618    tree.mainloop()
619