Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5abd69c
chore(memtrack): add argp.h stub for musl builds
moha-bekh Sep 4, 2026
51bb339
ci: add throwaway COD-3440 musl check workflow
moha-bekh Sep 4, 2026
331541e
feat(exec-harness)!: remove the LD_PRELOAD hack
moha-bekh Sep 7, 2026
f539a42
fix(instrument-hooks): never fall back to the noop impl on Linux
moha-bekh Sep 7, 2026
48d370f
ci: add throwaway COD-3218 exec-harness check workflow
moha-bekh Sep 7, 2026
4edc228
ci: trigger the COD-3218 check on spike branch pushes
moha-bekh Sep 7, 2026
59c9756
ci: fix three wrong assertions in the COD-3218 check
moha-bekh Sep 7, 2026
11785e4
ci: sweep benchmark size to test the fixed-overhead model
moha-bekh Sep 7, 2026
d7d36be
revert: restore measure.rs and shared.rs to their state on main
moha-bekh Sep 7, 2026
b631658
fix(valgrind): track subprocesses for exec-harness runs
moha-bekh Sep 7, 2026
c43aaba
build(memtrack): move the portable half of the musl recipe into cargo…
moha-bekh Sep 16, 2026
21525d5
test(memtrack): resolve libc symbols in a child, not in the test process
moha-bekh Sep 16, 2026
6bb8b75
ci: cover both arches in the COD-3440 musl check, and scope its CFLAGS
moha-bekh Sep 16, 2026
728fdde
refactor(exec-harness,memtrack): move each CLI into its crate's lib
moha-bekh Sep 16, 2026
5c8807b
feat(runner): bundle exec-harness as a subcommand instead of download…
moha-bekh Sep 16, 2026
7368b6a
feat(runner): bundle memtrack too, and drop the download machinery
moha-bekh Sep 17, 2026
763c894
build(memtrack): put the whole musl recipe in the cargo config
moha-bekh Sep 17, 2026
36afa18
build(exec-harness,memtrack): stop releasing the component crates
moha-bekh Sep 17, 2026
a05e42e
ci: catch up with memtrack and exec-harness being bundled
moha-bekh Sep 17, 2026
6fdfd07
docs(contributing): drop the component-crate release process
moha-bekh Sep 17, 2026
3ce6305
test(executor): serialize the tests that share the global runner FIFOs
moha-bekh Sep 17, 2026
c87836c
test(local): fail loudly when a git setup command fails
moha-bekh Sep 17, 2026
2ec7cee
ci: drop the two throwaway spike workflows
moha-bekh Sep 17, 2026
069fbab
docs(exec-harness,executor): cut two comments that narrate the change
moha-bekh Sep 17, 2026
99827e6
fix(cli): stop honouring CODSPEED_SELF_EXE outside tests
moha-bekh Sep 17, 2026
c862935
fix(cli): dispatch bundled subcommands before any runner setup
moha-bekh Sep 17, 2026
8e2d7e3
ci: build both musl targets on every pull request
moha-bekh Sep 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# What a musl build of the bundled `memtrack` needs. Refs COD-3440.
#
# `libbpf-sys` vendors elfutils, whose `configure` unconditionally looks for
# `argp`, `obstack` and `fts`. musl ships none of them, so the checks fail and
# the build stops before it reaches libelf — even though a libelf-only build
# never calls into any of them. Pre-seeding autoconf's cache skips the three
# checks. "none required" is the answer a glibc host reaches on its own, so
# these are unconditional rather than per-target; on gnu they only save three
# `configure` probes.
#
# Applies to everything below: cargo does *not* override a variable already set
# in the environment unless the entry carries `force = true`. A shell exporting
# `CFLAGS` or `CPATH` therefore loses these values, and the musl build fails on
# a missing <argp.h> or <asm/types.h>. They are left unforced so a caller who
# sets them deliberately keeps them; no CI job does.
[env]
ac_cv_search_argp_parse = "none required"
ac_cv_search__obstack_free = "none required"
ac_cv_search_fts_close = "none required"

# Where the `argp.h` stub lives. `CPATH` rather than `CFLAGS -I<path>`, because
# `relative = true` can only make a *bare* path absolute and a `CFLAGS` value
# has nowhere to put the `-I`. It resolves against the project root — the
# directory holding `.cargo/`, not `.cargo/` itself.
#
# Not target-scoped, so the stub is on the gnu build's include path too; the
# header defers to the real <argp.h> whenever it detects glibc.
CPATH = { value = "crates/memtrack/musl", relative = true }

# libbpf includes <asm/unistd.h> and <asm/types.h>. Debian's musl-gcc runs with
# -nostdinc and only sees /usr/include/<arch>-linux-musl, so the kernel UAPI
# headers from linux-libc-dev have to be added back. `-idirafter` puts them last,
# behind musl's own, which is what keeps a glibc build unaffected.
#
# Both Debian multiarch triplets are listed because `[env]` cannot branch on the
# host architecture. A `-idirafter` naming a directory that does not exist is
# ignored silently, so the wrong one does nothing — as do both off Debian.
CFLAGS = "-idirafter /usr/include/x86_64-linux-gnu -idirafter /usr/include/aarch64-linux-gnu -idirafter /usr/include"

