From 72952ec3f7237fb0b34efb0e636aabecddfface3 Mon Sep 17 00:00:00 2001 From: Jorge Gorbe Date: Mon, 5 Nov 2012 15:11:56 +0100 Subject: [PATCH] Viewer improvements: - Draw "containment" arrows, useful when a pointer points to a member struct and therefore that member struct gets its own node. - Draw names in arrows - Omit template parameters in non-selected nodes --- samples/view.py | 118 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 31 deletions(-) diff --git a/samples/view.py b/samples/view.py index e5edcb6..31b9999 100644 --- a/samples/view.py +++ b/samples/view.py @@ -4,6 +4,8 @@ import gtk import pango import pangocairo import math +import random +import sys, traceback def merge_dict(dst, src): for i in src: @@ -124,7 +126,8 @@ class GraphViewerNode(): self.height = 30 self.name = name self.gdb_value = gdb_value - self.connections = [] # outgoing pointers to other nodes, to draw arrows + self.connections = {} # outgoing pointers to other nodes, to draw arrows + self.contained_nodes = [] # member structs pointed to directly from the outside get their own nodes, we need to draw this relationship self.fill_color = (0.4, 0.4, 0.5) @@ -163,9 +166,14 @@ class GraphViewer(gtk.DrawingArea): # draw node name name_layout = context.create_layout() name_layout.set_font_description(pango.FontDescription("sans 8")) - name_layout.set_text(node.name) + display_name = str(node.gdb_value.address) #node.name + if node is not self.selected_node: + template_args_start = display_name.find("<") + if template_args_start != -1: + display_name = display_name[:template_args_start] + name_layout.set_text(display_name) _, name_height = name_layout.get_pixel_size() - context.move_to(node.x, node.y - name_height) + context.move_to(node.x, node.y - name_height - 2) # a couple of extra pixels up to avoid "_" collide with the node box context.show_layout(name_layout) # draw node rect @@ -181,19 +189,51 @@ class GraphViewer(gtk.DrawingArea): context.restore() - def draw_connection(self, context, n1, n2): - (p1, p2) = node_connection_points(n1, n2) - context.set_source_rgb(0,0,0) - context.set_line_width(1) + def draw_arrow(self, context, p1, p2): context.move_to(*p1) context.line_to(*p2) context.stroke() + + def draw_arrow_head(self, context, p1, p2): (a1, a2) = arrow_points(p1, p2) context.move_to(*a1) context.line_to(*p2) context.line_to(*a2) context.stroke() + def draw_connection(self, context, n1, n2, name): + (p1, p2) = node_connection_points(n1, n2) + context.save() + context.set_source_rgb(0,0,0) + context.set_line_width(1) + self.draw_arrow(context, p1, p2) + self.draw_arrow_head(context, p1, p2) + + # draw connection name + NAME_OFFSET = 0.05 # 0 = right at the origin, 1 = right at the end + vx = p2[0]-p1[0] + vy = p2[1]-p1[1] + name_layout = context.create_layout() + name_layout.set_font_description(pango.FontDescription("sans 8")) + name_layout.set_text(name) + _, name_height = name_layout.get_pixel_size() + context.move_to((1-NAME_OFFSET)*p1[0] + NAME_OFFSET*p2[0], (1-NAME_OFFSET)*p1[1] + NAME_OFFSET*p2[1]) + context.rotate(-orientation(vx, vy)) + context.show_layout(name_layout) + context.restore() + + def draw_containment(self, context, outer, inner): + (p1, p2) = node_connection_points(outer, inner) + context.save() + context.set_source_rgb(0.9,0.2,0) + context.set_line_width(2) + context.set_dash([5,3]) + self.draw_arrow(context, p1, p2) + context.set_dash([]) + self.draw_arrow_head(context, p1, p2) + context.restore() + + def draw(self, context): rect = self.get_allocation() # widget dimensions? @@ -201,8 +241,11 @@ class GraphViewer(gtk.DrawingArea): self.draw_node(context, node) for node in self.nodes: - for other in node.connections: - self.draw_connection(context, node, other) + for connection_name in node.connections: + self.draw_connection(context, node, node.connections[connection_name], connection_name) + + for other in node.contained_nodes: + self.draw_containment(context, node, other) class GdbValueViewer(gtk.TreeView): def __init__(self): @@ -271,61 +314,75 @@ class ViewerWindow(gtk.Window): self.set_default_size(1000,600) self.paned.set_position(750) -def visit_values(value, x, y, recursion_level): +def visit_values(node_dict, value, x, y, recursion_level): """ Recursively traverse children of a gdb Value, storing pairs (address, GraphViewerNode). returns (width, height, node_dict) """ - NODE_SPACING = 100 + NODE_SPACING = 150 + NODE_OFFSET = 50 + random.randint(-40,40) + + if str(value.address) in node_dict: + return 0, 0 + if recursion_level < 0: - return NODE_SPACING, NODE_SPACING, {str(value.address): GraphViewerNode(x, y, "...", value)} + node_dict[str(value.address)] = GraphViewerNode(x, y, "...", value) + return NODE_SPACING, NODE_SPACING - result = {} # check if node has already been visited, don't create repeated nodes - if str(value.address) not in result: - result[str(value.address)] = GraphViewerNode(x, y, str(value.type), value) + node_dict[str(value.address)] = GraphViewerNode(x, y, str(value.type), value) width = NODE_SPACING height = 0 children_x = x+NODE_SPACING - children_y = y + children_y = y+NODE_OFFSET children_height = 0 children = find_outgoing_pointers(value) for (child_name, child_value) in children.iteritems(): # compute child "tree" dimensions (layout is always tree-like even if there are edges that make this a generic graph) if child_value != 0: - child_width, child_height, child_nodes = visit_values(child_value.dereference(), children_x, children_y, recursion_level - 1) + child_width, child_height = visit_values(node_dict, child_value.dereference(), children_x, children_y, recursion_level - 1) children_y += child_height children_height += child_height width = max(width, NODE_SPACING + child_width) - merge_dict(result, child_nodes) else: pass # TODO: draw null pointers somehow height = max(NODE_SPACING, children_height) - return width, height, result + return width, height def create_viewer_nodes(value, recursion_level): # create the nodes print "creating nodes" - width, height, nodes = visit_values(value, 50, 50, recursion_level) + nodes = {} + width, height = visit_values(nodes, value, 50, 50, recursion_level) - print "creating connections" - # create connections between nodes when all the nodes are there + # establish connections between nodes when all the nodes are there for addr in nodes: value = nodes[addr].gdb_value if type_is_pointer(value.type) and value != 0: print "pointer node" - nodes[addr].connections.append(nodes[str(value)]) + nodes[addr].connections["*"] = nodes[str(value)] else: - print "struct node" + print "struct node@%s: %s"%(addr,value) for f in value.type.fields(): - print "connection", value, f.name child = value[f.name] - if type_is_pointer(child.type) and child != 0: - if str(child) in nodes: - nodes[addr].connections.append(nodes[str(child)]) + if type_is_pointer(child.type): + if child != 0: + print "connection %s --> %s"%(addr, child), value, f.name + if str(child) in nodes: + nodes[addr].connections[f.name] = nodes[str(child)] + + # establish containment relationships between nodes + for addr1 in nodes: + addr1_int = int(addr1, 0) + node1 = nodes[addr1] + for addr2 in nodes: + addr2_int = int(addr2, 0) + node2 = nodes[addr2] + if addr2_int > addr1_int and addr2_int < addr1_int + node1.gdb_value.type.sizeof: + node1.contained_nodes.append(node2) return nodes.values() @@ -362,9 +419,8 @@ class ViewerCommand(gdb.Command): gtk.main() except: if is_gtk_main_running: window.destroy() - raise - - #print print_node(value.dereference(), recursion_level) + exc_type, exc_value, exc_traceback = sys.exc_info() + traceback.print_exception(exc_type, exc_value, exc_traceback) -- 2.34.1