#!/usr/bin/gvpr -f // Split call sites into call site and return site nodes and add // return edges // // Run with graph ... | return-paths BEGIN { // Find the immediate parent subgraph of this object graph_t find_owner(obj_t o, graph_t g) { graph_t g1; for (g1 = fstsubg(g); g1; g1 = nxtsubg(g1)) if(isIn(g1,o)) return g1; return NULL; } } BEG_G { node_t calls[]; // Crude hash table for tracking who calls what graph_t g,g2; edge_t e,e2; string idx; node_t n, n2; int i; $tvtype = TV_en; } // Each call edge which hasn't already been seen E [op == "call" && tail.split != 1] { int offset=0; // Clear the label of this call label = ""; g = find_owner(tail, $G); // Consider each outgoing call. Split the node accordingly n = tail; for (e = fstout(tail); e; e = nxtout(e)) { if (e.op == "call") { // Split node n2 = node(g, sprintf("%s%s%d", tail.name, "x", offset++)); copyA(tail, n2); n2.line = e.line; n2.label = e.line; n2.col = e.col; n2.split = 1; n2.op = "target"; // Record this call g2 = find_owner(e.head, $G); i = 0; while(calls[sprintf("%s%d", g2.name, ++i)]) { } calls[sprintf("%s%d", g2.name, i)] = n2; // Connect original to split e2 = edge(n, n2, "call"); e2.style = "dotted"; e2.weight = 50; // Replace this outedge if (n != tail) { e2 = edge(n, e.head, "transformed-call"); copyA(e,e2); e2.label = ""; delete($G,e); } // Record where we were n = n2; } } // Consider the outgoing control flow: move down to the bottom of // the call sequence nodes for (e = fstout(tail); e; e = nxtout(e)) { if (e.op == "br") { // Replace this outedge e2 = edge(n,e.head,"transformed"); copyA(e,e2); delete($G,e); } } } // Each return node: add edges back to the caller N [op == "ret"] { for (g = fstsubg($G); g; g = nxtsubg(g)) { if(isIn(g,$)) { i = 0; while(calls[sprintf("%s%d", g.name, ++i)]) { e = edge($, calls[sprintf("%s%d", g.name, i)], "return"); e.style = "dotted"; e.op = "ret"; e.line = e.tail.line; e.weight = 5; } } } } END_G { write($); }