# rustc links with `-nodefaultlibs`, so gcc does not pull in libgcc. On aarch64,
# libbpf's C code needs the outline-atomic helpers (`__aarch64_ldadd4_sync` and
# friends) that live there, and the link fails without it. x86_64 has no such
# helpers and needs nothing.
[target.aarch64-unknown-linux-musl]
rustflags = ["-C", "link-arg=-lgcc"]
68 changes: 57 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ jobs:
- uses: ./.github/actions/install-rust
with:
components: rustfmt, clippy
# Building the runner builds memtrack's vendored libbpf-sys with it.
- uses: ./.github/actions/install-bpf-deps
if: matrix.os == 'ubuntu-latest'
Comment thread
moha-bekh marked this conversation as resolved.
- uses: j178/prek-action@bdca6f102f98e2b4c7029491a53dfd366469e33d # v2.0.4
with:
extra-args: --all-files
Expand All @@ -36,15 +39,10 @@ jobs:

- uses: ./.github/actions/install-rust

# Install memtrack for the memory integration tests
- uses: ./.github/actions/install-bpf-deps
- name: Install memtrack
run: |
cargo install --path crates/memtrack --locked

- name: Grant memtrack file capabilities
run: cargo r -- setup --mode memory

# No `setup --mode memory` here: the memory tests grant the capabilities
# themselves, pointed at the binary they actually re-exec.
- run: cargo test --all --exclude memtrack --exclude exec-harness

exec-harness-tests:
Expand All @@ -64,6 +62,7 @@ jobs:
with:
submodules: true
- uses: ./.github/actions/install-rust
- uses: ./.github/actions/install-bpf-deps
- name: Run tests
run: cargo run -- exec -m simulation,walltime,memory --warmup-time 0s --max-rounds 5 -- sleep 1

Expand All @@ -74,10 +73,6 @@ jobs:
with:
submodules: true
- uses: ./.github/actions/install-rust
- name: Install exec-harness
run: |
cargo install --path crates/exec-harness --locked

- name: Run tests
env:
# Profiling system commands (e.g. `ls`) with samply is not yet supported on MacOS
Expand Down Expand Up @@ -153,6 +148,56 @@ jobs:
mode: ${{ matrix.mode }}
run: cargo codspeed run -p runner-shared

# The released Linux artifacts are musl, and nothing else here builds them, so
# a break in the argp stub, the kernel-header paths or the aarch64 `-lgcc`
# link flag would otherwise surface for the first time in a release.
musl-build:
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-latest
target: x86_64-unknown-linux-musl
- runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-musl
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: true
- uses: ./.github/actions/install-rust
with:
cache-key: ${{ matrix.target }}
- uses: ./.github/actions/install-bpf-deps
- name: Install the musl toolchain
run: |
sudo apt-get install -y musl-tools linux-libc-dev
rustup target add "${{ matrix.target }}"

# No environment variables: the whole recipe lives in `.cargo/config.toml`,
# and a plain build is what proves it still stands on its own.
- name: Build
run: cargo build --bin codspeed --target "${{ matrix.target }}"

- name: Assert the artifact is static and carries both subcommands
run: |
BIN=target/${{ matrix.target }}/debug/codspeed
file "$BIN"
# Asserted through readelf rather than a `file` string: rustc emits a
# static-PIE for x86_64 musl, which `file` spells differently from the
# aarch64 one. What matters is that nothing is loaded at runtime.
if readelf -d "$BIN" 2>/dev/null | grep -qE 'NEEDED|RPATH|RUNPATH'; then
echo "the musl binary has a dynamic dependency"
exit 1
fi
if readelf -lW "$BIN" 2>/dev/null | grep -q 'INTERP'; then
echo "the musl binary requests a dynamic loader"
exit 1
fi
# These answer only if the two CLIs really are linked in.
"$BIN" exec-harness --version
"$BIN" memtrack --version

check:
runs-on: ubuntu-latest
if: always()
Expand All @@ -163,6 +208,7 @@ jobs:
- basic-run-test
- macos-basic-run-test
- bpf-tests
- musl-build
- benchmarks
steps:
- uses: re-actors/alls-green@release/v1
Expand Down
52 changes: 13 additions & 39 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,50 +10,28 @@ prek install

## Release Process

This repository is a Cargo workspace containing multiple crates. The release process differs depending on which crate you're releasing.
This repository is a Cargo workspace containing multiple crates, but only one of them is released: the main runner. Everything else is linked into its binary.

### Workspace Structure

- **`codspeed-runner`**: The main CLI binary (`codspeed`)
- **`memtrack`**: Memory tracking binary (`codspeed-memtrack`)
- **`exec-harness`**: Execution harness binary
- **`memtrack`**: Memory tracker, built into `codspeed` and reached as `codspeed memtrack`
- **`exec-harness`**: Execution harness, built into `codspeed` and reached as `codspeed exec-harness`
- **`runner-shared`**: Shared library used by other crates

