Skip to content

Commit 1e71589

Browse files
feat(codspeed): collect Rust toolchain environment at build time
Capture rustc and cargo version information during compilation via build.rs and register it through the instrument-hooks environment API at runtime. This enables tracking which toolchain was used to compile benchmarks. - Update instrument-hooks submodule to get set_environment/write_environment API - Regenerate FFI bindings for new environment functions - Add set_environment/write_environment wrappers to InstrumentHooks - Collect rustc (version, host, release, LLVM version) and cargo version at build time via env vars, register them during InstrumentHooks init - Always initialize InstrumentHooks (even in Valgrind mode) for env collection Generated with AI Agent (Claude Code)
1 parent 163ca0d commit 1e71589

5 files changed

Lines changed: 151 additions & 8 deletions

File tree

crates/codspeed/build.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ fn main() {
55
println!("cargo:rerun-if-changed=instrument-hooks/includes/core.h");
66
println!("cargo:rerun-if-changed=build.rs");
77

8+
collect_rustc_info();
9+
collect_cargo_info();
10+
811
let mut build = cc::Build::new();
912
build
1013
.flag("-std=c11")
@@ -44,3 +47,55 @@ fn main() {
4447
}
4548
}
4649
}
50+
51+
/// Collect rustc toolchain info at build time and expose as env vars.
52+
/// These env var names must be kept in sync with `src/instrument_hooks/mod.rs`.
53+
fn collect_rustc_info() {
54+
let Ok(output) = std::process::Command::new("rustc")
55+
.args(["--version", "--verbose"])
56+
.output()
57+
else {
58+
return;
59+
};
60+
if !output.status.success() {
61+
return;
62+
}
63+
64+
let stdout = String::from_utf8_lossy(&output.stdout);
65+
for line in stdout.lines() {
66+
if let Some(rest) = line.strip_prefix("rustc ") {
67+
println!("cargo:rustc-env=CODSPEED_RUSTC_VERSION={rest}");
68+
} else if let Some((key, value)) = line.split_once(':') {
69+
let key = key.trim();
70+
let value = value.trim();
71+
let env_key = match key {
72+
"host" => Some("CODSPEED_RUSTC_HOST"),
73+
"release" => Some("CODSPEED_RUSTC_RELEASE"),
74+
"LLVM version" => Some("CODSPEED_RUSTC_LLVM_VERSION"),
75+
_ => None,
76+
};
77+
if let Some(env_key) = env_key {
78+
println!("cargo:rustc-env={env_key}={value}");
79+
}
80+
}
81+
}
82+
}
83+
84+
/// Collect cargo version at build time and expose as an env var.
85+
/// This env var name must be kept in sync with `src/instrument_hooks/mod.rs`.
86+
fn collect_cargo_info() {
87+
let Ok(output) = std::process::Command::new("cargo")
88+
.arg("--version")
89+
.output()
90+
else {
91+
return;
92+
};
93+
if !output.status.success() {
94+
return;
95+
}
96+
97+
let stdout = String::from_utf8_lossy(&output.stdout);
98+
if let Some(rest) = stdout.trim().strip_prefix("cargo ") {
99+
println!("cargo:rustc-env=CODSPEED_CARGO_VERSION={rest}");
100+
}
101+
}

crates/codspeed/src/codspeed.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,20 @@ impl CodSpeed {
3636
pub fn new() -> Self {
3737
use crate::instrument_hooks::InstrumentHooks;
3838
let instrumentation_status = {
39-
// We completely bypass InstrumentHooks if we detect Valgrind via inline assembly
39+
// Always initialize InstrumentHooks for environment collection,
40+
// even when using Valgrind for the actual measurements.
41+
let hooks_instance = InstrumentHooks::instance();
42+
43+
// We bypass InstrumentHooks if we detect Valgrind via inline assembly
4044
// Until we can reliably get rid of the inline assembly without causing breaking
4145
// changes in CPU simulation measurements by switching to InstrumentHooks only, we need
4246
// to keep this separation.
4347
if measurement::is_instrumented() {
4448
InstrumentationStatus::Valgrind
49+
} else if hooks_instance.is_instrumented() {
50+
InstrumentationStatus::InstrumentHooks(hooks_instance)
4551
} else {
46-
let hooks_instance = InstrumentHooks::instance();
47-
if hooks_instance.is_instrumented() {
48-
InstrumentationStatus::InstrumentHooks(hooks_instance)
49-
} else {
50-
InstrumentationStatus::NotInstrumented
51-
}
52+
InstrumentationStatus::NotInstrumented
5253
}
5354
};
5455

crates/codspeed/src/instrument_hooks/bindings.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,14 @@ pub type instrument_hooks_feature_t = ::std::os::raw::c_uint;
5858
extern "C" {
5959
pub fn instrument_hooks_set_feature(feature: instrument_hooks_feature_t, enabled: bool);
6060
}
61+
extern "C" {
62+
pub fn instrument_hooks_set_environment(
63+
arg1: *mut InstrumentHooks,
64+
section_name: *const ::std::os::raw::c_char,
65+
key: *const ::std::os::raw::c_char,
66+
value: *const ::std::os::raw::c_char,
67+
) -> u8;
68+
}
69+
extern "C" {
70+
pub fn instrument_hooks_write_environment(arg1: *mut InstrumentHooks, pid: u32) -> u8;
71+
}

crates/codspeed/src/instrument_hooks/mod.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,39 @@ mod linux_impl {
3535
instance
3636
.set_integration("codspeed-rust", env!("CARGO_PKG_VERSION"))
3737
.expect("Failed to set integration");
38+
instance.register_toolchain_environment();
3839
instance
3940
})
4041
}
4142

