Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
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
Prev Previous commit
Next Next commit
Safe access
  • Loading branch information
zhengyu123 committed Jul 2, 2025
commit 0eda2ccad1e508c2481b4859163b0008585b6505
52 changes: 49 additions & 3 deletions ddprof-lib/src/main/cpp/vmStructs_dd.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,19 @@ namespace ddprof {
inline static int thread_osthread_offset() {
return _thread_osthread_offset;
}

inline static int osthread_state_offset() {
return _osthread_state_offset;
}

inline static int osthread_id_offset() {
return _osthread_id_offset;
}

inline static int thread_state_offset() {
return _thread_state_offset;
}

inline static int flag_type_offset() {
return _flag_type_offset;
}
Expand Down Expand Up @@ -145,11 +155,47 @@ namespace ddprof {
if (ddprof::VMStructs::thread_osthread_offset() >= 0 && ddprof::VMStructs::osthread_state_offset() >= 0) {
u32 *osthread = *(u32 **)at(ddprof::VMStructs::thread_osthread_offset());
if (osthread != nullptr) {
return static_cast<OSThreadState>(
SafeAccess::load32(osthread, static_cast<u32>(OSThreadState::TERMINATED)));
// If the location is accessible, the thread must have been terminated
Comment thread
zhengyu123 marked this conversation as resolved.
Outdated
u32 value = SafeAccess::load32(osthread + ddprof::VMStructs::osthread_state_offset(),
static_cast<u32>(OSThreadState::TERMINATED));
// Bad value, treat it as terminated
if (value > static_cast<u32>(OSThreadState::SYSCALL)) {
return OSThreadState::TERMINATED;
}
return static_cast<OSThreadState>(value);
}
}
return OSThreadState::UNKNOWN;
}

int osThreadIdSafe() {
if (ddprof::VMStructs::thread_osthread_offset() >= 0 && ddprof::VMStructs::osthread_id_offset() >=0) {
u32* osthread = *(u32**) at(ddprof::VMStructs::thread_osthread_offset());
if (osthread == nullptr) {
return -1;
} else {
u32 value = SafeAccess::load32(osthread + ddprof::VMStructs::osthread_id_offset(), -1);
return static_cast<int>(value);
}
}
return OSThreadState::UNKNOWN;
return -1;
}

int stateSafe() {
int offset = ddprof::VMStructs::thread_state_offset();
if (offset >= 0) {
u32* state = *(u32**)at(offset);
if (state == nullptr) {
return 0;
} else {
u32 value = SafeAccess::load32(state, 0);
if (value > static_cast<u32>(_thread_max_state)) {
value = 0;
}
return static_cast<int>(value);
}
}
return 0;
}
};

Expand Down
27 changes: 22 additions & 5 deletions ddprof-lib/src/main/cpp/wallClock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@
OS::installSignalHandler(SIGVTALRM, sharedSignalHandler);
}

static long eventCount = 0;

/* This method is extremely racy!
* Thread references that are returned from JVMTI GetAllThreads(), only guarantee thread objects
* not to be collected by GC, they don't prevent threads from exiting.
Expand Down Expand Up @@ -186,7 +188,8 @@
if (nThread == nullptr) {
continue;
}
int tid = nThread->osThreadId();
// Racy, use safe version
int tid = nThread->osThreadIdSafe();
if (tid != self && (!do_filter || Profiler::instance()->threadFilter()->accept(tid))) {
threads.push_back({nThread, thread, tid});
}
Expand All @@ -197,19 +200,30 @@

auto sampleThreads = [&](ThreadEntry& thread_entry, int& num_failures, int& threads_already_exited, int& permission_denied) {
static jint max_stack_depth = (jint)Profiler::instance()->max_stack_depth();
jvmtiEnv* jvmti = VM::jvmti();
jint thread_state;
// Reliable test
if (jvmti->GetThreadState(thread_entry.java, &thread_state) != JVMTI_ERROR_NONE ||
(thread_state & JVMTI_THREAD_STATE_ALIVE) == 0) {
printf("Thread no longer alive\n");
return false;
}

// Following code is racy, use safe version to access native structure.
ExecutionEvent event;
ddprof::VMThread* vm_thread = thread_entry.native;
int raw_thread_state = vm_thread->state();
int raw_thread_state = vm_thread->stateSafe();
bool is_initialized = raw_thread_state >= ddprof::JVMJavaThreadState::_thread_in_native &&
raw_thread_state < ddprof::JVMJavaThreadState::_thread_max_state;
OSThreadState state = OSThreadState::UNKNOWN;
ExecutionMode mode = ExecutionMode::UNKNOWN;
if (vm_thread == nullptr || !is_initialized) {
printf("Thread not initialized\n");
return false;
}
OSThreadState os_state = vm_thread->osThreadState();
OSThreadState os_state = vm_thread->osThreadStateSafe();
if (state == OSThreadState::TERMINATED) {
printf("Thread terminated\n");
return false;
} else if (state == OSThreadState::UNKNOWN) {
state = OSThreadState::RUNNABLE;
Expand All @@ -223,16 +237,19 @@
event._weight = 1;

Profiler::instance()->recordJVMTISample(1, thread_entry.tid, thread_entry.java, BCI_WALL, &event, false);
eventCount ++;
return true;
};

auto cleanThreads = [](ThreadEntry& thread_entry) {
auto clearThreadRefs = [](ThreadEntry& thread_entry) {
Comment thread
zhengyu123 marked this conversation as resolved.
Outdated
VM::jni()->DeleteLocalRef(thread_entry.java);
Comment thread
zhengyu123 marked this conversation as resolved.
Outdated
};

timerLoopCommon<ThreadEntry>(collectThreads, sampleThreads, cleanThreads, _reservoir_size, _interval);
eventCount = 0;
timerLoopCommon<ThreadEntry>(collectThreads, sampleThreads, clearThreadRefs, _reservoir_size, _interval);
// Don't forget to detach the thread
VM::detachThread();
printf("Event generated: %d\n", eventCount);
Comment thread Fixed
}

void WallClockASGCT::timerLoop() {
Expand Down
Loading