Skip to content

feat: run memtrack memory profiling without sudo via file capabilities#407

Merged
not-matthias merged 7 commits into
mainfrom
feat/memtrack-sudoless
Jun 17, 2026
Merged

feat: run memtrack memory profiling without sudo via file capabilities#407
not-matthias merged 7 commits into
mainfrom
feat/memtrack-sudoless

Conversation

@not-matthias

@not-matthias not-matthias commented Jun 15, 2026

Copy link
Copy Markdown
Member

No description provided.

@codspeed-hq

codspeed-hq Bot commented Jun 15, 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 feat/memtrack-sudoless (e2da80a) with main (66037be)

Open in CodSpeed

@not-matthias not-matthias force-pushed the feat/memtrack-sudoless branch from 6b0e488 to d729c92 Compare June 15, 2026 16:33
@not-matthias not-matthias marked this pull request as ready for review June 15, 2026 16:36
@greptile-apps

greptile-apps Bot commented Jun 15, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces the previous sudo-wrapped memtrack launch with Linux file capabilities (setcap), allowing codspeed-memtrack to load its eBPF programs without requiring the calling process to run as root. A one-time setcap grant happens during codspeed setup --mode memory (or lazily on the first run), verified via a hand-rolled security.capability xattr decoder.

  • Core capability flow: ensure_memtrack_capabilities() derives the setcap grammar string directly from MEMTRACK_REQUIRED_CAPS at runtime, eliminating the manual-sync hazard that the previous static string constant introduced. binary_has_capabilities() cross-checks the granted set via the xattr before any run, and the whole approach degrades gracefully (with warnings) on filesystems that don't support file capabilities.
  • Memtrack changes: bump_memlock_rlimit now logs-and-continues instead of failing hard, correctly reflecting that kernels ≥ 5.11 account BPF memory via cgroups; the no-IPC code path enables tracking upfront since no signal would otherwise toggle it.
  • Platform hygiene: memtrack, ipc-channel, and caps are moved to [target.'cfg(target_os = \"linux\")'.dependencies]; RunnerMode::Memory and related match arms are all #[cfg(target_os = \"linux\")]-gated.

Confidence Score: 5/5

Safe to merge. The capability grant is idempotent, best-effort, and falls back to root-mode if setcap or the filesystem don't cooperate; all changed code paths are well-tested.

The xattr decoder correctly handles all three VFS capability revisions, the setcap spec is now derived from the same constant that drives verification (eliminating drift), and the run-time ensure_privileges() guard provides a hard stop before any eBPF load attempt. The only observations are style nits.

No files require special attention. The Cargo.toml has a minor dependency-placement inconsistency (xattr in the wrong section), but it has no effect on correctness or macOS builds.

Important Files Changed

Filename Overview
src/executor/helpers/capabilities.rs New file: custom xattr-based capability check with correct v1/v2/v3 layout parsing and solid unit tests.
src/executor/memory/setup.rs Replaces static MEMTRACK_SETCAP_SPEC constant with a derived memtrack_setcap_spec() function, eliminating the caps-string / caps-array sync hazard; also introduces has/ensure capability helpers.
src/executor/memory/executor.rs Drops wrap_with_sudo in favour of file-capability check; adds ensure_privileges() safety guard, privilege_status(), and grant_privileges() Executor trait implementations.
src/executor/mod.rs Adds PrivilegeStatus enum and privilege_status/grant_privileges default-noop trait methods; calls grant_privileges() in the normal run path after setup.
Cargo.toml Moves memtrack/ipc-channel to Linux-specific deps correctly; adds caps to Linux deps, but adds xattr to the unconditional [dependencies] block even though it is only used in Linux-gated code.
crates/memtrack/src/ebpf/tracker.rs bump_memlock_rlimit now degrades gracefully instead of bailing, with a doc comment explaining the kernel-5.11 boundary.
.github/workflows/ci.yml Adds Grant memtrack file capabilities step before the test run; capabilities are idempotently re-granted by the test itself anyway.
src/cli/setup.rs Calls grant_privileges() after executor.setup() and adds privilege_status display in setup status output.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant run_executor
    participant MemoryExecutor
    participant ensure_caps as ensure_memtrack_capabilities
    participant setcap as sudo setcap
    participant xattr as binary_has_capabilities (xattr)

    User->>run_executor: codspeed exec --mode memory
    run_executor->>MemoryExecutor: setup() → install_memtrack()
    run_executor->>MemoryExecutor: grant_privileges()
    MemoryExecutor->>ensure_caps: ensure_memtrack_capabilities()
    ensure_caps->>xattr: check security.capability xattr
    alt caps already present
        xattr-->>ensure_caps: satisfied
        ensure_caps-->>MemoryExecutor: Ok(())
    else caps missing
        ensure_caps->>setcap: sudo setcap cap_bpf,cap_perfmon,...+ep /path/to/codspeed-memtrack
        setcap-->>ensure_caps: success / failure (warn-only)
        ensure_caps->>xattr: re-verify
        xattr-->>ensure_caps: pass / fail (warn if fail)
        ensure_caps-->>MemoryExecutor: Ok(())
    end
    run_executor->>MemoryExecutor: run(execution_context)
    MemoryExecutor->>MemoryExecutor: ensure_privileges() last-resort check
    alt root OR caps present
        MemoryExecutor-->>MemoryExecutor: proceed
    else neither
        MemoryExecutor-->>User: bail capabilities could not be granted
    end
    MemoryExecutor->>MemoryExecutor: build_memtrack_command() no sudo wrap
    MemoryExecutor-->>User: benchmark results
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 User
    participant run_executor
    participant MemoryExecutor
    participant ensure_caps as ensure_memtrack_capabilities
    participant setcap as sudo setcap
    participant xattr as binary_has_capabilities (xattr)

    User->>run_executor: codspeed exec --mode memory
    run_executor->>MemoryExecutor: setup() → install_memtrack()
    run_executor->>MemoryExecutor: grant_privileges()
    MemoryExecutor->>ensure_caps: ensure_memtrack_capabilities()
    ensure_caps->>xattr: check security.capability xattr
    alt caps already present
        xattr-->>ensure_caps: satisfied
        ensure_caps-->>MemoryExecutor: Ok(())
    else caps missing
        ensure_caps->>setcap: sudo setcap cap_bpf,cap_perfmon,...+ep /path/to/codspeed-memtrack
        setcap-->>ensure_caps: success / failure (warn-only)
        ensure_caps->>xattr: re-verify
        xattr-->>ensure_caps: pass / fail (warn if fail)
        ensure_caps-->>MemoryExecutor: Ok(())
    end
    run_executor->>MemoryExecutor: run(execution_context)
    MemoryExecutor->>MemoryExecutor: ensure_privileges() last-resort check
    alt root OR caps present
        MemoryExecutor-->>MemoryExecutor: proceed
    else neither
        MemoryExecutor-->>User: bail capabilities could not be granted
    end
    MemoryExecutor->>MemoryExecutor: build_memtrack_command() no sudo wrap
    MemoryExecutor-->>User: benchmark results
