Skip to content
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

Override TR_ResolvedJ9Method::isCompilable() for JITServer #17931

Closed
mpirvu opened this issue Aug 10, 2023 · 6 comments
Closed

Override TR_ResolvedJ9Method::isCompilable() for JITServer #17931

mpirvu opened this issue Aug 10, 2023 · 6 comments
Labels
comp:jitserver Artifacts related to JIT-as-a-Service project

Comments

@mpirvu
Copy link
Contributor

mpirvu commented Aug 10, 2023

TR_ResolvedJ9Method::isCompilable() accesses some fields from JavaVM and it's not safe to use as is in out-of-process compilations.
To fix this we have two alternatives:

  1. When the server constructs a ResolvedJ9Method, a mirror resolved method is also built at the client. When that happens we can precompute the isCompilable information and store it into a boolean field which will be passed to the server. The disadvantage is that we have to precompute this field even though the optimizer may not ask about it.
  2. For each client, store at the server the JavaVM fields that isCompilable is asking for.

Solution 2 should be preferred because it has less overhead. However, solution 2 is contingent on these JavaVM fields being populated exactly once before first compilation takes place:
javaVM->doPrivilegedWithContextMethodID1
javaVM->doPrivilegedWithContextMethodID2
javaVM->doPrivilegedWithContextPermissionMethodID1
javaVM->doPrivilegedWithContextPermissionMethodID2
javaVM->nativeLibrariesLoadMethodID

@mpirvu mpirvu added the comp:jitserver Artifacts related to JIT-as-a-Service project label Aug 10, 2023
@mpirvu
Copy link
Contributor Author

mpirvu commented Aug 10, 2023

Rectification: the code reads

   J9JNIMethodID * magic = (J9JNIMethodID *) javaVM->doPrivilegedWithContextMethodID1;
   if (magic && ramMethod() == magic->method)

so it's not doPrivilegedWithContextMethodID1 that needs to be cached, but the ramMethod stored inside.

This field is set in boolean JNICALL Java_java_security_AccessController_initializeInternal(JNIEnv *env, jclass thisClz)

jboolean JNICALL Java_java_security_AccessController_initializeInternal(JNIEnv *env, jclass thisClz)
{
	J9JavaVM *javaVM = ((J9VMThread *) env)->javaVM;
	jclass accessControllerClass;
	jmethodID mid;

	accessControllerClass = (*env)->FindClass(env, "java/security/AccessController");
	if(accessControllerClass == NULL) goto fail;
      ...
	mid = (*env)->GetStaticMethodID(env, accessControllerClass, "doPrivileged", "(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;");
	if(mid == NULL) goto fail;
	javaVM->doPrivilegedWithContextMethodID1 = (UDATA) mid;
     ...
}

I don't know if java/security/AccessController can ever be modified, but if it can be, we must reset the cached fields.

@mpirvu
Copy link
Contributor Author

mpirvu commented Aug 10, 2023

I see code that tests whether doPrivilegedWithContextMethodID1 is NULL. So, this field starts as NULL and when the Java program executes the JNI call java/security/AccessController.initializeInternal(), that field (and others) are populated.
Interestingly I found on the internet that java.security.AccessController is deprecated for removal since Java17

@mpirvu
Copy link
Contributor Author

mpirvu commented Aug 10, 2023

Another field that ResolveJ9Method looks at is javaVM->nativeLibrariesLoadMethodID;
This is set in standardInit(J9JavaVM *vm, char *dllName), but only for Java >= 15

if JAVA_SPEC_VERSION >= 15
				JNIEnv *env = (JNIEnv*)vmThread;
				jclass clz = (*env)->FindClass(env, "jdk/internal/loader/NativeLibraries");
				jmethodID mid = NULL;
				if (NULL == clz) {
					goto _fail;
				}
#if (17 <= JAVA_SPEC_VERSION) && (JAVA_SPEC_VERSION <= 18)
				mid = (*env)->GetStaticMethodID(env, clz, "load", "(Ljdk/internal/loader/NativeLibraries$NativeLibraryImpl;Ljava/lang/String;ZZZ)Z");
#else /* (17 <= JAVA_SPEC_VERSION) && (JAVA_SPEC_VERSION <= 18) */
				mid = (*env)->GetStaticMethodID(env, clz, "load", "(Ljdk/internal/loader/NativeLibraries$NativeLibraryImpl;Ljava/lang/String;ZZ)Z");
