Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JFR ThreadContextSwitchRate support #20725

Merged
merged 1 commit into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions runtime/oti/j9consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down
7 changes: 7 additions & 0 deletions runtime/oti/j9nonbuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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 {
Expand Down
23 changes: 23 additions & 0 deletions runtime/vm/JFRChunkWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) */
8 changes: 8 additions & 0 deletions runtime/vm/JFRChunkWriter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ enum MetadataTypeID {
CPUInformationID = 93,
CPULoadID = 95,
ThreadCPULoadID = 96,
ThreadContextSwitchRateID = 97,
ClassLoadingStatisticsID = 100,
PhysicalMemoryID = 108,
ExecutionSampleID = 109,
Expand Down Expand Up @@ -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));
thallium marked this conversation as resolved.
Show resolved Hide resolved

static constexpr int METADATA_ID = 1;

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -674,6 +678,8 @@ class VM_JFRChunkWriter {

static void writeClassLoadingStatisticsEvent(void *anElement, void *userData);

static void writeThreadContextSwitchRateEvent(void *anElement, void *userData);

UDATA
calculateRequiredBufferSize()
{
Expand Down Expand Up @@ -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;
}

Expand Down
18 changes: 17 additions & 1 deletion runtime/vm/JFRConstantPoolTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "j9protos.h"
#include "j9consts.h"
#include "j9vmconstantpool.h"
#include "pool_api.h"

#if defined(J9VM_OPT_JFR)

Expand Down Expand Up @@ -1162,11 +1163,26 @@ VM_JFRConstantPoolTypes::addClassLoadingStatisticsEntry(J9JFRClassLoadingStatist

index = _classLoadingStatisticsCount;
_classLoadingStatisticsCount += 1;
thallium marked this conversation as resolved.
Show resolved Hide resolved

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()
{
Expand Down
31 changes: 31 additions & 0 deletions runtime/vm/JFRConstantPoolTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ struct ClassLoadingStatisticsEntry {
I_64 unloadedClassCount;
};

struct ThreadContextSwitchRateEntry {
I_64 ticks;
float switchRate;
};

struct JVMInformationEntry {
const char *jvmName;
const char *jvmVersion;
Expand Down Expand Up @@ -317,6 +322,8 @@ class VM_JFRConstantPoolTypes {
UDATA _threadCPULoadCount;
J9Pool *_classLoadingStatisticsTable;
UDATA _classLoadingStatisticsCount;
J9Pool *_threadContextSwitchRateTable;
U_32 _threadContextSwitchRateCount;

/* Processing buffers */
StackFrame *_currentStackFrameBuffer;
Expand Down Expand Up @@ -586,6 +593,8 @@ class VM_JFRConstantPoolTypes {

U_32 addClassLoadingStatisticsEntry(J9JFRClassLoadingStatistics *classLoadingStatisticsData);

void addThreadContextSwitchRateEntry(J9JFRThreadContextSwitchRate *threadContextSwitchRateData);

J9Pool *getExecutionSampleTable()
{
return _executionSampleTable;
Expand Down Expand Up @@ -626,6 +635,11 @@ class VM_JFRConstantPoolTypes {
return _classLoadingStatisticsTable;
}

J9Pool *getThreadContextSwitchRateTable()
{
return _threadContextSwitchRateTable;
}

UDATA getExecutionSampleCount()
{
return _executionSampleCount;
Expand Down Expand Up @@ -666,6 +680,11 @@ class VM_JFRConstantPoolTypes {
return _classLoadingStatisticsCount;
}

U_32 getThreadContextSwitchRateCount()
{
return _threadContextSwitchRateCount;
}

ClassloaderEntry *getClassloaderEntry()
{
return _firstClassloaderEntry;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1143,6 +1165,8 @@ class VM_JFRConstantPoolTypes {
, _threadCPULoadCount(0)
, _classLoadingStatisticsTable(NULL)
, _classLoadingStatisticsCount(0)
, _threadContextSwitchRateTable(NULL)
, _threadContextSwitchRateCount(0)
, _previousStackTraceEntry(NULL)
, _firstStackTraceEntry(NULL)
, _previousThreadEntry(NULL)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -1352,6 +1382,7 @@ class VM_JFRConstantPoolTypes {
pool_kill(_cpuLoadTable);
pool_kill(_threadCPULoadTable);
pool_kill(_classLoadingStatisticsTable);
pool_kill(_threadContextSwitchRateTable);
j9mem_free_memory(_globalStringTable);
}

Expand Down
38 changes: 36 additions & 2 deletions runtime/vm/jfr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -719,6 +722,7 @@ initializeJFR(J9JavaVM *vm, BOOLEAN lateInit)

vm->jfrState.prevSysCPUTime.timestamp = -1;
vm->jfrState.prevProcTimestamp = -1;
vm->jfrState.prevContextSwitchTimestamp = -1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should explicitly initialize prevContextSwitches (or have a comment explaining why that's not necessary).

if (omrthread_monitor_init_with_name(&vm->jfrBufferMutex, 0, "JFR global buffer mutex")) {
goto fail;
}
Expand Down Expand Up @@ -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 = &currentThread->javaVM->jfrState;
thallium marked this conversation as resolved.
Show resolved Hide resolved
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;
}
Comment on lines +1012 to +1017
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should either use float or have an explicit cast to float (the type of switchRate).

jfrState->prevContextSwitches = switches;
jfrState->prevContextSwitchTimestamp = currentTime;
}
}
}

static int J9THREAD_PROC
jfrSamplingThreadProc(void *entryArg)
{
Expand All @@ -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);
Expand Down