Skip to content

Commit 2fa3fdd

Browse files
--wip-- [skip ci]
1 parent 2306d46 commit 2fa3fdd

File tree

7 files changed

+62
-44
lines changed

7 files changed

+62
-44
lines changed

crates/exec-harness/src/main.rs

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
use crate::walltime::WalltimeResults;
2+
use anyhow::Context;
3+
use anyhow::Result;
4+
use anyhow::bail;
25
use clap::Parser;
36
use codspeed::instrument_hooks::InstrumentHooks;
47
use codspeed::walltime_results::WalltimeBenchmark;
@@ -19,7 +22,7 @@ struct Args {
1922
command: Vec<String>,
2023
}
2124

22-
fn main() {
25+
fn main() -> Result<()> {
2326
let args = Args::parse();
2427

2528
if args.command.is_empty() {
@@ -31,18 +34,15 @@ fn main() {
3134
let bench_name = args.name.unwrap_or_else(|| {
3235
// Extract filename from command path
3336
let cmd = &args.command[0];
34-
std::path::Path::new(cmd)
35-
.file_name()
36-
.and_then(|n| n.to_str())
37-
.map(|s| s.to_string())
38-
.unwrap_or_else(|| "exec_benchmark".to_string())
37+
std::path::Path::new(cmd).to_string_lossy().into_owned()
3938
});
39+
4040
// TODO: Better URI generation
4141
let bench_uri = format!("standalone_run::{bench_name}");
4242

4343
let hooks = InstrumentHooks::instance();
4444

45-
// TODO: Change this to avoid impersonating `codspeed-rust`
45+
// TODO: Stop impersonating codspeed-rust 🥸
4646
hooks
4747
.set_integration("codspeed-rust", env!("CARGO_PKG_VERSION"))
4848
.unwrap();
@@ -53,36 +53,24 @@ fn main() {
5353
hooks.start_benchmark().unwrap();
5454
for _ in 0..NUM_ITERATIONS {
5555
// Spawn the command
56-
let mut child = match process::Command::new(&args.command[0])
56+
let mut child = process::Command::new(&args.command[0])
5757
.args(&args.command[1..])
5858
.spawn()
59-
{
60-
Ok(child) => child,
61-
Err(e) => {
62-
eprintln!("Failed to spawn command: {e}");
63-
process::exit(1);
64-
}
65-
};
59+
.context("Failed to spawn command")?;
60+
6661
// Start monotonic timer for this iteration
6762
let bench_start = InstrumentHooks::current_timestamp();
6863

6964
// Wait for the process to complete
70-
let status = match child.wait() {
71-
Ok(status) => status,
72-
Err(e) => {
73-
eprintln!("Failed to wait for command: {e}");
74-
process::exit(1);
75-
}
76-
};
65+
let status = child.wait().context("Failed to wait for command")?;
7766

7867
// Measure elapsed time
7968
let bench_end = InstrumentHooks::current_timestamp();
8069
hooks.add_benchmark_timestamps(bench_start, bench_end);
8170

8271
// Exit immediately if any iteration fails
8372
if !status.success() {
84-
eprintln!("Command failed with exit code: {:?}", status.code());
85-
process::exit(status.code().unwrap_or(1));
73+
bail!("Command failed with exit code: {:?}", status.code());
8674
}
8775

8876
// Calculate and store the elapsed time in nanoseconds
@@ -113,4 +101,6 @@ fn main() {
113101
.unwrap_or_else(|_| std::env::current_dir().unwrap().join(".codspeed")),
114102
)
115103
.expect("Failed to save walltime results");
104+
105+
Ok(())
116106
}

crates/exec-harness/src/walltime.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ impl WalltimeResults {
3232
type_: "walltime".to_string(),
3333
},
3434
creator: Creator {
35+
// TODO: Stop impersonating codspeed-rust 🥸
3536
name: "codspeed-rust".to_string(),
3637
version: env!("CARGO_PKG_VERSION").to_string(),
3738
pid: std::process::id(),

src/executor/config.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::exec::DEFAULT_REPOSITORY_NAME;
12
use crate::instruments::Instruments;
23
use crate::prelude::*;
34
use crate::run::{RunArgs, UnwindingMode};
@@ -150,11 +151,9 @@ impl TryFrom<crate::exec::ExecArgs> for Config {
150151
.repository
151152
.map(|repo| RepositoryOverride::from_arg(repo, args.shared.provider))
152153
.transpose()?
153-
//FIXME: This is ignored in the project upload endpoint, but it's easier to provide
154-
//a default override here to make the LocalProvider work without extra changes.
155154
.unwrap_or_else(|| RepositoryOverride {
156155
owner: "projects".to_string(),
157-
repository: "local-run".to_string(),
156+
repository: DEFAULT_REPOSITORY_NAME.to_string(),
158157
repository_provider: RepositoryProvider::GitHub,
159158
});
160159

src/executor/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ mod walltime {
221221

222222
WALLTIME_INIT
223223
.get_or_init(|| async {
224-
let executor = WallTimeExecutor::new(true);
224+
let executor = WallTimeExecutor::new();
225225
let system_info = SystemInfo::new().unwrap();
226226
executor.setup(&system_info, None).await.unwrap();
227227
})
@@ -234,7 +234,7 @@ mod walltime {
234234
.await;
235235
let permit = semaphore.acquire().await.unwrap();
236236

237-
(permit, WallTimeExecutor::new(true))
237+
(permit, WallTimeExecutor::new())
238238
}
239239

240240
fn walltime_config(command: &str, enable_perf: bool) -> Config {

src/executor/wall_time/executor.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,13 @@ pub struct WallTimeExecutor {
9494
}
9595

9696
impl WallTimeExecutor {
97-
pub fn new(output_pipedata: bool) -> Self {
97+
pub fn new() -> Self {
98+
Self {
99+
perf: cfg!(target_os = "linux").then(|| PerfRunner::new(true)),
100+
}
101+
}
102+
103+
pub fn new_with_output_pipedata(output_pipedata: bool) -> Self {
98104
Self {
99105
perf: cfg!(target_os = "linux").then(|| PerfRunner::new(output_pipedata)),
100106
}

src/executor/wall_time/perf/mod.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ const PERF_PIPEDATA_FILE_NAME: &str = "perf.pipedata";
4949
pub struct PerfRunner {
5050
benchmark_data: OnceCell<BenchmarkData>,
5151
/// Whether to output the perf data to a streamable .pipedata file
52+
/// This can be removed once we have upstreamed the the linux-perf-data crate changes to parse
53+
/// from pipedata directly, to only support pipedata.
5254
output_pipedata: bool,
5355
}
5456

@@ -167,11 +169,15 @@ impl PerfRunner {
167169
// Output the perf data to the profile folder
168170
let perf_data_file_path = self.get_perf_file_path(profile_folder);
169171

170-
let raw_command = format!(
171-
"set -o pipefail && {} | cat > {}",
172-
&cmd_builder.as_command_line(),
173-
perf_data_file_path.to_string_lossy()
174-
);
172+
let raw_command = if self.output_pipedata {
173+
format!(
174+
"set -o pipefail && {} | cat > {}",
175+
&cmd_builder.as_command_line(),
176+
perf_data_file_path.to_string_lossy()
177+
)
178+
} else {
179+
cmd_builder.as_command_line()
180+
};
175181
let mut wrapped_builder = CommandBuilder::new("bash");
176182
wrapped_builder.args(["-c", &raw_command]);
177183

@@ -270,7 +276,7 @@ impl PerfRunner {
270276

271277
match cmd {
272278
FifoCommand::CurrentBenchmark { pid, uri } => {
273-
bench_order_by_timestamp.push((current_time(), uri.clone()));
279+
bench_order_by_timestamp.push((current_time(), uri));
274280
bench_pids.insert(pid);
275281

276282
#[cfg(target_os = "linux")]

src/executor/wall_time/perf/parse_perf_file.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use super::perf_map::ProcessSymbols;
22
use super::unwind_data::UnwindDataExt;
3+
use crate::executor::helpers::run_with_sudo::run_with_sudo;
4+
use crate::prelude::*;
35
use libc::pid_t;
46
use linux_perf_data::PerfFileReader;
57
use linux_perf_data::PerfFileRecord;
@@ -8,26 +10,40 @@ use runner_shared::unwind_data::UnwindData;
810
use std::collections::HashMap;
911
use std::path::Path;
1012

11-
use crate::prelude::*;
12-
1313
pub struct MemmapRecordsOutput {
1414
pub symbols_by_pid: HashMap<pid_t, ProcessSymbols>,
1515
pub unwind_data_by_pid: HashMap<pid_t, Vec<UnwindData>>,
1616
}
1717

1818
pub(super) fn parse_for_memmap2<P: AsRef<Path>>(perf_file_path: P) -> Result<MemmapRecordsOutput> {
19-
let reader = std::fs::File::open(perf_file_path).unwrap();
2019
let mut symbols_by_pid = HashMap::<pid_t, ProcessSymbols>::new();
2120
let mut unwind_data_by_pid = HashMap::<pid_t, Vec<UnwindData>>::new();
2221

23-
// TODO: Handle pipedata here once linux_perf_data supports it upstream
22+
//FIXME: Remove this once again when we parse directly from pipedata
23+
{
24+
let tmp_perf_file_path = perf_file_path.as_ref().to_string_lossy();
25+
26+
// We ran perf with sudo, so we have to change the ownership of the perf.data
27+
run_with_sudo(
28+
"chown",
29+
[
30+
"-R",
31+
&format!(
32+
"{}:{}",
33+
nix::unistd::Uid::current(),
34+
nix::unistd::Gid::current()
35+
),
36+
&tmp_perf_file_path,
37+
],
38+
)?;
39+
}
40+
let reader = std::fs::File::open(perf_file_path.as_ref()).unwrap();
41+
2442
let PerfFileReader {
2543
mut perf_file,
2644
mut record_iter,
2745
} = PerfFileReader::parse_file(reader)?;
2846

29-
const PROT_EXEC: u32 = 0x4;
30-
3147
while let Some(record) = record_iter.next_record(&mut perf_file).unwrap() {
3248
let PerfFileRecord::EventRecord { record, .. } = record else {
3349
continue;
@@ -59,7 +75,7 @@ pub(super) fn parse_for_memmap2<P: AsRef<Path>>(perf_file_path: P) -> Result<Mem
5975

6076
if record_path_string.starts_with("[") && record_path_string.ends_with("]") {
6177
// Skip special mappings
62-
debug!(
78+
trace!(
6379
"Skipping special mapping: {} - {:x}-{:x}",
6480
record_path_string, record.address, end_addr
6581
);
@@ -76,7 +92,7 @@ pub(super) fn parse_for_memmap2<P: AsRef<Path>>(perf_file_path: P) -> Result<Mem
7692
record.protection,
7793
);
7894

79-
if record.protection & PROT_EXEC == 0 {
95+
if record.protection as i32 & libc::PROT_EXEC == 0 {
8096
continue;
8197
}
8298

0 commit comments

Comments
 (0)