001/*
002 * Copyright (c) 2011, 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 static com.oracle.graal.compiler.common.GraalOptions.*;
026
027import java.io.*;
028import java.util.*;
029import java.util.Map.Entry;
030
031import jdk.internal.jvmci.meta.*;
032
033import com.oracle.graal.graph.*;
034import com.oracle.graal.java.*;
035import com.oracle.graal.nodeinfo.*;
036import com.oracle.graal.nodes.*;
037import com.oracle.graal.nodes.cfg.*;
038import com.oracle.graal.phases.schedule.*;
039
040/**
041 * Generates a representation of {@link Graph Graphs} that can be visualized and inspected with the
042 * <a href="http://kenai.com/projects/igv">Ideal Graph Visualizer</a>.
043 */
044public class IdealGraphPrinter extends BasicIdealGraphPrinter implements GraphPrinter {
045
046    private final boolean tryToSchedule;
047
048    /**
049     * Creates a new {@link IdealGraphPrinter} that writes to the specified output stream.
050     *
051     * @param tryToSchedule If false, no scheduling is done, which avoids exceptions for
052     *            non-schedulable graphs.
053     */
054    public IdealGraphPrinter(OutputStream stream, boolean tryToSchedule) {
055        super(stream);
056        this.begin();
057        this.tryToSchedule = tryToSchedule;
058    }
059
060    /**
061     * Starts a new group of graphs with the given name, short name and method byte code index (BCI)
062     * as properties.
063     */
064    @Override
065    public void beginGroup(String name, String shortName, ResolvedJavaMethod method, int bci) {
066        beginGroup();
067        beginProperties();
068        printProperty("name", name);
069        endProperties();
070        beginMethod(name, shortName, bci);
071        if (method != null && method.getCode() != null) {
072            printBytecodes(new BytecodeDisassembler(false).disassemble(method));
073        }
074        endMethod();
075    }
076
077    public void print(Graph graph, String title) {
078        print(graph, title, null);
079    }
080
081    /**
082     * Prints an entire {@link Graph} with the specified title, optionally using short names for
083     * nodes.
084     */
085    @Override
086    public void print(Graph graph, String title, SchedulePhase predefinedSchedule) {
087        beginGraph(title);
088        Set<Node> noBlockNodes = Node.newSet();
089        SchedulePhase schedule = predefinedSchedule;
090        if (schedule == null && tryToSchedule) {
091            if (PrintIdealGraphSchedule.getValue()) {
092                try {
093                    schedule = new SchedulePhase();
094                    schedule.apply((StructuredGraph) graph);
095                } catch (Throwable t) {
096                }
097            }
098        }
099        ControlFlowGraph cfg = schedule == null ? null : schedule.getCFG();
100
101        beginNodes();
102        List<Edge> edges = printNodes(graph, cfg == null ? null : cfg.getNodeToBlock(), noBlockNodes);
103        endNodes();
104
105        beginEdges();
106        for (Edge edge : edges) {
107            printEdge(edge);
108        }
109        endEdges();
110
111        if (cfg != null && cfg.getBlocks() != null) {
112            beginControlFlow();
113            for (Block block : cfg.getBlocks()) {
114                printBlock(graph, block, cfg.getNodeToBlock());
115            }
116            printNoBlock(noBlockNodes);
117            endControlFlow();
118        }
119
120        endGraph();
121        flush();
122    }
123
124    private List<Edge> printNodes(Graph graph, NodeMap<Block> nodeToBlock, Set<Node> noBlockNodes) {
125        ArrayList<Edge> edges = new ArrayList<>();
126
127        NodeMap<Set<Entry<String, Integer>>> colors = graph.createNodeMap();
128        NodeMap<Set<Entry<String, String>>> colorsToString = graph.createNodeMap();
129        NodeMap<Set<String>> bits = graph.createNodeMap();
130
131        for (Node node : graph.getNodes()) {
132
133            beginNode(node.toString(Verbosity.Id));
134            beginProperties();
135            printProperty("idx", node.toString(Verbosity.Id));
136
137            Map<Object, Object> props = node.getDebugProperties();
138            if (!props.containsKey("name") || props.get("name").toString().trim().length() == 0) {
139                String name = node.toString(Verbosity.Name);
140                printProperty("name", name);
141            }
142            printProperty("class", node.getClass().getSimpleName());
143            Block block = nodeToBlock == null ? null : nodeToBlock.get(node);
144            if (block != null) {
145                printProperty("block", Integer.toString(block.getId()));
146                // if (!(node instanceof PhiNode || node instanceof FrameState || node instanceof
147                // ParameterNode) && !block.nodes().contains(node)) {
148                // printProperty("notInOwnBlock", "true");
149                // }
150            } else {
151                printProperty("block", "noBlock");
152                noBlockNodes.add(node);
153            }
154
155            Set<Entry<String, Integer>> nodeColors = colors.get(node);
156            if (nodeColors != null) {
157                for (Entry<String, Integer> color : nodeColors) {
158                    String name = color.getKey();
159                    Integer value = color.getValue();
160                    printProperty(name, Integer.toString(value));
161                }
162            }
163            Set<Entry<String, String>> nodeColorStrings = colorsToString.get(node);
164            if (nodeColorStrings != null) {
165                for (Entry<String, String> color : nodeColorStrings) {
166                    String name = color.getKey();
167                    String value = color.getValue();
168                    printProperty(name, value);
169                }
170            }
171            Set<String> nodeBits = bits.get(node);
172            if (nodeBits != null) {
173                for (String bit : nodeBits) {
174                    printProperty(bit, "true");
175                }
176            }
177            if (node instanceof BeginNode) {
178                printProperty("shortName", "B");
179            } else if (node.getClass() == EndNode.class) {
180                printProperty("shortName", "E");
181            }
182            if (node.predecessor() != null) {
183                printProperty("hasPredecessor", "true");
184            }
185
186            for (Entry<Object, Object> entry : props.entrySet()) {
187                String key = entry.getKey().toString();
188                Object value = entry.getValue();
189                String valueString;
190                if (value == null) {
191                    valueString = "null";
192                } else {
193                    Class<?> type = value.getClass();
194                    if (type.isArray()) {
195                        if (!type.getComponentType().isPrimitive()) {
196                            valueString = Arrays.toString((Object[]) value);
197                        } else if (type.getComponentType() == Integer.TYPE) {
198                            valueString = Arrays.toString((int[]) value);
199                        } else if (type.getComponentType() == Double.TYPE) {
200                            valueString = Arrays.toString((double[]) value);
201                        } else {
202                            valueString = toString();
203                        }
204                    } else {
205                        valueString = value.toString();
206                    }
207                }
208                printProperty(key, valueString);
209            }
210
211            endProperties();
212            endNode();
213
214            // successors
215            int fromIndex = 0;
216            NodePosIterator succIter = node.successors().iterator();
217            while (succIter.hasNext()) {
218                Position position = succIter.nextPosition();
219                Node successor = position.get(node);
220                if (successor != null) {
221                    edges.add(new Edge(node.toString(Verbosity.Id), fromIndex, successor.toString(Verbosity.Id), 0, position.getName()));
222                }
223                fromIndex++;
224            }
225
226            // inputs
227            int toIndex = 1;
228            NodePosIterator inputIter = node.inputs().iterator();
229            while (inputIter.hasNext()) {
230                Position position = inputIter.nextPosition();
231                Node input = position.get(node);
232                if (input != null) {
233                    edges.add(new Edge(input.toString(Verbosity.Id), input.successors().count(), node.toString(Verbosity.Id), toIndex, position.getName()));
234                }
235                toIndex++;
236            }
237        }
238
239        return edges;
240    }
241
242    private void printBlock(Graph graph, Block block, NodeMap<Block> nodeToBlock) {
243        beginBlock(Integer.toString(block.getId()));
244        beginSuccessors();
245        for (Block sux : block.getSuccessors()) {
246            if (sux != null) {
247                printSuccessor(Integer.toString(sux.getId()));
248            }
249        }
250        endSuccessors();
251        beginBlockNodes();
252
253        Set<Node> nodes = Node.newSet();
254
255        if (nodeToBlock != null) {
256            for (Node n : graph.getNodes()) {
257                Block blk = nodeToBlock.get(n);
258                if (blk == block) {
259                    nodes.add(n);
260                }
261            }
262        }
263
264        if (nodes.size() > 0) {
265            // if this is the first block: add all locals to this block
266            if (block.getBeginNode() == ((StructuredGraph) graph).start()) {
267                for (Node node : graph.getNodes()) {
268                    if (node instanceof ParameterNode) {
269                        nodes.add(node);
270                    }
271                }
272            }
273
274            Set<Node> snapshot = Node.newSet(nodes);
275            // add all framestates and phis to their blocks
276            for (Node node : snapshot) {
277                if (node instanceof StateSplit && ((StateSplit) node).stateAfter() != null) {
278                    nodes.add(((StateSplit) node).stateAfter());
279                }
280                if (node instanceof AbstractMergeNode) {
281                    for (PhiNode phi : ((AbstractMergeNode) node).phis()) {
282                        nodes.add(phi);
283                    }
284                }
285            }
286
287            for (Node node : nodes) {
288                printBlockNode(node.toString(Verbosity.Id));
289            }
290        }
291        endBlockNodes();
292        endBlock();
293    }
294
295    private void printNoBlock(Set<Node> noBlockNodes) {
296        if (!noBlockNodes.isEmpty()) {
297            beginBlock("noBlock");
298            beginBlockNodes();
299            for (Node node : noBlockNodes) {
300                printBlockNode(node.toString(Verbosity.Id));
301            }
302            endBlockNodes();
303            endBlock();
304        }
305    }
306}