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}