Skip to content

Commit bfd33f4

Browse files
committed
Simplify AccessAdvisor boot startup checking
1 parent 87f88bb commit bfd33f4

File tree

2 files changed

+49
-46
lines changed

2 files changed

+49
-46
lines changed

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/AccessAdvisor.java

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -209,61 +209,64 @@ public boolean shouldIgnore(LazyValue<String> queriedClass, LazyValue<String> ca
209209
return shouldIgnore(queriedClass, callerClass, true);
210210
}
211211

212-
record JNICallDescriptor(String declaringClass, String name, String signature, boolean required) {
213-
public boolean matches(String otherDeclaringClass, String otherName, String otherSignature) {
214-
return (declaringClass == null || declaringClass.equals(otherDeclaringClass)) &&
212+
record JNICallDescriptor(String jniFunction, String declaringClass, String name, String signature, boolean required) {
213+
public boolean matches(String otherJniFunction, String otherDeclaringClass, String otherName, String otherSignature) {
214+
return jniFunction.equals(otherJniFunction) &&
215+
(declaringClass == null || declaringClass.equals(otherDeclaringClass)) &&
215216
(otherName == null || name.equals(otherName)) &&
216217
(otherSignature == null || signature.equals(otherSignature));
217218
}
218219
}
219220

220221
private static final JNICallDescriptor[] JNI_STARTUP_SEQUENCE = new JNICallDescriptor[]{
221-
new JNICallDescriptor("sun.launcher.LauncherHelper", "getApplicationClass", "()Ljava/lang/Class;", true),
222-
new JNICallDescriptor("java.lang.Class", "getCanonicalName", "()Ljava/lang/String;", false),
223-
new JNICallDescriptor("java.lang.String", "lastIndexOf", "(I)I", false),
224-
new JNICallDescriptor("java.lang.String", "substring", "(I)Ljava/lang/String;", false),
225-
new JNICallDescriptor("java.lang.System", "getProperty", "(Ljava/lang/String;)Ljava/lang/String;", false),
226-
new JNICallDescriptor("java.lang.System", "setProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false),
227-
new JNICallDescriptor(null, "main", "([Ljava/lang/String;)V", true),
222+
new JNICallDescriptor("GetStaticMethodID", "sun.launcher.LauncherHelper", "getApplicationClass", "()Ljava/lang/Class;", true),
223+
new JNICallDescriptor("GetMethodID", "java.lang.Class", "getCanonicalName", "()Ljava/lang/String;", false),
224+
new JNICallDescriptor("GetMethodID", "java.lang.String", "lastIndexOf", "(I)I", false),
225+
new JNICallDescriptor("GetMethodID", "java.lang.String", "substring", "(I)Ljava/lang/String;", false),
226+
new JNICallDescriptor("GetStaticMethodID", "java.lang.System", "getProperty", "(Ljava/lang/String;)Ljava/lang/String;", false),
227+
new JNICallDescriptor("GetStaticMethodID", "java.lang.System", "setProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false),
228+
new JNICallDescriptor("GetStaticMethodID", null, "main", "([Ljava/lang/String;)V", true),
228229
};
230+
private static final int JNI_STARTUP_COMPLETE = JNI_STARTUP_SEQUENCE.length;
231+
private static final int JNI_STARTUP_MISMATCH_COMPLETE = JNI_STARTUP_COMPLETE + 1;
229232

230-
public boolean shouldIgnoreJniLookup(LazyValue<String> queriedClass, LazyValue<String> name, LazyValue<String> signature, LazyValue<String> callerClass) {
231-
assert !shouldIgnore(queriedClass, callerClass) : "must have been checked before";
232-
if (!heuristicsEnabled) {
233+
/**
234+
* The JVM uses JNI calls internally when starting up. To avoid emitting configuration for these
235+
* calls, we ignore calls until an expected sequence of calls ({@link #JNI_STARTUP_SEQUENCE}) is
236+
* observed.
237+
*/
238+
public boolean shouldIgnoreJniLookup(String jniFunction, LazyValue<String> queriedClass, LazyValue<String> name, LazyValue<String> signature, LazyValue<String> callerClass) {
239+
if (shouldIgnore(queriedClass, callerClass)) {
240+
throw new AssertionError("shouldIgnore must have been checked before shouldIgnoreJniLookup");
241+
}
242+
if (!heuristicsEnabled || launchPhase >= JNI_STARTUP_COMPLETE) {
243+
// Startup sequence completed (or we're not using the startup heuristics).
233244
return false;
234245
}
235-
if (launchPhase >= 0) {
236-
JNICallDescriptor expectedCall = JNI_STARTUP_SEQUENCE[launchPhase];
237-
while (!expectedCall.matches(queriedClass.get(), name.get(), signature.get())) {
238-
if ("sun.launcher.LauncherHelper".equals(queriedClass.get())) {
239-
return true;
240-
}
241-
if (!expectedCall.required) {
242-
launchPhase++;
243-
expectedCall = JNI_STARTUP_SEQUENCE[launchPhase];
244-
} else {
245-
launchPhase = -1;
246-
return false;
247-
}
248-
}
249-
if (name.get() != null && signature.get() != null) {
250-
/*
251-
* We ignore class lookups before field/method lookups but don't skip to the next
252-
* query
253-
*/
254-
launchPhase++;
246+
if (!"GetStaticMethodID".equals(jniFunction) && !"GetMethodID".equals(jniFunction)) {
247+
// Ignore function calls for functions not tracked by the startup sequence.
248+
return true;
249+
}
250+
251+
JNICallDescriptor expectedCall = JNI_STARTUP_SEQUENCE[launchPhase];
252+
while (!expectedCall.matches(jniFunction, queriedClass.get(), name.get(), signature.get())) {
253+
if ("sun.launcher.LauncherHelper".equals(queriedClass.get())) {
254+
// Ignore mismatched calls from sun.launcher.LauncherHelper.
255+
return true;
255256
}
256-
if (launchPhase == JNI_STARTUP_SEQUENCE.length) {
257-
launchPhase = -1;
257+
258+
if (expectedCall.required) {
259+
// Mismatch on a required call. Mark startup as complete and start tracing JNI
260+
// calls. (We prefer to emit extraneous configuration than to lose configuration).
261+
launchPhase = JNI_STARTUP_MISMATCH_COMPLETE;
262+
return false;
258263
}
259-
return true;
264+
265+
// The call is optional (e.g., it only happens on some platforms). Skip it.
266+
launchPhase++;
267+
expectedCall = JNI_STARTUP_SEQUENCE[launchPhase];
260268
}
261-
/*
262-
* NOTE: JVM invocations cannot be reliably filtered with callerClass == null because these
263-
* could also be calls in a manually launched thread which is attached to JNI, but is not
264-
* executing Java code (yet).
265-
*/
266-
return false;
269+
return true;
267270
}
268271

269272
public static boolean shouldIgnoreResourceLookup(LazyValue<String> resource) {

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ void processEntry(EconomicMap<String, ?> entry, ConfigurationSet configurationSe
6868
LazyValue<String> forNameStringLazyValue = lazyValue(forNameString);
6969
if (!advisor.shouldIgnore(forNameStringLazyValue, callerClassLazyValue)) {
7070
if (function.equals("FindClass")) {
71-
if (!advisor.shouldIgnoreJniLookup(forNameStringLazyValue, lazyNull(), lazyNull(), callerClassLazyValue)) {
71+
if (!advisor.shouldIgnoreJniLookup(function, forNameStringLazyValue, lazyNull(), lazyNull(), callerClassLazyValue)) {
7272
configurationSet.getJniConfiguration().getOrCreateType(condition, forNameString);
7373
}
7474
} else if (!AccessAdvisor.PROXY_CLASS_NAME_PATTERN.matcher(lookupName).matches()) { // DefineClass
@@ -99,7 +99,7 @@ void processEntry(EconomicMap<String, ?> entry, ConfigurationSet configurationSe
9999
expectSize(args, 2);
100100
String name = (String) args.get(0);
101101
String signature = (String) args.get(1);
102-
if (!advisor.shouldIgnoreJniLookup(lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) {
102+
if (!advisor.shouldIgnoreJniLookup(function, lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) {
103103
config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, declaration);
104104
if (!declaringClassOrClazz.equals(clazz)) {
105105
config.getOrCreateType(condition, clazz);
@@ -112,7 +112,7 @@ void processEntry(EconomicMap<String, ?> entry, ConfigurationSet configurationSe
112112
expectSize(args, 2);
113113
String name = (String) args.get(0);
114114
String signature = (String) args.get(1);
115-
if (!advisor.shouldIgnoreJniLookup(lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) {
115+
if (!advisor.shouldIgnoreJniLookup(function, lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) {
116116
config.getOrCreateType(condition, declaringClassOrClazz).addField(name, declaration, false);
117117
if (!declaringClassOrClazz.equals(clazz)) {
118118
config.getOrCreateType(condition, clazz);
@@ -124,7 +124,7 @@ void processEntry(EconomicMap<String, ?> entry, ConfigurationSet configurationSe
124124
expectSize(args, 1); // exception message, ignore
125125
String name = ConfigurationMethod.CONSTRUCTOR_NAME;
126126
String signature = "(Ljava/lang/String;)V";
127-
if (!advisor.shouldIgnoreJniLookup(lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) {
127+
if (!advisor.shouldIgnoreJniLookup(function, lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) {
128128
config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, declaration);
129129
assert declaringClassOrClazz.equals(clazz) : "Constructor can only be accessed via declaring class";
130130
}

0 commit comments

Comments
 (0)