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