+++ /dev/null
-import gdb
-import gtk
-import pangocairo
-
-def print_node(node, recursion_level):
- if len(node.type.fields()) == 0:
- print "node = %s", str(node)
- result = str(node)
- else:
- result = {}
- for f in node.type.fields():
- child = node[f.name]
- if child.type.code == gdb.TYPE_CODE_PTR and recursion_level > 0:
- if (child == 0):
- result_child = "NULL"
- else:
- result_child = print_node(child.dereference(), recursion_level - 1)
- else:
- result_child = str(child)
- result[f.name] = result_child
- return result
-
-
-
-class GraphViewer(gtk.DrawingArea):
- def __init__(self):
- super(gtk.DrawingArea, self).__init__()
- self.connect("expose_event", self.expose)
-
- def expose(self, widget, event):
- context = widget.window.cairo_create()
- context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
- context.clip()
- self.draw(context)
- return False
-
- def draw(self, context):
- rect = self.get_allocation() # widget dimensions?
-
- pangocairo_context = pangocairo.CairoContext(context)
- layout = pangocairo_context.create_layout()
- layout.set_text("Trololol")
- context.move_to(100,100)
- pangocairo_context.show_layout(layout)
-
-
- #context.set_source_rgb(1.0, 0.0, 0.0)
- context.move_to(0,0)
- context.line_to(200,100)
- context.set_line_width(4)
- context.stroke()
-
- context.rectangle(200,200,50,50)
- context.set_line_width(1)
- context.stroke()
-
-
-
-
-class ViewerCommand(gdb.Command):
- """View data structures"""
- def __init__(self):
- super(ViewerCommand, self).__init__("view", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL)
-
-
- def invoke(self, arg, from_tty):
- args = gdb.string_to_argv(arg)
- value = gdb.parse_and_eval(args[0])
-
- window = gtk.Window()
- viewer = GraphViewer()
- window.add(viewer)
- window.connect("destroy", gtk.main_quit)
- window.set_default_size(600,600)
- window.show_all()
- gtk.main()
-
- #if len(args) > 1:
- # recursion_level = int(args[1])
- #else:
- # recursion_level = 3
- #print print_node(value.dereference(), recursion_level)
-
-
-
-
-
-
-ViewerCommand()
-
-
--- /dev/null
+import gdb
+import gtk
+import pango
+import pangocairo
+import math
+
+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
+ None if the segments don't intersect."""
+ # see http://stackoverflow.com/a/565282 for the explanation
+ def cross2D(A, B):
+ return A[0]*B[1]-A[1]*B[0]
+
+ def diff2D(A, B):
+ return (A[0]-B[0], A[1]-B[1])
+
+ p = A
+ r = diff2D(B, A)
+ q = C
+ s = diff2D(D, C)
+
+ r_cross_s = cross2D(r, s)
+ if abs(r_cross_s) < 1e-5: return None
+
+ q_minus_p = diff2D(q, p)
+ t = cross2D(q_minus_p, s) / r_cross_s
+ u = cross2D(q_minus_p, r) / r_cross_s
+
+ if t < 0.0 or t > 1.0 or u < 0.0 or u > 1.0: return None
+ else:
+ x = p[0] + t*r[0]
+ y = p[1] + t*r[1]
+ return (x,y)
+
+def node_segment_intersection(node, A, B):
+ """Computes the intersection point between the rect AB and a node"""
+ P1 = (node.x, node.y)
+ P2 = (node.x + node.width, node.y)
+ P3 = (node.x + node.width, node.y + node.height)
+ P4 = (node.x, node.y + node.height)
+ return segment_intersection(A, B, P1, P2) or segment_intersection(A, B, P2, P3) or segment_intersection(A, B, P3, P4) or segment_intersection(A, B, P4, P1)
+
+
+def node_connection_points(node1, node2):
+ def node_center(n): return (n.x + 0.5*n.width, n.y + 0.5*n.height)
+ c1 = node_center(node1)
+ c2 = node_center(node2)
+ return (node_segment_intersection(node1, c1, c2), node_segment_intersection(node2, c1, c2))
+
+
+def orientation(vx, vy):
+ """returns the angle of the vector (vx, vy) respect to (1, 0)"""
+ r = math.hypot(vx, vy)
+ vx = vx/r
+ vy = vy/r
+ angle = math.acos(vx)
+ if vy > 0: angle = 2*math.pi-angle
+ return angle
+
+def arrow_points(A, B):
+ """coordinates of two points (R1, R2) that form an arrow head for the segment AB, such that
+ plotting the lines R1B and R2B result in an arrow head"""
+ ARROW_RADIUS = 10
+ ARROW_ANGLE = math.radians(15)
+ ax, ay = A
+ bx, by = B
+ # compute the angle of AB wrt the origin
+ vx = bx-ax
+ vy = by-ay
+ arrow_origin_angle = orientation(vx, vy) + math.pi
+ # compute R1 and R2
+ alpha1 = arrow_origin_angle + ARROW_ANGLE
+ alpha2 = arrow_origin_angle - ARROW_ANGLE
+ R1 = (bx + ARROW_RADIUS*math.cos(alpha1), by - ARROW_RADIUS*math.sin(alpha1))
+ R2 = (bx + ARROW_RADIUS*math.cos(alpha2), by - ARROW_RADIUS*math.sin(alpha2))
+ return (R1, R2)
+
+
+
+class GraphViewerNode():
+ def __init__(self, x, y, name, gdb_value):
+ self.x = x
+ self.y = y
+ self.width = 30
+ self.height = 30
+ self.name = name
+ self.gdb_value = gdb_value
+ self.connections = [] # outgoing pointers to other nodes, to draw arrows
+ self.fill_color = (0.8, 0.4, 0)
+
+
+
+class GraphViewer(gtk.DrawingArea):
+ def __init__(self):
+ super(gtk.DrawingArea, self).__init__()
+ self.nodes = []
+ self.connect("expose_event", self.expose)
+
+ def expose(self, widget, event):
+ context = pangocairo.CairoContext(widget.window.cairo_create())
+ context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
+ context.clip()
+ self.draw(context)
+ return False
+
+
+ def draw_node(self, context, node):
+ # draw node name
+ name_layout = context.create_layout()
+ name_layout.set_font_description(pango.FontDescription("sans 8"))
+ name_layout.set_text(node.name)
+ _, name_height = name_layout.get_pixel_size()
+ context.move_to(node.x, node.y - name_height)
+ context.show_layout(name_layout)
+
+ # draw node rect
+ context.rectangle(node.x, node.y, node.width, node.height)
+ context.set_source_rgb(*node.fill_color)
+ context.fill()
+ context.set_source_rgb(0,0,0)
+ context.set_line_width(1)
+ context.rectangle(node.x, node.y, node.width, node.height)
+ context.stroke()
+
+
+ def draw_connection(self, context, n1, n2):
+ (p1, p2) = node_connection_points(n1, n2)
+ context.move_to(*p1)
+ context.line_to(*p2)
+ context.stroke()
+ (a1, a2) = arrow_points(p1, p2)
+ context.move_to(*a1)
+ context.line_to(*p2)
+ context.line_to(*a2)
+ context.stroke()
+
+ def draw(self, context):
+ rect = self.get_allocation() # widget dimensions?
+
+ for node in self.nodes:
+ self.draw_node(context, node)
+
+ for node in self.nodes:
+ for other in node.connections:
+ self.draw_connection(context, node, other)
+
+
+class ViewerWindow(gtk.Window):
+ def __init__(self):
+ super(gtk.Window, self).__init__()
+ self.entry = gtk.Entry()
+ self.viewer = GraphViewer()
+ self.properties = gtk.TreeView()
+ self.vbox = gtk.VBox()
+ self.paned = gtk.HPaned()
+ labelhbox = gtk.HBox()
+ labelhbox.pack_start(gtk.Label("Expression:"), expand = False)
+ labelhbox.pack_start(self.entry)
+ self.vbox.pack_start(labelhbox, expand = False)
+ self.vbox.pack_start(self.paned)
+ self.paned.add1(self.viewer)
+ self.paned.add2(self.properties)
+ self.add(self.vbox)
+
+ 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)
+ 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 len(value.type.fields()) > 0:
+ 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
+
+
+
+def create_viewer_nodes(value, recursion_level):
+ # create the nodes
+ width, height, nodes = visit_values(value, 50, 50, recursion_level)
+
+ # 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:
+ nodes[addr].connections.append(nodes[str(value)])
+ else:
+ for f in value.type.fields():
+ child = value[f.name]
+ if child.type.code == gdb.TYPE_CODE_PTR and child != 0:
+ if str(child) in nodes:
+ nodes[addr].connections.append(nodes[str(child)])
+
+ return nodes.values()
+
+
+
+
+
+
+class ViewerCommand(gdb.Command):
+ """View data structures"""
+ def __init__(self):
+ super(ViewerCommand, self).__init__("view", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL)
+
+
+ def invoke(self, arg, from_tty):
+ args = gdb.string_to_argv(arg)
+ value = gdb.parse_and_eval(args[0])
+ if len(args) > 1:
+ recursion_level = int(args[1])
+ else:
+ recursion_level = 10
+
+ try:
+ window = ViewerWindow()
+ window.connect("destroy", gtk.main_quit)
+ window.show_all()
+
+ nodes = create_viewer_nodes(value, recursion_level)
+ window.viewer.nodes = nodes
+ window.viewer.queue_draw()
+ gtk.main()
+ except:
+ print "trololo"
+ window.destroy()
+ raise
+
+ #print print_node(value.dereference(), recursion_level)
+
+
+
+
+
+
+ViewerCommand()
+
+