Skip to content

Commit 3fddbb2

Browse files
committed
fix(criterion-compat): fix URI formatting when criterion_group!/criterion_main! are bypassed
1 parent dccf5f4 commit 3fddbb2

4 files changed

Lines changed: 74 additions & 7 deletions

File tree

crates/criterion_compat/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,7 @@ harness = false
5656
[[bench]]
5757
name = "test_benches"
5858
harness = false
59+
60+
[[bench]]
61+
name = "repro_custom_main"
62+
harness = false
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/// Reproducer for COD-2324: URI formatting issues when users bypass criterion_group!/criterion_main!
2+
/// and use a custom main function (like the rtmalloc project does).
3+
use codspeed_criterion_compat::{criterion_group, BenchmarkId, Criterion};
4+
5+
fn bench_with_group(c: &mut Criterion) {
6+
let mut group = c.benchmark_group("my_group");
7+
group.bench_function("my_bench", |b| b.iter(|| 2 + 2));
8+
group.bench_function(BenchmarkId::new("parameterized", 42), |b| b.iter(|| 2 + 2));
9+
group.finish();
10+
}
11+
12+
criterion_group!(benches, bench_with_group);
13+
14+
#[cfg(codspeed)]
15+
fn main() {
16+
// Pattern A: Using new_instrumented() but calling bench functions directly (not through criterion_group!)
17+
let mut criterion = Criterion::new_instrumented();
18+
bench_with_group(&mut criterion);
19+
20+
// Pattern B: Calling through criterion_group!-generated function (should work correctly)
21+
let mut criterion2 = Criterion::new_instrumented();
22+
benches(&mut criterion2);
23+
}
24+
25+
#[cfg(not(codspeed))]
26+
fn main() {
27+
// Without codspeed, just run through upstream criterion directly
28+
let mut criterion = Criterion::default().configure_from_args();
29+
bench_with_group(&mut criterion);
30+
}

crates/criterion_compat/src/compat/criterion.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ impl<M: Measurement> Criterion<M> {
9292
self.macro_group = macro_group.into();
9393
}
9494

95+
#[track_caller]
9596
pub fn bench_function<F>(&mut self, id: &str, f: F) -> &mut Criterion<M>
9697
where
9798
F: FnMut(&mut Bencher),
@@ -101,6 +102,7 @@ impl<M: Measurement> Criterion<M> {
101102
self
102103
}
103104

105+
#[track_caller]
104106
pub fn bench_with_input<F, I>(&mut self, id: BenchmarkId, input: &I, f: F) -> &mut Criterion<M>
105107
where
106108
F: FnMut(&mut Bencher, &I),
@@ -118,9 +120,28 @@ impl<M: Measurement> Criterion<M> {
118120
self
119121
}
120122

123+
#[track_caller]
121124
pub fn benchmark_group<S: Into<String>>(&mut self, group_name: S) -> BenchmarkGroup<M> {
125+
self.fill_missing_file_from_caller();
122126
BenchmarkGroup::<M>::new(self, group_name.into())
123127
}
128+
129+
/// When `current_file` wasn't set by `criterion_group!`, derive it from the caller location.
130+
#[track_caller]
131+
fn fill_missing_file_from_caller(&mut self) {
132+
if !self.current_file.is_empty() {
133+
return;
134+
}
135+
let caller = std::panic::Location::caller();
136+
if let Ok(workspace_root) = std::env::var("CODSPEED_CARGO_WORKSPACE_ROOT") {
137+
self.current_file = std::path::PathBuf::from(workspace_root)
138+
.join(caller.file())
139+
.to_string_lossy()
140+
.into_owned();
141+
} else {
142+
self.current_file = caller.file().to_string();
143+
}
144+
}
124145
}
125146

126147
// Dummy methods

crates/criterion_compat/src/compat/group.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,25 @@ impl<'a, M: Measurement> BenchmarkGroup<'a, M> {
6262
F: FnMut(&mut Bencher, &I),
6363
I: ?Sized,
6464
{
65-
let git_relative_file_path = get_git_relative_path(&self.current_file);
66-
let mut uri = format!(
67-
"{}::{}::{}",
68-
git_relative_file_path.to_string_lossy(),
69-
self.macro_group,
70-
self.group_name,
71-
);
65+
let mut uri = String::new();
66+
67+
if !self.current_file.is_empty() {
68+
let git_relative_file_path = get_git_relative_path(&self.current_file);
69+
let file_str = git_relative_file_path.to_string_lossy();
70+
if !file_str.is_empty() {
71+
uri.push_str(&file_str);
72+
}
73+
}
74+
if !self.macro_group.is_empty() {
75+
if !uri.is_empty() {
76+
uri.push_str("::");
77+
}
78+
uri.push_str(&self.macro_group);
79+
}
80+
if !uri.is_empty() {
81+
uri.push_str("::");
82+
}
83+
uri.push_str(&self.group_name);
7284
if let Some(function_name) = id.function_name {
7385
uri = format!("{uri}::{function_name}");
7486
}

0 commit comments

Comments
 (0)