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