Repository navigation
Conversation
Merging this PR will degrade performance by 65.7%3 warnings
Performance Changes
Comparing Footnotes
|
ad8eca1 to
2d8d983
Compare
[High impact] The reviewed changes appear safe to merge; no new blocking issue was established. SummaryThe PR bundles
|
`self_exe()` resolves the binary that internal subcommands are re-invoked through, and the memory executor hands that same path to `sudo setcap <caps>+ep` so the capabilities land on the binary that is actually exec'd. Reading an environment variable there means anyone able to set one variable chooses which file receives CAP_SYS_ADMIN and CAP_BPF. The override exists for the tests, where `current_exe()` is the test harness and cannot dispatch a subcommand. Nothing in production sets it -- the doc comment justified it with a launcher scenario that has no caller. Putting it behind `cfg(test)`, constant included, removes the escalation path outright while keeping the tests working; a release build now always resolves `current_exe()`. Reported by Greptile on #531. Refs COD-3440 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`codspeed memtrack` and `codspeed exec-harness` are a re-exec of this binary and share nothing with the runner, but they were dispatched at the bottom of `run()` -- after the profile config is loaded, after the API client is built, and after `DiscoveredProjectConfig::discover_and_load` walks the filesystem. That last one is the problem: the re-exec runs in the benchmark's working directory, which is the user's project. A malformed `codspeed.yaml` there aborts the subcommand, so a measurement fails for a reason that has nothing to do with the measurement, and a `--config` given to the outer run is not forwarded to the inner one to override it. Move them into `run_internal`, called right after `Cli::parse()`. The logger match loses its internal arms for the same reason it had them. Reported by Greptile on #531. Refs COD-3440 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The released Linux artifacts are `aarch64-unknown-linux-musl` and `x86_64-unknown-linux-musl`, and nothing in CI built either: a break in the argp stub, in the kernel-header paths or in the aarch64 `-lgcc` link flag would have surfaced for the first time during a tag-triggered release. The throwaway spike workflow used to cover this and was deleted with the spike. Both legs build on a native runner, with no environment variables, which is also what keeps `.cargo/config.toml` honest -- it has to carry the whole recipe on its own. The assertions are `readelf`-based rather than a `file` string, since rustc emits a static-PIE for x86_64 musl and spells it differently from aarch64, and `codspeed exec-harness --version` / `codspeed memtrack --version` answer only if both CLIs really are linked in. Reported by Greptile on #531. Refs COD-3440 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`self_exe()` resolves the binary that internal subcommands are re-invoked through, and the memory executor hands that same path to `sudo setcap <caps>+ep` so the capabilities land on the binary that is actually exec'd. Reading an environment variable there means anyone able to set one variable chooses which file receives CAP_SYS_ADMIN and CAP_BPF. The override exists for the tests, where `current_exe()` is the test harness and cannot dispatch a subcommand. Nothing in production sets it -- the doc comment justified it with a launcher scenario that has no caller. Putting it behind `cfg(test)`, constant included, removes the escalation path outright while keeping the tests working; a release build now always resolves `current_exe()`. Reported by Greptile on #531. Refs COD-3440 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`codspeed memtrack` and `codspeed exec-harness` are a re-exec of this binary and share nothing with the runner, but they were dispatched at the bottom of `run()` -- after the profile config is loaded, after the API client is built, and after `DiscoveredProjectConfig::discover_and_load` walks the filesystem. That last one is the problem: the re-exec runs in the benchmark's working directory, which is the user's project. A malformed `codspeed.yaml` there aborts the subcommand, so a measurement fails for a reason that has nothing to do with the measurement, and a `--config` given to the outer run is not forwarded to the inner one to override it. Move them into `run_internal`, called right after `Cli::parse()`. The logger match loses its internal arms for the same reason it had them. Reported by Greptile on #531. Refs COD-3440 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The released Linux artifacts are `aarch64-unknown-linux-musl` and `x86_64-unknown-linux-musl`, and nothing in CI built either: a break in the argp stub, in the kernel-header paths or in the aarch64 `-lgcc` link flag would have surfaced for the first time during a tag-triggered release. The throwaway spike workflow used to cover this and was deleted with the spike. Both legs build on a native runner, with no environment variables, which is also what keeps `.cargo/config.toml` honest -- it has to carry the whole recipe on its own. The assertions are `readelf`-based rather than a `file` string, since rustc emits a static-PIE for x86_64 musl and spells it differently from aarch64, and `codspeed exec-harness --version` / `codspeed memtrack --version` answer only if both CLIs really are linked in. Reported by Greptile on #531. Refs COD-3440 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3294b83 to
8e2d7e3
Compare
GuillaumeLagrange
left a comment
There was a problem hiding this comment.
@not-matthias can you do a first round of review 🙏 ? Let's ignore the LD_PRELOAD removal overhead for the exec-harness simulation, I'll spec this in a dedicated ticket and we'll do it before we merge and release this.
not-matthias
left a comment
There was a problem hiding this comment.
Overall pretty good, just a few comments on how to better structure the code/comments
And a few notes on how to best structure the PRs for reviewers:
- Try to keep the changes as minimal as possible
- Do not modify unrelated comments/code -> should be done in separate PRs for easier review
- Remove obvious/LLM-written/bloated comments (using the deslop skill)
- you should also review the PR before putting it into review
GuillaumeLagrange
left a comment
There was a problem hiding this comment.
See comments for details.
I also see that there are unresolved comments.
When you handle a review, all comments should either be
- resolved and handled properly (very important to not silently resolve comments)
- answered with either counter arguments or questions so the reviewer can either accept your arguments or open a discussion
Finally, there are 35 commits in the PR, you may want to re-do commits before the next round of reviews
GuillaumeLagrange
left a comment
There was a problem hiding this comment.
See comments for details.
I also see that there are unresolved comments.
When you handle a review, all comments should either be
- resolved and handled properly (very important to not silently resolve comments)
- answered with either counter arguments or questions so the reviewer can either accept your arguments or open a discussion
Finally, there are 35 commits in the PR, you may want to re-do commits before the next round of reviews
|
Yeah I didn't see them since I needed to click on the see more button, I will check If they are already resolved, and try to reduce the amount of commits by squashing where it's coherent |
96d9261 to
ac9a23c
Compare
b933455 to
3a766f1
Compare
2f56c2f to
ecbfb5b
Compare
GuillaumeLagrange
left a comment
There was a problem hiding this comment.
Still quite a few things to change but we're getting there. The main point is how we are handling the setcap copy for memtrack, which seems quite feeble right now.
69769df to
b774221
Compare
0ce29e7 to
56ed550
Compare
d5b22ed to
5840369
Compare
musl has no `close_range` wrapper, so call the syscall directly. libbpf-sys vendors elfutils, whose `configure` requires `argp`, `obstack` and `fts`, none of which musl provides even though a libelf-only build never calls them: `.cargo/config.toml` seeds the autoconf cache to skip those checks, adds an `argp.h` stub through `CPATH`, and points Debian's `musl-gcc` back at the kernel UAPI headers. aarch64 also links libgcc for the outline-atomic helpers. The libc symbol resolution test now reads the Ubuntu libc path and only runs in CI, since a static musl test binary maps no libc of its own. Refs COD-3440 Co-Authored-By: Claude <noreply@anthropic.com>
Move each binary's logger setup, argument parsing and dispatch into a `cli::run_cli` library function that takes its argv, leaving `main` a thin wrapper. This lets the runner call either CLI in-process with the same logging. No behavior change. Refs COD-3440 Co-Authored-By: Claude <noreply@anthropic.com>
In simulation mode, exec-harness ran each target with an `LD_PRELOAD` library that toggled instrumentation from inside it. That needed a C build step and failed for executables that ignore `LD_PRELOAD`, such as static binaries. Simulation now runs targets like memory mode does, and the runner enables subprocess tracking for exec-harness runs so valgrind follows into the target. Refs COD-3440 Co-Authored-By: Claude <noreply@anthropic.com>
exec-harness was released on its own and downloaded at a pinned version the first time a run had exec targets. Run it as a hidden `codspeed exec-harness` subcommand instead, so it always matches the runner and needs no download. The exec-harness crate is no longer released. Internal subcommands now dispatch before any runner setup: they run in the benchmark's working directory, where loading a stray `codspeed.yaml` could abort the measurement. `codspeed samply` is hidden from the help like the others. Refs COD-3440 Co-Authored-By: Claude <noreply@anthropic.com>
memtrack was released on its own as `codspeed-memtrack` and installed at a pinned version by the memory executor's setup. Link it into the runner with its eBPF feature and run it as a hidden `codspeed memtrack` subcommand, so it always matches the runner and needs no download. With exec-harness already bundled, the binary installer has no users left and is removed. memtrack is no longer released. The eBPF capabilities are granted to a copy of the runner executable rather than the runner itself, so the runner never runs in glibc's secure-execution mode, which would strip `LD_*` variables from the benchmark environment. The copy lives under `~/.cache/codspeed/memtrack`, keyed by the executable's hash, and is created before any sudo run can leave a root-owned `~/.cache`. Copies of other builds unused for a day are removed. `codspeed setup memtrack-path` prints the copy's path. Because that copy gets `setcap`, the `CODSPEED_SELF_EXE` override of the re-executed binary is now test-only. `codspeed setup status` reports no separate memtrack tool any more, and shows privilege status for executors without one. Building the runner now builds memtrack's vendored libbpf, so CI installs the bpf dependencies for it, the release gets memtrack's build dependencies, and a new CI job builds the musl targets the runner ships. Closes COD-3440 Co-Authored-By: Claude <noreply@anthropic.com>
With memtrack linked into the runner, every memory run hashes the runner binary to locate its capability-holding copy and runs memtrack from it. In the debug build both are unoptimized: hashing the 376 MB binary alone takes about 5.6s per run, which took the memory shards from ~10s to ~42s. Build and set up the executor tests with the release profile; the memory shards are back to 8-10s. Refs COD-3440 Co-Authored-By: Claude <noreply@anthropic.com>
Add set_executed_benchmark_for_pid, which passes an explicit pid to instrument_hooks_set_executed_benchmark instead of the calling process' own. set_executed_benchmark keeps its behavior and delegates to it. Bump instrument-hooks, whose valgrind instrument now writes a "Benchmark pid: <pid>" desc line in the dump part when that pid is not the calling process'. This also brings thread-safe C API exports and the callgrind_toggle_collect helper. Refs COD-3722 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
f2907d4 to
2673cb3
Compare
exec-harnessandmemtrackare compiled intocodspeedand reached as hidden subcommands, andexec-harnessno longer injectslibcodspeed_preload.so. The two are one change: a staticallylinked musl binary cannot be preloaded into a glibc process, so the single binary was blocked on
removing the preload.
One release artifact instead of three, 11.0 MB compressed against 13.5 MB today. The download
machinery and both installer pins are gone.
Three things worth a reviewer's attention:
fork, so it also covers fork/exec/wait and the child's pre-
mainstartup. It is a constant~940k Ir per exec-harness benchmark, not a percentage — stable to 0.22% across a 1000× range of
benchmark size. Shipping as is: no forced baseline, no history surgery.
benchmark URI, since
LD_PRELOADis what used to be inherited by every descendant.setcapnow lands oncodspeeditself, so memtrack's five capabilities sit on the CLI.They are
+epwith no inheritable set, so a spawned benchmark does not receive them.Also removes the user-facing "CPU Simulation mode does not support statically linked binaries"
error: nothing is injected into the benchmarked executable any more.
Closes COD-3218
Closes COD-3440