diff --git a/runtime/oti/j9consts.h b/runtime/oti/j9consts.h index 9f41c7cb943..c15186040d0 100644 --- a/runtime/oti/j9consts.h +++ b/runtime/oti/j9consts.h @@ -943,6 +943,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 ff3fbfdb467..49c632215f9 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 */ @@ -5713,6 +5718,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 03f88cc544b..d738c1c1302 100644 --- a/runtime/vm/JFRChunkWriter.hpp +++ b/runtime/vm/JFRChunkWriter.hpp @@ -74,6 +74,7 @@ enum MetadataTypeID { CPUInformationID = 92, CPULoadID = 94, ThreadCPULoadID = 95, + ThreadContextSwitchRateID = 96, ClassLoadingStatisticsID = 99, PhysicalMemoryID = 107, ExecutionSampleID = 108, @@ -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() { diff --git a/runtime/vm/JFRConstantPoolTypes.cpp b/runtime/vm/JFRConstantPoolTypes.cpp index 6d7e9f47d20..f0d8578f3d4 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) @@ -1160,8 +1161,28 @@ VM_JFRConstantPoolTypes::addClassLoadingStatisticsEntry(J9JFRClassLoadingStatist entry->loadedClassCount = classLoadingStatisticsData->loadedClassCount; entry->unloadedClassCount = classLoadingStatisticsData->unloadedClassCount; - index = _classLoadingStatisticsCount; + index = (U_32)_classLoadingStatisticsCount; _classLoadingStatisticsCount += 1; +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::addThreadContextSwitchRateEntry(J9JFRThreadContextSwitchRate *threadContextSwitchRateData) +{ + ThreadContextSwitchRateEntry *entry = (ThreadContextSwitchRateEntry *)pool_newElement(_threadContextSwitchRateTable); + U_32 index = U_32_MAX; + + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + entry->ticks = threadContextSwitchRateData->startTicks; + entry->switchRate = threadContextSwitchRateData->switchRate; + + index = _threadContextSwitchRateCount; + _threadContextSwitchRateCount += 1; done: return index; diff --git a/runtime/vm/JFRConstantPoolTypes.hpp b/runtime/vm/JFRConstantPoolTypes.hpp index 7caed147ab6..48284a46aa8 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); + U_32 addThreadContextSwitchRateEntry(J9JFRThreadContextSwitchRate *threadContextSwitchRateData); + J9Pool *getExecutionSampleTable() { return _executionSampleTable; @@ -625,6 +634,11 @@ class VM_JFRConstantPoolTypes { { return _classLoadingStatisticsTable; } + + J9Pool *getThreadContextSwitchRateTable() + { + return _threadContextSwitchRateTable; + } UDATA getExecutionSampleCount() { @@ -665,6 +679,11 @@ class VM_JFRConstantPoolTypes { { return _classLoadingStatisticsCount; } + + U_32 getThreadContextSwitchRateCount() + { + return _threadContextSwitchRateCount; + } ClassloaderEntry *getClassloaderEntry() { @@ -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 90335cd1dd8..d879cddf05c 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; + int32_t rc = omrsysinfo_get_number_context_switches(&switches); + + if (0 == rc) { + J9JFRThreadContextSwitchRate *jfrEvent = (J9JFRThreadContextSwitchRate *)reserveBuffer(currentThread, sizeof(J9JFRThreadContextSwitchRate)); + if (NULL != jfrEvent) { + initializeEventFields(currentThread, (J9JFREvent *)jfrEvent, J9JFR_EVENT_TYPE_THREAD_CONTEXT_SWITCH_RATE); + + JFRState *jfrState = ¤tThread->javaVM->jfrState; + int64_t currentTime = j9time_nano_time(); + + 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);