From 8dc38196df254dc9a5d6a8a8d2caea4900287c4e Mon Sep 17 00:00:00 2001 From: Scott Gerring Date: Fri, 10 Jul 2026 13:59:01 +0200 Subject: [PATCH 1/3] chore(heap-profiling): if a user doesnt opt into live heap, ensure the USDT does not land in the process --- libdd-profiling-heap-sampler/include/datadog/heap/probes.h | 6 ++++++ libdd-profiling-heap-sampler/src/allocation_freed.c | 2 ++ libdd-profiling-heap-sampler/src/probes.c | 4 +++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libdd-profiling-heap-sampler/include/datadog/heap/probes.h b/libdd-profiling-heap-sampler/include/datadog/heap/probes.h index a67066ecc0..e762a46742 100644 --- a/libdd-profiling-heap-sampler/include/datadog/heap/probes.h +++ b/libdd-profiling-heap-sampler/include/datadog/heap/probes.h @@ -31,10 +31,16 @@ */ void dd_probe_alloc(void *user, uint64_t size, uint64_t weight); +#if DD_HEAP_LIVE_TRACKING /* * Emits the `ddheap:free` USDT. * ptr - user-visible pointer being freed + * + * Only available when compiled with live-heap tracking. The absence of + * the `ddheap:free` note in .note.stapsdt signals to external profilers + * that this binary does not support live-heap correlation. */ void dd_probe_free(void *ptr); +#endif #endif diff --git a/libdd-profiling-heap-sampler/src/allocation_freed.c b/libdd-profiling-heap-sampler/src/allocation_freed.c index bf50e83bd3..b1aeebc769 100644 --- a/libdd-profiling-heap-sampler/src/allocation_freed.c +++ b/libdd-profiling-heap-sampler/src/allocation_freed.c @@ -22,7 +22,9 @@ dd_alloc_freed_t dd_allocation_freed_slow(void *ptr, void *raw, size_t size, size_t alignment) { /* Fire with the user-visible pointer, matching what was reported at alloc * time, so the profiler can correlate the two events by address. */ +#if DD_HEAP_LIVE_TRACKING dd_probe_free(ptr); +#endif dd_alloc_freed_t out = { /* Return the raw pointer so the caller passes the real allocation base diff --git a/libdd-profiling-heap-sampler/src/probes.c b/libdd-profiling-heap-sampler/src/probes.c index bdc6362013..e0168b0a9c 100644 --- a/libdd-profiling-heap-sampler/src/probes.c +++ b/libdd-profiling-heap-sampler/src/probes.c @@ -23,6 +23,8 @@ void dd_probe_alloc(void *user, uint64_t size, uint64_t weight) { USDT(ddheap, alloc, user, size, weight); } +#if DD_HEAP_LIVE_TRACKING void dd_probe_free(void *ptr) { USDT(ddheap, free, ptr); -} \ No newline at end of file +} +#endif \ No newline at end of file From 9f3653ea6fde741b3a2cb6544a9c411241939322 Mon Sep 17 00:00:00 2001 From: Scott Gerring Date: Fri, 10 Jul 2026 16:44:08 +0200 Subject: [PATCH 2/3] address clanker review comments --- .../include/datadog/heap/probes.h | 9 ++++----- libdd-profiling-heap-sampler/src/allocation_freed.c | 9 +++++---- libdd-profiling-heap-sampler/src/probes.c | 9 ++++++--- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/libdd-profiling-heap-sampler/include/datadog/heap/probes.h b/libdd-profiling-heap-sampler/include/datadog/heap/probes.h index e762a46742..28913194bc 100644 --- a/libdd-profiling-heap-sampler/include/datadog/heap/probes.h +++ b/libdd-profiling-heap-sampler/include/datadog/heap/probes.h @@ -31,16 +31,15 @@ */ void dd_probe_alloc(void *user, uint64_t size, uint64_t weight); -#if DD_HEAP_LIVE_TRACKING /* * Emits the `ddheap:free` USDT. * ptr - user-visible pointer being freed * - * Only available when compiled with live-heap tracking. The absence of - * the `ddheap:free` note in .note.stapsdt signals to external profilers - * that this binary does not support live-heap correlation. + * The symbol always exists, but the USDT is only emitted when compiled + * with live-heap tracking. The absence of the `ddheap:free` note in + * .note.stapsdt signals to external profilers that this binary does not + * support live-heap correlation. */ void dd_probe_free(void *ptr); -#endif #endif diff --git a/libdd-profiling-heap-sampler/src/allocation_freed.c b/libdd-profiling-heap-sampler/src/allocation_freed.c index b1aeebc769..a4bebbf96e 100644 --- a/libdd-profiling-heap-sampler/src/allocation_freed.c +++ b/libdd-profiling-heap-sampler/src/allocation_freed.c @@ -12,7 +12,8 @@ * dd_sample_flag_check confirmed that ptr carries the sample flag, * meaning this allocation was previously sampled. * - * Fires the ddheap:free USDT with the user-visible pointer, then returns + * Fires the ddheap:free USDT (when live-heap tracking is enabled) with the + * user-visible pointer, then returns * the raw pointer and adjusted size that the caller must forward to the * real deallocator. On x86-64 the size grows by DD_HEADER_BYTES to cover * the header that was reserved at allocation time; on arm64 the size is @@ -21,10 +22,10 @@ dd_alloc_freed_t dd_allocation_freed_slow(void *ptr, void *raw, size_t size, size_t alignment) { /* Fire with the user-visible pointer, matching what was reported at alloc - * time, so the profiler can correlate the two events by address. */ -#if DD_HEAP_LIVE_TRACKING + * time, so the profiler can correlate the two events by address. Safe to + * call unconditionally: dd_probe_free is a no-op USDT-wise when live-heap + * tracking is off. */ dd_probe_free(ptr); -#endif dd_alloc_freed_t out = { /* Return the raw pointer so the caller passes the real allocation base diff --git a/libdd-profiling-heap-sampler/src/probes.c b/libdd-profiling-heap-sampler/src/probes.c index e0168b0a9c..98cac75b80 100644 --- a/libdd-profiling-heap-sampler/src/probes.c +++ b/libdd-profiling-heap-sampler/src/probes.c @@ -18,13 +18,16 @@ */ #include +#include void dd_probe_alloc(void *user, uint64_t size, uint64_t weight) { USDT(ddheap, alloc, user, size, weight); } -#if DD_HEAP_LIVE_TRACKING void dd_probe_free(void *ptr) { +#if DD_HEAP_LIVE_TRACKING USDT(ddheap, free, ptr); -} -#endif \ No newline at end of file +#else + (void)ptr; +#endif +} \ No newline at end of file From 2989b1b52d269b724881846968f282063d862cf3 Mon Sep 17 00:00:00 2001 From: Scott Gerring Date: Mon, 13 Jul 2026 15:47:20 +0200 Subject: [PATCH 3/3] add free path test --- libdd-profiling-heap-sampler/src/lib.rs | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/libdd-profiling-heap-sampler/src/lib.rs b/libdd-profiling-heap-sampler/src/lib.rs index d5380de1ce..1984b636cf 100644 --- a/libdd-profiling-heap-sampler/src/lib.rs +++ b/libdd-profiling-heap-sampler/src/lib.rs @@ -210,6 +210,38 @@ mod tests { } } + // With live-heap tracking compiled out, dd_allocation_freed is a + // straight passthrough: it never inspects the sample-flag header and + // never fires ddheap:free. Verify this even when the underlying + // memory happens to contain the magic pattern that *would* trigger + // the slow path if live-heap were enabled. + #[cfg(not(feature = "live-heap"))] + #[test] + fn freed_is_passthrough_when_live_heap_disabled() { + const MAGIC: u64 = 0xfab1eddec0dedca7; + const HEADER_BYTES: usize = 16; + + // Set up a buffer with the magic header so that, if the flag-check + // were compiled in, it would consider this allocation sampled. + let mut buf = vec![0u8; 128]; + let base = buf.as_mut_ptr() as usize; + let user_addr = base + HEADER_BYTES; + + // Stamp magic at ptr - HEADER_BYTES + buf[0..8].copy_from_slice(&MAGIC.to_ne_bytes()); + // Stamp a plausible offset + buf[8..16].copy_from_slice(&(HEADER_BYTES as u64).to_ne_bytes()); + + let ptr = user_addr as *mut c_void; + unsafe { + let freed = dd_allocation_freed(ptr, 64, 8); + // Without live-heap the pointer is returned unchanged (no + // flag-check, no raw-pointer recovery). + assert_eq!(freed.ptr, ptr); + assert_eq!(freed.size, 64); + } + } + #[test] fn zero_sampling_interval_disables_sampling() { let mut tl = dd_tl_state_t {