Skip to content

Commit 8e22077

Browse files
committed
v1
1 parent 517f247 commit 8e22077

18 files changed

Lines changed: 733 additions & 541 deletions

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "threadState.h"
2828
#include "tsc.h"
2929
#include "vmStructs.h"
30+
#include "wallclock/wallClock.h"
3031
#include <arpa/inet.h>
3132
#include <cxxabi.h>
3233
#include <errno.h>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
#include "tsc.h"
3030
#include "vmEntry.h"
3131
#include "vmStructs.h"
32-
#include "wallClock.h"
3332
#include <errno.h>
3433
#include <fstream>
3534
#include <sstream>
@@ -154,7 +153,7 @@ JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0(jlong java_tid
154153
if (unlikely(slot_id == -1)) {
155154
return; // Failed to register thread
156155
}
157-
thread_filter->add(java_tid, slot_id);
156+
thread_filter->add(java_tid, current->tid(), slot_id);
158157
}
159158

160159
extern "C" DLLEXPORT void JNICALL
@@ -173,6 +172,7 @@ JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadRemove0() {
173172
// Thread doesn't have a slot ID yet - nothing to remove
174173
return;
175174
}
175+
current->setFilterSlotId(-1);
176176
thread_filter->remove(slot_id);
177177
}
178178

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include "flightRecorder.h"
1616
#include "itimer.h"
1717
#include "j9Ext.h"
18-
#include "j9WallClock.h"
18+
#include "wallclock/j9WallClock.h"
1919
#include "libraryPatcher.h"
2020
#include "objectSampler.h"
2121
#include "os.h"
@@ -27,7 +27,8 @@
2727
#include "thread.h"
2828
#include "tsc.h"
2929
#include "vmStructs.h"
30-
#include "wallClock.h"
30+
#include "wallclock/wallClockASGCT.h"
31+
#include "wallclock/wallClockJVMTI.h"
3132
#include <algorithm>
3233
#include <dlfcn.h>
3334
#include <fstream>
@@ -1187,10 +1188,10 @@ Engine *Profiler::selectCpuEngine(Arguments &args) {
11871188
}
11881189
}
11891190

