Skip to content

refactor(memtrack): split monolithic eBPF C and Rust into domain files#455

Draft
not-matthias wants to merge 1 commit into
mainfrom
cod-3142-improve-ebpf-multi-file-organization
Draft

refactor(memtrack): split monolithic eBPF C and Rust into domain files#455
not-matthias wants to merge 1 commit into
mainfrom
cod-3142-improve-ebpf-multi-file-organization

Conversation

@not-matthias

Copy link
Copy Markdown
Member

Summary

Splits the monolithic eBPF source (memtrack.bpf.c, 392 lines) and its Rust attach code (memtrack.rs, 478 lines) into small, single-responsibility files. Pure reorganization — no behavior change.

C side

src/ebpf/c/
├── event.h                # unchanged (bindgen contract)
├── main.bpf.c             # includes + LICENSE only — no programs, no maps
├── allocator.h            # UPROBE_* macros + all uprobes + mmap/munmap/brk tracepoints
└── utils/
    ├── map_helpers.h      # BPF_HASH_MAP / BPF_ARRAY_MAP / BPF_RINGBUF macros
    ├── tracking.h         # tracked_pids, pids_ppid, tracking_enabled + is_tracked/is_enabled/track_child + sched_fork
    └── event_helpers.h    # events ringbuf, dropped_events + SUBMIT_EVENT + submit_*_event + store_param/take_param

Encapsulation improvements

  • track_child() helpersched_fork body now reads as intent only, raw bpf_map_update_elem calls hidden
  • store_mmap_args() helper — mmap enter handler no longer pokes maps directly
  • brk enter now uses store_param() instead of raw bpf_map_update_elem

Rust side

src/ebpf/memtrack/
├── mod.rs         # skel include!, MemtrackBpf struct, new(), Drop, start_polling_with_channel
├── macros.rs      # attach_uprobe_uretprobe! / attach_uprobe! / attach_tracepoint! + ensure_symbol_exists
├── maps.rs        # add_tracked_pid, enable/disable_tracking, dropped_events_count
├── allocator.rs   # all attach_* probe methods + attach_allocator_probes + per-allocator methods
└── tracking.rs    # attach_sched_fork / attach_tracepoints

#[macro_use] mod macros; ensures declarative macros are visible to sibling modules. Each submodule imports use super::MemtrackBpf; for its impl block.

Notes

  • File named main.bpf.c (not main.c) because libbpf_cargo::SkeletonBuilder requires the .bpf.c suffix
  • Skeleton struct names change from MemtrackSkel/MemtrackSkelBuilder to MainSkel/MainSkelBuilder (derived from filename). Output file memtrack.skel.rs and include! path unchanged.
  • Four load-bearing map names (tracked_pids, tracking_enabled, events, dropped_events) preserved — Rust side reads them by name from the skeleton.

Verification

  • cargo check --features ebpf — skeleton generates + compiles ✓
  • cargo build --features ebpf
  • cargo test — 2 passed, 20 ignored (sudo/GITHUB_ACTIONS gate) ✓

Split the single-file eBPF source (memtrack.bpf.c) and its Rust attach
code (memtrack.rs) into small, single-responsibility files.

C side:
- main.bpf.c: includes + LICENSE only, no programs or maps
- utils/map_helpers.h: BPF_HASH_MAP/BPF_ARRAY_MAP/BPF_RINGBUF macros
- utils/tracking.h: tracked_pids/pids_ppid/tracking_enabled maps +
  is_tracked/is_enabled/track_child helpers + sched_fork program
- utils/event_helpers.h: events ringbuf, dropped_events, SUBMIT_EVENT,
  submit_*_event, store_param/take_param
- allocator.h: UPROBE_* macros + all allocator uprobes + mmap/munmap/brk
  tracepoints

Encapsulation improvements:
- track_child() helper extracted from sched_fork body
- store_mmap_args() helper extracted from mmap enter handler
- brk enter now uses store_param() instead of raw bpf_map_update_elem

Rust side:
- memtrack/mod.rs: skel include, MemtrackBpf struct, new(), Drop
- memtrack/macros.rs: attach_uprobe_uretprobe!/attach_uprobe!/
  attach_tracepoint! macros + ensure_symbol_exists
- memtrack/maps.rs: add_tracked_pid, enable/disable_tracking,
  dropped_events_count
- memtrack/allocator.rs: all attach_* probe methods + per-allocator
  attach methods
