Skip to content

Add JFR NativeLibrary event #21629

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

Merged
merged 1 commit into from
Apr 23, 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
33 changes: 33 additions & 0 deletions runtime/vm/JFRChunkWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,39 @@ stackWalkCallback(J9VMThread *vmThread, J9StackWalkState *state)
return J9_STACKWALK_KEEP_ITERATING;
}

void
VM_JFRChunkWriter::writeNativeLibraryEvent(void *anElement, void *userData)
{
NativeLibraryEntry *entry = (NativeLibraryEntry *)anElement;
VM_JFRChunkWriter *writer = (VM_JFRChunkWriter *)userData;
VM_BufferWriter *bufferWriter = writer->_bufferWriter;
PORT_ACCESS_FROM_JAVAVM(writer->_vm);

/* reserve size field */
U_8 *dataStart = writer->reserveEventSize(bufferWriter);

/* write event type */
bufferWriter->writeLEB128(NativeLibraryID);

/* write start time */
bufferWriter->writeLEB128(entry->ticks);

/* write library name */
writer->writeStringLiteral(entry->name);

/* write base address */
bufferWriter->writeLEB128(entry->addressLow);

/* write top address */
bufferWriter->writeLEB128(entry->addressHigh);

/* write event size */
writer->writeEventSize(bufferWriter, dataStart);

j9mem_free_memory(entry->name);
entry->name = NULL;
}

