1 /*
   2  * Copyright (c) 2015, 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.salver.dumper;
  24 
  25 import java.io.IOException;
  26 import java.util.Arrays;
  27 import java.util.HashMap;
  28 import java.util.LinkedHashMap;
  29 import java.util.List;
  30 import java.util.Map;
  31 
  32 import org.graalvm.compiler.core.common.Fields;
  33 import org.graalvm.compiler.core.common.cfg.BlockMap;
  34 import org.graalvm.compiler.debug.Debug;
  35 import org.graalvm.compiler.debug.Debug.Scope;
  36 import org.graalvm.compiler.debug.GraalDebugConfig.Options;
  37 import org.graalvm.compiler.graph.Edges;
  38 import org.graalvm.compiler.graph.Edges.Type;
  39 import org.graalvm.compiler.graph.Graph;
  40 import org.graalvm.compiler.graph.InputEdges;
  41 import org.graalvm.compiler.graph.Node;
  42 import org.graalvm.compiler.graph.NodeClass;
  43 import org.graalvm.compiler.graph.NodeList;
  44 import org.graalvm.compiler.graph.NodeMap;
  45 import org.graalvm.compiler.graph.iterators.NodeIterable;
  46 import org.graalvm.compiler.nodes.AbstractBeginNode;
  47 import org.graalvm.compiler.nodes.AbstractEndNode;
  48 import org.graalvm.compiler.nodes.AbstractMergeNode;
  49 import org.graalvm.compiler.nodes.ControlSinkNode;
  50 import org.graalvm.compiler.nodes.ControlSplitNode;
  51 import org.graalvm.compiler.nodes.FixedNode;
  52 import org.graalvm.compiler.nodes.PhiNode;
  53 import org.graalvm.compiler.nodes.ProxyNode;
  54 import org.graalvm.compiler.nodes.StructuredGraph;
  55 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
  56 import org.graalvm.compiler.nodes.VirtualState;
  57 import org.graalvm.compiler.nodes.cfg.Block;
  58 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  59 import org.graalvm.compiler.phases.schedule.SchedulePhase;
  60 import org.graalvm.compiler.salver.data.DataDict;
  61 import org.graalvm.compiler.salver.data.DataList;
  62 
  63 public class GraphDumper extends AbstractMethodScopeDumper {
  64 
  65     public static final String EVENT_NAMESPACE = "graal/graph";
  66 
  67     private static final Map<Class<?>, String> nodeClassCategoryMap;
  68 
  69     static {
  70         nodeClassCategoryMap = new LinkedHashMap<>();
  71         nodeClassCategoryMap.put(ControlSinkNode.class, "ControlSink");
  72         nodeClassCategoryMap.put(ControlSplitNode.class, "ControlSplit");
  73         nodeClassCategoryMap.put(AbstractMergeNode.class, "Merge");
  74         nodeClassCategoryMap.put(AbstractBeginNode.class, "Begin");
  75         nodeClassCategoryMap.put(AbstractEndNode.class, "End");
  76         nodeClassCategoryMap.put(FixedNode.class, "Fixed");
  77         nodeClassCategoryMap.put(VirtualState.class, "State");
  78         nodeClassCategoryMap.put(PhiNode.class, "Phi");
  79         nodeClassCategoryMap.put(ProxyNode.class, "Proxy");
  80         // nodeClassCategoryMap.put(Node.class, "Floating");
  81     }
  82 
  83     @Override
  84     public void beginDump() throws IOException {
  85         beginDump(EVENT_NAMESPACE);
  86     }
  87 
  88     @SuppressWarnings("try")
  89     public void dump(Graph graph, String msg) throws IOException {
  90         resolveMethodContext();
  91 
  92         try (Scope s = Debug.sandbox(getClass().getSimpleName(), null)) {
  93             processGraph(graph, msg);
  94         } catch (IOException e) {
  95             throw e;
  96         } catch (Throwable e) {
  97             throw Debug.handle(e);
  98         }
  99     }
 100 
 101     private void processGraph(Graph graph, String name) throws IOException {
 102 
 103         ScheduleResult scheduleResult = null;
 104         if (graph instanceof StructuredGraph) {
 105 
 106             StructuredGraph structuredGraph = (StructuredGraph) graph;
 107             scheduleResult = structuredGraph.getLastSchedule();
 108             if (scheduleResult == null) {
 109 
 110                 // Also provide a schedule when an error occurs
 111                 if (Options.PrintIdealGraphSchedule.getValue() || Debug.contextLookup(Throwable.class) != null) {
 112                     try {
 113                         SchedulePhase schedule = new SchedulePhase();
 114                         schedule.apply(structuredGraph);
 115                     } catch (Throwable t) {
 116                     }
 117                 }
 118 
 119             }
 120         }
 121 
 122         DataDict dataDict = new DataDict();
 123         dataDict.put("id", nextItemId());
 124         dataDict.put("name", name);
 125 
 126         DataDict graphDict = new DataDict();
 127         dataDict.put("graph", graphDict);
 128 
 129         processNodes(graphDict, graph.getNodes(), scheduleResult);
 130 
 131         if (scheduleResult != null) {
 132             ControlFlowGraph cfg = scheduleResult.getCFG();
 133             if (cfg != null) {
 134                 List<Block> blocks = Arrays.asList(cfg.getBlocks());
 135                 processBlocks(graphDict, blocks, scheduleResult);
 136             }
 137         }
 138         serializeAndFlush(createEventDictWithId("graph", dataDict));
 139     }
 140 
 141     private static void processNodes(DataDict graphDict, NodeIterable<Node> nodes, ScheduleResult schedule) {
 142         Map<NodeClass<?>, Integer> classMap = new HashMap<>();
 143 
 144         DataList classList = new DataList();
 145         graphDict.put("classes", classList);
 146 
 147         DataList nodeList = new DataList();
 148         graphDict.put("nodes", nodeList);
 149 
 150         DataList edgeList = new DataList();
 151         graphDict.put("edges", edgeList);
 152 
 153         for (Node node : nodes) {
 154             NodeClass<?> nodeClass = node.getNodeClass();
 155 
 156             DataDict nodeDict = new DataDict();
 157             nodeList.add(nodeDict);
 158 
 159             nodeDict.put("id", getNodeId(node));
 160             nodeDict.put("class", getNodeClassId(classMap, classList, nodeClass));
 161 
 162             if (schedule != null) {
 163                 processNodeSchedule(nodeDict, node, schedule);
 164             }
 165 
 166             DataDict propertyDict = new DataDict();
 167             node.getDebugProperties(propertyDict);
 168 
 169             if (!propertyDict.isEmpty()) {
 170                 nodeDict.put("properties", propertyDict);
 171             }
 172 
 173             appendEdges(edgeList, node, Type.Inputs);
 174             appendEdges(edgeList, node, Type.Successors);
 175         }
 176     }
 177 
 178     private static void processNodeSchedule(DataDict nodeDict, Node node, ScheduleResult schedule) {
 179         NodeMap<Block> nodeToBlock = schedule.getNodeToBlockMap();
 180         if (nodeToBlock != null) {
 181             if (nodeToBlock.isNew(node)) {
 182                 nodeDict.put("block", -1);
 183             } else {
 184                 Block block = nodeToBlock.get(node);
 185                 if (block != null) {
 186                     nodeDict.put("block", block.getId());
 187                 }
 188             }
 189         }
 190 
 191         ControlFlowGraph cfg = schedule.getCFG();
 192         if (cfg != null && Options.PrintGraphProbabilities.getValue() && node instanceof FixedNode) {
 193             try {
 194                 nodeDict.put("probability", cfg.blockFor(node).probability());
 195             } catch (Throwable t) {
 196                 nodeDict.put("probability", t);
 197             }
 198         }
 199     }
 200 
 201     private static void processBlocks(DataDict graphDict, List<Block> blocks, ScheduleResult schedule) {
 202         BlockMap<List<Node>> blockToNodes = schedule.getBlockToNodesMap();
 203         DataList blockList = new DataList();
 204         graphDict.put("blocks", blockList);
 205 
 206         for (Block block : blocks) {
 207             List<Node> nodes = blockToNodes.get(block);
 208             if (nodes != null) {
 209                 DataDict blockDict = new DataDict();
 210                 blockList.add(blockDict);
 211 
 212                 blockDict.put("id", block.getId());
 213 
 214                 DataList nodeList = new DataList();
 215                 blockDict.put("nodes", nodeList);
 216 
 217                 for (Node node : nodes) {
 218                     nodeList.add(getNodeId(node));
 219                 }
 220 
 221                 Block[] successors = block.getSuccessors();
 222                 if (successors != null && successors.length > 0) {
 223                     DataList successorList = new DataList();
 224                     blockDict.put("successors", successorList);
 225                     for (Block successor : successors) {
 226                         successorList.add(successor.getId());
 227                     }
 228                 }
 229             }
 230         }
 231     }
 232 
 233     private static void appendEdges(DataList edgeList, Node node, Edges.Type type) {
 234         NodeClass<?> nodeClass = node.getNodeClass();
 235 
 236         Edges edges = nodeClass.getEdges(type);
 237         final long[] curOffsets = edges.getOffsets();
 238 
 239         for (int i = 0; i < edges.getDirectCount(); i++) {
 240             Node other = Edges.getNode(node, curOffsets, i);
 241             if (other != null) {
 242                 DataDict edgeDict = new DataDict();
 243 
 244                 DataDict nodeDict = new DataDict();
 245                 nodeDict.put("node", getNodeId(node));
 246                 nodeDict.put("field", edges.getName(i));
 247 
 248                 edgeDict.put("from", type == Type.Inputs ? getNodeId(other) : nodeDict);
 249                 edgeDict.put("to", type == Type.Inputs ? nodeDict : getNodeId(other));
 250                 edgeList.add(edgeDict);
 251             }
 252         }
 253         for (int i = edges.getDirectCount(); i < edges.getCount(); i++) {
 254             NodeList<Node> list = Edges.getNodeList(node, curOffsets, i);
 255             if (list != null) {
 256                 for (int index = 0; index < list.size(); index++) {
 257                     Node other = list.get(index);
 258                     if (other != null) {
 259                         DataDict edgeDict = new DataDict();
 260 
 261                         DataDict nodeDict = new DataDict();
 262                         nodeDict.put("node", getNodeId(node));
 263                         nodeDict.put("field", edges.getName(i));
 264                         nodeDict.put("index", index);
 265 
 266                         edgeDict.put("from", type == Type.Inputs ? getNodeId(other) : nodeDict);
 267                         edgeDict.put("to", type == Type.Inputs ? nodeDict : getNodeId(other));
 268                         edgeList.add(edgeDict);
 269                     }
 270                 }
 271             }
 272         }
 273     }
 274 
 275     @SuppressWarnings("deprecation")
 276     private static int getNodeId(Node node) {
 277         return node != null ? node.getId() : -1;
 278     }
 279 
 280     private static int getNodeClassId(Map<NodeClass<?>, Integer> classMap, DataList classList, NodeClass<?> nodeClass) {
 281         if (classMap.containsKey(nodeClass)) {
 282             return classMap.get(nodeClass);
 283         }
 284         int classId = classMap.size();
 285         classMap.put(nodeClass, classId);
 286 
 287         Class<?> javaClass = nodeClass.getJavaClass();
 288 
 289         DataDict classDict = new DataDict();
 290         classList.add(classDict);
 291 
 292         classDict.put("id", classId);
 293         classDict.put("name", nodeClass.getNameTemplate());
 294         classDict.put("jtype", javaClass.getName());
 295 
 296         String category = getNodeClassCategory(javaClass);
 297         if (category != null) {
 298             classDict.put("category", category);
 299         }
 300 
 301         Object propertyInfo = getPropertyInfo(nodeClass);
 302         if (propertyInfo != null) {
 303             classDict.put("properties", propertyInfo);
 304         }
 305 
 306         Object inputInfo = getEdgeInfo(nodeClass, Type.Inputs);
 307         if (inputInfo != null) {
 308             classDict.put("inputs", inputInfo);
 309         }
 310         Object successorInfo = getEdgeInfo(nodeClass, Type.Successors);
 311         if (successorInfo != null) {
 312             classDict.put("successors", successorInfo);
 313         }
 314         return classId;
 315     }
 316 
 317     private static DataDict getPropertyInfo(NodeClass<?> nodeClass) {
 318         Fields properties = nodeClass.getData();
 319         if (properties.getCount() > 0) {
 320             DataDict propertyInfoDict = new DataDict();
 321             for (int i = 0; i < properties.getCount(); i++) {
 322                 DataDict propertyDict = new DataDict();
 323                 String name = properties.getName(i);
 324                 propertyDict.put("name", name);
 325                 propertyDict.put("jtype", properties.getType(i).getName());
 326                 propertyInfoDict.put(name, propertyDict);
 327             }
 328             return propertyInfoDict;
 329         }
 330         return null;
 331     }
 332 
 333     private static DataDict getEdgeInfo(NodeClass<?> nodeClass, Edges.Type type) {
 334         DataDict edgeInfoDict = new DataDict();
 335         Edges edges = nodeClass.getEdges(type);
 336         for (int i = 0; i < edges.getCount(); i++) {
 337             DataDict edgeDict = new DataDict();
 338             String name = edges.getName(i);
 339             Class<?> fieldClass = edges.getType(i);
 340             edgeDict.put("name", name);
 341             edgeDict.put("jtype", fieldClass.getName());
 342             if (NodeList.class.isAssignableFrom(fieldClass)) {
 343                 edgeDict.put("isList", true);
 344             }
 345             if (type == Type.Inputs) {
 346                 InputEdges inputEdges = ((InputEdges) edges);
 347                 edgeDict.put("type", inputEdges.getInputType(i));
 348                 if (inputEdges.isOptional(i)) {
 349                     edgeDict.put("isOptional", true);
 350                 }
 351             }
 352             edgeInfoDict.put(name, edgeDict);
 353         }
 354         return edgeInfoDict.isEmpty() ? null : edgeInfoDict;
 355     }
 356 
 357     private static String getNodeClassCategory(Class<?> clazz) {
 358         for (Map.Entry<Class<?>, String> entry : nodeClassCategoryMap.entrySet()) {
 359             if (entry.getKey().isAssignableFrom(clazz)) {
 360                 return entry.getValue();
 361             }
 362         }
 363         return null;
 364     }
 365 }