1 /*
   2  * Copyright (c) 2011, 2014, 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.GraphOutput;
  73 import org.graalvm.graphio.GraphStructure;
  74 import org.graalvm.graphio.GraphTypes;
  75 
  76 import jdk.vm.ci.meta.JavaType;
  77 import jdk.vm.ci.meta.ResolvedJavaField;
  78 import jdk.vm.ci.meta.ResolvedJavaMethod;
  79 import jdk.vm.ci.meta.Signature;
  80 import org.graalvm.graphio.GraphLocations;
  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         Graph graph = info.graph;
 233         ControlFlowGraph cfg = info.cfg;
 234         NodeMap<Block> nodeToBlocks = info.nodeToBlocks;
 235         if (cfg != null && DebugOptions.PrintGraphProbabilities.getValue(graph.getOptions()) && node instanceof FixedNode) {
 236             try {
 237                 props.put("probability", cfg.blockFor(node).probability());
 238             } catch (Throwable t) {
 239                 props.put("probability", 0.0);
 240                 props.put("probability-exception", t);
 241             }
 242         }
 243 
 244         try {
 245             props.put("NodeCost-Size", node.estimatedNodeSize());
 246             props.put("NodeCost-Cycles", node.estimatedNodeCycles());
 247         } catch (Throwable t) {
 248             props.put("node-cost-exception", t.getMessage());
 249         }
 250 
 251         if (nodeToBlocks != null) {
 252             Object block = getBlockForNode(node, nodeToBlocks);
 253             if (block != null) {
 254                 props.put("node-to-block", block);
 255             }
 256         }
 257 
 258         if (node instanceof ControlSinkNode) {
 259             props.put("category", "controlSink");
 260         } else if (node instanceof ControlSplitNode) {
 261             props.put("category", "controlSplit");
 262         } else if (node instanceof AbstractMergeNode) {
 263             props.put("category", "merge");
 264         } else if (node instanceof AbstractBeginNode) {
 265             props.put("category", "begin");
 266         } else if (node instanceof AbstractEndNode) {
 267             props.put("category", "end");
 268         } else if (node instanceof FixedNode) {
 269             props.put("category", "fixed");
 270         } else if (node instanceof VirtualState) {
 271             props.put("category", "state");
 272         } else if (node instanceof PhiNode) {
 273             props.put("category", "phi");
 274         } else if (node instanceof ProxyNode) {
 275             props.put("category", "proxy");
 276         } else {
 277             if (node instanceof ConstantNode) {
 278                 ConstantNode cn = (ConstantNode) node;
 279                 updateStringPropertiesForConstant((Map) props, cn);
 280             }
 281             props.put("category", "floating");
 282         }
 283         if (getSnippetReflectionProvider() != null) {
 284             for (Map.Entry<String, Object> prop : props.entrySet()) {
 285                 if (prop.getValue() instanceof JavaConstantFormattable) {
 286                     props.put(prop.getKey(), ((JavaConstantFormattable) prop.getValue()).format(this));
 287                 }
 288             }
 289         }
 290     }
 291 
 292     private Object getBlockForNode(Node node, NodeMap<Block> nodeToBlocks) {
 293         if (nodeToBlocks.isNew(node)) {
 294             return "NEW (not in schedule)";
 295         } else {
 296             Block block = nodeToBlocks.get(node);
 297             if (block != null) {
 298                 return block.getId();
 299             } else if (node instanceof PhiNode) {
 300                 return getBlockForNode(((PhiNode) node).merge(), nodeToBlocks);
 301             }
 302         }
 303         return null;
 304     }
 305 
 306     private static void findExtraNodes(Node node, Collection<? super Node> extraNodes) {
 307         if (node instanceof AbstractMergeNode) {
 308             AbstractMergeNode merge = (AbstractMergeNode) node;
 309             for (PhiNode phi : merge.phis()) {
 310                 extraNodes.add(phi);
 311             }
 312         }
 313     }
 314 
 315     @Override
 316     public boolean nodeHasPredecessor(Node node) {
 317         return node.predecessor() != null;
 318     }
 319 
 320     @Override
 321     public List<Block> blocks(GraphInfo graph) {
 322         return graph.blocks;
 323     }
 324 
 325     @Override
 326     public void print(DebugContext debug, Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException {
 327         output.print(new GraphInfo(debug, graph), properties, id, format, args);
 328     }
 329 
 330     @Override
 331     public int portSize(Edges port) {
 332         return port.getCount();
 333     }
 334 
 335     @Override
 336     public boolean edgeDirect(Edges port, int index) {
 337         return index < port.getDirectCount();
 338     }
 339 
 340     @Override
 341     public String edgeName(Edges port, int index) {
 342         return port.getName(index);
 343     }
 344 
 345     @Override
 346     public Object edgeType(Edges port, int index) {
 347         return ((InputEdges) port).getInputType(index);
 348     }
 349 
 350     @Override
 351     public Collection<? extends Node> edgeNodes(GraphInfo graph, Node node, Edges port, int i) {
 352         if (i < port.getDirectCount()) {
 353             Node single = Edges.getNode(node, port.getOffsets(), i);
 354             return Collections.singletonList(single);
 355         } else {
 356             return Edges.getNodeList(node, port.getOffsets(), i);
 357         }
 358     }
 359 
 360     @Override
 361     public Object enumClass(Object enumValue) {
 362         if (enumValue instanceof Enum) {
 363             return enumValue.getClass();
 364         }
 365         return null;
 366     }
 367 
 368     @Override
 369     public int enumOrdinal(Object obj) {
 370         if (obj instanceof Enum<?>) {
 371             return ((Enum<?>) obj).ordinal();
 372         }
 373         return -1;
 374     }
 375 
 376     @SuppressWarnings("unchecked")
 377     @Override
 378     public String[] enumTypeValues(Object clazz) {
 379         if (clazz instanceof Class<?>) {
 380             Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) clazz;
 381             Enum<?>[] constants = enumClass.getEnumConstants();
 382             if (constants != null) {
 383                 String[] names = new String[constants.length];
 384                 for (int i = 0; i < constants.length; i++) {
 385                     names[i] = constants[i].name();
 386                 }
 387                 return names;
 388             }
 389         }
 390         return null;
 391     }
 392 
 393     @Override
 394     public String typeName(Object obj) {
 395         if (obj instanceof Class<?>) {
 396             return ((Class<?>) obj).getName();
 397         }
 398         if (obj instanceof JavaType) {
 399             return ((JavaType) obj).toJavaName();
 400         }
 401         return null;
 402     }
 403 
 404     @Override
 405     public byte[] methodCode(ResolvedJavaMethod method) {
 406         return method.getCode();
 407     }
 408 
 409     @Override
 410     public int methodModifiers(ResolvedJavaMethod method) {
 411         return method.getModifiers();
 412     }
 413 
 414     @Override
 415     public Signature methodSignature(ResolvedJavaMethod method) {
 416         return method.getSignature();
 417     }
 418 
 419     @Override
 420     public String methodName(ResolvedJavaMethod method) {
 421         return method.getName();
 422     }
 423 
 424     @Override
 425     public Object methodDeclaringClass(ResolvedJavaMethod method) {
 426         return method.getDeclaringClass();
 427     }
 428 
 429     @Override
 430     public int fieldModifiers(ResolvedJavaField field) {
 431         return field.getModifiers();
 432     }
 433 
 434     @Override
 435     public String fieldTypeName(ResolvedJavaField field) {
 436         return field.getType().toJavaName();
 437     }
 438 
 439     @Override
 440     public String fieldName(ResolvedJavaField field) {
 441         return field.getName();
 442     }
 443 
 444     @Override
 445     public Object fieldDeclaringClass(ResolvedJavaField field) {
 446         return field.getDeclaringClass();
 447     }
 448 
 449     @Override
 450     public ResolvedJavaField field(Object object) {
 451         if (object instanceof ResolvedJavaField) {
 452             return (ResolvedJavaField) object;
 453         }
 454         return null;
 455     }
 456 
 457     @Override
 458     public Signature signature(Object object) {
 459         if (object instanceof Signature) {
 460             return (Signature) object;
 461         }
 462         return null;
 463     }
 464 
 465     @Override
 466     public int signatureParameterCount(Signature signature) {
 467         return signature.getParameterCount(false);
 468     }
 469 
 470     @Override
 471     public String signatureParameterTypeName(Signature signature, int index) {
 472         return signature.getParameterType(index, null).getName();
 473     }
 474 
 475     @Override
 476     public String signatureReturnTypeName(Signature signature) {
 477         return signature.getReturnType(null).getName();
 478     }
 479 
 480     @Override
 481     public NodeSourcePosition nodeSourcePosition(Object object) {
 482         if (object instanceof NodeSourcePosition) {
 483             return (NodeSourcePosition) object;
 484         }
 485         return null;
 486     }
 487 
 488     @Override
 489     public ResolvedJavaMethod nodeSourcePositionMethod(NodeSourcePosition pos) {
 490         return pos.getMethod();
 491     }
 492 
 493     @Override
 494     public NodeSourcePosition nodeSourcePositionCaller(NodeSourcePosition pos) {
 495         return pos.getCaller();
 496     }
 497 
 498     @Override
 499     public int nodeSourcePositionBCI(NodeSourcePosition pos) {
 500         return pos.getBCI();
 501     }
 502 
 503     @Override
 504     public StackTraceElement methodStackTraceElement(ResolvedJavaMethod method, int bci, NodeSourcePosition pos) {
 505         return method.asStackTraceElement(bci);
 506     }
 507 
 508     @Override
 509     public Iterable<SourceLanguagePosition> methodLocation(ResolvedJavaMethod method, int bci, NodeSourcePosition pos) {
 510         StackTraceElement e = methodStackTraceElement(method, bci, pos);
 511         class JavaSourcePosition implements SourceLanguagePosition {
 512 
 513             @Override
 514             public String toShortString() {
 515                 return e.toString();
 516             }
 517 
 518             @Override
 519             public int getOffsetEnd() {
 520                 return -1;
 521             }
 522 
 523             @Override
 524             public int getOffsetStart() {
 525                 return -1;
 526             }
 527 
 528             @Override
 529             public int getLineNumber() {
 530                 return e.getLineNumber();
 531             }
 532 
 533             @Override
 534             public URI getURI() {
 535                 String path = e.getFileName();
 536                 try {
 537                     return path == null ? null : new URI(null, null, path, null);
 538                 } catch (URISyntaxException ex) {
 539                     throw new IllegalArgumentException(ex);
 540                 }
 541             }
 542 
 543             @Override
 544             public String getLanguage() {
 545                 return "Java";
 546             }
 547         }
 548 
 549         List<SourceLanguagePosition> arr = new ArrayList<>();
 550         arr.add(new JavaSourcePosition());
 551         NodeSourcePosition at = pos;
 552         while (at != null) {
 553             SourceLanguagePosition cur = at.getSourceLanguage();
 554             if (cur != null) {
 555                 arr.add(cur);
 556             }
 557             at = at.getCaller();
 558         }
 559         return arr;
 560     }
 561 
 562     @Override
 563     public String locationLanguage(SourceLanguagePosition location) {
 564         return location.getLanguage();
 565     }
 566 
 567     @Override
 568     public URI locationURI(SourceLanguagePosition location) {
 569         return location.getURI();
 570     }
 571 
 572     @Override
 573     public int locationLineNumber(SourceLanguagePosition location) {
 574         return location.getLineNumber();
 575     }
 576 
 577     @Override
 578     public int locationOffsetStart(SourceLanguagePosition location) {
 579         return location.getOffsetStart();
 580     }
 581 
 582     @Override
 583     public int locationOffsetEnd(SourceLanguagePosition location) {
 584         return location.getOffsetEnd();
 585     }
 586 
 587     static final class GraphInfo {
 588         final DebugContext debug;
 589         final Graph graph;
 590         final ControlFlowGraph cfg;
 591         final BlockMap<List<Node>> blockToNodes;
 592         final NodeMap<Block> nodeToBlocks;
 593         final List<Block> blocks;
 594 
 595         private GraphInfo(DebugContext debug, Graph graph) {
 596             this.debug = debug;
 597             this.graph = graph;
 598             StructuredGraph.ScheduleResult scheduleResult = null;
 599             if (graph instanceof StructuredGraph) {
 600 
 601                 StructuredGraph structuredGraph = (StructuredGraph) graph;
 602                 scheduleResult = structuredGraph.getLastSchedule();
 603                 if (scheduleResult == null) {
 604 
 605                     // Also provide a schedule when an error occurs
 606                     if (DebugOptions.PrintGraphWithSchedule.getValue(graph.getOptions()) || debug.contextLookup(Throwable.class) != null) {
 607                         try {
 608                             SchedulePhase schedule = new SchedulePhase(graph.getOptions());
 609                             schedule.apply(structuredGraph);
 610                             scheduleResult = structuredGraph.getLastSchedule();
 611                         } catch (Throwable t) {
 612                         }
 613                     }
 614 
 615                 }
 616             }
 617             cfg = scheduleResult == null ? debug.contextLookup(ControlFlowGraph.class) : scheduleResult.getCFG();
 618             blockToNodes = scheduleResult == null ? null : scheduleResult.getBlockToNodesMap();
 619             nodeToBlocks = scheduleResult == null ? null : scheduleResult.getNodeToBlockMap();
 620             blocks = cfg == null ? null : Arrays.asList(cfg.getBlocks());
 621         }
 622     }
 623 
 624 }