| #!/usr/bin/env python3 | |
| import lldb | |
| import shlex | |
| import sys | |
| from tkinter import * | |
| import tkinter.ttk as ttk | |
| class ValueTreeItemDelegate(object): | |
| def __init__(self, value): | |
| self.value = value | |
| def get_item_dictionary(self): | |
| name = self.value.name | |
| if name is None: | |
| name = "" | |
| typename = self.value.type | |
| if typename is None: | |
| typename = "" | |
| value = self.value.value | |
| if value is None: | |
| value = "" | |
| summary = self.value.summary | |
| if summary is None: | |
| summary = "" | |
| has_children = self.value.MightHaveChildren() | |
| return { | |
| "#0": name, | |
| "typename": typename, | |
| "value": value, | |
| "summary": summary, | |
| "children": has_children, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| for i in range(self.value.num_children): | |
| item_delegate = ValueTreeItemDelegate(self.value.GetChildAtIndex(i)) | |
| item_dicts.append(item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class FrameTreeItemDelegate(object): | |
| def __init__(self, frame): | |
| self.frame = frame | |
| def get_item_dictionary(self): | |
| id = self.frame.GetFrameID() | |
| name = "frame #%u" % (id) | |
| value = "0x%16.16x" % (self.frame.GetPC()) | |
| stream = lldb.SBStream() | |
| self.frame.GetDescription(stream) | |
| summary = stream.GetData().split("`")[1] | |
| return { | |
| "#0": name, | |
| "value": value, | |
| "summary": summary, | |
| "children": self.frame.GetVariables(True, True, True, True).GetSize() > 0, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| variables = self.frame.GetVariables(True, True, True, True) | |
| n = variables.GetSize() | |
| for i in range(n): | |
| item_delegate = ValueTreeItemDelegate(variables[i]) | |
| item_dicts.append(item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class ThreadTreeItemDelegate(object): | |
| def __init__(self, thread): | |
| self.thread = thread | |
| def get_item_dictionary(self): | |
| num_frames = self.thread.GetNumFrames() | |
| name = "thread #%u" % (self.thread.GetIndexID()) | |
| value = "0x%x" % (self.thread.GetThreadID()) | |
| summary = "%u frames" % (num_frames) | |
| return { | |
| "#0": name, | |
| "value": value, | |
| "summary": summary, | |
| "children": num_frames > 0, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| for frame in self.thread: | |
| item_delegate = FrameTreeItemDelegate(frame) | |
| item_dicts.append(item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class ProcessTreeItemDelegate(object): | |
| def __init__(self, process): | |
| self.process = process | |
| def get_item_dictionary(self): | |
| id = self.process.GetProcessID() | |
| num_threads = self.process.GetNumThreads() | |
| value = str(self.process.GetProcessID()) | |
| summary = self.process.target.executable.fullpath | |
| return { | |
| "#0": "process", | |
| "value": value, | |
| "summary": summary, | |
| "children": num_threads > 0, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| for thread in self.process: | |
| item_delegate = ThreadTreeItemDelegate(thread) | |
| item_dicts.append(item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class TargetTreeItemDelegate(object): | |
| def __init__(self, target): | |
| self.target = target | |
| def get_item_dictionary(self): | |
| value = str(self.target.triple) | |
| summary = self.target.executable.fullpath | |
| return { | |
| "#0": "target", | |
| "value": value, | |
| "summary": summary, | |
| "children": True, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| image_item_delegate = TargetImagesTreeItemDelegate(self.target) | |
| item_dicts.append(image_item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class TargetImagesTreeItemDelegate(object): | |
| def __init__(self, target): | |
| self.target = target | |
| def get_item_dictionary(self): | |
| value = str(self.target.triple) | |
| summary = self.target.executable.fullpath | |
| num_modules = self.target.GetNumModules() | |
| return { | |
| "#0": "images", | |
| "value": "", | |
| "summary": "%u images" % num_modules, | |
| "children": num_modules > 0, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| for i in range(self.target.GetNumModules()): | |
| module = self.target.GetModuleAtIndex(i) | |
| image_item_delegate = ModuleTreeItemDelegate(self.target, module, i) | |
| item_dicts.append(image_item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class ModuleTreeItemDelegate(object): | |
| def __init__(self, target, module, index): | |
| self.target = target | |
| self.module = module | |
| self.index = index | |
| def get_item_dictionary(self): | |
| name = "module %u" % (self.index) | |
| value = self.module.file.basename | |
| summary = self.module.file.dirname | |
| return { | |
| "#0": name, | |
| "value": value, | |
| "summary": summary, | |
| "children": True, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| sections_item_delegate = ModuleSectionsTreeItemDelegate( | |
| self.target, self.module | |
| ) | |
| item_dicts.append(sections_item_delegate.get_item_dictionary()) | |
| symbols_item_delegate = ModuleSymbolsTreeItemDelegate(self.target, self.module) | |
| item_dicts.append(symbols_item_delegate.get_item_dictionary()) | |
| comp_units_item_delegate = ModuleCompileUnitsTreeItemDelegate( | |
| self.target, self.module | |
| ) | |
| item_dicts.append(comp_units_item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class ModuleSectionsTreeItemDelegate(object): | |
| def __init__(self, target, module): | |
| self.target = target | |
| self.module = module | |
| def get_item_dictionary(self): | |
| name = "sections" | |
| value = "" | |
| summary = "%u sections" % (self.module.GetNumSections()) | |
| return { | |
| "#0": name, | |
| "value": value, | |
| "summary": summary, | |
| "children": True, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| num_sections = self.module.GetNumSections() | |
| for i in range(num_sections): | |
| section = self.module.GetSectionAtIndex(i) | |
| image_item_delegate = SectionTreeItemDelegate(self.target, section) | |
| item_dicts.append(image_item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class SectionTreeItemDelegate(object): | |
| def __init__(self, target, section): | |
| self.target = target | |
| self.section = section | |
| def get_item_dictionary(self): | |
| name = self.section.name | |
| section_load_addr = self.section.GetLoadAddress(self.target) | |
| if section_load_addr != lldb.LLDB_INVALID_ADDRESS: | |
| value = "0x%16.16x" % (section_load_addr) | |
| else: | |
| value = "0x%16.16x *" % (self.section.file_addr) | |
| summary = "" | |
| return { | |
| "#0": name, | |
| "value": value, | |
| "summary": summary, | |
| "children": self.section.GetNumSubSections() > 0, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| num_sections = self.section.GetNumSubSections() | |
| for i in range(num_sections): | |
| section = self.section.GetSubSectionAtIndex(i) | |
| image_item_delegate = SectionTreeItemDelegate(self.target, section) | |
| item_dicts.append(image_item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class ModuleCompileUnitsTreeItemDelegate(object): | |
| def __init__(self, target, module): | |
| self.target = target | |
| self.module = module | |
| def get_item_dictionary(self): | |
| name = "compile units" | |
| value = "" | |
| summary = "%u compile units" % (self.module.GetNumSections()) | |
| return { | |
| "#0": name, | |
| "value": value, | |
| "summary": summary, | |
| "children": self.module.GetNumCompileUnits() > 0, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| num_cus = self.module.GetNumCompileUnits() | |
| for i in range(num_cus): | |
| cu = self.module.GetCompileUnitAtIndex(i) | |
| image_item_delegate = CompileUnitTreeItemDelegate(self.target, cu) | |
| item_dicts.append(image_item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class CompileUnitTreeItemDelegate(object): | |
| def __init__(self, target, cu): | |
| self.target = target | |
| self.cu = cu | |
| def get_item_dictionary(self): | |
| name = self.cu.GetFileSpec().basename | |
| value = "" | |
| num_lines = self.cu.GetNumLineEntries() | |
| summary = "" | |
| return { | |
| "#0": name, | |
| "value": value, | |
| "summary": summary, | |
| "children": num_lines > 0, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| item_delegate = LineTableTreeItemDelegate(self.target, self.cu) | |
| item_dicts.append(item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class LineTableTreeItemDelegate(object): | |
| def __init__(self, target, cu): | |
| self.target = target | |
| self.cu = cu | |
| def get_item_dictionary(self): | |
| name = "line table" | |
| value = "" | |
| num_lines = self.cu.GetNumLineEntries() | |
| summary = "%u line entries" % (num_lines) | |
| return { | |
| "#0": name, | |
| "value": value, | |
| "summary": summary, | |
| "children": num_lines > 0, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| num_lines = self.cu.GetNumLineEntries() | |
| for i in range(num_lines): | |
| line_entry = self.cu.GetLineEntryAtIndex(i) | |
| item_delegate = LineEntryTreeItemDelegate(self.target, line_entry, i) | |
| item_dicts.append(item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class LineEntryTreeItemDelegate(object): | |
| def __init__(self, target, line_entry, index): | |
| self.target = target | |
| self.line_entry = line_entry | |
| self.index = index | |
| def get_item_dictionary(self): | |
| name = str(self.index) | |
| address = self.line_entry.GetStartAddress() | |
| load_addr = address.GetLoadAddress(self.target) | |
| if load_addr != lldb.LLDB_INVALID_ADDRESS: | |
| value = "0x%16.16x" % (load_addr) | |
| else: | |
| value = "0x%16.16x *" % (address.file_addr) | |
| summary = ( | |
| self.line_entry.GetFileSpec().fullpath + ":" + str(self.line_entry.line) | |
| ) | |
| return { | |
| "#0": name, | |
| "value": value, | |
| "summary": summary, | |
| "children": False, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| return item_dicts | |
| class InstructionTreeItemDelegate(object): | |
| def __init__(self, target, instr): | |
| self.target = target | |
| self.instr = instr | |
| def get_item_dictionary(self): | |
| address = self.instr.GetAddress() | |
| load_addr = address.GetLoadAddress(self.target) | |
| if load_addr != lldb.LLDB_INVALID_ADDRESS: | |
| name = "0x%16.16x" % (load_addr) | |
| else: | |
| name = "0x%16.16x *" % (address.file_addr) | |
| value = ( | |
| self.instr.GetMnemonic(self.target) | |
| + " " | |
| + self.instr.GetOperands(self.target) | |
| ) | |
| summary = self.instr.GetComment(self.target) | |
| return { | |
| "#0": name, | |
| "value": value, | |
| "summary": summary, | |
| "children": False, | |
| "tree-item-delegate": self, | |
| } | |
| class ModuleSymbolsTreeItemDelegate(object): | |
| def __init__(self, target, module): | |
| self.target = target | |
| self.module = module | |
| def get_item_dictionary(self): | |
| name = "symbols" | |
| value = "" | |
| summary = "%u symbols" % (self.module.GetNumSymbols()) | |
| return { | |
| "#0": name, | |
| "value": value, | |
| "summary": summary, | |
| "children": True, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| num_symbols = self.module.GetNumSymbols() | |
| for i in range(num_symbols): | |
| symbol = self.module.GetSymbolAtIndex(i) | |
| image_item_delegate = SymbolTreeItemDelegate(self.target, symbol, i) | |
| item_dicts.append(image_item_delegate.get_item_dictionary()) | |
| return item_dicts | |
| class SymbolTreeItemDelegate(object): | |
| def __init__(self, target, symbol, index): | |
| self.target = target | |
| self.symbol = symbol | |
| self.index = index | |
| def get_item_dictionary(self): | |
| address = self.symbol.GetStartAddress() | |
| name = "[%u]" % self.index | |
| symbol_load_addr = address.GetLoadAddress(self.target) | |
| if symbol_load_addr != lldb.LLDB_INVALID_ADDRESS: | |
| value = "0x%16.16x" % (symbol_load_addr) | |
| else: | |
| value = "0x%16.16x *" % (address.file_addr) | |
| summary = self.symbol.name | |
| return { | |
| "#0": name, | |
| "value": value, | |
| "summary": summary, | |
| "children": False, | |
| "tree-item-delegate": self, | |
| } | |
| def get_child_item_dictionaries(self): | |
| item_dicts = list() | |
| return item_dicts | |
| class DelegateTree(ttk.Frame): | |
| def __init__(self, column_dicts, delegate, title, name): | |
| ttk.Frame.__init__(self, name=name) | |
| self.pack(expand=Y, fill=BOTH) | |
| self.master.title(title) | |
| self.delegate = delegate | |
| self.columns_dicts = column_dicts | |
| self.item_id_to_item_dict = dict() | |
| frame = Frame(self) | |
| frame.pack(side=TOP, fill=BOTH, expand=Y) | |
| self._create_treeview(frame) | |
| self._populate_root() | |
| def _create_treeview(self, parent): | |
| frame = ttk.Frame(parent) | |
| frame.pack(side=TOP, fill=BOTH, expand=Y) | |
| column_ids = list() | |
| for i in range(1, len(self.columns_dicts)): | |
| column_ids.append(self.columns_dicts[i]["id"]) | |
| # create the tree and scrollbars | |
| self.tree = ttk.Treeview(columns=column_ids) | |
| scroll_bar_v = ttk.Scrollbar(orient=VERTICAL, command=self.tree.yview) | |
| scroll_bar_h = ttk.Scrollbar(orient=HORIZONTAL, command=self.tree.xview) | |
| self.tree["yscroll"] = scroll_bar_v.set | |
| self.tree["xscroll"] = scroll_bar_h.set | |
| # setup column headings and columns properties | |
| for columns_dict in self.columns_dicts: | |
| self.tree.heading( | |
| columns_dict["id"], | |
| text=columns_dict["text"], | |
| anchor=columns_dict["anchor"], | |
| ) | |
| self.tree.column(columns_dict["id"], stretch=columns_dict["stretch"]) | |
| # add tree and scrollbars to frame | |
| self.tree.grid(in_=frame, row=0, column=0, sticky=NSEW) | |
| scroll_bar_v.grid(in_=frame, row=0, column=1, sticky=NS) | |
| scroll_bar_h.grid(in_=frame, row=1, column=0, sticky=EW) | |
| # set frame resizing priorities | |
| frame.rowconfigure(0, weight=1) | |
| frame.columnconfigure(0, weight=1) | |
| # action to perform when a node is expanded | |
| self.tree.bind("<<TreeviewOpen>>", self._update_tree) | |
| def insert_items(self, parent_id, item_dicts): | |
| for item_dict in item_dicts: | |
| name = None | |
| values = list() | |
| first = True | |
| for columns_dict in self.columns_dicts: | |
| if first: | |
| name = item_dict[columns_dict["id"]] | |
| first = False | |
| else: | |
| values.append(item_dict[columns_dict["id"]]) | |
| item_id = self.tree.insert( | |
| parent_id, END, text=name, values=values # root item has an empty name | |
| ) | |
| self.item_id_to_item_dict[item_id] = item_dict | |
| if item_dict["children"]: | |
| self.tree.insert(item_id, END, text="dummy") | |
| def _populate_root(self): | |
| # use current directory as root node | |
| self.insert_items("", self.delegate.get_child_item_dictionaries()) | |
| def _update_tree(self, event): | |
| # user expanded a node - build the related directory | |
| item_id = self.tree.focus() # the id of the expanded node | |
| children = self.tree.get_children(item_id) | |
| if len(children): | |
| first_child = children[0] | |
| # if the node only has a 'dummy' child, remove it and | |
| # build new directory; skip if the node is already | |
| # populated | |
| if self.tree.item(first_child, option="text") == "dummy": | |
| self.tree.delete(first_child) | |
| item_dict = self.item_id_to_item_dict[item_id] | |
| item_dicts = item_dict[ | |
| "tree-item-delegate" | |
| ].get_child_item_dictionaries() | |
| self.insert_items(item_id, item_dicts) | |
| @lldb.command("tk-variables") | |
| def tk_variable_display(debugger, command, exe_ctx, result, internal_dict): | |
| # needed for tree creation in TK library as it uses sys.argv... | |
| sys.argv = ["tk-variables"] | |
| if not exe_ctx.target: | |
| print("invalid target", file=result) | |
| return | |
| if not exe_ctx.process: | |
| print("invalid process", file=result) | |
| return | |
| if not exe_ctx.thread: | |
| print("invalid thread", file=result) | |
| return | |
| frame = exe_ctx.frame | |
| if not frame: | |
| print("invalid frame", file=result) | |
| return | |
| # Parse command line args | |
| command_args = shlex.split(command) | |
| column_dicts = [ | |
| {"id": "#0", "text": "Name", "anchor": W, "stretch": 0}, | |
| {"id": "typename", "text": "Type", "anchor": W, "stretch": 0}, | |
| {"id": "value", "text": "Value", "anchor": W, "stretch": 0}, | |
| {"id": "summary", "text": "Summary", "anchor": W, "stretch": 1}, | |
| ] | |
| tree = DelegateTree( | |
| column_dicts, FrameTreeItemDelegate(frame), "Variables", "lldb-tk-variables" | |
| ) | |
| tree.mainloop() | |
| @lldb.command("tk-process") | |
| def tk_process_display(debugger, command, exe_ctx, result, internal_dict): | |
| # needed for tree creation in TK library as it uses sys.argv... | |
| sys.argv = ["tk-process"] | |
| if not exe_ctx.target: | |
| print("invalid target", file=result) | |
| return | |
| process = exe_ctx.process | |
| if not process: | |
| print("invalid process", file=result) | |
| return | |
| # Parse command line args | |
| columnd_dicts = [ | |
| {"id": "#0", "text": "Name", "anchor": W, "stretch": 0}, | |
| {"id": "value", "text": "Value", "anchor": W, "stretch": 0}, | |
| {"id": "summary", "text": "Summary", "anchor": W, "stretch": 1}, | |
| ] | |
| command_args = shlex.split(command) | |
| tree = DelegateTree( | |
| columnd_dicts, ProcessTreeItemDelegate(process), "Process", "lldb-tk-process" | |
| ) | |
| tree.mainloop() | |
| @lldb.command("tk-target") | |
| def tk_target_display(debugger, command, exe_ctx, result, internal_dict): | |
| # needed for tree creation in TK library as it uses sys.argv... | |
| sys.argv = ["tk-target"] | |
| target = exe_ctx.target | |
| if not target: | |
| print("invalid target", file=result) | |
| return | |
| # Parse command line args | |
| columnd_dicts = [ | |
| {"id": "#0", "text": "Name", "anchor": W, "stretch": 0}, | |
| {"id": "value", "text": "Value", "anchor": W, "stretch": 0}, | |
| {"id": "summary", "text": "Summary", "anchor": W, "stretch": 1}, | |
| ] | |
| command_args = shlex.split(command) | |
| tree = DelegateTree( | |
| columnd_dicts, TargetTreeItemDelegate(target), "Target", "lldb-tk-target" | |
| ) | |
| tree.mainloop() |