Skip to content

feat(memtrack): attach allocator probes on demand#445

Open
not-matthias wants to merge 5 commits into
mainfrom
cod-1801-only-attach-to-used-libraries-in-memtrack
Open

feat(memtrack): attach allocator probes on demand#445
not-matthias wants to merge 5 commits into
mainfrom
cod-1801-only-attach-to-used-libraries-in-memtrack

Conversation

@not-matthias

Copy link
Copy Markdown
Member

Attach allocator uprobes only to libraries the tracked process tree actually maps, instead of pre-attaching every allocator discovered on the system at startup.

A BPF fentry on security_mmap_file signals the first mapping of each unknown executable inode. A background worker SIGSTOPs the mapping processes, classifies the file by its allocator symbols, attaches probes, and resumes them. This:

  • covers dlopen'd allocators and statically linked allocators in spawned children (previously missed entirely)
  • skips libraries the benchmark never loads
  • removes the startup discovery scan (system-wide library glob + build-dir walk), which was slow on nix-heavy systems

The eager discovery path and CODSPEED_MEMTRACK_BINARIES are removed since the watcher supersedes them (breaking: the env var is no longer read).

The crate API is now Tracker (owns the attach worker) and Session (owns the spawned child and its event pipeline); ring buffers are polled through a generic RingBufferPoller with caller-supplied parsing, replacing the keepalive forwarding thread.

Review focus: the stop-the-world attach in attach_worker.rs (fixpoint stop + synchronous ring buffer drain) — its correctness argument is that a full consume() after all producers are stopped cannot miss requests.

Fixes COD-1801

@codspeed-hq

codspeed-hq Bot commented Jul 8, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

✅ 17 untouched benchmarks


Comparing cod-1801-only-attach-to-used-libraries-in-memtrack (4d3fbb0) with main (5c27231)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces the eager startup-discovery scan (system-wide library glob + build-dir walk + CODSPEED_MEMTRACK_BINARIES) with an on-demand attach mechanism: a BPF fentry on security_mmap_file fires for every executable mapping by a tracked process, stops the process via bpf_send_signal(SIGSTOP), and queues an AttachRequest; a background AttachWorker classifies the file, attaches uprobes, and resumes the process.

  • New BPF watcher (attach.bpf.h): fentry on security_mmap_file with a known_inodes hash-map dedup, a 128 KB request ring buffer, and an overflow counter checked at shutdown.
  • Attach worker (attach_worker.rs): fixpoint stop-the-world loop — stops every producing pid, synchronously drains the ring buffer, processes new pids until stable — then attaches probes and resumes all stopped processes via ContGuard drop guards.
  • API restructure: Tracker now owns the AttachWorker and exposes spawn/finish; Session owns the child process and event pipeline; RingBufferPoller is now generic over parse closure, with a drain() protocol replacing the AtomicBool shutdown.

Confidence Score: 5/5

Safe to merge; the correctness argument for the stop-the-world drain is sound, error paths are well-guarded by ContGuard drop semantics and explicit fatal-error recording.

The fixpoint stop loop, synchronous drain after all producers are paused, and ContGuard resumption-on-drop cover the main correctness surface of the design. The remaining findings are style-only and carry no runtime risk.

No files require special attention; the core attach_worker.rs and attach.bpf.h logic is well-reasoned and commented.

Important Files Changed

