Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
55 changes: 31 additions & 24 deletions crates/memtrack/src/ebpf/c/memtrack.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ BPF_HASH_MAP(tracked_pids, __u32, __u8, 10000);
/* Map to store parent-child relationships to detect hierarchy */
BPF_HASH_MAP(pids_ppid, __u32, __u32, 10000);
/* Ring buffer for sending events to userspace */
BPF_RINGBUF(events, 256 * 1024);
BPF_RINGBUF(events, 16 * 1024 * 1024);
/* Map to control whether tracking is enabled (0 = disabled, 1 = enabled) */
BPF_ARRAY_MAP(tracking_enabled, __u8, 1);
/* Counter for events that couldn't be added to the ring buffer */
BPF_ARRAY_MAP(dropped_events, __u64, 1);

/* == Code that tracks process forks and execs == */

Expand Down Expand Up @@ -129,29 +131,34 @@ static __always_inline __u64* take_param(void* map) {
/* Macro to handle common event submission boilerplate
* Usage: SUBMIT_EVENT(event_type, { e->data.foo = bar; })
*/
#define SUBMIT_EVENT(evt_type, fill_data) \
{ \
__u64 tid = bpf_get_current_pid_tgid(); \
__u32 pid = tid >> 32; \
\
if (!is_tracked(pid) || !is_enabled()) { \
return 0; \
} \
\
struct event* e = bpf_ringbuf_reserve(&events, sizeof(*e), 0); \
if (!e) { \
return 0; \
} \
\
e->header.timestamp = bpf_ktime_get_ns(); \
e->header.pid = pid; \
e->header.tid = tid & 0xFFFFFFFF; \
e->header.event_type = evt_type; \
\
fill_data; \
\
bpf_ringbuf_submit(e, 0); \
return 0; \
#define SUBMIT_EVENT(evt_type, fill_data) \
{ \
__u64 tid = bpf_get_current_pid_tgid(); \
__u32 pid = tid >> 32; \
\
if (!is_tracked(pid) || !is_enabled()) { \
return 0; \
} \
\
struct event* e = bpf_ringbuf_reserve(&events, sizeof(*e), 0); \
if (!e) { \
__u32 zero = 0; \
__u64* drops = bpf_map_lookup_elem(&dropped_events, &zero); \
if (drops) { \
__sync_fetch_and_add(drops, 1); \
} \
return 0; \
} \
\
e->header.timestamp = bpf_ktime_get_ns(); \
e->header.pid = pid; \
e->header.tid = tid & 0xFFFFFFFF; \
e->header.event_type = evt_type; \
\
fill_data; \
\
bpf_ringbuf_submit(e, 0); \
return 0; \
}

/* Helper to submit an allocation event (malloc, calloc) */
Expand Down
18 changes: 18 additions & 0 deletions crates/memtrack/src/ebpf/memtrack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,24 @@ impl MemtrackBpf {
Ok(())
}

/// Read the count of events dropped because the ring buffer was full.
pub fn dropped_events_count(&self) -> Result<u64> {
let key = 0u32;
let value = self
.skel
.maps
.dropped_events
.lookup(&key.to_le_bytes(), libbpf_rs::MapFlags::ANY)
.context("Failed to read dropped_events counter")?
.ok_or_else(|| anyhow!("dropped_events slot 0 missing"))?;

let bytes: [u8; 8] = value
.as_slice()
.try_into()
.map_err(|_| anyhow!("dropped_events value has unexpected size"))?;
Ok(u64::from_le_bytes(bytes))
}

/// Disable event tracking
pub fn disable_tracking(&mut self) -> Result<()> {
let key = 0u32;
Expand Down
6 changes: 6 additions & 0 deletions crates/memtrack/src/ebpf/tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,10 @@ impl Tracker {
pub fn disable(&mut self) -> anyhow::Result<()> {
self.bpf.disable_tracking()
}

/// Number of events the kernel dropped because the ring buffer was full.
/// A non-zero value means the resulting trace is incomplete.
pub fn dropped_events_count(&self) -> anyhow::Result<u64> {
self.bpf.dropped_events_count()
}
}
16 changes: 16 additions & 0 deletions crates/memtrack/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,22 @@ fn track_command(
.join()
.map_err(|_| anyhow::anyhow!("Failed to join writer thread"))??;

// Read the eBPF dropped-event counter after the run is complete.
// A non-zero value means the ring buffer overflowed and the trace is
// incomplete.
let dropped_events = tracker_arc
.lock()
.map_err(|_| anyhow!("tracker mutex poisoned"))?
.dropped_events_count()
.context("Failed to read memtrack dropped-event counter")?;
if dropped_events > 0 {
bail!(
"Memtrack ring buffer overflowed: {dropped_events} events lost, aborting since the trace is incomplete.\n\
Try reducing the benchmark's allocation rate (fewer iterations or smaller inputs), \
or report it at https://github.com/CodSpeedHQ/codspeed/issues."
);
}

// IPC thread will exit when channel closes
drop(ipc_handle);

Expand Down
Loading