Skip to content

Commit 171c548

Browse files
authored
Remove jvmti wallclock sampler for none J9 VMs (#419)
1 parent b5abc18 commit 171c548

6 files changed

Lines changed: 16 additions & 133 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ static void (*orig_busHandler)(int signo, siginfo_t *siginfo, void *ucontext);
5454
static Engine noop_engine;
5555
static PerfEvents perf_events;
5656
static WallClockASGCT wall_asgct_engine;
57-
static WallClockJVMTI wall_jvmti_engine;
5857
static J9WallClock j9_engine;
5958
static ITimer itimer;
6059
static CTimer ctimer;
@@ -1224,7 +1223,8 @@ Engine *Profiler::selectWallEngine(Arguments &args) {
12241223
}
12251224
switch (args._wallclock_sampler) {
12261225
case JVMTI:
1227-
return (Engine*)&wall_jvmti_engine;
1226+
fprintf(stderr, "[ddprof] [WARN] JVMTI wallclock is not available on this JVM, fallback to ASGCT wallclock\n");
1227+
[[fallthrough]];
12281228
case ASGCT:
12291229
default:
12301230
return (Engine*)&wall_asgct_engine;

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

Lines changed: 0 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -154,122 +154,6 @@ void WallClockASGCT::initialize(Arguments& args) {
154154
OS::installSignalHandler(SIGVTALRM, sharedSignalHandler);
155155
}
156156

157-
/* This method is extremely racy!
158-
* Thread references, that are returned from JVMTI::GetAllThreads(), only guarantee thread objects
159-
* are not collected by GCs, they don't prevent threads from exiting.
160-
* We have to be extremely careful when accessing thread's data, so it may not be valid.
161-
*/
162-
void WallClockJVMTI::timerLoop() {
163-
// Check for enablement before attaching/dettaching the current thread
164-
if (!isEnabled()) {
165-
return;
166-
}
167-
168-
jvmtiEnv* jvmti = VM::jvmti();
169-
if (jvmti == nullptr) {
170-
return;
171-
}
172-
173-
// Notice:
174-
// We want to cache threads that are captured by collectThread(), so that we can
175-
// clean them up in cleanThreadRefs().
176-
// The approach is not ideal, but it is cleaner than cleaning individual thread
177-
// during filtering phases.
178-
jint threads_count = 0;
179-
jthread* threads_ptr = nullptr;
180-
181-
// Attach to JVM as the first step
182-
VM::attachThread("Datadog Profiler Wallclock Sampler");
183-
auto collectThreads = [&](std::vector<ThreadEntry>& threads) {
184-
jvmtiEnv* jvmti = VM::jvmti();
185-
if (jvmti == nullptr) {
186-
return;
187-
}
188-
189-
if (jvmti->GetAllThreads(&threads_count, &threads_ptr) != JVMTI_ERROR_NONE ||
190-
threads_count == 0) {
191-
return;
192-
}
193-
194-
JNIEnv* jni = VM::jni();
195-
196-
ThreadFilter* threadFilter = Profiler::instance()->threadFilter();
197-
bool do_filter = threadFilter->enabled();
198-
int self = OS::threadId();
199-
200-
// If filtering is enabled, collect the filtered TIDs first
201-
std::vector<int> filtered_tids;
202-
if (do_filter) {
203-
Profiler::instance()->threadFilter()->collect(filtered_tids);
204-
// Sort the TIDs for efficient lookup
205-
std::sort(filtered_tids.begin(), filtered_tids.end());
206-
}
207-
208-
for (int i = 0; i < threads_count; i++) {
209-
jthread thread = threads_ptr[i];
210-
if (thread != nullptr) {
211-
VMThread* nThread = VMThread::fromJavaThread(jni, thread);
212-
if (nThread == nullptr) {
213-
continue;
214-
}
215-
int tid = nThread->osThreadId();
216-
if (tid != self && (!do_filter ||
217-
// Use binary search to efficiently find if tid is in filtered_tids
218-
std::binary_search(filtered_tids.begin(), filtered_tids.end(), tid))) {
219-
threads.push_back({nThread, thread, tid});
220-
}
221-
}
222-
}
223-
};
224-
225-
auto sampleThreads = [&](ThreadEntry& thread_entry, int& num_failures, int& threads_already_exited, int& permission_denied) {
226-
static jint max_stack_depth = (jint)Profiler::instance()->max_stack_depth();
227-
228-
ExecutionEvent event;
229-
VMThread* vm_thread = thread_entry.native;
230-
int raw_thread_state = vm_thread->state();
231-
bool is_initialized = raw_thread_state >= JVMJavaThreadState::_thread_in_native &&
232-
raw_thread_state < JVMJavaThreadState::_thread_max_state;
233-
OSThreadState state = OSThreadState::UNKNOWN;
234-
ExecutionMode mode = ExecutionMode::UNKNOWN;
235-
if (vm_thread == nullptr || !is_initialized) {
236-
return false;
237-
}
238-
OSThreadState os_state = vm_thread->osThreadState();
239-
if (os_state == OSThreadState::TERMINATED) {
240-
return false;
241-
} else if (os_state == OSThreadState::UNKNOWN) {
242-
state = OSThreadState::RUNNABLE;
243-
} else {
244-
state = os_state;
245-
}
246-
mode = getThreadExecutionMode();
247-
248-
event._thread_state = state;
249-
event._execution_mode = mode;
250-
event._weight = 1;
251-
252-
Profiler::instance()->recordJVMTISample(1, thread_entry.tid, thread_entry.java, BCI_WALL, &event, false);
253-
return true;
254-
};
255-
256-
auto cleanThreadRefs = [&]() {
257-
JNIEnv* jni = VM::jni();
258-
for (jint index = 0; index < threads_count; index++) {
259-
jni->DeleteLocalRef(threads_ptr[index]);
260-
}
261-
jvmti->Deallocate((unsigned char*)threads_ptr);
262-
threads_ptr = nullptr;
263-
threads_count = 0;
264-
};
265-
266-
timerLoopCommon<ThreadEntry>(collectThreads, sampleThreads, cleanThreadRefs, _reservoir_size, _interval);
267-
268-
269-
// Don't forget to detach the thread
270-
VM::detachThread();
271-
}
272-
273157
void WallClockASGCT::timerLoop() {
274158
// todo: re-allocating the vector every time is not efficient
275159
auto collectThreads = [&](std::vector<int>& tids) {

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

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -155,19 +155,4 @@ class WallClockASGCT : public BaseWallClock {
155155
}
156156
};
157157

158-
class WallClockJVMTI : public BaseWallClock {
159-
private:
160-
void timerLoop() override;
161-
public:
162-
struct ThreadEntry {
163-
VMThread* native;
164-
jthread java;
165-
int tid;
166-
};
167-
WallClockJVMTI() : BaseWallClock() {}
168-
const char* name() override {
169-
return "WallClock (JVMTI)";
170-
}
171-
};
172-
173158
#endif // _WALLCLOCK_H

ddprof-test/src/test/java/com/datadoghq/profiler/wallclock/BaseContextWallClockTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ void before() {
4747
}
4848

4949
void after() throws InterruptedException {
50+
if (executor == null) {
51+
return;
52+
}
5053
executor.shutdownNow();
5154
boolean terminated = executor.awaitTermination(30, TimeUnit.SECONDS);
5255
if (!terminated) {

ddprof-test/src/test/java/com/datadoghq/profiler/wallclock/JvmtiBasedContextWallClockTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ protected void after() throws InterruptedException {
2020
base.after();
2121
}
2222

23+
@Override
24+
protected boolean isPlatformSupported() {
25+
return Platform.isJ9();
26+
}
27+
2328
@RetryingTest(5)
2429
public void test() throws ExecutionException, InterruptedException {
2530
// thread local handshake available only since Java 15

ddprof-test/src/test/java/com/datadoghq/profiler/wallclock/JvmtiBasedWallClockThreadFilterTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package com.datadoghq.profiler.wallclock;
22

3+
import com.datadoghq.profiler.Platform;
4+
35
public class JvmtiBasedWallClockThreadFilterTest extends WallClockThreadFilterTest {
6+
@Override
7+
protected boolean isPlatformSupported() {
8+
return Platform.isJ9();
9+
}
410

511
@Override
612
protected String getProfilerCommand() {

0 commit comments

Comments
 (0)