001/*
002 * Copyright (c) 2015, 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.debug;
024
025import java.util.*;
026
027import jdk.internal.jvmci.meta.*;
028
029import com.oracle.graal.graph.*;
030import com.oracle.graal.graphbuilderconf.*;
031import com.oracle.graal.nodes.*;
032import com.oracle.graal.nodes.java.*;
033import com.oracle.graal.nodes.virtual.*;
034import com.oracle.graal.truffle.*;
035
036public class HistogramInlineInvokePlugin implements InlineInvokePlugin {
037
038    private final Map<ResolvedJavaMethod, MethodStatistics> histogram = new HashMap<>();
039    private final StructuredGraph graph;
040
041    private HistogramInlineInvokePlugin.MethodStatistic currentStatistic;
042
043    public HistogramInlineInvokePlugin(StructuredGraph graph) {
044        this.graph = graph;
045    }
046
047    @Override
048    public void notifyBeforeInline(ResolvedJavaMethod methodToInline) {
049        currentStatistic = new MethodStatistic(currentStatistic, methodToInline, countNodes(), countCalls());
050    }
051
052    @Override
053    public void notifyAfterInline(ResolvedJavaMethod methodToInline) {
054        assert methodToInline.equals(currentStatistic.method);
055
056        currentStatistic.applyNodeCountAfter(countNodes());
057        currentStatistic.applyCallsAfter(countCalls());
058        accept(currentStatistic);
059        currentStatistic = currentStatistic.getParent();
060    }
061
062    private int countNodes() {
063        return graph.getNodes().filter(node -> isNonTrivial(node)).count();
064    }
065
066    private int countCalls() {
067        return graph.getNodes(MethodCallTargetNode.TYPE).count();
068    }
069
070    private static boolean isNonTrivial(Node node) {
071        return !(node instanceof VirtualState || node instanceof VirtualObjectNode || node instanceof BeginNode || node instanceof DeoptimizeNode);
072    }
073
074    private void accept(MethodStatistic current) {
075        ResolvedJavaMethod method = current.getMethod();
076        HistogramInlineInvokePlugin.MethodStatistics statistics = histogram.get(method);
077        if (statistics == null) {
078            statistics = new MethodStatistics(method);
079            histogram.put(method, statistics);
080        }
081        statistics.accept(current);
082    }
083
084    public void print(OptimizedCallTarget target) {
085        target.log(String.format("Truffle expansion histogram for %s", target));
086        target.log("  Invocations = Number of expanded invocations");
087        target.log("  Nodes = Number of non-trival Graal nodes created for this method during partial evaluation.");
088        target.log("  Calls = Number of not expanded calls created for this method during partial evaluation.");
089        target.log(String.format(" %-11s |Nodes %5s %5s %5s %8s |Calls %5s %5s %5s %8s | Method Name", "Invocations", "Sum", "Min", "Max", "Avg", "Sum", "Min", "Max", "Avg"));
090        histogram.values().stream().filter(statistics -> statistics.shallowCount.getSum() > 0).sorted().forEach(statistics -> statistics.print(target));
091    }
092
093    private static class MethodStatistics implements Comparable<MethodStatistics> {
094
095        private final ResolvedJavaMethod method;
096
097        private int count;
098        private final IntSummaryStatistics shallowCount = new IntSummaryStatistics();
099        private final IntSummaryStatistics callCount = new IntSummaryStatistics();
100
101        public MethodStatistics(ResolvedJavaMethod method) {
102            this.method = method;
103        }
104
105        public void print(OptimizedCallTarget target) {
106            target.log(String.format(" %11d |      %5d %5d %5d %8.2f |      %5d %5d %5d %8.2f | %s", //
107                            count, shallowCount.getSum(), shallowCount.getMin(), shallowCount.getMax(), //
108                            shallowCount.getAverage(), callCount.getSum(), callCount.getMin(), callCount.getMax(), //
109                            callCount.getAverage(), method.format("%h.%n(%p)")));
110        }
111
112        public int compareTo(MethodStatistics o) {
113            int result = Long.compare(o.shallowCount.getSum(), shallowCount.getSum());
114            if (result == 0) {
115                return Integer.compare(o.count, count);
116            }
117            return result;
118        }
119
120        public void accept(MethodStatistic statistic) {
121            if (!statistic.method.equals(method)) {
122                throw new IllegalArgumentException("invalid statistic");
123            }
124            count++;
125            callCount.accept(statistic.getShallowCallCount());
126            shallowCount.accept(statistic.getShallowNodeCount());
127        }
128    }
129
130    private static class MethodStatistic {
131
132        private final MethodStatistic parent;
133        private final List<MethodStatistic> children = new ArrayList<>();
134
135        private final ResolvedJavaMethod method;
136        private int deepNodeCount;
137        private int callCount;
138
139        public MethodStatistic(MethodStatistic parent, ResolvedJavaMethod method, int nodeCountBefore, int callsBefore) {
140            this.parent = parent;
141            this.method = method;
142            this.callCount = callsBefore;
143            this.deepNodeCount = nodeCountBefore;
144            if (parent != null) {
145                this.parent.getChildren().add(this);
146            }
147        }
148
149        public ResolvedJavaMethod getMethod() {
150            return method;
151        }
152
153        public List<MethodStatistic> getChildren() {
154            return children;
155        }
156
157        public int getShallowNodeCount() {
158            int shallowCount = deepNodeCount;
159            for (MethodStatistic child : children) {
160                shallowCount -= child.deepNodeCount;
161            }
162            return shallowCount;
163        }
164
165        public int getShallowCallCount() {
166            int shallowCount = callCount;
167            for (MethodStatistic child : children) {
168                shallowCount -= child.callCount;
169            }
170            return shallowCount;
171        }
172
173        public void applyNodeCountAfter(int nodeCountAfter) {
174            deepNodeCount = nodeCountAfter - this.deepNodeCount;
175        }
176
177        public void applyCallsAfter(int callsAfter) {
178            callCount = callsAfter - this.callCount;
179        }
180
181        public MethodStatistic getParent() {
182            return parent;
183        }
184
185    }
186
187}