Filename Overview
crates/memtrack/src/ebpf/attach_worker.rs New background worker that stops tracked processes, classifies mapped files, attaches allocator probes, and resumes — core of the on-demand attach design. Shutdown, error, and panic paths are well-handled.
crates/memtrack/src/ebpf/c/attach.bpf.h New BPF fentry on security_mmap_file that stops tracked processes on first exec-mapping of an unknown inode and queues a userspace attach request. Overflow is counted and surfaced as a fatal error at shutdown.
crates/memtrack/src/ebpf/proc_fs.rs New helpers: wait_all_stopped polls /proc//task for T/t state with D-state tolerance; resolve_mapping looks up dev/ino to an attachable /proc/map_files path. Both have unit tests.
crates/memtrack/src/ebpf/spawn.rs New helpers to wrap a Command in a self-stopping shell stub (avoids pre_exec/SIGSTOP deadlock), spawn it, and resume it; includes a regression test for the deadlock scenario.
crates/memtrack/src/ebpf/poller.rs RingBufferPoller refactored to generic parse closure + drain() protocol; the ctl channel doubles as shutdown signal so no separate AtomicBool is needed.
crates/memtrack/src/ebpf/tracker.rs Tracker refactored to own the AttachWorker and expose spawn/finish instead of track/stop_polling; bpf is now Arc<Mutex<>> to share between tracker and worker.
crates/memtrack/src/session.rs New Session type owns the spawned Child, event Receiver, and RingBufferPoller lifetime.
crates/memtrack/tests/dlopen_tests.rs New integration tests for dlopen-at-runtime and concurrent two-thread dlopen scenarios.
src/cli/exec/mod.rs Removes the CODSPEED_MEMTRACK_BINARIES env-var discovery path; the on-demand watcher supersedes it.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant T as Tracker
    participant W as AttachWorker
    participant BPF as BPF (fentry)
    participant P as Tracked Process
    participant PF as /proc fs

    T->>P: spawn (shell stub, self-STOPs)
    T->>BPF: add_tracked_pid(pid)
    T->>P: SIGCONT (resume)
    P->>P: execve target binary
    BPF->>BPF: security_mmap_file fires
    BPF->>BPF: submit AttachRequest to ring buffer
    BPF->>P: bpf_send_signal(SIGSTOP)
    Note over P: Process stops at syscall return
    W->>PF: wait_all_stopped(pid, 1s)
    PF-->>W: all threads T/t
    W->>BPF: drain() - full ringbuf.consume()
    W->>PF: resolve_mapping(pid, dev, ino)
    PF-->>W: /proc/pid/map_files/range
    W->>W: AllocatorLib::from_path_static()
    W->>BPF: attach_allocator_probes()
    W->>BPF: insert_known_inode(dev, ino)
    Note over W: ContGuard drops - SIGCONT
    W->>P: SIGCONT (resume)
    P->>P: allocations now tracked
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant T as Tracker
    participant W as AttachWorker
    participant BPF as BPF (fentry)
    participant P as Tracked Process
    participant PF as /proc fs

    T->>P: spawn (shell stub, self-STOPs)
    T->>BPF: add_tracked_pid(pid)
    T->>P: SIGCONT (resume)
    P->>P: execve target binary
    BPF->>BPF: security_mmap_file fires
    BPF->>BPF: submit AttachRequest to ring buffer
    BPF->>P: bpf_send_signal(SIGSTOP)
    Note over P: Process stops at syscall return
    W->>PF: wait_all_stopped(pid, 1s)
    PF-->>W: all threads T/t
    W->>BPF: drain() - full ringbuf.consume()
    W->>PF: resolve_mapping(pid, dev, ino)
    PF-->>W: /proc/pid/map_files/range
    W->>W: AllocatorLib::from_path_static()
    W->>BPF: attach_allocator_probes()
    W->>BPF: insert_known_inode(dev, ino)
    Note over W: ContGuard drops - SIGCONT
    W->>P: SIGCONT (resume)
    P->>P: allocations now tracked
Loading

Reviews (6): Last reviewed commit: "test(memtrack): add dlopen/on-demand all..." | Re-trigger Greptile

Comment thread crates/memtrack/src/ebpf/attach_worker.rs Outdated
@not-matthias
not-matthias force-pushed the cod-1801-only-attach-to-used-libraries-in-memtrack branch from 8bce092 to 16ffc38 Compare July 9, 2026 16:28
@not-matthias
not-matthias marked this pull request as ready for review July 9, 2026 16:29
@not-matthias
not-matthias force-pushed the cod-1801-only-attach-to-used-libraries-in-memtrack branch from 16ffc38 to 5b76788 Compare July 9, 2026 16:50
Comment thread crates/memtrack/src/ebpf/attach_worker.rs

@GuillaumeLagrange GuillaumeLagrange left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stopping here becuase I want us to dig the TRACEME thing before leaning in the stop wrapper

Comment thread crates/memtrack/src/ebpf/c/attach.bpf.h Outdated
Comment thread crates/memtrack/src/ebpf/c/memtrack.bpf.c
Comment thread crates/memtrack/src/ebpf/c/memtrack.bpf.c Outdated
Comment thread crates/memtrack/src/ebpf/events.rs
Comment thread crates/memtrack/src/ebpf/tracker.rs Outdated
Comment thread crates/memtrack/src/ebpf/tracker.rs
Comment thread crates/memtrack/src/ebpf/tracker.rs
Comment thread crates/memtrack/src/ebpf/spawn.rs
…tcher

Instead of pre-attaching every allocator found on the system, a BPF
fentry on security_mmap_file signals the first mapping of each unknown
executable inode. A background worker stops the mapping processes,
classifies the file by its symbols, attaches probes, and resumes them.
This covers dlopen'd and statically linked allocators in spawned
children, and skips libraries the benchmark never loads.

The public API shrinks to Tracker (owns the attach worker) and Session
(owns the spawned child and its event pipeline); ring buffers are
polled through a generic RingBufferPoller with caller-supplied parsing.

Fixes COD-1801
On-demand attach supersedes the startup scan: delete the system-wide
library glob, the build-dir walk, and the CODSPEED_MEMTRACK_BINARIES
env var (the runner no longer resolves exec target binaries for it).

BREAKING CHANGE: CODSPEED_MEMTRACK_BINARIES is no longer read

Refs COD-1801
Distro libjemalloc is built without the je_ symbol prefix, so symbol
classification missed it and fell through to libc++ (jemalloc exports
operator new), leaving plain malloc/aligned_alloc unprobed. Match on
mallocx, which keeps its name in unprefixed builds and is unique to
jemalloc.
@not-matthias
not-matthias force-pushed the cod-1801-only-attach-to-used-libraries-in-memtrack branch from a0a194d to 4d3fbb0 Compare July 16, 2026 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants