Skip to content

feat(memtrack): collect RSS via rss_stat and folio-rmap reconstruction#453

Open
not-matthias wants to merge 12 commits into
mainfrom
cod-3089-collect-rss-in-memtrack
Open

feat(memtrack): collect RSS via rss_stat and folio-rmap reconstruction#453
not-matthias wants to merge 12 commits into
mainfrom
cod-3089-collect-rss-in-memtrack

Conversation

@not-matthias

@not-matthias not-matthias commented Jul 13, 2026

Copy link
Copy Markdown
Member

Summary

Adds RSS (resident set size) collection to memtrack, in two layers:

  1. Authoritative RSS from the kernel's kmem:rss_stat tracepoint — absolute per-mm resident bytes (anon/file/shmem/swap), latest-wins.
  2. Reconstructed anonymous RSS from raw kernel folio-rmap events: fentry hooks on the anon folio-rmap add/remove functions emit signed page-count deltas, so anon RSS can be rebuilt over time as Σ(add − remove) × PAGE_SIZE.

The reconstruction is a total anon RSS delta-sum, not a per-vaddr resident map — the kernel remove hook (folio_remove_rmap_ptes) carries no address, so removals can't be attributed to a vaddr (the add hooks' faulting vaddr is emitted for observability only).

Commits

  • feat(memtrack): track RSS via kmem:rss_stat tracepoint — the baseline: EVENT_TYPE_RSS contract, MemtrackEventKind::Rss, parser arm, writer bench case, gated integration test.
  • feat(memtrack): reconstruct anon RSS from gated folio rmap fentry hooksEVENT_TYPE_RMAP_ANON + RmapAnon event, five fentry programs (add_new / add_ptes / remove_ptes / remove_pmd / remove_pud), CO-RE folio helpers, and the load/attach gating.
  • test(memtrack): validate anon RSS reconstruction against rss_stat — ramps anon RSS via mmap/munmap and asserts the reconstructed estimate tracks the rss_stat MM_ANONPAGES peak within 25%.

What's on by default vs gated

  • rss_stat tracepoint: always on. Emitting Rss events is the intended new default behavior introduced by this change — the RSS tracepoint is not gated.
  • RmapAnon folio-rmap fentry programs: off by default, gated behind CODSPEED_MEMTRACK_TRACK_RMAP=1. When the flag is unset they are set_autoload(false) before load and never attached, so:
    • the skeleton still loads on any kernel (a missing fentry BTF target would otherwise fail the whole load), and
    • the folio-rmap reconstruction path stays out of production (--mode memory) and out of the existing test suites — no RmapAnon events are produced by default.

Verification

Run in a privileged, --pid=host container sharing the host kernel (7.0.12):

  • Reconstruction (flag on):real anon amplitude = 64 MiB, estimated peak = 64 MiB (ratio 1.00).
  • Regression (rss_tests, flag unset): ✅ passes — no RmapAnon events, folio-rmap programs stay unloaded.
  • Parser unit tests, cargo fmt, and clippy clean.
  • Kernel BTF signatures for all five folio_*_rmap* functions verified to match the BPF_PROG arg layouts.

Review notes (draft)

  • track_command ordering: the shared test helper spawns the child before enable()/track(root_pid). In practice the child's fork→execve→ld.so→libc-init far outlasts the two BPF-map updates, so tracking is armed before the workload allocates (both fixtures captured full event streams). Flagging in case we'd prefer a leading settle-usleep in the fixtures or an enable-before-spawn change in the helper.
  • PAGE_SIZE: hardcoded to 4096 (correct on x86_64). On a 16K/64K-page arm64 runner the estimate would need sysconf(_SC_PAGESIZE); rss_stat is already in bytes and unaffected. Happy to switch to sysconf if these tests run on arm64 CI.
  • The chart/inspection tooling used during development lives outside the tree (dev artifact) and is not part of this PR.

@codspeed-hq

codspeed-hq Bot commented Jul 13, 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.

✅ 7 untouched benchmarks


Comparing cod-3089-collect-rss-in-memtrack (41945a5) with main (58d994a)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Jul 13, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds RSS tracking to memtrack. The main changes are:

  • kmem:rss_stat events for per-process RSS counters.
  • Optional anon RSS reconstruction from folio-rmap fentry hooks.
  • Fork, exec, and exit events for RSS state reconciliation.
  • Parser, artifact, benchmark, and test coverage for the new event kinds.

Confidence Score: 4/5

This is close, but one startup failure should be fixed before merging.

  • The direct rss_stat attach failure now falls back cleanly.
  • The same optional RSS path still requires new lifecycle tracepoints.
  • A host without those lifecycle tracepoints can fail tracker startup before existing allocation tracking runs.

crates/memtrack/src/ebpf/memtrack.rs

Important Files Changed

