1 /* 2 * Copyright (c) 2011, 2016, 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 */ 23 package org.graalvm.compiler.printer; 24 25 import static org.graalvm.compiler.debug.DebugOptions.PrintCFG; 26 import static org.graalvm.compiler.printer.GraalDebugHandlersFactory.createDumpPath; 27 28 import java.io.BufferedOutputStream; 29 import java.io.File; 30 import java.io.FileOutputStream; 31 import java.io.IOException; 32 import java.io.OutputStream; 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.List; 36 37 import org.graalvm.compiler.bytecode.BytecodeDisassembler; 38 import org.graalvm.compiler.code.CompilationResult; 39 import org.graalvm.compiler.code.DisassemblerProvider; 40 import org.graalvm.compiler.core.common.CompilationIdentifier; 41 import org.graalvm.compiler.core.common.alloc.Trace; 42 import org.graalvm.compiler.core.common.alloc.TraceBuilderResult; 43 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; 44 import org.graalvm.compiler.core.gen.NodeLIRBuilder; 45 import org.graalvm.compiler.debug.DebugContext; 46 import org.graalvm.compiler.debug.DebugDumpHandler; 47 import org.graalvm.compiler.debug.DebugDumpScope; 48 import org.graalvm.compiler.debug.GraalError; 49 import org.graalvm.compiler.debug.TTY; 50 import org.graalvm.compiler.graph.Graph; 51 import org.graalvm.compiler.java.BciBlockMapping; 52 import org.graalvm.compiler.lir.LIR; 53 import org.graalvm.compiler.lir.alloc.trace.GlobalLivenessInfo; 54 import org.graalvm.compiler.lir.debug.IntervalDumper; 55 import org.graalvm.compiler.lir.gen.LIRGenerationResult; 56 import org.graalvm.compiler.nodes.StructuredGraph; 57 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; 58 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; 59 import org.graalvm.compiler.options.OptionValues; 60 import org.graalvm.compiler.serviceprovider.GraalServices; 61 62 import jdk.vm.ci.code.CodeCacheProvider; 63 import jdk.vm.ci.code.InstalledCode; 64 import jdk.vm.ci.meta.JavaMethod; 65 import jdk.vm.ci.meta.ResolvedJavaMethod; 66 67 /** 68 * Observes compilation events and uses {@link CFGPrinter} to produce a control flow graph for the 69 * <a href="http://java.net/projects/c1visualizer/">C1 Visualizer</a>. 70 */ 71 public class CFGPrinterObserver implements DebugDumpHandler { 72 73 private CFGPrinter cfgPrinter; 74 private File cfgFile; 75 private JavaMethod curMethod; 76 private CompilationIdentifier curCompilation; 77 private List<String> curDecorators = Collections.emptyList(); 78 79 @Override 80 public void dump(DebugContext debug, Object object, String format, Object... arguments) { 81 String message = String.format(format, arguments); 82 try { 83 dumpSandboxed(debug, object, message); 84 } catch (Throwable ex) { 85 TTY.println("CFGPrinter: Exception during output of " + message + ": " + ex); 86 ex.printStackTrace(); 87 } 88 } 89 90 /** 91 * Looks for the outer most method and its {@link DebugDumpScope#decorator}s in the current 92 * debug scope and opens a new compilation scope if this pair does not match the current method 93 * and decorator pair. 94 */ 95 private boolean checkMethodScope(DebugContext debug) { 96 JavaMethod method = null; 97 CompilationIdentifier compilation = null; 98 ArrayList<String> decorators = new ArrayList<>(); 99 for (Object o : debug.context()) { 100 if (o instanceof JavaMethod) { 101 method = (JavaMethod) o; 102 decorators.clear(); 103 } else if (o instanceof StructuredGraph) { 104 StructuredGraph graph = (StructuredGraph) o; 105 if (graph.method() != null) { 106 method = graph.method(); 107 decorators.clear(); 108 compilation = graph.compilationId(); 109 } 110 } else if (o instanceof DebugDumpScope) { 111 DebugDumpScope debugDumpScope = (DebugDumpScope) o; 112 if (debugDumpScope.decorator) { 113 decorators.add(debugDumpScope.name); 114 } 115 } else if (o instanceof CompilationResult) { 116 CompilationResult compilationResult = (CompilationResult) o; 117 compilation = compilationResult.getCompilationId(); 118 } 119 } 120 121 if (method == null && compilation == null) { 122 return false; 123 } 124 125 if (compilation != null) { 126 if (!compilation.equals(curCompilation) || !curDecorators.equals(decorators)) { 127 cfgPrinter.printCompilation(compilation); 128 } 129 } else { 130 if (!method.equals(curMethod) || !curDecorators.equals(decorators)) { 131 cfgPrinter.printCompilation(method); 132 } 133 } 134 curCompilation = compilation; 135 curMethod = method; 136 curDecorators = decorators; 137 return true; 138 } 139 140 private static boolean isFrontendObject(Object object) { 141 return object instanceof Graph || object instanceof BciBlockMapping; 142 } 143 144 private LIR lastLIR = null; 145 private IntervalDumper delayedIntervals = null; 146 147 public void dumpSandboxed(DebugContext debug, Object object, String message) { 148 OptionValues options = debug.getOptions(); 149 boolean dumpFrontend = PrintCFG.getValue(options); 150 if (!dumpFrontend && isFrontendObject(object)) { 151 return; 152 } 153 154 if (cfgPrinter == null) { 155 try { 156 Graph graph = debug.contextLookupTopdown(Graph.class); 157 cfgFile = createDumpPath(options, graph, "cfg", false).toFile(); 158 OutputStream out = new BufferedOutputStream(new FileOutputStream(cfgFile)); 159 cfgPrinter = new CFGPrinter(out); 160 } catch (IOException e) { 161 throw (GraalError) new GraalError("Could not open %s", cfgFile == null ? "[null]" : cfgFile.getAbsolutePath()).initCause(e); 162 } 163 } 164 165 if (!checkMethodScope(debug)) { 166 return; 167 } 168 try { 169 if (curMethod instanceof ResolvedJavaMethod) { 170 cfgPrinter.method = (ResolvedJavaMethod) curMethod; 171 } 172 173 if (object instanceof LIR) { 174 cfgPrinter.lir = (LIR) object; 175 } else { 176 cfgPrinter.lir = debug.contextLookup(LIR.class); 177 } 178 cfgPrinter.nodeLirGenerator = debug.contextLookup(NodeLIRBuilder.class); 179 cfgPrinter.livenessInfo = debug.contextLookup(GlobalLivenessInfo.class); 180 cfgPrinter.res = debug.contextLookup(LIRGenerationResult.class); 181 if (cfgPrinter.nodeLirGenerator != null) { 182 cfgPrinter.target = cfgPrinter.nodeLirGenerator.getLIRGeneratorTool().target(); 183 } 184 if (cfgPrinter.lir != null && cfgPrinter.lir.getControlFlowGraph() instanceof ControlFlowGraph) { 185 cfgPrinter.cfg = (ControlFlowGraph) cfgPrinter.lir.getControlFlowGraph(); 186 } 187 188 CodeCacheProvider codeCache = debug.contextLookup(CodeCacheProvider.class); 189 if (codeCache != null) { 190 cfgPrinter.target = codeCache.getTarget(); 191 } 192 193 if (object instanceof BciBlockMapping) { 194 BciBlockMapping blockMap = (BciBlockMapping) object; 195 cfgPrinter.printCFG(message, blockMap); 196 if (blockMap.code.getCode() != null) { 197 cfgPrinter.printBytecodes(new BytecodeDisassembler(false).disassemble(blockMap.code)); 198 } 199 200 } else if (object instanceof LIR) { 201 // Currently no node printing for lir 202 cfgPrinter.printCFG(message, cfgPrinter.lir.codeEmittingOrder(), false); 203 lastLIR = (LIR) object; 204 if (delayedIntervals != null) { 205 cfgPrinter.printIntervals(message, delayedIntervals); 206 delayedIntervals = null; 207 } 208 } else if (object instanceof ScheduleResult) { 209 cfgPrinter.printSchedule(message, (ScheduleResult) object); 210 } else if (object instanceof StructuredGraph) { 211 if (cfgPrinter.cfg == null) { 212 StructuredGraph graph = (StructuredGraph) object; 213 cfgPrinter.cfg = ControlFlowGraph.compute(graph, true, true, true, false); 214 cfgPrinter.printCFG(message, cfgPrinter.cfg.getBlocks(), true); 215 } else { 216 cfgPrinter.printCFG(message, cfgPrinter.cfg.getBlocks(), true); 217 } 218 219 } else if (object instanceof CompilationResult) { 220 final CompilationResult compResult = (CompilationResult) object; 221 cfgPrinter.printMachineCode(disassemble(codeCache, compResult, null), message); 222 } else if (object instanceof InstalledCode) { 223 CompilationResult compResult = debug.contextLookup(CompilationResult.class); 224 if (compResult != null) { 225 cfgPrinter.printMachineCode(disassemble(codeCache, compResult, (InstalledCode) object), message); 226 } 227 } else if (object instanceof IntervalDumper) { 228 if (lastLIR == cfgPrinter.lir) { 229 cfgPrinter.printIntervals(message, (IntervalDumper) object); 230 } else { 231 if (delayedIntervals != null) { 232 debug.log("Some delayed intervals were dropped (%s)", delayedIntervals); 233 } 234 delayedIntervals = (IntervalDumper) object; 235 } 236 } else if (object instanceof AbstractBlockBase<?>[]) { 237 cfgPrinter.printCFG(message, (AbstractBlockBase<?>[]) object, false); 238 } else if (object instanceof Trace) { 239 cfgPrinter.printCFG(message, ((Trace) object).getBlocks(), false); 240 } else if (object instanceof TraceBuilderResult) { 241 cfgPrinter.printTraces(message, (TraceBuilderResult) object); 242 } 243 } finally { 244 cfgPrinter.target = null; 245 cfgPrinter.lir = null; 246 cfgPrinter.res = null; 247 cfgPrinter.nodeLirGenerator = null; 248 cfgPrinter.livenessInfo = null; 249 cfgPrinter.cfg = null; 250 cfgPrinter.flush(); 251 } 252 } 253 254 /** Lazy initialization to delay service lookup until disassembler is actually needed. */ 255 static class DisassemblerHolder { 256 private static final DisassemblerProvider disassembler; 257 258 static { 259 DisassemblerProvider selected = null; 260 for (DisassemblerProvider d : GraalServices.load(DisassemblerProvider.class)) { 261 String name = d.getName().toLowerCase(); 262 if (name.contains("hcf") || name.contains("hexcodefile")) { 263 selected = d; 264 break; 265 } 266 } 267 if (selected == null) { 268 selected = new DisassemblerProvider() { 269 @Override 270 public String getName() { 271 return "nop"; 272 } 273 }; 274 } 275 disassembler = selected; 276 } 277 } 278 279 private static String disassemble(CodeCacheProvider codeCache, CompilationResult compResult, InstalledCode installedCode) { 280 DisassemblerProvider dis = DisassemblerHolder.disassembler; 281 if (installedCode != null) { 282 return dis.disassembleInstalledCode(codeCache, compResult, installedCode); 283 } 284 return dis.disassembleCompiledCode(codeCache, compResult); 285 } 286 287 @Override 288 public void close() { 289 if (cfgPrinter != null) { 290 cfgPrinter.close(); 291 cfgPrinter = null; 292 curDecorators = Collections.emptyList(); 293 curMethod = null; 294 curCompilation = null; 295 } 296 } 297 298 public String getDumpPath() { 299 if (cfgFile != null) { 300 return cfgFile.getAbsolutePath(); 301 } 302 return null; 303 } 304 }