Greg Clayton | 994ba64 | 2014-09-22 22:06:41 +0000 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | import lldb |
| 4 | import shlex |
| 5 | import sys |
| 6 | from Tkinter import * |
| 7 | import ttk |
| 8 | |
| 9 | def get_item_dictionary_for_sbvalue(v, include_typename): |
| 10 | '''Given an lldb.SBValue, create an item dictionary for that value and return it |
| 11 | |
| 12 | The dictionary must have the following key/value pairs: |
| 13 | 'values' - must be a list of string values for each column defined in self.get_column_definitions() |
| 14 | 'children' - a boolean value that indicates if an item has children or not |
| 15 | ''' |
| 16 | name = v.name |
| 17 | if name is None: |
| 18 | name = '' |
| 19 | if include_typename: |
| 20 | typename = v.type |
| 21 | if typename is None: |
| 22 | typename = '' |
| 23 | value = v.value |
| 24 | if value is None: |
| 25 | value = '' |
| 26 | summary = v.summary |
| 27 | if summary is None: |
| 28 | summary = '' |
| 29 | if include_typename: |
| 30 | return { 'values' : [name, typename, value, summary], |
| 31 | 'children' : v.MightHaveChildren(), |
| 32 | 'type' : 'SBValue', |
| 33 | 'object' : v } |
| 34 | else: |
| 35 | return { 'values' : [name, value, summary], |
| 36 | 'children' : v.MightHaveChildren(), |
| 37 | 'type' : 'SBValue', |
| 38 | 'object' : v } |
| 39 | |
| 40 | |
| 41 | def get_item_dictionary_for_process(process): |
| 42 | id = process.GetProcessID() |
| 43 | num_threads = process.GetNumThreads() |
| 44 | value = str(process.GetProcessID()) |
| 45 | summary = process.target.executable.fullpath |
| 46 | return { 'values' : ['process', value, summary], |
| 47 | 'children' : num_threads > 0, |
| 48 | 'type' : 'SBProcess', |
| 49 | 'object' : process } |
| 50 | |
| 51 | def get_item_dictionary_for_thread(thread): |
| 52 | num_frames = thread.GetNumFrames() |
| 53 | value = '0x%x' % (thread.GetThreadID()) |
| 54 | summary = '%u frames' % (num_frames) |
| 55 | return { 'values' : ['thread #%u' % (thread.GetIndexID()), value, summary], |
| 56 | 'children' : num_frames > 0, |
| 57 | 'type' : 'SBThread', |
| 58 | 'object' : thread } |
| 59 | |
| 60 | def get_item_dictionary_for_frame(frame): |
| 61 | id = frame.GetFrameID() |
| 62 | value = '0x%16.16x' % (frame.GetPC()) |
| 63 | stream = lldb.SBStream() |
| 64 | frame.GetDescription(stream) |
| 65 | summary = stream.GetData().split("`")[1] |
| 66 | return { 'values' : ['frame #%u' % (id), value, summary], |
| 67 | 'children' : frame.GetVariables(True, True, True, True).GetSize() > 0, |
| 68 | 'type' : 'SBFrame', |
| 69 | 'object' : frame } |
| 70 | |
| 71 | class ProcessTreeDelegate(object): |
| 72 | def __init__(self, process): |
| 73 | self.process = process |
| 74 | |
| 75 | def get_column_definitions(self): |
| 76 | '''Return an array of column definition dictionaries''' |
| 77 | return [{ 'id' : '#0' , 'text' : 'Name' , 'anchor' : W , 'stretch' : 0 }, |
| 78 | { 'id' : 'value' , 'text' : 'Value' , 'anchor' : W , 'stretch' : 0 }, |
| 79 | { 'id' : 'summary', 'text' : 'Summary', 'anchor' : W , 'stretch' : 1 }] |
| 80 | |
| 81 | def get_item_dictionary(self, sbvalue): |
| 82 | '''Given an lldb.SBValue, create an item dictionary for that value and return it |
| 83 | |
| 84 | The dictionary must have the following key/value pairs: |
| 85 | 'values' - must be a list of string values for each column defined in self.get_column_definitions() |
| 86 | 'children' - a boolean value that indicates if an item has children or not |
| 87 | ''' |
| 88 | |
| 89 | def get_child_item_dictionaries(self, parent_item_dict): |
| 90 | '''Given an lldb.SBValue, create an item dictionary for that value and return it''' |
| 91 | item_dicts = list() |
| 92 | if parent_item_dict is None: |
| 93 | # Create root items if parent_item_dict is None |
| 94 | item_dicts.append(get_item_dictionary_for_process(self.process)) |
| 95 | else: |
| 96 | # Get children for a specified item given its item dictionary |
| 97 | item_type = parent_item_dict['type'] |
| 98 | if item_type == 'SBProcess': |
| 99 | for thread in parent_item_dict['object']: |
| 100 | item_dicts.append(get_item_dictionary_for_thread(thread)) |
| 101 | elif item_type == 'SBThread': |
| 102 | for frame in parent_item_dict['object']: |
| 103 | item_dicts.append(get_item_dictionary_for_frame(frame)) |
| 104 | elif item_type == 'SBFrame': |
| 105 | frame = parent_item_dict['object'] |
| 106 | variables = frame.GetVariables(True, True, True, True) |
| 107 | n = variables.GetSize() |
| 108 | for i in range(n): |
| 109 | item_dicts.append(get_item_dictionary_for_sbvalue(variables[i], False)) |
| 110 | elif item_type == 'SBValue': |
| 111 | sbvalue = parent_item_dict['object'] |
| 112 | if sbvalue.IsValid(): |
| 113 | for i in range(sbvalue.num_children): |
| 114 | item_dicts.append(get_item_dictionary_for_sbvalue(sbvalue.GetChildAtIndex(i), False)) |
| 115 | return item_dicts |
| 116 | |
| 117 | class VariableTreeDelegate(object): |
| 118 | def __init__(self, frame): |
| 119 | self.frame = frame |
| 120 | |
| 121 | def get_column_definitions(self): |
| 122 | '''Return an array of column definition dictionaries''' |
| 123 | return [{ 'id' : '#0' , 'text' : 'Name' , 'anchor' : W , 'stretch' : 0 }, |
| 124 | { 'id' : 'type' , 'text' : 'Type' , 'anchor' : W , 'stretch' : 0 }, |
| 125 | { 'id' : 'value' , 'text' : 'Value' , 'anchor' : W , 'stretch' : 0 }, |
| 126 | { 'id' : 'summary', 'text' : 'Summary', 'anchor' : W , 'stretch' : 1 }] |
| 127 | |
| 128 | def get_child_item_dictionaries(self, parent_item_dict): |
| 129 | '''Given an lldb.SBValue, create an item dictionary for that value and return it''' |
| 130 | item_dicts = list() |
| 131 | if parent_item_dict is None: |
| 132 | # Create root items if parent_item_dict is None |
| 133 | variables = self.frame.GetVariables(True, True, True, True) |
| 134 | n = variables.GetSize() |
| 135 | for i in range(n): |
| 136 | item_dicts.append(get_item_dictionary_for_sbvalue(variables[i], True)) |
| 137 | else: |
| 138 | # Get children for a specified item given its item dictionary |
| 139 | sbvalue = parent_item_dict['object'] |
| 140 | if sbvalue.IsValid(): |
| 141 | for i in range(sbvalue.num_children): |
| 142 | item_dicts.append(get_item_dictionary_for_sbvalue(sbvalue.GetChildAtIndex(i), True)) |
| 143 | return item_dicts |
| 144 | |
| 145 | class DelegateTree(ttk.Frame): |
| 146 | |
| 147 | def __init__(self, delegate, title, name): |
| 148 | ttk.Frame.__init__(self, name=name) |
| 149 | self.pack(expand=Y, fill=BOTH) |
| 150 | self.master.title(title) |
| 151 | self.delegate = delegate |
| 152 | self.item_id_to_item_dict = dict() |
| 153 | frame = Frame(self) |
| 154 | frame.pack(side=TOP, fill=BOTH, expand=Y) |
| 155 | self._create_treeview(frame) |
| 156 | self._populate_root() |
| 157 | |
| 158 | def _create_treeview(self, parent): |
| 159 | frame = ttk.Frame(parent) |
| 160 | frame.pack(side=TOP, fill=BOTH, expand=Y) |
| 161 | |
| 162 | columns_dicts = self.delegate.get_column_definitions() |
| 163 | column_ids = list() |
| 164 | for i in range(1,len(columns_dicts)): |
| 165 | column_ids.append(columns_dicts[i]['id']) |
| 166 | # create the tree and scrollbars |
| 167 | self.tree = ttk.Treeview(columns=column_ids) |
| 168 | |
| 169 | scroll_bar_v = ttk.Scrollbar(orient=VERTICAL, command= self.tree.yview) |
| 170 | scroll_bar_h = ttk.Scrollbar(orient=HORIZONTAL, command= self.tree.xview) |
| 171 | self.tree['yscroll'] = scroll_bar_v.set |
| 172 | self.tree['xscroll'] = scroll_bar_h.set |
| 173 | |
| 174 | # setup column headings and columns properties |
| 175 | for columns_dict in columns_dicts: |
| 176 | self.tree.heading(columns_dict['id'], text=columns_dict['text'], anchor=columns_dict['anchor']) |
| 177 | self.tree.column(columns_dict['id'], stretch=columns_dict['stretch']) |
| 178 | |
| 179 | # add tree and scrollbars to frame |
| 180 | self.tree.grid(in_=frame, row=0, column=0, sticky=NSEW) |
| 181 | scroll_bar_v.grid(in_=frame, row=0, column=1, sticky=NS) |
| 182 | scroll_bar_h.grid(in_=frame, row=1, column=0, sticky=EW) |
| 183 | |
| 184 | # set frame resizing priorities |
| 185 | frame.rowconfigure(0, weight=1) |
| 186 | frame.columnconfigure(0, weight=1) |
| 187 | |
| 188 | # action to perform when a node is expanded |
| 189 | self.tree.bind('<<TreeviewOpen>>', self._update_tree) |
| 190 | |
| 191 | def insert_items(self, parent_id, item_dicts): |
| 192 | for item_dict in item_dicts: |
| 193 | values = item_dict['values'] |
| 194 | item_id = self.tree.insert (parent_id, # root item has an empty name |
| 195 | END, |
| 196 | text=values[0], |
| 197 | values=values[1:]) |
| 198 | self.item_id_to_item_dict[item_id] = item_dict |
| 199 | if item_dict['children']: |
| 200 | self.tree.insert(item_id, END, text='dummy') |
| 201 | |
| 202 | def _populate_root(self): |
| 203 | # use current directory as root node |
| 204 | self.insert_items('', self.delegate.get_child_item_dictionaries(None)) |
| 205 | |
| 206 | def _update_tree(self, event): |
| 207 | # user expanded a node - build the related directory |
| 208 | item_id = self.tree.focus() # the id of the expanded node |
| 209 | children = self.tree.get_children (item_id) |
| 210 | if len(children): |
| 211 | first_child = children[0] |
| 212 | # if the node only has a 'dummy' child, remove it and |
| 213 | # build new directory; skip if the node is already |
| 214 | # populated |
| 215 | if self.tree.item(first_child, option='text') == 'dummy': |
| 216 | self.tree.delete(first_child) |
| 217 | item_dicts = self.delegate.get_child_item_dictionaries(self.item_id_to_item_dict[item_id]) |
| 218 | self.insert_items(item_id, item_dicts) |
| 219 | |
| 220 | @lldb.command("tk-variables") |
| 221 | def tk_variable_display(debugger, command, result, dict): |
| 222 | sys.argv = ['tk-variables'] # needed for tree creation in TK library as it uses sys.argv... |
| 223 | target = debugger.GetSelectedTarget() |
| 224 | if not target: |
| 225 | print >>result, "invalid target" |
| 226 | return |
| 227 | process = target.GetProcess() |
| 228 | if not process: |
| 229 | print >>result, "invalid process" |
| 230 | return |
| 231 | thread = process.GetSelectedThread() |
| 232 | if not thread: |
| 233 | print >>result, "invalid thread" |
| 234 | return |
| 235 | frame = thread.GetSelectedFrame() |
| 236 | if not frame: |
| 237 | print >>result, "invalid frame" |
| 238 | return |
| 239 | # Parse command line args |
| 240 | command_args = shlex.split(command) |
| 241 | |
| 242 | tree = DelegateTree(VariableTreeDelegate(frame), 'Variables', 'lldb-tk-variables') |
| 243 | tree.mainloop() |
| 244 | |
| 245 | @lldb.command("tk-process") |
| 246 | def tk_process_display(debugger, command, result, dict): |
| 247 | sys.argv = ['tk-process'] # needed for tree creation in TK library as it uses sys.argv... |
| 248 | target = debugger.GetSelectedTarget() |
| 249 | if not target: |
| 250 | print >>result, "invalid target" |
| 251 | return |
| 252 | process = target.GetProcess() |
| 253 | if not process: |
| 254 | print >>result, "invalid process" |
| 255 | return |
| 256 | # Parse command line args |
| 257 | command_args = shlex.split(command) |
| 258 | tree = DelegateTree(ProcessTreeDelegate(process), 'Process', 'lldb-tk-process') |
| 259 | tree.mainloop() |
| 260 | |