# HG changeset patch # User Peter Hofer # Date 1304528246 -7200 # Node ID 4984c8ebd6c7f0f13bc2683bef0891fb8336cf3a # Parent 999407dbfe10655f8091fab55639f83d9c35729d Graphviz visualization support diff -r 999407dbfe10 -r 4984c8ebd6c7 graal/GraalGraph/src/com/oracle/graal/graph/vis/GraphvizPrinter.java --- /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 Graphviz. + */ +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=< "); + out.println("
"); + out.println(" "); + out.println("
"); + out.println("
"); + + if (ninputs == 1 && nsuccessors == 1) { + out.println(" "); + } + + for (int i = 0; i < nsuccessors - ninputs; i++) { + out.println(" "); + } + + for (int i = 0; i < ninputs; i++) { + out.println(" "); + } + + out.println("
" + label + "
"); + + for (int i = 0; i < nsuccessors; i++) { + out.println(" "); + } + + for (int i = 0; i < ninputs - nsuccessors; i++) { + out.println(" "); + } + + if (ninputs == 1 && nsuccessors == 1) { + out.println(" "); + } + + out.println("
"); + out.println("
>]; "); + } + + 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];"); + } + +} diff -r 999407dbfe10 -r 4984c8ebd6c7 graal/GraalGraph/src/com/oracle/graal/graph/vis/GraphvizRunner.java --- /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(); + } +} diff -r 999407dbfe10 -r 4984c8ebd6c7 graal/GraalGraph/test/com/oracle/graal/graph/vis/GraphvizTest.java --- /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 { + } + } + +}