Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2d6d688
v0
zhengyu123 Jul 7, 2026
f7e3255
AI reviews
zhengyu123 Jul 7, 2026
dd2acd5
Merge branch 'main' into zgu/profiled_thread
zhengyu123 Jul 8, 2026
1793aea
Potential fix for pull request finding
zhengyu123 Jul 8, 2026
4414df3
Potential fix for pull request finding
zhengyu123 Jul 8, 2026
e74e4b3
Potential fix for pull request finding
zhengyu123 Jul 8, 2026
e1ed4cf
v1
zhengyu123 Jul 8, 2026
09ad293
Merge branch 'zgu/profiled_thread' of github.com:DataDog/java-profile…
zhengyu123 Jul 8, 2026
dfbe748
Fix test
zhengyu123 Jul 8, 2026
ac039e3
Merge
zhengyu123 Jul 8, 2026
7b9ac45
Remove ProfiledThread::currentSignalSafe()
zhengyu123 Jul 8, 2026
f766961
Pre-existed - add nullptr check
zhengyu123 Jul 8, 2026
63d7b61
Fix
zhengyu123 Jul 8, 2026
e5d59e7
Restore early behavior
zhengyu123 Jul 9, 2026
9ded4c2
Harden initCurrentThread() with initCurrentThreadSignalSafe()
zhengyu123 Jul 9, 2026
2d10883
Block signal for deleting ProfiledThread
zhengyu123 Jul 9, 2026
d59a7c2
Review comments
zhengyu123 Jul 9, 2026
00edbd4
Review comments
zhengyu123 Jul 10, 2026
800d38d
Review
zhengyu123 Jul 10, 2026
34b18f0
Profiler must be initalized on non-virtual thread
zhengyu123 Jul 10, 2026
d8f9c02
Merge branch 'main' into zgu/profiled_thread
zhengyu123 Jul 10, 2026
dcaba6a
New methods after merge
zhengyu123 Jul 10, 2026
42fd898
Comment JVMSupport::initialize()
zhengyu123 Jul 10, 2026
c4aa652
Merge branch 'main' into zgu/profiled_thread
zhengyu123 Jul 13, 2026
34db19a
Update ddprof-lib/src/main/cpp/threadLocalData.cpp
zhengyu123 Jul 13, 2026
8c3769d
Update ddprof-lib/src/main/cpp/profiler.cpp
zhengyu123 Jul 13, 2026
2636428
Update ddprof-lib/src/main/cpp/profiler.cpp
zhengyu123 Jul 13, 2026
d341e7b
Update ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java
zhengyu123 Jul 13, 2026
6b78a6c
Merge branch 'main' into zgu/profiled_thread
zhengyu123 Jul 13, 2026
7886564
Merge branch 'main' into zgu/profiled_thread
zhengyu123 Jul 14, 2026
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
4 changes: 2 additions & 2 deletions ddprof-lib/src/main/cpp/context_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void ContextApi::initializeContextTLS(ProfiledThread* thrd) {
}