43+
/// Registers Rust toolchain information (captured at build time) via the
44+
/// environment API.
45+
///
46+
/// The env var names here must be kept in sync with `build.rs`.
47+
fn register_toolchain_environment(&self) {
48+
const SECTION: &str = "Rust";
49+
50+
if let Some(v) = option_env!("CODSPEED_RUSTC_VERSION") {
51+
let _ = self.set_environment(SECTION, "rustc", v);
52+
}
53+
if let Some(v) = option_env!("CODSPEED_RUSTC_HOST") {
54+
let _ = self.set_environment(SECTION, "host", v);
55+
}
56+
if let Some(v) = option_env!("CODSPEED_RUSTC_RELEASE") {
57+
let _ = self.set_environment(SECTION, "release", v);
58+
}
59+
if let Some(v) = option_env!("CODSPEED_RUSTC_LLVM_VERSION") {
60+
let _ = self.set_environment(SECTION, "LLVM version", v);
61+
}
62+
if let Some(v) = option_env!("CODSPEED_CARGO_VERSION") {
63+
let _ = self.set_environment(SECTION, "cargo", v);
64+
}
65+
66+
if let Err(e) = self.write_environment() {
67+
eprintln!("Warning: failed to write environment info: {e}");
68+
}
69+
}
70+
4271
#[inline(always)]
4372
pub fn is_instrumented(&self) -> bool {
4473
unsafe { ffi::instrument_hooks_is_instrumented(self.0) }
@@ -131,6 +160,40 @@ mod linux_impl {
131160
}
132161
}
133162

163+
pub fn set_environment(
164+
&self,
165+
section_name: &str,
166+
key: &str,
167+
value: &str,
168+
) -> Result<(), u8> {
169+
let c_section = CString::new(section_name).map_err(|_| 1u8)?;
170+
let c_key = CString::new(key).map_err(|_| 1u8)?;
171+
let c_value = CString::new(value).map_err(|_| 1u8)?;
172+
let result = unsafe {
173+
ffi::instrument_hooks_set_environment(
174+
self.0,
175+
c_section.as_ptr(),
176+
c_key.as_ptr(),
177+
c_value.as_ptr(),
178+
)
179+
};
180+
if result == 0 {
181+
Ok(())
182+
} else {
183+
Err(result)
184+
}
185+
}
186+
187+
pub fn write_environment(&self) -> Result<(), u8> {
188+
let pid = std::process::id();
189+
let result = unsafe { ffi::instrument_hooks_write_environment(self.0, pid) };
190+
if result == 0 {
191+
Ok(())
192+
} else {
193+
Err(result)
194+
}
195+
}
196+
134197
pub fn disable_callgrind_markers() {
135198
unsafe {
136199
ffi::instrument_hooks_set_feature(
@@ -187,6 +250,19 @@ mod other_impl {
187250
0
188251
}
189252

253+
pub fn set_environment(
254+
&self,
255+
_section_name: &str,
256+
_key: &str,
257+
_value: &str,
258+
) -> Result<(), u8> {
259+
Ok(())
260+
}
261+
262+
pub fn write_environment(&self) -> Result<(), u8> {
263+
Ok(())
264+
}
265+
190266
pub fn disable_callgrind_markers() {}
191267
}
192268
}

0 commit comments

Comments
 (0)