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 }