Skip to content

Extend PrintAnalysisCallTree option #3227

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/reference-manual/native-image/Reports.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ The call tree is a a breadth-first tree reduction of the call graph as seen by t
The points-to analysis eliminates calls to methods that it determines cannot be reachable at runtime, based on the analysed receiver types.
It also completely eliminates invocations in unreachable code blocks, e.g., blocks guarded by a type check that always fails.
The call tree report is enabled using the `-H:+PrintAnalysisCallTree` option.
Option `-H:PrintAnalysisCallTreeFilter=<comma-separated method list>` can be used to only print out the specified methods'
call tree, and it implicitly sets the `-H:+PrintAnalysisCallTree`.
The method should be specified with its full qualified name, such as `java.lang.String.hashCode()` for normal method
and `java.lang.String.<clinit>()` for class static initialization method.
It produces a file with the structure:

```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ public static void printAnalysisReports(String imageName, OptionValues options,
}

if (AnalysisReportsOptions.PrintAnalysisCallTree.getValue(options)) {
CallTreePrinter.print(bb, reportsPath, ReportUtils.extractImageName(imageName));
String filterMethods = "";
if (AnalysisReportsOptions.PrintAnalysisCallTreeFilter.hasBeenSet(options)) {
filterMethods = AnalysisReportsOptions.PrintAnalysisCallTreeFilter.getValue(options);
}
CallTreePrinter.print(bb, filterMethods, reportsPath, ReportUtils.extractImageName(imageName));
}

if (AnalysisReportsOptions.PrintImageObjectTree.getValue(options)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/
package com.oracle.graal.pointsto.reports;

import org.graalvm.collections.EconomicMap;
import org.graalvm.compiler.options.Option;
import org.graalvm.compiler.options.OptionKey;

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

@Option(help = "Print analysis call tree with method filters.")//
public static final OptionKey<String> PrintAnalysisCallTreeFilter = new OptionKey<String>(null) {
@Override
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, String oldValue, String newValue) {
PrintAnalysisCallTree.update(values, true);
}
};

@Option(help = "Print image object hierarchy.")//
public static final OptionKey<Boolean> PrintImageObjectTree = new OptionKey<>(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,20 @@
import jdk.vm.ci.meta.ResolvedJavaType;

public final class CallTreePrinter {
private static Set<String> printMethods = new HashSet<>();
private static Set<Integer> printedID = new HashSet<>();
private static boolean printAll = false;

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

public static void print(BigBang bb, String reportsPath, String reportName) {
public static void print(BigBang bb, String printCallTreeMethods, String reportsPath, String reportName) {
CallTreePrinter printer = new CallTreePrinter(bb);
printer.buildCallTree();

Arrays.stream(printCallTreeMethods.split(",")).forEach(printMethods::add);
if (printCallTreeMethods.length() == 0) {
printAll = true;
}
ReportUtils.report("call tree", reportsPath, "call_tree_" + reportName, "txt",
printer::printMethods);
ReportUtils.report("list of used methods", reportsPath, "used_methods_" + reportName, "txt",
Expand Down Expand Up @@ -241,42 +247,65 @@ private void printMethods(PrintWriter out) {
while (iterator.hasNext()) {
MethodNode node = iterator.next();
boolean lastEntryPoint = !iterator.hasNext();
out.format("%s%s %s %n", lastEntryPoint ? LAST_CHILD : CHILD, "entry", node.format());
printCallTreeNode(out, lastEntryPoint ? EMPTY_INDENT : CONNECTING_INDENT, node);
printCallTreeNode(out, lastEntryPoint ? EMPTY_INDENT : CONNECTING_INDENT, node, false, lastEntryPoint ? LAST_CHILD : CHILD);
}
out.println();
}

private static void printCallTreeNode(PrintWriter out, String prefix, MethodNode node) {
private static void printCallTreeNode(PrintWriter out, String prefix, MethodNode node, boolean shouldPrint, String beginning) {
boolean print = shouldPrint;
if (!shouldPrint) {
String methodName = node.method.getQualifiedName();
if (printAll || printMethods.remove(methodName)) {
print = true;
out.format("%s%s %s %n", beginning, printAll ? "entry" : "filtered start", node.format());
}
}

for (int invokeIdx = 0; invokeIdx < node.invokes.size(); invokeIdx++) {
InvokeNode invoke = node.invokes.get(invokeIdx);
boolean lastInvoke = invokeIdx == node.invokes.size() - 1;
if (invoke.isDirectInvoke) {
if (invoke.callees.size() > 0) {
Node calleeNode = invoke.callees.get(0);
out.format("%s%s%s %s @bci=%s %n", prefix, (lastInvoke ? LAST_CHILD : CHILD),
"directly calls", calleeNode.format(), invoke.formatLocation());
if (calleeNode instanceof MethodNode) {
printCallTreeNode(out, prefix + (lastInvoke ? EMPTY_INDENT : CONNECTING_INDENT), (MethodNode) calleeNode);
boolean isNative = invoke.targetMethod.wrapped.getClass().getName().equals("com.oracle.svm.jni.hosted.JNINativeCallWrapperMethod");
if (print) {
calleeNode = extendNodeReference(calleeNode);
out.format("%s%s%s %s @bci=%s %s %n", prefix, (lastInvoke ? LAST_CHILD : CHILD),
"directly calls", calleeNode.format(), invoke.formatLocation(), isNative ? "(Native Method)" : "");
}
if (!isNative && calleeNode instanceof MethodNode) {
printCallTreeNode(out, prefix + (lastInvoke ? EMPTY_INDENT : CONNECTING_INDENT), (MethodNode) calleeNode, print, beginning);
}
}
} else {
out.format("%s%s%s %s @bci=%s%n", prefix, (lastInvoke ? LAST_CHILD : CHILD),
"virtually calls", invoke.formatTarget(), invoke.formatLocation());
if (print) {
out.format("%s%s%s %s @bci=%s%n", prefix, (lastInvoke ? LAST_CHILD : CHILD),
"virtually calls", invoke.formatTarget(), invoke.formatLocation());
}
for (int calleeIdx = 0; calleeIdx < invoke.callees.size(); calleeIdx++) {
boolean lastCallee = calleeIdx == invoke.callees.size() - 1;
Node calleeNode = invoke.callees.get(calleeIdx);
out.format("%s%s%s %s %n", prefix + (lastInvoke ? EMPTY_INDENT : CONNECTING_INDENT), (lastCallee ? LAST_CHILD : CHILD),
"is overridden by", calleeNode.format());
if (print) {
calleeNode = extendNodeReference(calleeNode);
out.format("%s%s%s %s %n", prefix + (lastInvoke ? EMPTY_INDENT : CONNECTING_INDENT), (lastCallee ? LAST_CHILD : CHILD),
"is overridden by", calleeNode.format());
}
if (calleeNode instanceof MethodNode) {
printCallTreeNode(out, prefix + (lastInvoke ? EMPTY_INDENT : CONNECTING_INDENT) + (lastCallee ? EMPTY_INDENT : CONNECTING_INDENT), (MethodNode) calleeNode);
printCallTreeNode(out, prefix + (lastInvoke ? EMPTY_INDENT : CONNECTING_INDENT) + (lastCallee ? EMPTY_INDENT : CONNECTING_INDENT), (MethodNode) calleeNode, print, beginning);
}
}
}
}
}

private static Node extendNodeReference(Node calleeNode) {
if (calleeNode instanceof MethodNodeReference && printedID.add(((MethodNodeReference) calleeNode).methodNode.id)) {
return ((MethodNodeReference) calleeNode).methodNode;
}
return calleeNode;
}

private void printUsedMethods(PrintWriter out) {
List<String> methodsList = new ArrayList<>();
for (ResolvedJavaMethod method : methodToNode.keySet()) {
Expand Down