/* * Copyright (c) 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.max.graal.compiler.debug; import java.io.*; import java.net.*; import java.util.regex.*; import com.oracle.max.criutils.*; import com.oracle.max.graal.compiler.*; import com.oracle.max.graal.compiler.observer.*; import com.oracle.max.graal.compiler.schedule.*; import com.oracle.max.graal.graph.*; import com.oracle.max.graal.nodes.*; import com.sun.cri.ri.*; /** * Observes compilation events and uses {@link IdealGraphPrinter} to generate a graph representation that can be * inspected with the <a href="http://kenai.com/projects/igv">Ideal Graph Visualizer</a>. */ public class IdealGraphPrinterObserver implements CompilationObserver { private static final Pattern INVALID_CHAR = Pattern.compile("[^A-Za-z0-9_.-]"); private final String host; private final int port; private static class PrintingContext { public IdealGraphPrinter printer; private OutputStream stream; private Socket socket; } private final ThreadLocal<PrintingContext> context = new ThreadLocal<PrintingContext>() { @Override protected PrintingContext initialValue() { return new PrintingContext(); } }; /** * Creates a new {@link IdealGraphPrinterObserver} that writes output to a file named after the compiled method. */ public IdealGraphPrinterObserver() { this(null, -1); } /** * Creates a new {@link IdealGraphPrinterObserver} that sends output to a remote IdealGraphVisualizer instance. */ public IdealGraphPrinterObserver(String host, int port) { this.host = host; this.port = port; } private PrintingContext context() { return context.get(); } private IdealGraphPrinter printer() { return context().printer; } private Socket socket() { return context().socket; } @Override public void compilationStarted(GraalCompilation compilation) { openPrinter(compilation, false); } private void openPrinter(GraalCompilation compilation, boolean error) { assert (context().stream == null && printer() == null); if ((!TTY.isSuppressed() && GraalOptions.Plot) || (GraalOptions.PlotOnError && error)) { String name; if (compilation != null) { name = compilation.method.holder().name(); name = name.substring(1, name.length() - 1).replace('/', '.'); name = name + "." + compilation.method.name(); } else { name = "null"; } openPrinter(name, compilation == null ? null : compilation.method); } } private void openPrinter(String title, RiResolvedMethod method) { assert (context().stream == null && printer() == null); if (!TTY.isSuppressed()) { // Use a filter to suppress a recursive attempt to open a printer TTY.Filter filter = new TTY.Filter(); try { if (host != null) { openNetworkPrinter(title, method); } else { openFilePrinter(title, method); } } finally { filter.remove(); } } } private void openFilePrinter(String title, RiResolvedMethod method) { String filename = title + ".igv.xml"; filename = INVALID_CHAR.matcher(filename).replaceAll("_"); try { context().stream = new FileOutputStream(filename); context().printer = new IdealGraphPrinter(context().stream); if (GraalOptions.OmitDOTFrameStates) { printer().addOmittedClass(FrameState.class); } printer().begin(); printer().beginGroup(title, title, method, -1, "Graal"); } catch (IOException e) { e.printStackTrace(); } } public boolean networkAvailable() { try { Socket s = new Socket(host, port); s.setSoTimeout(10); s.close(); return true; } catch (IOException e) { return false; } } private void openNetworkPrinter(String title, RiResolvedMethod method) { try { context().socket = new Socket(host, port); if (socket().getInputStream().read() == 'y') { context().stream = new BufferedOutputStream(socket().getOutputStream(), 0x4000); } else { // server currently does not accept any input socket().close(); context().socket = null; return; } context().printer = new IdealGraphPrinter(new BufferedOutputStream(context().stream)); if (GraalOptions.OmitDOTFrameStates) { printer().addOmittedClass(FrameState.class); } printer().begin(); printer().beginGroup(title, title, method, -1, "Graal"); printer().flush(); if (socket().getInputStream().read() != 'y') { // server declines input for this method socket().close(); context().socket = null; context().stream = null; context().printer = null; } } catch (IOException e) { System.err.println("Error opening connection to " + host + ":" + port + ": " + e); if (socket() != null) { try { socket().close(); } catch (IOException ioe) { } context().socket = null; } context().stream = null; context().printer = null; } } @Override public void compilationEvent(CompilationEvent event) { boolean lazyStart = false; if (printer() == null && event.hasDebugObject(CompilationEvent.ERROR)) { openPrinter(event.debugObject(GraalCompilation.class), true); lazyStart = true; } Graph graph = event.debugObject(Graph.class); if (printer() != null && graph != null) { printer().print(graph, event.label, true, event.debugObject(IdentifyBlocksPhase.class)); } if (lazyStart && printer() != null) { closePrinter(); } } @Override public void compilationFinished(GraalCompilation compilation) { if (printer() != null) { closePrinter(); } } private void closePrinter() { assert (printer() != null); try { printer().endGroup(); printer().end(); if (socket() != null) { socket().close(); // also closes stream } else { context().stream.close(); } } catch (IOException e) { e.printStackTrace(); } finally { context().printer = null; context().stream = null; context().socket = null; } } public void printGraphs(String groupTitle, Graph... graphs) { openPrinter(groupTitle, null); if (printer() != null) { int i = 0; for (Graph graph : graphs) { printer().print(graph, "Graph " + i, true); i++; } closePrinter(); } } public void compilationStarted(String groupTitle) { openPrinter(groupTitle, null); } public void printGraph(String graphTitle, Graph graph) { if (printer() != null) { printer().print(graph, graphTitle, true); } } public void printSingleGraph(String title, Graph graph) { printSingleGraph(title, title, graph); } public void printSingleGraph(String groupTitle, String graphTitle, Graph graph) { openPrinter(groupTitle, null); if (printer() != null) { printer().print(graph, graphTitle, true); closePrinter(); } } }