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
8 changes: 5 additions & 3 deletions crates/exec-harness/src/walltime/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,13 @@ fn test_with_sleep_command() -> Result<()> {
// Should run exactly 3 times
assert_eq!(times.len(), 3, "Expected exactly 3 iterations");

// Each iteration should take at least 10ms (10_000_000 ns)
// The round clock starts after the child is spawned, so a preempted parent
// can miss the beginning of the sleep. Allow a small shortfall.
const MIN_ROUND_NS: u128 = 9_800_000;
for (i, &time_ns) in times.iter().enumerate() {
assert!(
time_ns >= 10_000_000,
"Iteration {i} took only {time_ns}ns, expected at least 10ms"
time_ns >= MIN_ROUND_NS,
"Iteration {i} took only {time_ns}ns, expected at least {MIN_ROUND_NS}ns"
);
}

Expand Down
2 changes: 1 addition & 1 deletion crates/memtrack/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Project Overview

Attaches uprobes/uretprobes to allocator functions (`malloc`/`free`/`calloc`/`realloc`/`aligned_alloc`/`memalign`) and tracepoints to `mmap`/`munmap`/`brk` + `sched_process_fork` in a target process tree, streams allocation events through a BPF ring buffer to userspace, and writes them to a `MemtrackArtifact` file. Ships a CLI binary `codspeed-memtrack track`.
Attaches uprobes/uretprobes to allocator functions (`malloc`/`free`/`calloc`/`realloc`/`aligned_alloc`/`memalign`) and BTF tracepoints to `sched_process_fork`/`exec`/`exit` in a target process tree, streams allocation events through a BPF ring buffer to userspace, and writes them to a `MemtrackArtifact` file. Ships a CLI binary `codspeed-memtrack track`.

## Architecture & Data Flow

Expand Down
74 changes: 0 additions & 74 deletions crates/memtrack/src/ebpf/c/allocator.h
Comment thread
GuillaumeLagrange marked this conversation as resolved.
Comment thread
GuillaumeLagrange marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -143,78 +143,4 @@ int uretprobe_posix_memalign(struct pt_regs* ctx) {
return submit_aligned_alloc_event(a.size, addr);
}

struct mmap_args {
__u64 addr;
__u64 len;
};

BPF_HASH_MAP(mmap_temp, __u64, struct mmap_args, 10000);

static __always_inline void store_mmap_args(__u64 addr, __u64 len) {
struct task_ids ids = current_task_ids();
__u64 tid = ids.tid;
if (is_tracked(ids.tgid)) {
struct mmap_args args = {.addr = addr, .len = len};
bpf_map_update_elem(&mmap_temp, &tid, &args, BPF_ANY);
}
}

SEC("tracepoint/syscalls/sys_enter_mmap")
int tracepoint_sys_enter_mmap(struct trace_event_raw_sys_enter* ctx) {
store_mmap_args(ctx->args[0], ctx->args[1]);
return 0;
}

SEC("tracepoint/syscalls/sys_exit_mmap")
int tracepoint_sys_exit_mmap(struct trace_event_raw_sys_exit* ctx) {
struct mmap_args* args = (struct mmap_args*)take_param(&mmap_temp);
if (!args) {
return 0;
}

__s64 ret = ctx->ret;
if (ret <= 0) {
return 0;
}

return submit_mmap_event((__u64)ret, args->len, EVENT_TYPE_MMAP);
}

SEC("tracepoint/syscalls/sys_enter_munmap")
int tracepoint_sys_enter_munmap(struct trace_event_raw_sys_enter* ctx) {
__u64 addr = ctx->args[0];
__u64 len = ctx->args[1];

if (addr == 0 || len == 0) {
return 0;
}

return submit_mmap_event(addr, len, EVENT_TYPE_MUNMAP);
}

BPF_HASH_MAP(brk_temp, __u64, __u64, 10000);

SEC("tracepoint/syscalls/sys_enter_brk")
int tracepoint_sys_enter_brk(struct trace_event_raw_sys_enter* ctx) {
store_param(&brk_temp, ctx->args[0]);
return 0;
}

SEC("tracepoint/syscalls/sys_exit_brk")
int tracepoint_sys_exit_brk(struct trace_event_raw_sys_exit* ctx) {
__u64* requested_brk = take_param(&brk_temp);
if (!requested_brk) {
return 0;
}

__u64 new_brk = ctx->ret;
__u64 req_brk = *requested_brk;

if (req_brk == 0 || new_brk <= 0) {
return 0;
}

return submit_mmap_event(new_brk, 0, EVENT_TYPE_BRK);
}

#endif /* __ALLOCATOR_H__ */
19 changes: 5 additions & 14 deletions crates/memtrack/src/ebpf/c/event.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@
#define EVENT_TYPE_CALLOC 3
#define EVENT_TYPE_REALLOC 4
#define EVENT_TYPE_ALIGNED_ALLOC 5
#define EVENT_TYPE_MMAP 6
#define EVENT_TYPE_MUNMAP 7
#define EVENT_TYPE_BRK 8
#define EVENT_TYPE_FORK 9
#define EVENT_TYPE_EXEC 10
#define EVENT_TYPE_EXIT 11
#define EVENT_TYPE_RSS 12
#define EVENT_TYPE_RMAP 13
#define EVENT_TYPE_FORK 6
#define EVENT_TYPE_EXEC 7
#define EVENT_TYPE_EXIT 8
#define EVENT_TYPE_RSS 9
#define EVENT_TYPE_RMAP 10

/* Common header shared by all event types */
struct event_header {
Expand Down Expand Up @@ -45,12 +42,6 @@ struct event {
uint64_t size; /* new size requested */
} realloc;

/* Memory mapping events (mmap, munmap, brk) */
struct {
uint64_t addr; /* address of mapping */
uint64_t size; /* size of mapping */
} mmap;

/* Process lifecycle events (fork carries the parent; exec/exit have no payload) */
struct {
uint32_t parent_pid;
Expand Down
8 changes: 4 additions & 4 deletions crates/memtrack/src/ebpf/c/process_tracking.bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ int BPF_PROG(tracepoint_sched_process_fork, struct task_struct* parent, struct t
SUBMIT_EVENT_AS(child_pid, EVENT_TYPE_FORK, { e->data.fork.parent_pid = parent_pid; });
}

SEC("tracepoint/sched/sched_process_exec")
int tracepoint_sched_process_exec(void* ctx) {
SEC("tp_btf/sched_process_exec")
int BPF_PROG(tracepoint_sched_process_exec) {
__u32 pid = current_tgid();
if (!is_tracked(pid)) {
return 0;
Expand All @@ -58,8 +58,8 @@ int tracepoint_sched_process_exec(void* ctx) {
SUBMIT_EVENT_AS(pid, EVENT_TYPE_EXEC, {});
}

SEC("tracepoint/sched/sched_process_exit")
int tracepoint_sched_process_exit(void* ctx) {
SEC("tp_btf/sched_process_exit")
int BPF_PROG(tracepoint_sched_process_exit) {
__u32 pid = current_tgid();
if (!is_tracked(pid)) {
return 0;
Expand Down
7 changes: 0 additions & 7 deletions crates/memtrack/src/ebpf/c/utils/event_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,4 @@ static __always_inline int submit_realloc_event(__u64 old_addr, __u64 new_addr,
});
}

static __always_inline int submit_mmap_event(__u64 addr, __u64 size, __u8 event_type) {
SUBMIT_GATED_EVENT(event_type, {
e->data.mmap.addr = addr;
e->data.mmap.size = size;
});
}

#endif /* __EVENT_HELPERS_H__ */
18 changes: 0 additions & 18 deletions crates/memtrack/src/ebpf/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,24 +56,6 @@ pub fn parse_event(data: &[u8]) -> Option<MemtrackEvent> {
size: event.data.alloc.size,
},
),
EVENT_TYPE_MMAP => (
event.data.mmap.addr,
MemtrackEventKind::Mmap {
size: event.data.mmap.size,
},
),
EVENT_TYPE_MUNMAP => (
event.data.mmap.addr,
MemtrackEventKind::Munmap {
size: event.data.mmap.size,
},
),
EVENT_TYPE_BRK => (
event.data.mmap.addr,
MemtrackEventKind::Brk {
size: event.data.mmap.size,
},
),
EVENT_TYPE_FORK => (
0,
MemtrackEventKind::Fork {
Expand Down
10 changes: 0 additions & 10 deletions crates/memtrack/src/ebpf/memtrack/tracking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,11 @@ impl MemtrackBpf {
attach_tracepoint!(sched_process_fork);
attach_tracepoint!(sched_process_exec);
attach_tracepoint!(sched_process_exit);
attach_tracepoint!(sys_enter_mmap);
attach_tracepoint!(sys_exit_mmap);
attach_tracepoint!(sys_enter_munmap);
attach_tracepoint!(sys_enter_brk);
attach_tracepoint!(sys_exit_brk);

pub fn attach_tracepoints(&mut self) -> Result<()> {
self.attach_sched_process_fork()?;
self.attach_sched_process_exec()?;
self.attach_sched_process_exit()?;
self.attach_sys_enter_mmap()?;
self.attach_sys_exit_mmap()?;
self.attach_sys_enter_munmap()?;
self.attach_sys_enter_brk()?;
self.attach_sys_exit_brk()?;
if self.physical {
if let Err(e) = self.attach_rss_stat() {
warn!("Failed to attach rss_stat tracepoint, RSS collection disabled: {e:#}");
Expand Down
8 changes: 4 additions & 4 deletions crates/memtrack/tests/c_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,12 @@ fn test_track_allocators_disabled_skips_allocations() -> Result<(), Box<dyn std:
.build(),
)?;

let has_mmap = events
let has_lifecycle = events
.iter()
.any(|e| matches!(e.kind, MemtrackEventKind::Mmap { .. }));
.any(|e| matches!(e.kind, MemtrackEventKind::Exec | MemtrackEventKind::Exit));
assert!(
has_mmap,
"expected at least one Mmap event with allocators disabled"
has_lifecycle,
"expected at least one lifecycle event with allocators disabled"
);

let alloc_events: Vec<_> = events
Expand Down
4 changes: 1 addition & 3 deletions crates/memtrack/tests/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ macro_rules! assert_events_snapshot {
use runner_shared::artifacts::MemtrackEventKind;
use std::mem::discriminant;

// Keep only allocator events. mmap/munmap/brk sizes reflect allocator
// arena reservations that vary per run, so including them here would
// make these snapshots nondeterministic.
// Keep only allocator events.
let formatted_events: Vec<String> = $events
.iter()
.filter(|e| {
Expand Down
31 changes: 8 additions & 23 deletions crates/runner-shared/benches/memtrack_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fn generate_events(n: usize) -> Vec<MemtrackEvent> {
let mut events = Vec::with_capacity(n);
for _ in 0..n {
let size = rng.gen_range(8..8192);
let kind = match rng.gen_range(0..10) {
let kind = match rng.gen_range(0..7) {
0 => MemtrackEventKind::Malloc { size },
1 => MemtrackEventKind::Free,
2 => MemtrackEventKind::Realloc {
Expand All @@ -22,14 +22,11 @@ fn generate_events(n: usize) -> Vec<MemtrackEvent> {
},
3 => MemtrackEventKind::Calloc { size },
4 => MemtrackEventKind::AlignedAlloc { size },
5 => MemtrackEventKind::Mmap { size },
6 => MemtrackEventKind::Munmap { size },
7 => MemtrackEventKind::Brk { size },
8 => MemtrackEventKind::Rss {
5 => MemtrackEventKind::Rss {
member: rng.gen_range(0..4),
size,
},
9 => MemtrackEventKind::Rmap {
6 => MemtrackEventKind::Rmap {
member: rng.gen_range(0..4),
delta: rng.gen_range(-1024..1024),
},
Expand Down Expand Up @@ -67,7 +64,6 @@ fn generate_realistic_events(n: usize) -> Vec<MemtrackEvent> {
let mut rng = StdRng::seed_from_u64(42);
let mut events = Vec::with_capacity(n);
let mut live_heap: Vec<u64> = Vec::new();
let mut live_mmap: Vec<(u64, u64)> = Vec::new();
let mut free_list: Vec<u64> = Vec::new();
let mut next_addr: u64 = 0x5555_5555_0000;
let mut ts: u64 = 1_700_000_000_000_000_000;
Expand All @@ -91,26 +87,15 @@ fn generate_realistic_events(n: usize) -> Vec<MemtrackEvent> {
});
let kind = match rng.gen_range(0..20) {
0 => MemtrackEventKind::Calloc { size },
1 => MemtrackEventKind::Mmap { size },
_ => MemtrackEventKind::Malloc { size },
};
if let MemtrackEventKind::Mmap { size } = kind {
live_mmap.push((addr, size));
} else {
live_heap.push(addr);
}
live_heap.push(addr);
(addr, kind)
} else if roll < 90 {
let idx = rng.gen_range(0..live_heap.len() + live_mmap.len());
if idx < live_heap.len() {
let addr = live_heap.swap_remove(idx);
free_list.push(addr);
(addr, MemtrackEventKind::Free)
} else {
let (addr, size) = live_mmap.swap_remove(idx - live_heap.len());
free_list.push(addr);
(addr, MemtrackEventKind::Munmap { size })
}
let idx = rng.gen_range(0..live_heap.len());
let addr = live_heap.swap_remove(idx);
free_list.push(addr);
(addr, MemtrackEventKind::Free)
} else {
let idx = rng.gen_range(0..live_heap.len());
let old_addr = live_heap[idx];
Expand Down
12 changes: 0 additions & 12 deletions crates/runner-shared/src/artifacts/memtrack/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,6 @@ pub enum MemtrackEventKind {
AlignedAlloc {
Comment thread
GuillaumeLagrange marked this conversation as resolved.
size: u64,
},
Mmap {
size: u64,
},
Munmap {
size: u64,
},
Brk {
size: u64,
},
Fork {
parent_pid: pid_t,
},
Expand Down Expand Up @@ -179,9 +170,6 @@ mod tests {
},
MemtrackEventKind::Calloc { size: 9 },
MemtrackEventKind::AlignedAlloc { size: 9 },
MemtrackEventKind::Mmap { size: 9 },
MemtrackEventKind::Munmap { size: 9 },
MemtrackEventKind::Brk { size: 9 },
];

for kind in kinds {
Expand Down