001/*
002 * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.truffle;
024
025import java.io.*;
026import java.util.*;
027import java.util.Map.Entry;
028
029import jdk.internal.jvmci.meta.*;
030
031import com.oracle.graal.graph.*;
032import com.oracle.graal.nodes.*;
033import com.oracle.graal.nodes.java.*;
034import com.oracle.graal.phases.util.*;
035
036public class TruffleExpansionLogger {
037
038    private final Providers providers;
039    private final ExpansionTree root;
040    private final Map<MethodCallTargetNode, ExpansionTree> callToParentTree = new HashMap<>();
041
042    public TruffleExpansionLogger(Providers providers, StructuredGraph graph) {
043        this.providers = providers;
044        root = new ExpansionTree(null, null, graph.method(), -1);
045        registerParentInCalls(root, graph);
046    }
047
048    public void preExpand(MethodCallTargetNode callTarget, StructuredGraph inliningGraph) {
049        ResolvedJavaMethod sourceMethod = callTarget.invoke().stateAfter().method();
050
051        int sourceMethodBci = callTarget.invoke().bci();
052        ResolvedJavaMethod targetMethod = callTarget.targetMethod();
053        ResolvedJavaType targetReceiverType = null;
054        if (!sourceMethod.isStatic() && callTarget.receiver() != null && callTarget.receiver().isConstant()) {
055            targetReceiverType = providers.getMetaAccess().lookupJavaType(callTarget.arguments().first().asJavaConstant());
056        }
057
058        if (targetReceiverType != null) {
059            ExpansionTree parent = callToParentTree.get(callTarget);
060            assert parent != null;
061            callToParentTree.remove(callTarget);
062            ExpansionTree tree = new ExpansionTree(parent, targetReceiverType, targetMethod, sourceMethodBci);
063            registerParentInCalls(tree, inliningGraph);
064        }
065    }
066
067    @SuppressWarnings("unchecked")
068    public void postExpand(Map<Node, Node> states) {
069        Iterable<Entry<Node, Node>> entries;
070        if (states instanceof NodeMap) {
071            entries = ((NodeMap<Node>) states).entries();
072        } else {
073            entries = states.entrySet();
074        }
075
076        for (Entry<Node, Node> entry : entries) {
077            Node key = entry.getKey();
078            Node value = entry.getValue();
079
080            if (value instanceof MethodCallTargetNode && callToParentTree.containsKey(key)) {
081                callToParentTree.put((MethodCallTargetNode) value, callToParentTree.get(key));
082                callToParentTree.remove(key);
083            }
084        }
085    }
086
087    private void registerParentInCalls(ExpansionTree parentTree, StructuredGraph graph) {
088        for (MethodCallTargetNode target : graph.getNodes(MethodCallTargetNode.TYPE)) {
089            callToParentTree.put(target, parentTree);
090        }
091    }
092
093    public void print(OptimizedCallTarget target) {
094        System.out.printf("Expansion tree for %s: %n", target);
095        root.print(System.out);
096    }
097
098    private static final class ExpansionTree implements Comparable<ExpansionTree> {
099
100        private final ExpansionTree parent;
101        private final ResolvedJavaType targetReceiverType;
102        private final ResolvedJavaMethod targetMethod;
103        private final int parentBci;
104        private final List<ExpansionTree> children = new ArrayList<>();
105
106        public ExpansionTree(ExpansionTree parent, ResolvedJavaType targetReceiverType, ResolvedJavaMethod targetMethod, int parentBci) {
107            this.parent = parent;
108            this.targetReceiverType = targetReceiverType;
109            this.targetMethod = targetMethod;
110            this.parentBci = parentBci;
111            if (parent != null) {
112                parent.children.add(this);
113            }
114        }
115
116        public int compareTo(ExpansionTree o) {
117            if (parent == o.parent) {
118                return parentBci - o.parentBci;
119            }
120            return 0;
121        }
122
123        public void print(PrintStream p) {
124            print(p, "");
125        }
126
127        private void print(PrintStream p, String indent) {
128            StackTraceElement targetElement = targetMethod.asStackTraceElement(0);
129            StackTraceElement sourceElement = null;
130
131            ExpansionTree currentParent = this.parent;
132            if (currentParent != null) {
133                sourceElement = currentParent.targetMethod.asStackTraceElement(parentBci);
134            }
135
136            String className = targetElement.getClassName();
137            int lastIndex = className.lastIndexOf('.');
138            if (lastIndex != -1) {
139                className = className.substring(lastIndex + 1, className.length());
140            }
141
142            className = extractInnerClassName(className);
143
144            String constantType = "";
145            if (targetReceiverType != null) {
146                String javaName = targetReceiverType.toJavaName(false);
147
148                javaName = extractInnerClassName(javaName);
149
150                if (!javaName.equals(className)) {
151                    constantType = "<" + javaName + ">";
152                }
153            }
154
155            String sourceSource = "";
156            String targetSource = "";
157            if (TruffleCompilerOptions.TraceTruffleExpansionSource.getValue()) {
158                sourceSource = formatSource(sourceElement);
159                targetSource = formatSource(targetElement);
160            }
161            p.printf("%s%s %s%s.%s%s%n", indent, sourceSource, className, constantType, targetMethod.getName(), targetSource);
162
163            Collections.sort(children);
164
165            for (ExpansionTree child : children) {
166                child.print(p, indent + "  ");
167            }
168        }
169
170        private static String extractInnerClassName(String className) {
171            int lastIndex = className.lastIndexOf('$');
172            if (lastIndex != -1) {
173                return className.substring(lastIndex + 1, className.length());
174            }
175            return className;
176        }
177
178        private static String formatSource(StackTraceElement e) {
179            if (e == null) {
180                return "";
181            }
182            if (e.getFileName() != null) {
183                if (e.getLineNumber() >= 0) {
184                    return String.format("(%s:%d)", e.getFileName(), e.getLineNumber());
185                } else {
186                    return String.format("(%s)", e.getFileName());
187                }
188            } else {
189                return String.format("(Unknown Source)");
190            }
191        }
192    }
193
194}