Skip to content

Commit 5ed7a66

Browse files
authored
Refactor: JVM implementation separation and JVMThread abstraction (#435)
1 parent 312fc25 commit 5ed7a66

41 files changed

Lines changed: 434 additions & 1030 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Copyright 2023 Andrei Pangin
3-
* Copyright 2025, Datadog, Inc.
3+
* Copyright 2025, 2026, Datadog, Inc.
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@
2323
#include "libraries.h"
2424
#include "profiler.h"
2525
#include "threadState.inline.h"
26-
#include "vmStructs.h"
2726
#include <assert.h>
2827
#include <stdlib.h>
2928
#include <sys/syscall.h>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
#include "threadFilter.h"
2727
#include "threadState.h"
2828
#include "tsc.h"
29-
#include "vmStructs.h"
29+
#include "hotspot/vmStructs.h"
3030
#include <arpa/inet.h>
3131
#include <cxxabi.h>
3232
#include <errno.h>

ddprof-lib/src/main/cpp/vmStructs.cpp renamed to ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp

Lines changed: 86 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
/*
22
* Copyright The async-profiler authors
3+
* Copyright 2025, 2026 Datadog, Inc
34
* SPDX-License-Identifier: Apache-2.0
45
*/
56

67
#include <pthread.h>
78
#include <string.h>
89
#include <unistd.h>
910
#include <stdarg.h>
10-
#include "vmStructs.h"
11+
#include "hotspot/vmStructs.inline.h"
1112
#include "vmEntry.h"
12-
#include "j9Ext.h"
1313
#include "jniHelper.h"
1414
#include "jvmHeap.h"
15+
#include "jvmThread.h"
1516
#include "safeAccess.h"
1617
#include "spinLock.h"
17-
#include "common.h"
18+
#include "threadState.h"
1819

1920
CodeCache* VMStructs::_libjvm = nullptr;
2021
bool VMStructs::_has_class_names = false;
@@ -79,11 +80,8 @@ DECLARE_LONG_CONSTANTS_DO(INIT_LONG_CONSTANT, INIT_LONG_CONSTANT)
7980
#undef INIT_INT_CONSTANT
8081
#undef INIT_LONG_CONSTANT
8182

82-
83-
jfieldID VMStructs::_eetop;
84-
jfieldID VMStructs::_tid;
83+
jfieldID VMStructs::_eetop = NULL;
8584
jfieldID VMStructs::_klass = NULL;
86-
int VMStructs::_tls_index = -1;
8785
intptr_t VMStructs::_env_offset = -1;
8886
void* VMStructs::_java_thread_vtbl[6];
8987

@@ -122,7 +120,6 @@ void VMStructs::init(CodeCache* libjvm) {
122120
void VMStructs::ready() {
123121
resolveOffsets();
124122
patchSafeFetch();
125-
initThreadBridge();
126123
}
127124

128125
bool matchAny(const char* target_name, std::initializer_list<const char*> names) {
@@ -482,51 +479,6 @@ void VMStructs::patchSafeFetch() {
482479
}
483480
}
484481

485-
void VMStructs::initTLS(void* vm_thread) {
486-
for (int i = 0; i < 1024; i++) {
487-
if (pthread_getspecific((pthread_key_t)i) == vm_thread) {
488-
_tls_index = i;
489-
break;
490-
}
491-
}
492-
}
493-
494-
void VMStructs::initThreadBridge() {
495-
jthread thread;
496-
if (VM::jvmti()->GetCurrentThread(&thread) != 0) {
497-
return;
498-
}
499-
500-
JNIEnv* env = VM::jni();
501-
jclass thread_class = env->FindClass("java/lang/Thread");
502-
if (thread_class == NULL || (_tid = env->GetFieldID(thread_class, "tid", "J")) == NULL) {
503-
env->ExceptionClear();
504-
return;
505-
}
506-
507-
if (VM::isOpenJ9()) {
508-
void* j9thread = J9Ext::j9thread_self();
509-
if (j9thread != NULL) {
510-
initTLS(j9thread);
511-
}
512-
} else {
513-
// Get eetop field - a bridge from Java Thread to VMThread
514-
if ((_eetop = env->GetFieldID(thread_class, "eetop", "J")) == NULL) {
515-
// No such field - probably not a HotSpot JVM
516-
env->ExceptionClear();
517-
return;
518-
}
519-
520-
VMThread* vm_thread = VMThread::fromJavaThread(env, thread);
521-
if (vm_thread != NULL) {
522-
_has_native_thread_id = _thread_osthread_offset >= 0 && _osthread_id_offset >= 0;
523-
initTLS(vm_thread);
524-
_env_offset = (intptr_t)env - (intptr_t)vm_thread;
525-
memcpy(_java_thread_vtbl, vm_thread->vtable(), sizeof(_java_thread_vtbl));
526-
}
527-
}
528-
}
529-
530482
// ===== Datadog-specific VMStructs extensions =====
531483

532484
void VMStructs::initUnsafeFunctions() {
@@ -654,16 +606,89 @@ void VMStructs::checkNativeBinding(jvmtiEnv *jvmti, JNIEnv *jni,
654606
jvmti->Deallocate((unsigned char *)method_name);
655607
}
656608

657-
VMThread* VMThread::current() {
658-
return _tls_index >= 0 ? (VMThread*)pthread_getspecific((pthread_key_t)_tls_index) : NULL;
659-
}
609+
void* VMThread::initialize(jthread thread) {
610+
JNIEnv* env = VM::jni();
611+
jclass thread_class = env->FindClass("java/lang/Thread");
612+
if (thread_class == NULL) {
613+
env->ExceptionClear();
614+
return nullptr;
615+
}
660616

661-
int VMThread::nativeThreadId(JNIEnv* jni, jthread thread) {
662-
if (_has_native_thread_id) {
663-
VMThread* vm_thread = fromJavaThread(jni, thread);
664-
return vm_thread != NULL ? vm_thread->osThreadId() : -1;
617+
// Get eetop field - a bridge from Java Thread to VMThread
618+
if ((_eetop = env->GetFieldID(thread_class, "eetop", "J")) == NULL) {
619+
// No such field - probably not a HotSpot JVM
620+
env->ExceptionClear();
621+
return nullptr;
665622
}
666-
return VM::isOpenJ9() ? J9Ext::GetOSThreadID(thread) : -1;
623+
624+
void* vm_thread = fromJavaThread(env, thread);
625+
assert(vm_thread != nullptr);
626+
_has_native_thread_id = _thread_osthread_offset >= 0 && _osthread_id_offset >= 0;
627+
_env_offset = (intptr_t)env - (intptr_t)vm_thread;
628+
memcpy(_java_thread_vtbl, VMThread::cast(vm_thread)->vtable(), sizeof(_java_thread_vtbl));
629+
return vm_thread;
630+
}
631+
632+
static ExecutionMode convertJvmExecutionState(int state) {
633+
switch (state) {
634+
case 4:
635+
case 5:
636+
return ExecutionMode::NATIVE;
637+
case 6:
638+
case 7:
639+
return ExecutionMode::JVM;
640+
case 8:
641+
case 9:
642+
return ExecutionMode::JAVA;
643+
case 10:
644+
case 11:
645+
return ExecutionMode::SAFEPOINT;
646+
default:
647+
return ExecutionMode::UNKNOWN;
648+
}
649+
}
650+
651+
ExecutionMode VMThread::getExecutionMode() {
652+
assert(VM::isHotspot());
653+
654+
VMThread* vm_thread = VMThread::current();
655+
// Not a JVM thread - native thread, e.g. thread launched by JNI code
656+
if (vm_thread == nullptr) {
657+
return ExecutionMode::NATIVE;
658+
}
659+
660+
ProfiledThread *prof_thread = ProfiledThread::currentSignalSafe();
661+
bool is_java_thread = prof_thread != nullptr && prof_thread->isJavaThread();
662+
663+
// A Java thread that JVM tells us via jvmti `ThreadStart()` callback.
664+
if (is_java_thread) {
665+
int raw_thread_state = vm_thread->state();
666+
667+
// Java threads: [4, 12) = [_thread_in_native, _thread_max_state)
668+
// JVM internal threads: 0 or outside this range
669+
is_java_thread = raw_thread_state >= 4 && raw_thread_state < 12;
670+
671+
return is_java_thread ? convertJvmExecutionState(raw_thread_state)
672+
: ExecutionMode::JVM;
673+
} else {
674+
// It is a JVM internal thread, may or may not be a Java thread,
675+
// e.g. Compiler thread or GC thread, etc
676+
return ExecutionMode::JVM;
677+
}
678+
}
679+
680+
OSThreadState VMThread::getOSThreadState() {
681+
VMThread* vm_thread = VMThread::current();
682+
if (vm_thread == nullptr) {
683+
return OSThreadState::UNKNOWN;
684+
}
685+
int raw_thread_state = vm_thread->state();
686+
bool is_java_thread = raw_thread_state >= 4 && raw_thread_state < 12;
687+
OSThreadState state = OSThreadState::UNKNOWN;
688+
if (is_java_thread) {
689+
state = vm_thread->osThreadState();
690+
}
691+
return state;
667692
}
668693

669694
int VMThread::osThreadId() {

ddprof-lib/src/main/cpp/vmStructs.h renamed to ddprof-lib/src/main/cpp/hotspot/vmStructs.h

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
/*
22
* Copyright The async-profiler authors
3-
* Copyright 2026 Datadog, Inc
3+
* Copyright 2025, 2026 Datadog, Inc
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
#ifndef _VMSTRUCTS_H
8-
#define _VMSTRUCTS_H
7+
#ifndef _HOTSPOT_VMSTRUCTS_H
8+
#define _HOTSPOT_VMSTRUCTS_H
99

1010
#include <initializer_list>
11+
#include <jni.h>
1112
#include <jvmti.h>
1213
#include <stdint.h>
1314
#include <type_traits>
1415
#include "codeCache.h"
1516
#include "counters.h"
17+
#include "jvmThread.h"
1618
#include "safeAccess.h"
1719
#include "thread.h"
1820
#include "threadState.h"
@@ -31,10 +33,6 @@ class VMNMethod;
3133
// sending SIGABRT which is uncatchable by crash protection.
3234
// When crash protection is active the assert is redundant — any bad read will
3335
// be caught by the SIGSEGV handler and recovered via longjmp — so we skip it.
34-
//
35-
// Defined at the bottom of this file after VMThread is declared so that the
36-
// VMThread fallback path (isExceptionActive) is accessible without forward-
37-
// declaring the full class.
3836
inline bool crashProtectionActive();
3937

4038
template <typename T>
@@ -352,9 +350,7 @@ class VMStructs {
352350

353351

354352
static jfieldID _eetop;
355-
static jfieldID _tid;
356353
static jfieldID _klass;
357-
static int _tls_index;
358354
static intptr_t _env_offset;
359355
static void* _java_thread_vtbl[6];
360356

@@ -444,10 +440,6 @@ class VMStructs {
444440
return _has_native_thread_id;
445441
}
446442

447-
static bool hasJavaThreadId() {
448-
return _tid != NULL;
449-
}
450-
451443
static bool isInterpretedFrameValidFunc(const void* pc) {
452444
return pc >= _interpreted_frame_valid_start && pc < _interpreted_frame_valid_end;
453445
}
@@ -696,32 +688,24 @@ enum JVMJavaThreadState {
696688
};
697689

698690
DECLARE(VMThread)
691+
friend class JVMThread;
699692
public:
700-
static VMThread* current();
701-
702-
static int key() {
703-
return _tls_index;
704-
}
693+
static void* initialize(jthread thread);
705694

706-
static VMThread* fromJavaThread(JNIEnv* env, jthread thread) {
707-
return VMThread::cast_raw((const void*)env->GetLongField(thread, _eetop));
708-
}
709-
710-
static jlong javaThreadId(JNIEnv* env, jthread thread) {
711-
return env->GetLongField(thread, _tid);
712-
}
713-
714-
static int nativeThreadId(JNIEnv* jni, jthread thread);
695+
static inline VMThread* current();
696+
static inline VMThread* fromJavaThread(JNIEnv* env, jthread thread);
697+
static ExecutionMode getExecutionMode();
698+
static OSThreadState getOSThreadState();
715699

716700
int osThreadId();
717-
718701
JNIEnv* jni();
719702

720703
const void** vtable() {
721704
assert(SafeAccess::isReadable(this));
722705
return *(const void***)this;
723706
}
724707

708+
725709
// This thread is considered a JavaThread if at least 2 of the selected 3 vtable entries
726710
// match those of a known JavaThread (which is either application thread or AttachListener).
727711
// Indexes were carefully chosen to work on OpenJDK 8 to 25, both product an debug builds.
@@ -795,7 +779,7 @@ DECLARE(VMThread)
795779
// Safe because 'this' is the current live thread (we are in its signal handler).
796780
static bool isExceptionActive() {
797781
if (_thread_exception_offset < 0) return false;
798-
VMThread* vt = current();
782+
void* vt = JVMThread::current();
799783
if (vt == nullptr) return false;
800784
return *(const void* const*)((const char*)vt + _thread_exception_offset) != nullptr;
801785
}
@@ -807,6 +791,8 @@ DECLARE(VMThread)
807791
}
808792

809793
inline VMMethod* compiledMethod();
794+
private:
795+
static inline int nativeThreadId(JNIEnv* jni, jthread thread);
810796
DECLARE_END
811797

812798
DECLARE(VMConstMethod)
@@ -1146,7 +1132,7 @@ inline bool crashProtectionActive() {
11461132
// is equally redundant — any bad read will be caught by the SIGSEGV handler.
11471133
// Uses VMThread::isExceptionActive() which reads the field directly without
11481134
// going through at() to avoid recursive assertion.
1149-
return VMThread::key() >= 0 && VMThread::isExceptionActive();
1135+
return JVMThread::key() != pthread_key_t(-1) && VMThread::isExceptionActive();
11501136
}
11511137

1152-
#endif // _VMSTRUCTS_H
1138+
#endif // _HOTSPOT_VMSTRUCTS_H

0 commit comments

Comments
 (0)