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 }