Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1118f62
feat(wall): track all threads for unfiltered wall-clock precheck
kaahos Jul 19, 2026
40dbe8e
Merge branch 'main' into paul.fournillon/wallclock-all-threads
kaahos Jul 20, 2026
d82291f
Merge branch 'main' into paul.fournillon/wallclock-all-threads
kaahos Jul 21, 2026
ec76f58
fix: apply review suggestions
kaahos Jul 21, 2026
52def55
Merge branch 'main' into paul.fournillon/wallclock-all-threads
kaahos Jul 21, 2026
9723a5a
Merge branch 'main' into paul.fournillon/wallclock-all-threads
kaahos Jul 22, 2026
e6c6793
fix: apply review
kaahos Jul 23, 2026
51a9178
Merge branch 'main' into paul.fournillon/wallclock-all-threads
kaahos Jul 24, 2026
9e3701e
Merge branch 'main' into paul.fournillon/wallclock-all-threads
kaahos Jul 24, 2026
4954d49
test: scope precheck workloads to all threads
kaahos Jul 16, 2026
80f8049
fix: preserve JVMTI frames in overlapping buffers
kaahos Jul 16, 2026
31c5cfc
test: compare precheck counter delta
kaahos Jul 17, 2026
efb6648
fix: address sphinx review
kaahos Jul 19, 2026
22b8e00
fix(wall): register unfiltered threads from lifecycle callbacks
kaahos Jul 24, 2026
4174130
feat(taskblock): capture stacks synchronously at block exit
kaahos Jul 15, 2026
571aaf1
fix: restrict block suppression to all-thread scope
kaahos Jul 16, 2026
ecd91b8
fix: preserve JVMTI frames in overlapping buffers
kaahos Jul 16, 2026
e495408
fix: address sphinx review
kaahos Jul 19, 2026
14fccf9
fix(taskblock): preserve unowned wall fallback outside owned blocks
kaahos Jul 24, 2026
114be60
fix: fix emission problems
kaahos Jul 27, 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
15 changes: 13 additions & 2 deletions ddprof-lib/src/main/cpp/counters.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023 Datadog, Inc
* Copyright 2023, 2026 Datadog, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -60,6 +60,8 @@
X(THREAD_NAMES_COUNT, "thread_names_count") \
X(THREAD_FILTER_PAGES, "thread_filter_pages") \
X(THREAD_FILTER_BYTES, "thread_filter_bytes") \
X(THREAD_REGISTRY_CAPACITY_EXHAUSTED, "thread_registry_capacity_exhausted") \
X(THREAD_REGISTRY_INDEX_FAILURES, "thread_registry_index_failures") \
X(JMETHODID_SKIPPED, "jmethodid_skipped_count") \
X(CODECACHE_NATIVE_SIZE_BYTES, "codecache_native_size_bytes") \
X(CODECACHE_NATIVE_COUNT, "native_codecache_count") \
Expand All @@ -69,10 +71,19 @@
X(AGCT_NATIVE_NO_JAVA_CONTEXT, "agct_native_no_java_context") \
X(AGCT_BLOCKED_IN_VM, "agct_blocked_in_vm") \
X(SKIPPED_WALLCLOCK_UNWINDS, "skipped_wallclock_unwinds") \
X(WC_SIGNAL_SUPPRESSED_SAMPLED_RUN, "wc_signals_suppressed_sampled_run") \
X(WC_PRECHECK_REGISTRY_LOOKUPS, "wc_precheck_registry_lookups") \
X(WC_PRECHECK_CANDIDATES_REJECTED, "wc_precheck_candidates_rejected") \
X(WC_PRECHECK_LOOKUP_BUDGET_EXHAUSTED, "wc_precheck_lookup_budget_exhausted") \
X(WC_SIGNAL_SUPPRESSED_OWNED_BLOCK, "wc_signals_suppressed_owned_block") \
X(WC_UNOWNED_BLOCKED_SUPPRESSED, "wc_unowned_blocked_suppressed") \
X(WC_UNOWNED_BLOCKED_RECORDED, "wc_unowned_blocked_recorded") \
X(WC_SIGNAL_QUEUE_FULL, "wc_signals_queue_full") \
X(TASK_BLOCK_EMITTED, "task_block_emitted") \
X(TASK_BLOCK_SKIPPED_TRACE_CONTEXT, "task_block_skipped_trace_context") \
X(TASK_BLOCK_SKIPPED_TOO_SHORT, "task_block_skipped_too_short") \
X(TASK_BLOCK_STACK_CAPTURE_FAILED, "task_block_stack_capture_failed") \
X(TASK_BLOCK_RECORD_FAILED, "task_block_record_failed") \
X(TASK_BLOCK_DROPPED_ROTATION, "task_block_dropped_rotation") \
X(UNWINDING_TIME_ASYNC, "unwinding_ticks_async") \
X(UNWINDING_TIME_JVMTI, "unwinding_ticks_jvmti") \
X(CALLTRACE_STORAGE_DROPPED, "calltrace_storage_dropped_traces") \
Expand Down
4 changes: 4 additions & 0 deletions ddprof-lib/src/main/cpp/engine.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright 2017 Andrei Pangin
* Copyright 2026, Datadog, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -51,6 +52,9 @@ class Engine {
virtual void stop();
virtual long interval() const { return 0L; }

// Whether empty-filter wall prechecks can consume ThreadFilter registry state.
virtual bool supportsUnfilteredWallPrecheck() const { return false; }

virtual int registerThread(int tid) { return -1; }
virtual void unregisterThread(int tid) {}

Expand Down
22 changes: 16 additions & 6 deletions ddprof-lib/src/main/cpp/event.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class ExecutionEvent : public Event {
OSThreadState _thread_state;
ExecutionMode _execution_mode;
u64 _weight;
u32 _call_trace_id;
u64 _call_trace_id;

ExecutionEvent()
: Event(), _thread_state(OSThreadState::RUNNABLE), _execution_mode(ExecutionMode::UNKNOWN),
Expand Down Expand Up @@ -123,13 +123,13 @@ class WallClockEpochEvent {
u32 _num_failed_samples;
u32 _num_exited_threads;
u32 _num_permission_denied;
u64 _num_suppressed_sampled_run;
u64 _num_suppressed_owned_block;

WallClockEpochEvent(u64 start_time)
: _dirty(false), _start_time(start_time), _duration_millis(0),
_num_samplable_threads(0), _num_successful_samples(0),
_num_failed_samples(0), _num_exited_threads(0),
_num_permission_denied(0), _num_suppressed_sampled_run(0) {}
_num_permission_denied(0), _num_suppressed_owned_block(0) {}

bool hasChanged() { return _dirty; }

Expand Down Expand Up @@ -168,10 +168,10 @@ class WallClockEpochEvent {
}
}

void addNumSuppressedSampledRun(u64 n) {
void addNumSuppressedOwnedBlock(u64 n) {
if (n > 0) {
_dirty = true;
_num_suppressed_sampled_run += n;
_num_suppressed_owned_block += n;
}
}

Expand All @@ -182,7 +182,7 @@ class WallClockEpochEvent {
void newEpoch(u64 start_time) {
_dirty = false;
_start_time = start_time;
_num_suppressed_sampled_run = 0;
_num_suppressed_owned_block = 0;
}
};

Expand All @@ -207,4 +207,14 @@ typedef struct QueueTimeEvent {
u32 _queueLength;
} QueueTimeEvent;

typedef struct TaskBlockEvent {
u64 _start;
u64 _end;
u64 _blocker;
u64 _unblockingSpanId;
Context _ctx;
u64 _callTraceId;
OSThreadState _observedBlockingState;
} TaskBlockEvent;

#endif // _EVENT_H
32 changes: 31 additions & 1 deletion ddprof-lib/src/main/cpp/flightRecorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1945,6 +1945,21 @@ void Recording::recordMethodSample(Buffer *buf, int tid, u64 call_trace_id,
flushIfNeeded(buf);
}

void Recording::recordTaskBlock(Buffer *buf, int tid, TaskBlockEvent *event) {
int start = buf->skip(1);
buf->putVar64(T_TASK_BLOCK);
buf->putVar64(event->_start);
buf->putVar64(event->_end - event->_start);
buf->putVar64(tid);
buf->putVar64(event->_blocker);
buf->putVar64(event->_unblockingSpanId);
buf->putVar64(event->_callTraceId);
buf->put8(static_cast<int>(event->_observedBlockingState));
writeContextSnapshot(buf, event->_ctx);
writeEventSizePrefix(buf, start);
flushIfNeeded(buf);
}

void Recording::recordWallClockEpoch(Buffer *buf, WallClockEpochEvent *event) {
int start = buf->skip(1);
buf->putVar64(T_WALLCLOCK_SAMPLE_EPOCH);
Expand All @@ -1955,7 +1970,7 @@ void Recording::recordWallClockEpoch(Buffer *buf, WallClockEpochEvent *event) {
buf->putVar64(event->_num_failed_samples);
buf->putVar64(event->_num_exited_threads);
buf->putVar64(event->_num_permission_denied);
buf->putVar64(event->_num_suppressed_sampled_run);
buf->putVar64(event->_num_suppressed_owned_block);
writeEventSizePrefix(buf, start);
flushIfNeeded(buf);
}
Expand Down Expand Up @@ -2218,6 +2233,21 @@ void FlightRecorder::recordQueueTime(int lock_index, int tid,
}
}

bool FlightRecorder::recordTaskBlock(int lock_index, int tid,
TaskBlockEvent *event) {
OptionalSharedLockGuard locker(&_rec_lock);
if (locker.ownsLock()) {
Recording* rec = _rec;
if (rec != nullptr) {
Buffer *buf = rec->buffer(lock_index);
rec->addThread(lock_index, tid);
rec->recordTaskBlock(buf, tid, event);
return true;
}
}
return false;
}

void FlightRecorder::recordDatadogSetting(int lock_index, int length,
const char *name, const char *value,
const char *unit) {
Expand Down
2 changes: 2 additions & 0 deletions ddprof-lib/src/main/cpp/flightRecorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ class Recording {
void recordWallClockEpoch(Buffer *buf, WallClockEpochEvent *event);
void recordTraceRoot(Buffer *buf, int tid, TraceRootEvent *event);
void recordQueueTime(Buffer *buf, int tid, QueueTimeEvent *event);
void recordTaskBlock(Buffer *buf, int tid, TaskBlockEvent *event);
void recordAllocation(RecordingBuffer *buf, int tid, u64 call_trace_id,
AllocEvent *event);
void recordMallocSample(Buffer *buf, int tid, u64 call_trace_id,
Expand Down Expand Up @@ -427,6 +428,7 @@ class FlightRecorder {
void wallClockEpoch(int lock_index, WallClockEpochEvent *event);
void recordTraceRoot(int lock_index, int tid, TraceRootEvent *event);
void recordQueueTime(int lock_index, int tid, QueueTimeEvent *event);
bool recordTaskBlock(int lock_index, int tid, TaskBlockEvent *event);

bool active() const { return _rec != NULL; }

Expand Down
30 changes: 30 additions & 0 deletions ddprof-lib/src/main/cpp/frames.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,39 @@
/*
* Copyright 2026, Datadog, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _FRAMES_H
#define _FRAMES_H

#include <jni.h>
#include <jvmti.h>
#include "vmEntry.h"

inline void copyJvmtiFrames(ASGCT_CallFrame *frames,
const jvmtiFrameInfo *jvmti_frames,
jint num_frames) {
// The source and destination commonly refer to the two views of the same
// CallTraceBuffer union. Read both source fields before either write.
for (jint i = 0; i < num_frames; ++i) {
jmethodID method = jvmti_frames[i].method;
jlocation location = jvmti_frames[i].location;
frames[i].method_id = method;
frames[i].bci = static_cast<jint>(location);
LP64_ONLY(frames[i].padding = 0;)
}
}

inline int makeFrame(ASGCT_CallFrame *frames, jint type, jmethodID id) {
frames[0].bci = type;
frames[0].method_id = id;
Expand Down
Loading
Loading