#endif /* (17 <= JAVA_SPEC_VERSION) && (JAVA_SPEC_VERSION <= 18) */
				if (NULL == mid) {
					goto _fail;
				}
				vm->nativeLibrariesLoadMethodID = (UDATA)mid;
				Trc_JCL_init_nativeLibrariesLoadMethodID(vmThread, vm->nativeLibrariesLoadMethodID);
#endif /* JAVA_SPEC_VERSION >= 15 */

The method id corresponds to jdk/internal/loader/NativeLibraries.load(Ljdk/internal/loader/NativeLibraries$NativeLibraryImpl;Ljava/lang/String;ZZZ)Z

@mpirvu
Copy link
Contributor Author

mpirvu commented Aug 10, 2023

Interesting point:
TR_ResolvedJ9MethodBase::isCompilable(TR_Memory * trMemory) already rejects the following methods (based on name)

"java/security/AccessController.doPrivileged(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;",
"java/security/AccessController.doPrivileged(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;",
"java/security/AccessController.doPrivileged(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;[Ljava/security/Permission;)Ljava/lang/Object;",
"java/security/AccessController.doPrivileged(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;[Ljava/security/Permission;)Ljava/lang/Object;",

TR_ResolvedJ9Method::isCompilable(TR_Memory * trMemory) first applies the filters from TR_ResolvedJ9MethodBase::isCompilable(trMemory) and filters out
javaVM->doPrivilegedWithContextMethodID1
javaVM->doPrivilegedWithContextMethodID2
javaVM->doPrivilegedWithContextPermissionMethodID1;
javaVM->doPrivilegedWithContextPermissionMethodID2;
which correspond to
doPrivileged(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;)
doPrivileged(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;)
"doPrivileged(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;[Ljava/security/Permission;)Ljava/lang/Object;)
"doPrivileged(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;[Ljava/security/Permission;)Ljava/lang/Object;)

There are also these two which are not captured by TR_ResolvedJ9Method::isCompilable
doPrivileged(Ljava/security/PrivilegedAction;)Ljava/lang/Object;)
doPrivileged(Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;)

So, to recap:

  1. We don't need the "doPrivileged" code in TR_ResolvedJ9Method::isCompilable because TR_ResolvedJ9MethodBase::isCompilable() handles those cases already.
  2. We may need to add two extra doPrivileged methods in TR_ResolvedJ9MethodBase::isCompilable()

@mpirvu
Copy link
Contributor Author

mpirvu commented Aug 10, 2023

Even "jdk/internal/loader/NativeLibraries.load()" is handled by TR_ResolvedJ9MethodBase::isCompilable(TR_Memory * trMemory), so the code can be cleaned-up a lot.

Proposed solution is to simplify TR_ResolvedJ9Method::isCompilable(TR_Memory * trMemory) by eliminating the following code

   /* AOT handles this by having the names in the string compare list */
   J9JavaVM * javaVM = _fe->_jitConfig->javaVM;
   J9JNIMethodID * magic = (J9JNIMethodID *) javaVM->doPrivilegedWithContextMethodID1;
   if (magic && ramMethod() == magic->method)
      return false;

   magic = (J9JNIMethodID *) javaVM->doPrivilegedWithContextMethodID2;
   if (magic && ramMethod() == magic->method)
      return false;

   magic = (J9JNIMethodID *) javaVM->doPrivilegedWithContextPermissionMethodID1;
   if (magic && ramMethod() == magic->method)
      return false;

   magic = (J9JNIMethodID *) javaVM->doPrivilegedWithContextPermissionMethodID2;
   if (magic && ramMethod() == magic->method)
      return false;

   magic = (J9JNIMethodID *) javaVM->nativeLibrariesLoadMethodID;
   if (magic && ramMethod() == magic->method) {
      return false;
   }

@mpirvu
Copy link
Contributor Author

mpirvu commented Aug 10, 2023

Attn: @SajinaKandy

SajinaKandy added a commit to SajinaKandy/openj9 that referenced this issue Aug 14, 2023
TR_ResolvedJ9Method::isCompilable() accesses some fields from JavaVM and it's not safe to use as is in out-of-process compilations.
As described in the issue eclipse-openj9#17931, the deleted "doPrivileged" code is redundant since the base class method TR_ResolvedJ9MethodBase::isCompilable() already filters those methods out.

Closes: #eclipse-openj9#17931
Signed-off-by: Sajina Kandy <[email protected]>
@mpirvu mpirvu closed this as completed Jan 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp:jitserver Artifacts related to JIT-as-a-Service project
Projects
None yet
Development

No branches or pull requests

1 participant