Loading

Reviews (10): Last reviewed commit: "ci: grant memtrack file capabilities bef..." | Re-trigger Greptile

Comment thread src/executor/memory/setup.rs Outdated

@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.

olgtm

Comment thread crates/memtrack/src/ebpf/tracker.rs Outdated
Comment thread src/executor/helpers/run_with_sudo.rs Outdated
Comment thread src/executor/helpers/run_with_sudo.rs Outdated
Comment thread src/executor/memory/executor.rs Outdated
Comment thread src/executor/mod.rs Outdated
Comment thread src/executor/memory/setup.rs Outdated
@not-matthias not-matthias force-pushed the feat/memtrack-sudoless branch 4 times, most recently from fb4e5fa to 1f22d61 Compare June 16, 2026 12:15
@not-matthias not-matthias force-pushed the feat/memtrack-sudoless branch from 1f22d61 to dc0f8a6 Compare June 16, 2026 12:32

@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.

olgtm

Comment thread src/executor/memory/executor.rs Outdated
@not-matthias not-matthias force-pushed the feat/memtrack-sudoless branch from dc0f8a6 to 93f53cf Compare June 17, 2026 09:14
Raising RLIMIT_MEMLOCK requires CAP_SYS_RESOURCE, which unprivileged agents and many containers lack. On kernels >= 5.11 BPF memory is accounted against the cgroup, so the limit is irrelevant; treat a failed setrlimit as a warning instead of bailing.
Without an IPC server nothing toggles the tracking_enabled map, so the eBPF is_enabled() check drops every event. Enable tracking up front in that path.
codspeed setup --mode memory now grants codspeed-memtrack the capabilities it needs (cap_bpf, cap_perfmon, cap_dac_read_search, cap_sys_admin, cap_sys_resource) via setcap, idempotently and with a single sudo prompt. At run time the memory executor no longer wraps memtrack in sudo: it requires root or those capabilities, otherwise failing with guidance to run codspeed setup. Lets agents run memory benchmarks unattended.

Related: COD-1801
setup status now prints a privileges line under the memory executor:
satisfied when running as root or the codspeed-memtrack binary carries
the required file capabilities, missing (with remediation) otherwise.
Lets agents confirm sudo-less memory profiling is ready before a run.

Related: COD-1801
memtrack-based memory profiling is Linux-only; the caps crate fails to
build on macOS. Move caps/memtrack/ipc-channel to Linux-only dependencies
and cfg-gate the memory module and its wiring so macOS builds compile.
@not-matthias not-matthias force-pushed the feat/memtrack-sudoless branch from 93f53cf to e2da80a Compare June 17, 2026 10:40
@not-matthias not-matthias merged commit e2da80a into main Jun 17, 2026
22 checks passed
@not-matthias not-matthias deleted the feat/memtrack-sudoless branch June 17, 2026 12:48
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