Skip to content

Commit 649873a

Browse files
committed
feat(jfr): resolve unblocking span for synchronized via direct ObjectMonitor::_owner read
1 parent b228ebc commit 649873a

5 files changed

Lines changed: 63 additions & 5 deletions

File tree

ddprof-lib/src/main/cpp/profiler.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -946,10 +946,11 @@ void JNICALL Profiler::MonitorContendedEnterCallback(jvmtiEnv *jvmti, JNIEnv *jn
946946
if (thrd == nullptr) return;
947947
Context& ctx = Contexts::get();
948948
if (ctx.spanId == 0) return;
949-
thrd->_monitor_block.start_ticks = TSC::ticks();
950-
thrd->_monitor_block.span_id = ctx.spanId;
951-
thrd->_monitor_block.root_span_id = ctx.rootSpanId;
952-
thrd->_monitor_block.obj_addr = (uintptr_t)(void*)object;
949+
thrd->_monitor_block.start_ticks = TSC::ticks();
950+
thrd->_monitor_block.span_id = ctx.spanId;
951+
thrd->_monitor_block.root_span_id = ctx.rootSpanId;
952+
thrd->_monitor_block.obj_addr = (uintptr_t)(void*)object;
953+
thrd->_monitor_block.unblocking_span_id = VMObjectMonitor::ownerSpanId((const void*)object);
953954
}
954955

955956
void JNICALL Profiler::MonitorContendedEnteredCallback(jvmtiEnv *jvmti, JNIEnv *jni,
@@ -963,7 +964,7 @@ void JNICALL Profiler::MonitorContendedEnteredCallback(jvmtiEnv *jvmti, JNIEnv *
963964
event._span_id = thrd->_monitor_block.span_id;
964965
event._root_span_id = thrd->_monitor_block.root_span_id;
965966
event._blocker = thrd->_monitor_block.obj_addr;
966-
event._unblocking_span_id = 0;
967+
event._unblocking_span_id = thrd->_monitor_block.unblocking_span_id;
967968

968969
thrd->_monitor_block.obj_addr = 0;
969970

ddprof-lib/src/main/cpp/thread.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ ProfiledThread *ProfiledThread::currentSignalSafe() {
133133
return __atomic_load_n(&_tls_key_initialized, __ATOMIC_ACQUIRE) ? (ProfiledThread *)pthread_getspecific(_tls_key) : nullptr;
134134
}
135135

136+
ProfiledThread* ProfiledThread::findByTid(int tid) {
137+
int size = __atomic_load_n(&_running_buffer_pos, __ATOMIC_ACQUIRE);
138+
for (int i = 0; i < size; i++) {
139+
ProfiledThread* t = _buffer[i];
140+
if (t != nullptr && t->_tid == tid) return t;
141+
}
142+
return nullptr;
143+
}
144+
136145
int ProfiledThread::popFreeSlot() {
137146
int current_top;
138147
int new_top;

ddprof-lib/src/main/cpp/thread.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class ProfiledThread : public ThreadLocalData {
6262
u64 span_id;
6363
u64 root_span_id;
6464
uintptr_t obj_addr;
65+
u64 unblocking_span_id;
6566
};
6667

6768
u64 _pc;
@@ -91,6 +92,7 @@ class ProfiledThread : public ThreadLocalData {
9192

9293
public:
9394
static ProfiledThread *forTid(int tid) { return new ProfiledThread(-1, tid); }
95+
static ProfiledThread* findByTid(int tid);
9496
static ProfiledThread *inBuffer(int buffer_pos) {
9597
return new ProfiledThread(buffer_pos, 0);
9698
}

ddprof-lib/src/main/cpp/vmStructs.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <stdarg.h>
1010
#include "vmStructs.h"
1111
#include "vmEntry.h"
12+
#include "context.h"
13+
#include "thread.h"
1214
#include "j9Ext.h"
1315
#include "jniHelper.h"
1416
#include "jvmHeap.h"
@@ -675,6 +677,38 @@ int VMThread::osThreadId() {
675677
return -1;
676678
}
677679

680+
u64 VMObjectMonitor::ownerSpanId(const void* object) {
681+
if (_monitor_owner_offset == 0) return 0;
682+
683+
// Read the mark word from the object header.
684+
uintptr_t mark = (uintptr_t)SafeAccess::load((void**)object, nullptr);
685+
686+
// At MonitorContendedEnter time the monitor must be inflated (MONITOR_BIT set
687+
// in bits [1:0]). Guard defensively for any narrow races.
688+
if ((mark & 0x3) != MONITOR_BIT) return 0;
689+
690+
// Extract ObjectMonitor* (pointer stored in bits 63:2, 4-byte aligned).
691+
const char* monitor = (const char*)(mark & ~(uintptr_t)0x3);
692+
693+
// Read ObjectMonitor::_owner (JavaThread*).
694+
const char* owner_thread = (const char*)SafeAccess::load(
695+
(void**)(monitor + _monitor_owner_offset), nullptr);
696+
if (owner_thread == nullptr) return 0;
697+
698+
// JavaThread* -> OS thread ID via existing VMThread infrastructure.
699+
int owner_tid = VMThread::cast(owner_thread)->osThreadId();
700+
if (owner_tid <= 0) return 0;
701+
702+
// OS thread ID -> ProfiledThread* -> span ID.
703+
ProfiledThread* owner = ProfiledThread::findByTid(owner_tid);
704+
if (owner == nullptr || !owner->isContextTlsInitialized()) return 0;
705+
706+
Context* ctx = owner->getContextTlsPtr();
707+
if (ctx == nullptr) return 0;
708+
709+
return ctx->spanId; // volatile u64, safe to read cross-thread on x86/aarch64
710+
}
711+
678712
JNIEnv* VMThread::jni() {
679713
if (_env_offset < 0) {
680714
return VM::jni(); // fallback for non-HotSpot JVM

ddprof-lib/src/main/cpp/vmStructs.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ typedef void* address;
196196
field(_osthread_id_offset, offset, MATCH_SYMBOLS("_thread_id")) \
197197
field_with_version(_osthread_state_offset, offset, 10, MAX_VERSION, MATCH_SYMBOLS("_state")) \
198198
type_end() \
199+
type_begin(VMObjectMonitor, MATCH_SYMBOLS("ObjectMonitor")) \
200+
field(_monitor_owner_offset, offset, MATCH_SYMBOLS("_owner")) \
201+
type_end() \
199202
type_begin(VMThreadShadow, MATCH_SYMBOLS("ThreadShadow")) \
200203
field(_thread_exception_offset, offset, MATCH_SYMBOLS("_exception_file")) \
201204
type_end() \
@@ -800,6 +803,15 @@ DECLARE_END
800803
DECLARE(VMConstMethod)
801804
DECLARE_END
802805

806+
DECLARE(VMObjectMonitor)
807+
public:
808+
// Returns the span ID of the JavaThread currently owning this monitor,
809+
// reading ObjectMonitor::_owner directly from JVM memory without a safepoint.
810+
// Returns 0 if the owner cannot be determined (non-HotSpot, uninitialized
811+
// offset, or any failure in the pointer chain).
812+
static u64 ownerSpanId(const void* object);
813+
DECLARE_END
814+
803815

804816
DECLARE(VMMethod)
805817
private:

0 commit comments

Comments
 (0)