- memtrack/tracking.rs: attach_sched_fork / attach_tracepoints

Skeleton struct names change from MemtrackSkel to MainSkel (derived
from main.bpf.c filename). Output file memtrack.skel.rs unchanged.
@codspeed-hq

codspeed-hq Bot commented Jul 14, 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-3142-improve-ebpf-multi-file-organization (03ba372) with main (58d994a)

Open in CodSpeed

@not-matthias not-matthias force-pushed the cod-3142-improve-ebpf-multi-file-organization branch from 344d4f2 to 03ba372 Compare July 14, 2026 17:58
@greptile-apps

greptile-apps Bot commented Jul 14, 2026

Copy link
Copy Markdown

Greptile Summary

Splits the monolithic memtrack.bpf.c (392 lines) and memtrack.rs (478 lines) into smaller, single-responsibility files with no behavioral changes.

  • C side: main.bpf.c now serves as a thin includes-only entry point, with map macros in utils/map_helpers.h, tracking logic in utils/tracking.h, event helpers in utils/event_helpers.h, and all allocator probes in allocator.h. Two small readability helpers (store_mmap_args, track_child) are introduced to hide raw bpf_map_update_elem calls.
  • Rust side: memtrack.rs is split into mod.rs (struct + lifecycle), macros.rs (declarative attach macros + ensure_symbol_exists), maps.rs (map operations), allocator.rs (all probe-attach impls), and tracking.rs (sched-fork attach). Skeleton type names change from MemtrackSkel/MemtrackSkelBuilder to MainSkel/MainSkelBuilder to reflect the renamed source file.

Confidence Score: 5/5

Pure structural reorganization with no BPF program logic, map definitions, or Rust attach logic changes — safe to merge.

Every probe, map, and event-submission code path is a faithful copy of the original monolith. The only functional change is sys_enter_brk now calling store_param instead of inlining three lines — the semantics are identical. cargo build and cargo check pass, the two runnable unit tests pass, and the integration tests are gated by privilege so their absence here is expected. No map names, SEC sections, or event struct layouts changed.

No files require special attention. The redundant direct includes in main.bpf.c (headers already pulled in transitively via allocator.h) are harmless due to include guards and do not affect the compiled BPF object.

Important Files Changed

