1 /*
   2  * Copyright (c) 2011, 2018, 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 
  24 
  25 package org.graalvm.compiler.printer;
  26 
  27 import static org.graalvm.compiler.graph.Edges.Type.Inputs;
  28 import static org.graalvm.compiler.graph.Edges.Type.Successors;
  29 
  30 import java.io.IOException;
  31 import java.net.URI;
  32 import java.net.URISyntaxException;
  33 import java.util.ArrayList;
  34 import java.util.Arrays;
  35 import java.util.Collection;
  36 import java.util.Collections;
  37 import java.util.LinkedList;
  38 import java.util.List;
  39 import java.util.Map;
  40 
  41 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
  42 import org.graalvm.compiler.bytecode.Bytecode;
  43 import org.graalvm.compiler.core.common.cfg.BlockMap;
  44 import org.graalvm.compiler.debug.DebugContext;
  45 import org.graalvm.compiler.debug.DebugOptions;
  46 import org.graalvm.compiler.graph.CachedGraph;
  47 import org.graalvm.compiler.graph.Edges;
  48 import org.graalvm.compiler.graph.Graph;
  49 import org.graalvm.compiler.graph.InputEdges;
  50 import org.graalvm.compiler.graph.Node;
  51 import org.graalvm.compiler.graph.NodeClass;
  52 import org.graalvm.compiler.graph.NodeMap;
  53 import org.graalvm.compiler.graph.NodeSourcePosition;
  54 import org.graalvm.compiler.graph.SourceLanguagePosition;
  55 import org.graalvm.compiler.nodes.AbstractBeginNode;
  56 import org.graalvm.compiler.nodes.AbstractEndNode;
  57 import org.graalvm.compiler.nodes.AbstractMergeNode;
  58 import org.graalvm.compiler.nodes.ConstantNode;
  59 import org.graalvm.compiler.nodes.ControlSinkNode;
  60 import org.graalvm.compiler.nodes.ControlSplitNode;
  61 import org.graalvm.compiler.nodes.FixedNode;
  62 import org.graalvm.compiler.nodes.PhiNode;
  63 import org.graalvm.compiler.nodes.ProxyNode;
  64 import org.graalvm.compiler.nodes.StructuredGraph;
  65 import org.graalvm.compiler.nodes.VirtualState;
  66 import org.graalvm.compiler.nodes.cfg.Block;
  67 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  68 import org.graalvm.compiler.nodes.util.JavaConstantFormattable;
  69 import org.graalvm.compiler.phases.schedule.SchedulePhase;
  70 import org.graalvm.graphio.GraphBlocks;
  71 import org.graalvm.graphio.GraphElements;
  72 import org.graalvm.graphio.GraphLocations;
  73 import org.graalvm.graphio.GraphOutput;
  74 import org.graalvm.graphio.GraphStructure;
  75 import org.graalvm.graphio.GraphTypes;
  76 
  77 import jdk.vm.ci.meta.JavaType;
  78 import jdk.vm.ci.meta.ResolvedJavaField;
  79 import jdk.vm.ci.meta.ResolvedJavaMethod;
  80 import jdk.vm.ci.meta.Signature;
  81 
  82 public class BinaryGraphPrinter implements
  83                 GraphStructure<BinaryGraphPrinter.GraphInfo, Node, NodeClass<?>, Edges>,
  84                 GraphBlocks<BinaryGraphPrinter.GraphInfo, Block, Node>,
  85                 GraphElements<ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition>,
  86                 GraphLocations<ResolvedJavaMethod, NodeSourcePosition, SourceLanguagePosition>,
  87                 GraphTypes, GraphPrinter {
  88     private final SnippetReflectionProvider snippetReflection;
  89     private final GraphOutput<BinaryGraphPrinter.GraphInfo, ResolvedJavaMethod> output;
  90 
  91     public BinaryGraphPrinter(DebugContext ctx, SnippetReflectionProvider snippetReflection) throws IOException {
  92         // @formatter:off
  93         this.output = ctx.buildOutput(GraphOutput.newBuilder(this).
  94                         protocolVersion(6, 0).
  95                         blocks(this).
  96                         elementsAndLocations(this, this).
  97                         types(this)
  98         );
  99         // @formatter:on
 100         this.snippetReflection = snippetReflection;
 101     }
 102 
 103     @Override
 104     public SnippetReflectionProvider getSnippetReflectionProvider() {
 105         return snippetReflection;
 106     }
 107 
 108     @Override
 109     public void beginGroup(DebugContext debug, String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException {
 110         output.beginGroup(new GraphInfo(debug, null), name, shortName, method, bci, DebugContext.addVersionProperties(properties));
 111     }
 112 
 113     @Override
 114     public void endGroup() throws IOException {
 115         output.endGroup();
 116     }
 117 
 118     @Override
 119     public void close() {
 120         output.close();
 121     }
 122 
 123     @Override
 124     public ResolvedJavaMethod method(Object object) {
 125         if (object instanceof Bytecode) {
 126             return ((Bytecode) object).getMethod();
 127         } else if (object instanceof ResolvedJavaMethod) {
 128             return ((ResolvedJavaMethod) object);
 129         } else {
 130             return null;
 131         }
 132     }
 133 
 134     @Override
 135     public Node node(Object obj) {
 136         return obj instanceof Node ? (Node) obj : null;
 137     }
 138 
 139     @Override
 140     public NodeClass<?> nodeClass(Object obj) {
 141         if (obj instanceof NodeClass<?>) {
 142             return (NodeClass<?>) obj;
 143         }
 144         return null;
 145     }
 146 
 147     @Override
 148     public NodeClass<?> classForNode(Node node) {
 149         return node.getNodeClass();
 150     }
 151 
 152     @Override
 153     public Object nodeClassType(NodeClass<?> node) {
 154         return node.getJavaClass();
 155     }
 156 
 157     @Override
 158     public String nameTemplate(NodeClass<?> nodeClass) {
 159         return nodeClass.getNameTemplate();
 160     }
 161 
 162     @Override
 163     public final GraphInfo graph(GraphInfo currrent, Object obj) {
 164         if (obj instanceof Graph) {
 165             return new GraphInfo(currrent.debug, (Graph) obj);
 166         } else if (obj instanceof CachedGraph) {
 167             return new GraphInfo(currrent.debug, ((CachedGraph<?>) obj).getReadonlyCopy());
 168         } else {
 169             return null;
 170         }
 171     }
 172 
 173     @Override
 174     public int nodeId(Node n) {
 175         return getNodeId(n);
 176     }
 177 
 178     @Override
 179     public Edges portInputs(NodeClass<?> nodeClass) {
 180         return nodeClass.getEdges(Inputs);
 181     }
 182 
 183     @Override
 184     public Edges portOutputs(NodeClass<?> nodeClass) {
 185         return nodeClass.getEdges(Successors);
 186     }
 187 
 188     @SuppressWarnings("deprecation")
 189     private static int getNodeId(Node node) {
 190         return node == null ? -1 : node.getId();
 191     }
 192 
 193     @Override
 194     public List<Node> blockNodes(GraphInfo info, Block block) {
 195         List<Node> nodes = info.blockToNodes.get(block);
 196         if (nodes == null) {
 197             return null;
 198         }
 199         List<Node> extraNodes = new LinkedList<>();
 200         for (Node node : nodes) {
 201             findExtraNodes(node, extraNodes);
 202         }
 203         extraNodes.removeAll(nodes);
 204         extraNodes.addAll(0, nodes);
 205         return extraNodes;
 206     }
 207 
 208     @Override
 209     public int blockId(Block sux) {
 210         return sux.getId();
 211     }
 212 
 213     @Override
 214     public List<Block> blockSuccessors(Block block) {
 215         return Arrays.asList(block.getSuccessors());
 216     }
 217 
 218     @Override
 219     public Iterable<Node> nodes(GraphInfo info) {
 220         return info.graph.getNodes();
 221     }
 222 
 223     @Override
 224     public int nodesCount(GraphInfo info) {
 225         return info.graph.getNodeCount();
 226     }
 227 
 228     @Override
 229     @SuppressWarnings({"unchecked", "rawtypes"})
 230     public void nodeProperties(GraphInfo info, Node node, Map<String, Object> props) {
 231         node.getDebugProperties((Map) props);
 232         NodeMap<Block> nodeToBlocks = info.nodeToBlocks;
 233 
 234         if (nodeToBlocks != null) {
 235             Block block = getBlockForNode(node, nodeToBlocks);
 236             if (block != null) {
 237                 props.put("relativeFrequency", block.getRelativeFrequency());
 238                 props.put("nodeToBlock", block);
 239             }
 240         }
 241 
 242         props.put("nodeCostSize", node.estimatedNodeSize());
 243         props.put("nodeCostCycles", node.estimatedNodeCycles());
 244 
 245         if (nodeToBlocks != null) {
 246             Object block = getBlockForNode(node, nodeToBlocks);
 247             if (block != null) {
 248                 props.put("nodeToBlock", block);
 249             }
 250         }
 251 
 252         if (node instanceof ControlSinkNode) {
 253             props.put("category", "controlSink");
 254         } else if (node instanceof ControlSplitNode) {
 255             props.put("category", "controlSplit");
 256         } else if (node instanceof AbstractMergeNode) {
 257             props.put("category", "merge");
 258         } else if (node instanceof AbstractBeginNode) {
 259             props.put("category", "begin");
 260         } else if (node instanceof AbstractEndNode) {
 261             props.put("category", "end");
 262         } else if (node instanceof FixedNode) {
 263             props.put("category", "fixed");
 264         } else if (node instanceof VirtualState) {
 265             props.put("category", "state");
 266         } else if (node instanceof PhiNode) {
 267             props.put("category", "phi");
 268         } else if (node instanceof ProxyNode) {
 269             props.put("category", "proxy");
 270         } else {
 271             if (node instanceof ConstantNode) {
 272                 ConstantNode cn = (ConstantNode) node;
 273                 updateStringPropertiesForConstant((Map) props, cn);
 274             }
 275             props.put("category", "floating");
 276         }
 277         if (getSnippetReflectionProvider() != null) {
 278             for (Map.Entry<String, Object> prop : props.entrySet()) {
 279                 if (prop.getValue() instanceof JavaConstantFormattable) {
 280                     props.put(prop.getKey(), ((JavaConstantFormattable) prop.getValue()).format(this));
 281                 }
 282             }
 283         }
 284     }
 285 
 286     private Block getBlockForNode(Node node, NodeMap<Block> nodeToBlocks) {
 287         if (nodeToBlocks.isNew(node)) {
 288             return null;
 289         } else {
 290             Block block = nodeToBlocks.get(node);
 291             if (block != null) {
 292                 return block;
 293             } else if (node instanceof PhiNode) {
 294                 return getBlockForNode(((PhiNode) node).merge(), nodeToBlocks);
 295             }
 296         }
 297         return null;
 298     }
 299 
 300     private static void findExtraNodes(Node node, Collection<? super Node> extraNodes) {
 301         if (node instanceof AbstractMergeNode) {
 302             AbstractMergeNode merge = (AbstractMergeNode) node;
 303             for (PhiNode phi : merge.phis()) {
 304                 extraNodes.add(phi);
 305             }
 306         }
 307     }
 308 
 309     @Override
 310     public boolean nodeHasPredecessor(Node node) {
 311         return node.predecessor() != null;
 312     }
 313 
 314     @Override
 315     public List<Block> blocks(GraphInfo graph) {
 316         return graph.blocks;
 317     }
 318 
 319     @Override
 320     public void print(DebugContext debug, Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException {
 321         output.print(new GraphInfo(debug, graph), properties, id, format, args);
 322     }
 323 
 324     @Override
 325     public int portSize(Edges port) {
 326         return port.getCount();
 327     }
 328 
 329     @Override
 330     public boolean edgeDirect(Edges port, int index) {
 331         return index < port.getDirectCount();
 332     }
 333 
 334     @Override
 335     public String edgeName(Edges port, int index) {
 336         return port.getName(index);
 337     }
 338 
 339     @Override
 340     public Object edgeType(Edges port, int index) {
 341         return ((InputEdges) port).getInputType(index);
 342     }
 343 
 344     @Override
 345     public Collection<? extends Node> edgeNodes(GraphInfo graph, Node node, Edges port, int i) {
 346         if (i < port.getDirectCount()) {
 347             Node single = Edges.getNode(node, port.getOffsets(), i);
 348             return Collections.singletonList(single);
 349         } else {
 350             return Edges.getNodeList(node, port.getOffsets(), i);
 351         }
 352     }
 353 
 354     @Override
 355     public Object enumClass(Object enumValue) {
 356         if (enumValue instanceof Enum) {
 357             return enumValue.getClass();
 358         }
 359         return null;
 360     }
 361 
 362     @Override
 363     public int enumOrdinal(Object obj) {
 364         if (obj instanceof Enum<?>) {
 365             return ((Enum<?>) obj).ordinal();
 366         }
 367         return -1;
 368     }
 369 
 370     @SuppressWarnings("unchecked")
 371     @Override
 372     public String[] enumTypeValues(Object clazz) {
 373         if (clazz instanceof Class<?>) {
 374             Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) clazz;
 375             Enum<?>[] constants = enumClass.getEnumConstants();
 376             if (constants != null) {
 377                 String[] names = new String[constants.length];
 378                 for (int i = 0; i < constants.length; i++) {
 379                     names[i] = constants[i].name();
 380                 }
 381                 return names;
 382             }
 383         }
 384         return null;
 385     }
 386 
 387     @Override
 388     public String typeName(Object obj) {
 389         if (obj instanceof Class<?>) {
 390             return ((Class<?>) obj).getName();
 391         }
 392         if (obj instanceof JavaType) {
 393             return ((JavaType) obj).toJavaName();
 394         }
 395         return null;
 396     }
 397 
 398     @Override
 399     public byte[] methodCode(ResolvedJavaMethod method) {
 400         return method.getCode();
 401     }
 402 
 403     @Override
 404     public int methodModifiers(ResolvedJavaMethod method) {
 405         return method.getModifiers();
 406     }
 407 
 408     @Override
 409     public Signature methodSignature(ResolvedJavaMethod method) {
 410         return method.getSignature();
 411     }
 412 
 413     @Override
 414     public String methodName(ResolvedJavaMethod method) {
 415         return method.getName();
 416     }
 417 
 418     @Override
 419     public Object methodDeclaringClass(ResolvedJavaMethod method) {
 420         return method.getDeclaringClass();
 421     }
 422 
 423     @Override
 424     public int fieldModifiers(ResolvedJavaField field) {
 425         return field.getModifiers();
 426     }
 427 
 428     @Override
 429     public String fieldTypeName(ResolvedJavaField field) {
 430         return field.getType().toJavaName();
 431     }
 432 
 433     @Override
 434     public String fieldName(ResolvedJavaField field) {
 435         return field.getName();
 436     }
 437 
 438     @Override
 439     public Object fieldDeclaringClass(ResolvedJavaField field) {
 440         return field.getDeclaringClass();
 441     }
 442 
 443     @Override
 444     public ResolvedJavaField field(Object object) {
 445         if (object instanceof ResolvedJavaField) {
 446             return (ResolvedJavaField) object;
 447         }
 448         return null;
 449     }
 450 
 451     @Override
 452     public Signature signature(Object object) {
 453         if (object instanceof Signature) {
 454             return (Signature) object;
 455         }
 456         return null;
 457     }
 458 
 459     @Override
 460     public int signatureParameterCount(Signature signature) {
 461         return signature.getParameterCount(false);
 462     }
 463 
 464     @Override
 465     public String signatureParameterTypeName(Signature signature, int index) {
 466         return signature.getParameterType(index, null).getName();
 467     }
 468 
 469     @Override
 470     public String signatureReturnTypeName(Signature signature) {
 471         return signature.getReturnType(null).getName();
 472     }
 473 
 474     @Override
 475     public NodeSourcePosition nodeSourcePosition(Object object) {
 476         if (object instanceof NodeSourcePosition) {
 477             return (NodeSourcePosition) object;
 478         }
 479         return null;
 480     }
 481 
 482     @Override
 483     public ResolvedJavaMethod nodeSourcePositionMethod(NodeSourcePosition pos) {
 484         return pos.getMethod();
 485     }
 486 
 487     @Override
 488     public NodeSourcePosition nodeSourcePositionCaller(NodeSourcePosition pos) {
 489         return pos.getCaller();
 490     }
 491 
 492     @Override
 493     public int nodeSourcePositionBCI(NodeSourcePosition pos) {
 494         return pos.getBCI();
 495     }
 496 
 497     @Override
 498     public StackTraceElement methodStackTraceElement(ResolvedJavaMethod method, int bci, NodeSourcePosition pos) {
 499         return method.asStackTraceElement(bci);
 500     }
 501 
 502     @Override
 503     public Iterable<SourceLanguagePosition> methodLocation(ResolvedJavaMethod method, int bci, NodeSourcePosition pos) {
 504         StackTraceElement e = methodStackTraceElement(method, bci, pos);
 505         class JavaSourcePosition implements SourceLanguagePosition {
 506 
 507             @Override
 508             public String toShortString() {
 509                 return e.toString();
 510             }
 511 
 512             @Override
 513             public int getOffsetEnd() {
 514                 return -1;
 515             }
 516 
 517             @Override
 518             public int getOffsetStart() {
 519                 return -1;
 520             }
 521 
 522             @Override
 523             public int getLineNumber() {
 524                 return e.getLineNumber();
 525             }
 526 
 527             @Override
 528             public URI getURI() {
 529                 String path = e.getFileName();
 530                 try {
 531                     return new URI(null, null, path == null ? "(Unknown Source)" : path, null);
 532                 } catch (URISyntaxException ex) {
 533                     throw new IllegalArgumentException(ex);
 534                 }
 535             }
 536 
 537             @Override
 538             public String getLanguage() {
 539                 return "Java";
 540             }
 541         }
 542 
 543         List<SourceLanguagePosition> arr = new ArrayList<>();
 544         arr.add(new JavaSourcePosition());
 545         NodeSourcePosition at = pos;
 546         while (at != null) {
 547             SourceLanguagePosition cur = at.getSourceLanguage();
 548             if (cur != null) {
 549                 arr.add(cur);
 550             }
 551             at = at.getCaller();
 552         }
 553         return arr;
 554     }
 555 
 556     @Override
 557     public String locationLanguage(SourceLanguagePosition location) {
 558         return location.getLanguage();
 559     }
 560 
 561     @Override
 562     public URI locationURI(SourceLanguagePosition location) {
 563         return location.getURI();
 564     }
 565 
 566     @Override
 567     public int locationLineNumber(SourceLanguagePosition location) {
 568         return location.getLineNumber();
 569     }
 570 
 571     @Override
 572     public int locationOffsetStart(SourceLanguagePosition location) {
 573         return location.getOffsetStart();
 574     }
 575 
 576     @Override
 577     public int locationOffsetEnd(SourceLanguagePosition location) {
 578         return location.getOffsetEnd();
 579     }
 580 
 581     static final class GraphInfo {
 582         final DebugContext debug;
 583         final Graph graph;
 584         final ControlFlowGraph cfg;
 585         final BlockMap<List<Node>> blockToNodes;
 586         final NodeMap<Block> nodeToBlocks;
 587         final List<Block> blocks;
 588 
 589         private GraphInfo(DebugContext debug, Graph graph) {
 590             this.debug = debug;
 591             this.graph = graph;
 592             StructuredGraph.ScheduleResult scheduleResult = null;
 593             if (graph instanceof StructuredGraph) {
 594 
 595                 StructuredGraph structuredGraph = (StructuredGraph) graph;
 596                 scheduleResult = structuredGraph.getLastSchedule();
 597                 if (scheduleResult == null) {
 598 
 599                     // Also provide a schedule when an error occurs
 600                     if (DebugOptions.PrintGraphWithSchedule.getValue(graph.getOptions()) || debug.contextLookup(Throwable.class) != null) {
 601                         try {
 602                             SchedulePhase schedule = new SchedulePhase(graph.getOptions());
 603                             schedule.apply(structuredGraph);
 604                             scheduleResult = structuredGraph.getLastSchedule();
 605                         } catch (Throwable t) {
 606                         }
 607                     }
 608 
 609                 }
 610             }
 611             cfg = scheduleResult == null ? debug.contextLookup(ControlFlowGraph.class) : scheduleResult.getCFG();
 612             blockToNodes = scheduleResult == null ? null : scheduleResult.getBlockToNodesMap();
 613             nodeToBlocks = scheduleResult == null ? null : scheduleResult.getNodeToBlockMap();
 614             blocks = cfg == null ? null : Arrays.asList(cfg.getBlocks());
 615         }
 616     }
 617 
 618 }