inheritance support in property view
authorJorge Gorbe <j.gorbe@stcsl.es>
Fri, 2 Nov 2012 13:56:41 +0000 (14:56 +0100)
committerJorge Gorbe <j.gorbe@stcsl.es>
Fri, 2 Nov 2012 13:56:41 +0000 (14:56 +0100)
samples/view.py

index 793b09366f17652aa9ccc140b95e3e8a7e47a739..e5edcb6ad157e9e9ecf5192d0f11e98afcdc43a3 100644 (file)
@@ -5,10 +5,42 @@ import pango
 import pangocairo
 import math
 
+def merge_dict(dst, src):
+    for i in src:
+        dst[i] = src[i]
+
+
 def type_has_fields(t):
     basic_type = gdb.types.get_basic_type(t)
     return basic_type.code == gdb.TYPE_CODE_STRUCT or basic_type.code == gdb.TYPE_CODE_UNION
 
+def type_is_pointer(t):
+    basic_type = gdb.types.get_basic_type(t)
+    return basic_type.code == gdb.TYPE_CODE_PTR
+
+def find_outgoing_pointers(value):
+    """Finds all outgoing pointers from a value, traversing each non-pointer member of a composite type
+    (e.g. struct members inside structs) and also each member in every base class, recursively.
+    Returns a dict of {field_name:address} where field_name is a string, and address is a gdb.Value."""
+    if type_is_pointer(value.dynamic_type):
+        return {"*": value}
+    elif type_has_fields(value.dynamic_type):
+        result = {}
+        for f in value.type.fields():
+            if f.is_base_class:
+                base_pointers = find_outgoing_pointers(value.cast(f.type))
+                merge_dict(result, base_pointers)
+            elif type_has_fields(f.type):
+                member_pointers = find_outgoing_pointers(value[f.name])
+                merge_dict(result, member_pointers)
+            elif type_is_pointer(f.type):
+                result[f.name] = value[f.name]
+
+        return result
+    else:
+        return {}
+
+
 def segment_intersection(A, B, C, D):
     """Computes the intersection between the rect segment AB and the rect segment CD.
     All parameters are 2D points expressed as (x,y) tuples. Result is also a (x,y) tuple, or
@@ -85,6 +117,7 @@ def arrow_points(A, B):
 
 class GraphViewerNode():
     def __init__(self, x, y, name, gdb_value):
+        print "node created: %d, %d, %s, %s"% (x, y, name, gdb_value.address)
         self.x = x
         self.y = y
         self.width = 30
@@ -197,12 +230,16 @@ class GdbValueViewer(gtk.TreeView):
     def visit_value(self, value, store_iter):
         if type_has_fields(value.type):
             for f in value.type.fields():
-                field_value = value[f.name]
-                if type_has_fields(field_value.type) > 0:
-                    child_iter = self.store.append(store_iter, (f.name, str(f.type)))
-                    self.visit_value(field_value, child_iter)
+                if f.is_base_class:
+                    child_iter = self.store.append(store_iter, ("Base class", str(f.type)))
+                    self.visit_value(value.cast(f.type), child_iter)
                 else:
-                    self.store.append(store_iter, (f.name, str(field_value)))
+                    field_value = value[f.name]
+                    if type_has_fields(field_value.type) > 0:
+                        child_iter = self.store.append(store_iter, (f.name, "type [%s]"%str(f.type)))
+                        self.visit_value(field_value, child_iter)
+                    else:
+                        self.store.append(store_iter, (f.name, str(field_value)))
         else:
             self.store.append(None, ("<value>", str(value)))
 
@@ -234,64 +271,59 @@ class ViewerWindow(gtk.Window):
         self.set_default_size(1000,600)
         self.paned.set_position(750)
 
-def merge_dict(dst, src):
-    for i in src:
-        dst[i] = src[i]
-
 def visit_values(value, x, y, recursion_level):
     """
     Recursively traverse children of a gdb Value, storing pairs (address, GraphViewerNode).
     returns (width, height, node_dict)
     """
-    # TODO: CHECK IF NODE HAS ALREADY BEEN VISITED, DON'T CREATE REPEATED NODES :P
     NODE_SPACING = 100
     if recursion_level < 0:
         return NODE_SPACING, NODE_SPACING, {str(value.address): GraphViewerNode(x, y, "...", value)}
 
     result = {}
-    result[str(value.address)] = GraphViewerNode(x, y, str(value.type), value)
+    # 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)
     width = NODE_SPACING
     height = 0
     children_x = x+NODE_SPACING
     children_y = y
     children_height = 0
-    if value.type.code == gdb.TYPE_CODE_PTR and value != 0:
-        pointee = value.dereference()
-        pointee_width, pointee_height, pointee_nodes = visit_values(pointee, children_x, children_y, recursion_level - 1)
-        merge_dict(result, pointee_nodes)
-        return NODE_SPACING+pointee_width, max(NODE_SPACING, pointee_height), result
-    elif type_has_fields(value.type):
-        for f in value.type.fields():
-            child = value[f.name]
-            if child.type.code == gdb.TYPE_CODE_PTR:
-                if child != 0:
-                    child_width, child_height, child_nodes = visit_values(child.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: handle nulls
-        height = max(NODE_SPACING, children_height)
-        return width, height, result
-    else:
-        return NODE_SPACING, NODE_SPACING, result
 
+    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)
+            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
 
 
 def create_viewer_nodes(value, recursion_level):
     # create the nodes
+    print "creating nodes"
     width, height, nodes = visit_values(value, 50, 50, recursion_level)
 
+    print "creating connections"
     # create connections between nodes when all the nodes are there
     for addr in nodes:
         value = nodes[addr].gdb_value
-        if value.type.code == gdb.TYPE_CODE_PTR and value != 0:
+        if type_is_pointer(value.type) and value != 0:
+            print "pointer node"
             nodes[addr].connections.append(nodes[str(value)])
         else:
+            print "struct node"
             for f in value.type.fields():
+                print "connection", value, f.name
                 child = value[f.name]
-                if child.type.code == gdb.TYPE_CODE_PTR and child != 0:
+                if type_is_pointer(child.type) and child != 0:
                     if str(child) in nodes:
                         nodes[addr].connections.append(nodes[str(child)])
 
@@ -316,6 +348,8 @@ class ViewerCommand(gdb.Command):
         else:
             recursion_level = 10
 
+        is_gtk_main_running = False
+
         try:
             window = ViewerWindow()
             window.connect("destroy", gtk.main_quit)
@@ -324,9 +358,10 @@ class ViewerCommand(gdb.Command):
             nodes = create_viewer_nodes(value, recursion_level)
             window.viewer.nodes = nodes
             window.viewer.queue_draw()
+            is_gtk_main_running = True
             gtk.main()
         except:
-            window.destroy()
+            if is_gtk_main_running: window.destroy()
             raise
 
         #print print_node(value.dereference(), recursion_level)