Filename Overview
crates/memtrack/src/ebpf/c/main.bpf.c New thin entry point: includes only (vmlinux, bpf helpers, allocator.h, event.h, utils/*.h) + LICENSE section; all programs and maps live in headers now.
crates/memtrack/src/ebpf/c/allocator.h Moved UPROBE_* macros, all uprobe/uretprobe probe definitions, and mmap/munmap/brk tracepoints from the monolithic .c file. Adds store_mmap_args helper. Redundant includes relative to what main.bpf.c already pulls transitively, but guarded by include guards.
crates/memtrack/src/ebpf/c/utils/tracking.h Extracts tracked_pids, pids_ppid, tracking_enabled maps plus is_tracked/is_enabled/track_child helpers and sched_fork tracepoint; behavior identical to original.
crates/memtrack/src/ebpf/c/utils/event_helpers.h Extracts events ringbuf, dropped_events, store_param/take_param helpers, SUBMIT_EVENT macro, and all submit_*_event functions; faithful copy from the original monolith.
crates/memtrack/src/ebpf/c/utils/map_helpers.h Clean extraction of BPF_HASH_MAP / BPF_ARRAY_MAP / BPF_RINGBUF macros with proper include guard; no changes to macro bodies.
crates/memtrack/src/ebpf/memtrack/mod.rs Core MemtrackBpf struct and lifecycle; skeleton include path unchanged (memtrack.skel.rs) but type references updated to MainSkel/MainSkelBuilder to match renamed source file.
crates/memtrack/src/ebpf/memtrack/macros.rs Declarative attach macros and ensure_symbol_exists helper extracted from old memtrack.rs; pub(super) visibility is correct for cross-sibling use via use super::macros::ensure_symbol_exists.
crates/memtrack/src/ebpf/memtrack/allocator.rs All allocator probe-attach impl blocks; cleanly imports ensure_symbol_exists from macros sibling module and relies on #[macro_use] for the attach_* macros.
crates/memtrack/src/ebpf/memtrack/maps.rs BPF map operations (add_tracked_pid, enable/disable_tracking, dropped_events_count) extracted without any change to logic.
crates/memtrack/src/ebpf/memtrack/tracking.rs Minimal module: attach_sched_fork (via attach_tracepoint! macro) and attach_tracepoints wrapper; correct and complete.
crates/memtrack/build.rs Single-line source path change (memtrack.bpf.c → main.bpf.c); rerun-if-changed=src/ebpf/c covers the whole directory tree, so the new utils/ subdirectory is automatically watched.
crates/memtrack/AGENTS.md New documentation file covering project overview, architecture, key directories, development commands, and testing details for the memtrack crate.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph C["C (eBPF) side"]
        MB["main.bpf.c\n(includes + LICENSE only)"]
        AH["allocator.h\nUPROBE_* macros\nall uprobe/uretprobe programs\nmmap/munmap/brk tracepoints"]
        EH["utils/event_helpers.h\nevents ringbuf, dropped_events\nSUBMIT_EVENT, submit_*_event\nstore_param, take_param"]
        MH["utils/map_helpers.h\nBPF_HASH_MAP\nBPF_ARRAY_MAP\nBPF_RINGBUF"]
        TH["utils/tracking.h\ntracked_pids, pids_ppid,\ntracking_enabled maps\nis_tracked, is_enabled\ntrack_child, sched_fork"]
        EV["event.h\n(bindgen contract, unchanged)"]
        MB --> AH
        MB --> EV
        MB --> EH
        MB --> MH
        MB --> TH
        AH --> EH
        AH --> MH
        AH --> TH
        EH --> MH
        EH --> TH
        EH --> EV
        TH --> MH
    end

    subgraph RS["Rust side"]
        MOD["mod.rs\nMemtrackBpf struct\nnew(), Drop\nstart_polling_with_channel"]
        MAC["macros.rs\nattach_uprobe_uretprobe!\nattach_uprobe!\nattach_tracepoint!\nensure_symbol_exists"]
        MAPS["maps.rs\nadd_tracked_pid\nenable/disable_tracking\ndropped_events_count"]
        ALLOC["allocator.rs\nattach_*_probes methods\nattach_allocator_probes\nper-allocator helpers"]
        TRACK["tracking.rs\nattach_sched_fork\nattach_tracepoints"]
        MOD -->|"#[macro_use]"| MAC
        MOD --> MAPS
        MOD --> ALLOC
        MOD --> TRACK
        ALLOC -->|"use super::macros"| MAC
    end

    C -->|"SkeletonBuilder → MainSkel"| RS
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"}}}%%
flowchart TD
    subgraph C["C (eBPF) side"]
        MB["main.bpf.c\n(includes + LICENSE only)"]
        AH["allocator.h\nUPROBE_* macros\nall uprobe/uretprobe programs\nmmap/munmap/brk tracepoints"]
        EH["utils/event_helpers.h\nevents ringbuf, dropped_events\nSUBMIT_EVENT, submit_*_event\nstore_param, take_param"]
        MH["utils/map_helpers.h\nBPF_HASH_MAP\nBPF_ARRAY_MAP\nBPF_RINGBUF"]
        TH["utils/tracking.h\ntracked_pids, pids_ppid,\ntracking_enabled maps\nis_tracked, is_enabled\ntrack_child, sched_fork"]
        EV["event.h\n(bindgen contract, unchanged)"]
        MB --> AH
        MB --> EV
        MB --> EH
        MB --> MH
        MB --> TH
        AH --> EH
        AH --> MH
        AH --> TH
        EH --> MH
        EH --> TH
        EH --> EV
        TH --> MH
    end

    subgraph RS["Rust side"]
        MOD["mod.rs\nMemtrackBpf struct\nnew(), Drop\nstart_polling_with_channel"]
        MAC["macros.rs\nattach_uprobe_uretprobe!\nattach_uprobe!\nattach_tracepoint!\nensure_symbol_exists"]
        MAPS["maps.rs\nadd_tracked_pid\nenable/disable_tracking\ndropped_events_count"]
        ALLOC["allocator.rs\nattach_*_probes methods\nattach_allocator_probes\nper-allocator helpers"]
        TRACK["tracking.rs\nattach_sched_fork\nattach_tracepoints"]
        MOD -->|"#[macro_use]"| MAC
        MOD --> MAPS
        MOD --> ALLOC
        MOD --> TRACK
        ALLOC -->|"use super::macros"| MAC
    end

    C -->|"SkeletonBuilder → MainSkel"| RS
Loading

Reviews (1): Last reviewed commit: "refactor(memtrack): split monolithic eBP..." | Re-trigger Greptile

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