/* * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.graalvm.compiler.salver.dumper; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.graalvm.compiler.core.common.Fields; import org.graalvm.compiler.core.common.cfg.BlockMap; import org.graalvm.compiler.debug.Debug; import org.graalvm.compiler.debug.Debug.Scope; import org.graalvm.compiler.debug.GraalDebugConfig.Options; import org.graalvm.compiler.graph.Edges; import org.graalvm.compiler.graph.Edges.Type; import org.graalvm.compiler.graph.Graph; import org.graalvm.compiler.graph.InputEdges; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.graph.NodeList; import org.graalvm.compiler.graph.NodeMap; import org.graalvm.compiler.graph.iterators.NodeIterable; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.AbstractEndNode; import org.graalvm.compiler.nodes.AbstractMergeNode; import org.graalvm.compiler.nodes.ControlSinkNode; import org.graalvm.compiler.nodes.ControlSplitNode; import org.graalvm.compiler.nodes.FixedNode; import org.graalvm.compiler.nodes.PhiNode; import org.graalvm.compiler.nodes.ProxyNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; import org.graalvm.compiler.nodes.VirtualState; import org.graalvm.compiler.nodes.cfg.Block; import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; import org.graalvm.compiler.phases.schedule.SchedulePhase; import org.graalvm.compiler.salver.data.DataDict; import org.graalvm.compiler.salver.data.DataList; public class GraphDumper extends AbstractMethodScopeDumper { public static final String EVENT_NAMESPACE = "graal/graph"; private static final Map, String> nodeClassCategoryMap; static { nodeClassCategoryMap = new LinkedHashMap<>(); nodeClassCategoryMap.put(ControlSinkNode.class, "ControlSink"); nodeClassCategoryMap.put(ControlSplitNode.class, "ControlSplit"); nodeClassCategoryMap.put(AbstractMergeNode.class, "Merge"); nodeClassCategoryMap.put(AbstractBeginNode.class, "Begin"); nodeClassCategoryMap.put(AbstractEndNode.class, "End"); nodeClassCategoryMap.put(FixedNode.class, "Fixed"); nodeClassCategoryMap.put(VirtualState.class, "State"); nodeClassCategoryMap.put(PhiNode.class, "Phi"); nodeClassCategoryMap.put(ProxyNode.class, "Proxy"); // nodeClassCategoryMap.put(Node.class, "Floating"); } @Override public void beginDump() throws IOException { beginDump(EVENT_NAMESPACE); } @SuppressWarnings("try") public void dump(Graph graph, String msg) throws IOException { resolveMethodContext(); try (Scope s = Debug.sandbox(getClass().getSimpleName(), null)) { processGraph(graph, msg); } catch (IOException e) { throw e; } catch (Throwable e) { throw Debug.handle(e); } } private void processGraph(Graph graph, String name) throws IOException { ScheduleResult scheduleResult = null; if (graph instanceof StructuredGraph) { StructuredGraph structuredGraph = (StructuredGraph) graph; scheduleResult = structuredGraph.getLastSchedule(); if (scheduleResult == null) { // Also provide a schedule when an error occurs if (Options.PrintIdealGraphSchedule.getValue() || Debug.contextLookup(Throwable.class) != null) { try { SchedulePhase schedule = new SchedulePhase(); schedule.apply(structuredGraph); } catch (Throwable t) { } } } } DataDict dataDict = new DataDict(); dataDict.put("id", nextItemId()); dataDict.put("name", name); DataDict graphDict = new DataDict(); dataDict.put("graph", graphDict); processNodes(graphDict, graph.getNodes(), scheduleResult); if (scheduleResult != null) { ControlFlowGraph cfg = scheduleResult.getCFG(); if (cfg != null) { List blocks = Arrays.asList(cfg.getBlocks()); processBlocks(graphDict, blocks, scheduleResult); } } serializeAndFlush(createEventDictWithId("graph", dataDict)); } private static void processNodes(DataDict graphDict, NodeIterable nodes, ScheduleResult schedule) { Map, Integer> classMap = new HashMap<>(); DataList classList = new DataList(); graphDict.put("classes", classList); DataList nodeList = new DataList(); graphDict.put("nodes", nodeList); DataList edgeList = new DataList(); graphDict.put("edges", edgeList); for (Node node : nodes) { NodeClass nodeClass = node.getNodeClass(); DataDict nodeDict = new DataDict(); nodeList.add(nodeDict); nodeDict.put("id", getNodeId(node)); nodeDict.put("class", getNodeClassId(classMap, classList, nodeClass)); if (schedule != null) { processNodeSchedule(nodeDict, node, schedule); } DataDict propertyDict = new DataDict(); node.getDebugProperties(propertyDict); if (!propertyDict.isEmpty()) { nodeDict.put("properties", propertyDict); } appendEdges(edgeList, node, Type.Inputs); appendEdges(edgeList, node, Type.Successors); } } private static void processNodeSchedule(DataDict nodeDict, Node node, ScheduleResult schedule) { NodeMap nodeToBlock = schedule.getNodeToBlockMap(); if (nodeToBlock != null) { if (nodeToBlock.isNew(node)) { nodeDict.put("block", -1); } else { Block block = nodeToBlock.get(node); if (block != null) { nodeDict.put("block", block.getId()); } } } ControlFlowGraph cfg = schedule.getCFG(); if (cfg != null && Options.PrintGraphProbabilities.getValue() && node instanceof FixedNode) { try { nodeDict.put("probability", cfg.blockFor(node).probability()); } catch (Throwable t) { nodeDict.put("probability", t); } } } private static void processBlocks(DataDict graphDict, List blocks, ScheduleResult schedule) { BlockMap> blockToNodes = schedule.getBlockToNodesMap(); DataList blockList = new DataList(); graphDict.put("blocks", blockList); for (Block block : blocks) { List nodes = blockToNodes.get(block); if (nodes != null) { DataDict blockDict = new DataDict(); blockList.add(blockDict); blockDict.put("id", block.getId()); DataList nodeList = new DataList(); blockDict.put("nodes", nodeList); for (Node node : nodes) { nodeList.add(getNodeId(node)); } Block[] successors = block.getSuccessors(); if (successors != null && successors.length > 0) { DataList successorList = new DataList(); blockDict.put("successors", successorList); for (Block successor : successors) { successorList.add(successor.getId()); } } } } } private static void appendEdges(DataList edgeList, Node node, Edges.Type type) { NodeClass nodeClass = node.getNodeClass(); Edges edges = nodeClass.getEdges(type); final long[] curOffsets = edges.getOffsets(); for (int i = 0; i < edges.getDirectCount(); i++) { Node other = Edges.getNode(node, curOffsets, i); if (other != null) { DataDict edgeDict = new DataDict(); DataDict nodeDict = new DataDict(); nodeDict.put("node", getNodeId(node)); nodeDict.put("field", edges.getName(i)); edgeDict.put("from", type == Type.Inputs ? getNodeId(other) : nodeDict); edgeDict.put("to", type == Type.Inputs ? nodeDict : getNodeId(other)); edgeList.add(edgeDict); } } for (int i = edges.getDirectCount(); i < edges.getCount(); i++) { NodeList list = Edges.getNodeList(node, curOffsets, i); if (list != null) { for (int index = 0; index < list.size(); index++) { Node other = list.get(index); if (other != null) { DataDict edgeDict = new DataDict(); DataDict nodeDict = new DataDict(); nodeDict.put("node", getNodeId(node)); nodeDict.put("field", edges.getName(i)); nodeDict.put("index", index); edgeDict.put("from", type == Type.Inputs ? getNodeId(other) : nodeDict); edgeDict.put("to", type == Type.Inputs ? nodeDict : getNodeId(other)); edgeList.add(edgeDict); } } } } } @SuppressWarnings("deprecation") private static int getNodeId(Node node) { return node != null ? node.getId() : -1; } private static int getNodeClassId(Map, Integer> classMap, DataList classList, NodeClass nodeClass) { if (classMap.containsKey(nodeClass)) { return classMap.get(nodeClass); } int classId = classMap.size(); classMap.put(nodeClass, classId); Class javaClass = nodeClass.getJavaClass(); DataDict classDict = new DataDict(); classList.add(classDict); classDict.put("id", classId); classDict.put("name", nodeClass.getNameTemplate()); classDict.put("jtype", javaClass.getName()); String category = getNodeClassCategory(javaClass); if (category != null) { classDict.put("category", category); } Object propertyInfo = getPropertyInfo(nodeClass); if (propertyInfo != null) { classDict.put("properties", propertyInfo); } Object inputInfo = getEdgeInfo(nodeClass, Type.Inputs); if (inputInfo != null) { classDict.put("inputs", inputInfo); } Object successorInfo = getEdgeInfo(nodeClass, Type.Successors); if (successorInfo != null) { classDict.put("successors", successorInfo); } return classId; } private static DataDict getPropertyInfo(NodeClass nodeClass) { Fields properties = nodeClass.getData(); if (properties.getCount() > 0) { DataDict propertyInfoDict = new DataDict(); for (int i = 0; i < properties.getCount(); i++) { DataDict propertyDict = new DataDict(); String name = properties.getName(i); propertyDict.put("name", name); propertyDict.put("jtype", properties.getType(i).getName()); propertyInfoDict.put(name, propertyDict); } return propertyInfoDict; } return null; } private static DataDict getEdgeInfo(NodeClass nodeClass, Edges.Type type) { DataDict edgeInfoDict = new DataDict(); Edges edges = nodeClass.getEdges(type); for (int i = 0; i < edges.getCount(); i++) { DataDict edgeDict = new DataDict(); String name = edges.getName(i); Class fieldClass = edges.getType(i); edgeDict.put("name", name); edgeDict.put("jtype", fieldClass.getName()); if (NodeList.class.isAssignableFrom(fieldClass)) { edgeDict.put("isList", true); } if (type == Type.Inputs) { InputEdges inputEdges = ((InputEdges) edges); edgeDict.put("type", inputEdges.getInputType(i)); if (inputEdges.isOptional(i)) { edgeDict.put("isOptional", true); } } edgeInfoDict.put(name, edgeDict); } return edgeInfoDict.isEmpty() ? null : edgeInfoDict; } private static String getNodeClassCategory(Class clazz) { for (Map.Entry, String> entry : nodeClassCategoryMap.entrySet()) { if (entry.getKey().isAssignableFrom(clazz)) { return entry.getValue(); } } return null; } }