From 12e8004ce298c3448501a64a72b93dbb151027bb Mon Sep 17 00:00:00 2001 From: Nathan Henderson Date: Wed, 4 Dec 2024 11:12:41 -0500 Subject: [PATCH] Add hiddenInstanceFields to the VM snapshot On restore runs, hidden instance fields have already been added. This patch adds the JavaVM::hiddenInstanceFields to the snapshot and adds the functionality to find and fill-in the hidden instance field's correct offset during restore runs. Related: https://github.com/eclipse-openj9/openj9/issues/20612 Signed-off-by: Nathan Henderson --- runtime/oti/SnapshotFileFormat.h | 1 + runtime/vm/VMSnapshotImpl.cpp | 27 +++++++++++++ runtime/vm/VMSnapshotImpl.hpp | 3 ++ runtime/vm/jvminit.c | 4 +- runtime/vm/resolvefield.cpp | 68 +++++++++++++++++++++++++++++--- 5 files changed, 97 insertions(+), 6 deletions(-) diff --git a/runtime/oti/SnapshotFileFormat.h b/runtime/oti/SnapshotFileFormat.h index eaabfa12283..141f032f05d 100644 --- a/runtime/oti/SnapshotFileFormat.h +++ b/runtime/oti/SnapshotFileFormat.h @@ -56,6 +56,7 @@ typedef struct SavedJ9JavaVMStructures { J9ClassLoader *systemClassLoader; J9ClassLoader *extensionClassLoader; J9ClassLoader *applicationClassLoader; + J9HiddenInstanceField *hiddenInstanceFields; } SavedJ9JavaVMStructures; /* diff --git a/runtime/vm/VMSnapshotImpl.cpp b/runtime/vm/VMSnapshotImpl.cpp index ebc223d521e..f9d88f63b7b 100644 --- a/runtime/vm/VMSnapshotImpl.cpp +++ b/runtime/vm/VMSnapshotImpl.cpp @@ -374,6 +374,19 @@ VMSnapshotImpl::isImmortalClassLoader(J9ClassLoader *classLoader) return isImmortal; } +void +VMSnapshotImpl::saveHiddenInstanceFields() +{ + _snapshotHeader->savedJavaVMStructs.hiddenInstanceFields = _vm->hiddenInstanceFields; +} + +void +VMSnapshotImpl::restoreHiddenInstanceFields() +{ + _vm->hiddenInstanceFields = _snapshotHeader->savedJavaVMStructs.hiddenInstanceFields; +} + + void printAllSegments(J9MemorySegmentList *segmentList, J9JavaVM *_vm) { @@ -806,6 +819,7 @@ VMSnapshotImpl::saveJ9JavaVMStructures() saveClassLoaderBlocks(); saveMemorySegments(); savePrimitiveAndArrayClasses(); + saveHiddenInstanceFields(); _snapshotHeader->vm = _vm; } @@ -821,6 +835,7 @@ VMSnapshotImpl::restoreJ9JavaVMStructures() restoreClassLoaderBlocks(); restoreMemorySegments(); restorePrimitiveAndArrayClasses(); + restoreHiddenInstanceFields(); if (omrthread_monitor_init_with_name(&_vm->classMemorySegments->segmentMutex, 0, "VM class mem segment list")) { success = false; @@ -833,6 +848,17 @@ VMSnapshotImpl::restoreJ9JavaVMStructures() return success; } +/** + * Some data written to the snapshot cannot be freed at their usual points during VM shutdown. + * This function handles those data to ensure that they are freed after they are written to + * the snapshot. + */ +void +VMSnapshotImpl::freeJ9JavaVMStructures() +{ + freeHiddenInstanceFieldsList(_vm); +} + bool VMSnapshotImpl::writeSnapshotToFile() { @@ -1083,6 +1109,7 @@ teardownVMSnapshotImpl(J9JavaVM *javaVM) } else { vmSnapshotImpl->saveMemorySegments(); } + vmSnapshotImpl->freeJ9JavaVMStructures(); } extern "C" void diff --git a/runtime/vm/VMSnapshotImpl.hpp b/runtime/vm/VMSnapshotImpl.hpp index be2b8d9bbde..84c831740cf 100644 --- a/runtime/vm/VMSnapshotImpl.hpp +++ b/runtime/vm/VMSnapshotImpl.hpp @@ -93,6 +93,8 @@ class VMSnapshotImpl bool restoreJ9JavaVMStructures(); void savePrimitiveAndArrayClasses(); bool isImmortalClassLoader(J9ClassLoader *classLoader); + void saveHiddenInstanceFields(); + void restoreHiddenInstanceFields(); J9MemorySegmentList *copyUnPersistedMemorySegmentsToNewList(J9MemorySegmentList *oldMemorySegmentList); protected: @@ -118,6 +120,7 @@ class VMSnapshotImpl void fixupJITVtable(J9Class *ramClass); void fixupVMStructures(); void writeSnapshot(); + void freeJ9JavaVMStructures(); void *subAllocateMemory(uintptr_t byteAmount, bool sub4G); void *reallocateMemory(void *address, uintptr_t byteAmount, bool sub4G); diff --git a/runtime/vm/jvminit.c b/runtime/vm/jvminit.c index f898e35a70e..eb6b7fc8b24 100644 --- a/runtime/vm/jvminit.c +++ b/runtime/vm/jvminit.c @@ -768,7 +768,9 @@ freeJavaVM(J9JavaVM * vm) #endif /* J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH */ freeNativeMethodBindTable(vm); - freeHiddenInstanceFieldsList(vm); + if (!IS_SNAPSHOTTING_ENABLED(vm)) { + freeHiddenInstanceFieldsList(vm); + } cleanupLockwordConfig(vm); cleanupEnsureHashedConfig(vm); diff --git a/runtime/vm/resolvefield.cpp b/runtime/vm/resolvefield.cpp index 23ef28c6537..fdf9d31bb30 100644 --- a/runtime/vm/resolvefield.cpp +++ b/runtime/vm/resolvefield.cpp @@ -64,6 +64,9 @@ static J9ROMFieldShape* findFieldInTable(J9VMThread *vmThread, J9Class *clazz, U static J9ROMFieldShape * findFieldInClass (J9VMThread *vmStruct, J9Class *clazz, U_8 *fieldName, UDATA fieldNameLength, U_8 *signature, UDATA signatureLength, UDATA *offsetOrAddress, J9Class **definingClass); static J9ROMFieldShape* findFieldAndCheckVisibility (J9VMThread *vmStruct, J9Class *clazz, U_8 *fieldName, UDATA fieldNameLength, U_8 *signature, UDATA signatureLength, J9Class **definingClass, UDATA *offsetOrAddress, UDATA options, J9Class *sourceClass); static J9ROMFieldShape* findField (J9VMThread *vmStruct, J9Class *clazz, U_8 *fieldName, UDATA fieldNameLength, U_8 *signature, UDATA signatureLength, J9Class **definingClass, UDATA *offsetOrAddress, UDATA options); +#if defined(J9VM_OPT_SNAPSHOTS) +static UDATA findHiddenInstanceFieldOffset(J9JavaVM *javaVM, const char *className, const char *fieldName, const char *signature); +#endif /* defined(J9VM_OPT_SNAPSHOTS) */ VMINLINE static UDATA calculateJ9UTFSize(UDATA stringLength); VMINLINE static UDATA calculateFakeJ9ROMFieldShapeSize(UDATA nameLength, UDATA signatureLength); static void initFakeJ9ROMFieldShape(J9ROMFieldShape *shape, U_16 nameLength, U_8 *nameData, U_16 signatureLength, U_8 *signatureData); @@ -238,7 +241,41 @@ findFieldInClass(J9VMThread *vmStruct, J9Class *clazz, U_8 *fieldName, UDATA fie return shape; } - +#if defined(J9VM_OPT_SNAPSHOTS) +static UDATA +findHiddenInstanceFieldOffset(J9JavaVM *javaVM, const char *className, const char *fieldName, const char *signature) +{ + J9HiddenInstanceField *field = javaVM->hiddenInstanceFields; + UDATA offset = UDATA_MAX; + const UDATA classNameLength = strlen(className); + const UDATA fieldNameLength = strlen(fieldName); + const UDATA signatureLength = strlen(signature); + + while (NULL != field) { + J9UTF8 *currentClassName = field->className; + J9UTF8 *currentFieldName = J9ROMFIELDSHAPE_NAME(field->shape); + J9UTF8 *currentFieldSig = J9ROMFIELDSHAPE_SIGNATURE(field->shape); + const UDATA currentClassNameLength = J9UTF8_LENGTH(currentClassName); + const UDATA currentFieldNameLength = J9UTF8_LENGTH(currentFieldName); + const UDATA currentSignatureLength = J9UTF8_LENGTH(currentFieldSig); + + if ((classNameLength == currentClassNameLength) + && (fieldNameLength == currentFieldNameLength) + && (signatureLength == currentSignatureLength) + ) { + if (J9UTF8_DATA_EQUALS(J9UTF8_DATA(currentClassName), currentClassNameLength, className, classNameLength) + && J9UTF8_DATA_EQUALS(J9UTF8_DATA(currentFieldName), currentFieldNameLength, fieldName, fieldNameLength) + && J9UTF8_DATA_EQUALS(J9UTF8_DATA(currentFieldSig), currentSignatureLength, signature, signatureLength) + ) { + offset = field->fieldOffset; + break; + } + } + field = field->next; + } + return offset; +} +#endif /* defined(J9VM_OPT_SNAPSHOTS) */ IDATA instanceFieldOffset(J9VMThread *vmStruct, J9Class *clazz, U_8 *fieldName, UDATA fieldNameLength, U_8 *signature, UDATA signatureLength, J9Class **definingClass, UDATA *instanceField, UDATA options) @@ -488,7 +525,9 @@ initializeHiddenInstanceFieldsList(J9JavaVM *vm) goto destroyMutexAndCleanup; } - vm->hiddenInstanceFields = NULL; + if (!IS_RESTORE_RUN(vm)) { + vm->hiddenInstanceFields = NULL; + } exit: return rc; @@ -516,7 +555,15 @@ freeHiddenInstanceFieldsList(J9JavaVM *vm) while (NULL != field) { J9HiddenInstanceField *next = field->next; - j9mem_free_memory(field); +#if defined(J9VM_OPT_SNAPSHOTS) + if (IS_SNAPSHOT_RUN(vm)) { + VMSNAPSHOTIMPLPORT_ACCESS_FROM_JAVAVM(vm); + vmsnapshot_free_memory(field); + } else if (!IS_RESTORE_RUN(vm)) +#endif /* defined(J9VM_OPT_SNAPSHOTS) */ + { + j9mem_free_memory(field); + } field = next; } vm->hiddenInstanceFields = NULL; @@ -579,10 +626,13 @@ addHiddenInstanceField(J9JavaVM *vm, const char *className, const char *fieldNam if ((NULL != vm->systemClassLoader) && (NULL != hashClassTableAt(vm->systemClassLoader, (U_8*)className, classNameLength)) ) { - /* By this point during a restore run, the hidden field is already added. */ +#if defined(J9VM_OPT_SNAPSHOTS) + /* By this point during a restore run, the hidden field is already added. Just fill in the offset. */ if (IS_RESTORE_RUN(vm)) { + *offsetReturn = findHiddenInstanceFieldOffset(vm, className, fieldName, fieldSignature); return 0; } +#endif /* defined(J9VM_OPT_SNAPSHOTS) */ return 2; } @@ -604,7 +654,15 @@ addHiddenInstanceField(J9JavaVM *vm, const char *className, const char *fieldNam } /* All is good - proceed to add the entry to the start of the list. */ - field = (J9HiddenInstanceField *) j9mem_allocate_memory(neededSize, OMRMEM_CATEGORY_VM); +#if defined(J9VM_OPT_SNAPSHOTS) + if (IS_SNAPSHOT_RUN(vm)) { + VMSNAPSHOTIMPLPORT_ACCESS_FROM_JAVAVM(vm); + field = (J9HiddenInstanceField *)vmsnapshot_allocate_memory(neededSize, OMRMEM_CATEGORY_VM); + } else +#endif /* defined(J9VM_OPT_SNAPSHOTS) */ + { + field = (J9HiddenInstanceField *)j9mem_allocate_memory(neededSize, OMRMEM_CATEGORY_VM); + } if (NULL == field) { omrthread_monitor_exit(vm->hiddenInstanceFieldsMutex); return 4;