From b261309785796fe7b487869c78219c971967ccd1 Mon Sep 17 00:00:00 2001 From: Gengchen Tuo Date: Thu, 21 Nov 2024 13:10:28 -0500 Subject: [PATCH] Add JFR ClassLoadingStatistics support Signed-off-by: Gengchen Tuo --- runtime/oti/j9consts.h | 1 + runtime/oti/j9nonbuilder.h | 9 ++++++++ runtime/vm/JFRChunkWriter.cpp | 25 +++++++++++++++++++++ runtime/vm/JFRChunkWriter.hpp | 8 +++++++ runtime/vm/JFRConstantPoolTypes.cpp | 22 ++++++++++++++++++ runtime/vm/JFRConstantPoolTypes.hpp | 32 ++++++++++++++++++++++++++ runtime/vm/classallocation.c | 5 +++++ runtime/vm/createramclass.cpp | 4 ++++ runtime/vm/jfr.cpp | 35 +++++++++++++++++++++++++++++ 9 files changed, 141 insertions(+) diff --git a/runtime/oti/j9consts.h b/runtime/oti/j9consts.h index 9715affa6b9..9f41c7cb943 100644 --- a/runtime/oti/j9consts.h +++ b/runtime/oti/j9consts.h @@ -942,6 +942,7 @@ extern "C" { #define J9JFR_EVENT_TYPE_OBJECT_WAIT 4 #define J9JFR_EVENT_TYPE_CPU_LOAD 5 #define J9JFR_EVENT_TYPE_THREAD_CPU_LOAD 6 +#define J9JFR_EVENT_TYPE_CLASS_LOADING_STATISTICS 7 /* JFR thread states */ diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index 5c562bbec62..0781c01b57c 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -434,6 +434,12 @@ typedef struct J9JFRThreadCPULoad { float system; } J9JFRThreadCPULoad; +typedef struct J9JFRClassLoadingStatistics { + J9JFR_EVENT_COMMON_FIELDS + I_64 loadedClassCount; + I_64 unloadedClassCount; +} J9JFRClassLoadingStatistics; + #endif /* defined(J9VM_OPT_JFR) */ /* @ddr_namespace: map_to_type=J9CfrError */ @@ -3601,6 +3607,9 @@ typedef struct J9ClassLoader { omrthread_rwmutex_t cpEntriesMutex; UDATA initClassPathEntryCount; UDATA asyncGetCallTraceUsed; +#if defined(J9VM_OPT_JFR) + UDATA loadedClassCount; +#endif /* defined(J9VM_OPT_JFR) */ } J9ClassLoader; #define J9CLASSLOADER_SHARED_CLASSES_ENABLED 8 diff --git a/runtime/vm/JFRChunkWriter.cpp b/runtime/vm/JFRChunkWriter.cpp index 56f9d229ef8..faaf093f7eb 100644 --- a/runtime/vm/JFRChunkWriter.cpp +++ b/runtime/vm/JFRChunkWriter.cpp @@ -863,4 +863,29 @@ VM_JFRChunkWriter::writeInitialEnvironmentVariableEvents() } } } + +void +VM_JFRChunkWriter::writeClassLoadingStatisticsEvent(void *anElement, void *userData) +{ + ClassLoadingStatisticsEntry *entry = (ClassLoadingStatisticsEntry *)anElement; + VM_BufferWriter *_bufferWriter = (VM_BufferWriter *)userData; + + /* reserve size field */ + U_8 *dataStart = _bufferWriter->getAndIncCursor(sizeof(U_32)); + + /* write event type */ + _bufferWriter->writeLEB128(ClassLoadingStatisticsID); + + /* write start time */ + _bufferWriter->writeLEB128(entry->ticks); + + /* write user loaded class count */ + _bufferWriter->writeLEB128(entry->loadedClassCount); + + /* write system unloaded class count */ + _bufferWriter->writeLEB128(entry->unloadedClassCount); + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); +} #endif /* defined(J9VM_OPT_JFR) */ diff --git a/runtime/vm/JFRChunkWriter.hpp b/runtime/vm/JFRChunkWriter.hpp index f5fa40a5054..03f88cc544b 100644 --- a/runtime/vm/JFRChunkWriter.hpp +++ b/runtime/vm/JFRChunkWriter.hpp @@ -74,6 +74,7 @@ enum MetadataTypeID { CPUInformationID = 92, CPULoadID = 94, ThreadCPULoadID = 95, + ClassLoadingStatisticsID = 99, PhysicalMemoryID = 107, ExecutionSampleID = 108, ThreadID = 163, @@ -164,6 +165,7 @@ class VM_JFRChunkWriter { static constexpr int CPU_LOAD_EVENT_SIZE = (3 * sizeof(float)) + (3 * sizeof(I_64)); static constexpr int THREAD_CPU_LOAD_EVENT_SIZE = (2 * sizeof(float)) + (4 * sizeof(I_64)); static constexpr int INITIAL_ENVIRONMENT_VARIABLE_EVENT_SIZE = 6000; + static constexpr int CLASS_LOADING_STATISTICS_EVENT_SIZE = 5 * sizeof(I_64); static constexpr int METADATA_ID = 1; @@ -339,6 +341,8 @@ class VM_JFRChunkWriter { pool_do(_constantPoolTypes.getThreadCPULoadTable(), &writeThreadCPULoadEvent, _bufferWriter); + pool_do(_constantPoolTypes.getClassLoadingStatisticsTable(), &writeClassLoadingStatisticsEvent, _bufferWriter); + /* Only write constant events in first chunk */ if (0 == _vm->jfrState.jfrChunkCount) { writeJVMInformationEvent(); @@ -668,6 +672,8 @@ class VM_JFRChunkWriter { void writeInitialEnvironmentVariableEvents(); + static void writeClassLoadingStatisticsEvent(void *anElement, void *userData); + UDATA calculateRequiredBufferSize() { @@ -728,6 +734,8 @@ class VM_JFRChunkWriter { requiredBufferSize += _constantPoolTypes.getThreadCPULoadCount() * THREAD_CPU_LOAD_EVENT_SIZE; + requiredBufferSize += _constantPoolTypes.getClassLoadingStatisticsCount() * CLASS_LOADING_STATISTICS_EVENT_SIZE; + return requiredBufferSize; } diff --git a/runtime/vm/JFRConstantPoolTypes.cpp b/runtime/vm/JFRConstantPoolTypes.cpp index 5d3f6090a8a..153e4a7e054 100644 --- a/runtime/vm/JFRConstantPoolTypes.cpp +++ b/runtime/vm/JFRConstantPoolTypes.cpp @@ -1145,6 +1145,28 @@ VM_JFRConstantPoolTypes::addThreadCPULoadEntry(J9JFRThreadCPULoad *threadCPULoad return index; } +U_32 +VM_JFRConstantPoolTypes::addClassLoadingStatisticsEntry(J9JFRClassLoadingStatistics *classLoadingStatisticsData) +{ + ClassLoadingStatisticsEntry *entry = (ClassLoadingStatisticsEntry *)pool_newElement(_classLoadingStatisticsTable); + U_32 index = U_32_MAX; + + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + entry->ticks = classLoadingStatisticsData->startTicks; + entry->loadedClassCount = classLoadingStatisticsData->loadedClassCount; + entry->unloadedClassCount = classLoadingStatisticsData->unloadedClassCount; + + index = _classLoadingStatisticsCount; + _classLoadingStatisticsCount += 1; + +done: + return index; +} + void VM_JFRConstantPoolTypes::printTables() { diff --git a/runtime/vm/JFRConstantPoolTypes.hpp b/runtime/vm/JFRConstantPoolTypes.hpp index c65993e4745..d7171290f77 100644 --- a/runtime/vm/JFRConstantPoolTypes.hpp +++ b/runtime/vm/JFRConstantPoolTypes.hpp @@ -227,6 +227,12 @@ struct ThreadCPULoadEntry { float system; }; +struct ClassLoadingStatisticsEntry { + I_64 ticks; + I_64 loadedClassCount; + I_64 unloadedClassCount; +}; + struct JVMInformationEntry { const char *jvmName; const char *jvmVersion; @@ -308,6 +314,8 @@ class VM_JFRConstantPoolTypes { UDATA _cpuLoadCount; J9Pool *_threadCPULoadTable; UDATA _threadCPULoadCount; + J9Pool *_classLoadingStatisticsTable; + UDATA _classLoadingStatisticsCount; /* Processing buffers */ StackFrame *_currentStackFrameBuffer; @@ -560,6 +568,8 @@ class VM_JFRConstantPoolTypes { U_32 addThreadCPULoadEntry(J9JFRThreadCPULoad *threadCPULoadData); + U_32 addClassLoadingStatisticsEntry(J9JFRClassLoadingStatistics *classLoadingStatisticsData); + J9Pool *getExecutionSampleTable() { return _executionSampleTable; @@ -595,6 +605,11 @@ class VM_JFRConstantPoolTypes { return _threadCPULoadTable; } + J9Pool *getClassLoadingStatisticsTable() + { + return _classLoadingStatisticsTable; + } + UDATA getExecutionSampleCount() { return _executionSampleCount; @@ -630,6 +645,11 @@ class VM_JFRConstantPoolTypes { return _threadCPULoadCount; } + UDATA getClassLoadingStatisticsCount() + { + return _classLoadingStatisticsCount; + } + ClassloaderEntry *getClassloaderEntry() { return _firstClassloaderEntry; @@ -779,6 +799,9 @@ class VM_JFRConstantPoolTypes { case J9JFR_EVENT_TYPE_THREAD_CPU_LOAD: addThreadCPULoadEntry((J9JFRThreadCPULoad *)event); break; + case J9JFR_EVENT_TYPE_CLASS_LOADING_STATISTICS: + addClassLoadingStatisticsEntry((J9JFRClassLoadingStatistics *)event); + break; default: Assert_VM_unreachable(); break; @@ -1102,6 +1125,8 @@ class VM_JFRConstantPoolTypes { , _cpuLoadCount(0) , _threadCPULoadTable(NULL) , _threadCPULoadCount(0) + , _classLoadingStatisticsTable(NULL) + , _classLoadingStatisticsCount(0) , _previousStackTraceEntry(NULL) , _firstStackTraceEntry(NULL) , _previousThreadEntry(NULL) @@ -1216,6 +1241,12 @@ class VM_JFRConstantPoolTypes { goto done; } + _classLoadingStatisticsTable = pool_new(sizeof(ClassLoadingStatisticsEntry), 0, sizeof(U_64), 0, J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM, POOL_FOR_PORT(privatePortLibrary)); + if (NULL == _classLoadingStatisticsTable ) { + _buildResult = OutOfMemory; + goto done; + } + /* Add reserved index for default entries. For strings zero is the empty or NUll string. * For package zero is the deafult package, for Module zero is the unnamed module. ThreadGroup * zero is NULL threadGroup. @@ -1303,6 +1334,7 @@ class VM_JFRConstantPoolTypes { pool_kill(_monitorWaitTable); pool_kill(_cpuLoadTable); pool_kill(_threadCPULoadTable); + pool_kill(_classLoadingStatisticsTable); j9mem_free_memory(_globalStringTable); } diff --git a/runtime/vm/classallocation.c b/runtime/vm/classallocation.c index 01a105dbabc..27b6245cc54 100644 --- a/runtime/vm/classallocation.c +++ b/runtime/vm/classallocation.c @@ -169,6 +169,11 @@ allocateClassLoader(J9JavaVM *javaVM) classLoader->moduleHashTable = hashModuleNameTableNew(javaVM, INITIAL_MODULE_HASHTABLE_SIZE); classLoader->packageHashTable = hashPackageTableNew(javaVM, INITIAL_PACKAGE_HASHTABLE_SIZE); #endif /* JAVA_SPEC_VERSION > 8 */ + +#if defined(J9VM_OPT_JFR) + classLoader->loadedClassCount = 0; +#endif /* defined(J9VM_OPT_JFR) */ + /* Allocate classLocationHashTable only for bootloader which is the first classloader to be allocated. * The classLoader being allocated must be the bootloader if javaVM->systemClassLoader is NULL. */ diff --git a/runtime/vm/createramclass.cpp b/runtime/vm/createramclass.cpp index 9a3bdec8b4a..82eadef58bf 100644 --- a/runtime/vm/createramclass.cpp +++ b/runtime/vm/createramclass.cpp @@ -2163,6 +2163,10 @@ internalCreateRAMClassDone(J9VMThread *vmThread, J9ClassLoader *classLoader, J9C javaVM->anonClassCount += 1; } +#if defined(J9VM_OPT_JFR) + hostClassLoader->loadedClassCount += 1; +#endif /* defined(J9VM_OPT_JFR) */ + /* Create all the method IDs if class load is hooked */ if (J9_EVENT_IS_HOOKED(javaVM->hookInterface, J9HOOK_VM_CLASS_LOAD)) { U_32 count = romClass->romMethodCount; diff --git a/runtime/vm/jfr.cpp b/runtime/vm/jfr.cpp index 2af4d1b6a2c..7fd9310294a 100644 --- a/runtime/vm/jfr.cpp +++ b/runtime/vm/jfr.cpp @@ -22,6 +22,7 @@ #include "JFRConstantPoolTypes.hpp" #include "j9protos.h" #include "omrlinkedlist.h" +#include "pool_api.h" #include "thread_api.h" #include "ut_j9vm.h" #include "vm_internal.h" @@ -92,6 +93,9 @@ jfrEventSize(J9JFREvent *jfrEvent) case J9JFR_EVENT_TYPE_THREAD_CPU_LOAD: size = sizeof(J9JFRThreadCPULoad); break; + case J9JFR_EVENT_TYPE_CLASS_LOADING_STATISTICS: + size = sizeof(J9JFRClassLoadingStatistics); + break; default: Assert_VM_unreachable(); break; @@ -939,6 +943,36 @@ jfrThreadCPULoadCallback(J9VMThread *currentThread, IDATA handlerKey, void *user jfrThreadCPULoad(currentThread, currentThread); } +void +jfrClassLoadingStatistics(J9VMThread *currentThread) +{ + J9JavaVM *vm = currentThread->javaVM; + J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions; + J9JFRClassLoadingStatistics *jfrEvent = (J9JFRClassLoadingStatistics *)reserveBuffer(currentThread, sizeof(J9JFRClassLoadingStatistics)); + + if (NULL != jfrEvent) { + initializeEventFields(currentThread, (J9JFREvent *)jfrEvent, J9JFR_EVENT_TYPE_CLASS_LOADING_STATISTICS); + + UDATA unloadedClassCount = 0; + vm->memoryManagerFunctions->j9gc_get_cumulative_class_unloading_stats(currentThread, NULL, &unloadedClassCount, NULL); + jfrEvent->unloadedClassCount = (I_64)unloadedClassCount; + + internalReleaseVMAccess(currentThread); + + J9ClassLoaderWalkState walkState = {0}; + J9ClassLoader *classLoader = vmFuncs->allClassLoadersStartDo(&walkState, vm, 0); + + while (NULL != classLoader) { + jfrEvent->loadedClassCount += classLoader->loadedClassCount; + + classLoader= vmFuncs->allClassLoadersNextDo(&walkState); + } + vmFuncs->allClassLoadersEndDo(&walkState); + + internalAcquireVMAccess(currentThread); + } +} + static int J9THREAD_PROC jfrSamplingThreadProc(void *entryArg) { @@ -956,6 +990,7 @@ jfrSamplingThreadProc(void *entryArg) omrthread_monitor_exit(vm->jfrSamplerMutex); internalAcquireVMAccess(currentThread); jfrCPULoad(currentThread); + jfrClassLoadingStatistics(currentThread); internalReleaseVMAccess(currentThread); omrthread_monitor_enter(vm->jfrSamplerMutex); if (0 == (count % 1000)) { // 10 seconds