diff --git a/.github/actions/rust-install/action.yml b/.github/actions/rust-install/action.yml new file mode 100644 index 0000000..fa03f72 --- /dev/null +++ b/.github/actions/rust-install/action.yml @@ -0,0 +1,10 @@ +name: "Install Rust toolchain" +description: "Install the Rust toolchain from rust-toolchain.toml and cache cargo artifacts" + +runs: + using: "composite" + steps: + - name: Install rust-toolchain.toml + shell: bash + run: rustup show + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bff068..4a1e4ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: with: submodules: true - uses: actions/setup-go@v5 - - uses: moonrepo/setup-rust@v1 + - uses: ./.github/actions/rust-install - uses: pre-commit/action@v3.0.1 with: extra_args: --all-files @@ -36,7 +36,7 @@ jobs: go-version: ${{ matrix.go-version }} - uses: taiki-e/install-action@cargo-nextest - - uses: moonrepo/setup-rust@v1 + - uses: ./.github/actions/rust-install - run: | cd go-runner cargo nextest run --all @@ -53,9 +53,7 @@ jobs: with: submodules: true - uses: actions/setup-go@v5 - - uses: moonrepo/setup-rust@v1 - with: - cache-target: release + - uses: ./.github/actions/rust-install - name: Run the benchmarks uses: CodSpeedHQ/action@main @@ -66,6 +64,23 @@ jobs: working-directory: example run: cargo r --release --manifest-path ../go-runner/Cargo.toml -- test -bench=. ${{ matrix.target }} + example-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: actions/setup-go@v6 + with: + go-version: '1.25.x' + - uses: ./.github/actions/rust-install + + - name: Run the benchmarks + run: cargo r --release --manifest-path ../go-runner/Cargo.toml -- test -bench=. example + working-directory: example + env: + CODSPEED_GO_PKG_VERSION: ${{ github.head_ref || github.ref_name }} + go-runner-benchmarks: runs-on: codspeed-macro steps: @@ -73,12 +88,9 @@ jobs: with: lfs: true submodules: true - - name: Setup rust toolchain, cache and cargo-codspeed binary - uses: moonrepo/setup-rust@v1 - with: - channel: stable - cache-target: release - bins: cargo-codspeed + - uses: ./.github/actions/rust-install + - name: Install cargo-codspeed + run: cargo install cargo-codspeed - uses: actions/setup-go@v5 with: @@ -100,6 +112,7 @@ jobs: - lint - tests - compat-integration-test-walltime + - example-macos - go-runner-benchmarks steps: - uses: re-actors/alls-green@release/v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index beed208..d9aa406 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,7 +56,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: persist-credentials: false submodules: recursive @@ -64,9 +64,9 @@ jobs: # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash - run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.29.0/cargo-dist-installer.sh | sh" + run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.31.0/cargo-dist-installer.sh | sh" - name: Cache dist - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: cargo-dist-cache path: ~/.cargo/bin/dist @@ -82,7 +82,7 @@ jobs: cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: artifacts-plan-dist-manifest path: plan-dist-manifest.json @@ -116,7 +116,7 @@ jobs: - name: enable windows longpaths run: | git config --global core.longpaths true - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: persist-credentials: false submodules: recursive @@ -131,7 +131,7 @@ jobs: run: ${{ matrix.install_dist.run }} # Get the dist-manifest - name: Fetch local artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: pattern: artifacts-* path: target/distrib/ @@ -158,7 +158,7 @@ jobs: cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: artifacts-build-local-${{ join(matrix.targets, '_') }} path: | @@ -175,19 +175,19 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: persist-credentials: false submodules: recursive - name: Install cached dist - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: pattern: artifacts-* path: target/distrib/ @@ -205,7 +205,7 @@ jobs: cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: artifacts-build-global path: | @@ -217,27 +217,27 @@ jobs: - plan - build-local-artifacts - build-global-artifacts - # Only run if we're "publishing", and only if local and global didn't fail (skipped is fine) - if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} + # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) + if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "ubuntu-22.04" outputs: val: ${{ steps.host.outputs.manifest }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: persist-credentials: false submodules: recursive - name: Install cached dist - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: pattern: artifacts-* path: target/distrib/ @@ -250,14 +250,14 @@ jobs: cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: # Overwrite the previous copy name: artifacts-dist-manifest path: dist-manifest.json # Create a GitHub Release while uploading all files to it - name: "Download GitHub Artifacts" - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: pattern: artifacts-* path: artifacts @@ -290,7 +290,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: persist-credentials: false submodules: recursive diff --git a/dist-workspace.toml b/dist-workspace.toml index a1b1b77..511008a 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -6,13 +6,13 @@ members = ["cargo:."] # Whether to consider the binaries in a package for distribution (defaults true) dist = true # The preferred dist version to use in CI (Cargo.toml SemVer syntax) -cargo-dist-version = "0.29.0" +cargo-dist-version = "0.31.0" # CI backends to support ci = "github" # The installers to generate for each app installers = ["shell"] # Target platforms to build apps for (Rust target-triple syntax) -targets = ["aarch64-unknown-linux-musl", "x86_64-unknown-linux-musl"] +targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-musl", "x86_64-unknown-linux-musl"] # Path that installers should place binaries in install-path = "CARGO_HOME" # Whether to install an updater program diff --git a/go-runner/overlay/codspeed.go b/go-runner/overlay/codspeed.go index cd04335..6e68b33 100644 --- a/go-runner/overlay/codspeed.go +++ b/go-runner/overlay/codspeed.go @@ -131,6 +131,12 @@ func ensureBenchmarkIsStopped(b *B) { } func (b *B) AddBenchmarkMarkers(endTimestamp uint64) { + // When instrument hooks are stubs (e.g. on macOS), CurrentTimestamp returns 0. + // Skip marker collection in that case — timestamps are only meaningful when instrumented. + if b.startTimestamp == 0 && endTimestamp == 0 { + return + } + if b.startTimestamp >= endTimestamp { // This should never happen, unless we have a bug in the timer logic. panic(fmt.Sprintf("Invalid benchmark timestamps: start timestamp (%d) is greater than or equal to end timestamp (%d)", b.startTimestamp, endTimestamp)) diff --git a/go-runner/overlay/instrument-hooks.go b/go-runner/overlay/instrument-hooks.go index 1a254ca..cd9e753 100644 --- a/go-runner/overlay/instrument-hooks.go +++ b/go-runner/overlay/instrument-hooks.go @@ -3,10 +3,6 @@ package testing /* #cgo CFLAGS: -I@@INSTRUMENT_HOOKS_DIR@@/includes -Wno-format -Wno-format-security #include "@@INSTRUMENT_HOOKS_DIR@@/dist/core.c" - -#define MARKER_TYPE_BENCHMARK_START c_MARKER_TYPE_BENCHMARK_START__247 -#define MARKER_TYPE_BENCHMARK_END c_MARKER_TYPE_BENCHMARK_END__248 -typedef struct instrument_hooks_InstrumentHooks__547 InstrumentHooks; */ import "C" import ( @@ -51,7 +47,7 @@ func (i *InstrumentHooks) SetIntegration(name, version string) { defer C.free(unsafe.Pointer(nameC)) defer C.free(unsafe.Pointer(versionC)) - C.instrument_hooks_set_integration(i.hooks, (*C.uint8_t)(unsafe.Pointer(nameC)), (*C.uint8_t)(unsafe.Pointer(versionC))) + C.instrument_hooks_set_integration(i.hooks, nameC, versionC) } func (i *InstrumentHooks) StartBenchmark() { @@ -73,7 +69,7 @@ func (i *InstrumentHooks) SetExecutedBenchmark(pid uint32, name string) { nameC := C.CString(name) defer C.free(unsafe.Pointer(nameC)) - C.instrument_hooks_set_executed_benchmark(i.hooks, C.uint(pid), (*C.uint8_t)(unsafe.Pointer(nameC))) + C.instrument_hooks_set_executed_benchmark(i.hooks, C.uint(pid), nameC) } func (i *InstrumentHooks) IsInstrumented() bool { @@ -107,7 +103,7 @@ func (i *InstrumentHooks) SetEnvironment(sectionName, key, value string) { defer C.free(unsafe.Pointer(keyC)) defer C.free(unsafe.Pointer(valueC)) - C.instrument_hooks_set_environment(i.hooks, (*C.uint8_t)(unsafe.Pointer(sectionNameC)), (*C.uint8_t)(unsafe.Pointer(keyC)), (*C.uint8_t)(unsafe.Pointer(valueC))) + C.instrument_hooks_set_environment(i.hooks, sectionNameC, keyC, valueC) } func (i *InstrumentHooks) WriteEnvironment(pid uint32) { diff --git a/go-runner/src/runner/overlay/instrument_hooks.rs b/go-runner/src/runner/overlay/instrument_hooks.rs index ecc1c33..ba97551 100644 --- a/go-runner/src/runner/overlay/instrument_hooks.rs +++ b/go-runner/src/runner/overlay/instrument_hooks.rs @@ -6,11 +6,23 @@ use tar::Archive; use tempfile::TempDir; const INSTRUMENT_HOOKS_REPO: &str = "CodSpeedHQ/instrument-hooks"; -const INSTRUMENT_HOOKS_COMMIT: &str = "0c971823b17cb5a3bbd0cce4411cbee2c6fe4317"; +const INSTRUMENT_HOOKS_COMMIT: &str = "662fbd3b90eed7b52018bc8b9b6bf493e102bd6f"; /// Get the instrument-hooks directory, downloading if necessary /// Downloads to /tmp/codspeed-instrument-hooks-{commit}/ pub fn download_instrument_hooks(temp_dir: &TempDir) -> Result { + // Allow overriding with a local path for development + if let Ok(local_path) = std::env::var("CODSPEED_INSTRUMENT_HOOKS_DIR") { + let path = PathBuf::from(local_path); + ensure!( + path.exists(), + "CODSPEED_INSTRUMENT_HOOKS_DIR path does not exist: {:?}", + path + ); + debug!("Using local instrument-hooks at {:?}", path); + return Ok(path); + } + let hooks_dir = temp_dir .path() .join(format!("instrument-hooks-{}", INSTRUMENT_HOOKS_COMMIT));