diff --git a/runtime/oti/j9consts.h b/runtime/oti/j9consts.h index ea060e00eee..4aa61beccbf 100644 --- a/runtime/oti/j9consts.h +++ b/runtime/oti/j9consts.h @@ -953,6 +953,7 @@ extern "C" { #define J9JFR_EVENT_TYPE_CLASS_LOADING_STATISTICS 7 #define J9JFR_EVENT_TYPE_THREAD_CONTEXT_SWITCH_RATE 8 #define J9JFR_EVENT_TYPE_THREAD_PARK 9 +#define J9JFR_EVENT_TYPE_THREAD_STATISTICS 10 /* JFR thread states */ diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index c93b722760b..0c09e232044 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -453,6 +453,14 @@ typedef struct J9JFRClassLoadingStatistics { I_64 unloadedClassCount; } J9JFRClassLoadingStatistics; +typedef struct J9JFRThreadStatistics { + J9JFR_EVENT_COMMON_FIELDS + U_32 activeThreadCount; + U_32 daemonThreadCount; + U_32 accumulatedThreadCount; + U_32 peakThreadCount; +} J9JFRThreadStatistics; + typedef struct J9JFRThreadContextSwitchRate { J9JFR_EVENT_COMMON_FIELDS float switchRate; @@ -5873,6 +5881,8 @@ typedef struct J9JavaVM { UDATA anonClassCount; UDATA totalThreadCount; UDATA daemonThreadCount; + UDATA accumulatedThreadCount; + UDATA peakThreadCount; omrthread_t finalizeMainThread; omrthread_monitor_t finalizeMainMonitor; omrthread_monitor_t processReferenceMonitor; /* the monitor for synchronizing between reference process and j9gc_wait_for_reference_processing() (only for Java 9 and later) */ diff --git a/runtime/vm/JFRChunkWriter.cpp b/runtime/vm/JFRChunkWriter.cpp index 524e7cc9630..7dfaf419aec 100644 --- a/runtime/vm/JFRChunkWriter.cpp +++ b/runtime/vm/JFRChunkWriter.cpp @@ -911,4 +911,35 @@ VM_JFRChunkWriter::writeThreadContextSwitchRateEvent(void *anElement, void *user _bufferWriter->writeLEB128PaddedU32(dataStart, (U_32)(_bufferWriter->getCursor() - dataStart)); } +void +VM_JFRChunkWriter::writeThreadStatisticsEvent(void *anElement, void *userData) +{ + ThreadStatisticsEntry *entry = (ThreadStatisticsEntry *)anElement; + VM_BufferWriter *_bufferWriter = (VM_BufferWriter *)userData; + + /* reserve event size */ + U_8 *dataStart = _bufferWriter->getAndIncCursor(sizeof(U_32)); + + /* write event type */ + _bufferWriter->writeLEB128(ThreadStatisticsID); + + /* write start ticks */ + _bufferWriter->writeLEB128(entry->ticks); + + /* write active thread count */ + _bufferWriter->writeLEB128(entry->activeThreadCount); + + /* write daemon thread count */ + _bufferWriter->writeLEB128(entry->daemonThreadCount); + + /* write accumulated thread count */ + _bufferWriter->writeLEB128(entry->accumulatedThreadCount); + + /* write peak thread count */ + _bufferWriter->writeLEB128(entry->peakThreadCount); + + /* 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 171c124fc02..d81284dffe1 100644 --- a/runtime/vm/JFRChunkWriter.hpp +++ b/runtime/vm/JFRChunkWriter.hpp @@ -76,6 +76,7 @@ enum MetadataTypeID { CPULoadID = 95, ThreadCPULoadID = 96, ThreadContextSwitchRateID = 97, + ThreadStatisticsID = 99, ClassLoadingStatisticsID = 100, PhysicalMemoryID = 108, ExecutionSampleID = 109, @@ -169,6 +170,7 @@ class VM_JFRChunkWriter { 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 THREAD_STATISTICS_EVENT_SIZE = (6 * sizeof(U_64)) + sizeof(U_32); static constexpr int METADATA_ID = 1; @@ -350,6 +352,8 @@ class VM_JFRChunkWriter { pool_do(_constantPoolTypes.getThreadContextSwitchRateTable(), &writeThreadContextSwitchRateEvent, _bufferWriter); + pool_do(_constantPoolTypes.getThreadStatisticsTable(), &writeThreadStatisticsEvent, _bufferWriter); + /* Only write constant events in first chunk */ if (0 == _vm->jfrState.jfrChunkCount) { writeJVMInformationEvent(); @@ -597,7 +601,6 @@ class VM_JFRChunkWriter { _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); } - static void writeCPULoadEvent(void *anElement, void *userData) { @@ -726,6 +729,8 @@ class VM_JFRChunkWriter { static void writeThreadContextSwitchRateEvent(void *anElement, void *userData); + static void writeThreadStatisticsEvent(void *anElement, void *userData); + UDATA calculateRequiredBufferSize() { @@ -790,6 +795,8 @@ class VM_JFRChunkWriter { requiredBufferSize += _constantPoolTypes.getThreadContextSwitchRateCount() * THREAD_CONTEXT_SWITCH_RATE_SIZE; + requiredBufferSize += (_constantPoolTypes.getThreadStatisticsCount() * THREAD_STATISTICS_EVENT_SIZE); + return requiredBufferSize; } diff --git a/runtime/vm/JFRConstantPoolTypes.cpp b/runtime/vm/JFRConstantPoolTypes.cpp index bbca435fbab..5e6dc63621b 100644 --- a/runtime/vm/JFRConstantPoolTypes.cpp +++ b/runtime/vm/JFRConstantPoolTypes.cpp @@ -1126,6 +1126,8 @@ VM_JFRConstantPoolTypes::addThreadParkEntry(J9JFRThreadParked* threadParkData) entry->timeOut = threadParkData->timeOut; entry->untilTime = threadParkData->untilTime; + _threadParkCount += 1; + done: return; } @@ -1217,6 +1219,28 @@ VM_JFRConstantPoolTypes::addThreadContextSwitchRateEntry(J9JFRThreadContextSwitc _threadContextSwitchRateCount += 1; } +void +VM_JFRConstantPoolTypes::addThreadStatisticsEntry(J9JFRThreadStatistics *threadStatisticsData) +{ + ThreadStatisticsEntry *entry = (ThreadStatisticsEntry *)pool_newElement(_threadStatisticsTable); + + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + entry->ticks = threadStatisticsData->startTicks; + entry->activeThreadCount = threadStatisticsData->activeThreadCount; + entry->daemonThreadCount = threadStatisticsData->daemonThreadCount; + entry->accumulatedThreadCount = threadStatisticsData->accumulatedThreadCount; + entry->peakThreadCount = threadStatisticsData->peakThreadCount; + + _threadStatisticsCount += 1; + +done: + return; +} + void VM_JFRConstantPoolTypes::printTables() { diff --git a/runtime/vm/JFRConstantPoolTypes.hpp b/runtime/vm/JFRConstantPoolTypes.hpp index 58d035d8092..6031965b7b2 100644 --- a/runtime/vm/JFRConstantPoolTypes.hpp +++ b/runtime/vm/JFRConstantPoolTypes.hpp @@ -251,6 +251,14 @@ struct ThreadContextSwitchRateEntry { float switchRate; }; +struct ThreadStatisticsEntry { + I_64 ticks; + U_32 activeThreadCount; + U_32 daemonThreadCount; + U_32 accumulatedThreadCount; + U_32 peakThreadCount; +}; + struct JVMInformationEntry { const char *jvmName; const char *jvmVersion; @@ -338,6 +346,8 @@ class VM_JFRConstantPoolTypes { UDATA _classLoadingStatisticsCount; J9Pool *_threadContextSwitchRateTable; U_32 _threadContextSwitchRateCount; + J9Pool *_threadStatisticsTable; + UDATA _threadStatisticsCount; /* Processing buffers */ StackFrame *_currentStackFrameBuffer; @@ -611,6 +621,8 @@ class VM_JFRConstantPoolTypes { void addThreadContextSwitchRateEntry(J9JFRThreadContextSwitchRate *threadContextSwitchRateData); + void addThreadStatisticsEntry(J9JFRThreadStatistics *threadStatisticsData); + J9Pool *getExecutionSampleTable() { return _executionSampleTable; @@ -661,6 +673,11 @@ class VM_JFRConstantPoolTypes { return _threadContextSwitchRateTable; } + J9Pool *getThreadStatisticsTable() + { + return _threadStatisticsTable; + } + UDATA getExecutionSampleCount() { return _executionSampleCount; @@ -711,6 +728,11 @@ class VM_JFRConstantPoolTypes { return _threadContextSwitchRateCount; } + UDATA getThreadStatisticsCount() + { + return _threadStatisticsCount; + } + ClassloaderEntry *getClassloaderEntry() { return _firstClassloaderEntry; @@ -869,6 +891,9 @@ class VM_JFRConstantPoolTypes { case J9JFR_EVENT_TYPE_THREAD_CONTEXT_SWITCH_RATE: addThreadContextSwitchRateEntry((J9JFRThreadContextSwitchRate *)event); break; + case J9JFR_EVENT_TYPE_THREAD_STATISTICS: + addThreadStatisticsEntry((J9JFRThreadStatistics *)event); + break; default: Assert_VM_unreachable(); break; @@ -1201,6 +1226,8 @@ class VM_JFRConstantPoolTypes { , _classLoadingStatisticsCount(0) , _threadContextSwitchRateTable(NULL) , _threadContextSwitchRateCount(0) + , _threadStatisticsTable(NULL) + , _threadStatisticsCount(0) , _previousStackTraceEntry(NULL) , _firstStackTraceEntry(NULL) , _previousThreadEntry(NULL) @@ -1333,6 +1360,12 @@ class VM_JFRConstantPoolTypes { goto done; } + _threadStatisticsTable = pool_new(sizeof(ThreadStatisticsEntry), 0, sizeof(U_64), 0, J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM, POOL_FOR_PORT(privatePortLibrary)); + if (NULL == _threadStatisticsTable ) { + _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. @@ -1424,6 +1457,7 @@ class VM_JFRConstantPoolTypes { pool_kill(_threadCPULoadTable); pool_kill(_classLoadingStatisticsTable); pool_kill(_threadContextSwitchRateTable); + pool_kill(_threadStatisticsTable); j9mem_free_memory(_globalStringTable); } diff --git a/runtime/vm/jfr.cpp b/runtime/vm/jfr.cpp index 1eb894f9a69..d977ca60f26 100644 --- a/runtime/vm/jfr.cpp +++ b/runtime/vm/jfr.cpp @@ -103,6 +103,9 @@ jfrEventSize(J9JFREvent *jfrEvent) case J9JFR_EVENT_TYPE_THREAD_CONTEXT_SWITCH_RATE: size = sizeof(J9JFRThreadContextSwitchRate); break; + case J9JFR_EVENT_TYPE_THREAD_STATISTICS: + size = sizeof(J9JFRThreadStatistics); + break; default: Assert_VM_unreachable(); break; @@ -1078,6 +1081,23 @@ jfrThreadContextSwitchRate(J9VMThread *currentThread) } } +void +jfrThreadStatistics(J9VMThread *currentThread) +{ + J9JavaVM *vm = currentThread->javaVM; + J9JFRThreadStatistics *jfrEvent = (J9JFRThreadStatistics *)reserveBuffer(currentThread, sizeof(J9JFRThreadStatistics)); + + if (NULL != jfrEvent) { + initializeEventFields(currentThread, (J9JFREvent *)jfrEvent, J9JFR_EVENT_TYPE_THREAD_STATISTICS); + + jfrEvent->activeThreadCount = vm->totalThreadCount; + jfrEvent->daemonThreadCount = vm->daemonThreadCount; + jfrEvent->accumulatedThreadCount = vm->accumulatedThreadCount; + jfrEvent->peakThreadCount = vm->peakThreadCount; + } + +} + static int J9THREAD_PROC jfrSamplingThreadProc(void *entryArg) { @@ -1096,6 +1116,7 @@ jfrSamplingThreadProc(void *entryArg) internalAcquireVMAccess(currentThread); jfrCPULoad(currentThread); jfrClassLoadingStatistics(currentThread); + jfrThreadStatistics(currentThread); if (0 == (count % 1000)) { // 10 seconds J9SignalAsyncEvent(vm, NULL, vm->jfrThreadCPULoadAsyncKey); jfrThreadContextSwitchRate(currentThread); diff --git a/runtime/vm/vmthread.cpp b/runtime/vm/vmthread.cpp index c18d4980374..f1715ba0dc1 100644 --- a/runtime/vm/vmthread.cpp +++ b/runtime/vm/vmthread.cpp @@ -348,6 +348,11 @@ allocateVMThread(J9JavaVM *vm, omrthread_t osThread, UDATA privateFlags, void *m /* Update counters for total # of threads and daemon threads and notify anyone waiting */ ++(vm->totalThreadCount); + ++(vm->accumulatedThreadCount); + if (vm->totalThreadCount > vm->peakThreadCount) { + vm->peakThreadCount = vm->totalThreadCount; + } + if (privateFlags & J9_PRIVATE_FLAGS_DAEMON_THREAD) { ++(vm->daemonThreadCount); }