001/* 002 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.printer; 024 025import java.io.*; 026import java.util.*; 027import java.util.concurrent.atomic.*; 028 029import jdk.internal.jvmci.code.*; 030import jdk.internal.jvmci.common.*; 031import com.oracle.graal.debug.*; 032 033import jdk.internal.jvmci.meta.*; 034import jdk.internal.jvmci.service.*; 035 036import com.oracle.graal.code.*; 037import com.oracle.graal.compiler.common.cfg.*; 038import com.oracle.graal.compiler.gen.*; 039import com.oracle.graal.graph.*; 040import com.oracle.graal.java.*; 041import com.oracle.graal.lir.*; 042import com.oracle.graal.lir.alloc.lsra.*; 043import com.oracle.graal.lir.stackslotalloc.*; 044import com.oracle.graal.nodes.*; 045import com.oracle.graal.nodes.cfg.*; 046import com.oracle.graal.phases.schedule.*; 047 048/** 049 * Observes compilation events and uses {@link CFGPrinter} to produce a control flow graph for the 050 * <a href="http://java.net/projects/c1visualizer/">C1 Visualizer</a>. 051 */ 052public class CFGPrinterObserver implements DebugDumpHandler { 053 054 private CFGPrinter cfgPrinter; 055 private File cfgFile; 056 private JavaMethod curMethod; 057 private List<String> curDecorators = Collections.emptyList(); 058 private final boolean dumpFrontend; 059 060 public CFGPrinterObserver(boolean dumpFrontend) { 061 this.dumpFrontend = dumpFrontend; 062 } 063 064 @Override 065 public void dump(Object object, String message) { 066 try { 067 dumpSandboxed(object, message); 068 } catch (Throwable ex) { 069 TTY.println("CFGPrinter: Exception during output of " + message + ": " + ex); 070 ex.printStackTrace(); 071 } 072 } 073 074 /** 075 * Looks for the outer most method and its {@link DebugDumpScope#decorator}s in the current 076 * debug scope and opens a new compilation scope if this pair does not match the current method 077 * and decorator pair. 078 */ 079 private boolean checkMethodScope() { 080 JavaMethod method = null; 081 ArrayList<String> decorators = new ArrayList<>(); 082 for (Object o : Debug.context()) { 083 if (o instanceof JavaMethod) { 084 method = (JavaMethod) o; 085 decorators.clear(); 086 } else if (o instanceof StructuredGraph) { 087 StructuredGraph graph = (StructuredGraph) o; 088 if (graph.method() != null) { 089 method = graph.method(); 090 decorators.clear(); 091 } 092 } else if (o instanceof DebugDumpScope) { 093 DebugDumpScope debugDumpScope = (DebugDumpScope) o; 094 if (debugDumpScope.decorator) { 095 decorators.add(debugDumpScope.name); 096 } 097 } 098 } 099 100 if (method == null) { 101 return false; 102 } 103 104 if (!method.equals(curMethod) || !curDecorators.equals(decorators)) { 105 cfgPrinter.printCompilation(method); 106 TTY.println("CFGPrinter: Dumping method %s to %s", method, cfgFile.getAbsolutePath()); 107 } 108 curMethod = method; 109 curDecorators = decorators; 110 return true; 111 } 112 113 private static final long timestamp = System.currentTimeMillis(); 114 private static final AtomicInteger uniqueId = new AtomicInteger(); 115 116 private static boolean isFrontendObject(Object object) { 117 return object instanceof Graph || object instanceof BciBlockMapping; 118 } 119 120 private LIR lastLIR = null; 121 private Interval[] delayedIntervals = null; 122 123 public void dumpSandboxed(Object object, String message) { 124 125 if (!dumpFrontend && isFrontendObject(object)) { 126 return; 127 } 128 129 if (cfgPrinter == null) { 130 cfgFile = new File("compilations-" + timestamp + "_" + uniqueId.incrementAndGet() + ".cfg"); 131 try { 132 OutputStream out = new BufferedOutputStream(new FileOutputStream(cfgFile)); 133 cfgPrinter = new CFGPrinter(out); 134 } catch (FileNotFoundException e) { 135 throw new JVMCIError("Could not open " + cfgFile.getAbsolutePath()); 136 } 137 TTY.println("CFGPrinter: Output to file %s", cfgFile.getAbsolutePath()); 138 } 139 140 if (!checkMethodScope()) { 141 return; 142 } 143 if (curMethod instanceof ResolvedJavaMethod) { 144 cfgPrinter.method = (ResolvedJavaMethod) curMethod; 145 } 146 147 if (object instanceof LIR) { 148 cfgPrinter.lir = (LIR) object; 149 } else { 150 cfgPrinter.lir = Debug.contextLookup(LIR.class); 151 } 152 cfgPrinter.nodeLirGenerator = Debug.contextLookup(NodeLIRBuilder.class); 153 if (cfgPrinter.nodeLirGenerator != null) { 154 cfgPrinter.target = cfgPrinter.nodeLirGenerator.getLIRGeneratorTool().target(); 155 } 156 if (cfgPrinter.lir != null && cfgPrinter.lir.getControlFlowGraph() instanceof ControlFlowGraph) { 157 cfgPrinter.cfg = (ControlFlowGraph) cfgPrinter.lir.getControlFlowGraph(); 158 } 159 160 CodeCacheProvider codeCache = Debug.contextLookup(CodeCacheProvider.class); 161 if (codeCache != null) { 162 cfgPrinter.target = codeCache.getTarget(); 163 } 164 165 if (object instanceof BciBlockMapping) { 166 BciBlockMapping blockMap = (BciBlockMapping) object; 167 cfgPrinter.printCFG(message, blockMap); 168 if (blockMap.method.getCode() != null) { 169 cfgPrinter.printBytecodes(new BytecodeDisassembler(false).disassemble(blockMap.method)); 170 } 171 172 } else if (object instanceof LIR) { 173 // Currently no node printing for lir 174 cfgPrinter.printCFG(message, cfgPrinter.lir.codeEmittingOrder(), false); 175 lastLIR = (LIR) object; 176 if (delayedIntervals != null) { 177 cfgPrinter.printIntervals(message, delayedIntervals); 178 delayedIntervals = null; 179 } 180 } else if (object instanceof SchedulePhase) { 181 cfgPrinter.printSchedule(message, (SchedulePhase) object); 182 } else if (object instanceof StructuredGraph) { 183 if (cfgPrinter.cfg == null) { 184 StructuredGraph graph = (StructuredGraph) object; 185 cfgPrinter.cfg = ControlFlowGraph.compute(graph, true, true, true, false); 186 } 187 cfgPrinter.printCFG(message, cfgPrinter.cfg.getBlocks(), true); 188 189 } else if (object instanceof CompilationResult) { 190 final CompilationResult compResult = (CompilationResult) object; 191 cfgPrinter.printMachineCode(disassemble(codeCache, compResult, null), message); 192 } else if (isCompilationResultAndInstalledCode(object)) { 193 Object[] tuple = (Object[]) object; 194 cfgPrinter.printMachineCode(disassemble(codeCache, (CompilationResult) tuple[0], (InstalledCode) tuple[1]), message); 195 } else if (object instanceof Interval[]) { 196 if (lastLIR == cfgPrinter.lir) { 197 cfgPrinter.printIntervals(message, (Interval[]) object); 198 } else { 199 if (delayedIntervals != null) { 200 Debug.log("Some delayed intervals were dropped (%s)", (Object) delayedIntervals); 201 } 202 delayedIntervals = (Interval[]) object; 203 } 204 } else if (object instanceof StackInterval[]) { 205 cfgPrinter.printStackIntervals(message, (StackInterval[]) object); 206 } else if (isBlockList(object)) { 207 cfgPrinter.printCFG(message, getBlockList(object), false); 208 } 209 210 cfgPrinter.target = null; 211 cfgPrinter.lir = null; 212 cfgPrinter.nodeLirGenerator = null; 213 cfgPrinter.cfg = null; 214 cfgPrinter.flush(); 215 216 } 217 218 @SuppressWarnings("unchecked") 219 private static List<? extends AbstractBlockBase<?>> getBlockList(Object object) { 220 return (List<? extends AbstractBlockBase<?>>) object; 221 } 222 223 private static boolean isBlockList(Object object) { 224 return object instanceof List<?> && ((List<?>) object).size() > 0 && ((List<?>) object).get(0) instanceof AbstractBlockBase<?>; 225 } 226 227 private static DisassemblerProvider disassembler; 228 229 private static final DisassemblerProvider NOP_DISASSEMBLER = new DisassemblerProvider() { 230 public String getName() { 231 return null; 232 } 233 }; 234 235 private static DisassemblerProvider getDisassembler() { 236 if (disassembler == null) { 237 DisassemblerProvider selected = NOP_DISASSEMBLER; 238 for (DisassemblerProvider d : Services.load(DisassemblerProvider.class)) { 239 String name = d.getName().toLowerCase(); 240 if (name.contains("hcf") || name.contains("hexcodefile")) { 241 selected = d; 242 break; 243 } 244 } 245 disassembler = selected; 246 } 247 return disassembler; 248 } 249 250 private static String disassemble(CodeCacheProvider codeCache, CompilationResult compResult, InstalledCode installedCode) { 251 DisassemblerProvider dis = getDisassembler(); 252 if (installedCode != null) { 253 return dis.disassembleInstalledCode(codeCache, compResult, installedCode); 254 } 255 return dis.disassembleCompiledCode(codeCache, compResult); 256 } 257 258 private static boolean isCompilationResultAndInstalledCode(Object object) { 259 if (object instanceof Object[]) { 260 Object[] tuple = (Object[]) object; 261 if (tuple.length == 2 && tuple[0] instanceof CompilationResult && tuple[1] instanceof InstalledCode) { 262 return true; 263 } 264 } 265 return false; 266 } 267 268 @Override 269 public void close() { 270 if (cfgPrinter != null) { 271 cfgPrinter.close(); 272 cfgPrinter = null; 273 curDecorators = Collections.emptyList(); 274 curMethod = null; 275 } 276 } 277}