Played around with TK UI a bit this weekend.

If you "command script import" this file, then you will have two new commands:

(lldb) tk-variables
(lldb) tk-process

Not sure how this will work on all other systems, but on MacOSX, you will get a window with a tree view that allows you to inspect your local variables by expanding variables to see the child values.

The "tk-process" allows you to inspect the currently selected process by expanding the process to see the threads, the threads to see the frames, and the frames to see the variables. Very handy if you want to view variables for all frames simultaneously.

llvm-svn: 218279
This commit is contained in:
Greg Clayton 2014-09-22 22:06:41 +00:00
parent 5cf7561d21
commit 994ba64756
1 changed files with 260 additions and 0 deletions

View File

@ -0,0 +1,260 @@
#!/usr/bin/python
import lldb
import shlex
import sys
from Tkinter import *
import ttk
def get_item_dictionary_for_sbvalue(v, include_typename):
'''Given an lldb.SBValue, create an item dictionary for that value and return it
The dictionary must have the following key/value pairs:
'values' - must be a list of string values for each column defined in self.get_column_definitions()
'children' - a boolean value that indicates if an item has children or not
'''
name = v.name
if name is None:
name = ''
if include_typename:
typename = v.type
if typename is None:
typename = ''
value = v.value
if value is None:
value = ''
summary = v.summary
if summary is None:
summary = ''
if include_typename:
return { 'values' : [name, typename, value, summary],
'children' : v.MightHaveChildren(),
'type' : 'SBValue',
'object' : v }
else:
return { 'values' : [name, value, summary],
'children' : v.MightHaveChildren(),
'type' : 'SBValue',
'object' : v }
def get_item_dictionary_for_process(process):
id = process.GetProcessID()
num_threads = process.GetNumThreads()
value = str(process.GetProcessID())
summary = process.target.executable.fullpath
return { 'values' : ['process', value, summary],
'children' : num_threads > 0,
'type' : 'SBProcess',
'object' : process }
def get_item_dictionary_for_thread(thread):
num_frames = thread.GetNumFrames()
value = '0x%x' % (thread.GetThreadID())
summary = '%u frames' % (num_frames)
return { 'values' : ['thread #%u' % (thread.GetIndexID()), value, summary],
'children' : num_frames > 0,
'type' : 'SBThread',
'object' : thread }
def get_item_dictionary_for_frame(frame):
id = frame.GetFrameID()
value = '0x%16.16x' % (frame.GetPC())
stream = lldb.SBStream()
frame.GetDescription(stream)
summary = stream.GetData().split("`")[1]
return { 'values' : ['frame #%u' % (id), value, summary],
'children' : frame.GetVariables(True, True, True, True).GetSize() > 0,
'type' : 'SBFrame',
'object' : frame }
class ProcessTreeDelegate(object):
def __init__(self, process):
self.process = process
def get_column_definitions(self):
'''Return an array of column definition dictionaries'''
return [{ 'id' : '#0' , 'text' : 'Name' , 'anchor' : W , 'stretch' : 0 },
{ 'id' : 'value' , 'text' : 'Value' , 'anchor' : W , 'stretch' : 0 },
{ 'id' : 'summary', 'text' : 'Summary', 'anchor' : W , 'stretch' : 1 }]
def get_item_dictionary(self, sbvalue):
'''Given an lldb.SBValue, create an item dictionary for that value and return it
The dictionary must have the following key/value pairs:
'values' - must be a list of string values for each column defined in self.get_column_definitions()
'children' - a boolean value that indicates if an item has children or not
'''
def get_child_item_dictionaries(self, parent_item_dict):
'''Given an lldb.SBValue, create an item dictionary for that value and return it'''
item_dicts = list()
if parent_item_dict is None:
# Create root items if parent_item_dict is None
item_dicts.append(get_item_dictionary_for_process(self.process))
else:
# Get children for a specified item given its item dictionary
item_type = parent_item_dict['type']
if item_type == 'SBProcess':
for thread in parent_item_dict['object']:
item_dicts.append(get_item_dictionary_for_thread(thread))
elif item_type == 'SBThread':
for frame in parent_item_dict['object']:
item_dicts.append(get_item_dictionary_for_frame(frame))
elif item_type == 'SBFrame':
frame = parent_item_dict['object']
variables = frame.GetVariables(True, True, True, True)
n = variables.GetSize()
for i in range(n):
item_dicts.append(get_item_dictionary_for_sbvalue(variables[i], False))
elif item_type == 'SBValue':
sbvalue = parent_item_dict['object']
if sbvalue.IsValid():
for i in range(sbvalue.num_children):
item_dicts.append(get_item_dictionary_for_sbvalue(sbvalue.GetChildAtIndex(i), False))
return item_dicts
class VariableTreeDelegate(object):
def __init__(self, frame):
self.frame = frame
def get_column_definitions(self):
'''Return an array of column definition dictionaries'''
return [{ 'id' : '#0' , 'text' : 'Name' , 'anchor' : W , 'stretch' : 0 },
{ 'id' : 'type' , 'text' : 'Type' , 'anchor' : W , 'stretch' : 0 },
{ 'id' : 'value' , 'text' : 'Value' , 'anchor' : W , 'stretch' : 0 },
{ 'id' : 'summary', 'text' : 'Summary', 'anchor' : W , 'stretch' : 1 }]
def get_child_item_dictionaries(self, parent_item_dict):
'''Given an lldb.SBValue, create an item dictionary for that value and return it'''
item_dicts = list()
if parent_item_dict is None:
# Create root items if parent_item_dict is None
variables = self.frame.GetVariables(True, True, True, True)
n = variables.GetSize()
for i in range(n):
item_dicts.append(get_item_dictionary_for_sbvalue(variables[i], True))
else:
# Get children for a specified item given its item dictionary
sbvalue = parent_item_dict['object']
if sbvalue.IsValid():
for i in range(sbvalue.num_children):
item_dicts.append(get_item_dictionary_for_sbvalue(sbvalue.GetChildAtIndex(i), True))
return item_dicts
class DelegateTree(ttk.Frame):
def __init__(self, delegate, title, name):
ttk.Frame.__init__(self, name=name)
self.pack(expand=Y, fill=BOTH)
self.master.title(title)
self.delegate = delegate
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)
columns_dicts = self.delegate.get_column_definitions()
column_ids = list()
for i in range(1,len(columns_dicts)):
column_ids.append(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 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:
values = item_dict['values']
item_id = self.tree.insert (parent_id, # root item has an empty name
END,
text=values[0],
values=values[1:])
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(None))
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_dicts = self.delegate.get_child_item_dictionaries(self.item_id_to_item_dict[item_id])
self.insert_items(item_id, item_dicts)
@lldb.command("tk-variables")
def tk_variable_display(debugger, command, result, dict):
sys.argv = ['tk-variables'] # needed for tree creation in TK library as it uses sys.argv...
target = debugger.GetSelectedTarget()
if not target:
print >>result, "invalid target"
return
process = target.GetProcess()
if not process:
print >>result, "invalid process"
return
thread = process.GetSelectedThread()
if not thread:
print >>result, "invalid thread"
return
frame = thread.GetSelectedFrame()
if not frame:
print >>result, "invalid frame"
return
# Parse command line args
command_args = shlex.split(command)
tree = DelegateTree(VariableTreeDelegate(frame), 'Variables', 'lldb-tk-variables')
tree.mainloop()
@lldb.command("tk-process")
def tk_process_display(debugger, command, result, dict):
sys.argv = ['tk-process'] # needed for tree creation in TK library as it uses sys.argv...
target = debugger.GetSelectedTarget()
if not target:
print >>result, "invalid target"
return
process = target.GetProcess()
if not process:
print >>result, "invalid process"
return
# Parse command line args
command_args = shlex.split(command)
tree = DelegateTree(ProcessTreeDelegate(process), 'Process', 'lldb-tk-process')
tree.mainloop()