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 }