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