Filename Overview
crates/memtrack/src/ebpf/memtrack.rs Adds RSS and rmap loading, gating, and attach logic, but lifecycle tracepoint attach failures can still stop tracker startup.
crates/memtrack/src/ebpf/c/rss.bpf.h Adds the RSS tracepoint program and optional anon folio-rmap fentry programs.
crates/memtrack/tests/shared.rs Adds explicit rmap test plumbing and C fixture compilation support.
crates/memtrack/tests/rss_tests.rs Adds RSS and anon reconstruction tests over C fixtures.
crates/runner-shared/src/artifacts/memtrack.rs Extends the shared memtrack event model with RSS, rmap, and lifecycle variants.

Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
crates/memtrack/src/ebpf/memtrack.rs:588-590
**Lifecycle attaches still abort**

The `rss_stat` attach now warns and continues, but these new RSS lifecycle tracepoints still use `?`. If a host can run the existing allocator probes but lacks or disables `task:task_newtask`, `sched:sched_process_exec`, or `sched:sched_process_exit`, `attach_tracepoints()` returns an error before the tracker starts. That keeps memory tracking unavailable on a host where only the RSS accounting side is unsupported. Make these lifecycle-only attaches best-effort as well, or gate RSS reconciliation on the lifecycle tracepoints that attach successfully.

Reviews (5): Last reviewed commit: "test(memtrack): validate fork-seeded chi..." | Re-trigger Greptile

Comment thread crates/memtrack/tests/rss_reconstruction_tests.rs Outdated
Comment thread crates/memtrack/tests/rss_reconstruction_tests.rs Outdated
Comment thread crates/memtrack/src/ebpf/memtrack.rs
Comment thread crates/memtrack/tests/rss_reconstruction_tests.rs Outdated
Comment thread crates/memtrack/tests/shared.rs Outdated
@not-matthias not-matthias force-pushed the cod-3089-collect-rss-in-memtrack branch from d992a0a to b670b0a Compare July 13, 2026 17:21
Sample the kernel's per-mm resident counter through the kmem:rss_stat tracepoint, emitting absolute byte values per mm member. Adds the EVENT_TYPE_RSS contract, MemtrackEventKind::Rss, the parser arm, a writer bench case, and a gated integration test.
Attach fentry hooks on the anon folio-rmap add/remove functions, emitting signed page-count deltas so anon RSS can be reconstructed over time. Gated behind CODSPEED_MEMTRACK_TRACK_RMAP; the programs stay autoload-off by default so the skeleton loads on any kernel. Adds the EVENT_TYPE_RMAP_ANON contract, MemtrackEventKind::RmapAnon, parser arm, and bench case.
Add a gated integration test that ramps anon RSS via mmap/munmap, reconstructs it from RmapAnon page-count deltas, and asserts the estimate tracks the rss_stat MM_ANONPAGES peak within 25%.
@not-matthias not-matthias force-pushed the cod-3089-collect-rss-in-memtrack branch from b670b0a to 2f41984 Compare July 13, 2026 17:32
@not-matthias not-matthias marked this pull request as ready for review July 14, 2026 15:38
A forked child's inherited RSS is invisible to rss_stat: the fork-time
counter copies fire outside the child's context, and anon COW faults
are counter-neutral, so a child that only touches inherited memory
never reports anything on its own. A fork event carrying the parent
pid lets consumers seed the child from the parent's last absolutes;
exec and exit mark where the address space is replaced or torn down.
fork_idle's child only touches inherited memory (counter-neutral COW,
no allocations), so its 32 MiB is observable exclusively through the
fork-event seed; the parent samples /proc/<child>/status via a pipe
handshake since any child-side allocation would emit a self-correcting
absolute and defeat the test.
Comment on lines +588 to +590
self.attach_task_newtask()?;
self.attach_sched_process_exec()?;
self.attach_sched_process_exit()?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Lifecycle attaches still abort

The rss_stat attach now warns and continues, but these new RSS lifecycle tracepoints still use ?. If a host can run the existing allocator probes but lacks or disables task:task_newtask, sched:sched_process_exec, or sched:sched_process_exit, attach_tracepoints() returns an error before the tracker starts. That keeps memory tracking unavailable on a host where only the RSS accounting side is unsupported. Make these lifecycle-only attaches best-effort as well, or gate RSS reconciliation on the lifecycle tracepoints that attach successfully.

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/memtrack/src/ebpf/memtrack.rs
Line: 588-590

Comment:
**Lifecycle attaches still abort**

The `rss_stat` attach now warns and continues, but these new RSS lifecycle tracepoints still use `?`. If a host can run the existing allocator probes but lacks or disables `task:task_newtask`, `sched:sched_process_exec`, or `sched:sched_process_exit`, `attach_tracepoints()` returns an error before the tracker starts. That keeps memory tracking unavailable on a host where only the RSS accounting side is unsupported. Make these lifecycle-only attaches best-effort as well, or gate RSS reconciliation on the lifecycle tracepoints that attach successfully.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

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.

1 participant