From c64d8e61d905323a312b33eee619fb4e81ec192e Mon Sep 17 00:00:00 2001 From: "cengfeng.lzy" Date: Tue, 2 Feb 2021 17:45:09 +0800 Subject: [PATCH] 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. --- docs/reference-manual/native-image/Reports.md | 4 ++ .../pointsto/reports/AnalysisReporter.java | 6 +- .../reports/AnalysisReportsOptions.java | 9 +++ .../pointsto/reports/CallTreePrinter.java | 57 ++++++++++++++----- 4 files changed, 61 insertions(+), 15 deletions(-) diff --git a/docs/reference-manual/native-image/Reports.md b/docs/reference-manual/native-image/Reports.md index d0aee5feae2b..bf3338729092 100644 --- a/docs/reference-manual/native-image/Reports.md +++ b/docs/reference-manual/native-image/Reports.md @@ -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=` 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.()` for class static initialization method. It produces a file with the structure: ``` diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisReporter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisReporter.java index c1bd31aa2efe..677b55b86505 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisReporter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisReporter.java @@ -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)) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisReportsOptions.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisReportsOptions.java index 62b9e9d5780f..22f8e2fde76a 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisReportsOptions.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/AnalysisReportsOptions.java @@ -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; @@ -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 PrintAnalysisCallTree = new OptionKey<>(false); + @Option(help = "Print analysis call tree with method filters.")// + public static final OptionKey PrintAnalysisCallTreeFilter = new OptionKey(null) { + @Override + protected void onValueUpdate(EconomicMap, Object> values, String oldValue, String newValue) { + PrintAnalysisCallTree.update(values, true); + } + }; + @Option(help = "Print image object hierarchy.")// public static final OptionKey PrintImageObjectTree = new OptionKey<>(false); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java index 8f0c4d7ed3a6..c4b9f1b470d5 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java @@ -67,14 +67,20 @@ import jdk.vm.ci.meta.ResolvedJavaType; public final class CallTreePrinter { + private static Set printMethods = new HashSet<>(); + private static Set 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", @@ -241,13 +247,20 @@ 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); @@ -255,28 +268,44 @@ private static void printCallTreeNode(PrintWriter out, String prefix, MethodNode 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 methodsList = new ArrayList<>(); for (ResolvedJavaMethod method : methodToNode.keySet()) {