bool ContextApi::get(u64& span_id, u64& root_span_id) {
ProfiledThread* thrd = ProfiledThread::currentSignalSafe();
ProfiledThread* thrd = ProfiledThread::current();
if (thrd == nullptr || !thrd->isContextInitialized()) {
return false;
}
Expand All @@ -59,7 +59,7 @@ bool ContextApi::get(u64& span_id, u64& root_span_id) {
}

Context ContextApi::snapshot() {
ProfiledThread* thrd = ProfiledThread::currentSignalSafe();
ProfiledThread* thrd = ProfiledThread::current();
if (thrd == nullptr) {
return {};
}
Expand Down
4 changes: 2 additions & 2 deletions ddprof-lib/src/main/cpp/ctimer_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ void CTimerJvmti::signalHandler(int signo, siginfo_t *siginfo, void *ucontext) {
return;
}
int tid = 0;
ProfiledThread *current = ProfiledThread::currentSignalSafe();
ProfiledThread *current = ProfiledThread::current();
assert(current == nullptr || !current->isDeepCrashHandler());
if (current != nullptr && JVMThread::current() == nullptr
&& current->inInitWindow()) {
Expand Down Expand Up @@ -281,7 +281,7 @@ void CTimer::signalHandler(int signo, siginfo_t *siginfo, void *ucontext) {
return;
}
int tid = 0;
ProfiledThread *current = ProfiledThread::currentSignalSafe();
ProfiledThread *current = ProfiledThread::current();
assert(current == nullptr || !current->isDeepCrashHandler());
// Guard against the race window between Profiler::registerThread() and
// thread_native_entry setting JVM TLS (PROF-13072): skip at most one signal
Expand Down
2 changes: 1 addition & 1 deletion ddprof-lib/src/main/cpp/flightRecorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1785,7 +1785,7 @@ void Recording::writeCurrentContext(Buffer *buf) {
buf->putVar64(rootSpanId);

size_t numAttrs = Profiler::instance()->numContextAttributes();
ProfiledThread* thrd = hasContext ? ProfiledThread::currentSignalSafe() : nullptr;
ProfiledThread* thrd = hasContext ? ProfiledThread::current() : nullptr;
for (size_t i = 0; i < numAttrs; i++) {
buf->putVar32(thrd != nullptr ? thrd->getOtelTagEncoding(i) : 0);
}
Expand Down
14 changes: 7 additions & 7 deletions ddprof-lib/src/main/cpp/guards.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@
// rejected because of the static TLS surplus on Graal).

int getInSignalDepth() {
ProfiledThread *pt = ProfiledThread::currentSignalSafe();
ProfiledThread *pt = ProfiledThread::current();
return pt != nullptr ? static_cast<int>(pt->signalDepth()) : 0;
}

bool isInTrackedSignalContext() {
ProfiledThread *pt = ProfiledThread::currentSignalSafe();
ProfiledThread *pt = ProfiledThread::current();
// null ProfiledThread = no thread context; the SignalHandlerScope
// never ran, so we have no positive evidence of a signal frame.
// See header comment for the rationale of returning false here.
return pt != nullptr && pt->signalDepth() != 0;
}

SignalHandlerScope::SignalHandlerScope() : _active(true) {
ProfiledThread *pt = ProfiledThread::currentSignalSafe();
ProfiledThread *pt = ProfiledThread::current();
if (pt != nullptr) {
pt->enterSignalScope();
} else {
Expand All @@ -49,23 +49,23 @@ SignalHandlerScope::SignalHandlerScope() : _active(true) {

SignalHandlerScope::~SignalHandlerScope() {
if (!_active) return;
ProfiledThread *pt = ProfiledThread::currentSignalSafe();
ProfiledThread *pt = ProfiledThread::current();
if (pt != nullptr) {
pt->exitSignalScope();
}
}

void SignalHandlerScope::release() {
if (!_active) return;
ProfiledThread *pt = ProfiledThread::currentSignalSafe();
ProfiledThread *pt = ProfiledThread::current();
if (pt != nullptr) {
pt->exitSignalScope();
}
_active = false;
}

void signalHandlerUnwindAfterLongjmp() {
ProfiledThread *pt = ProfiledThread::currentSignalSafe();
ProfiledThread *pt = ProfiledThread::current();
if (pt != nullptr) {
pt->exitSignalScope();
}
Expand All @@ -75,7 +75,7 @@ void signalHandlerUnwindAfterLongjmp() {
uint64_t CriticalSection::_fallback_bitmap[CriticalSection::FALLBACK_BITMAP_WORDS] = {};

CriticalSection::CriticalSection() : _entered(false), _using_fallback(false), _word_index(0), _bit_mask(0), _thread_ptr(nullptr) {
_thread_ptr = ProfiledThread::currentSignalSafe();
_thread_ptr = ProfiledThread::current();
if (_thread_ptr != nullptr) {
// Primary path: Use ProfiledThread storage (fast and memory-efficient)
_entered = _thread_ptr->tryEnterCriticalSection();
Expand Down
6 changes: 3 additions & 3 deletions ddprof-lib/src/main/cpp/hotspot/hotspotSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ __attribute__((no_sanitize("address"))) int HotspotSupport::walkVM(void* ucontex
// VMStructs is only available for hotspot JVM
assert(VM::isHotspot());

ProfiledThread* prof_thread = ProfiledThread::currentSignalSafe();
ProfiledThread* prof_thread = ProfiledThread::current();
if (prof_thread == nullptr) {
Counters::increment(SAMPLES_DROPPED_THREAD_LOCAL);
return 0;
Expand Down Expand Up @@ -1198,7 +1198,7 @@ int HotspotSupport::walkJavaStack(StackWalkRequest& request) {
if (cstack >= CSTACK_VM) {
java_frames = walkVM(ucontext, frames, max_depth, features, eventTypeFromBCI(request.event_type), lock_index, truncated);
} else {
AsyncSampleMutex mutex(ProfiledThread::currentSignalSafe());
AsyncSampleMutex mutex(ProfiledThread::current());
if (mutex.acquired()) {
java_frames = getJavaTraceAsync(ucontext, frames, max_depth, java_ctx, truncated);
if (java_frames > 0 && java_ctx->pc != NULL && VMStructs::hasMethodStructs()) {
Expand All @@ -1223,7 +1223,7 @@ int HotspotSupport::walkJavaStack(StackWalkRequest& request) {
java_frames = walkVM(ucontext, frames, max_depth, features, eventTypeFromBCI(request.event_type), lock_index, truncated);
} else {
// Async events
AsyncSampleMutex mutex(ProfiledThread::currentSignalSafe());
AsyncSampleMutex mutex(ProfiledThread::current());
if (mutex.acquired()) {
java_frames = getJavaTraceAsync(ucontext, frames, max_depth, java_ctx, truncated);
if (java_frames > 0 && java_ctx->pc != NULL && VMStructs::hasMethodStructs()) {
Expand Down
3 changes: 3 additions & 0 deletions ddprof-lib/src/main/cpp/hotspot/jitCodeCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "jitCodeCache.h"

#include "hotspot/vmStructs.h"
#include "threadLocalData.h"

SpinLock JitCodeCache::_stubs_lock;
CodeCache JitCodeCache::_runtime_stubs("[stubs]");
Expand All @@ -19,11 +20,13 @@ void JNICALL JitCodeCache::CompiledMethodLoad(jvmtiEnv *jvmti, jmethodID method,
jint map_length,
const jvmtiAddrLocationMap *map,
const void *compile_info) {
ProfiledThread::initCurrentThreadSignalSafe();
CodeHeap::updateBounds(code_addr, (const char *)code_addr + code_size);
}

void JNICALL JitCodeCache::DynamicCodeGenerated(jvmtiEnv *jvmti, const char *name,
const void *address, jint length) {
ProfiledThread::initCurrentThreadSignalSafe();
_stubs_lock.lock();
_runtime_stubs.add(address, length, name, true);
_stubs_lock.unlock();
Expand Down
4 changes: 3 additions & 1 deletion ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,8 @@ void JNICALL VMStructs::NativeMethodBind(jvmtiEnv *jvmti, JNIEnv *jni, jthread t
static int delayedCounter = 0;
static void **delayed = (void **)malloc(512 * sizeof(void *) * 2);

ProfiledThread::initCurrentThreadSignalSafe();

if (_memory_usage_func == NULL) {
if (jvmti != NULL && jni != NULL) {
checkNativeBinding(jvmti, jni, method, address);
Expand Down Expand Up @@ -684,7 +686,7 @@ bool VMThread::isJavaThread(VMThread* vm_thread) {

// JVMTI ThreadStart callback may have set the flag, which is reliable.
// Or we may already compute and cache it, so use it instead.
ProfiledThread *prof_thread = ProfiledThread::currentSignalSafe();
ProfiledThread *prof_thread = ProfiledThread::current();
if (prof_thread != nullptr) {
ProfiledThread::ThreadType type = prof_thread->threadType();
if (type != ProfiledThread::ThreadType::TYPE_UNKNOWN) {
Expand Down
2 changes: 1 addition & 1 deletion ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include "threadLocalData.h"

inline bool crashProtectionActive() {
ProfiledThread* pt = ProfiledThread::currentSignalSafe();
ProfiledThread* pt = ProfiledThread::current();
if (pt != nullptr) {
return pt->isProtected();
}
Expand Down
4 changes: 2 additions & 2 deletions ddprof-lib/src/main/cpp/itimer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void ITimer::signalHandler(int signo, siginfo_t *siginfo, void *ucontext) {
return; // Another critical section is active, defer profiling
}
int tid = 0;
ProfiledThread *current = ProfiledThread::currentSignalSafe();
ProfiledThread *current = ProfiledThread::current();
if (current != NULL) {
current->noteCPUSample(Profiler::instance()->recordingEpoch());
tid = current->tid();
Expand Down Expand Up @@ -116,7 +116,7 @@ void ITimerJvmti::signalHandler(int signo, siginfo_t *siginfo, void *ucontext) {
errno = saved_errno;
return;
}
ProfiledThread *current = ProfiledThread::currentSignalSafe();
ProfiledThread *current = ProfiledThread::current();
if (current != nullptr && JVMThread::current() == nullptr
&& current->inInitWindow()) {
current->tickInitWindow();
Expand Down
52 changes: 40 additions & 12 deletions ddprof-lib/src/main/cpp/javaApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "engine.h"
#include "hotspot/vmStructs.inline.h"
#include "incbin.h"
#include "jvmSupport.h"
#include "jvmThread.h"
#include "os.h"
#include "otel_process_ctx.h"
Expand Down Expand Up @@ -68,6 +69,12 @@ class JniString {

extern "C" DLLEXPORT jboolean JNICALL
Java_com_datadoghq_profiler_JavaProfiler_init0(JNIEnv *env, jclass unused) {
Error error = Profiler::instance()->init();
Comment thread
zhengyu123 marked this conversation as resolved.
Comment thread
zhengyu123 marked this conversation as resolved.
if (error) {
throwNew(env, "java/lang/IllegalStateException", error.message());
return JNI_FALSE;
}

Comment thread
zhengyu123 marked this conversation as resolved.
// JavaVM* has already been stored when the native library was loaded so we can pass nullptr here
return VM::initProfilerBridge(nullptr, true);
}
Comment thread
Copilot marked this conversation as resolved.
Expand Down Expand Up @@ -137,8 +144,12 @@ Java_com_datadoghq_profiler_JavaProfiler_getSamples(JNIEnv *env,
// still compatible in the event of signature changes in the future.
extern "C" DLLEXPORT void JNICALL
JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0() {
ProfiledThread *current = ProfiledThread::current();
assert(current != nullptr);
// Initialize thread TLS if it has not yet done
ProfiledThread *current = ProfiledThread::initCurrentThreadSignalSafe();
if(current == nullptr) {
return;
}
Comment thread
zhengyu123 marked this conversation as resolved.

int tid = current->tid();
if (unlikely(tid < 0)) {
return;
Expand Down Expand Up @@ -167,8 +178,12 @@ JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0() {

extern "C" DLLEXPORT void JNICALL
JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadRemove0() {
ProfiledThread *current = ProfiledThread::current();
assert(current != nullptr);
// Initialize thread TLS if it has not yet done
ProfiledThread *current = ProfiledThread::initCurrentThreadSignalSafe();
if(current == nullptr) {
return;
}
Comment thread
zhengyu123 marked this conversation as resolved.

int tid = current->tid();
if (unlikely(tid < 0)) {
return;
Expand Down Expand Up @@ -204,6 +219,10 @@ Java_com_datadoghq_profiler_JavaProfiler_recordTrace0(
JNIEnv *env, jclass unused, jlong rootSpanId, jstring endpoint,
jstring operation, jint sizeLimit) {
JniString endpoint_str(env, endpoint);

// Initialize thread TLS if it has not yet done
ProfiledThread::initCurrentThreadSignalSafe();

u32 endpointLabel = Profiler::instance()->stringLabelMap()->bounded_lookup(
endpoint_str.c_str(), endpoint_str.length(), sizeLimit);
// StringDictionary reserves 0 as "no entry"; valid IDs start at 1.
Expand Down Expand Up @@ -261,6 +280,9 @@ Java_com_datadoghq_profiler_JavaProfiler_describeDebugCounters0(
extern "C" DLLEXPORT void JNICALL
Java_com_datadoghq_profiler_JavaProfiler_recordSettingEvent0(
JNIEnv *env, jclass unused, jstring name, jstring value, jstring unit) {
// Initialize thread TLS if it has not yet done
ProfiledThread::initCurrentThreadSignalSafe();

int tid = ProfiledThread::currentTid();
if (tid < 0) {
return;
Expand All @@ -285,6 +307,10 @@ extern "C" DLLEXPORT void JNICALL
Java_com_datadoghq_profiler_JavaProfiler_recordQueueEnd0(
JNIEnv *env, jclass unused, jlong startTime, jlong endTime, jstring task,
jstring scheduler, jthread origin, jstring queueType, jint queueLength) {

// Initialize thread TLS if it has not yet done
ProfiledThread::initCurrentThreadSignalSafe();

int tid = ProfiledThread::currentTid();
if (tid < 0) {
return;
Expand Down Expand Up @@ -319,7 +345,7 @@ Java_com_datadoghq_profiler_JavaProfiler_recordQueueEnd0(

extern "C" DLLEXPORT void JNICALL
Java_com_datadoghq_profiler_JavaProfiler_parkEnter0(JNIEnv *env, jclass unused) {
ProfiledThread *current = ProfiledThread::current();
ProfiledThread *current = ProfiledThread::initCurrentThreadSignalSafe();
if (current == nullptr) {
return;
}
Expand All @@ -337,10 +363,11 @@ Java_com_datadoghq_profiler_JavaProfiler_parkEnter0(JNIEnv *env, jclass unused)
extern "C" DLLEXPORT void JNICALL
Java_com_datadoghq_profiler_JavaProfiler_parkExit0(
JNIEnv *env, jclass unused, jlong blocker, jlong unblockingSpanId) {
ProfiledThread *current = ProfiledThread::current();
ProfiledThread *current = ProfiledThread::initCurrentThreadSignalSafe();
if (current == nullptr) {
return;
}

u64 park_block_token = 0;
if (!current->parkExit(park_block_token) || park_block_token == 0) {
return;
Expand Down Expand Up @@ -370,7 +397,7 @@ Java_com_datadoghq_profiler_JavaProfiler_blockEnter0(
if (!decodeJavaBlockState(state, decoded)) {
return 0;
}
ProfiledThread *current = ProfiledThread::current();
ProfiledThread *current = ProfiledThread::initCurrentThreadSignalSafe();
if (current == nullptr) {
return 0;
}
Expand All @@ -392,7 +419,7 @@ Java_com_datadoghq_profiler_JavaProfiler_blockExit0(
if (block_token == 0) {
return;
}
ProfiledThread *current = ProfiledThread::current();
ProfiledThread *current = ProfiledThread::initCurrentThreadSignalSafe();
if (current == nullptr) {
return;
}
Expand Down Expand Up @@ -685,7 +712,8 @@ Java_com_datadoghq_profiler_OTelContext_readProcessCtx0(JNIEnv *env, jclass unus

extern "C" DLLEXPORT jobject JNICALL
Java_com_datadoghq_profiler_JavaProfiler_initializeContextTLS0(JNIEnv* env, jclass unused, jlongArray metadata) {
ProfiledThread* thrd = ProfiledThread::current();
// Initialize thread TLS if it has not yet done
ProfiledThread* thrd = ProfiledThread::initCurrentThreadSignalSafe();
assert(thrd != nullptr);

if (!thrd->isContextInitialized()) {
Expand Down Expand Up @@ -850,7 +878,7 @@ extern "C" DLLEXPORT void JNICALL
Java_com_datadoghq_profiler_JavaProfiler_setTraceContext0(JNIEnv* env, jclass unused,
jlong localRootSpanId, jlong spanId, jlong traceIdHigh, jlong traceIdLow,
jint slot0, jint enc0, jbyteArray utf0, jint slot1, jint enc1, jbyteArray utf1) {
ProfiledThread* thrd = ProfiledThread::current();
ProfiledThread* thrd = ProfiledThread::initCurrentThreadSignalSafe();
if (thrd == nullptr) {
return;
}
Expand Down Expand Up @@ -906,7 +934,7 @@ Java_com_datadoghq_profiler_JavaProfiler_setTraceContext0(JNIEnv* env, jclass un
// detached (valid=0), mirroring the DBB clear path (setContext(0,0,0,0) + clearContextValue*).
extern "C" DLLEXPORT void JNICALL
Java_com_datadoghq_profiler_JavaProfiler_clearTraceContext0(JNIEnv* env, jclass unused) {
ProfiledThread* thrd = ProfiledThread::current();
ProfiledThread* thrd = ProfiledThread::initCurrentThreadSignalSafe();
if (thrd == nullptr) {
return;
}
Expand All @@ -931,7 +959,7 @@ Java_com_datadoghq_profiler_JavaProfiler_clearTraceContext0(JNIEnv* env, jclass
extern "C" DLLEXPORT jboolean JNICALL
Java_com_datadoghq_profiler_JavaProfiler_setContextValue0(JNIEnv* env, jclass unused,
jint slot, jint encoding, jbyteArray utf8) {
ProfiledThread* thrd = ProfiledThread::current();
ProfiledThread* thrd = ProfiledThread::initCurrentThreadSignalSafe();
if (thrd == nullptr || slot < 0 || slot >= (jint)DD_TAGS_CAPACITY) {
return JNI_FALSE;
}
Expand Down
14 changes: 10 additions & 4 deletions ddprof-lib/src/main/cpp/jvmSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
volatile JVMSupport::JMethodIDLoadStats JVMSupport::jmethodID_load_state = JVMSupport::No_loaded;
Mutex JVMSupport::_initialization_lock;


// This method must be called after JVM has been properly initialized, e.g. after JVMTI::VMinit()
// callback.
// Currently, there are two paths lead to this call
// - JVMTI::VMInit() callback (vmEntry.cpp)
// - JavaProfiler.getInstance() via JNI down call - JVM must have been initialized
bool JVMSupport::initialize() {
MutexLocker locker(_initialization_lock);

Expand All @@ -32,12 +38,12 @@ bool JVMSupport::initialize() {
return false;
}

// Add ProfiledThread key checking here in next PR
return true;
// Check ProfiledThread key, it is critical for storing per-thread metadata
return ProfiledThread::isThreadKeyValid();
}

bool JVMSupport::isInitialized() {
return JVMThread::isInitialized();
return JVMThread::isInitialized() && ProfiledThread::isThreadKeyValid();
}

JVMSupport::JMethodIDLoadStats JVMSupport::getLoadState() {
Expand Down Expand Up @@ -95,7 +101,7 @@ int JVMSupport::asyncGetCallTrace(ASGCT_CallFrame *frames, int max_depth, void*
return 0;
}

AsyncSampleMutex mutex(ProfiledThread::currentSignalSafe());
AsyncSampleMutex mutex(ProfiledThread::current());
Comment thread
zhengyu123 marked this conversation as resolved.
if (!mutex.acquired()) {
return 0;
}
Expand Down
Loading
Loading