diff --git a/libdd-profiling-heap-sampler/include/datadog/heap/probes.h b/libdd-profiling-heap-sampler/include/datadog/heap/probes.h index a67066ecc0..28913194bc 100644 --- a/libdd-profiling-heap-sampler/include/datadog/heap/probes.h +++ b/libdd-profiling-heap-sampler/include/datadog/heap/probes.h @@ -34,6 +34,11 @@ void dd_probe_alloc(void *user, uint64_t size, uint64_t weight); /* * Emits the `ddheap:free` USDT. * ptr - user-visible pointer being freed + * + * 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); diff --git a/libdd-profiling-heap-sampler/src/allocation_freed.c b/libdd-profiling-heap-sampler/src/allocation_freed.c index bf50e83bd3..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,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. */ + * 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); dd_alloc_freed_t out = { 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 { diff --git a/libdd-profiling-heap-sampler/src/probes.c b/libdd-profiling-heap-sampler/src/probes.c index bdc6362013..98cac75b80 100644 --- a/libdd-profiling-heap-sampler/src/probes.c +++ b/libdd-profiling-heap-sampler/src/probes.c @@ -18,11 +18,16 @@ */ #include +#include void dd_probe_alloc(void *user, uint64_t size, uint64_t weight) { USDT(ddheap, alloc, user, size, weight); } void dd_probe_free(void *ptr) { +#if DD_HEAP_LIVE_TRACKING USDT(ddheap, free, ptr); +#else + (void)ptr; +#endif } \ No newline at end of file