1 /* 2 * Copyright (c) 2011, 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 java.io.OutputStream; 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.Map.Entry; 31 import java.util.Set; 32 33 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 34 import org.graalvm.compiler.bytecode.BytecodeDisassembler; 35 import org.graalvm.compiler.debug.GraalDebugConfig.Options; 36 import org.graalvm.compiler.graph.Graph; 37 import org.graalvm.compiler.graph.Node; 38 import org.graalvm.compiler.graph.NodeMap; 39 import org.graalvm.compiler.graph.Position; 40 import org.graalvm.compiler.nodeinfo.Verbosity; 41 import org.graalvm.compiler.nodes.AbstractMergeNode; 42 import org.graalvm.compiler.nodes.BeginNode; 43 import org.graalvm.compiler.nodes.ConstantNode; 44 import org.graalvm.compiler.nodes.EndNode; 45 import org.graalvm.compiler.nodes.ParameterNode; 46 import org.graalvm.compiler.nodes.PhiNode; 47 import org.graalvm.compiler.nodes.StateSplit; 48 import org.graalvm.compiler.nodes.StructuredGraph; 49 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; 50 import org.graalvm.compiler.nodes.cfg.Block; 51 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; 52 import org.graalvm.compiler.phases.schedule.SchedulePhase; 53 54 import jdk.vm.ci.meta.ResolvedJavaMethod; 55 56 /** 57 * Generates a representation of {@link Graph Graphs} that can be visualized and inspected with the 58 * <a href="http://kenai.com/projects/igv">Ideal Graph Visualizer</a>. 59 */ 60 public class IdealGraphPrinter extends BasicIdealGraphPrinter implements GraphPrinter { 61 62 private final boolean tryToSchedule; 63 private final SnippetReflectionProvider snippetReflection; 64 65 /** 66 * Creates a new {@link IdealGraphPrinter} that writes to the specified output stream. 67 * 68 * @param tryToSchedule If false, no scheduling is done, which avoids exceptions for 69 * non-schedulable graphs. 70 */ 71 public IdealGraphPrinter(OutputStream stream, boolean tryToSchedule, SnippetReflectionProvider snippetReflection) { 72 super(stream); 73 this.begin(); 74 this.tryToSchedule = tryToSchedule; 75 this.snippetReflection = snippetReflection; 76 } 77 78 @Override 79 public SnippetReflectionProvider getSnippetReflectionProvider() { 80 return snippetReflection; 81 } 82 83 /** 84 * Starts a new group of graphs with the given name, short name and method byte code index (BCI) 85 * as properties. 86 */ 87 @Override 88 public void beginGroup(String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) { 89 beginGroup(); 90 beginProperties(); 91 printProperty("name", name); 92 if (properties != null) { 93 for (Entry<Object, Object> entry : properties.entrySet()) { 94 printProperty(entry.getKey().toString(), entry.getValue().toString()); 95 } 96 } 97 endProperties(); 98 beginMethod(name, shortName, bci); 99 if (method != null && method.getCode() != null) { 100 printBytecodes(new BytecodeDisassembler(false).disassemble(method)); 101 } 102 endMethod(); 103 } 104 105 /** 106 * Prints an entire {@link Graph} with the specified title, optionally using short names for 107 * nodes. 108 */ 109 @Override 110 public void print(Graph graph, String title, Map<Object, Object> properties) { 111 beginGraph(title); 112 Set<Node> noBlockNodes = Node.newSet(); 113 ScheduleResult schedule = null; 114 if (graph instanceof StructuredGraph) { 115 StructuredGraph structuredGraph = (StructuredGraph) graph; 116 schedule = structuredGraph.getLastSchedule(); 117 if (schedule == null && tryToSchedule) { 118 if (Options.PrintIdealGraphSchedule.getValue()) { 119 try { 120 SchedulePhase schedulePhase = new SchedulePhase(); 121 schedulePhase.apply(structuredGraph); 122 schedule = structuredGraph.getLastSchedule(); 123 } catch (Throwable t) { 124 } 125 } 126 } 127 } 128 ControlFlowGraph cfg = schedule == null ? null : schedule.getCFG(); 129 130 if (properties != null) { 131 beginProperties(); 132 for (Entry<Object, Object> entry : properties.entrySet()) { 133 printProperty(entry.getKey().toString(), entry.getValue().toString()); 134 } 135 endProperties(); 136 } 137 138 beginNodes(); 139 List<Edge> edges = printNodes(graph, cfg == null ? null : cfg.getNodeToBlock(), noBlockNodes); 140 endNodes(); 141 142 beginEdges(); 143 for (Edge edge : edges) { 144 printEdge(edge); 145 } 146 endEdges(); 147 148 if (cfg != null && cfg.getBlocks() != null) { 149 beginControlFlow(); 150 for (Block block : cfg.getBlocks()) { 151 printBlock(graph, block, cfg.getNodeToBlock()); 152 } 153 printNoBlock(noBlockNodes); 154 endControlFlow(); 155 } 156 157 endGraph(); 158 flush(); 159 } 160 161 private List<Edge> printNodes(Graph graph, NodeMap<Block> nodeToBlock, Set<Node> noBlockNodes) { 162 ArrayList<Edge> edges = new ArrayList<>(); 163 164 NodeMap<Set<Entry<String, Integer>>> colors = graph.createNodeMap(); 165 NodeMap<Set<Entry<String, String>>> colorsToString = graph.createNodeMap(); 166 NodeMap<Set<String>> bits = graph.createNodeMap(); 167 168 for (Node node : graph.getNodes()) { 169 170 beginNode(node.toString(Verbosity.Id)); 171 beginProperties(); 172 printProperty("idx", node.toString(Verbosity.Id)); 173 174 Map<Object, Object> props = node.getDebugProperties(); 175 if (!props.containsKey("name") || props.get("name").toString().trim().length() == 0) { 176 String name = node.toString(Verbosity.Name); 177 printProperty("name", name); 178 } 179 printProperty("class", node.getClass().getSimpleName()); 180 181 Block block = nodeToBlock == null || nodeToBlock.isNew(node) ? null : nodeToBlock.get(node); 182 if (block != null) { 183 printProperty("block", Integer.toString(block.getId())); 184 // if (!(node instanceof PhiNode || node instanceof FrameState || node instanceof 185 // ParameterNode) && !block.nodes().contains(node)) { 186 // printProperty("notInOwnBlock", "true"); 187 // } 188 } else { 189 printProperty("block", "noBlock"); 190 noBlockNodes.add(node); 191 } 192 193 Set<Entry<String, Integer>> nodeColors = colors.get(node); 194 if (nodeColors != null) { 195 for (Entry<String, Integer> color : nodeColors) { 196 String name = color.getKey(); 197 Integer value = color.getValue(); 198 printProperty(name, Integer.toString(value)); 199 } 200 } 201 Set<Entry<String, String>> nodeColorStrings = colorsToString.get(node); 202 if (nodeColorStrings != null) { 203 for (Entry<String, String> color : nodeColorStrings) { 204 String name = color.getKey(); 205 String value = color.getValue(); 206 printProperty(name, value); 207 } 208 } 209 Set<String> nodeBits = bits.get(node); 210 if (nodeBits != null) { 211 for (String bit : nodeBits) { 212 printProperty(bit, "true"); 213 } 214 } 215 if (node instanceof BeginNode) { 216 printProperty("shortName", "B"); 217 } else if (node.getClass() == EndNode.class) { 218 printProperty("shortName", "E"); 219 } else if (node instanceof ConstantNode) { 220 ConstantNode cn = (ConstantNode) node; 221 updateStringPropertiesForConstant(props, cn); 222 } 223 if (node.predecessor() != null) { 224 printProperty("hasPredecessor", "true"); 225 } 226 227 for (Entry<Object, Object> entry : props.entrySet()) { 228 String key = entry.getKey().toString(); 229 Object value = entry.getValue(); 230 String valueString; 231 if (value == null) { 232 valueString = "null"; 233 } else { 234 Class<?> type = value.getClass(); 235 if (type.isArray()) { 236 if (!type.getComponentType().isPrimitive()) { 237 valueString = Arrays.toString((Object[]) value); 238 } else if (type.getComponentType() == Integer.TYPE) { 239 valueString = Arrays.toString((int[]) value); 240 } else if (type.getComponentType() == Double.TYPE) { 241 valueString = Arrays.toString((double[]) value); 242 } else { 243 valueString = toString(); 244 } 245 } else { 246 valueString = value.toString(); 247 } 248 } 249 printProperty(key, valueString); 250 } 251 252 endProperties(); 253 endNode(); 254 255 // successors 256 int fromIndex = 0; 257 for (Position position : node.successorPositions()) { 258 Node successor = position.get(node); 259 if (successor != null) { 260 edges.add(new Edge(node.toString(Verbosity.Id), fromIndex, successor.toString(Verbosity.Id), 0, position.getName())); 261 } 262 fromIndex++; 263 } 264 265 // inputs 266 int toIndex = 1; 267 for (Position position : node.inputPositions()) { 268 Node input = position.get(node); 269 if (input != null) { 270 edges.add(new Edge(input.toString(Verbosity.Id), input.successors().count(), node.toString(Verbosity.Id), toIndex, position.getName())); 271 } 272 toIndex++; 273 } 274 } 275 276 return edges; 277 } 278 279 private void printBlock(Graph graph, Block block, NodeMap<Block> nodeToBlock) { 280 beginBlock(Integer.toString(block.getId())); 281 beginSuccessors(); 282 for (Block sux : block.getSuccessors()) { 283 if (sux != null) { 284 printSuccessor(Integer.toString(sux.getId())); 285 } 286 } 287 endSuccessors(); 288 beginBlockNodes(); 289 290 Set<Node> nodes = Node.newSet(); 291 292 if (nodeToBlock != null) { 293 for (Node n : graph.getNodes()) { 294 Block blk = nodeToBlock.isNew(n) ? null : nodeToBlock.get(n); 295 if (blk == block) { 296 nodes.add(n); 297 } 298 } 299 } 300 301 if (nodes.size() > 0) { 302 // if this is the first block: add all locals to this block 303 if (block.getBeginNode() == ((StructuredGraph) graph).start()) { 304 for (Node node : graph.getNodes()) { 305 if (node instanceof ParameterNode) { 306 nodes.add(node); 307 } 308 } 309 } 310 311 Set<Node> snapshot = Node.newSet(nodes); 312 // add all framestates and phis to their blocks 313 for (Node node : snapshot) { 314 if (node instanceof StateSplit && ((StateSplit) node).stateAfter() != null) { 315 nodes.add(((StateSplit) node).stateAfter()); 316 } 317 if (node instanceof AbstractMergeNode) { 318 for (PhiNode phi : ((AbstractMergeNode) node).phis()) { 319 nodes.add(phi); 320 } 321 } 322 } 323 324 for (Node node : nodes) { 325 printBlockNode(node.toString(Verbosity.Id)); 326 } 327 } 328 endBlockNodes(); 329 endBlock(); 330 } 331 332 private void printNoBlock(Set<Node> noBlockNodes) { 333 if (!noBlockNodes.isEmpty()) { 334 beginBlock("noBlock"); 335 beginBlockNodes(); 336 for (Node node : noBlockNodes) { 337 printBlockNode(node.toString(Verbosity.Id)); 338 } 339 endBlockNodes(); 340 endBlock(); 341 } 342 } 343 }