Viewer improvements:
authorJorge Gorbe <j.gorbe@stcsl.es>
Mon, 5 Nov 2012 14:11:56 +0000 (15:11 +0100)
committerJorge Gorbe <j.gorbe@stcsl.es>
Mon, 5 Nov 2012 14:11:56 +0000 (15:11 +0100)
- 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

index e5edcb6ad157e9e9ecf5192d0f11e98afcdc43a3..31b9999f9ded2a2c18f49e1646853cbfbdcd25ad 100644 (file)
@@ -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)