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 
BinaryGraphPrinter(DebugContext ctx, SnippetReflectionProvider snippetReflection)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
getSnippetReflectionProvider()104     public SnippetReflectionProvider getSnippetReflectionProvider() {
105         return snippetReflection;
106     }
107 
108     @Override
beginGroup(DebugContext debug, String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties)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
endGroup()114     public void endGroup() throws IOException {
115         output.endGroup();
116     }
117 
118     @Override
close()119     public void close() {
120         output.close();
121     }
122 
123     @Override
method(Object object)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
node(Object obj)135     public Node node(Object obj) {
136         return obj instanceof Node ? (Node) obj : null;
137     }
138 
139     @Override
nodeClass(Object obj)140     public NodeClass<?> nodeClass(Object obj) {
141         if (obj instanceof NodeClass<?>) {
142             return (NodeClass<?>) obj;
143         }
144         return null;
145     }
146 
147     @Override
classForNode(Node node)148     public NodeClass<?> classForNode(Node node) {
149         return node.getNodeClass();
150     }
151 
152     @Override
nodeClassType(NodeClass<?> node)153     public Object nodeClassType(NodeClass<?> node) {
154         return node.getJavaClass();
155     }
156 
157     @Override
nameTemplate(NodeClass<?> nodeClass)158     public String nameTemplate(NodeClass<?> nodeClass) {
159         return nodeClass.getNameTemplate();
160     }
161 
162     @Override
graph(GraphInfo currrent, Object obj)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
nodeId(Node n)174     public int nodeId(Node n) {
175         return getNodeId(n);
176     }
177 
178     @Override
portInputs(NodeClass<?> nodeClass)179     public Edges portInputs(NodeClass<?> nodeClass) {
180         return nodeClass.getEdges(Inputs);
181     }
182 
183     @Override
portOutputs(NodeClass<?> nodeClass)184     public Edges portOutputs(NodeClass<?> nodeClass) {
185         return nodeClass.getEdges(Successors);
186     }
187 
188     @SuppressWarnings("deprecation")
getNodeId(Node node)189     private static int getNodeId(Node node) {
190         return node == null ? -1 : node.getId();
191     }
192 
193     @Override
blockNodes(GraphInfo info, Block block)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
blockId(Block sux)209     public int blockId(Block sux) {
210         return sux.getId();
211     }
212 
213     @Override
blockSuccessors(Block block)214     public List<Block> blockSuccessors(Block block) {
215         return Arrays.asList(block.getSuccessors());
216     }
217 
218     @Override
nodes(GraphInfo info)219     public Iterable<Node> nodes(GraphInfo info) {
220         return info.graph.getNodes();
221     }
222 
223     @Override
nodesCount(GraphInfo info)224     public int nodesCount(GraphInfo info) {
225         return info.graph.getNodeCount();
226     }
227 
228     @Override
229     @SuppressWarnings({"unchecked", "rawtypes"})
nodeProperties(GraphInfo info, Node node, Map<String, Object> props)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 
getBlockForNode(Node node, NodeMap<Block> nodeToBlocks)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 
findExtraNodes(Node node, Collection<? super Node> extraNodes)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
nodeHasPredecessor(Node node)316     public boolean nodeHasPredecessor(Node node) {
317         return node.predecessor() != null;
318     }
319 
320     @Override
blocks(GraphInfo graph)321     public List<Block> blocks(GraphInfo graph) {
322         return graph.blocks;
323     }
324 
325     @Override
print(DebugContext debug, Graph graph, Map<Object, Object> properties, int id, String format, Object... args)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
portSize(Edges port)331     public int portSize(Edges port) {
332         return port.getCount();
333     }
334 
335     @Override
edgeDirect(Edges port, int index)336     public boolean edgeDirect(Edges port, int index) {
337         return index < port.getDirectCount();
338     }
339 
340     @Override
edgeName(Edges port, int index)341     public String edgeName(Edges port, int index) {
342         return port.getName(index);
343     }
344 
345     @Override
edgeType(Edges port, int index)346     public Object edgeType(Edges port, int index) {
347         return ((InputEdges) port).getInputType(index);
348     }
349 
350     @Override
edgeNodes(GraphInfo graph, Node node, Edges port, int i)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
enumClass(Object enumValue)361     public Object enumClass(Object enumValue) {
362         if (enumValue instanceof Enum) {
363             return enumValue.getClass();
364         }
365         return null;
366     }
367 
368     @Override
enumOrdinal(Object obj)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
enumTypeValues(Object clazz)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
typeName(Object obj)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
methodCode(ResolvedJavaMethod method)405     public byte[] methodCode(ResolvedJavaMethod method) {
406         return method.getCode();
407     }
408 
409     @Override
methodModifiers(ResolvedJavaMethod method)410     public int methodModifiers(ResolvedJavaMethod method) {
411         return method.getModifiers();
412     }
413 
414     @Override
methodSignature(ResolvedJavaMethod method)415     public Signature methodSignature(ResolvedJavaMethod method) {
416         return method.getSignature();
417     }
418 
419     @Override
methodName(ResolvedJavaMethod method)420     public String methodName(ResolvedJavaMethod method) {
421         return method.getName();
422     }
423 
424     @Override
methodDeclaringClass(ResolvedJavaMethod method)425     public Object methodDeclaringClass(ResolvedJavaMethod method) {
426         return method.getDeclaringClass();
427     }
428 
429     @Override
fieldModifiers(ResolvedJavaField field)430     public int fieldModifiers(ResolvedJavaField field) {
431         return field.getModifiers();
432     }
433 
434     @Override
fieldTypeName(ResolvedJavaField field)435     public String fieldTypeName(ResolvedJavaField field) {
436         return field.getType().toJavaName();
437     }
438 
439     @Override
fieldName(ResolvedJavaField field)440     public String fieldName(ResolvedJavaField field) {
441         return field.getName();
442     }
443 
444     @Override
fieldDeclaringClass(ResolvedJavaField field)445     public Object fieldDeclaringClass(ResolvedJavaField field) {
446         return field.getDeclaringClass();
447     }
448 
449     @Override
field(Object object)450     public ResolvedJavaField field(Object object) {
451         if (object instanceof ResolvedJavaField) {
452             return (ResolvedJavaField) object;
453         }
454         return null;
455     }
456 
457     @Override
signature(Object object)458     public Signature signature(Object object) {
459         if (object instanceof Signature) {
460             return (Signature) object;
461         }
462         return null;
463     }
464 
465     @Override
signatureParameterCount(Signature signature)466     public int signatureParameterCount(Signature signature) {
467         return signature.getParameterCount(false);
468     }
469 
470     @Override
signatureParameterTypeName(Signature signature, int index)471     public String signatureParameterTypeName(Signature signature, int index) {
472         return signature.getParameterType(index, null).getName();
473     }
474 
475     @Override
signatureReturnTypeName(Signature signature)476     public String signatureReturnTypeName(Signature signature) {
477         return signature.getReturnType(null).getName();
478     }
479 
480     @Override
nodeSourcePosition(Object object)481     public NodeSourcePosition nodeSourcePosition(Object object) {
482         if (object instanceof NodeSourcePosition) {
483             return (NodeSourcePosition) object;
484         }
485         return null;
486     }
487 
488     @Override
nodeSourcePositionMethod(NodeSourcePosition pos)489     public ResolvedJavaMethod nodeSourcePositionMethod(NodeSourcePosition pos) {
490         return pos.getMethod();
491     }
492 
493     @Override
nodeSourcePositionCaller(NodeSourcePosition pos)494     public NodeSourcePosition nodeSourcePositionCaller(NodeSourcePosition pos) {
495         return pos.getCaller();
496     }
497 
498     @Override
nodeSourcePositionBCI(NodeSourcePosition pos)499     public int nodeSourcePositionBCI(NodeSourcePosition pos) {
500         return pos.getBCI();
501     }
502 
503     @Override
methodStackTraceElement(ResolvedJavaMethod method, int bci, NodeSourcePosition pos)504     public StackTraceElement methodStackTraceElement(ResolvedJavaMethod method, int bci, NodeSourcePosition pos) {
505         return method.asStackTraceElement(bci);
506     }
507 
508     @Override
methodLocation(ResolvedJavaMethod method, int bci, NodeSourcePosition pos)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
locationLanguage(SourceLanguagePosition location)563     public String locationLanguage(SourceLanguagePosition location) {
564         return location.getLanguage();
565     }
566 
567     @Override
locationURI(SourceLanguagePosition location)568     public URI locationURI(SourceLanguagePosition location) {
569         return location.getURI();
570     }
571 
572     @Override
locationLineNumber(SourceLanguagePosition location)573     public int locationLineNumber(SourceLanguagePosition location) {
574         return location.getLineNumber();
575     }
576 
577     @Override
locationOffsetStart(SourceLanguagePosition location)578     public int locationOffsetStart(SourceLanguagePosition location) {
579         return location.getOffsetStart();
580     }
581 
582     @Override
locationOffsetEnd(SourceLanguagePosition location)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 
GraphInfo(DebugContext debug, Graph graph)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 }
625