1190-
Engine *Profiler::selectWallEngine(Arguments &args) {
1191+
BaseWallClock *Profiler::selectWallEngine(Arguments &args) {
11911192
if (args._wall < 0 &&
11921193
(args._event == NULL || strcmp(args._event, EVENT_WALL) != 0)) {
1193-
return &noop_engine;
1194+
return nullptr;
11941195
}
11951196
if (VM::isOpenJ9()) {
11961197
if (args._wallclock_sampler == JVMTI || !J9Ext::shouldUseAsgct() || !J9Ext::can_use_ASGCT()) {
@@ -1201,18 +1202,18 @@ Engine *Profiler::selectWallEngine(Arguments &args) {
12011202
}
12021203
j9_engine.sampleIdleThreads();
12031204
TEST_LOG("J9[wall]=jvmti");
1204-
return (Engine *)&j9_engine;
1205+
return (BaseWallClock *)&j9_engine;
12051206
} else {
12061207
TEST_LOG("J9[wall]=asgct");
1207-
return (Engine *)&wall_asgct_engine;
1208+
return &wall_asgct_engine;
12081209
}
12091210
}
12101211
switch (args._wallclock_sampler) {
12111212
case JVMTI:
1212-
return (Engine*)&wall_jvmti_engine;
1213+
return &wall_jvmti_engine;
12131214
case ASGCT:
12141215
default:
1215-
return (Engine*)&wall_asgct_engine;
1216+
return &wall_asgct_engine;
12161217
}
12171218
}
12181219

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class FrameName;
5555
class VMNMethod;
5656
class StackContext;
5757
class VM;
58+
class BaseWallClock;
5859

5960
enum State { NEW, IDLE, RUNNING, TERMINATED };
6061

@@ -124,7 +125,7 @@ class alignas(alignof(SpinLock)) Profiler {
124125
CallTraceStorage _call_trace_storage;
125126
FlightRecorder _jfr;
126127
Engine *_cpu_engine;
127-
Engine *_wall_engine = NULL;
128+
BaseWallClock *_wall_engine = NULL;
128129
Engine *_alloc_engine;
129130
int _event_mask;
130131

@@ -186,7 +187,7 @@ class alignas(alignof(SpinLock)) Profiler {
186187
void mangle(const char *name, char *buf, size_t size);
187188

188189
Engine *selectCpuEngine(Arguments &args);
189-
Engine *selectWallEngine(Arguments &args);
190+
BaseWallClock *selectWallEngine(Arguments &args);
190191
Engine *selectAllocEngine(Arguments &args);
191192
Error checkJvmCapabilities();
192193

@@ -240,7 +241,7 @@ class alignas(alignof(SpinLock)) Profiler {
240241
int max_stack_depth() { return _max_stack_depth; }
241242
time_t uptime() { return time(NULL) - _start_time; }
242243
Engine *cpuEngine() { return _cpu_engine; }
243-
Engine *wallEngine() { return _wall_engine; }
244+
BaseWallClock *wallEngine() const { return _wall_engine; }
244245

245246
Dictionary *classMap() { return &_class_map; }
246247
Dictionary *stringLabelMap() { return &_string_label_map; }

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

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "threadFilter.h"
2323
#include "arch.h"
2424
#include "os.h"
25+
#include "profiler.h"
26+
#include "wallclock/wallClock.h"
2527
#include <cassert>
2628
#include <cstdlib>
2729
#include <cstdio>
@@ -77,7 +79,7 @@ void ThreadFilter::initializeChunk(int chunk_idx) {
7779
// Allocate and initialize new chunk completely before swapping
7880
ChunkStorage* new_chunk = new ChunkStorage();
7981
for (auto& slot : new_chunk->slots) {
80-
slot.value.store(-1, std::memory_order_relaxed);
82+
slot.value.java_tid.store(-1, std::memory_order_relaxed);
8183
}
8284

8385
// Try to install it atomically
@@ -145,6 +147,11 @@ void ThreadFilter::initFreeList() {
145147
}
146148
}
147149

150+
bool ThreadFilter::filter_by_os_tid() {
151+
BaseWallClock* wall_clock = Profiler::instance()->wallEngine();
152+
return wall_clock == nullptr || wall_clock->mode() == BaseWallClock::Mode::ASGCT;
153+
}
154+
148155
bool ThreadFilter::accept(SlotID slot_id) const {
149156
// Fast path: if disabled, accept everything (relaxed to avoid fences on hot path)
150157
if (unlikely(!_enabled.load(std::memory_order_relaxed))) {
@@ -158,12 +165,16 @@ bool ThreadFilter::accept(SlotID slot_id) const {
158165
// This is not a fast path like the add operation.
159166
ChunkStorage* chunk = _chunks[chunk_idx].load(std::memory_order_acquire);
160167
if (likely(chunk != nullptr)) {
161-
return chunk->slots[slot_idx].value.load(std::memory_order_relaxed) != -1;
168+
if (filter_by_os_tid()) {
169+
return chunk->slots[slot_idx].value.os_tid.load(std::memory_order_relaxed) != -1;
170+
} else {
171+
return chunk->slots[slot_idx].value.java_tid.load(std::memory_order_relaxed) != -1;
172+
}
162173
}
163174
return false;
164175
}
165176

166-
void ThreadFilter::add(long tid, SlotID slot_id) {
177+
void ThreadFilter::add(long java_tid, int os_tid, SlotID slot_id) {
167178
// PRECONDITION: slot_id must be from registerThread() or negative
168179
// Undefined behavior for invalid positive slot_ids (performance optimization)
169180
if (slot_id < 0) return;
@@ -174,7 +185,11 @@ void ThreadFilter::add(long tid, SlotID slot_id) {
174185
// Fast path: assume valid slot_id from registerThread()
175186
ChunkStorage* chunk = _chunks[chunk_idx].load(std::memory_order_acquire);
176187
if (likely(chunk != nullptr)) {
177-
chunk->slots[slot_idx].value.store(tid, std::memory_order_release);
188+
if (filter_by_os_tid()) {
189+
chunk->slots[slot_idx].value.os_tid.store(os_tid, std::memory_order_release);
190+
} else {
191+
chunk->slots[slot_idx].value.java_tid.store(java_tid, std::memory_order_release);
192+
}
178193
}
179194
}
180195

@@ -195,8 +210,11 @@ void ThreadFilter::remove(SlotID slot_id) {
195210
if (unlikely(chunk == nullptr)) {
196211
return;
197212
}
198-
199-
chunk->slots[slot_idx].value.store(-1, std::memory_order_release);
213+
if (filter_by_os_tid()) {
214+
chunk->slots[slot_idx].value.java_tid.store(-1, std::memory_order_release);
215+
} else {
216+
chunk->slots[slot_idx].value.os_tid.store(-1, std::memory_order_release);
217+
}
200218
}
201219

202220
void ThreadFilter::unregisterThread(SlotID slot_id) {
@@ -255,12 +273,13 @@ ThreadFilter::SlotID ThreadFilter::popFromFreeList() {
255273
return -1; // Empty list
256274
}
257275

258-
void ThreadFilter::collect(std::vector<int>& tids) const {
259-
tids.clear();
276+
void ThreadFilter::collect_java_tids(std::vector<long>& java_tids) const {
277+
assert(!filter_by_os_tid());
278+
java_tids.clear();
260279

261280
// Reserve space for efficiency
262281
// The eventual resize is not the bottleneck, so we reserve a reasonable size
263-
tids.reserve(512);
282+
java_tids.reserve(512);
264283

265284
// Scan only initialized chunks
266285
int num_chunks = _num_chunks.load(std::memory_order_relaxed);
@@ -271,16 +290,36 @@ void ThreadFilter::collect(std::vector<int>& tids) const {
271290
}
272291

273292
for (const auto& slot : chunk->slots) {
274-
int slot_tid = slot.value.load(std::memory_order_relaxed);
275-
if (slot_tid != -1) {
276-
tids.push_back(slot_tid);
293+
long java_tid = slot.value.java_tid.load(std::memory_order_relaxed);
294+
if (java_tid != -1) {
295+
java_tids.push_back(java_tid);
277296
}
278297
}
279298
}
299+
}
300+
301+
void ThreadFilter::collect_os_tids(std::vector<int>& os_tids) const {
302+
assert(!filter_by_os_tid());
303+
os_tids.clear();
304+
305+
// Reserve space for efficiency
306+
// The eventual resize is not the bottleneck, so we reserve a reasonable size
307+
os_tids.reserve(512);
280308

281-
// Optional: shrink if we over-reserved significantly
282-
if (tids.capacity() > tids.size() * 2) {
283-
tids.shrink_to_fit();
309+
// Scan only initialized chunks
310+
int num_chunks = _num_chunks.load(std::memory_order_relaxed);
311+
for (int chunk_idx = 0; chunk_idx < num_chunks; ++chunk_idx) {
312+
ChunkStorage* chunk = _chunks[chunk_idx].load(std::memory_order_acquire);
313+
if (chunk == nullptr) {
314+
continue; // Skip unallocated chunks
315+
}
316+
317+
for (const auto& slot : chunk->slots) {
318+
int os_tid = slot.value.os_tid.load(std::memory_order_relaxed);
319+
if (os_tid != -1) {
320+
os_tids.push_back(os_tid);
321+
}
322+
}
284323
}
285324
}
286325

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,25 @@ class ThreadFilter {
4545
bool enabled() const;
4646
// Hot path methods - slot_id MUST be from registerThread(), undefined behavior otherwise
4747
bool accept(SlotID slot_id) const;
48-
void add(long tid, SlotID slot_id);
48+
void add(long java_tid, int os_tid, SlotID slot_id);
4949
void remove(SlotID slot_id);
50-
void collect(std::vector<int>& tids) const;
50+
51+
void collect_java_tids(std::vector<long>& java_tids) const;
52+
void collect_os_tids(std::vector<int>& os_tids) const;
5153

5254
SlotID registerThread();
5355
void unregisterThread(SlotID slot_id);
5456

5557
private:
58+
// Use os tid for filtering vs java tid
59+
static bool filter_by_os_tid();
60+
5661
// Optimized slot structure with padding to avoid false sharing
5762
struct alignas(DEFAULT_CACHE_LINE_SIZE) Slot {
58-
std::atomic<long> value{-1};
63+
union {
64+
std::atomic<long> java_tid;
65+
std::atomic<int> os_tid;
66+
} value;
5967
char padding[DEFAULT_CACHE_LINE_SIZE - sizeof(value)]; // Pad to cache line size
6068
};
6169
static_assert(sizeof(Slot) == DEFAULT_CACHE_LINE_SIZE, "Slot must be exactly one cache line");

0 commit comments

Comments
 (0)