feat(memtrack): collect RSS via rss_stat and folio-rmap reconstruction#453
feat(memtrack): collect RSS via rss_stat and folio-rmap reconstruction#453not-matthias wants to merge 9 commits into
Conversation
Merging this PR will not alter performance
|
Greptile SummaryThis PR adds RSS collection and optional rmap-based reconstruction to memtrack. The main changes are:
Confidence Score: 4/5This is close, but the startup failure path should be fixed before merging.
crates/memtrack/src/ebpf/memtrack/tracking.rs Important Files Changed
|
b670b0a to
2f41984
Compare
41945a5 to
64688a9
Compare
b02f3cb to
411b713
Compare
4d3fbb0 to
a7e1666
Compare
e00fb6b to
a68424a
Compare
e48a0a0 to
f305358
Compare
|
@greptileai pause reviews for now until i enable them again |
|
Understood — pausing reviews. I'll hold off until you re-enable them. Tip: You can customize Greptile's behavior for this repo with |
f4df079 to
921e469
Compare
f8dc4b1 to
7c2d372
Compare
Set AllowShortFunctionsOnASingleLine: None in .clang-format and apply it, reformatting the allocator uprobe macros accordingly.
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, and a writer bench case. An rss_stat update from reclaim or another process's madvise fires in the actor's context; track (mm_id, member) -> owning pid so those updates reach the owner. External events may only lower a counter, so stale reads and mm_id collisions cannot invent peaks.
Attach fentry hooks on the folio-rmap add/remove functions, emitting signed page-count deltas per MM_* bucket so anon, file, and shmem 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 contract, MemtrackEventKind::Rmap, parser arm, and bench case.
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.
7c2d372 to
fa0526f
Compare
| self.attach_task_newtask()?; | ||
| self.attach_sched_process_exec()?; | ||
| self.attach_sched_process_exit()?; |
There was a problem hiding this comment.
These lifecycle tracepoint attaches still return errors from attach_tracepoints(), while rss_stat only logs and continues. Tracker::new() and Tracker::new_without_allocators_with_rmap() call this during startup, so a host that can attach the allocator probes but cannot attach task:task_newtask, sched:sched_process_exec, or sched:sched_process_exit still fails before memory tracking starts. These hooks only support RSS reconciliation, so their attach failures should disable that RSS path instead of taking down the tracker.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/memtrack/src/ebpf/memtrack/tracking.rs
Line: 55-57
Comment:
**Lifecycle Attach Aborts**
These lifecycle tracepoint attaches still return errors from `attach_tracepoints()`, while `rss_stat` only logs and continues. `Tracker::new()` and `Tracker::new_without_allocators_with_rmap()` call this during startup, so a host that can attach the allocator probes but cannot attach `task:task_newtask`, `sched:sched_process_exec`, or `sched:sched_process_exit` still fails before memory tracking starts. These hooks only support RSS reconciliation, so their attach failures should disable that RSS path instead of taking down the tracker.
How can I resolve this? If you propose a fix, please make it concise.Add /proc-backed integration cases, the rmap late-enable case, and the shared harness helpers used across the rss test suite.
Recover the owning pid for rmap events run by another task (reclaim, process_madvise, khugepaged, KSM) from the mm_struct pointer, and maintain the ownership maps across exec and thread-group exit.
Add the exec_anon and foreign_add cases and extend the rss suite for the mm-ownership attribution and thread-group lifecycle paths.
…ap hooks The batched folio_*_rmap_ptes interface only exists since v6.8; older kernels (e.g. the codspeed-macro runners on 6.5) account the same mapping events through page_add_anon_rmap / page_add_file_rmap / page_remove_rmap. Hook those as an alternative tier. Selection is now per accounting family (anon add-new, anon add, file add, remove) with the newest available API generation winning, since kernels keep transitional wrappers around their successors and hooking two generations double-counts. If any family has no sound hook (pre-6.3, or 6.6/6.7 where file adds flow through folio_add_file_rmap_range whose wrapper inlining is build-dependent), the whole reconstruction is disabled loudly instead of emitting silently drifting partial data. page_remove_rmap also fires for hugetlb teardown, which never touches the MM_*PAGES counters; filtered via VM_HUGETLB to avoid the version-drifting hugetlb folio markers.
Run the rss/bpf tests on arm and codspeed-macro runners over old- and new-layout kernels.
fa0526f to
e3a67b4
Compare
| self.attach_task_newtask()?; | ||
| self.attach_sched_process_exec()?; | ||
| self.attach_sched_process_exit()?; |
There was a problem hiding this comment.
These lifecycle tracepoints still make tracker startup fail before allocator tracking can run. rss_stat is now best-effort, but task_newtask, sched_process_exec, and sched_process_exit still propagate attach errors through attach_tracepoints(). If a host can attach the existing allocator probes but cannot attach one of these RSS reconciliation tracepoints, Tracker::new() returns an error instead of starting memtrack with RSS lifecycle accounting disabled.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/memtrack/src/ebpf/memtrack/tracking.rs
Line: 55-57
Comment:
**Lifecycle Attaches Abort**
These lifecycle tracepoints still make tracker startup fail before allocator tracking can run. `rss_stat` is now best-effort, but `task_newtask`, `sched_process_exec`, and `sched_process_exit` still propagate attach errors through `attach_tracepoints()`. If a host can attach the existing allocator probes but cannot attach one of these RSS reconciliation tracepoints, `Tracker::new()` returns an error instead of starting memtrack with RSS lifecycle accounting disabled.
How can I resolve this? If you propose a fix, please make it concise.
Summary
Adds RSS (resident set size) collection to
memtrack, in two layers:kmem:rss_stattracepoint — absolute per-mm resident bytes (anon/file/shmem/swap), latest-wins.fentryhooks 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_RSScontract,MemtrackEventKind::Rss, parser arm, writer bench case, gated integration test.feat(memtrack): reconstruct anon RSS from gated folio rmap fentry hooks—EVENT_TYPE_RMAP_ANON+RmapAnonevent, fivefentryprograms (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 viammap/munmapand asserts the reconstructed estimate tracks therss_statMM_ANONPAGES peak within 25%.What's on by default vs gated
rss_stattracepoint: always on. EmittingRssevents is the intended new default behavior introduced by this change — the RSS tracepoint is not gated.RmapAnonfolio-rmapfentryprograms: off by default, gated behindCODSPEED_MEMTRACK_TRACK_RMAP=1. When the flag is unset they areset_autoload(false)before load and never attached, so:fentryBTF target would otherwise fail the whole load), and--mode memory) and out of the existing test suites — noRmapAnonevents are produced by default.Verification
Run in a privileged,
--pid=hostcontainer sharing the host kernel (7.0.12):real anon amplitude = 64 MiB, estimated peak = 64 MiB(ratio 1.00).rss_tests, flag unset): ✅ passes — noRmapAnonevents, folio-rmap programs stay unloaded.cargo fmt, andclippyclean.folio_*_rmap*functions verified to match theBPF_PROGarg layouts.Review notes (draft)
track_commandordering: the shared test helper spawns the child beforeenable()/track(root_pid). In practice the child'sfork→execve→ld.so→libc-initfar 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-usleepin 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 needsysconf(_SC_PAGESIZE);rss_statis already in bytes and unaffected. Happy to switch tosysconfif these tests run on arm64 CI.