From e889f003d1e235957de0ccedf4423e9bf6ac1c4d Mon Sep 17 00:00:00 2001 From: Jorge Gorbe Date: Wed, 31 Oct 2012 12:44:27 +0100 Subject: [PATCH] New test. Viewer improvements. --- samples/script.py | 91 --------------- samples/test3.cc | 19 ++++ samples/view.py | 274 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 293 insertions(+), 91 deletions(-) delete mode 100644 samples/script.py create mode 100644 samples/test3.cc create mode 100644 samples/view.py diff --git a/samples/script.py b/samples/script.py deleted file mode 100644 index b93f299..0000000 --- a/samples/script.py +++ /dev/null @@ -1,91 +0,0 @@ -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() - - diff --git a/samples/test3.cc b/samples/test3.cc new file mode 100644 index 0000000..17196d5 --- /dev/null +++ b/samples/test3.cc @@ -0,0 +1,19 @@ +struct treenode { + int value; + treenode *left; + treenode *right; + treenode(): value(-1), left(0), right(0) {} +}; + + +int main() { + treenode *root = new treenode; + root->left = new treenode; + root->right = new treenode; + root->left->left = new treenode; + root->left->right = new treenode; + root->left->left->left = new treenode; + root->left->left->right = new treenode; + + return 0; +} diff --git a/samples/view.py b/samples/view.py new file mode 100644 index 0000000..f4ba35b --- /dev/null +++ b/samples/view.py @@ -0,0 +1,274 @@ +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() + + -- 2.34.1