Skip to content

Commit c64d8e6

Browse files
committed
Extend PrintAnalysisCallTree option
-H:PrintAnalysisCallTreeFilter= option accept a list of methods to print the call tree of them, the default value is an empty string to print all call trees.
1 parent 53da61f commit c64d8e6

File tree

4 files changed

+61
-15
lines changed

4 files changed

+61
-15
lines changed

docs/reference-manual/native-image/Reports.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ The call tree is a a breadth-first tree reduction of the call graph as seen by t
1818
The points-to analysis eliminates calls to methods that it determines cannot be reachable at runtime, based on the analysed receiver types.
1919
It also completely eliminates invocations in unreachable code blocks, e.g., blocks guarded by a type check that always fails.
2020
The call tree report is enabled using the `-H:+PrintAnalysisCallTree` option.
21+
Option `-H:PrintAnalysisCallTreeFilter=<comma-separated method list>` can be used to only print out the specified methods'
22+
call tree, and it implicitly sets the `-H:+PrintAnalysisCallTree`.
23+
The method should be specified with its full qualified name, such as `java.lang.String.hashCode()` for normal method
24+
and `java.lang.String.<clinit>()` for class static initialization method.
2125
It produces a file with the structure:
2226

2327
```

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisReporter.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ public static void printAnalysisReports(String imageName, OptionValues options,
4646
}
4747

4848
if (AnalysisReportsOptions.PrintAnalysisCallTree.getValue(options)) {
49-
CallTreePrinter.print(bb, reportsPath, ReportUtils.extractImageName(imageName));
49+
String filterMethods = "";
50+
if (AnalysisReportsOptions.PrintAnalysisCallTreeFilter.hasBeenSet(options)) {
51+
filterMethods = AnalysisReportsOptions.PrintAnalysisCallTreeFilter.getValue(options);
52+
}
53+
CallTreePrinter.print(bb, filterMethods, reportsPath, ReportUtils.extractImageName(imageName));
5054
}
5155

5256
if (AnalysisReportsOptions.PrintImageObjectTree.getValue(options)) {

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisReportsOptions.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
package com.oracle.graal.pointsto.reports;
2626

27+
import org.graalvm.collections.EconomicMap;
2728
import org.graalvm.compiler.options.Option;
2829
import org.graalvm.compiler.options.OptionKey;
2930

@@ -38,6 +39,14 @@ public class AnalysisReportsOptions {
3839
@Option(help = "Print analysis call tree, a breadth-first tree reduction of the call graph.")//
3940
public static final OptionKey<Boolean> PrintAnalysisCallTree = new OptionKey<>(false);
4041

42+
@Option(help = "Print analysis call tree with method filters.")//
43+
public static final OptionKey<String> PrintAnalysisCallTreeFilter = new OptionKey<String>(null) {
44+
@Override
45+
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, String oldValue, String newValue) {
46+
PrintAnalysisCallTree.update(values, true);
47+
}
48+
};
49+
4150
@Option(help = "Print image object hierarchy.")//
4251
public static final OptionKey<Boolean> PrintImageObjectTree = new OptionKey<>(false);
4352

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,20 @@
6767
import jdk.vm.ci.meta.ResolvedJavaType;
6868

6969
public final class CallTreePrinter {
70+
private static Set<String> printMethods = new HashSet<>();
71+
private static Set<Integer> printedID = new HashSet<>();
72+
private static boolean printAll = false;
7073

7174
public static final Pattern CAMEL_CASE_PATTERN = Pattern.compile(
7275
"\\b[a-zA-Z]|[A-Z]|\\.");
7376

74-
public static void print(BigBang bb, String reportsPath, String reportName) {
77+
public static void print(BigBang bb, String printCallTreeMethods, String reportsPath, String reportName) {
7578
CallTreePrinter printer = new CallTreePrinter(bb);
7679
printer.buildCallTree();
77-
80+
Arrays.stream(printCallTreeMethods.split(",")).forEach(printMethods::add);
81+
if (printCallTreeMethods.length() == 0) {
82+
printAll = true;
83+
}
7884
ReportUtils.report("call tree", reportsPath, "call_tree_" + reportName, "txt",
7985
printer::printMethods);
8086
ReportUtils.report("list of used methods", reportsPath, "used_methods_" + reportName, "txt",
@@ -241,42 +247,65 @@ private void printMethods(PrintWriter out) {
241247
while (iterator.hasNext()) {
242248
MethodNode node = iterator.next();
243249
boolean lastEntryPoint = !iterator.hasNext();
244-
out.format("%s%s %s %n", lastEntryPoint ? LAST_CHILD : CHILD, "entry", node.format());
245-
printCallTreeNode(out, lastEntryPoint ? EMPTY_INDENT : CONNECTING_INDENT, node);
250+
printCallTreeNode(out, lastEntryPoint ? EMPTY_INDENT : CONNECTING_INDENT, node, false, lastEntryPoint ? LAST_CHILD : CHILD);
246251
}
247252
out.println();
248253
}
249254

250-
private static void printCallTreeNode(PrintWriter out, String prefix, MethodNode node) {
255+
private static void printCallTreeNode(PrintWriter out, String prefix, MethodNode node, boolean shouldPrint, String beginning) {
256+
boolean print = shouldPrint;
257+
if (!shouldPrint) {
258+
String methodName = node.method.getQualifiedName();
259+
if (printAll || printMethods.remove(methodName)) {
260+
print = true;
261+
out.format("%s%s %s %n", beginning, printAll ? "entry" : "filtered start", node.format());
262+
}
263+
}
251264

252265
for (int invokeIdx = 0; invokeIdx < node.invokes.size(); invokeIdx++) {
253266
InvokeNode invoke = node.invokes.get(invokeIdx);
254267
boolean lastInvoke = invokeIdx == node.invokes.size() - 1;
255268
if (invoke.isDirectInvoke) {
256269
if (invoke.callees.size() > 0) {
257270
Node calleeNode = invoke.callees.get(0);
258-
out.format("%s%s%s %s @bci=%s %n", prefix, (lastInvoke ? LAST_CHILD : CHILD),
259-
"directly calls", calleeNode.format(), invoke.formatLocation());
260-
if (calleeNode instanceof MethodNode) {
261-
printCallTreeNode(out, prefix + (lastInvoke ? EMPTY_INDENT : CONNECTING_INDENT), (MethodNode) calleeNode);
271+
boolean isNative = invoke.targetMethod.wrapped.getClass().getName().equals("com.oracle.svm.jni.hosted.JNINativeCallWrapperMethod");
272+
if (print) {
273+
calleeNode = extendNodeReference(calleeNode);
274+
out.format("%s%s%s %s @bci=%s %s %n", prefix, (lastInvoke ? LAST_CHILD : CHILD),
275+
"directly calls", calleeNode.format(), invoke.formatLocation(), isNative ? "(Native Method)" : "");
276+
}
277+
if (!isNative && calleeNode instanceof MethodNode) {
278+
printCallTreeNode(out, prefix + (lastInvoke ? EMPTY_INDENT : CONNECTING_INDENT), (MethodNode) calleeNode, print, beginning);
262279
}
263280
}
264281
} else {
265-
out.format("%s%s%s %s @bci=%s%n", prefix, (lastInvoke ? LAST_CHILD : CHILD),
266-
"virtually calls", invoke.formatTarget(), invoke.formatLocation());
282+
if (print) {
283+
out.format("%s%s%s %s @bci=%s%n", prefix, (lastInvoke ? LAST_CHILD : CHILD),
284+
"virtually calls", invoke.formatTarget(), invoke.formatLocation());
285+
}
267286
for (int calleeIdx = 0; calleeIdx < invoke.callees.size(); calleeIdx++) {
268287
boolean lastCallee = calleeIdx == invoke.callees.size() - 1;
269288
Node calleeNode = invoke.callees.get(calleeIdx);
270-
out.format("%s%s%s %s %n", prefix + (lastInvoke ? EMPTY_INDENT : CONNECTING_INDENT), (lastCallee ? LAST_CHILD : CHILD),
271-
"is overridden by", calleeNode.format());
289+
if (print) {
290+
calleeNode = extendNodeReference(calleeNode);
291+
out.format("%s%s%s %s %n", prefix + (lastInvoke ? EMPTY_INDENT : CONNECTING_INDENT), (lastCallee ? LAST_CHILD : CHILD),
292+
"is overridden by", calleeNode.format());
293+
}
272294
if (calleeNode instanceof MethodNode) {
273-
printCallTreeNode(out, prefix + (lastInvoke ? EMPTY_INDENT : CONNECTING_INDENT) + (lastCallee ? EMPTY_INDENT : CONNECTING_INDENT), (MethodNode) calleeNode);
295+
printCallTreeNode(out, prefix + (lastInvoke ? EMPTY_INDENT : CONNECTING_INDENT) + (lastCallee ? EMPTY_INDENT : CONNECTING_INDENT), (MethodNode) calleeNode, print, beginning);
274296
}
275297
}
276298
}
277299
}
278300
}
279301

302+
private static Node extendNodeReference(Node calleeNode) {
303+
if (calleeNode instanceof MethodNodeReference && printedID.add(((MethodNodeReference) calleeNode).methodNode.id)) {
304+
return ((MethodNodeReference) calleeNode).methodNode;
305+
}
306+
return calleeNode;
307+
}
308+
280309
private void printUsedMethods(PrintWriter out) {
281310
List<String> methodsList = new ArrayList<>();
282311
for (ResolvedJavaMethod method : methodToNode.keySet()) {

0 commit comments

Comments
 (0)