From b15f55748fb38cbad89ee9b46367911e4f0ec866 Mon Sep 17 00:00:00 2001 From: Gengchen Tuo Date: Wed, 27 Nov 2024 15:20:08 -0500 Subject: [PATCH] Add JFR ThreadContextSwitchRate support Depends on: https://github.com/eclipse-omr/omr/pull/7580 Signed-off-by: Gengchen Tuo --- runtime/oti/j9consts.h | 1 + runtime/oti/j9nonbuilder.h | 7 ++++++ runtime/vm/JFRChunkWriter.cpp | 23 +++++++++++++++++ runtime/vm/JFRChunkWriter.hpp | 8 ++++++ runtime/vm/JFRConstantPoolTypes.cpp | 18 +++++++++++++- runtime/vm/JFRConstantPoolTypes.hpp | 31 +++++++++++++++++++++++ runtime/vm/jfr.cpp | 38 +++++++++++++++++++++++++++-- 7 files changed, 123 insertions(+), 3 deletions(-) diff --git a/runtime/oti/j9consts.h b/runtime/oti/j9consts.h index 7d7cdfff0dd..59ddfcf7aca 100644 --- a/runtime/oti/j9consts.h +++ b/runtime/oti/j9consts.h @@ -948,6 +948,7 @@ extern "C" { #define J9JFR_EVENT_TYPE_CPU_LOAD 5 #define J9JFR_EVENT_TYPE_THREAD_CPU_LOAD 6 #define J9JFR_EVENT_TYPE_CLASS_LOADING_STATISTICS 7 +#define J9JFR_EVENT_TYPE_THREAD_CONTEXT_SWITCH_RATE 8 /* JFR thread states */ diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index 5e1fc143ece..dd212ac0b32 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -440,6 +440,11 @@ typedef struct J9JFRClassLoadingStatistics { I_64 unloadedClassCount; } J9JFRClassLoadingStatistics; +typedef struct J9JFRThreadContextSwitchRate { + J9JFR_EVENT_COMMON_FIELDS + float switchRate; +} J9JFRThreadContextSwitchRate; + #endif /* defined(J9VM_OPT_JFR) */ /* @ddr_namespace: map_to_type=J9CfrError */ @@ -5715,6 +5720,8 @@ typedef struct JFRState { J9SysinfoCPUTime prevSysCPUTime; omrthread_process_time_t prevProcCPUTimes; int64_t prevProcTimestamp; + int64_t prevContextSwitchTimestamp; + uint64_t prevContextSwitches; } JFRState; typedef struct J9ReflectFunctionTable { diff --git a/runtime/vm/JFRChunkWriter.cpp b/runtime/vm/JFRChunkWriter.cpp index faaf093f7eb..524e7cc9630 100644 --- a/runtime/vm/JFRChunkWriter.cpp +++ b/runtime/vm/JFRChunkWriter.cpp @@ -888,4 +888,27 @@ VM_JFRChunkWriter::writeClassLoadingStatisticsEvent(void *anElement, void *userD /* write size */ _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); } + +void +VM_JFRChunkWriter::writeThreadContextSwitchRateEvent(void *anElement, void *userData) +{ + ThreadContextSwitchRateEntry *entry = (ThreadContextSwitchRateEntry *)anElement; + VM_BufferWriter *_bufferWriter = (VM_BufferWriter *)userData; + + /* reserve size field */ + U_8 *dataStart = _bufferWriter->getAndIncCursor(sizeof(U_32)); + + /* write event type */ + _bufferWriter->writeLEB128(ThreadContextSwitchRateID); + + /* write start time */ + _bufferWriter->writeLEB128(entry->ticks); + + /* write switch rate */ + _bufferWriter->writeFloat(entry->switchRate); + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, (U_32)(_bufferWriter->getCursor() - dataStart)); +} + #endif /* defined(J9VM_OPT_JFR) */ diff --git a/runtime/vm/JFRChunkWriter.hpp b/runtime/vm/JFRChunkWriter.hpp index 4ca4381bbf8..521352644b6 100644 --- a/runtime/vm/JFRChunkWriter.hpp +++ b/runtime/vm/JFRChunkWriter.hpp @@ -74,6 +74,7 @@ enum MetadataTypeID { CPUInformationID = 93, CPULoadID = 95, ThreadCPULoadID = 96, + ThreadContextSwitchRateID = 97, ClassLoadingStatisticsID = 100, PhysicalMemoryID = 108, ExecutionSampleID = 109, @@ -166,6 +167,7 @@ class VM_JFRChunkWriter { 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 THREAD_CONTEXT_SWITCH_RATE_SIZE = sizeof(float) + (3 * sizeof(I_64)); static constexpr int METADATA_ID = 1; @@ -343,6 +345,8 @@ class VM_JFRChunkWriter { pool_do(_constantPoolTypes.getClassLoadingStatisticsTable(), &writeClassLoadingStatisticsEvent, _bufferWriter); + pool_do(_constantPoolTypes.getThreadContextSwitchRateTable(), &writeThreadContextSwitchRateEvent, _bufferWriter); + /* Only write constant events in first chunk */ if (0 == _vm->jfrState.jfrChunkCount) { writeJVMInformationEvent(); @@ -674,6 +678,8 @@ class VM_JFRChunkWriter { static void writeClassLoadingStatisticsEvent(void *anElement, void *userData); + static void writeThreadContextSwitchRateEvent(void *anElement, void *userData); + UDATA calculateRequiredBufferSize() { @@ -736,6 +742,8 @@ class VM_JFRChunkWriter { requiredBufferSize += _constantPoolTypes.getClassLoadingStatisticsCount() * CLASS_LOADING_STATISTICS_EVENT_SIZE; + requiredBufferSize += _constantPoolTypes.getThreadContextSwitchRateCount() * THREAD_CONTEXT_SWITCH_RATE_SIZE; + return requiredBufferSize; } diff --git a/runtime/vm/JFRConstantPoolTypes.cpp b/runtime/vm/JFRConstantPoolTypes.cpp index da5fc45d313..67663e19bf6 100644 --- a/runtime/vm/JFRConstantPoolTypes.cpp +++ b/runtime/vm/JFRConstantPoolTypes.cpp @@ -23,6 +23,7 @@ #include "j9protos.h" #include "j9consts.h" #include "j9vmconstantpool.h" +#include "pool_api.h" #if defined(J9VM_OPT_JFR) @@ -1162,11 +1163,26 @@ VM_JFRConstantPoolTypes::addClassLoadingStatisticsEntry(J9JFRClassLoadingStatist index = _classLoadingStatisticsCount; _classLoadingStatisticsCount += 1; - done: return index; } +void +VM_JFRConstantPoolTypes::addThreadContextSwitchRateEntry(J9JFRThreadContextSwitchRate *threadContextSwitchRateData) +{ + ThreadContextSwitchRateEntry *entry = (ThreadContextSwitchRateEntry *)pool_newElement(_threadContextSwitchRateTable); + + if (NULL == entry) { + _buildResult = OutOfMemory; + return; + } + + entry->ticks = threadContextSwitchRateData->startTicks; + entry->switchRate = threadContextSwitchRateData->switchRate; + + _threadContextSwitchRateCount += 1; +} + void VM_JFRConstantPoolTypes::printTables() { diff --git a/runtime/vm/JFRConstantPoolTypes.hpp b/runtime/vm/JFRConstantPoolTypes.hpp index 48fc801ba1b..f368c50adc7 100644 --- a/runtime/vm/JFRConstantPoolTypes.hpp +++ b/runtime/vm/JFRConstantPoolTypes.hpp @@ -234,6 +234,11 @@ struct ClassLoadingStatisticsEntry { I_64 unloadedClassCount; }; +struct ThreadContextSwitchRateEntry { + I_64 ticks; + float switchRate; +}; + struct JVMInformationEntry { const char *jvmName; const char *jvmVersion; @@ -317,6 +322,8 @@ class VM_JFRConstantPoolTypes { UDATA _threadCPULoadCount; J9Pool *_classLoadingStatisticsTable; UDATA _classLoadingStatisticsCount; + J9Pool *_threadContextSwitchRateTable; + U_32 _threadContextSwitchRateCount; /* Processing buffers */ StackFrame *_currentStackFrameBuffer; @@ -586,6 +593,8 @@ class VM_JFRConstantPoolTypes { U_32 addClassLoadingStatisticsEntry(J9JFRClassLoadingStatistics *classLoadingStatisticsData); + void addThreadContextSwitchRateEntry(J9JFRThreadContextSwitchRate *threadContextSwitchRateData); + J9Pool *getExecutionSampleTable() { return _executionSampleTable; @@ -626,6 +635,11 @@ class VM_JFRConstantPoolTypes { return _classLoadingStatisticsTable; } + J9Pool *getThreadContextSwitchRateTable() + { + return _threadContextSwitchRateTable; + } + UDATA getExecutionSampleCount() { return _executionSampleCount; @@ -666,6 +680,11 @@ class VM_JFRConstantPoolTypes { return _classLoadingStatisticsCount; } + U_32 getThreadContextSwitchRateCount() + { + return _threadContextSwitchRateCount; + } + ClassloaderEntry *getClassloaderEntry() { return _firstClassloaderEntry; @@ -818,6 +837,9 @@ class VM_JFRConstantPoolTypes { case J9JFR_EVENT_TYPE_CLASS_LOADING_STATISTICS: addClassLoadingStatisticsEntry((J9JFRClassLoadingStatistics *)event); break; + case J9JFR_EVENT_TYPE_THREAD_CONTEXT_SWITCH_RATE: + addThreadContextSwitchRateEntry((J9JFRThreadContextSwitchRate *)event); + break; default: Assert_VM_unreachable(); break; @@ -1143,6 +1165,8 @@ class VM_JFRConstantPoolTypes { , _threadCPULoadCount(0) , _classLoadingStatisticsTable(NULL) , _classLoadingStatisticsCount(0) + , _threadContextSwitchRateTable(NULL) + , _threadContextSwitchRateCount(0) , _previousStackTraceEntry(NULL) , _firstStackTraceEntry(NULL) , _previousThreadEntry(NULL) @@ -1263,6 +1287,12 @@ class VM_JFRConstantPoolTypes { goto done; } + _threadContextSwitchRateTable = pool_new(sizeof(ThreadContextSwitchRateEntry), 0, sizeof(U_64), 0, J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM, POOL_FOR_PORT(privatePortLibrary)); + if (NULL == _threadContextSwitchRateTable ) { + _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. @@ -1352,6 +1382,7 @@ class VM_JFRConstantPoolTypes { pool_kill(_cpuLoadTable); pool_kill(_threadCPULoadTable); pool_kill(_classLoadingStatisticsTable); + pool_kill(_threadContextSwitchRateTable); j9mem_free_memory(_globalStringTable); } diff --git a/runtime/vm/jfr.cpp b/runtime/vm/jfr.cpp index b252a5c82a5..19b5b3d1d34 100644 --- a/runtime/vm/jfr.cpp +++ b/runtime/vm/jfr.cpp @@ -96,6 +96,9 @@ jfrEventSize(J9JFREvent *jfrEvent) case J9JFR_EVENT_TYPE_CLASS_LOADING_STATISTICS: size = sizeof(J9JFRClassLoadingStatistics); break; + case J9JFR_EVENT_TYPE_THREAD_CONTEXT_SWITCH_RATE: + size = sizeof(J9JFRThreadContextSwitchRate); + break; default: Assert_VM_unreachable(); break; @@ -719,6 +722,7 @@ initializeJFR(J9JavaVM *vm, BOOLEAN lateInit) vm->jfrState.prevSysCPUTime.timestamp = -1; vm->jfrState.prevProcTimestamp = -1; + vm->jfrState.prevContextSwitchTimestamp = -1; if (omrthread_monitor_init_with_name(&vm->jfrBufferMutex, 0, "JFR global buffer mutex")) { goto fail; } @@ -988,6 +992,35 @@ jfrClassLoadingStatistics(J9VMThread *currentThread) } } +void +jfrThreadContextSwitchRate(J9VMThread *currentThread) +{ + PORT_ACCESS_FROM_VMC(currentThread); + OMRPORT_ACCESS_FROM_J9PORT(PORTLIB); + + uint64_t switches = 0; + int32_t rc = omrsysinfo_get_number_context_switches(&switches); + + if (0 == rc) { + J9JFRThreadContextSwitchRate *jfrEvent = (J9JFRThreadContextSwitchRate *)reserveBuffer(currentThread, sizeof(J9JFRThreadContextSwitchRate)); + if (NULL != jfrEvent) { + JFRState *jfrState = ¤tThread->javaVM->jfrState; + int64_t currentTime = j9time_nano_time(); + + initializeEventFields(currentThread, (J9JFREvent *)jfrEvent, J9JFR_EVENT_TYPE_THREAD_CONTEXT_SWITCH_RATE); + + if (-1 == jfrState->prevContextSwitchTimestamp) { + jfrEvent->switchRate = 0; + } else { + int64_t timeDelta = currentTime - jfrState->prevContextSwitchTimestamp; + jfrEvent->switchRate = ((double)(switches - jfrState->prevContextSwitches) / timeDelta) * 1e9; + } + jfrState->prevContextSwitches = switches; + jfrState->prevContextSwitchTimestamp = currentTime; + } + } +} + static int J9THREAD_PROC jfrSamplingThreadProc(void *entryArg) { @@ -1006,11 +1039,12 @@ jfrSamplingThreadProc(void *entryArg) internalAcquireVMAccess(currentThread); jfrCPULoad(currentThread); jfrClassLoadingStatistics(currentThread); - internalReleaseVMAccess(currentThread); - omrthread_monitor_enter(vm->jfrSamplerMutex); if (0 == (count % 1000)) { // 10 seconds J9SignalAsyncEvent(vm, NULL, vm->jfrThreadCPULoadAsyncKey); + jfrThreadContextSwitchRate(currentThread); } + internalReleaseVMAccess(currentThread); + omrthread_monitor_enter(vm->jfrSamplerMutex); } count += 1; omrthread_monitor_wait_timed(vm->jfrSamplerMutex, J9JFR_SAMPLING_RATE, 0);