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