GraalDebugHandlersFactory.java revision 13264:48566d838608
1/* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23package org.graalvm.compiler.printer; 24 25import static org.graalvm.compiler.debug.DebugOptions.PrintBinaryGraphPort; 26import static org.graalvm.compiler.debug.DebugOptions.PrintBinaryGraphs; 27import static org.graalvm.compiler.debug.DebugOptions.PrintGraphHost; 28import static org.graalvm.compiler.debug.DebugOptions.PrintXmlGraphPort; 29import static org.graalvm.compiler.debug.DebugOptions.ShowDumpFiles; 30 31import java.io.IOException; 32import java.io.InterruptedIOException; 33import java.net.InetSocketAddress; 34import java.net.Socket; 35import java.nio.channels.ClosedByInterruptException; 36import java.nio.channels.FileChannel; 37import java.nio.channels.SocketChannel; 38import java.nio.file.FileAlreadyExistsException; 39import java.nio.file.Files; 40import java.nio.file.InvalidPathException; 41import java.nio.file.Path; 42import java.nio.file.Paths; 43import java.nio.file.StandardOpenOption; 44import java.util.ArrayList; 45import java.util.List; 46import java.util.concurrent.atomic.AtomicInteger; 47 48import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 49import org.graalvm.compiler.core.common.CompilationIdentifier; 50import org.graalvm.compiler.debug.DebugContext; 51import org.graalvm.compiler.debug.DebugDumpHandler; 52import org.graalvm.compiler.debug.DebugHandler; 53import org.graalvm.compiler.debug.DebugHandlersFactory; 54import org.graalvm.compiler.debug.DebugOptions; 55import org.graalvm.compiler.debug.TTY; 56import org.graalvm.compiler.debug.UniquePathUtilities; 57import org.graalvm.compiler.graph.Graph; 58import org.graalvm.compiler.graph.Node; 59import org.graalvm.compiler.nodeinfo.Verbosity; 60import org.graalvm.compiler.nodes.StructuredGraph; 61import org.graalvm.compiler.nodes.util.GraphUtil; 62import org.graalvm.compiler.options.OptionValues; 63import org.graalvm.compiler.serviceprovider.ServiceProvider; 64 65@ServiceProvider(DebugHandlersFactory.class) 66public class GraalDebugHandlersFactory implements DebugHandlersFactory { 67 68 private final SnippetReflectionProvider snippetReflection; 69 70 public GraalDebugHandlersFactory() { 71 this.snippetReflection = null; 72 } 73 74 public GraalDebugHandlersFactory(SnippetReflectionProvider snippetReflection) { 75 this.snippetReflection = snippetReflection; 76 } 77 78 @Override 79 public List<DebugHandler> createHandlers(OptionValues options) { 80 List<DebugHandler> handlers = new ArrayList<>(); 81 if (DebugOptions.PrintGraphFile.getValue(options)) { 82 handlers.add(new GraphPrinterDumpHandler((graph) -> createFilePrinter(graph, options, snippetReflection))); 83 } else { 84 handlers.add(new GraphPrinterDumpHandler((graph) -> createNetworkPrinter(graph, options, snippetReflection))); 85 } 86 if (DebugOptions.PrintCanonicalGraphStrings.getValue(options)) { 87 handlers.add(new GraphPrinterDumpHandler((graph) -> createStringPrinter(snippetReflection))); 88 } 89 handlers.add(new NodeDumper()); 90 if (DebugOptions.PrintCFG.getValue(options) || DebugOptions.PrintBackendCFG.getValue(options)) { 91 if (DebugOptions.PrintBinaryGraphs.getValue(options) && DebugOptions.PrintCFG.getValue(options)) { 92 TTY.out.println("Complete C1Visualizer dumping slows down PrintBinaryGraphs: use -Dgraal.PrintCFG=false to disable it"); 93 } 94 handlers.add(new CFGPrinterObserver()); 95 } 96 handlers.add(new NoDeadCodeVerifyHandler()); 97 return handlers; 98 } 99 100 private static class NodeDumper implements DebugDumpHandler { 101 @Override 102 public void dump(DebugContext debug, Object object, String format, Object... arguments) { 103 if (object instanceof Node) { 104 Node node = (Node) object; 105 String location = GraphUtil.approxSourceLocation(node); 106 String nodeName = node.toString(Verbosity.Debugger); 107 if (location != null) { 108 debug.log("Context obj %s (approx. location: %s)", nodeName, location); 109 } else { 110 debug.log("Context obj %s", nodeName); 111 } 112 } 113 } 114 } 115 116 private static CanonicalStringGraphPrinter createStringPrinter(SnippetReflectionProvider snippetReflection) { 117 return new CanonicalStringGraphPrinter(snippetReflection); 118 } 119 120 public static String sanitizedFileName(String name) { 121 try { 122 Paths.get(name); 123 return name; 124 } catch (InvalidPathException e) { 125 // fall through 126 } 127 StringBuilder buf = new StringBuilder(name.length()); 128 for (int i = 0; i < name.length(); i++) { 129 char c = name.charAt(i); 130 try { 131 Paths.get(String.valueOf(c)); 132 } catch (InvalidPathException e) { 133 buf.append('_'); 134 } 135 buf.append(c); 136 } 137 return buf.toString(); 138 } 139 140 private static GraphPrinter createNetworkPrinter(Graph graph, OptionValues options, SnippetReflectionProvider snippetReflection) throws IOException { 141 String host = PrintGraphHost.getValue(options); 142 int port = PrintBinaryGraphs.getValue(options) ? PrintBinaryGraphPort.getValue(options) : PrintXmlGraphPort.getValue(options); 143 try { 144 GraphPrinter printer; 145 if (DebugOptions.PrintBinaryGraphs.getValue(options)) { 146 printer = new BinaryGraphPrinter(SocketChannel.open(new InetSocketAddress(host, port)), snippetReflection); 147 } else { 148 printer = new IdealGraphPrinter(new Socket(host, port).getOutputStream(), true, snippetReflection); 149 } 150 TTY.println("Connected to the IGV on %s:%d", host, port); 151 return printer; 152 } catch (ClosedByInterruptException | InterruptedIOException e) { 153 /* 154 * Interrupts should not count as errors because they may be caused by a cancelled Graal 155 * compilation. ClosedByInterruptException occurs if the SocketChannel could not be 156 * opened. InterruptedIOException occurs if new Socket(..) was interrupted. 157 */ 158 return null; 159 } catch (IOException e) { 160 if (!DebugOptions.PrintGraphFile.hasBeenSet(options)) { 161 return createFilePrinter(graph, options, snippetReflection); 162 } else { 163 throw new IOException(String.format("Could not connect to the IGV on %s:%d", host, port), e); 164 } 165 } 166 } 167 168 private static final AtomicInteger unknownCompilationId = new AtomicInteger(); 169 170 /** 171 * Creates a new file or directory for dumping based on a given graph and a file extension. 172 * 173 * @param graph a base path name is derived from {@code graph} 174 * @param extension a suffix which if non-null and non-empty added to the end of the returned 175 * path separated by a {@code "."} 176 * @param createDirectory specifies if this is a request to create a directory instead of a file 177 * @return the created directory or file 178 * @throws IOException if there was an error creating the directory or file 179 */ 180 static Path createDumpPath(OptionValues options, Graph graph, String extension, boolean createDirectory) throws IOException { 181 CompilationIdentifier compilationId = CompilationIdentifier.INVALID_COMPILATION_ID; 182 String id = null; 183 String label = null; 184 if (graph instanceof StructuredGraph) { 185 StructuredGraph sgraph = (StructuredGraph) graph; 186 label = getGraphName(sgraph); 187 compilationId = sgraph.compilationId(); 188 if (compilationId == CompilationIdentifier.INVALID_COMPILATION_ID) { 189 id = graph.getClass().getSimpleName() + "-" + sgraph.graphId(); 190 } else { 191 id = compilationId.toString(CompilationIdentifier.Verbosity.ID); 192 } 193 } else { 194 label = graph == null ? null : graph.name != null ? graph.name : graph.toString(); 195 id = "UnknownCompilation-" + unknownCompilationId.incrementAndGet(); 196 } 197 String ext = UniquePathUtilities.formatExtension(extension); 198 Path result = createUnique(DebugOptions.getDumpDirectory(options), id, label, ext, createDirectory); 199 if (ShowDumpFiles.getValue(options)) { 200 TTY.println("Dumping debug output to %s", result.toAbsolutePath().toString()); 201 } 202 return result; 203 } 204 205 /** 206 * A maximum file name length supported by most file systems. There is no platform independent 207 * way to get this in Java. 208 */ 209 private static final int MAX_FILE_NAME_LENGTH = 255; 210 211 private static final String ELLIPSIS = "..."; 212 213 private static Path createUnique(Path dumpDir, String id, String label, String ext, boolean createDirectory) throws IOException { 214 String timestamp = ""; 215 for (;;) { 216 int fileNameLengthWithoutLabel = timestamp.length() + ext.length() + id.length() + "[]".length(); 217 int labelLengthLimit = MAX_FILE_NAME_LENGTH - fileNameLengthWithoutLabel; 218 String fileName; 219 if (labelLengthLimit < ELLIPSIS.length()) { 220 // This means `id` is very long 221 String suffix = timestamp + ext; 222 int idLengthLimit = Math.min(MAX_FILE_NAME_LENGTH - suffix.length(), id.length()); 223 fileName = id.substring(0, idLengthLimit) + suffix; 224 } else { 225 if (label == null) { 226 fileName = sanitizedFileName(id + timestamp + ext); 227 } else { 228 String adjustedLabel = label; 229 if (label.length() > labelLengthLimit) { 230 adjustedLabel = label.substring(0, labelLengthLimit - ELLIPSIS.length()) + ELLIPSIS; 231 } 232 fileName = sanitizedFileName(id + '[' + adjustedLabel + ']' + timestamp + ext); 233 } 234 } 235 Path result = dumpDir.resolve(fileName); 236 try { 237 if (createDirectory) { 238 return Files.createDirectory(result); 239 } else { 240 return Files.createFile(result); 241 } 242 } catch (FileAlreadyExistsException e) { 243 timestamp = "_" + Long.toString(System.currentTimeMillis()); 244 } 245 } 246 } 247 248 private static String getGraphName(StructuredGraph graph) { 249 if (graph.name != null) { 250 return graph.name; 251 } else if (graph.method() != null) { 252 return graph.method().format("%h.%n(%p)").replace(" ", ""); 253 } else { 254 return graph.toString(); 255 } 256 } 257 258 private static GraphPrinter createFilePrinter(Graph graph, OptionValues options, SnippetReflectionProvider snippetReflection) throws IOException { 259 Path path = createDumpPath(options, graph, PrintBinaryGraphs.getValue(options) ? "bgv" : "gv.xml", false); 260 try { 261 GraphPrinter printer; 262 if (DebugOptions.PrintBinaryGraphs.getValue(options)) { 263 printer = new BinaryGraphPrinter(FileChannel.open(path, StandardOpenOption.WRITE), snippetReflection); 264 } else { 265 printer = new IdealGraphPrinter(Files.newOutputStream(path), true, snippetReflection); 266 } 267 return printer; 268 } catch (IOException e) { 269 throw new IOException(String.format("Failed to open %s to dump IGV graphs", path), e); 270 } 271 } 272} 273