Skip to content

Commit 33767c2

Browse files
authored
Merge pull request eclipse-openj9#20301 from thallium/threaddump
Add JFR ThreadDump support
2 parents 0c17bb5 + 095c7e6 commit 33767c2

File tree

5 files changed

+285
-14
lines changed

5 files changed

+285
-14
lines changed

runtime/vm/BufferWriter.hpp

+18-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class VM_BufferWriter {
3131
* Data members
3232
*/
3333
private:
34+
J9PortLibrary *_portLibrary;
3435
U_8 *_buffer;
3536
U_8 *_cursor;
3637
U_8 *_bufferEnd;
@@ -90,8 +91,9 @@ class VM_BufferWriter {
9091

9192
public:
9293

93-
VM_BufferWriter(U_8 *buffer, UDATA size)
94-
: _buffer(buffer)
94+
VM_BufferWriter(J9PortLibrary *portLibrary, U_8 *buffer, UDATA size)
95+
: _portLibrary(portLibrary)
96+
, _buffer(buffer)
9597
, _cursor(buffer)
9698
, _bufferEnd(buffer + size)
9799
, _maxCursor(NULL)
@@ -342,6 +344,20 @@ class VM_BufferWriter {
342344
writeU8(val ? 1 : 0);
343345
}
344346

347+
void
348+
writeFormattedString(const char *format, ...)
349+
{
350+
OMRPORT_ACCESS_FROM_J9PORT(_portLibrary);
351+
va_list args;
352+
va_start(args, format);
353+
uintptr_t totalLength = omrstr_vprintf(NULL, 0, format, args);
354+
if (checkBounds(totalLength)) {
355+
omrstr_vprintf((char *)_cursor, _bufferEnd - _cursor, format, args);
356+
_cursor += totalLength;
357+
}
358+
va_end(args);
359+
}
360+
345361
static U_32
346362
convertFromLEB128ToU32(U_8 *start)
347363
{

runtime/vm/JFRChunkWriter.cpp

+243
Original file line numberDiff line numberDiff line change
@@ -965,4 +965,247 @@ VM_JFRChunkWriter::writeThreadStatisticsEvent(void *anElement, void *userData)
965965
writeEventSize(_bufferWriter, dataStart);
966966
}
967967

968+
static void
969+
writeObject(J9JavaVM *vm, j9object_t obj, VM_BufferWriter *bufferWriter)
970+
{
971+
J9ROMClass *romClass = NULL;
972+
if (J9VM_IS_INITIALIZED_HEAPCLASS_VM(vm, obj)) {
973+
romClass = J9VM_J9CLASS_FROM_HEAPCLASS_VM(vm, obj)->romClass;
974+
} else {
975+
romClass = J9OBJECT_CLAZZ_VM(vm, obj)->romClass;
976+
}
977+
978+
J9UTF8 *className = J9ROMCLASS_CLASSNAME(romClass);
979+
bufferWriter->writeFormattedString("%.*s@%p", J9UTF8_LENGTH(className), J9UTF8_DATA(className), obj);
980+
}
981+
982+
static UDATA
983+
stackWalkCallback(J9VMThread *vmThread, J9StackWalkState *state)
984+
{
985+
J9JavaVM *vm = vmThread->javaVM;
986+
J9ObjectMonitorInfo *monitorInfo = (J9ObjectMonitorInfo *)state->userData2;
987+
IDATA *monitorCount = (IDATA *)(&state->userData3);
988+
J9Method *method = state->method;
989+
J9Class *methodClass = J9_CLASS_FROM_METHOD(method);
990+
J9UTF8 *className = J9ROMCLASS_CLASSNAME(methodClass->romClass);
991+
J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method);
992+
J9UTF8 *methodName = J9ROMMETHOD_NAME(romMethod);
993+
994+
VM_BufferWriter *bufferWriter = (VM_BufferWriter *)state->userData1;
995+
996+
bufferWriter->writeFormattedString(
997+
"at %.*s.%.*s",
998+
J9UTF8_LENGTH(className), J9UTF8_DATA(className),
999+
J9UTF8_LENGTH(methodName), J9UTF8_DATA(methodName));
1000+
1001+
if (J9_ARE_ANY_BITS_SET(romMethod->modifiers, J9AccNative)) {
1002+
bufferWriter->writeFormattedString("(Native Method)\n");
1003+
} else {
1004+
UDATA offsetPC = state->bytecodePCOffset;
1005+
bool compiledMethod = (NULL != state->jitInfo);
1006+
J9UTF8 *sourceFile = getSourceFileNameForROMClass(vm, methodClass->classLoader, methodClass->romClass);
1007+
if (NULL != sourceFile) {
1008+
bufferWriter->writeFormattedString(
1009+
"(%.*s", J9UTF8_LENGTH(sourceFile), J9UTF8_DATA(sourceFile));
1010+
1011+
UDATA lineNumber = getLineNumberForROMClass(vm, method, offsetPC);
1012+
1013+
if ((UDATA)-1 != lineNumber) {
1014+
bufferWriter->writeFormattedString(":%zu", lineNumber);
1015+
}
1016+
1017+
if (compiledMethod) {
1018+
bufferWriter->writeFormattedString("(Compiled Code)");
1019+
}
1020+
1021+
bufferWriter->writeFormattedString(")\n");
1022+
} else {
1023+
bufferWriter->writeFormattedString("(Bytecode PC: %zu", offsetPC);
1024+
if (compiledMethod) {
1025+
bufferWriter->writeFormattedString("(Compiled Code)");
1026+
}
1027+
bufferWriter->writeFormattedString(")\n");
1028+
}
1029+
1030+
/* Use a while loop as there may be more than one lock taken in a stack frame. */
1031+
while ((0 != *monitorCount) && ((UDATA)monitorInfo->depth == state->framesWalked)) {
1032+
bufferWriter->writeFormattedString("\t(entered lock: ");
1033+
writeObject(vm, monitorInfo->object, bufferWriter);
1034+
bufferWriter->writeFormattedString(")\n");
1035+
1036+
monitorInfo += 1;
1037+
state->userData2 = monitorInfo;
1038+
1039+
(*monitorCount) -= 1;
1040+
}
1041+
}
1042+
1043+
return J9_STACKWALK_KEEP_ITERATING;
1044+
}
1045+
1046+
static void
1047+
writeThreadInfo(J9VMThread *currentThread, J9VMThread *walkThread, VM_BufferWriter *bufferWriter)
1048+
{
1049+
J9JavaVM *vm = currentThread->javaVM;
1050+
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
1051+
UDATA javaTID = J9VMJAVALANGTHREAD_TID(currentThread, walkThread->threadObject);
1052+
UDATA osTID = ((J9AbstractThread *)walkThread->osThread)->tid;
1053+
UDATA javaPriority = vmFuncs->getJavaThreadPriority(vm, walkThread);
1054+
UDATA state = J9VMTHREAD_STATE_UNKNOWN;
1055+
const char *stateStr = "?";
1056+
j9object_t monitorObject = NULL;
1057+
char *threadName = NULL;
1058+
1059+
/* Get thread state and monitor */
1060+
state = getVMThreadObjectState(walkThread, &monitorObject, NULL, NULL);
1061+
switch (state) {
1062+
case J9VMTHREAD_STATE_RUNNING:
1063+
stateStr = "R";
1064+
break;
1065+
case J9VMTHREAD_STATE_BLOCKED:
1066+
stateStr = "B";
1067+
break;
1068+
case J9VMTHREAD_STATE_WAITING:
1069+
case J9VMTHREAD_STATE_WAITING_TIMED:
1070+
case J9VMTHREAD_STATE_SLEEPING:
1071+
stateStr = "CW";
1072+
break;
1073+
case J9VMTHREAD_STATE_PARKED:
1074+
case J9VMTHREAD_STATE_PARKED_TIMED:
1075+
stateStr = "P";
1076+
break;
1077+
case J9VMTHREAD_STATE_SUSPENDED:
1078+
stateStr = "S";
1079+
break;
1080+
case J9VMTHREAD_STATE_DEAD:
1081+
stateStr = "Z";
1082+
break;
1083+
case J9VMTHREAD_STATE_INTERRUPTED:
1084+
stateStr = "I";
1085+
break;
1086+
case J9VMTHREAD_STATE_UNKNOWN:
1087+
stateStr = "?";
1088+
break;
1089+
default:
1090+
stateStr = "??";
1091+
break;
1092+
}
1093+
1094+
/* Get thread name */
1095+
#if JAVA_SPEC_VERSION >= 21
1096+
if (IS_JAVA_LANG_VIRTUALTHREAD(currentThread, walkThread->threadObject)) {
1097+
/* For VirtualThread, get name from threadObject directly. */
1098+
j9object_t nameObject = J9VMJAVALANGTHREAD_NAME(currentThread, walkThread->threadObject);
1099+
threadName = getVMThreadNameFromString(currentThread, nameObject);
1100+
} else
1101+
#endif /* JAVA_SPEC_VERSION >= 21 */
1102+
{
1103+
threadName = getOMRVMThreadName(walkThread->omrVMThread);
1104+
releaseOMRVMThreadName(walkThread->omrVMThread);
1105+
}
1106+
bufferWriter->writeFormattedString(
1107+
"\"%s\" J9VMThread: %p tid: %zd nid: %zd prio: %zd state: %s",
1108+
threadName,
1109+
walkThread,
1110+
javaTID,
1111+
osTID,
1112+
javaPriority,
1113+
stateStr);
1114+
1115+
if (J9VMTHREAD_STATE_BLOCKED == state) {
1116+
bufferWriter->writeFormattedString(" blocked on: ");
1117+
} else if ((J9VMTHREAD_STATE_WAITING == state) || (J9VMTHREAD_STATE_WAITING_TIMED == state)) {
1118+
bufferWriter->writeFormattedString(" waiting on: ");
1119+
} else if ((J9VMTHREAD_STATE_PARKED == state) || (J9VMTHREAD_STATE_PARKED_TIMED == state)) {
1120+
bufferWriter->writeFormattedString(" parked on: ");
1121+
} else {
1122+
bufferWriter->writeFormattedString("\n");
1123+
return;
1124+
}
1125+
1126+
if (NULL != monitorObject) {
1127+
writeObject(vm, monitorObject, bufferWriter);
1128+
} else {
1129+
bufferWriter->writeFormattedString("<unknown>");
1130+
}
1131+
bufferWriter->writeFormattedString("\n");
1132+
}
1133+
1134+
static void
1135+
writeStacktrace(J9VMThread *currentThread, J9VMThread *walkThread, VM_BufferWriter *bufferWriter)
1136+
{
1137+
J9JavaVM *vm = currentThread->javaVM;
1138+
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
1139+
J9StackWalkState stackWalkState = {0};
1140+
const size_t maxMonitorInfosPerThread = 32;
1141+
J9ObjectMonitorInfo monitorInfos[maxMonitorInfosPerThread];
1142+
memset(monitorInfos, 0, sizeof(monitorInfos));
1143+
1144+
IDATA monitorCount = vmFuncs->getOwnedObjectMonitors(currentThread, walkThread, monitorInfos, maxMonitorInfosPerThread, FALSE);
1145+
1146+
stackWalkState.walkThread = walkThread;
1147+
stackWalkState.flags =
1148+
J9_STACKWALK_ITERATE_FRAMES
1149+
| J9_STACKWALK_INCLUDE_NATIVES
1150+
| J9_STACKWALK_VISIBLE_ONLY
1151+
| J9_STACKWALK_RECORD_BYTECODE_PC_OFFSET
1152+
| J9_STACKWALK_NO_ERROR_REPORT;
1153+
stackWalkState.skipCount = 0;
1154+
stackWalkState.frameWalkFunction = stackWalkCallback;
1155+
stackWalkState.userData1 = bufferWriter;
1156+
stackWalkState.userData2 = monitorInfos;
1157+
stackWalkState.userData3 = (void *)monitorCount;
1158+
1159+
vmFuncs->haltThreadForInspection(currentThread, walkThread);
1160+
vm->walkStackFrames(currentThread, &stackWalkState);
1161+
vmFuncs->resumeThreadForInspection(currentThread, walkThread);
1162+
1163+
bufferWriter->writeFormattedString("\n");
1164+
}
1165+
1166+
U_8 *
1167+
VM_JFRChunkWriter::writeThreadDumpEvent()
1168+
{
1169+
/* reserve size field */
1170+
U_8 *dataStart = reserveEventSize();
1171+
1172+
_bufferWriter->writeLEB128(ThreadDumpID);
1173+
1174+
/* write start time */
1175+
_bufferWriter->writeLEB128(j9time_current_time_millis());
1176+
1177+
const U_64 bufferSize = THREAD_DUMP_EVENT_SIZE_PER_THREAD * _vm->peakThreadCount;
1178+
U_8 *resultBuffer = (U_8 *)j9mem_allocate_memory(sizeof(U_8) * bufferSize, OMRMEM_CATEGORY_VM);
1179+
1180+
if (NULL != resultBuffer) {
1181+
VM_BufferWriter resultWriter(privatePortLibrary, resultBuffer, bufferSize);
1182+
J9VMThread *walkThread = J9_LINKED_LIST_START_DO(_vm->mainThread);
1183+
UDATA numThreads = 0;
1184+
J9InternalVMFunctions *vmFuncs = _vm->internalVMFunctions;
1185+
1186+
Assert_VM_mustHaveVMAccess(_currentThread);
1187+
vmFuncs->acquireExclusiveVMAccess(_currentThread);
1188+
1189+
while (NULL != walkThread) {
1190+
writeThreadInfo(_currentThread, walkThread, &resultWriter);
1191+
writeStacktrace(_currentThread, walkThread, &resultWriter);
1192+
1193+
walkThread = J9_LINKED_LIST_NEXT_DO(_vm->mainThread, walkThread);
1194+
numThreads += 1;
1195+
}
1196+
resultWriter.writeFormattedString("Number of threads: %zd", numThreads);
1197+
1198+
vmFuncs->releaseExclusiveVMAccess(_currentThread);
1199+
1200+
writeUTF8String(resultWriter.getBufferStart(), resultWriter.getSize());
1201+
j9mem_free_memory(resultBuffer);
1202+
} else {
1203+
_buildResult = OutOfMemory;
1204+
}
1205+
1206+
/* write size */
1207+
writeEventSize(dataStart);
1208+
1209+
return dataStart;
1210+
}
9681211
#endif /* defined(J9VM_OPT_JFR) */

runtime/vm/JFRChunkWriter.hpp

+15-3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ enum MetadataTypeID {
8181
ClassLoadingStatisticsID = 100,
8282
PhysicalMemoryID = 108,
8383
ExecutionSampleID = 109,
84+
ThreadDumpID = 111,
8485
ThreadID = 164,
8586
ThreadGroupID = 165,
8687
ClassID = 166,
@@ -176,6 +177,7 @@ class VM_JFRChunkWriter {
176177
static constexpr int CLASS_LOADING_STATISTICS_EVENT_SIZE = 5 * sizeof(I_64);
177178
static constexpr int THREAD_CONTEXT_SWITCH_RATE_SIZE = sizeof(float) + (3 * sizeof(I_64));
178179
static constexpr int THREAD_STATISTICS_EVENT_SIZE = (6 * sizeof(U_64)) + sizeof(U_32);
180+
static constexpr int THREAD_DUMP_EVENT_SIZE_PER_THREAD = 1000;
179181

180182
static constexpr int METADATA_ID = 1;
181183

@@ -290,7 +292,7 @@ class VM_JFRChunkWriter {
290292

291293
}
292294

293-
void writeJFRChunk()
295+
void writeJFRChunk(bool dumpCalled)
294296
{
295297
U_8 *buffer = NULL;
296298
UDATA requiredBufferSize = 0;
@@ -328,7 +330,7 @@ class VM_JFRChunkWriter {
328330
if (NULL == buffer) {
329331
_buildResult = OutOfMemory;
330332
} else {
331-
VM_BufferWriter writer(buffer, requiredBufferSize);
333+
VM_BufferWriter writer(privatePortLibrary, buffer, requiredBufferSize);
332334

333335
_bufferWriter = &writer;
334336

@@ -401,6 +403,10 @@ class VM_JFRChunkWriter {
401403

402404
writePhysicalMemoryEvent();
403405

406+
if (dumpCalled) {
407+
writeThreadDumpEvent();
408+
}
409+
404410
writeJFRHeader();
405411

406412
if (_bufferWriter->overflowOccurred()) {
@@ -758,6 +764,8 @@ class VM_JFRChunkWriter {
758764

759765
void writeStringLiteral(const char *string, UDATA len);
760766

767+
void writeFormattedString(const char *format, ...);
768+
761769
U_8 *writeThreadStateCheckpointEvent();
762770

763771
U_8 *writePackageCheckpointEvent();
@@ -790,6 +798,8 @@ class VM_JFRChunkWriter {
790798

791799
U_8 *writeOSInformationEvent();
792800

801+
U_8 *writeThreadDumpEvent();
802+
793803
void writeInitialSystemPropertyEvents(J9JavaVM *vm);
794804

795805
void writeInitialEnvironmentVariableEvents();
@@ -868,7 +878,9 @@ class VM_JFRChunkWriter {
868878

869879
requiredBufferSize += _constantPoolTypes.getThreadContextSwitchRateCount() * THREAD_CONTEXT_SWITCH_RATE_SIZE;
870880

871-
requiredBufferSize += (_constantPoolTypes.getThreadStatisticsCount() * THREAD_STATISTICS_EVENT_SIZE);
881+
requiredBufferSize += _constantPoolTypes.getThreadStatisticsCount() * THREAD_STATISTICS_EVENT_SIZE;
882+
883+
requiredBufferSize += _vm->peakThreadCount * THREAD_DUMP_EVENT_SIZE_PER_THREAD;
872884

873885
return requiredBufferSize;
874886
}

runtime/vm/JFRWriter.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ class VM_JFRWriter {
175175
}
176176

177177
static bool
178-
flushJFRDataToFile(J9VMThread *currentThread, bool finalWrite)
178+
flushJFRDataToFile(J9VMThread *currentThread, bool finalWrite, bool dumpCalled)
179179
{
180180
bool result = true;
181181
VM_JFRChunkWriter chunkWriter(currentThread, finalWrite);
@@ -191,7 +191,7 @@ class VM_JFRWriter {
191191
goto fail;
192192
}
193193

194-
chunkWriter.writeJFRChunk();
194+
chunkWriter.writeJFRChunk(dumpCalled);
195195
if (!chunkWriter.isOkay()) {
196196
result = false;
197197
goto fail;

0 commit comments

Comments
 (0)