import pango
import pangocairo
import math
+import random
+import sys, traceback
def merge_dict(dst, src):
for i in src:
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)
# 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
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?
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):
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()
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)