### Releasing Support Crates (memtrack, exec-harness, runner-shared)
`memtrack` and `exec-harness` are **not released on their own**. They are linked into the
`codspeed` binary and invoked as hidden subcommands, so one tag produces one artifact set and
there is no version for the runner to be out of step with. Their `[[bin]]` targets remain for
development and for the tests, which build them to exercise the standalone path.

For any crate other than the main runner:

```bash
cargo release -p <PACKAGE_NAME> --execute <VERSION_BUMP>
```

Where `<VERSION_BUMP>` is one of: `alpha`, `beta`, `patch`, `minor`, or `major`.

**Examples:**

```bash
# Release a new patch version of memtrack
cargo release -p memtrack --execute patch

# Release a beta version of exec-harness
cargo release -p exec-harness --execute beta
```

#### Post-Release: Update Version References

After releasing `memtrack` or `exec-harness`, you **must** update the version references in the runner code:

1. **For memtrack**: Update the `MEMTRACK_INSTALLER` pin record in `src/binary_pins.rs` (see [Pinned binary hashes](#pinned-binary-hashes) below).

2. **For exec-harness**: Update the `EXEC_HARNESS_INSTALLER` pin record in `src/binary_pins.rs`.

These constants are used by the runner to download and install the correct versions of the binaries from GitHub releases.
Both still keep their own `version` in `Cargo.toml` — that is what
`codspeed exec-harness --version` reports — but bumping it is a plain edit, not a release.
Comment on lines +22 to +28

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

deslop + simplify, way too verbose


### Pinned binary hashes

Every binary the runner downloads at install time is SHA-256-pinned. The pins live in two places:

- **`src/binary_pins.rs`** — the patched valgrind `.deb`, the memtrack installer, the exec-harness installer, and the mongo-tracer installer. Each artifact keeps its version, URL template, and hash together in a pin record.
- **`src/binary_pins.rs`** — the patched valgrind `.deb` and the mongo-tracer installer. Each artifact keeps its version, URL template, and hash together in a pin record.
- **`src/executor/helpers/introspected_golang/go.sh`** — the go-runner installer published by [CodSpeedHQ/codspeed-go](https://github.com/CodSpeedHQ/codspeed-go), one `<version> <sha256>` row per release in the `GO_RUNNER_INSTALLER_SHA256S` table. `DEFAULT_GO_RUNNER_VERSION` (just below the table) selects the row used by default.

When you bump a pinned version (or add a new go-runner row), update the matching pin record / table row with the new version and its SHA-256.
Expand Down Expand Up @@ -84,16 +62,12 @@ These tests also run in CI, but running them locally before opening the PR avoid

### Releasing the Main Runner

The main runner (`codspeed-runner`) should be released after ensuring all dependency versions are correct.
The main runner (`codspeed-runner`) is the only crate that is released.

#### Pre-Release Check

**Verify binary version references**: Check that version constants in the runner code match the released versions:

- `MEMTRACK_VERSION` in `src/binary_pins.rs`
- `EXEC_HARNESS_VERSION` in `src/binary_pins.rs`

Also confirm the SHA-256 entries in the pin records in `src/binary_pins.rs` match the released artifacts.
Confirm the SHA-256 entries in the pin records in `src/binary_pins.rs` match the released
artifacts they point at.

#### Release Command

Expand Down
2 changes: 0 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 18 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ samply = { path = "crates/samply-codspeed/samply" }
[target.'cfg(target_os = "linux")'.dependencies]
procfs = "0.18"
caps = "0.5"
memtrack = { path = "crates/memtrack", default-features = false }
# Default features on purpose: `ebpf` carries the tracker itself, which the
# bundled `memtrack` subcommand needs, not just the IPC types.
memtrack = { path = "crates/memtrack" }
ipc-channel = { workspace = true }

[dev-dependencies]
Expand Down Expand Up @@ -139,3 +141,18 @@ targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-musl", "x86_64-unknown
binaries.aarch64-apple-darwin = ["codspeed"]
binaries.aarch64-unknown-linux-musl = ["codspeed"]
binaries.x86_64-unknown-linux-musl = ["codspeed"]

# Linking memtrack in pulls its vendored libbpf/elfutils build into this
# package, so releasing the CLI needs memtrack's build toolchain.
[package.metadata.dist.dependencies.apt]
build-essential = "*"
pkgconf = "*"
zlib1g-dev = "*"
libbpf-dev = "*"
musl-tools = "*"
linux-libc-dev = "*"

# Required for the vendored feature
autopoint = "*"
bison = "*"
flex = "*"
9 changes: 3 additions & 6 deletions crates/exec-harness/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ serde = { workspace = true }
humantime = "2.3"
runner-shared = { path = "../runner-shared" }
tempfile = { workspace = true }
object = { workspace = true }

[build-dependencies]
cc = "1"

[package.metadata.dist]
targets = ["aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"]
# Deliberately no `[package.metadata.dist]`: exec-harness ships inside the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Remove this comment, we don't need to document the non-existence of something ^^

(also, this would be a good test/use-case for the deslop skill)

# `codspeed` binary and is not released on its own. The `[[bin]]` stays for
# development and for the tests, which exercise the standalone path.
Loading