changeset 2579:4984c8ebd6c7

Graphviz visualization support
author Peter Hofer <peter.hofer@jku.at>
date Wed, 04 May 2011 18:57:26 +0200
parents 999407dbfe10
children 70d8d239eb89 4a36a0bd6d18
files graal/GraalGraph/src/com/oracle/graal/graph/vis/GraphvizPrinter.java graal/GraalGraph/src/com/oracle/graal/graph/vis/GraphvizRunner.java graal/GraalGraph/test/com/oracle/graal/graph/vis/GraphvizTest.java
diffstat 3 files changed, 376 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/GraalGraph/src/com/oracle/graal/graph/vis/GraphvizPrinter.java	Wed May 04 18:57:26 2011 +0200
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2011, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.graph.vis;
+
+import java.awt.Color;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+import com.oracle.graal.graph.Graph;
+import com.oracle.graal.graph.Node;
+import com.oracle.graal.graph.Node.NodeArray;
+
+/**
+ * Generates a representation of {@link Node Nodes} or entire {@link Graph Graphs} in the DOT language that can be
+ * visualized with <a href="http://www.graphviz.org/">Graphviz</a>.
+ */
+public class GraphvizPrinter {
+
+    public static final Color NODE_BGCOLOR = Color.WHITE;
+    private static final String NODE_BGCOLOR_STRING = formatColorString(NODE_BGCOLOR);
+
+    private static String formatColorString(Color c) {
+        return String.format("#%02x%02x%02x%02x", c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
+    }
+
+    private final PrintStream out;
+
+    /**
+     * Creates a new {@link GraphvizPrinter} that writes to the specified output stream.
+     */
+    public GraphvizPrinter(OutputStream out) {
+        this.out = new PrintStream(out);
+    }
+
+    /**
+     * Opens a graph with the specified title (label). Call this before printing any nodes, but not more than once
+     * without calling {@link #end()} first.
+     * 
+     * @param title
+     *            The graph's label.
+     */
+    public void begin(String title) {
+        out.println("digraph g {");
+        if (title != null) {
+            out.println("  label=\"" + title + "\";");
+        }
+    }
+
+    /**
+     * Closes the graph. No nodes should be printed afterwards. Another graph can be opened with {@link #begin(String)},
+     * but many Graphviz output plugins ignore additional graphs in their input.
+     */
+    public void end() {
+        out.println("}");
+    }
+
+    /**
+     * Prints all nodes and edges in the specified graph.
+     */
+    public void print(Graph graph) {
+        // graph.getNodes() returns all the graph's nodes, not just "roots"
+        for (Node n : graph.getNodes()) {
+            printNode(n);
+        }
+    }
+
+    /**
+     * Prints a single node and edges for all its inputs and successors.
+     */
+    public void printNode(Node node) {
+        int id = node.id();
+        String name = "n" + id;
+        NodeArray inputs = node.inputs();
+        NodeArray successors = node.successors();
+
+        printNode(name, node.toString(), inputs.size(), successors.size());
+
+        for (int i = 0; i < successors.size(); ++i) {
+            Node successor = successors.get(i);
+            if (successor != Node.Null) {
+                printControlEdge(id, i, successor.id());
+            }
+        }
+
+        for (int i = 0; i < inputs.size(); ++i) {
+            Node input = inputs.get(i);
+            if (input != Node.Null) {
+                printDataEdge(id, i, input.id());
+            }
+        }
+    }
+
+    private void printNode(String name, String label, int ninputs, int nsuccessors) {
+        out.println(name + "  [shape=plaintext,");
+        out.println("   label=< <TABLE BORDER=\"0\" CELLSPACING=\"0\"><TR><TD CELLPADDING=\"0\">");
+        out.println("    <TABLE BORDER=\"0\" CELLSPACING=\"2\" CELLPADDING=\"0\"><TR>");
+        out.println("    <TD WIDTH=\"15\" HEIGHT=\"5\" PORT=\"predecessors\" BGCOLOR=\"rosybrown1\"></TD></TR></TABLE>");
+        out.println("    </TD><TD COLSPAN=\"2\" CELLPADDING=\"0\" ALIGN=\"RIGHT\"><TABLE BORDER=\"0\" CELLSPACING=\"2\" CELLPADDING=\"0\"><TR>");
+
+        if (ninputs == 1 && nsuccessors == 1) {
+            out.println("    <TD WIDTH=\"15\" HEIGHT=\"5\"></TD>");
+        }
+
+        for (int i = 0; i < nsuccessors - ninputs; i++) {
+            out.println("    <TD WIDTH=\"15\" HEIGHT=\"5\"></TD>");
+        }
+
+        for (int i = 0; i < ninputs; i++) {
+            out.println("    <TD WIDTH=\"15\" HEIGHT=\"5\" PORT=\"in" + i + "\" BGCOLOR=\"lightgrey\"></TD>");
+        }
+
+        out.println("    </TR></TABLE></TD></TR><TR><TD BORDER=\"1\" COLSPAN=\"3\" BGCOLOR=\"" + NODE_BGCOLOR_STRING + "\">" + label + "</TD></TR>");
+        out.println("    <TR><TD COLSPAN=\"2\" CELLPADDING=\"0\" ALIGN=\"RIGHT\"><TABLE BORDER=\"0\" CELLSPACING=\"2\" CELLPADDING=\"0\"><TR>");
+
+        for (int i = 0; i < nsuccessors; i++) {
+            out.println("    <TD WIDTH=\"15\" HEIGHT=\"5\" PORT=\"succ" + i + "\" BGCOLOR=\"rosybrown1\"></TD>");
+        }
+
+        for (int i = 0; i < ninputs - nsuccessors; i++) {
+            out.println("    <TD WIDTH=\"15\" HEIGHT=\"5\"></TD>");
+        }
+
+        if (ninputs == 1 && nsuccessors == 1) {
+            out.println("    <TD WIDTH=\"15\" HEIGHT=\"5\"></TD>");
+        }
+
+        out.println("    </TR></TABLE></TD><TD CELLPADDING=\"0\"><TABLE BORDER=\"0\" CELLSPACING=\"2\" CELLPADDING=\"0\"><TR>");
+        out.println("    <TD WIDTH=\"15\" HEIGHT=\"5\" PORT=\"usages\" BGCOLOR=\"lightgrey\"></TD></TR></TABLE></TD></TR></TABLE>>]; ");
+    }
+
+    private void printControlEdge(int from, int fromPort, int to) {
+        out.println("n" + from + ":succ" + fromPort + " -> n" + to + ":predecessors:n [color=red];");
+    }
+
+    private void printDataEdge(int from, int fromPort, int to) {
+        out.println("n" + to + ":usages -> n" + from + ":in" + fromPort + ":n [color=black,dir=back];");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/GraalGraph/src/com/oracle/graal/graph/vis/GraphvizRunner.java	Wed May 04 18:57:26 2011 +0200
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2011, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.graph.vis;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Provides functionality to process graphs in the DOT language with a Graphviz tool and obtain the generated output.
+ */
+public class GraphvizRunner {
+
+    public static final String DOT_COMMAND = "dot";
+
+    /**
+     * Processes data from an input stream with a Graphviz tool such as {@code dot}, writing output in the specified
+     * format to the given output stream. The method waits for the executed tool to finish and then returns its exit
+     * code.
+     * 
+     * @param command
+     *            The Graphviz tool to call.
+     * @param in
+     *            Stream to read input from.
+     * @param out
+     *            Stream to write output to.
+     * @param format
+     *            Desired output format (-T parameter).
+     * @return Exit code of the called utility.
+     * @throws IOException
+     *             When the process can not be started (e.g. Graphviz missing) or reading/writing a stream fails.
+     */
+    public static int process(String command, InputStream in, OutputStream out, String format) throws IOException {
+        byte[] buffer = new byte[4096];
+
+        // create and start process
+        ProcessBuilder pb = new ProcessBuilder(command, "-T", format);
+        Process p = pb.start();
+
+        // write data from in to stdin
+        OutputStream stdin = p.getOutputStream();
+        transfer(buffer, in, stdin);
+        stdin.close();
+        in.close();
+
+        // read output from stdout and write to out
+        InputStream stdout = p.getInputStream();
+        transfer(buffer, stdout, out);
+        stdout.close();
+
+        // wait for process to terminate
+        for (;;) {
+            try {
+                return p.waitFor();
+            } catch (InterruptedException e) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * Reads all data from an {@link InputStream} and writes it to an {@link OutputStream}, using the provided buffer.
+     */
+    private static void transfer(byte[] buffer, InputStream in, OutputStream out) throws IOException {
+        int count;
+        while ((count = in.read(buffer, 0, buffer.length)) != -1) {
+            out.write(buffer, 0, count);
+        }
+        in.close();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/GraalGraph/test/com/oracle/graal/graph/vis/GraphvizTest.java	Wed May 04 18:57:26 2011 +0200
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2011, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.graph.vis;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.junit.Test;
+
+import com.oracle.graal.graph.Graph;
+import com.oracle.graal.graph.Node;
+
+/**
+ * Tests for the Graphviz graph generator. Needs Graphviz (more specifically, dot) installed to verify produced output.
+ */
+public class GraphvizTest {
+
+    @Test
+    public void testSimpleGraph() throws IOException {
+        Graph g = new Graph();
+
+        DummyNode start = new DummyNode("start", 0, 1, g);
+
+        DummyNode ifnode = new DummyNode("if", 2, 2, g);
+        start.setSuccessor(0, ifnode);
+
+        // branch 1
+        DummyNode nop = new DummyNode("nop", 0, 1, g);
+        ifnode.setSuccessor(0, nop);
+
+        // branch 2
+        DummyNode a = new DummyNode("a", 0, 1, g);
+        DummyNode b = new DummyNode("b", 0, 1, g);
+        DummyNode plus = new DummyNode("+", 2, 1, g);
+        plus.setInput(0, a);
+        plus.setInput(1, b);
+        ifnode.setSuccessor(1, plus);
+
+        DummyNode end = new DummyNode("end", 0, 1, g);
+        plus.setSuccessor(0, end);
+        nop.setSuccessor(0, end);
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        GraphvizPrinter printer = new GraphvizPrinter(out);
+        printer.begin("Simple test");
+        printer.print(g);
+        printer.end();
+
+        int exitCode = GraphvizRunner.process(GraphvizRunner.DOT_COMMAND, new ByteArrayInputStream(out.toByteArray()), new NullOutputStream(), "xdot");
+        assertEquals(0, exitCode);
+    }
+
+    private static class DummyNode extends Node {
+
+        private final int inputCount;
+        private final int successorCount;
+        private final String name;
+
+        public DummyNode(String name, int inputCount, int successorCount, Graph graph) {
+            super(inputCount, successorCount, graph);
+            this.name = name;
+            this.inputCount = inputCount;
+            this.successorCount = successorCount;
+        }
+
+        @Override
+        public Node copy(Graph into) {
+            return new DummyNode(name, inputCount, successorCount, into);
+        }
+
+        public void setInput(int idx, Node n) {
+            inputs().set(idx, n);
+        }
+
+        public void setSuccessor(int idx, Node n) {
+            successors().set(idx, n);
+        }
+
+        @Override
+        protected int inputCount() {
+            return super.inputCount() + inputCount;
+        }
+
+        @Override
+        protected int successorCount() {
+            return super.inputCount() + successorCount;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+    }
+
+    private static class NullOutputStream extends OutputStream {
+
+        @Override
+        public void write(int b) throws IOException {
+        }
+    }
+
+}