diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57342c8f..d1c9c730 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,32 +21,53 @@ jobs: with: extra_args: --all-files + - uses: taiki-e/install-action@v2 + with: + tool: bindgen-cli@0.72.1 + - name: Check if bindings are up-to-date + working-directory: crates/codspeed/src/instrument_hooks + run: | + ./update-bindings.sh + + if ! git diff --exit-code bindings.rs; then + echo "Error: FFI bindings are out of date!" + exit 1 + fi + test-codspeed: + runs-on: ${{ matrix.job.os }} strategy: + fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - runs-on: ${{ matrix.os }} + job: + - { os: ubuntu-latest, target: arm-unknown-linux-gnueabihf } + - { os: ubuntu-latest, target: aarch64-unknown-linux-musl } + - { os: ubuntu-latest, target: i686-unknown-linux-gnu } + - { os: ubuntu-latest, target: i686-unknown-linux-musl } + - { os: ubuntu-latest, target: x86_64-unknown-linux-gnu } + - { os: ubuntu-latest, target: x86_64-unknown-linux-musl } + - { os: macos-latest, target: aarch64-apple-darwin } + - { os: macos-latest, target: x86_64-apple-darwin } + - { os: windows-latest, target: i686-pc-windows-msvc } + - { os: windows-latest, target: x86_64-pc-windows-msvc } + - { os: windows-latest, target: aarch64-pc-windows-msvc } steps: - uses: actions/checkout@v4 with: submodules: true + - uses: moonrepo/setup-rust@v1 with: - cache-target: release + targets: ${{ matrix.job.target }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - run: cargo test -p codspeed - if: runner.os == 'Linux' + - uses: taiki-e/install-action@v2 + with: + tool: cross@0.2.5 - # We don't support Windows/MacOS for now, due to libclang not found on some runners: - - run: LIBCLANG_PATH= cargo test -p codspeed - if: runner.os == 'macOS' - - run: | - $env:LIBCLANG_PATH="" - cargo test -p codspeed - if: runner.os == 'Windows' - shell: powershell + - name: Build codspeed + run: cross build -p codspeed --target ${{ matrix.job.target }} msrv-check: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 27d4c025..b3448dbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -386,26 +386,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dfdb4953a096c551ce9ace855a604d702e6e62d77fac690575ae347571717f5" -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.6.0", - "cexpr", - "clang-sys", - "itertools 0.10.5", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -521,15 +501,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -569,17 +540,6 @@ dependencies = [ "half", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.5.17" @@ -626,7 +586,6 @@ name = "codspeed" version = "4.0.4" dependencies = [ "anyhow", - "bindgen", "cc", "colored", "glob", @@ -1230,16 +1189,6 @@ version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link", -] - [[package]] name = "libmimalloc-sys" version = "0.1.39" @@ -1286,12 +1235,6 @@ dependencies = [ "libmimalloc-sys", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.7.4" @@ -1313,16 +1256,6 @@ dependencies = [ "libc", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -1505,16 +1438,6 @@ dependencies = [ "termtree", ] -[[package]] -name = "prettyplease" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" -dependencies = [ - "proc-macro2", - "syn", -] - [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -1651,12 +1574,6 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - [[package]] name = "rustix" version = "0.37.27" @@ -2111,12 +2028,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-link" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" - [[package]] name = "windows-sys" version = "0.48.0" diff --git a/crates/codspeed/Cargo.toml b/crates/codspeed/Cargo.toml index 8dfdfd7a..cf810367 100644 --- a/crates/codspeed/Cargo.toml +++ b/crates/codspeed/Cargo.toml @@ -36,5 +36,4 @@ harness = false tempfile = { workspace = true } [build-dependencies] -bindgen = "0.72.1" cc = "1.0" diff --git a/crates/codspeed/build.rs b/crates/codspeed/build.rs index 52f0256c..72ff2677 100644 --- a/crates/codspeed/build.rs +++ b/crates/codspeed/build.rs @@ -1,46 +1,46 @@ -use std::{env, path::PathBuf}; - fn main() { + println!("cargo:rustc-check-cfg=cfg(use_instrument_hooks)"); + println!("cargo:rerun-if-changed=instrument-hooks/dist/core.c"); println!("cargo:rerun-if-changed=instrument-hooks/includes/core.h"); println!("cargo:rerun-if-changed=build.rs"); - if cfg!(not(target_os = "linux")) { - // The instrument-hooks library is only supported on Linux. - return; - } - let mut build = cc::Build::new(); build - .flag("-std=gnu17") + .flag("-std=c11") .file("instrument-hooks/dist/core.c") .include("instrument-hooks/includes") + // We generated the C code from Zig, which contains some warnings + // that can be safely ignored. + .flag("-Wno-format") + .flag("-Wno-format-security") + .flag("-Wno-unused-but-set-variable") + .flag("-Wno-unused-const-variable") + .flag("-Wno-type-limits") + .flag("-Wno-uninitialized") + // Ignore warnings when cross-compiling: + .flag("-Wno-overflow") + .flag("-Wno-unused-function") + .flag("-Wno-constant-conversion") + .flag("-Wno-incompatible-pointer-types") + // Disable warnings, as we will have lots of them .warnings(false) .extra_warnings(false) - .cargo_warnings(false); + .cargo_warnings(false) + .opt_level(3); let result = build.try_compile("instrument_hooks"); - if let Err(e) = result { - let compiler = build.try_get_compiler().expect("Failed to get C compiler"); - - eprintln!("\n\nERROR: Failed to compile instrument-hooks native library with cc-rs. Ensure you have an up-to-date C compiler installed."); - eprintln!("Compiler information: {compiler:?}"); - eprintln!("Compilation error: {e}"); - - std::process::exit(1); + match result { + Ok(_) => println!("cargo:rustc-cfg=use_instrument_hooks"), + Err(e) => { + let compiler = build.try_get_compiler().expect("Failed to get C compiler"); + + eprintln!("\n\nWARNING: Failed to compile instrument-hooks native library with cc-rs."); + eprintln!("The library will still compile, but instrument-hooks functionality will be disabled."); + eprintln!("Compiler information: {compiler:?}"); + eprintln!("Compilation error: {e}\n"); + + println!("cargo:warning=Failed to compile instrument-hooks native library with cc-rs. Continuing with noop implementation."); + } } - - let bindings = bindgen::Builder::default() - .header("instrument-hooks/includes/core.h") - // Tell cargo to invalidate the built crate whenever any of the - // included header files changed. - .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) - .generate() - .expect("Unable to generate bindings"); - - // Write the bindings to the $OUT_DIR/bindings.rs file. - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("Couldn't write bindings!"); } diff --git a/crates/codspeed/src/instrument_hooks/bindings.rs b/crates/codspeed/src/instrument_hooks/bindings.rs new file mode 100644 index 00000000..4a29394f --- /dev/null +++ b/crates/codspeed/src/instrument_hooks/bindings.rs @@ -0,0 +1,60 @@ +/* automatically generated by rust-bindgen 0.72.1 */ + +pub const MARKER_TYPE_SAMPLE_START: u32 = 0; +pub const MARKER_TYPE_SAMPLE_END: u32 = 1; +pub const MARKER_TYPE_BENCHMARK_START: u32 = 2; +pub const MARKER_TYPE_BENCHMARK_END: u32 = 3; +pub type InstrumentHooks = *mut u64; +extern "C" { + pub fn instrument_hooks_init() -> *mut InstrumentHooks; +} +extern "C" { + pub fn instrument_hooks_deinit(arg1: *mut InstrumentHooks); +} +extern "C" { + pub fn instrument_hooks_is_instrumented(arg1: *mut InstrumentHooks) -> bool; +} +extern "C" { + pub fn instrument_hooks_start_benchmark(arg1: *mut InstrumentHooks) -> u8; +} +extern "C" { + pub fn instrument_hooks_stop_benchmark(arg1: *mut InstrumentHooks) -> u8; +} +extern "C" { + pub fn instrument_hooks_set_executed_benchmark( + arg1: *mut InstrumentHooks, + pid: i32, + uri: *const ::std::os::raw::c_char, + ) -> u8; +} +extern "C" { + pub fn instrument_hooks_executed_benchmark( + arg1: *mut InstrumentHooks, + pid: i32, + uri: *const ::std::os::raw::c_char, + ) -> u8; +} +extern "C" { + pub fn instrument_hooks_set_integration( + arg1: *mut InstrumentHooks, + name: *const ::std::os::raw::c_char, + version: *const ::std::os::raw::c_char, + ) -> u8; +} +extern "C" { + pub fn instrument_hooks_add_marker( + arg1: *mut InstrumentHooks, + pid: u32, + marker_type: u8, + timestamp: u64, + ) -> u8; +} +extern "C" { + pub fn instrument_hooks_current_timestamp() -> u64; +} +pub const instrument_hooks_feature_t_FEATURE_DISABLE_CALLGRIND_MARKERS: instrument_hooks_feature_t = + 0; +pub type instrument_hooks_feature_t = ::std::os::raw::c_uint; +extern "C" { + pub fn instrument_hooks_set_feature(feature: instrument_hooks_feature_t, enabled: bool); +} diff --git a/crates/codspeed/src/instrument_hooks/ffi.rs b/crates/codspeed/src/instrument_hooks/ffi.rs index 04d76a43..db7350ff 100644 --- a/crates/codspeed/src/instrument_hooks/ffi.rs +++ b/crates/codspeed/src/instrument_hooks/ffi.rs @@ -3,4 +3,11 @@ #![allow(non_snake_case)] #![allow(unused)] -include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +// Use pre-generated bindings instead of generating at build time so that downstream +// users don't need to install `libclang`. +// +// To regenerate bindings, run: +// ``` +// ./update-bindings.sh +// ``` +include!("bindings.rs"); diff --git a/crates/codspeed/src/instrument_hooks/mod.rs b/crates/codspeed/src/instrument_hooks/mod.rs index 6596390c..2c47ebb4 100644 --- a/crates/codspeed/src/instrument_hooks/mod.rs +++ b/crates/codspeed/src/instrument_hooks/mod.rs @@ -1,9 +1,8 @@ -#[cfg(target_os = "linux")] +#[cfg(use_instrument_hooks)] mod ffi; -#[cfg(target_os = "linux")] +#[cfg(use_instrument_hooks)] mod linux_impl { - use nix::sys::time::TimeValLike; use super::ffi; use std::ffi::CString; @@ -116,9 +115,19 @@ mod linux_impl { #[inline(always)] pub fn current_timestamp() -> u64 { - nix::time::clock_gettime(nix::time::ClockId::CLOCK_MONOTONIC) - .expect("Failed to get current time") - .num_nanoseconds() as u64 + #[cfg(target_os = "linux")] + { + use nix::sys::time::TimeValLike; + + nix::time::clock_gettime(nix::time::ClockId::CLOCK_MONOTONIC) + .expect("Failed to get current time") + .num_nanoseconds() as u64 + } + + #[cfg(not(target_os = "linux"))] + unsafe { + ffi::instrument_hooks_current_timestamp() + } } } @@ -131,7 +140,7 @@ mod linux_impl { } } -#[cfg(not(target_os = "linux"))] +#[cfg(not(use_instrument_hooks))] mod other_impl { pub struct InstrumentHooks; @@ -169,10 +178,10 @@ mod other_impl { } } -#[cfg(target_os = "linux")] +#[cfg(use_instrument_hooks)] pub use linux_impl::InstrumentHooks; -#[cfg(not(target_os = "linux"))] +#[cfg(not(use_instrument_hooks))] pub use other_impl::InstrumentHooks; #[cfg(test)] diff --git a/crates/codspeed/src/instrument_hooks/update-bindings.sh b/crates/codspeed/src/instrument_hooks/update-bindings.sh new file mode 100755 index 00000000..0b7beed4 --- /dev/null +++ b/crates/codspeed/src/instrument_hooks/update-bindings.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +bindgen ../../instrument-hooks/includes/core.h \ + -o bindings.rs \ + --rust-target 1.74 \ + --allowlist-function "instrument_hooks_.*" \ + --allowlist-var "MARKER_TYPE_.*"