From 7209a2bc8021d61b7bdc996a4e2c2b8b63beed45 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Wed, 2 Apr 2025 16:23:11 +0200 Subject: [PATCH] provide patches for running Judge * print line numbers in CSV call graph * disallow inlining in all Judge test methods --- .../graal/pointsto/api/PointstoOptions.java | 3 +++ .../graal/pointsto/meta/AnalysisMethod.java | 21 +++++++++++++++++++ .../pointsto/reports/CallTreePrinter.java | 14 +++++++------ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java index 0c8c0569a331..aa0c452d4c67 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java @@ -36,6 +36,9 @@ public class PointstoOptions { + @Option(help = "Specify which jar contains the Judge Test classes, whose methods should not be inlined.")// + public static final OptionKey JudgeJarName = new OptionKey<>(""); + @Option(help = "Track primitive values using the infrastructure of points-to analysis.")// public static final OptionKey TrackPrimitiveValues = new OptionKey<>(true); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java index 61f4567aebe6..2a0b0d1fa6ac 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java @@ -31,6 +31,7 @@ import java.lang.reflect.Executable; import java.lang.reflect.Modifier; import java.lang.reflect.Type; +import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -119,6 +120,8 @@ public abstract class AnalysisMethod extends AnalysisElement implements WrappedJ private static final AtomicReferenceFieldUpdater reachableInCurrentLayerUpdater = AtomicReferenceFieldUpdater .newUpdater(AnalysisMethod.class, Boolean.class, "reachableInCurrentLayer"); + private boolean isJudgeTestMethod; + public record Signature(String name, AnalysisType[] parameterTypes) { } @@ -276,6 +279,20 @@ protected AnalysisMethod(AnalysisUniverse universe, ResolvedJavaMethod wrapped, parsingContextMaxDepth = PointstoOptions.ParsingContextMaxDepth.getValue(declaringClass.universe.hostVM.options()); this.enableReachableInCurrentLayer = universe.hostVM.enableReachableInCurrentLayer(); + + var judgeTestJar = PointstoOptions.JudgeJarName.getValue(getUniverse().hostVM.options()); + if (judgeTestJar != null && !judgeTestJar.isBlank()) { + ProtectionDomain protectionDomain = getDeclaringClass().getJavaClass().getProtectionDomain(); + if (protectionDomain != null) { + var codeSource = protectionDomain.getCodeSource(); + if (codeSource != null) { + var location = codeSource.getLocation(); + if (location != null && location.toString().contains(judgeTestJar)) { + isJudgeTestMethod = true; + } + } + } + } } @SuppressWarnings("this-escape") @@ -907,6 +924,10 @@ public Type[] getGenericParameterTypes() { @Override public boolean canBeInlined() { + if (isJudgeTestMethod) { + System.out.println("Disallow inlining of " + this); + return false; + } return !hasNeverInlineDirective(); } 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 281a14d0094d..13bf9d82aec8 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 @@ -50,6 +50,7 @@ import java.util.Map; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -397,7 +398,7 @@ private static void printMethodNodes(Collection methods, PrintWriter } private static void printInvokeNodes(Map methodToNode, PrintWriter writer) { - writer.println(convertToCSV("Id", "MethodId", "BytecodeIndexes", "TargetId", "IsDirect")); + writer.println(convertToCSV("Id", "MethodId", "BytecodeIndexes", "TargetId", "IsDirect", "LineNumbers")); /* * Methods that act as call targets, but are not reachable (e.g. abstract methods), will not * have a MethodNode allocated yet. We store them in a separate map, because methodToNode @@ -448,9 +449,10 @@ private static List invokeNodeInfo(Map metho return Arrays.asList( String.valueOf(invoke.id), String.valueOf(method.id), - showBytecodeIndexes(bytecodeIndexes(invoke)), + showIndexes(extractIndexes(invoke, source -> source.bci)), String.valueOf(targetMethod.id), - String.valueOf(invoke.isDirectInvoke)); + String.valueOf(invoke.isDirectInvoke), + showIndexes(extractIndexes(invoke, source -> source.trace.getLineNumber()))); } private static List callTargetInfo(InvokeNode invoke, Node callee) { @@ -458,13 +460,13 @@ private static List callTargetInfo(InvokeNode invoke, Node callee) { return Arrays.asList(String.valueOf(invoke.id), String.valueOf(node.id)); } - private static List bytecodeIndexes(InvokeNode node) { + private static List extractIndexes(InvokeNode node, Function extractor) { return Stream.of(node.sourceReferences) - .map(source -> source.bci) + .map(extractor) .collect(Collectors.toList()); } - private static String showBytecodeIndexes(List bytecodeIndexes) { + private static String showIndexes(List bytecodeIndexes) { return bytecodeIndexes.stream() .map(String::valueOf) .collect(Collectors.joining("->"));