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 }