Skip to content

Commit 558fd82

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 558fd82

File tree

5 files changed

+147
-8
lines changed

5 files changed

+147
-8
lines changed

crates/codspeed/build.rs

Lines changed: 54 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,54 @@ 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+
"release" => Some("CODSPEED_RUSTC_RELEASE"),
73+
"LLVM version" => Some("CODSPEED_RUSTC_LLVM_VERSION"),
74+
_ => None,
75+
};
76+
if let Some(env_key) = env_key {
77+
println!("cargo:rustc-env={env_key}={value}");
78+
}
79+
}
80+
}
81+
}
82+
83+
/// Collect cargo version at build time and expose as an env var.
84+
/// This env var name must be kept in sync with `src/instrument_hooks/mod.rs`.
85+
fn collect_cargo_info() {
86+
let Ok(output) = std::process::Command::new("cargo")
87+
.arg("--version")
88+
.output()
89+
else {
90+
return;
91+
};
92+
if !output.status.success() {
93+
return;
94+
}
95+
96+
let stdout = String::from_utf8_lossy(&output.stdout);
97+
if let Some(rest) = stdout.trim().strip_prefix("cargo ") {
98+
println!("cargo:rustc-env=CODSPEED_CARGO_VERSION={rest}");
99+
}
100+
}

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: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,36 @@ 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_RELEASE") {
54+
let _ = self.set_environment(SECTION, "release", v);
55+
}
56+
if let Some(v) = option_env!("CODSPEED_RUSTC_LLVM_VERSION") {
57+
let _ = self.set_environment(SECTION, "LLVM version", v);
58+
}
59+
if let Some(v) = option_env!("CODSPEED_CARGO_VERSION") {
60+
let _ = self.set_environment(SECTION, "cargo", v);
61+
}
62+
63+
if let Err(e) = self.write_environment() {
64+
eprintln!("Warning: failed to write environment info: {e}");
65+
}
66+
}
67+
4268
#[inline(always)]
4369
pub fn is_instrumented(&self) -> bool {
4470
unsafe { ffi::instrument_hooks_is_instrumented(self.0) }
@@ -131,6 +157,40 @@ mod linux_impl {
131157
}
132158
}
133159

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

250+
pub fn set_environment(
251+
&self,
252+
_section_name: &str,
253+
_key: &str,
254+
_value: &str,
255+
) -> Result<(), u8> {
256+
Ok(())
257+
}
258+
259+
pub fn write_environment(&self) -> Result<(), u8> {
260+
Ok(())
261+
}
262+
190263
pub fn disable_callgrind_markers() {}
191264
}
192265
}

0 commit comments

Comments
 (0)