static void
writeThreadInfo(J9VMThread *currentThread, J9VMThread *walkThread, VM_BufferWriter *bufferWriter)
{
Expand Down
9 changes: 9 additions & 0 deletions runtime/vm/JFRChunkWriter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ enum MetadataTypeID {
PhysicalMemoryID = 108,
ExecutionSampleID = 109,
ThreadDumpID = 111,
NativeLibraryID = 112,
GCHeapConfigID = 133,
ThreadID = 164,
ThreadGroupID = 165,
Expand Down Expand Up @@ -185,6 +186,7 @@ class VM_JFRChunkWriter {
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 THREAD_DUMP_EVENT_SIZE_PER_THREAD = 1000;
static constexpr int NATIVE_LIBRARY_ADDRESS_SIZE = (4 * sizeof(U_64)) + (2 * sizeof(UDATA)) + sizeof(U_8);

static constexpr int METADATA_ID = 1;

Expand Down Expand Up @@ -397,6 +399,8 @@ class VM_JFRChunkWriter {

pool_do(_constantPoolTypes.getThreadStatisticsTable(), &writeThreadStatisticsEvent, _bufferWriter);

pool_do(_constantPoolTypes.getNativeLibraryTable(), &writeNativeLibraryEvent, this);

/* Only write constant events in first chunk */
if (0 == _vm->jfrState.jfrChunkCount) {
writeJVMInformationEvent();
Expand Down Expand Up @@ -827,6 +831,8 @@ class VM_JFRChunkWriter {

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

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

UDATA
calculateRequiredBufferSize()
{
Expand Down Expand Up @@ -899,6 +905,9 @@ class VM_JFRChunkWriter {

requiredBufferSize += _vm->peakThreadCount * THREAD_DUMP_EVENT_SIZE_PER_THREAD;

requiredBufferSize += (_constantPoolTypes.getNativeLibraryCount() * NATIVE_LIBRARY_ADDRESS_SIZE)
+ _constantPoolTypes.getNativeLibraryPathSizeTotal();

return requiredBufferSize;
}

Expand Down
105 changes: 102 additions & 3 deletions runtime/vm/JFRConstantPoolTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,14 @@ struct OSInformationEntry {
char *osVersion;
};

struct NativeLibraryEntry {
I_64 ticks;
char *name;
UDATA addressLow;
UDATA addressHigh;
NativeLibraryEntry *next;
};

struct JFRConstantEvents {
JVMInformationEntry JVMInfoEntry;
CPUInformationEntry CPUInfoEntry;
Expand Down Expand Up @@ -377,6 +385,9 @@ class VM_JFRConstantPoolTypes {
UDATA _threadContextSwitchRateCount;
J9Pool *_threadStatisticsTable;
UDATA _threadStatisticsCount;
J9Pool *_nativeLibrariesTable;
UDATA _nativeLibrariesCount;
UDATA _nativeLibraryPathSizeTotal;

/* Processing buffers */
StackFrame *_currentStackFrameBuffer;
Expand All @@ -396,6 +407,8 @@ class VM_JFRConstantPoolTypes {
ClassloaderEntry *_firstClassloaderEntry;
PackageEntry *_previousPackageEntry;
PackageEntry *_firstPackageEntry;
NativeLibraryEntry *_firstNativeLibraryEntry;
NativeLibraryEntry *_previousNativeLibraryEntry;

/* default values */
ThreadGroupEntry _defaultThreadGroup;
Expand Down Expand Up @@ -714,6 +727,11 @@ class VM_JFRConstantPoolTypes {
return _threadStatisticsTable;
}

J9Pool *getNativeLibraryTable()
{
return _nativeLibrariesTable;
}

UDATA getExecutionSampleCount()
{
return _executionSampleCount;
Expand Down Expand Up @@ -774,6 +792,16 @@ class VM_JFRConstantPoolTypes {
return _threadStatisticsCount;
}

UDATA getNativeLibraryCount()
{
return _nativeLibrariesCount;
}

UDATA getNativeLibraryPathSizeTotal()
{
return _nativeLibraryPathSizeTotal;
}

ClassloaderEntry *getClassloaderEntry()
{
return _firstClassloaderEntry;
Expand Down Expand Up @@ -1246,6 +1274,63 @@ class VM_JFRConstantPoolTypes {
gcConfiguration->heapAddressBits = J9JAVAVM_REFERENCE_SIZE(vm) * 8;
}

static uintptr_t processNativeLibrariesCallback(const char *libraryName, void *lowAddress, void *highAddress, void *userData)
{
VM_JFRConstantPoolTypes *constantPoolTypes = reinterpret_cast<VM_JFRConstantPoolTypes *>(userData);
J9Pool *nativeLibrariesTable = constantPoolTypes->_nativeLibrariesTable;
NativeLibraryEntry *firstNativeLibraryEntry = constantPoolTypes->_firstNativeLibraryEntry;
NativeLibraryEntry *previousNativeLibraryEntry = constantPoolTypes->_previousNativeLibraryEntry;
NativeLibraryEntry *entry = firstNativeLibraryEntry;
PORT_ACCESS_FROM_JAVAVM(constantPoolTypes->_vm);
for (; NULL != entry; entry = entry->next) {
if (0 == strcmp(entry->name, libraryName)) {
if (entry->addressLow > (UDATA)lowAddress) {
entry->addressLow = (UDATA)lowAddress;
}
if (entry->addressHigh < (UDATA)highAddress) {
entry->addressHigh = (UDATA)highAddress;
}
return 0;
}
}
size_t libraryNameLength = strlen(libraryName);
char *libraryNameCopy = (char *)j9mem_allocate_memory(libraryNameLength + 1, OMRMEM_CATEGORY_VM);
if (NULL == libraryNameCopy) {
/* Allocation for library name failed. */
return 1;
}
NativeLibraryEntry *newEntry = reinterpret_cast<NativeLibraryEntry *>(pool_newElement(nativeLibrariesTable));
if (NULL == newEntry) {
/* Allocation failed. */
j9mem_free_memory(libraryNameCopy);
return 1;
}
memcpy(libraryNameCopy, libraryName, libraryNameLength + 1);
newEntry->ticks = j9time_nano_time();
newEntry->name = libraryNameCopy;
newEntry->addressLow = (UDATA)lowAddress;
newEntry->addressHigh = (UDATA)highAddress;
newEntry->next = NULL;
constantPoolTypes->_nativeLibrariesCount += 1;
constantPoolTypes->_nativeLibraryPathSizeTotal += libraryNameLength;
if (NULL != previousNativeLibraryEntry) {
previousNativeLibraryEntry->next = newEntry;
} else {
constantPoolTypes->_firstNativeLibraryEntry = newEntry;
}
constantPoolTypes->_previousNativeLibraryEntry = newEntry;
return 0;
}

void loadNativeLibraries(J9VMThread *currentThread)
{
OMRPORT_ACCESS_FROM_J9VMTHREAD(currentThread);
uintptr_t result = omrsl_get_libraries(processNativeLibrariesCallback, this);
if (0 != result) {
_buildResult = OutOfMemory;
}
}

VM_JFRConstantPoolTypes(J9VMThread *currentThread)
: _currentThread(currentThread)
, _vm(currentThread->javaVM)
Expand Down Expand Up @@ -1295,6 +1380,9 @@ class VM_JFRConstantPoolTypes {
, _threadContextSwitchRateCount(0)
, _threadStatisticsTable(NULL)
, _threadStatisticsCount(0)
, _nativeLibrariesTable(NULL)
, _nativeLibrariesCount(0)
, _nativeLibraryPathSizeTotal(0)
, _previousStackTraceEntry(NULL)
, _firstStackTraceEntry(NULL)
, _previousThreadEntry(NULL)
Expand All @@ -1311,6 +1399,8 @@ class VM_JFRConstantPoolTypes {
, _firstClassloaderEntry(NULL)
, _previousPackageEntry(NULL)
, _firstPackageEntry(NULL)
, _firstNativeLibraryEntry(NULL)
, _previousNativeLibraryEntry(NULL)
, _requiredBufferSize(0)
{
_classTable = hashTableNew(OMRPORT_FROM_J9PORT(privatePortLibrary), J9_GET_CALLSITE(), 0, sizeof(ClassEntry), sizeof(ClassEntry *), 0, J9MEM_CATEGORY_CLASSES, jfrClassHashFn, jfrClassHashEqualFn, NULL, _vm);
Expand Down Expand Up @@ -1422,23 +1512,31 @@ class VM_JFRConstantPoolTypes {
}

_classLoadingStatisticsTable = pool_new(sizeof(ClassLoadingStatisticsEntry), 0, sizeof(U_64), 0, J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM, POOL_FOR_PORT(privatePortLibrary));
if (NULL == _classLoadingStatisticsTable ) {
if (NULL == _classLoadingStatisticsTable) {
_buildResult = OutOfMemory;
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 ) {
if (NULL == _threadContextSwitchRateTable) {
_buildResult = OutOfMemory;
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 ) {
if (NULL == _threadStatisticsTable) {
_buildResult = OutOfMemory;
goto done;
}

_nativeLibrariesTable = pool_new(sizeof(NativeLibraryEntry), 0, sizeof(U_64), 0, J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM, POOL_FOR_PORT(privatePortLibrary));
if (NULL == _nativeLibrariesTable) {
_buildResult = OutOfMemory;
goto done;
}

loadNativeLibraries(_currentThread);

/* 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 @@ -1532,6 +1630,7 @@ class VM_JFRConstantPoolTypes {
pool_kill(_classLoadingStatisticsTable);
pool_kill(_threadContextSwitchRateTable);
pool_kill(_threadStatisticsTable);
pool_kill(_nativeLibrariesTable);
j9mem_free_memory(_globalStringTable);
}

Expand Down
9 changes: 9 additions & 0 deletions test/functional/cmdLineTests/jfr/jfrevents.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,13 @@ SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-ex
<output type="success" caseSensitive="yes" regex="no">jdk.GCHeapConfiguration</output>
<output type="failure" caseSensitive="yes" regex="no">jfr print: could not read recording</output>
</test>
<test id="test jfr native library - approx 30 seconds">
<command>$JFR_EXE$ print --xml --events "NativeLibrary" defaultJ9recording.jfr</command>
<output type="required" caseSensitive="yes" regex="no">http://www.w3.org/2001/XMLSchema-instance</output>
<output type="required" caseSensitive="yes" regex="no">jdk.NativeLibrary</output>
<output type="success" caseSensitive="yes" regex="no">name</output>
<output type="success" caseSensitive="yes" regex="no">baseAddress</output>
<output type="success" caseSensitive="yes" regex="no">topAddress</output>
<output type="failure" caseSensitive="yes" regex="no">jfr print: could not read recording</output>
</test>
</suite>