diff --git a/.config/nextest.toml b/.config/nextest.toml new file mode 100644 index 00000000..9216cea5 --- /dev/null +++ b/.config/nextest.toml @@ -0,0 +1,8 @@ +# Valgrind and memtrack tests must never run concurrently, even across multiple processes, else valgrind crashes. +# We have a semaphore setup in the test code, but it's not sufficient since nextest runs tests in multiple processes. +[test-groups] +bpf-instrumentation = { max-threads = 1 } + +[[profile.default.overrides]] +filter = 'test(~executor::tests::valgrind) | test(~executor::tests::memory)' +test-group = 'bpf-instrumentation' diff --git a/.gitattributes b/.gitattributes index 1df80dfd..d34aa96b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ testdata/perf_map/* filter=lfs diff=lfs merge=lfs -text -src/run/runner/wall_time/perf/snapshots/*.snap filter=lfs diff=lfs merge=lfs -text +*.snap filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index f164cd97..05923927 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /target .DS_Store -samples diff --git a/Cargo.lock b/Cargo.lock index 3e428c34..0dada523 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,6 +98,15 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "async-compression" version = "0.4.34" @@ -346,6 +355,24 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +[[package]] +name = "codspeed" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b847e05a34be5c38f3f2a5052178a3bd32e6b5702f3ea775efde95c483a539" +dependencies = [ + "anyhow", + "cc", + "colored", + "getrandom 0.2.16", + "glob", + "libc", + "nix 0.30.1", + "serde", + "serde_json", + "statrs", +] + [[package]] name = "codspeed-runner" version = "4.4.2-beta.1" @@ -415,6 +442,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + [[package]] name = "compression-codecs" version = "0.4.33" @@ -652,6 +689,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "exec-harness" +version = "4.4.2-beta.1" +dependencies = [ + "anyhow", + "clap", + "codspeed", + "serde", + "serde_json", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -1524,7 +1572,7 @@ dependencies = [ [[package]] name = "memtrack" -version = "4.4.1" +version = "4.4.2-beta.1" dependencies = [ "anyhow", "bindgen", @@ -2763,6 +2811,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "statrs" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a3fe7c28c6512e766b0874335db33c94ad7b8f9054228ae1c2abd47ce7d335e" +dependencies = [ + "approx", + "num-traits", +] + [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index 0155303e..20457e86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,7 +80,7 @@ rstest_reuse = "0.7.0" shell-quote = "0.7.2" [workspace] -members = ["crates/runner-shared", "crates/memtrack"] +members = ["crates/runner-shared", "crates/memtrack", "crates/exec-harness"] [workspace.dependencies] anyhow = "1.0" diff --git a/crates/exec-harness/Cargo.toml b/crates/exec-harness/Cargo.toml new file mode 100644 index 00000000..f2e76116 --- /dev/null +++ b/crates/exec-harness/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "exec-harness" +version = "4.4.2-beta.1" +edition = "2024" + +[dependencies] +anyhow = { workspace = true } +codspeed = "4.1.0" +clap = { workspace = true } +serde_json = { workspace = true } +serde = { workspace = true } diff --git a/crates/exec-harness/src/main.rs b/crates/exec-harness/src/main.rs new file mode 100644 index 00000000..aa48b943 --- /dev/null +++ b/crates/exec-harness/src/main.rs @@ -0,0 +1,105 @@ +use crate::walltime::WalltimeResults; +use anyhow::Context; +use anyhow::Result; +use anyhow::bail; +use clap::Parser; +use codspeed::instrument_hooks::InstrumentHooks; +use codspeed::walltime_results::WalltimeBenchmark; +use std::path::PathBuf; +use std::process; + +mod walltime; + +#[derive(Parser, Debug)] +#[command(name = "exec-harness")] +#[command(about = "CodSpeed exec harness - wraps commands with performance instrumentation")] +struct Args { + /// Optional benchmark name (defaults to command filename) + #[arg(long)] + name: Option, + + /// The command and arguments to execute + command: Vec, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + if args.command.is_empty() { + bail!("Error: No command provided"); + } + + // Derive benchmark name from command if not provided + let bench_name = args.name.unwrap_or_else(|| { + // Extract filename from command path + let cmd = &args.command[0]; + std::path::Path::new(cmd).to_string_lossy().into_owned() + }); + + // TODO: Better URI generation + let bench_uri = format!("standalone_run::{bench_name}"); + + let hooks = InstrumentHooks::instance(); + + // TODO: Stop impersonating codspeed-rust ๐Ÿฅธ + hooks + .set_integration("codspeed-rust", env!("CARGO_PKG_VERSION")) + .unwrap(); + + const NUM_ITERATIONS: usize = 1; + let mut times_per_round_ns = Vec::with_capacity(NUM_ITERATIONS); + + hooks.start_benchmark().unwrap(); + for _ in 0..NUM_ITERATIONS { + // Spawn the command + let mut child = process::Command::new(&args.command[0]) + .args(&args.command[1..]) + .spawn() + .context("Failed to spawn command")?; + + // Start monotonic timer for this iteration + let bench_start = InstrumentHooks::current_timestamp(); + + // Wait for the process to complete + let status = child.wait().context("Failed to wait for command")?; + + // Measure elapsed time + let bench_end = InstrumentHooks::current_timestamp(); + hooks.add_benchmark_timestamps(bench_start, bench_end); + + // Exit immediately if any iteration fails + if !status.success() { + bail!("Command failed with exit code: {:?}", status.code()); + } + + // Calculate and store the elapsed time in nanoseconds + let elapsed_ns = (bench_end - bench_start) as u128; + times_per_round_ns.push(elapsed_ns); + } + + hooks.stop_benchmark().unwrap(); + hooks.set_executed_benchmark(&bench_uri).unwrap(); + + // Collect walltime results + let max_time_ns = times_per_round_ns.iter().copied().max(); + let walltime_benchmark = WalltimeBenchmark::from_runtime_data( + bench_name.clone(), + bench_uri.clone(), + vec![1; NUM_ITERATIONS], + times_per_round_ns, + max_time_ns, + ); + + let walltime_results = WalltimeResults::from_benchmarks(vec![walltime_benchmark]) + .expect("Failed to create walltime results"); + + walltime_results + .save_to_file( + std::env::var("CODSPEED_PROFILE_FOLDER") + .map(PathBuf::from) + .unwrap_or_else(|_| std::env::current_dir().unwrap().join(".codspeed")), + ) + .expect("Failed to save walltime results"); + + Ok(()) +} diff --git a/crates/exec-harness/src/walltime.rs b/crates/exec-harness/src/walltime.rs new file mode 100644 index 00000000..6c373745 --- /dev/null +++ b/crates/exec-harness/src/walltime.rs @@ -0,0 +1,63 @@ +use anyhow::Context; +use anyhow::Result; +use codspeed::walltime_results::WalltimeBenchmark; +use serde::Deserialize; +use serde::Serialize; +use std::path::Path; + +#[derive(Debug, Serialize, Deserialize)] +struct Instrument { + #[serde(rename = "type")] + type_: String, +} + +#[derive(Debug, Serialize, Deserialize)] +struct Creator { + name: String, + version: String, + pid: u32, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct WalltimeResults { + creator: Creator, + instrument: Instrument, + benchmarks: Vec, +} + +impl WalltimeResults { + pub fn from_benchmarks(benchmarks: Vec) -> Result { + Ok(WalltimeResults { + instrument: Instrument { + type_: "walltime".to_string(), + }, + creator: Creator { + // TODO: Stop impersonating codspeed-rust ๐Ÿฅธ + name: "codspeed-rust".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + pid: std::process::id(), + }, + benchmarks, + }) + } + + pub fn save_to_file>(&self, profile_folder: P) -> Result<()> { + let results_path = { + let results_dir = profile_folder.as_ref().join("results"); + std::fs::create_dir_all(&results_dir).with_context(|| { + format!( + "Failed to create results directory: {}", + results_dir.display() + ) + })?; + + results_dir.join(format!("{}.json", self.creator.pid)) + }; + + let file = std::fs::File::create(&results_path) + .with_context(|| format!("Failed to create file: {}", results_path.display()))?; + serde_json::to_writer_pretty(file, &self) + .with_context(|| format!("Failed to write JSON to file: {}", results_path.display()))?; + Ok(()) + } +} diff --git a/crates/memtrack/Cargo.toml b/crates/memtrack/Cargo.toml index 006a2fce..e1e13625 100644 --- a/crates/memtrack/Cargo.toml +++ b/crates/memtrack/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "memtrack" -version = "4.4.1" +version = "4.4.2-beta.1" edition = "2024" repository = "https://github.com/CodSpeedHQ/runner" publish = false @@ -16,11 +16,7 @@ required-features = ["ebpf"] [features] default = ["ebpf"] -ebpf = [ - "dep:libbpf-rs", - "dep:libbpf-cargo", - "dep:vmlinux", -] +ebpf = ["dep:libbpf-rs", "dep:libbpf-cargo", "dep:vmlinux"] [dependencies] anyhow = { workspace = true } diff --git a/src/api_client.rs b/src/api_client.rs index 7439c4be..fff9519e 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -83,6 +83,13 @@ pub struct FetchLocalRunReportVars { pub name: String, pub run_id: String, } + +#[derive(Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct FetchLocalExecReportVars { + pub name: String, + pub run_id: String, +} #[derive(Deserialize, Serialize, Debug, Clone, PartialEq)] pub enum ReportConclusion { AcknowledgedFailure, @@ -157,11 +164,25 @@ nest! { } } +nest! { + #[derive(Debug, Deserialize, Serialize)]* + #[serde(rename_all = "camelCase")]* + struct FetchLocalExecReportData { + project: pub struct FetchLocalExecReportProject { + run: FetchLocalRunReportRun, + } + } +} + pub struct FetchLocalRunReportResponse { pub allowed_regression: f64, pub run: FetchLocalRunReportRun, } +pub struct FetchLocalExecReportResponse { + pub run: FetchLocalRunReportRun, +} + impl CodSpeedAPIClient { pub async fn create_login_session(&self) -> Result { let response = self @@ -215,4 +236,26 @@ impl CodSpeedAPIClient { Err(err) => bail!("Failed to fetch local run report: {err}"), } } + + pub async fn fetch_local_exec_report( + &self, + vars: FetchLocalExecReportVars, + ) -> Result { + let response = self + .gql_client + .query_with_vars_unwrap::( + include_str!("queries/FetchLocalExecReport.gql"), + vars.clone(), + ) + .await; + match response { + Ok(response) => Ok(FetchLocalExecReportResponse { + run: response.project.run, + }), + Err(err) if err.contains_error_code("UNAUTHENTICATED") => { + bail!("Your session has expired, please login again using `codspeed auth login`") + } + Err(err) => bail!("Failed to fetch local run report: {err}"), + } + } } diff --git a/src/app.rs b/src/app.rs index 314217d4..b87c7e7b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,6 +4,7 @@ use crate::{ api_client::CodSpeedAPIClient, auth, config::CodSpeedConfig, + exec, local_logger::{CODSPEED_U8_COLOR_CODE, init_local_logger}, prelude::*, run, setup, @@ -59,8 +60,11 @@ pub struct Cli { #[derive(Subcommand, Debug)] enum Commands { - /// Run the bench command and upload the results to CodSpeed + /// Run a benchmark program that already contains the CodSpeed instrumentation and upload the results to CodSpeed Run(Box), + /// Run a command after adding CodSpeed instrumentation to it and upload the results to + /// CodSpeed + Exec(Box), /// Manage the CLI authentication state Auth(auth::AuthArgs), /// Pre-install the codspeed executors @@ -80,7 +84,7 @@ pub async fn run() -> Result<()> { let setup_cache_dir = setup_cache_dir.as_deref(); match cli.command { - Commands::Run(_) => {} // Run is responsible for its own logger initialization + Commands::Run(_) | Commands::Exec(_) => {} // Run and Exec are responsible for their own logger initialization _ => { init_local_logger()?; } @@ -90,6 +94,9 @@ pub async fn run() -> Result<()> { Commands::Run(args) => { run::run(*args, &api_client, &codspeed_config, setup_cache_dir).await? } + Commands::Exec(args) => { + exec::run(*args, &api_client, &codspeed_config, setup_cache_dir).await? + } Commands::Auth(args) => auth::run(args, &api_client, cli.config_name.as_deref()).await?, Commands::Setup => setup::setup(setup_cache_dir).await?, } diff --git a/src/config.rs b/src/config.rs index c8481b09..1e3fdf94 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,6 +7,14 @@ use serde::{Deserialize, Serialize}; nest! { #[derive(Debug, Deserialize, Serialize)]* #[serde(rename_all = "kebab-case")]* + /// Persistent configuration for CodSpeed CLI. + /// + /// This struct represents the user's persistent configuration stored in the filesystem, + /// typically at `~/.config/codspeed/config.yaml`. It contains settings that persist + /// across multiple benchmark runs, such as authentication credentials. + /// + /// The configuration follows the XDG Base Directory Specification and can be loaded + /// with [`CodSpeedConfig::load_with_override`] or persisted with [`CodSpeedConfig::persist`]. pub struct CodSpeedConfig { pub auth: pub struct AuthConfig { pub token: Option, diff --git a/src/exec/mod.rs b/src/exec/mod.rs new file mode 100644 index 00000000..79ebbb8a --- /dev/null +++ b/src/exec/mod.rs @@ -0,0 +1,50 @@ +use crate::api_client::CodSpeedAPIClient; +use crate::config::CodSpeedConfig; +use crate::executor; +use crate::prelude::*; +use clap::Args; +use std::path::Path; + +mod poll_results; + +/// We temporarily force this name for all exec runs +pub const DEFAULT_REPOSITORY_NAME: &str = "local-runs"; + +#[derive(Args, Debug)] +pub struct ExecArgs { + #[command(flatten)] + pub shared: crate::run::ExecAndRunSharedArgs, + + /// Optional benchmark name (defaults to command filename) + #[arg(long)] + pub name: Option, + + /// The command to execute with the exec harness + pub command: Vec, +} + +pub async fn run( + args: ExecArgs, + api_client: &CodSpeedAPIClient, + codspeed_config: &CodSpeedConfig, + setup_cache_dir: Option<&Path>, +) -> Result<()> { + let config = crate::executor::Config::try_from(args)?; + let mut execution_context = executor::ExecutionContext::try_from((config, codspeed_config))?; + let executor = executor::get_executor_from_mode( + &execution_context.config.mode, + executor::ExecutorCommand::Exec, + ); + + let poll_results_fn = |run_id: String| poll_results::poll_results(api_client, run_id); + + executor::execute_benchmarks( + executor.as_ref(), + &mut execution_context, + setup_cache_dir, + poll_results_fn, + ) + .await?; + + Ok(()) +} diff --git a/src/exec/poll_results.rs b/src/exec/poll_results.rs new file mode 100644 index 00000000..5b2be7df --- /dev/null +++ b/src/exec/poll_results.rs @@ -0,0 +1,69 @@ +use console::style; +use tokio::time::{Instant, sleep}; + +use crate::api_client::{ + CodSpeedAPIClient, FetchLocalExecReportResponse, FetchLocalExecReportVars, RunStatus, +}; +use crate::exec::DEFAULT_REPOSITORY_NAME; +use crate::prelude::*; +use crate::run::helpers::poll_results::{ + POLLING_INTERVAL, RUN_PROCESSING_MAX_DURATION, build_benchmark_table, retry_on_timeout, +}; + +#[allow(clippy::borrowed_box)] +pub async fn poll_results(api_client: &CodSpeedAPIClient, run_id: String) -> Result<()> { + let start = Instant::now(); + let fetch_local_exec_report_vars = FetchLocalExecReportVars { + // TODO: Set this dynamically based on the upload endpoint return value + name: DEFAULT_REPOSITORY_NAME.to_owned(), + run_id: run_id.clone(), + }; + + start_group!("Fetching the results"); + let response; + loop { + if start.elapsed() > RUN_PROCESSING_MAX_DURATION { + bail!("Polling results timed out"); + } + + let fetch_result = retry_on_timeout(|| async { + api_client + .fetch_local_exec_report(fetch_local_exec_report_vars.clone()) + .await + }) + .await?; + + match fetch_result { + FetchLocalExecReportResponse { run, .. } + if run.status == RunStatus::Pending || run.status == RunStatus::Processing => + { + sleep(POLLING_INTERVAL).await; + } + response_from_api => { + response = response_from_api; + break; + } + } + } + + if response.run.status == RunStatus::Failure { + bail!("Run failed to be processed, try again in a few minutes"); + } + + info!( + "\nTo see the full report, visit: {}", + style(response.run.url).blue().bold().underlined() + ); + end_group!(); + + if !response.run.results.is_empty() { + start_group!("Benchmark results"); + + let table = build_benchmark_table(&response.run.results); + info!("\n{table}"); + + end_group!(); + } + + Ok(()) +} diff --git a/src/executor/config.rs b/src/executor/config.rs new file mode 100644 index 00000000..015a839b --- /dev/null +++ b/src/executor/config.rs @@ -0,0 +1,371 @@ +use crate::exec::DEFAULT_REPOSITORY_NAME; +use crate::instruments::Instruments; +use crate::prelude::*; +use crate::run::{RunArgs, UnwindingMode}; +use crate::run_environment::RepositoryProvider; +use crate::runner_mode::RunnerMode; +use std::path::PathBuf; +use url::Url; + +/// Execution configuration for running benchmarks. +/// +/// This struct contains all the configuration parameters needed to execute benchmarks, +/// including API settings, execution mode, instrumentation options, and various flags +/// for controlling the execution flow. It is typically constructed from command-line +/// arguments via [`RunArgs`] and combined with [`CodSpeedConfig`] to create an +/// [`ExecutionContext`]. +#[derive(Debug)] +pub struct Config { + pub upload_url: Url, + pub token: Option, + pub repository_override: Option, + pub working_directory: Option, + pub command: String, + + pub mode: RunnerMode, + pub instruments: Instruments, + pub enable_perf: bool, + /// Stack unwinding mode for perf (if enabled) + pub perf_unwinding_mode: Option, + + pub profile_folder: Option, + pub skip_upload: bool, + pub skip_run: bool, + pub skip_setup: bool, + /// If true, allow execution even when no benchmarks are found + pub allow_empty: bool, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct RepositoryOverride { + pub owner: String, + pub repository: String, + pub repository_provider: RepositoryProvider, +} + +impl RepositoryOverride { + /// Creates a RepositoryOverride from an "owner/repository" string + pub fn from_arg( + repository_and_owner: String, + provider: Option, + ) -> Result { + let (owner, repository) = repository_and_owner + .split_once('/') + .context("Invalid owner/repository format")?; + Ok(Self { + owner: owner.to_string(), + repository: repository.to_string(), + repository_provider: provider.unwrap_or_default(), + }) + } +} + +impl Config { + pub fn set_token(&mut self, token: Option) { + self.token = token; + } +} + +#[cfg(test)] +impl Config { + /// Constructs a new `Config` with default values for testing purposes + pub fn test() -> Self { + Self { + upload_url: Url::parse(DEFAULT_UPLOAD_URL).unwrap(), + token: None, + repository_override: None, + working_directory: None, + command: "".into(), + mode: RunnerMode::Simulation, + instruments: Instruments::test(), + perf_unwinding_mode: None, + enable_perf: false, + profile_folder: None, + skip_upload: false, + skip_run: false, + skip_setup: false, + allow_empty: false, + } + } +} + +const DEFAULT_UPLOAD_URL: &str = "https://api.codspeed.io/upload"; + +impl TryFrom for Config { + type Error = Error; + fn try_from(args: RunArgs) -> Result { + let instruments = Instruments::try_from(&args)?; + let raw_upload_url = args + .shared + .upload_url + .unwrap_or_else(|| DEFAULT_UPLOAD_URL.into()); + let upload_url = Url::parse(&raw_upload_url) + .map_err(|e| anyhow!("Invalid upload URL: {raw_upload_url}, {e}"))?; + + Ok(Self { + upload_url, + token: args.shared.token, + repository_override: args + .shared + .repository + .map(|repo| RepositoryOverride::from_arg(repo, args.shared.provider)) + .transpose()?, + working_directory: args.shared.working_directory, + mode: args.shared.mode, + instruments, + perf_unwinding_mode: args.shared.perf_run_args.perf_unwinding_mode, + enable_perf: args.shared.perf_run_args.enable_perf, + command: args.command.join(" "), + profile_folder: args.shared.profile_folder, + skip_upload: args.shared.skip_upload, + skip_run: args.shared.skip_run, + skip_setup: args.shared.skip_setup, + allow_empty: args.shared.allow_empty, + }) + } +} + +impl TryFrom for Config { + type Error = Error; + fn try_from(args: crate::exec::ExecArgs) -> Result { + let raw_upload_url = args + .shared + .upload_url + .unwrap_or_else(|| DEFAULT_UPLOAD_URL.into()); + let mut upload_url = Url::parse(&raw_upload_url) + .map_err(|e| anyhow!("Invalid upload URL: {raw_upload_url}, {e}"))?; + + // For exec command, append /project to the upload URL path + upload_url + .path_segments_mut() + .map_err(|_| anyhow!("Cannot append to upload URL"))? + .push("project"); + + let wrapped_command = std::iter::once("exec-harness".to_string()) + .chain(args.command) + .collect::>() + .join(" "); + + let repository_override = args + .shared + .repository + .map(|repo| RepositoryOverride::from_arg(repo, args.shared.provider)) + .transpose()? + .unwrap_or_else(|| RepositoryOverride { + owner: "projects".to_string(), + repository: DEFAULT_REPOSITORY_NAME.to_string(), + repository_provider: RepositoryProvider::GitHub, + }); + + Ok(Self { + upload_url, + token: args.shared.token, + repository_override: Some(repository_override), + working_directory: args.shared.working_directory, + mode: args.shared.mode, + instruments: Instruments { mongodb: None }, // exec doesn't support MongoDB + perf_unwinding_mode: args.shared.perf_run_args.perf_unwinding_mode, + enable_perf: args.shared.perf_run_args.enable_perf, + command: wrapped_command, + profile_folder: args.shared.profile_folder, + skip_upload: args.shared.skip_upload, + skip_run: args.shared.skip_run, + skip_setup: args.shared.skip_setup, + allow_empty: args.shared.allow_empty, + }) + } +} + +#[cfg(test)] +mod tests { + use crate::instruments::MongoDBConfig; + use crate::run::PerfRunArgs; + + use super::*; + + #[test] + fn test_try_from_env_empty() { + let config = Config::try_from(RunArgs { + shared: crate::run::ExecAndRunSharedArgs { + upload_url: None, + token: None, + repository: None, + provider: None, + working_directory: None, + mode: RunnerMode::Simulation, + profile_folder: None, + skip_upload: false, + skip_run: false, + skip_setup: false, + allow_empty: false, + perf_run_args: PerfRunArgs { + enable_perf: false, + perf_unwinding_mode: None, + }, + }, + instruments: vec![], + mongo_uri_env_name: None, + message_format: None, + command: vec!["cargo".into(), "codspeed".into(), "bench".into()], + }) + .unwrap(); + assert_eq!(config.upload_url, Url::parse(DEFAULT_UPLOAD_URL).unwrap()); + assert_eq!(config.token, None); + assert_eq!(config.repository_override, None); + assert_eq!(config.working_directory, None); + assert_eq!(config.instruments, Instruments { mongodb: None }); + assert!(!config.skip_upload); + assert!(!config.skip_run); + assert!(!config.skip_setup); + assert!(!config.allow_empty); + assert_eq!(config.command, "cargo codspeed bench"); + } + + #[test] + fn test_try_from_args() { + let config = Config::try_from(RunArgs { + shared: crate::run::ExecAndRunSharedArgs { + upload_url: Some("https://example.com/upload".into()), + token: Some("token".into()), + repository: Some("owner/repo".into()), + provider: Some(RepositoryProvider::GitLab), + working_directory: Some("/tmp".into()), + mode: RunnerMode::Simulation, + profile_folder: Some("./codspeed.out".into()), + skip_upload: true, + skip_run: true, + skip_setup: true, + allow_empty: true, + perf_run_args: PerfRunArgs { + enable_perf: false, + perf_unwinding_mode: Some(UnwindingMode::FramePointer), + }, + }, + instruments: vec!["mongodb".into()], + mongo_uri_env_name: Some("MONGODB_URI".into()), + message_format: Some(crate::run::MessageFormat::Json), + command: vec!["cargo".into(), "codspeed".into(), "bench".into()], + }) + .unwrap(); + + assert_eq!( + config.upload_url, + Url::parse("https://example.com/upload").unwrap() + ); + assert_eq!(config.token, Some("token".into())); + assert_eq!( + config.repository_override, + Some(RepositoryOverride { + owner: "owner".into(), + repository: "repo".into(), + repository_provider: RepositoryProvider::GitLab, + }) + ); + assert_eq!(config.working_directory, Some("/tmp".into())); + assert_eq!( + config.instruments, + Instruments { + mongodb: Some(MongoDBConfig { + uri_env_name: Some("MONGODB_URI".into()) + }) + } + ); + assert_eq!(config.profile_folder, Some("./codspeed.out".into())); + assert!(config.skip_upload); + assert!(config.skip_run); + assert!(config.skip_setup); + assert!(config.allow_empty); + assert_eq!(config.command, "cargo codspeed bench"); + } + + #[test] + fn test_repository_override_from_arg() { + let override_result = + RepositoryOverride::from_arg("CodSpeedHQ/runner".to_string(), None).unwrap(); + assert_eq!(override_result.owner, "CodSpeedHQ"); + assert_eq!(override_result.repository, "runner"); + assert_eq!( + override_result.repository_provider, + RepositoryProvider::GitHub + ); + + let override_with_provider = RepositoryOverride::from_arg( + "CodSpeedHQ/runner".to_string(), + Some(RepositoryProvider::GitLab), + ) + .unwrap(); + assert_eq!( + override_with_provider.repository_provider, + RepositoryProvider::GitLab + ); + + let result = RepositoryOverride::from_arg("CodSpeedHQ_runner".to_string(), None); + assert!(result.is_err()); + } + + #[test] + fn test_try_from_exec_args_appends_project_to_url() { + let exec_args = crate::exec::ExecArgs { + shared: crate::run::ExecAndRunSharedArgs { + upload_url: Some("https://api.codspeed.io/upload".into()), + token: Some("token".into()), + repository: None, + provider: None, + working_directory: None, + mode: RunnerMode::Simulation, + profile_folder: None, + skip_upload: false, + skip_run: false, + skip_setup: false, + allow_empty: false, + perf_run_args: PerfRunArgs { + enable_perf: false, + perf_unwinding_mode: None, + }, + }, + name: None, + command: vec!["my-binary".into()], + }; + + let config = Config::try_from(exec_args).unwrap(); + + assert_eq!( + config.upload_url, + Url::parse("https://api.codspeed.io/upload/project").unwrap() + ); + assert_eq!(config.command, "exec-harness my-binary"); + } + + #[test] + fn test_try_from_exec_args_default_url() { + let exec_args = crate::exec::ExecArgs { + shared: crate::run::ExecAndRunSharedArgs { + upload_url: None, + token: None, + repository: None, + provider: None, + working_directory: None, + mode: RunnerMode::Simulation, + profile_folder: None, + skip_upload: false, + skip_run: false, + skip_setup: false, + allow_empty: false, + perf_run_args: PerfRunArgs { + enable_perf: false, + perf_unwinding_mode: None, + }, + }, + name: None, + command: vec!["my-binary".into(), "arg1".into(), "arg2".into()], + }; + + let config = Config::try_from(exec_args).unwrap(); + + assert_eq!( + config.upload_url, + Url::parse("https://api.codspeed.io/upload/project").unwrap() + ); + assert_eq!(config.command, "exec-harness my-binary arg1 arg2"); + } +} diff --git a/src/executor/execution_context.rs b/src/executor/execution_context.rs new file mode 100644 index 00000000..cd46e8fc --- /dev/null +++ b/src/executor/execution_context.rs @@ -0,0 +1,75 @@ +use super::Config; +use crate::config::CodSpeedConfig; +use crate::prelude::*; +use crate::run::check_system::{self, SystemInfo}; +use crate::run::logger::Logger; +use crate::run_environment::{self, RunEnvironment}; +use crate::runner_mode::RunnerMode; +use std::path::PathBuf; + +use super::create_profile_folder; + +/// Runtime context for benchmark execution. +/// +/// This struct contains all the necessary information and dependencies needed to execute +/// benchmarks, including the execution configuration, system information, environment provider, +/// and logging facilities. It is constructed from a [`Config`] and [`CodSpeedConfig`] and +/// serves as the primary context passed to executors during the benchmark run lifecycle. +pub struct ExecutionContext { + pub config: Config, + /// Directory path where profiling data and results are stored + pub profile_folder: PathBuf, + pub system_info: SystemInfo, + /// The run environment provider (GitHub Actions, GitLab CI, local, etc.) + pub provider: Box, + pub logger: Logger, +} + +impl ExecutionContext { + pub fn is_local(&self) -> bool { + self.provider.get_run_environment() == RunEnvironment::Local + } +} + +impl TryFrom<(Config, &CodSpeedConfig)> for ExecutionContext { + type Error = anyhow::Error; + + fn try_from( + (mut config, codspeed_config): (Config, &CodSpeedConfig), + ) -> Result { + let provider = run_environment::get_provider(&config)?; + let system_info = SystemInfo::new()?; + check_system::check_system(&system_info)?; + let logger = Logger::new(provider.as_ref())?; + + let profile_folder = if let Some(profile_folder) = &config.profile_folder { + profile_folder.clone() + } else { + create_profile_folder()? + }; + + if provider.get_run_environment() == RunEnvironment::Local { + if codspeed_config.auth.token.is_none() { + bail!("You have to authenticate the CLI first. Run `codspeed auth login`."); + } + debug!("Using the token from the CodSpeed configuration file"); + config.set_token(codspeed_config.auth.token.clone()); + } + + #[allow(deprecated)] + if config.mode == RunnerMode::Instrumentation { + warn!( + "The 'instrumentation' runner mode is deprecated and will be removed in a future version. \ + Please use 'simulation' instead." + ); + } + + Ok(ExecutionContext { + config, + profile_folder, + system_info, + provider, + logger, + }) + } +} diff --git a/src/run/runner/helpers/apt.rs b/src/executor/helpers/apt.rs similarity index 100% rename from src/run/runner/helpers/apt.rs rename to src/executor/helpers/apt.rs diff --git a/src/run/runner/helpers/command.rs b/src/executor/helpers/command.rs similarity index 100% rename from src/run/runner/helpers/command.rs rename to src/executor/helpers/command.rs diff --git a/src/run/runner/helpers/env.rs b/src/executor/helpers/env.rs similarity index 97% rename from src/run/runner/helpers/env.rs rename to src/executor/helpers/env.rs index 03fcdf40..e337bb70 100644 --- a/src/run/runner/helpers/env.rs +++ b/src/executor/helpers/env.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, env::consts::ARCH, path::Path}; -use crate::run::runner::RunnerMode; +use crate::runner_mode::RunnerMode; pub fn get_base_injected_env( mode: RunnerMode, diff --git a/src/run/runner/helpers/get_bench_command.rs b/src/executor/helpers/get_bench_command.rs similarity index 97% rename from src/run/runner/helpers/get_bench_command.rs rename to src/executor/helpers/get_bench_command.rs index 18732f09..4250f061 100644 --- a/src/run/runner/helpers/get_bench_command.rs +++ b/src/executor/helpers/get_bench_command.rs @@ -1,5 +1,5 @@ +use crate::executor::Config; use crate::prelude::*; -use crate::run::config::Config; pub fn get_bench_command(config: &Config) -> Result { let bench_command = &config.command.trim(); diff --git a/src/run/runner/helpers/introspected_golang/go.sh b/src/executor/helpers/introspected_golang/go.sh similarity index 100% rename from src/run/runner/helpers/introspected_golang/go.sh rename to src/executor/helpers/introspected_golang/go.sh diff --git a/src/run/runner/helpers/introspected_golang/mod.rs b/src/executor/helpers/introspected_golang/mod.rs similarity index 100% rename from src/run/runner/helpers/introspected_golang/mod.rs rename to src/executor/helpers/introspected_golang/mod.rs diff --git a/src/run/runner/helpers/introspected_nodejs/mod.rs b/src/executor/helpers/introspected_nodejs/mod.rs similarity index 100% rename from src/run/runner/helpers/introspected_nodejs/mod.rs rename to src/executor/helpers/introspected_nodejs/mod.rs diff --git a/src/run/runner/helpers/introspected_nodejs/node.sh b/src/executor/helpers/introspected_nodejs/node.sh similarity index 100% rename from src/run/runner/helpers/introspected_nodejs/node.sh rename to src/executor/helpers/introspected_nodejs/node.sh diff --git a/src/run/runner/helpers/mod.rs b/src/executor/helpers/mod.rs similarity index 100% rename from src/run/runner/helpers/mod.rs rename to src/executor/helpers/mod.rs diff --git a/src/run/runner/helpers/profile_folder.rs b/src/executor/helpers/profile_folder.rs similarity index 100% rename from src/run/runner/helpers/profile_folder.rs rename to src/executor/helpers/profile_folder.rs diff --git a/src/run/runner/helpers/run_command_with_log_pipe.rs b/src/executor/helpers/run_command_with_log_pipe.rs similarity index 98% rename from src/run/runner/helpers/run_command_with_log_pipe.rs rename to src/executor/helpers/run_command_with_log_pipe.rs index b4e43037..caeea7f0 100644 --- a/src/run/runner/helpers/run_command_with_log_pipe.rs +++ b/src/executor/helpers/run_command_with_log_pipe.rs @@ -1,6 +1,6 @@ +use crate::executor::EXECUTOR_TARGET; use crate::local_logger::suspend_progress_bar; use crate::prelude::*; -use crate::run::runner::EXECUTOR_TARGET; use std::future::Future; use std::io::{Read, Write}; use std::process::Command; diff --git a/src/run/runner/helpers/run_with_sudo.rs b/src/executor/helpers/run_with_sudo.rs similarity index 96% rename from src/run/runner/helpers/run_with_sudo.rs rename to src/executor/helpers/run_with_sudo.rs index 811f6dc0..25d77416 100644 --- a/src/run/runner/helpers/run_with_sudo.rs +++ b/src/executor/helpers/run_with_sudo.rs @@ -1,6 +1,5 @@ -use crate::{ - local_logger::suspend_progress_bar, prelude::*, run::runner::helpers::command::CommandBuilder, -}; +use crate::executor::helpers::command::CommandBuilder; +use crate::{local_logger::suspend_progress_bar, prelude::*}; use std::{ ffi::OsStr, io::IsTerminal, diff --git a/src/run/runner/interfaces.rs b/src/executor/interfaces.rs similarity index 86% rename from src/run/runner/interfaces.rs rename to src/executor/interfaces.rs index bc17e089..52c6ed16 100644 --- a/src/run/runner/interfaces.rs +++ b/src/executor/interfaces.rs @@ -1,9 +1,4 @@ use serde::{Deserialize, Serialize}; -use std::path::PathBuf; - -pub struct RunData { - pub profile_folder: PathBuf, -} #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] #[serde(rename_all = "lowercase")] diff --git a/src/run/runner/memory/executor.rs b/src/executor/memory/executor.rs similarity index 82% rename from src/run/runner/memory/executor.rs rename to src/executor/memory/executor.rs index 9c3024c1..4a01678e 100644 --- a/src/run/runner/memory/executor.rs +++ b/src/executor/memory/executor.rs @@ -1,13 +1,13 @@ +use crate::executor::ExecutorName; +use crate::executor::helpers::command::CommandBuilder; +use crate::executor::helpers::get_bench_command::get_bench_command; +use crate::executor::helpers::run_command_with_log_pipe::run_command_with_log_pipe_and_callback; +use crate::executor::helpers::run_with_sudo::wrap_with_sudo; +use crate::executor::shared::fifo::RunnerFifo; +use crate::executor::{ExecutionContext, Executor}; +use crate::instruments::mongo_tracer::MongoTracer; use crate::prelude::*; -use crate::run::instruments::mongo_tracer::MongoTracer; -use crate::run::runner::executor::Executor; -use crate::run::runner::helpers::command::CommandBuilder; -use crate::run::runner::helpers::get_bench_command::get_bench_command; -use crate::run::runner::helpers::run_command_with_log_pipe::run_command_with_log_pipe_and_callback; -use crate::run::runner::helpers::run_with_sudo::wrap_with_sudo; -use crate::run::runner::shared::fifo::RunnerFifo; -use crate::run::runner::{ExecutorName, RunData}; -use crate::run::{check_system::SystemInfo, config::Config}; +use crate::run::check_system::SystemInfo; use async_trait::async_trait; use ipc_channel::ipc; use memtrack::MemtrackIpcClient; @@ -23,8 +23,7 @@ pub struct MemoryExecutor; impl MemoryExecutor { fn build_memtrack_command( - config: &Config, - run_data: &RunData, + execution_context: &ExecutionContext, ) -> Result<(MemtrackIpcServer, CommandBuilder)> { // FIXME: We only support native languages for now @@ -34,9 +33,9 @@ impl MemoryExecutor { let mut cmd_builder = CommandBuilder::new(memtrack_path); cmd_builder.arg("track"); - cmd_builder.arg(get_bench_command(config)?); + cmd_builder.arg(get_bench_command(&execution_context.config)?); cmd_builder.arg("--output"); - cmd_builder.arg(run_data.profile_folder.join("results")); + cmd_builder.arg(execution_context.profile_folder.join("results")); // Setup memtrack IPC server let (ipc_server, server_name) = ipc::IpcOneShotServer::new()?; @@ -77,15 +76,13 @@ impl Executor for MemoryExecutor { async fn run( &self, - config: &Config, - _system_info: &SystemInfo, - run_data: &RunData, + execution_context: &ExecutionContext, _mongo_tracer: &Option, ) -> Result<()> { // Create the results/ directory inside the profile folder to avoid having memtrack create it with wrong permissions - std::fs::create_dir_all(run_data.profile_folder.join("results"))?; + std::fs::create_dir_all(execution_context.profile_folder.join("results"))?; - let (ipc, cmd_builder) = Self::build_memtrack_command(config, run_data)?; + let (ipc, cmd_builder) = Self::build_memtrack_command(execution_context)?; let cmd = wrap_with_sudo(cmd_builder)?.build(); debug!("cmd: {cmd:?}"); @@ -95,7 +92,7 @@ impl Executor for MemoryExecutor { // Directly write to the profile folder, to avoid having to define another field marker_result - .save_to(run_data.profile_folder.join("results")) + .save_to(execution_context.profile_folder.join("results")) .unwrap(); Ok(()) @@ -111,12 +108,7 @@ impl Executor for MemoryExecutor { Ok(()) } - async fn teardown( - &self, - _config: &Config, - _system_info: &SystemInfo, - _run_data: &RunData, - ) -> Result<()> { + async fn teardown(&self, _execution_context: &ExecutionContext) -> Result<()> { Ok(()) } } diff --git a/src/run/runner/memory/mod.rs b/src/executor/memory/mod.rs similarity index 100% rename from src/run/runner/memory/mod.rs rename to src/executor/memory/mod.rs diff --git a/src/executor/mod.rs b/src/executor/mod.rs new file mode 100644 index 00000000..62c3bcae --- /dev/null +++ b/src/executor/mod.rs @@ -0,0 +1,174 @@ +use std::fmt::Display; + +pub mod config; +mod execution_context; +mod helpers; +mod interfaces; +mod memory; +mod shared; +#[cfg(test)] +mod tests; +mod valgrind; +mod wall_time; + +use crate::instruments::mongo_tracer::{MongoTracer, install_mongodb_tracer}; +use crate::prelude::*; +use crate::run::check_system::SystemInfo; +use crate::runner_mode::RunnerMode; +use async_trait::async_trait; +pub use config::Config; +pub use execution_context::ExecutionContext; +pub use helpers::profile_folder::create_profile_folder; +pub use interfaces::ExecutorName; +use memory::executor::MemoryExecutor; +use std::path::Path; +use valgrind::executor::ValgrindExecutor; +use wall_time::executor::WallTimeExecutor; + +impl Display for RunnerMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + #[allow(deprecated)] + RunnerMode::Instrumentation => write!(f, "instrumentation"), + RunnerMode::Simulation => write!(f, "simulation"), + RunnerMode::Walltime => write!(f, "walltime"), + RunnerMode::Memory => write!(f, "memory"), + } + } +} + +pub const EXECUTOR_TARGET: &str = "executor"; + +/// Whether the executor is used for a `run` or an `exec` +/// FIXME: This should not really be a concern for the executor itself +pub enum ExecutorCommand { + Run, + Exec, +} + +pub fn get_executor_from_mode(mode: &RunnerMode, command: ExecutorCommand) -> Box { + match mode { + #[allow(deprecated)] + RunnerMode::Instrumentation | RunnerMode::Simulation => Box::new(ValgrindExecutor), + RunnerMode::Walltime => { + let output_pipedata = match command { + ExecutorCommand::Run => true, + ExecutorCommand::Exec => false, + }; + Box::new(WallTimeExecutor::new_with_output_pipedata(output_pipedata)) + } + RunnerMode::Memory => Box::new(MemoryExecutor), + } +} + +pub fn get_all_executors() -> Vec> { + vec![ + Box::new(ValgrindExecutor), + Box::new(WallTimeExecutor::new()), + Box::new(MemoryExecutor), + ] +} + +#[async_trait(?Send)] +pub trait Executor { + fn name(&self) -> ExecutorName; + + async fn setup( + &self, + _system_info: &SystemInfo, + _setup_cache_dir: Option<&Path>, + ) -> Result<()> { + Ok(()) + } + + /// Runs the executor + async fn run( + &self, + execution_context: &ExecutionContext, + // TODO: use Instruments instead of directly passing the mongodb tracer + mongo_tracer: &Option, + ) -> Result<()>; + + async fn teardown(&self, execution_context: &ExecutionContext) -> Result<()>; +} + +/// Execute benchmarks with the given configuration +/// This is the core execution logic shared between `run` and `exec` commands +pub async fn execute_benchmarks( + executor: &dyn Executor, + execution_context: &mut ExecutionContext, + setup_cache_dir: Option<&Path>, + poll_results: F, +) -> Result<()> +where + F: AsyncFn(String) -> Result<()>, +{ + if !execution_context.config.skip_setup { + start_group!("Preparing the environment"); + executor + .setup(&execution_context.system_info, setup_cache_dir) + .await?; + // TODO: refactor and move directly in the Instruments struct as a `setup` method + if execution_context.config.instruments.is_mongodb_enabled() { + install_mongodb_tracer().await?; + } + info!("Environment ready"); + end_group!(); + } + + if !execution_context.config.skip_run { + start_opened_group!("Running the benchmarks"); + + // TODO: refactor and move directly in the Instruments struct as a `start` method + let mongo_tracer = + if let Some(mongodb_config) = &execution_context.config.instruments.mongodb { + let mut mongo_tracer = + MongoTracer::try_from(&execution_context.profile_folder, mongodb_config)?; + mongo_tracer.start().await?; + Some(mongo_tracer) + } else { + None + }; + + executor.run(execution_context, &mongo_tracer).await?; + + // TODO: refactor and move directly in the Instruments struct as a `stop` method + if let Some(mut mongo_tracer) = mongo_tracer { + mongo_tracer.stop().await?; + } + executor.teardown(execution_context).await?; + + execution_context + .logger + .persist_log_to_profile_folder(execution_context)?; + + end_group!(); + } else { + debug!("Skipping the run of the benchmarks"); + }; + + // Handle upload and polling + if !execution_context.config.skip_upload { + if !execution_context.is_local() { + // If relevant, set the OIDC token for authentication + // Note: OIDC tokens can expire quickly, so we set it just before the upload + execution_context + .provider + .set_oidc_token(&mut execution_context.config) + .await?; + } + + start_group!("Uploading performance data"); + let upload_result = + crate::run::uploader::upload(execution_context, executor.name()).await?; + end_group!(); + + if execution_context.is_local() { + poll_results(upload_result.run_id).await?; + } + } else { + debug!("Skipping upload of performance data"); + } + + Ok(()) +} diff --git a/src/run/runner/shared/fifo.rs b/src/executor/shared/fifo.rs similarity index 100% rename from src/run/runner/shared/fifo.rs rename to src/executor/shared/fifo.rs diff --git a/src/run/runner/shared/mod.rs b/src/executor/shared/mod.rs similarity index 100% rename from src/run/runner/shared/mod.rs rename to src/executor/shared/mod.rs diff --git a/src/run/runner/tests.rs b/src/executor/tests.rs similarity index 67% rename from src/run/runner/tests.rs rename to src/executor/tests.rs index 50de4cf4..8146c123 100644 --- a/src/run/runner/tests.rs +++ b/src/executor/tests.rs @@ -1,10 +1,11 @@ +use super::Config; +use crate::executor::ExecutionContext; +use crate::executor::Executor; +use crate::executor::memory::executor::MemoryExecutor; +use crate::executor::valgrind::executor::ValgrindExecutor; +use crate::executor::wall_time::executor::WallTimeExecutor; use crate::run::check_system::SystemInfo; -use crate::run::config::Config; -use crate::run::runner::executor::Executor; -use crate::run::runner::interfaces::RunData; -use crate::run::runner::memory::executor::MemoryExecutor; -use crate::run::runner::valgrind::executor::ValgrindExecutor; -use crate::run::{RunnerMode, runner::wall_time::executor::WallTimeExecutor}; +use crate::runner_mode::RunnerMode; use rstest_reuse::{self, *}; use shell_quote::{Bash, QuoteRefExt}; use tempfile::TempDir; @@ -120,14 +121,38 @@ fn test_cases(#[case] cmd: &str) {} #[case(ENV_TESTS[7])] fn env_test_cases(#[case] env_case: (&str, &str)) {} -async fn create_test_setup() -> (SystemInfo, RunData, TempDir) { - let system_info = SystemInfo::new().unwrap(); +async fn create_test_setup(config: Config) -> (ExecutionContext, TempDir) { + use crate::config::CodSpeedConfig; + use crate::executor::config::RepositoryOverride; + use crate::run_environment::interfaces::RepositoryProvider; let temp_dir = TempDir::new().unwrap(); - let run_data = RunData { - profile_folder: temp_dir.path().to_path_buf(), - }; - (system_info, run_data, temp_dir) + + // Use try_from to create a proper ExecutionContext with all fields + let codspeed_config = CodSpeedConfig::default(); + let mut config_with_folder = config; + config_with_folder.profile_folder = Some(temp_dir.path().to_path_buf()); + + // Provide a repository override so tests don't need a git repository + if config_with_folder.repository_override.is_none() { + config_with_folder.repository_override = Some(RepositoryOverride { + owner: "test-owner".to_string(), + repository: "test-repo".to_string(), + repository_provider: RepositoryProvider::GitHub, + }); + } + + // Provide a test token so authentication doesn't fail + let mut codspeed_config_with_token = codspeed_config; + if config_with_folder.token.is_none() { + codspeed_config_with_token.auth.token = Some("test-token".to_string()); + } + + let execution_context = + ExecutionContext::try_from((config_with_folder, &codspeed_config_with_token)) + .expect("Failed to create ExecutionContext for test"); + + (execution_context, temp_dir) } // Uprobes set by memtrack, lead to crashes in valgrind because they work by setting breakpoints on the first @@ -173,31 +198,32 @@ mod valgrind { #[apply(test_cases)] #[test_log::test(tokio::test)] async fn test_valgrind_executor(#[case] cmd: &str) { - let (system_info, run_data, _temp_dir) = create_test_setup().await; let (_lock, executor) = get_valgrind_executor().await; let config = valgrind_config(cmd); - executor - .run(&config, &system_info, &run_data, &None) - .await - .unwrap(); + // Unset GITHUB_ACTIONS to force LocalProvider which supports repository_override + temp_env::async_with_vars(&[("GITHUB_ACTIONS", None::<&str>)], async { + let (execution_context, _temp_dir) = create_test_setup(config).await; + executor.run(&execution_context, &None).await.unwrap(); + }) + .await; } #[apply(env_test_cases)] #[test_log::test(tokio::test)] async fn test_valgrind_executor_with_env(#[case] env_case: (&str, &str)) { - let (system_info, run_data, _temp_dir) = create_test_setup().await; let (_lock, executor) = get_valgrind_executor().await; let (env_var, env_value) = env_case; - temp_env::async_with_vars(&[(env_var, Some(env_value))], async { - let cmd = env_var_validation_script(env_var, env_value); - let config = valgrind_config(&cmd); - executor - .run(&config, &system_info, &run_data, &None) - .await - .unwrap(); - }) + temp_env::async_with_vars( + &[(env_var, Some(env_value)), ("GITHUB_ACTIONS", None)], + async { + let cmd = env_var_validation_script(env_var, env_value); + let config = valgrind_config(&cmd); + let (execution_context, _temp_dir) = create_test_setup(config).await; + executor.run(&execution_context, &None).await.unwrap(); + }, + ) .await; } } @@ -240,14 +266,15 @@ mod walltime { #[rstest::rstest] #[test_log::test(tokio::test)] async fn test_walltime_executor(#[case] cmd: &str, #[values(false, true)] enable_perf: bool) { - let (system_info, run_data, _temp_dir) = create_test_setup().await; let (_permit, executor) = get_walltime_executor().await; let config = walltime_config(cmd, enable_perf); - executor - .run(&config, &system_info, &run_data, &None) - .await - .unwrap(); + // Unset GITHUB_ACTIONS to force LocalProvider which supports repository_override + temp_env::async_with_vars(&[("GITHUB_ACTIONS", None::<&str>)], async { + let (execution_context, _temp_dir) = create_test_setup(config).await; + executor.run(&execution_context, &None).await.unwrap(); + }) + .await; } #[apply(env_test_cases)] @@ -257,18 +284,18 @@ mod walltime { #[case] env_case: (&str, &str), #[values(false, true)] enable_perf: bool, ) { - let (system_info, run_data, _temp_dir) = create_test_setup().await; let (_permit, executor) = get_walltime_executor().await; let (env_var, env_value) = env_case; - temp_env::async_with_vars(&[(env_var, Some(env_value))], async { - let cmd = env_var_validation_script(env_var, env_value); - let config = walltime_config(&cmd, enable_perf); - executor - .run(&config, &system_info, &run_data, &None) - .await - .unwrap(); - }) + temp_env::async_with_vars( + &[(env_var, Some(env_value)), ("GITHUB_ACTIONS", None)], + async { + let cmd = env_var_validation_script(env_var, env_value); + let config = walltime_config(&cmd, enable_perf); + let (execution_context, _temp_dir) = create_test_setup(config).await; + executor.run(&execution_context, &None).await.unwrap(); + }, + ) .await; } @@ -276,7 +303,6 @@ mod walltime { #[rstest::rstest] #[test_log::test(tokio::test)] async fn test_walltime_executor_in_working_dir(#[values(false, true)] enable_perf: bool) { - let (system_info, run_data, _temp_dir) = create_test_setup().await; let (_permit, executor) = get_walltime_executor().await; let cmd = r#" @@ -297,22 +323,28 @@ fi ); std::fs::create_dir_all(config.working_directory.as_ref().unwrap()).unwrap(); - executor - .run(&config, &system_info, &run_data, &None) - .await - .unwrap(); + // Unset GITHUB_ACTIONS to force LocalProvider which supports repository_override + temp_env::async_with_vars(&[("GITHUB_ACTIONS", None::<&str>)], async { + let (execution_context, _temp_dir) = create_test_setup(config).await; + executor.run(&execution_context, &None).await.unwrap(); + }) + .await; } // Ensure that commands that fail actually fail #[rstest::rstest] #[test_log::test(tokio::test)] async fn test_walltime_executor_fails(#[values(false, true)] enable_perf: bool) { - let (system_info, run_data, _temp_dir) = create_test_setup().await; let (_permit, executor) = get_walltime_executor().await; let config = walltime_config("exit 1", enable_perf); - let result = executor.run(&config, &system_info, &run_data, &None).await; - assert!(result.is_err(), "Command should fail"); + // Unset GITHUB_ACTIONS to force LocalProvider which supports repository_override + temp_env::async_with_vars(&[("GITHUB_ACTIONS", None::<&str>)], async { + let (execution_context, _temp_dir) = create_test_setup(config).await; + let result = executor.run(&execution_context, &None).await; + assert!(result.is_err(), "Command should fail"); + }) + .await; } } @@ -357,31 +389,32 @@ mod memory { #[apply(test_cases)] #[test_log::test(tokio::test)] async fn test_memory_executor(#[case] cmd: &str) { - let (system_info, run_data, _temp_dir) = create_test_setup().await; let (_permit, _lock, executor) = get_memory_executor().await; - let config = memory_config(cmd); - executor - .run(&config, &system_info, &run_data, &None) - .await - .unwrap(); + // Unset GITHUB_ACTIONS to force LocalProvider which supports repository_override + temp_env::async_with_vars(&[("GITHUB_ACTIONS", None::<&str>)], async { + let config = memory_config(cmd); + let (execution_context, _temp_dir) = create_test_setup(config).await; + executor.run(&execution_context, &None).await.unwrap(); + }) + .await; } #[apply(env_test_cases)] #[test_log::test(tokio::test)] async fn test_memory_executor_with_env(#[case] env_case: (&str, &str)) { - let (system_info, run_data, _temp_dir) = create_test_setup().await; let (_permit, _lock, executor) = get_memory_executor().await; let (env_var, env_value) = env_case; - temp_env::async_with_vars(&[(env_var, Some(env_value))], async { - let cmd = env_var_validation_script(env_var, env_value); - let config = memory_config(&cmd); - executor - .run(&config, &system_info, &run_data, &None) - .await - .unwrap(); - }) + temp_env::async_with_vars( + &[(env_var, Some(env_value)), ("GITHUB_ACTIONS", None)], + async { + let cmd = env_var_validation_script(env_var, env_value); + let config = memory_config(&cmd); + let (execution_context, _temp_dir) = create_test_setup(config).await; + executor.run(&execution_context, &None).await.unwrap(); + }, + ) .await; } } diff --git a/src/run/runner/valgrind/executor.rs b/src/executor/valgrind/executor.rs similarity index 70% rename from src/run/runner/valgrind/executor.rs rename to src/executor/valgrind/executor.rs index da970794..53d76034 100644 --- a/src/run/runner/valgrind/executor.rs +++ b/src/executor/valgrind/executor.rs @@ -1,11 +1,11 @@ use async_trait::async_trait; use std::path::Path; +use crate::executor::Executor; +use crate::executor::{ExecutionContext, ExecutorName}; +use crate::instruments::mongo_tracer::MongoTracer; use crate::prelude::*; -use crate::run::instruments::mongo_tracer::MongoTracer; -use crate::run::runner::executor::Executor; -use crate::run::runner::{ExecutorName, RunData}; -use crate::run::{check_system::SystemInfo, config::Config}; +use crate::run::check_system::SystemInfo; use super::setup::install_valgrind; use super::{helpers::perf_maps::harvest_perf_maps, helpers::venv_compat, measure}; @@ -31,24 +31,22 @@ impl Executor for ValgrindExecutor { async fn run( &self, - config: &Config, - _system_info: &SystemInfo, - run_data: &RunData, + execution_context: &ExecutionContext, mongo_tracer: &Option, ) -> Result<()> { //TODO: add valgrind version check - measure::measure(config, &run_data.profile_folder, mongo_tracer).await?; + measure::measure( + &execution_context.config, + &execution_context.profile_folder, + mongo_tracer, + ) + .await?; Ok(()) } - async fn teardown( - &self, - _config: &Config, - _system_info: &SystemInfo, - run_data: &RunData, - ) -> Result<()> { - harvest_perf_maps(&run_data.profile_folder).await?; + async fn teardown(&self, execution_context: &ExecutionContext) -> Result<()> { + harvest_perf_maps(&execution_context.profile_folder).await?; // No matter the command in input, at this point valgrind will have been run and have produced output files. // diff --git a/src/run/runner/valgrind/helpers/ignored_objects_path.rs b/src/executor/valgrind/helpers/ignored_objects_path.rs similarity index 100% rename from src/run/runner/valgrind/helpers/ignored_objects_path.rs rename to src/executor/valgrind/helpers/ignored_objects_path.rs diff --git a/src/run/runner/valgrind/helpers/mod.rs b/src/executor/valgrind/helpers/mod.rs similarity index 100% rename from src/run/runner/valgrind/helpers/mod.rs rename to src/executor/valgrind/helpers/mod.rs diff --git a/src/run/runner/valgrind/helpers/perf_maps.rs b/src/executor/valgrind/helpers/perf_maps.rs similarity index 100% rename from src/run/runner/valgrind/helpers/perf_maps.rs rename to src/executor/valgrind/helpers/perf_maps.rs diff --git a/src/run/runner/valgrind/helpers/python.rs b/src/executor/valgrind/helpers/python.rs similarity index 100% rename from src/run/runner/valgrind/helpers/python.rs rename to src/executor/valgrind/helpers/python.rs diff --git a/src/run/runner/valgrind/helpers/venv_compat.rs b/src/executor/valgrind/helpers/venv_compat.rs similarity index 100% rename from src/run/runner/valgrind/helpers/venv_compat.rs rename to src/executor/valgrind/helpers/venv_compat.rs diff --git a/src/run/runner/valgrind/helpers/venv_compat.sh b/src/executor/valgrind/helpers/venv_compat.sh similarity index 100% rename from src/run/runner/valgrind/helpers/venv_compat.sh rename to src/executor/valgrind/helpers/venv_compat.sh diff --git a/src/run/runner/valgrind/measure.rs b/src/executor/valgrind/measure.rs similarity index 92% rename from src/run/runner/valgrind/measure.rs rename to src/executor/valgrind/measure.rs index 42758962..03fa01e2 100644 --- a/src/run/runner/valgrind/measure.rs +++ b/src/executor/valgrind/measure.rs @@ -1,13 +1,14 @@ +use crate::executor::Config; +use crate::executor::RunnerMode; +use crate::executor::helpers::env::get_base_injected_env; +use crate::executor::helpers::get_bench_command::get_bench_command; +use crate::executor::helpers::introspected_golang; +use crate::executor::helpers::introspected_nodejs; +use crate::executor::helpers::run_command_with_log_pipe::run_command_with_log_pipe; +use crate::executor::valgrind::helpers::ignored_objects_path::get_objects_path_to_ignore; +use crate::executor::valgrind::helpers::python::is_free_threaded_python; +use crate::instruments::mongo_tracer::MongoTracer; use crate::prelude::*; -use crate::run::runner::RunnerMode; -use crate::run::runner::helpers::env::get_base_injected_env; -use crate::run::runner::helpers::get_bench_command::get_bench_command; -use crate::run::runner::helpers::introspected_golang; -use crate::run::runner::helpers::introspected_nodejs; -use crate::run::runner::helpers::run_command_with_log_pipe::run_command_with_log_pipe; -use crate::run::runner::valgrind::helpers::ignored_objects_path::get_objects_path_to_ignore; -use crate::run::runner::valgrind::helpers::python::is_free_threaded_python; -use crate::run::{config::Config, instruments::mongo_tracer::MongoTracer}; use lazy_static::lazy_static; use std::env; use std::fs::canonicalize; diff --git a/src/run/runner/valgrind/mod.rs b/src/executor/valgrind/mod.rs similarity index 100% rename from src/run/runner/valgrind/mod.rs rename to src/executor/valgrind/mod.rs diff --git a/src/run/runner/valgrind/setup.rs b/src/executor/valgrind/setup.rs similarity index 99% rename from src/run/runner/valgrind/setup.rs rename to src/executor/valgrind/setup.rs index e1da8cf5..21ef20cb 100644 --- a/src/run/runner/valgrind/setup.rs +++ b/src/executor/valgrind/setup.rs @@ -1,4 +1,4 @@ -use crate::run::runner::helpers::apt; +use crate::executor::helpers::apt; use crate::{VALGRIND_CODSPEED_DEB_VERSION, run::check_system::SystemInfo}; use crate::{ VALGRIND_CODSPEED_VERSION, VALGRIND_CODSPEED_VERSION_STRING, prelude::*, diff --git a/src/run/runner/wall_time/executor.rs b/src/executor/wall_time/executor.rs similarity index 80% rename from src/run/runner/wall_time/executor.rs rename to src/executor/wall_time/executor.rs index 4d586817..83288379 100644 --- a/src/run/runner/wall_time/executor.rs +++ b/src/executor/wall_time/executor.rs @@ -1,18 +1,19 @@ use super::helpers::validate_walltime_results; use super::perf::PerfRunner; +use crate::executor::Config; +use crate::executor::Executor; +use crate::executor::helpers::command::CommandBuilder; +use crate::executor::helpers::env::{get_base_injected_env, is_codspeed_debug_enabled}; +use crate::executor::helpers::get_bench_command::get_bench_command; +use crate::executor::helpers::introspected_golang; +use crate::executor::helpers::introspected_nodejs; +use crate::executor::helpers::run_command_with_log_pipe::run_command_with_log_pipe; +use crate::executor::helpers::run_with_sudo::wrap_with_sudo; +use crate::executor::{ExecutionContext, ExecutorName}; +use crate::instruments::mongo_tracer::MongoTracer; use crate::prelude::*; -use crate::run::RunnerMode; -use crate::run::instruments::mongo_tracer::MongoTracer; -use crate::run::runner::executor::Executor; -use crate::run::runner::helpers::command::CommandBuilder; -use crate::run::runner::helpers::env::{get_base_injected_env, is_codspeed_debug_enabled}; -use crate::run::runner::helpers::get_bench_command::get_bench_command; -use crate::run::runner::helpers::introspected_golang; -use crate::run::runner::helpers::introspected_nodejs; -use crate::run::runner::helpers::run_command_with_log_pipe::run_command_with_log_pipe; -use crate::run::runner::helpers::run_with_sudo::wrap_with_sudo; -use crate::run::runner::{ExecutorName, RunData}; -use crate::run::{check_system::SystemInfo, config::Config}; +use crate::run::check_system::SystemInfo; +use crate::runner_mode::RunnerMode; use async_trait::async_trait; use std::fs::canonicalize; use std::io::Write; @@ -95,19 +96,25 @@ pub struct WallTimeExecutor { impl WallTimeExecutor { pub fn new() -> Self { Self { - perf: cfg!(target_os = "linux").then(PerfRunner::new), + perf: cfg!(target_os = "linux").then(|| PerfRunner::new(true)), + } + } + + pub fn new_with_output_pipedata(output_pipedata: bool) -> Self { + Self { + perf: cfg!(target_os = "linux").then(|| PerfRunner::new(output_pipedata)), } } fn walltime_bench_cmd( config: &Config, - run_data: &RunData, + execution_context: &ExecutionContext, ) -> Result<(NamedTempFile, NamedTempFile, CommandBuilder)> { let bench_cmd = get_bench_command(config)?; let system_env = get_exported_system_env()?; let base_injected_env = - get_base_injected_env(RunnerMode::Walltime, &run_data.profile_folder) + get_base_injected_env(RunnerMode::Walltime, &execution_context.profile_folder) .into_iter() .map(|(k, v)| format!("export {k}={v}",)) .collect::>() @@ -179,21 +186,23 @@ impl Executor for WallTimeExecutor { async fn run( &self, - config: &Config, - _system_info: &SystemInfo, - run_data: &RunData, + execution_context: &ExecutionContext, _mongo_tracer: &Option, ) -> Result<()> { let status = { let _guard = HookScriptsGuard::setup(); let (_env_file, _script_file, cmd_builder) = - WallTimeExecutor::walltime_bench_cmd(config, run_data)?; + WallTimeExecutor::walltime_bench_cmd(&execution_context.config, execution_context)?; if let Some(perf) = &self.perf - && config.enable_perf + && execution_context.config.enable_perf { - perf.run(cmd_builder, config, &run_data.profile_folder) - .await + perf.run( + cmd_builder, + &execution_context.config, + &execution_context.profile_folder, + ) + .await } else { let cmd = wrap_with_sudo(cmd_builder)?.build(); debug!("cmd: {cmd:?}"); @@ -211,21 +220,20 @@ impl Executor for WallTimeExecutor { Ok(()) } - async fn teardown( - &self, - config: &Config, - _system_info: &SystemInfo, - run_data: &RunData, - ) -> Result<()> { + async fn teardown(&self, execution_context: &ExecutionContext) -> Result<()> { debug!("Copying files to the profile folder"); if let Some(perf) = &self.perf - && config.enable_perf + && execution_context.config.enable_perf { - perf.save_files_to(&run_data.profile_folder).await?; + perf.save_files_to(&execution_context.profile_folder) + .await?; } - validate_walltime_results(&run_data.profile_folder, config.allow_empty)?; + validate_walltime_results( + &execution_context.profile_folder, + execution_context.config.allow_empty, + )?; Ok(()) } diff --git a/src/run/runner/wall_time/helpers.rs b/src/executor/wall_time/helpers.rs similarity index 100% rename from src/run/runner/wall_time/helpers.rs rename to src/executor/wall_time/helpers.rs diff --git a/src/run/runner/wall_time/mod.rs b/src/executor/wall_time/mod.rs similarity index 100% rename from src/run/runner/wall_time/mod.rs rename to src/executor/wall_time/mod.rs diff --git a/src/run/runner/wall_time/perf/debug_info.rs b/src/executor/wall_time/perf/debug_info.rs similarity index 97% rename from src/run/runner/wall_time/perf/debug_info.rs rename to src/executor/wall_time/perf/debug_info.rs index e3432f60..ec59ffff 100644 --- a/src/run/runner/wall_time/perf/debug_info.rs +++ b/src/executor/wall_time/perf/debug_info.rs @@ -1,5 +1,5 @@ +use crate::executor::wall_time::perf::perf_map::ModuleSymbols; use crate::prelude::*; -use crate::run::runner::wall_time::perf::perf_map::ModuleSymbols; use addr2line::gimli; use object::{Object, ObjectSection}; use runner_shared::debug_info::{DebugInfo, ModuleDebugInfo}; @@ -92,7 +92,7 @@ pub struct ProcessDebugInfo { impl ProcessDebugInfo { pub fn new( - process_symbols: &crate::run::runner::wall_time::perf::perf_map::ProcessSymbols, + process_symbols: &crate::executor::wall_time::perf::perf_map::ProcessSymbols, ) -> Self { let mut modules = Vec::new(); for (path, module_symbols) in process_symbols.modules_with_symbols() { diff --git a/src/run/runner/wall_time/perf/elf_helper.rs b/src/executor/wall_time/perf/elf_helper.rs similarity index 100% rename from src/run/runner/wall_time/perf/elf_helper.rs rename to src/executor/wall_time/perf/elf_helper.rs diff --git a/src/run/runner/wall_time/perf/fifo.rs b/src/executor/wall_time/perf/fifo.rs similarity index 96% rename from src/run/runner/wall_time/perf/fifo.rs rename to src/executor/wall_time/perf/fifo.rs index c82eb695..e3a37bb8 100644 --- a/src/run/runner/wall_time/perf/fifo.rs +++ b/src/executor/wall_time/perf/fifo.rs @@ -2,7 +2,7 @@ use std::ops::Deref; use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use crate::run::runner::shared::fifo::GenericFifo; +use crate::executor::shared::fifo::GenericFifo; pub struct PerfFifo { fifo: GenericFifo, } diff --git a/src/run/runner/wall_time/perf/jit_dump.rs b/src/executor/wall_time/perf/jit_dump.rs similarity index 98% rename from src/run/runner/wall_time/perf/jit_dump.rs rename to src/executor/wall_time/perf/jit_dump.rs index 81dbbb26..a337f263 100644 --- a/src/run/runner/wall_time/perf/jit_dump.rs +++ b/src/executor/wall_time/perf/jit_dump.rs @@ -1,6 +1,6 @@ use crate::{ + executor::wall_time::perf::perf_map::{ModuleSymbols, Symbol}, prelude::*, - run::runner::wall_time::perf::perf_map::{ModuleSymbols, Symbol}, }; use linux_perf_data::jitdump::{JitDumpReader, JitDumpRecord}; use runner_shared::unwind_data::UnwindData; diff --git a/src/executor/wall_time/perf/memory_mappings.rs b/src/executor/wall_time/perf/memory_mappings.rs new file mode 100644 index 00000000..3c1515f7 --- /dev/null +++ b/src/executor/wall_time/perf/memory_mappings.rs @@ -0,0 +1,75 @@ +use super::perf_map::ProcessSymbols; +use super::unwind_data::UnwindDataExt; +use crate::prelude::*; +use libc::pid_t; +use procfs::process::MMPermissions; +use runner_shared::unwind_data::UnwindData; +use std::collections::HashMap; + +#[cfg(target_os = "linux")] +pub(super) fn process_memory_mappings( + pid: pid_t, + symbols_by_pid: &mut HashMap, + unwind_data_by_pid: &mut HashMap>, +) -> anyhow::Result<()> { + let bench_proc = + procfs::process::Process::new(pid as _).expect("Failed to find benchmark process"); + let exe_maps = bench_proc.maps().expect("Failed to read /proc/{pid}/maps"); + + debug!("Process memory mappings for PID {pid}:"); + for map in exe_maps.iter().sorted_by_key(|m| m.address.0) { + let (base_addr, end_addr) = map.address; + debug!( + " {:016x}-{:016x} {:08x} {:?} {:?} ", + base_addr, end_addr, map.offset, map.pathname, map.perms, + ); + } + + for map in &exe_maps { + let page_offset = map.offset; + let (base_addr, end_addr) = map.address; + let path = match &map.pathname { + procfs::process::MMapPath::Path(path) => Some(path.clone()), + _ => None, + }; + + let Some(path) = &path else { + if map.perms.contains(MMPermissions::EXECUTE) { + debug!("Found executable mapping without path: {base_addr:x} - {end_addr:x}"); + } + continue; + }; + + if !map.perms.contains(MMPermissions::EXECUTE) { + continue; + } + + symbols_by_pid + .entry(pid) + .or_insert(ProcessSymbols::new(pid)) + .add_mapping(pid, path, base_addr, end_addr, map.offset); + debug!("Added mapping for module {path:?}"); + + match UnwindData::new( + path.to_string_lossy().as_bytes(), + page_offset, + base_addr, + end_addr, + None, + ) { + Ok(unwind_data) => { + unwind_data_by_pid.entry(pid).or_default().push(unwind_data); + debug!("Added unwind data for {path:?} ({base_addr:x} - {end_addr:x})"); + } + Err(error) => { + debug!( + "Failed to create unwind data for module {}: {}", + path.display(), + error + ); + } + } + } + + Ok(()) +} diff --git a/src/run/runner/wall_time/perf/mod.rs b/src/executor/wall_time/perf/mod.rs similarity index 74% rename from src/run/runner/wall_time/perf/mod.rs rename to src/executor/wall_time/perf/mod.rs index a5a4a05a..cd0dd284 100644 --- a/src/run/runner/wall_time/perf/mod.rs +++ b/src/executor/wall_time/perf/mod.rs @@ -1,21 +1,20 @@ #![cfg_attr(not(unix), allow(dead_code, unused_mut))] +use crate::executor::Config; +use crate::executor::helpers::command::CommandBuilder; +use crate::executor::helpers::env::is_codspeed_debug_enabled; +use crate::executor::helpers::run_command_with_log_pipe::run_command_with_log_pipe_and_callback; +use crate::executor::helpers::run_with_sudo::run_with_sudo; +use crate::executor::helpers::run_with_sudo::wrap_with_sudo; +use crate::executor::shared::fifo::FifoBenchmarkData; +use crate::executor::shared::fifo::RunnerFifo; +use crate::executor::valgrind::helpers::ignored_objects_path::get_objects_path_to_ignore; +use crate::executor::valgrind::helpers::perf_maps::harvest_perf_maps_for_pids; +use crate::executor::wall_time::perf::debug_info::ProcessDebugInfo; +use crate::executor::wall_time::perf::jit_dump::harvest_perf_jit_for_pids; +use crate::executor::wall_time::perf::perf_executable::get_working_perf_executable; use crate::prelude::*; use crate::run::UnwindingMode; -use crate::run::config::Config; -use crate::run::runner::helpers::command::CommandBuilder; -use crate::run::runner::helpers::env::is_codspeed_debug_enabled; -use crate::run::runner::helpers::run_command_with_log_pipe::run_command_with_log_pipe_and_callback; -use crate::run::runner::helpers::run_with_sudo::run_with_sudo; -use crate::run::runner::helpers::run_with_sudo::wrap_with_sudo; -use crate::run::runner::shared::fifo::FifoBenchmarkData; -use crate::run::runner::shared::fifo::RunnerFifo; -use crate::run::runner::valgrind::helpers::ignored_objects_path::get_objects_path_to_ignore; -use crate::run::runner::valgrind::helpers::perf_maps::harvest_perf_maps_for_pids; -use crate::run::runner::wall_time::perf::debug_info::ProcessDebugInfo; -use crate::run::runner::wall_time::perf::jit_dump::harvest_perf_jit_for_pids; -use crate::run::runner::wall_time::perf::perf_executable::get_working_perf_executable; -use crate::run::runner::wall_time::perf::unwind_data::UnwindDataExt; use anyhow::Context; use fifo::PerfFifo; use libc::pid_t; @@ -28,12 +27,15 @@ use runner_shared::fifo::IntegrationMode; use runner_shared::metadata::PerfMetadata; use runner_shared::unwind_data::UnwindData; use std::path::Path; +use std::path::PathBuf; use std::sync::Arc; use std::time::Duration; use std::{cell::OnceCell, collections::HashMap, process::ExitStatus}; use tokio::sync::Mutex; mod jit_dump; +mod memory_mappings; +mod parse_perf_file; mod setup; pub mod debug_info; @@ -44,10 +46,15 @@ pub mod perf_map; pub mod unwind_data; const PERF_METADATA_CURRENT_VERSION: u64 = 1; -const PERF_DATA_FILE_NAME: &str = "perf.pipedata"; +const PERF_DATA_FILE_NAME: &str = "perf.data"; +const PERF_PIPEDATA_FILE_NAME: &str = "perf.pipedata"; pub struct PerfRunner { benchmark_data: OnceCell, + /// Whether to output the perf data to a streamable .pipedata file + /// This can be removed once we have upstreamed the the linux-perf-data crate changes to parse + /// from pipedata directly, to only support pipedata. + output_pipedata: bool, } impl PerfRunner { @@ -82,8 +89,9 @@ impl PerfRunner { Ok(()) } - pub fn new() -> Self { + pub fn new(output_pipedata: bool) -> Self { Self { + output_pipedata, benchmark_data: OnceCell::new(), } } @@ -143,14 +151,26 @@ impl PerfRunner { perf_fifo.ctl_path().to_string_lossy(), perf_fifo.ack_path().to_string_lossy() ), - "-o", - "-", // forces pipe mode - "--", ]); + + if self.output_pipedata { + perf_wrapper_builder.args([ + "-o", "-", // forces pipe mode + ]); + } else { + perf_wrapper_builder.args([ + "-o", + self.get_perf_file_path(profile_folder) + .to_string_lossy() + .as_ref(), + ]); + } + + perf_wrapper_builder.arg("--"); cmd_builder.wrap_with(perf_wrapper_builder); - // Copy the perf data to the profile folder - let perf_data_file_path = profile_folder.join(PERF_DATA_FILE_NAME); + // Output the perf data to the profile folder + let perf_data_file_path = self.get_perf_file_path(profile_folder); let raw_command = format!( "set -o pipefail && {} | cat > {}", @@ -169,7 +189,9 @@ impl PerfRunner { debug!("cmd: {cmd:?}"); let on_process_started = async |_| -> anyhow::Result<()> { - let data = Self::handle_fifo(runner_fifo, perf_fifo).await?; + // If we output pipedata, we do not parse the perf map during teardown yet, so we need to parse memory + // maps as we receive the `CurrentBenchmark` fifo commands. + let data = Self::handle_fifo(runner_fifo, perf_fifo, self.output_pipedata).await?; self.benchmark_data.set(data).unwrap_or_else(|_| { error!("Failed to set benchmark data in PerfRunner"); }); @@ -193,7 +215,8 @@ impl PerfRunner { harvest_perf_jit_for_pids(profile_folder, &bench_data.fifo_data.bench_pids).await?; // Append perf maps, unwind info and other metadata - if let Err(BenchmarkDataSaveError::MissingIntegration) = bench_data.save_to(profile_folder) + if let Err(BenchmarkDataSaveError::MissingIntegration) = + bench_data.save_to(profile_folder, &self.get_perf_file_path(profile_folder)) { warn!( "Perf is enabled, but failed to detect benchmarks. If you wish to disable this warning, set CODSPEED_PERF_ENABLED=false" @@ -206,79 +229,10 @@ impl PerfRunner { Ok(()) } - #[cfg(target_os = "linux")] - fn process_memory_mappings( - pid: pid_t, - symbols_by_pid: &mut HashMap, - unwind_data_by_pid: &mut HashMap>, - ) -> anyhow::Result<()> { - use procfs::process::MMPermissions; - - let bench_proc = - procfs::process::Process::new(pid as _).expect("Failed to find benchmark process"); - let exe_maps = bench_proc.maps().expect("Failed to read /proc/{pid}/maps"); - - debug!("Process memory mappings for PID {pid}:"); - for map in exe_maps.iter().sorted_by_key(|m| m.address.0) { - let (base_addr, end_addr) = map.address; - debug!( - " {:016x}-{:016x} {:08x} {:?} {:?} ", - base_addr, end_addr, map.offset, map.pathname, map.perms, - ); - } - - for map in &exe_maps { - let page_offset = map.offset; - let (base_addr, end_addr) = map.address; - let path = match &map.pathname { - procfs::process::MMapPath::Path(path) => Some(path.clone()), - _ => None, - }; - - let Some(path) = &path else { - if map.perms.contains(MMPermissions::EXECUTE) { - debug!("Found executable mapping without path: {base_addr:x} - {end_addr:x}"); - } - continue; - }; - - if !map.perms.contains(MMPermissions::EXECUTE) { - continue; - } - - symbols_by_pid - .entry(pid) - .or_insert(ProcessSymbols::new(pid)) - .add_mapping(pid, path, base_addr, end_addr, map.offset); - debug!("Added mapping for module {path:?}"); - - match UnwindData::new( - path.to_string_lossy().as_bytes(), - page_offset, - base_addr, - end_addr, - None, - ) { - Ok(unwind_data) => { - unwind_data_by_pid.entry(pid).or_default().push(unwind_data); - debug!("Added unwind data for {path:?} ({base_addr:x} - {end_addr:x})"); - } - Err(error) => { - debug!( - "Failed to create unwind data for module {}: {}", - path.display(), - error - ); - } - } - } - - Ok(()) - } - async fn handle_fifo( mut runner_fifo: RunnerFifo, perf_fifo: PerfFifo, + parse_memory_maps: bool, ) -> anyhow::Result { let mut symbols_by_pid = HashMap::::new(); let mut unwind_data_by_pid = HashMap::>::new(); @@ -311,8 +265,11 @@ impl PerfRunner { } FifoCommand::CurrentBenchmark { pid, .. } => { #[cfg(target_os = "linux")] - if !symbols_by_pid.contains_key(pid) && !unwind_data_by_pid.contains_key(pid) { - Self::process_memory_mappings( + if parse_memory_maps + && !symbols_by_pid.contains_key(pid) + && !unwind_data_by_pid.contains_key(pid) + { + memory_mappings::process_memory_mappings( *pid, &mut symbols_by_pid, &mut unwind_data_by_pid, @@ -346,6 +303,14 @@ impl PerfRunner { unwind_data_by_pid, }) } + + fn get_perf_file_path>(&self, profile_folder: P) -> PathBuf { + if self.output_pipedata { + profile_folder.as_ref().join(PERF_PIPEDATA_FILE_NAME) + } else { + profile_folder.as_ref().join(PERF_DATA_FILE_NAME) + } + } } pub struct BenchmarkData { @@ -358,29 +323,54 @@ pub struct BenchmarkData { #[derive(Debug)] pub enum BenchmarkDataSaveError { MissingIntegration, + FailedToParsePerfFile, } impl BenchmarkData { pub fn save_to>( &self, path: P, + perf_file_path: P, ) -> Result<(), BenchmarkDataSaveError> { self.marker_result.save_to(&path).unwrap(); - for proc_sym in self.symbols_by_pid.values() { + let parsed_perf_map_output = + if self.symbols_by_pid.is_empty() && self.unwind_data_by_pid.is_empty() { + debug!("Reading perf data from file for mmap extraction"); + Some( + parse_perf_file::parse_for_memmap2(perf_file_path).map_err(|e| { + error!("Failed to parse perf file: {e}"); + BenchmarkDataSaveError::FailedToParsePerfFile + })?, + ) + } else { + None + }; + + let (symbols_by_pid, unwind_data_by_pid) = + if let Some(parsed_perf_map_output) = parsed_perf_map_output.as_ref() { + ( + &parsed_perf_map_output.symbols_by_pid, + &parsed_perf_map_output.unwind_data_by_pid, + ) + } else { + (&self.symbols_by_pid, &self.unwind_data_by_pid) + }; + + for proc_sym in symbols_by_pid.values() { proc_sym.save_to(&path).unwrap(); } // Collect debug info for each process by looking up file/line for symbols let mut debug_info_by_pid = HashMap::>::new(); - for (pid, proc_sym) in &self.symbols_by_pid { + for (pid, proc_sym) in symbols_by_pid { debug_info_by_pid .entry(*pid) .or_default() .extend(ProcessDebugInfo::new(proc_sym).modules()); } - for (pid, modules) in &self.unwind_data_by_pid { + for (pid, modules) in unwind_data_by_pid { for module in modules { module.save_to(&path, *pid).unwrap(); } @@ -400,7 +390,7 @@ impl BenchmarkData { // Check if any of the ignored modules has been loaded in the process for ignore_path in get_objects_path_to_ignore() { - for proc in self.symbols_by_pid.values() { + for proc in symbols_by_pid.values() { if let Some(mapping) = proc.module_mapping(&ignore_path) { let (Some((base_addr, _)), Some((_, end_addr))) = ( mapping.iter().min_by_key(|(base_addr, _)| base_addr), @@ -415,7 +405,7 @@ impl BenchmarkData { } // When python is statically linked, we'll not find it in the ignored modules. Add it manually: - let python_modules = self.symbols_by_pid.values().filter_map(|proc| { + let python_modules = symbols_by_pid.values().filter_map(|proc| { proc.loaded_modules().find(|path| { path.file_name() .map(|name| name.to_string_lossy().starts_with("python")) @@ -423,8 +413,7 @@ impl BenchmarkData { }) }); for path in python_modules { - if let Some(mapping) = self - .symbols_by_pid + if let Some(mapping) = symbols_by_pid .values() .find_map(|proc| proc.module_mapping(path)) { diff --git a/src/executor/wall_time/perf/parse_perf_file.rs b/src/executor/wall_time/perf/parse_perf_file.rs new file mode 100644 index 00000000..20f47587 --- /dev/null +++ b/src/executor/wall_time/perf/parse_perf_file.rs @@ -0,0 +1,138 @@ +use super::perf_map::ProcessSymbols; +use super::unwind_data::UnwindDataExt; +use crate::executor::helpers::run_with_sudo::run_with_sudo; +use crate::prelude::*; +use libc::pid_t; +use linux_perf_data::PerfFileReader; +use linux_perf_data::PerfFileRecord; +use linux_perf_data::linux_perf_event_reader::EventRecord; +use runner_shared::unwind_data::UnwindData; +use std::collections::HashMap; +use std::path::Path; + +pub struct MemmapRecordsOutput { + pub symbols_by_pid: HashMap, + pub unwind_data_by_pid: HashMap>, +} + +pub(super) fn parse_for_memmap2>(perf_file_path: P) -> Result { + let mut symbols_by_pid = HashMap::::new(); + let mut unwind_data_by_pid = HashMap::>::new(); + + //FIXME: Remove this once again when we parse directly from pipedata + { + let tmp_perf_file_path = perf_file_path.as_ref().to_string_lossy(); + + // We ran perf with sudo, so we have to change the ownership of the perf.data + run_with_sudo( + "chown", + [ + "-R", + &format!( + "{}:{}", + nix::unistd::Uid::current(), + nix::unistd::Gid::current() + ), + &tmp_perf_file_path, + ], + )?; + } + let reader = std::fs::File::open(perf_file_path.as_ref()).unwrap(); + + let PerfFileReader { + mut perf_file, + mut record_iter, + } = PerfFileReader::parse_file(reader)?; + + while let Some(record) = record_iter.next_record(&mut perf_file).unwrap() { + let PerfFileRecord::EventRecord { record, .. } = record else { + continue; + }; + + let Ok(parsed_record) = record.parse() else { + continue; + }; + + let EventRecord::Mmap2(record) = parsed_record else { + continue; + }; + + let record_path_string = { + let path_slice = record.path.as_slice(); + String::from_utf8_lossy(&path_slice).into_owned() + }; + + let end_addr = record.address + record.length; + + if record_path_string == "//anon" { + // Skip anonymous mappings + trace!( + "Skipping anonymous mapping: {:x}-{:x}", + record.address, end_addr + ); + continue; + } + + if record_path_string.starts_with("[") && record_path_string.ends_with("]") { + // Skip special mappings + trace!( + "Skipping special mapping: {} - {:x}-{:x}", + record_path_string, record.address, end_addr + ); + continue; + } + + debug!( + "Pid {}: {:016x}-{:016x} {:08x} {:?} (Prot {:?})", + record.pid, + record.address, + end_addr, + record.page_offset, + record_path_string, + record.protection, + ); + + if record.protection as i32 & libc::PROT_EXEC == 0 { + continue; + } + + symbols_by_pid + .entry(record.pid) + .or_insert(ProcessSymbols::new(record.pid)) + .add_mapping( + record.pid, + &record_path_string, + record.address, + end_addr, + record.page_offset, + ); + debug!("Added symbols mapping for module {record_path_string:?}"); + + match UnwindData::new( + record_path_string.as_bytes(), + record.page_offset, + record.address, + end_addr, + None, + ) { + Ok(unwind_data) => { + unwind_data_by_pid + .entry(record.pid) + .or_default() + .push(unwind_data); + debug!( + "Added unwind data for {record_path_string} ({:x} - {:x})", + record.address, end_addr + ); + } + Err(error) => { + debug!("Failed to create unwind data for module {record_path_string}: {error}"); + } + } + } + + Ok(MemmapRecordsOutput { + symbols_by_pid, + unwind_data_by_pid, + }) +} diff --git a/src/run/runner/wall_time/perf/perf_executable.rs b/src/executor/wall_time/perf/perf_executable.rs similarity index 100% rename from src/run/runner/wall_time/perf/perf_executable.rs rename to src/executor/wall_time/perf/perf_executable.rs diff --git a/src/run/runner/wall_time/perf/perf_map.rs b/src/executor/wall_time/perf/perf_map.rs similarity index 85% rename from src/run/runner/wall_time/perf/perf_map.rs rename to src/executor/wall_time/perf/perf_map.rs index 100f289c..4cadf0f4 100644 --- a/src/run/runner/wall_time/perf/perf_map.rs +++ b/src/executor/wall_time/perf/perf_map.rs @@ -1,5 +1,5 @@ +use crate::executor::wall_time::perf::elf_helper; use crate::prelude::*; -use crate::run::runner::wall_time::perf::elf_helper; use libc::pid_t; use object::{Object, ObjectSymbol, ObjectSymbolTable}; use std::{ @@ -79,7 +79,30 @@ impl ModuleSymbols { })); } - symbols.retain(|symbol| symbol.addr > 0 && symbol.size > 0); + // Update zero-sized symbols to cover the range until the next symbol + // This is what perf does + // https://github.com/torvalds/linux/blob/e538109ac71d801d26776af5f3c54f548296c29c/tools/perf/util/symbol.c#L256 + // A common source for these is inline assembly functions. + symbols.sort_by_key(|symbol| symbol.addr); + for i in 0..symbols.len() { + if symbols[i].size == 0 { + if i + 1 < symbols.len() { + // Set size to the distance to the next symbol + symbols[i].size = symbols[i + 1].addr.saturating_sub(symbols[i].addr); + } else { + // Last symbol: round up to next 4KB page boundary and add 4KiB + // This matches perf's behavior: roundup(curr->start, 4096) + 4096 + const PAGE_SIZE: u64 = 4096; + let addr = symbols[i].addr; + let end_addr = addr.next_multiple_of(PAGE_SIZE) + PAGE_SIZE; + symbols[i].size = end_addr.saturating_sub(addr); + } + } + } + + // Filter out any symbols that still have zero size + symbols.retain(|symbol| symbol.size > 0); + if symbols.is_empty() { return Err(anyhow::anyhow!("No symbols found")); } @@ -143,7 +166,6 @@ impl ProcessSymbols { return; } - debug!("Loading module symbols at {start_addr:x}-{end_addr:x} (offset: {file_offset:x})"); let path = module_path.as_ref().to_path_buf(); match ModuleSymbols::new(module_path, start_addr, end_addr, file_offset) { Ok(symbol) => { diff --git a/src/run/runner/wall_time/perf/setup.rs b/src/executor/wall_time/perf/setup.rs similarity index 90% rename from src/run/runner/wall_time/perf/setup.rs rename to src/executor/wall_time/perf/setup.rs index 68b03b1b..58ca12c4 100644 --- a/src/run/runner/wall_time/perf/setup.rs +++ b/src/executor/wall_time/perf/setup.rs @@ -1,5 +1,5 @@ -use crate::run::runner::helpers::apt; -use crate::run::runner::wall_time::perf::perf_executable::get_working_perf_executable; +use crate::executor::helpers::apt; +use crate::executor::wall_time::perf::perf_executable::get_working_perf_executable; use crate::{prelude::*, run::check_system::SystemInfo}; use std::{path::Path, process::Command}; diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__cpp_debug_info.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__cpp_debug_info.snap new file mode 100644 index 00000000..532203b7 --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__cpp_debug_info.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9af51e449587563ee665bc0c365c1e92b8fdbbd3182d70d026bc9f91890f06d8 +size 148236 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__golang_debug_info.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__golang_debug_info.snap new file mode 100644 index 00000000..3f10eb99 --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__golang_debug_info.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a80d0a7b245514f01b8719ce71a5776e95c395b1986c5c78cfae948abee23a39 +size 490624 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__ruff_debug_info.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__ruff_debug_info.snap new file mode 100644 index 00000000..7f0e0a9c --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__ruff_debug_info.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be6c5d4b5981911d0ccc635cf0b0e1163ff5879e72b7c3076cac04423856fc68 +size 5996146 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__rust_divan_debug_info.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__rust_divan_debug_info.snap new file mode 100644 index 00000000..d5156bd8 --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__rust_divan_debug_info.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cecbdde3c24b806a75f9e8a438f82419f33f9fcc86b9966352685a35784abec6 +size 520675 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__the_algorithms_debug_info.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__the_algorithms_debug_info.snap new file mode 100644 index 00000000..5305098a --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__debug_info__tests__the_algorithms_debug_info.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da29e6ceb514be0437b501e406016656d2ddbc9f1cd0619ea29df64e7bc69e54 +size 568064 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__cpp_symbols.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__cpp_symbols.snap new file mode 100644 index 00000000..1335d7b7 --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__cpp_symbols.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f03bda66a87fef7b32e3aa6ce67c36527cc01b26731e98dff292ac6f889a9431 +size 104083 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__golang_symbols.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__golang_symbols.snap new file mode 100644 index 00000000..e8d6e6eb --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__golang_symbols.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:008b4129d13e2caaee74de5fc5492fbb9bc0e8168c491fcb730d5a4eb9fdf812 +size 342862 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__ruff_symbols.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__ruff_symbols.snap new file mode 100644 index 00000000..bb56b521 --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__ruff_symbols.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b66645f8ee52271ff108bc73b69558cac3ed642c4ab9e101f99c4354b762f803 +size 5033521 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__rust_divan_symbols.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__rust_divan_symbols.snap new file mode 100644 index 00000000..fc101f0b --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__rust_divan_symbols.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8355b90acf9569acb7288849bda81ec1d10b8457245088ed78835df7d6785fdc +size 499264 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__the_algorithms_symbols.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__the_algorithms_symbols.snap new file mode 100644 index 00000000..a3f1612d --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__perf_map__tests__the_algorithms_symbols.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f74ef3ee84c89fc51e4546c0d8a41fe4565f91004eb80bed83812df31d01e4fb +size 557917 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__cpp_unwind_data.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__cpp_unwind_data.snap new file mode 100644 index 00000000..069543fc --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__cpp_unwind_data.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cfb7b85d124051eb28e4092443c1a6443e7c5972f34fe735ef870bd0797dd1e +size 525 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__golang_unwind_data.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__golang_unwind_data.snap new file mode 100644 index 00000000..0fbfeaa5 --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__golang_unwind_data.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:983cec02d0c182362831e8d5c4bd50eafa5fcdf31274a3bf9cffc16f361be0d9 +size 514 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__ruff_unwind_data.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__ruff_unwind_data.snap new file mode 100644 index 00000000..ab65e89c --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__ruff_unwind_data.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06ee96bd69fe1f0e4cbff05561dced0c3119e648ed0c28f8c59db1b0125d64a1 +size 528 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__rust_divan_unwind_data.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__rust_divan_unwind_data.snap new file mode 100644 index 00000000..f09a6df0 --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__rust_divan_unwind_data.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87e7744313c8f604f31c725c4d0263a4a0e5cfddd63e255ad2bd179686da0721 +size 535 diff --git a/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__the_algorithms_unwind_data.snap b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__the_algorithms_unwind_data.snap new file mode 100644 index 00000000..1d5877e2 --- /dev/null +++ b/src/executor/wall_time/perf/snapshots/codspeed__executor__wall_time__perf__unwind_data__tests__the_algorithms_unwind_data.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:864d09d0abe59ea1145d417592546aec312c75aadc76168f59f7a7d27a902422 +size 531 diff --git a/src/run/runner/wall_time/perf/unwind_data.rs b/src/executor/wall_time/perf/unwind_data.rs similarity index 98% rename from src/run/runner/wall_time/perf/unwind_data.rs rename to src/executor/wall_time/perf/unwind_data.rs index d78931fa..045962ef 100644 --- a/src/run/runner/wall_time/perf/unwind_data.rs +++ b/src/executor/wall_time/perf/unwind_data.rs @@ -1,6 +1,6 @@ //! WARNING: This file has to be in sync with perf-parser! -use crate::run::runner::wall_time::perf::elf_helper; +use crate::executor::wall_time::perf::elf_helper; use anyhow::{Context, bail}; use debugid::CodeId; use object::Object; @@ -11,9 +11,9 @@ use std::ops::Range; pub trait UnwindDataExt { fn new( path_slice: &[u8], - mapping_start_file_offset: u64, - mapping_start_avma: u64, - mapping_size: u64, + runtime_file_offset: u64, + runtime_start_addr: u64, + runtime_end_addr: u64, build_id: Option<&[u8]>, ) -> anyhow::Result where diff --git a/src/run/instruments/mod.rs b/src/instruments/mod.rs similarity index 100% rename from src/run/instruments/mod.rs rename to src/instruments/mod.rs diff --git a/src/run/instruments/mongo_tracer.rs b/src/instruments/mongo_tracer.rs similarity index 100% rename from src/run/instruments/mongo_tracer.rs rename to src/instruments/mongo_tracer.rs diff --git a/src/main.rs b/src/main.rs index a0d77663..a05203d1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,11 +2,16 @@ mod api_client; mod app; mod auth; mod config; +mod exec; +mod executor; +mod instruments; mod local_logger; mod logger; mod prelude; mod request_client; mod run; +mod run_environment; +mod runner_mode; mod setup; use console::style; diff --git a/src/queries/FetchLocalExecReport.gql b/src/queries/FetchLocalExecReport.gql new file mode 100644 index 00000000..146ecae2 --- /dev/null +++ b/src/queries/FetchLocalExecReport.gql @@ -0,0 +1,20 @@ +query FetchLocalRunReport($name: String!, $runId: String!) { + project(name: $name) { + run(id: $runId) { + id + status + url + headReports { + id + impact + conclusion + } + results { + time + benchmark { + name + } + } + } + } +} diff --git a/src/run/config.rs b/src/run/config.rs deleted file mode 100644 index f74c4013..00000000 --- a/src/run/config.rs +++ /dev/null @@ -1,225 +0,0 @@ -use crate::prelude::*; -use crate::run::instruments::Instruments; -use std::path::PathBuf; -use url::Url; - -use crate::run::RunArgs; -use crate::run::run_environment::RepositoryProvider; - -use super::{RunnerMode, UnwindingMode}; - -#[derive(Debug)] -pub struct Config { - pub upload_url: Url, - pub token: Option, - pub repository_override: Option, - pub working_directory: Option, - pub command: String, - - pub mode: RunnerMode, - pub instruments: Instruments, - pub enable_perf: bool, - pub perf_unwinding_mode: Option, - - pub profile_folder: Option, - pub skip_upload: bool, - pub skip_run: bool, - pub skip_setup: bool, - pub allow_empty: bool, -} - -#[derive(Debug, PartialEq, Clone)] -pub struct RepositoryOverride { - pub owner: String, - pub repository: String, - pub repository_provider: RepositoryProvider, -} - -impl Config { - pub fn set_token(&mut self, token: Option) { - self.token = token; - } -} - -#[cfg(test)] -impl Config { - /// Constructs a new `Config` with default values for testing purposes - pub fn test() -> Self { - Self { - upload_url: Url::parse(DEFAULT_UPLOAD_URL).unwrap(), - token: None, - repository_override: None, - working_directory: None, - command: "".into(), - mode: RunnerMode::Simulation, - instruments: Instruments::test(), - perf_unwinding_mode: None, - enable_perf: false, - profile_folder: None, - skip_upload: false, - skip_run: false, - skip_setup: false, - allow_empty: false, - } - } -} - -const DEFAULT_UPLOAD_URL: &str = "https://api.codspeed.io/upload"; - -impl TryFrom for Config { - type Error = Error; - fn try_from(args: RunArgs) -> Result { - let instruments = Instruments::try_from(&args)?; - let raw_upload_url = args.upload_url.unwrap_or_else(|| DEFAULT_UPLOAD_URL.into()); - let upload_url = Url::parse(&raw_upload_url) - .map_err(|e| anyhow!("Invalid upload URL: {raw_upload_url}, {e}"))?; - - Ok(Self { - upload_url, - token: args.token, - repository_override: args - .repository - .map(|respository_and_owner| -> Result { - let (owner, repository) = - extract_owner_and_repository_from_arg(&respository_and_owner)?; - Ok(RepositoryOverride { - owner, - repository, - repository_provider: args.provider.unwrap_or_default(), - }) - }) - .transpose()?, - working_directory: args.working_directory, - mode: args.mode, - instruments, - perf_unwinding_mode: args.perf_run_args.perf_unwinding_mode, - enable_perf: args.perf_run_args.enable_perf, - command: args.command.join(" "), - profile_folder: args.profile_folder, - skip_upload: args.skip_upload, - skip_run: args.skip_run, - skip_setup: args.skip_setup, - allow_empty: args.allow_empty, - }) - } -} - -fn extract_owner_and_repository_from_arg(owner_and_repository: &str) -> Result<(String, String)> { - let (owner, repository) = owner_and_repository - .split_once('/') - .context("Invalid owner/repository format")?; - Ok((owner.to_string(), repository.to_string())) -} - -#[cfg(test)] -mod tests { - use crate::run::PerfRunArgs; - use crate::run::instruments::MongoDBConfig; - - use super::*; - - #[test] - fn test_try_from_env_empty() { - let config = Config::try_from(RunArgs { - upload_url: None, - token: None, - repository: None, - provider: None, - working_directory: None, - mode: RunnerMode::Simulation, - instruments: vec![], - mongo_uri_env_name: None, - message_format: None, - profile_folder: None, - skip_upload: false, - skip_run: false, - skip_setup: false, - allow_empty: false, - perf_run_args: PerfRunArgs { - enable_perf: false, - perf_unwinding_mode: None, - }, - command: vec!["cargo".into(), "codspeed".into(), "bench".into()], - }) - .unwrap(); - assert_eq!(config.upload_url, Url::parse(DEFAULT_UPLOAD_URL).unwrap()); - assert_eq!(config.token, None); - assert_eq!(config.repository_override, None); - assert_eq!(config.working_directory, None); - assert_eq!(config.instruments, Instruments { mongodb: None }); - assert!(!config.skip_upload); - assert!(!config.skip_run); - assert!(!config.skip_setup); - assert!(!config.allow_empty); - assert_eq!(config.command, "cargo codspeed bench"); - } - - #[test] - fn test_try_from_args() { - let config = Config::try_from(RunArgs { - upload_url: Some("https://example.com/upload".into()), - token: Some("token".into()), - repository: Some("owner/repo".into()), - provider: Some(RepositoryProvider::GitLab), - working_directory: Some("/tmp".into()), - mode: RunnerMode::Simulation, - instruments: vec!["mongodb".into()], - mongo_uri_env_name: Some("MONGODB_URI".into()), - message_format: Some(crate::run::MessageFormat::Json), - profile_folder: Some("./codspeed.out".into()), - skip_upload: true, - skip_run: true, - skip_setup: true, - allow_empty: true, - perf_run_args: PerfRunArgs { - enable_perf: false, - perf_unwinding_mode: Some(UnwindingMode::FramePointer), - }, - command: vec!["cargo".into(), "codspeed".into(), "bench".into()], - }) - .unwrap(); - - assert_eq!( - config.upload_url, - Url::parse("https://example.com/upload").unwrap() - ); - assert_eq!(config.token, Some("token".into())); - assert_eq!( - config.repository_override, - Some(RepositoryOverride { - owner: "owner".into(), - repository: "repo".into(), - repository_provider: RepositoryProvider::GitLab, - }) - ); - assert_eq!(config.working_directory, Some("/tmp".into())); - assert_eq!( - config.instruments, - Instruments { - mongodb: Some(MongoDBConfig { - uri_env_name: Some("MONGODB_URI".into()) - }) - } - ); - assert_eq!(config.profile_folder, Some("./codspeed.out".into())); - assert!(config.skip_upload); - assert!(config.skip_run); - assert!(config.skip_setup); - assert!(config.allow_empty); - assert_eq!(config.command, "cargo codspeed bench"); - } - - #[test] - fn test_extract_owner_and_repository_from_arg() { - let owner_and_repository = "CodSpeedHQ/runner"; - let (owner, repository) = - extract_owner_and_repository_from_arg(owner_and_repository).unwrap(); - assert_eq!(owner, "CodSpeedHQ"); - assert_eq!(repository, "runner"); - - let owner_and_repository = "CodSpeedHQ_runner"; - - let result = extract_owner_and_repository_from_arg(owner_and_repository); - assert!(result.is_err()); - } -} diff --git a/src/run/helpers/mod.rs b/src/run/helpers/mod.rs index 9f5d7422..4ef10b7d 100644 --- a/src/run/helpers/mod.rs +++ b/src/run/helpers/mod.rs @@ -3,6 +3,7 @@ mod find_repository_root; mod format_duration; mod get_env_var; mod parse_git_remote; +pub(crate) mod poll_results; pub(crate) use download_file::download_file; pub(crate) use find_repository_root::find_repository_root; diff --git a/src/run/helpers/poll_results.rs b/src/run/helpers/poll_results.rs new file mode 100644 index 00000000..ce51fe44 --- /dev/null +++ b/src/run/helpers/poll_results.rs @@ -0,0 +1,110 @@ +use std::future::Future; +use std::time::Duration; + +use tabled::settings::Style; +use tabled::{Table, Tabled}; +use tokio::time::sleep; + +use crate::prelude::*; +use crate::run::helpers; + +pub const RUN_PROCESSING_MAX_DURATION: Duration = Duration::from_secs(60 * 5); // 5 minutes +pub const POLLING_INTERVAL: Duration = Duration::from_secs(1); +pub const MAX_FETCH_RETRIES: u32 = 3; +pub const FETCH_RETRY_DELAY: Duration = Duration::from_secs(5); + +#[derive(Tabled)] +struct BenchmarkRow { + #[tabled(rename = "Benchmark")] + name: String, + #[tabled(rename = "Time")] + time: String, +} + +pub fn build_benchmark_table( + results: &[crate::api_client::FetchLocalRunBenchmarkResult], +) -> String { + let table_rows: Vec = results + .iter() + .map(|result| BenchmarkRow { + name: result.benchmark.name.clone(), + time: helpers::format_duration(result.time, Some(2)), + }) + .collect(); + + Table::new(&table_rows).with(Style::modern()).to_string() +} + +/// Retry logic for API calls that may timeout due to cold start in dev environments +pub async fn retry_on_timeout(fetch_fn: F) -> Result +where + F: Fn() -> Fut, + Fut: Future>, +{ + let mut fetch_attempt = 0; + loop { + fetch_attempt += 1; + match fetch_fn().await { + Ok(result) => return Ok(result), + Err(err) => { + let error_message = err.to_string(); + let is_timeout = + error_message.contains("timed out") || error_message.contains("timeout"); + + if is_timeout && fetch_attempt < MAX_FETCH_RETRIES { + debug!( + "Fetch request timed out (attempt {fetch_attempt}/{MAX_FETCH_RETRIES}), retrying in {FETCH_RETRY_DELAY:?}..." + ); + sleep(FETCH_RETRY_DELAY).await; + continue; + } + + return Err(err); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::api_client::{FetchLocalRunBenchmark, FetchLocalRunBenchmarkResult}; + + #[test] + fn test_benchmark_table_formatting() { + let results = vec![ + FetchLocalRunBenchmarkResult { + benchmark: FetchLocalRunBenchmark { + name: "benchmark_fast".to_string(), + }, + time: 0.001234, // 1.23 ms + }, + FetchLocalRunBenchmarkResult { + benchmark: FetchLocalRunBenchmark { + name: "benchmark_slow".to_string(), + }, + time: 1.5678, // 1.57 s + }, + FetchLocalRunBenchmarkResult { + benchmark: FetchLocalRunBenchmark { + name: "benchmark_medium".to_string(), + }, + time: 0.000567, // 567 ยตs + }, + ]; + + let table = build_benchmark_table(&results); + + insta::assert_snapshot!(table, @r###" + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Benchmark โ”‚ Time โ”‚ + โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค + โ”‚ benchmark_fast โ”‚ 1.23 ms โ”‚ + โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค + โ”‚ benchmark_slow โ”‚ 1.57 s โ”‚ + โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค + โ”‚ benchmark_medium โ”‚ 567.00 ยตs โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + "###); + } +} diff --git a/src/run/logger.rs b/src/run/logger.rs index dfcef7a2..ba8786fc 100644 --- a/src/run/logger.rs +++ b/src/run/logger.rs @@ -1,6 +1,7 @@ +use crate::executor::ExecutionContext; use crate::logger::{GROUP_TARGET, OPENED_GROUP_TARGET}; use crate::prelude::*; -use crate::run::{run_environment::RunEnvironmentProvider, runner::RunData}; +use crate::run_environment::RunEnvironmentProvider; use log::LevelFilter; use simplelog::{CombinedLogger, WriteLogger}; use std::fs::copy; @@ -12,24 +13,32 @@ pub struct Logger { } impl Logger { - #[allow(clippy::borrowed_box)] - pub fn new(provider: &Box) -> Result { + pub fn new(provider: &dyn RunEnvironmentProvider) -> Result { let provider_logger = provider.get_logger(); let log_file = NamedTempFile::new().context("Failed to create log file")?; let log_file_path = log_file.path().to_path_buf(); + let file_logger_config = simplelog::ConfigBuilder::new() // Groups are not logged to the file .add_filter_ignore_str(GROUP_TARGET) .add_filter_ignore_str(OPENED_GROUP_TARGET) .build(); let file_logger = WriteLogger::new(LevelFilter::Trace, file_logger_config, log_file); - CombinedLogger::init(vec![provider_logger, file_logger]) - .context("Failed to init logger")?; + + if let Err(_e) = CombinedLogger::init(vec![provider_logger, file_logger]) { + // In tests, test_log already initializes a logger, so we ignore the error + #[cfg(not(test))] + return Err(anyhow::Error::from(_e).context("Failed to init logger")); + } + Ok(Self { log_file_path }) } - pub fn persist_log_to_profile_folder(&self, run_data: &RunData) -> Result<()> { - let profile_folder = run_data.profile_folder.clone(); + pub fn persist_log_to_profile_folder( + &self, + execution_context: &ExecutionContext, + ) -> Result<()> { + let profile_folder = execution_context.profile_folder.clone(); let dest_log_file_path = profile_folder.join("runner.log"); debug!("Persisting log file to {}", dest_log_file_path.display()); log::logger().flush(); diff --git a/src/run/mod.rs b/src/run/mod.rs index 64b8c333..f6419645 100644 --- a/src/run/mod.rs +++ b/src/run/mod.rs @@ -1,29 +1,23 @@ use crate::VERSION; use crate::api_client::CodSpeedAPIClient; use crate::config::CodSpeedConfig; +use crate::executor; +use crate::executor::Config; use crate::prelude::*; -use crate::run::{config::Config, logger::Logger}; -use check_system::SystemInfo; +use crate::run_environment::interfaces::RepositoryProvider; +use crate::runner_mode::RunnerMode; use clap::{Args, ValueEnum}; -use instruments::mongo_tracer::{MongoTracer, install_mongodb_tracer}; -use run_environment::interfaces::{RepositoryProvider, RunEnvironment}; -use runner::get_run_data; -use serde::Serialize; use std::path::Path; use std::path::PathBuf; pub mod check_system; pub mod helpers; -mod instruments; -mod poll_results; -pub mod run_environment; -pub mod runner; -mod uploader; +pub(crate) mod poll_results; +pub(crate) mod uploader; -pub mod config; pub mod logger; -fn show_banner() { +pub(crate) fn show_banner() { let banner = format!( r#" ______ __ _____ __ @@ -54,15 +48,16 @@ pub struct PerfRunArgs { /// Enable the linux perf profiler to collect granular performance data. /// This is only supported on Linux. #[arg(long, env = "CODSPEED_PERF_ENABLED", default_value_t = true)] - enable_perf: bool, + pub enable_perf: bool, /// The unwinding mode that should be used with perf to collect the call stack. #[arg(long, env = "CODSPEED_PERF_UNWINDING_MODE")] - perf_unwinding_mode: Option, + pub perf_unwinding_mode: Option, } -#[derive(Args, Debug)] -pub struct RunArgs { +/// Arguments shared between run and exec commands +#[derive(Args, Debug, Clone)] +pub struct ExecAndRunSharedArgs { /// The upload URL to use for uploading the results, useful for on-premises installations #[arg(long, env = "CODSPEED_UPLOAD_URL")] pub upload_url: Option, @@ -95,24 +90,10 @@ pub struct RunArgs { #[arg(short, long, value_enum, env = "CODSPEED_RUNNER_MODE")] pub mode: RunnerMode, - /// Comma-separated list of instruments to enable. Possible values: mongodb. - #[arg(long, value_delimiter = ',')] - pub instruments: Vec, - - /// The name of the environment variable that contains the MongoDB URI to patch. - /// If not provided, user will have to provide it dynamically through a CodSpeed integration. - /// - /// Only used if the `mongodb` instrument is enabled. - #[arg(long)] - pub mongo_uri_env_name: Option, - /// Profile folder to use for the run. #[arg(long)] pub profile_folder: Option, - #[arg(long, hide = true)] - pub message_format: Option, - /// Only for debugging purposes, skips the upload of the results #[arg( long, @@ -121,6 +102,7 @@ pub struct RunArgs { env = "CODSPEED_SKIP_UPLOAD" )] pub skip_upload: bool, + /// Used internally to upload the results after running the benchmarks in a sandbox environment /// with no internet access #[arg(long, default_value = "false", hide = true)] @@ -136,21 +118,31 @@ pub struct RunArgs { #[command(flatten)] pub perf_run_args: PerfRunArgs, +} + +#[derive(Args, Debug)] +pub struct RunArgs { + #[command(flatten)] + pub shared: ExecAndRunSharedArgs, + + /// Comma-separated list of instruments to enable. Possible values: mongodb. + #[arg(long, value_delimiter = ',')] + pub instruments: Vec, + + /// The name of the environment variable that contains the MongoDB URI to patch. + /// If not provided, user will have to provide it dynamically through a CodSpeed integration. + /// + /// Only used if the `mongodb` instrument is enabled. + #[arg(long)] + pub mongo_uri_env_name: Option, + + #[arg(long, hide = true)] + pub message_format: Option, /// The bench command to run pub command: Vec, } -#[derive(ValueEnum, Clone, Debug, Serialize, PartialEq)] -#[serde(rename_all = "lowercase")] -pub enum RunnerMode { - #[deprecated(note = "Use `RunnerMode::Simulation` instead")] - Instrumentation, - Simulation, - Walltime, - Memory, -} - #[derive(ValueEnum, Clone, Debug, PartialEq)] pub enum MessageFormat { Json, @@ -161,24 +153,26 @@ impl RunArgs { /// Constructs a new `RunArgs` with default values for testing purposes pub fn test() -> Self { Self { - upload_url: None, - token: None, - repository: None, - provider: None, - working_directory: None, - mode: RunnerMode::Simulation, + shared: ExecAndRunSharedArgs { + upload_url: None, + token: None, + repository: None, + provider: None, + working_directory: None, + mode: RunnerMode::Simulation, + profile_folder: None, + skip_upload: false, + skip_run: false, + skip_setup: false, + allow_empty: false, + perf_run_args: PerfRunArgs { + enable_perf: false, + perf_unwinding_mode: None, + }, + }, instruments: vec![], mongo_uri_env_name: None, message_format: None, - profile_folder: None, - skip_upload: false, - skip_run: false, - skip_setup: false, - allow_empty: false, - perf_run_args: PerfRunArgs { - enable_perf: false, - perf_unwinding_mode: None, - }, command: vec![], } } @@ -191,98 +185,33 @@ pub async fn run( setup_cache_dir: Option<&Path>, ) -> Result<()> { let output_json = args.message_format == Some(MessageFormat::Json); - let mut config = Config::try_from(args)?; - let mut provider = run_environment::get_provider(&config)?; - let logger = Logger::new(&provider)?; - - #[allow(deprecated)] - if config.mode == RunnerMode::Instrumentation { - warn!( - "The 'instrumentation' runner mode is deprecated and will be removed in a future version. \ - Please use 'simulation' instead." - ); - } - - if provider.get_run_environment() != RunEnvironment::Local { - show_banner(); - } - debug!("config: {config:#?}"); - - if provider.get_run_environment() == RunEnvironment::Local { - if codspeed_config.auth.token.is_none() { - bail!("You have to authenticate the CLI first. Run `codspeed auth login`."); - } - debug!("Using the token from the CodSpeed configuration file"); - config.set_token(codspeed_config.auth.token.clone()); - } else { - provider.check_oidc_configuration(&config)?; - } + let config = Config::try_from(args)?; - let system_info = SystemInfo::new()?; - check_system::check_system(&system_info)?; + // Create execution context + let mut execution_context = executor::ExecutionContext::try_from((config, codspeed_config))?; - let executor = runner::get_executor_from_mode(&config.mode); - - if !config.skip_setup { - start_group!("Preparing the environment"); - executor.setup(&system_info, setup_cache_dir).await?; - // TODO: refactor and move directly in the Instruments struct as a `setup` method - if config.instruments.is_mongodb_enabled() { - install_mongodb_tracer().await?; - } - info!("Environment ready"); - end_group!(); + if !execution_context.is_local() { + show_banner(); } + debug!("config: {:#?}", execution_context.config); - let run_data = get_run_data(&config)?; - - if !config.skip_run { - start_opened_group!("Running the benchmarks"); - - // TODO: refactor and move directly in the Instruments struct as a `start` method - let mongo_tracer = if let Some(mongodb_config) = &config.instruments.mongodb { - let mut mongo_tracer = MongoTracer::try_from(&run_data.profile_folder, mongodb_config)?; - mongo_tracer.start().await?; - Some(mongo_tracer) - } else { - None - }; - - executor - .run(&config, &system_info, &run_data, &mongo_tracer) - .await?; - - // TODO: refactor and move directly in the Instruments struct as a `stop` method - if let Some(mut mongo_tracer) = mongo_tracer { - mongo_tracer.stop().await?; - } - executor.teardown(&config, &system_info, &run_data).await?; - - logger.persist_log_to_profile_folder(&run_data)?; + // Execute benchmarks + let executor = executor::get_executor_from_mode( + &execution_context.config.mode, + executor::ExecutorCommand::Run, + ); - end_group!(); - } else { - debug!("Skipping the run of the benchmarks"); + let run_environment_metadata = execution_context.provider.get_run_environment_metadata()?; + let poll_results_fn = |run_id: String| { + poll_results::poll_results(api_client, &run_environment_metadata, run_id, output_json) }; - - if !config.skip_upload { - start_group!("Uploading performance data"); - let upload_result = uploader::upload( - &mut config, - &system_info, - &provider, - &run_data, - executor.name(), - ) - .await?; - end_group!(); - - if provider.get_run_environment() == RunEnvironment::Local { - poll_results::poll_results(api_client, &provider, upload_result.run_id, output_json) - .await?; - end_group!(); - } - } + executor::execute_benchmarks( + executor.as_ref(), + &mut execution_context, + setup_cache_dir, + poll_results_fn, + ) + .await?; Ok(()) } diff --git a/src/run/poll_results.rs b/src/run/poll_results.rs index 06319cfd..bb0c6fa7 100644 --- a/src/run/poll_results.rs +++ b/src/run/poll_results.rs @@ -1,56 +1,29 @@ -use std::time::Duration; - use console::style; -use tabled::settings::Style; -use tabled::{Table, Tabled}; use tokio::time::{Instant, sleep}; use crate::api_client::{ CodSpeedAPIClient, FetchLocalRunReportResponse, FetchLocalRunReportVars, RunStatus, }; use crate::prelude::*; -use crate::run::helpers; - -use super::run_environment::RunEnvironmentProvider; - -const RUN_PROCESSING_MAX_DURATION: Duration = Duration::from_secs(60 * 5); // 5 minutes -const POLLING_INTERVAL: Duration = Duration::from_secs(1); - -#[derive(Tabled)] -struct BenchmarkRow { - #[tabled(rename = "Benchmark")] - name: String, - #[tabled(rename = "Time")] - time: String, -} - -fn build_benchmark_table(results: &[crate::api_client::FetchLocalRunBenchmarkResult]) -> String { - let table_rows: Vec = results - .iter() - .map(|result| BenchmarkRow { - name: result.benchmark.name.clone(), - time: helpers::format_duration(result.time, Some(2)), - }) - .collect(); - - Table::new(&table_rows).with(Style::modern()).to_string() -} +use crate::run::helpers::poll_results::{ + POLLING_INTERVAL, RUN_PROCESSING_MAX_DURATION, build_benchmark_table, retry_on_timeout, +}; +use crate::run_environment::RunEnvironmentMetadata; #[allow(clippy::borrowed_box)] pub async fn poll_results( api_client: &CodSpeedAPIClient, - provider: &Box, + run_environment_metadata: &RunEnvironmentMetadata, run_id: String, output_json: bool, ) -> Result<()> { let start = Instant::now(); - let run_environment_metadata = provider.get_run_environment_metadata()?; - let owner = run_environment_metadata.owner; - let name = run_environment_metadata.repository; + let owner = run_environment_metadata.owner.as_str(); + let name = run_environment_metadata.repository.as_str(); let fetch_local_run_report_vars = FetchLocalRunReportVars { - owner: owner.clone(), - name: name.clone(), - run_id: run_id.clone(), + owner: owner.to_owned(), + name: name.to_owned(), + run_id: run_id.to_owned(), }; start_group!("Fetching the results"); @@ -60,10 +33,14 @@ pub async fn poll_results( bail!("Polling results timed out"); } - match api_client - .fetch_local_run_report(fetch_local_run_report_vars.clone()) - .await? - { + let fetch_result = retry_on_timeout(|| async { + api_client + .fetch_local_run_report(fetch_local_run_report_vars.clone()) + .await + }) + .await?; + + match fetch_result { FetchLocalRunReportResponse { run, .. } if run.status == RunStatus::Pending || run.status == RunStatus::Processing => { @@ -140,47 +117,3 @@ pub async fn poll_results( Ok(()) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::api_client::{FetchLocalRunBenchmark, FetchLocalRunBenchmarkResult}; - - #[test] - fn test_benchmark_table_formatting() { - let results = vec![ - FetchLocalRunBenchmarkResult { - benchmark: FetchLocalRunBenchmark { - name: "benchmark_fast".to_string(), - }, - time: 0.001234, // 1.23 ms - }, - FetchLocalRunBenchmarkResult { - benchmark: FetchLocalRunBenchmark { - name: "benchmark_slow".to_string(), - }, - time: 1.5678, // 1.57 s - }, - FetchLocalRunBenchmarkResult { - benchmark: FetchLocalRunBenchmark { - name: "benchmark_medium".to_string(), - }, - time: 0.000567, // 567 ยตs - }, - ]; - - let table = build_benchmark_table(&results); - - insta::assert_snapshot!(table, @r###" - โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” - โ”‚ Benchmark โ”‚ Time โ”‚ - โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค - โ”‚ benchmark_fast โ”‚ 1.23 ms โ”‚ - โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค - โ”‚ benchmark_slow โ”‚ 1.57 s โ”‚ - โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค - โ”‚ benchmark_medium โ”‚ 567.00 ยตs โ”‚ - โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - "###); - } -} diff --git a/src/run/run_environment/buildkite/snapshots/codspeed__run__run_environment__buildkite__provider__tests__pull_request_run_environment_metadata.snap b/src/run/run_environment/buildkite/snapshots/codspeed__run__run_environment__buildkite__provider__tests__pull_request_run_environment_metadata.snap deleted file mode 100644 index 634ff292..00000000 --- a/src/run/run_environment/buildkite/snapshots/codspeed__run__run_environment__buildkite__provider__tests__pull_request_run_environment_metadata.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: src/run/run_environment/buildkite/provider.rs -expression: run_environment_metadata ---- -{ - "ref": "refs/pull/22/merge", - "headRef": "feat/codspeed-runner", - "baseRef": "main", - "owner": "my-org", - "repository": "adrien-python-test", - "event": "pull_request", - "sender": null, - "ghData": null, - "glData": null, - "repositoryRootPath": "/buildkite/builds/7b10eca7600b-1/my-org/buildkite-test/" -} diff --git a/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__fork_pull_request_run_environment_metadata-2.snap b/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__fork_pull_request_run_environment_metadata-2.snap deleted file mode 100644 index 00d51e01..00000000 --- a/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__fork_pull_request_run_environment_metadata-2.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: src/run/run_environment/github_actions/provider.rs -expression: run_part ---- -{ - "runId": "6957110437", - "runPartId": "log-env", - "jobName": "log-env", - "metadata": {} -} diff --git a/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__fork_pull_request_run_environment_metadata.snap b/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__fork_pull_request_run_environment_metadata.snap deleted file mode 100644 index 7154fdf6..00000000 --- a/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__fork_pull_request_run_environment_metadata.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: src/run/run_environment/github_actions/provider.rs -expression: run_environment_metadata ---- -{ - "ref": "refs/pull/22/merge", - "headRef": "fork-owner:feat/codspeed-runner", - "baseRef": "main", - "owner": "my-org", - "repository": "adrien-python-test", - "event": "pull_request", - "sender": { - "id": "19605940", - "login": "adriencaccia" - }, - "ghData": { - "runId": "6957110437", - "job": "log-env" - }, - "glData": null, - "repositoryRootPath": "/home/runner/work/adrien-python-test/adrien-python-test/" -} diff --git a/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__matrix_job_run_environment_metadata-2.snap b/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__matrix_job_run_environment_metadata-2.snap deleted file mode 100644 index 737ea261..00000000 --- a/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__matrix_job_run_environment_metadata-2.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: src/run/run_environment/github_actions/provider.rs -expression: run_part ---- -{ - "runId": "6957110437", - "runPartId": "log-env-{\"runner-version\":\"3.2.1\",\"numeric-value\":123456789}-{\"job-total\":2,\"job-index\":1}", - "jobName": "log-env", - "metadata": { - "job-index": 1, - "job-total": 2, - "numeric-value": 123456789, - "runner-version": "3.2.1" - } -} diff --git a/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__matrix_job_run_environment_metadata.snap b/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__matrix_job_run_environment_metadata.snap deleted file mode 100644 index 45b5b79c..00000000 --- a/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__matrix_job_run_environment_metadata.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: src/run/run_environment/github_actions/provider.rs -expression: run_environment_metadata ---- -{ - "ref": "refs/pull/22/merge", - "headRef": "feat/codspeed-runner", - "baseRef": "main", - "owner": "my-org", - "repository": "adrien-python-test", - "event": "pull_request", - "sender": { - "id": "19605940", - "login": "adriencaccia" - }, - "ghData": { - "runId": "6957110437", - "job": "log-env" - }, - "glData": null, - "repositoryRootPath": "/home/runner/work/adrien-python-test/adrien-python-test/" -} diff --git a/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__pull_request_run_environment_metadata-2.snap b/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__pull_request_run_environment_metadata-2.snap deleted file mode 100644 index 00d51e01..00000000 --- a/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__pull_request_run_environment_metadata-2.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: src/run/run_environment/github_actions/provider.rs -expression: run_part ---- -{ - "runId": "6957110437", - "runPartId": "log-env", - "jobName": "log-env", - "metadata": {} -} diff --git a/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__pull_request_run_environment_metadata.snap b/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__pull_request_run_environment_metadata.snap deleted file mode 100644 index 45b5b79c..00000000 --- a/src/run/run_environment/github_actions/snapshots/codspeed__run__run_environment__github_actions__provider__tests__pull_request_run_environment_metadata.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: src/run/run_environment/github_actions/provider.rs -expression: run_environment_metadata ---- -{ - "ref": "refs/pull/22/merge", - "headRef": "feat/codspeed-runner", - "baseRef": "main", - "owner": "my-org", - "repository": "adrien-python-test", - "event": "pull_request", - "sender": { - "id": "19605940", - "login": "adriencaccia" - }, - "ghData": { - "runId": "6957110437", - "job": "log-env" - }, - "glData": null, - "repositoryRootPath": "/home/runner/work/adrien-python-test/adrien-python-test/" -} diff --git a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__fork_merge_request_provider_metadata.snap b/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__fork_merge_request_provider_metadata.snap deleted file mode 100644 index 4e408c8b..00000000 --- a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__fork_merge_request_provider_metadata.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: src/run/run_environment/gitlab_ci/provider.rs -expression: run_environment_metadata ---- -{ - "ref": "refs/pull/5/merge", - "headRef": "fork-owner:feat/awesome-feature", - "baseRef": "main", - "owner": "owner", - "repository": "repository", - "event": "pull_request", - "sender": { - "id": "19605940", - "login": "actor" - }, - "ghData": null, - "glData": { - "runId": "6957110437", - "job": "build-job" - }, - "repositoryRootPath": "/builds/owner/repository" -} diff --git a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__fork_merge_request_run_environment_metadata-2.snap b/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__fork_merge_request_run_environment_metadata-2.snap deleted file mode 100644 index 23423e53..00000000 --- a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__fork_merge_request_run_environment_metadata-2.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: src/run/run_environment/gitlab_ci/provider.rs -expression: run_part ---- -{ - "runId": "6957110437", - "runPartId": "build-job", - "jobName": "build-job", - "metadata": {} -} diff --git a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__fork_merge_request_run_environment_metadata.snap b/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__fork_merge_request_run_environment_metadata.snap deleted file mode 100644 index 4e408c8b..00000000 --- a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__fork_merge_request_run_environment_metadata.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: src/run/run_environment/gitlab_ci/provider.rs -expression: run_environment_metadata ---- -{ - "ref": "refs/pull/5/merge", - "headRef": "fork-owner:feat/awesome-feature", - "baseRef": "main", - "owner": "owner", - "repository": "repository", - "event": "pull_request", - "sender": { - "id": "19605940", - "login": "actor" - }, - "ghData": null, - "glData": { - "runId": "6957110437", - "job": "build-job" - }, - "repositoryRootPath": "/builds/owner/repository" -} diff --git a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__merge_request_provider_metadata.snap b/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__merge_request_provider_metadata.snap deleted file mode 100644 index ae588919..00000000 --- a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__merge_request_provider_metadata.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: src/run/run_environment/gitlab_ci/provider.rs -expression: run_environment_metadata ---- -{ - "ref": "refs/pull/5/merge", - "headRef": "feat/awesome-feature", - "baseRef": "main", - "owner": "owner", - "repository": "repository", - "event": "pull_request", - "sender": { - "id": "19605940", - "login": "actor" - }, - "ghData": null, - "glData": { - "runId": "6957110437", - "job": "build-job" - }, - "repositoryRootPath": "/builds/owner/repository" -} diff --git a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__merge_request_run_environment_metadata-2.snap b/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__merge_request_run_environment_metadata-2.snap deleted file mode 100644 index 23423e53..00000000 --- a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__merge_request_run_environment_metadata-2.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: src/run/run_environment/gitlab_ci/provider.rs -expression: run_part ---- -{ - "runId": "6957110437", - "runPartId": "build-job", - "jobName": "build-job", - "metadata": {} -} diff --git a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__merge_request_run_environment_metadata.snap b/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__merge_request_run_environment_metadata.snap deleted file mode 100644 index ae588919..00000000 --- a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__merge_request_run_environment_metadata.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: src/run/run_environment/gitlab_ci/provider.rs -expression: run_environment_metadata ---- -{ - "ref": "refs/pull/5/merge", - "headRef": "feat/awesome-feature", - "baseRef": "main", - "owner": "owner", - "repository": "repository", - "event": "pull_request", - "sender": { - "id": "19605940", - "login": "actor" - }, - "ghData": null, - "glData": { - "runId": "6957110437", - "job": "build-job" - }, - "repositoryRootPath": "/builds/owner/repository" -} diff --git a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__push_main_provider_metadata.snap b/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__push_main_provider_metadata.snap deleted file mode 100644 index de2361e9..00000000 --- a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__push_main_provider_metadata.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: src/run/run_environment/gitlab_ci/provider.rs -expression: run_environment_metadata ---- -{ - "ref": "refs/heads/main", - "headRef": null, - "baseRef": "main", - "owner": "owner", - "repository": "repository", - "event": "push", - "sender": { - "id": "1234567890", - "login": "actor" - }, - "ghData": null, - "glData": { - "runId": "1234567890", - "job": "job" - }, - "repositoryRootPath": "/builds/owner/repository" -} diff --git a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__push_main_run_environment_metadata-2.snap b/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__push_main_run_environment_metadata-2.snap deleted file mode 100644 index ce460485..00000000 --- a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__push_main_run_environment_metadata-2.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: src/run/run_environment/gitlab_ci/provider.rs -expression: run_part ---- -{ - "runId": "1234567890", - "runPartId": "job", - "jobName": "job", - "metadata": {} -} diff --git a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__push_main_run_environment_metadata.snap b/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__push_main_run_environment_metadata.snap deleted file mode 100644 index de2361e9..00000000 --- a/src/run/run_environment/gitlab_ci/snapshots/codspeed__run__run_environment__gitlab_ci__provider__tests__push_main_run_environment_metadata.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: src/run/run_environment/gitlab_ci/provider.rs -expression: run_environment_metadata ---- -{ - "ref": "refs/heads/main", - "headRef": null, - "baseRef": "main", - "owner": "owner", - "repository": "repository", - "event": "push", - "sender": { - "id": "1234567890", - "login": "actor" - }, - "ghData": null, - "glData": { - "runId": "1234567890", - "job": "job" - }, - "repositoryRootPath": "/builds/owner/repository" -} diff --git a/src/run/run_environment/mod.rs b/src/run/run_environment/mod.rs deleted file mode 100644 index f2ac6b80..00000000 --- a/src/run/run_environment/mod.rs +++ /dev/null @@ -1,46 +0,0 @@ -pub mod interfaces; -pub mod logger; -mod provider; - -use buildkite::BuildkiteProvider; -use github_actions::GitHubActionsProvider; -use gitlab_ci::GitLabCIProvider; -use local::LocalProvider; -use provider::RunEnvironmentDetector; - -use crate::prelude::*; -use crate::run::config::Config; - -pub use self::interfaces::*; -pub use self::provider::RunEnvironmentProvider; - -// RunEnvironment Provider implementations -mod buildkite; -mod github_actions; -mod gitlab_ci; -mod local; - -pub fn get_provider(config: &Config) -> Result> { - if BuildkiteProvider::detect() { - let provider = BuildkiteProvider::try_from(config)?; - return Ok(Box::new(provider)); - } - - if GitHubActionsProvider::detect() { - let provider = GitHubActionsProvider::try_from(config)?; - return Ok(Box::new(provider)); - } - - if GitLabCIProvider::detect() { - let provider = GitLabCIProvider::try_from(config)?; - return Ok(Box::new(provider)); - } - - if LocalProvider::detect() { - let provider = LocalProvider::try_from(config)?; - return Ok(Box::new(provider)); - } - - // By design, this should not happen as the `LocalProvider` is a fallback - bail!("No RunEnvironment provider detected") -} diff --git a/src/run/runner/executor.rs b/src/run/runner/executor.rs deleted file mode 100644 index 716a011a..00000000 --- a/src/run/runner/executor.rs +++ /dev/null @@ -1,36 +0,0 @@ -use super::interfaces::{ExecutorName, RunData}; -use crate::prelude::*; -use crate::run::instruments::mongo_tracer::MongoTracer; -use crate::run::{check_system::SystemInfo, config::Config}; -use async_trait::async_trait; -use std::path::Path; - -#[async_trait(?Send)] -pub trait Executor { - fn name(&self) -> ExecutorName; - - async fn setup( - &self, - _system_info: &SystemInfo, - _setup_cache_dir: Option<&Path>, - ) -> Result<()> { - Ok(()) - } - - /// Runs the executor - async fn run( - &self, - config: &Config, - system_info: &SystemInfo, - run_data: &RunData, - // TODO: use Instruments instead of directly passing the mongodb tracer - mongo_tracer: &Option, - ) -> Result<()>; - - async fn teardown( - &self, - config: &Config, - system_info: &SystemInfo, - run_data: &RunData, - ) -> Result<()>; -} diff --git a/src/run/runner/mod.rs b/src/run/runner/mod.rs deleted file mode 100644 index ffbe32e5..00000000 --- a/src/run/runner/mod.rs +++ /dev/null @@ -1,62 +0,0 @@ -use std::fmt::Display; - -use crate::prelude::*; - -use super::{RunnerMode, config::Config}; - -mod executor; -mod helpers; -mod interfaces; -mod memory; -mod shared; -#[cfg(test)] -mod tests; -mod valgrind; -mod wall_time; - -use executor::Executor; -use helpers::profile_folder::create_profile_folder; -pub use interfaces::{ExecutorName, RunData}; -use memory::executor::MemoryExecutor; -use valgrind::executor::ValgrindExecutor; -use wall_time::executor::WallTimeExecutor; - -impl Display for RunnerMode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - #[allow(deprecated)] - RunnerMode::Instrumentation => write!(f, "instrumentation"), - RunnerMode::Simulation => write!(f, "simulation"), - RunnerMode::Walltime => write!(f, "walltime"), - RunnerMode::Memory => write!(f, "memory"), - } - } -} - -pub const EXECUTOR_TARGET: &str = "executor"; - -pub fn get_executor_from_mode(mode: &RunnerMode) -> Box { - match mode { - #[allow(deprecated)] - RunnerMode::Instrumentation | RunnerMode::Simulation => Box::new(ValgrindExecutor), - RunnerMode::Walltime => Box::new(WallTimeExecutor::new()), - RunnerMode::Memory => Box::new(MemoryExecutor), - } -} - -pub fn get_all_executors() -> Vec> { - vec![ - Box::new(ValgrindExecutor), - Box::new(WallTimeExecutor::new()), - Box::new(MemoryExecutor), - ] -} - -pub fn get_run_data(config: &Config) -> Result { - let profile_folder = if let Some(profile_folder) = &config.profile_folder { - profile_folder.clone() - } else { - create_profile_folder()? - }; - Ok(RunData { profile_folder }) -} diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__cpp_debug_info.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__cpp_debug_info.snap deleted file mode 100644 index 0969433a..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__cpp_debug_info.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:98a399b1981f5f24df1965eed80070bcaa480bf234c895b993a4fb64bd41d9f8 -size 148097 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__golang_debug_info.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__golang_debug_info.snap deleted file mode 100644 index ae3e97df..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__golang_debug_info.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:39418257719dc43b90583a3e7d4af61fb42d9201acf72eb3a9c9e7cd5c7ab8fa -size 490514 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__ruff_debug_info.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__ruff_debug_info.snap deleted file mode 100644 index 713fe2fe..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__ruff_debug_info.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0e0d9438ad3c68cf69bb933c786687e7b23715a062a552c44529d8b856f4a1bb -size 5995727 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__rust_divan_debug_info.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__rust_divan_debug_info.snap deleted file mode 100644 index 503d2497..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__rust_divan_debug_info.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a1f142b35dccc82fc3299ee263a5e34ee39babf16c2dc3cf7ff5fda55bb284f8 -size 519686 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__the_algorithms_debug_info.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__the_algorithms_debug_info.snap deleted file mode 100644 index 07a7cc4c..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__debug_info__tests__the_algorithms_debug_info.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1e2492cd854c83115c65f2631d7b381ba9b70e1e69b9190b7349fec48d314dc6 -size 567247 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__cpp_symbols.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__cpp_symbols.snap deleted file mode 100644 index dec7d96b..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__cpp_symbols.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7bc8744d07ed1bbea45509901976bf3f5ca7cb9ab0d4f837a9744438746d11cb -size 102927 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__golang_symbols.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__golang_symbols.snap deleted file mode 100644 index 624a0a52..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__golang_symbols.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:690cc262f1ebc557ed1e0b7a2d032fb73b4bb00b80c5946663fd9299735d51d4 -size 340599 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__ld_linux_symbols.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__ld_linux_symbols.snap deleted file mode 100644 index 7eed61b2..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__ld_linux_symbols.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ddb17fe0f396824aa13601ed5a3e58401f81ff613dcf5836fa8cbc512d00272d -size 44196 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__ruff_symbols.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__ruff_symbols.snap deleted file mode 100644 index 80130fe6..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__ruff_symbols.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0aa42b91182f851090d21ce5c14804b8b1036485f0a1f6b98c1cbe361ca2cd86 -size 4495238 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__rust_divan_symbols.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__rust_divan_symbols.snap deleted file mode 100644 index 3f5b02a7..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__rust_divan_symbols.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:99b52d4732fef52d8e2e86b5a27bc33b78c52a95cef831f58ea01db1d58e206b -size 372746 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__the_algorithms_symbols.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__the_algorithms_symbols.snap deleted file mode 100644 index 7675b092..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__perf_map__tests__the_algorithms_symbols.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3ba0e35373233250feb5939e720bd24c6c2f9f0234e7dcb7cd3836f3761e2174 -size 421522 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__cpp_unwind_data.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__cpp_unwind_data.snap deleted file mode 100644 index 71801f7d..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__cpp_unwind_data.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bce3b6322b4364a29e98b9a04572dec3deab0fcf2cedd195ae9f290c552afec0 -size 527 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__golang_unwind_data.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__golang_unwind_data.snap deleted file mode 100644 index f0d71450..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__golang_unwind_data.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:59b621e57118b9007e1992a959d89556a245100bb628edb18fb683d2e4730e54 -size 516 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__ruff_unwind_data.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__ruff_unwind_data.snap deleted file mode 100644 index 15e7e2e0..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__ruff_unwind_data.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:db1ca7c70efc959b97974e7f839eed43e81105c3435636edb4455c794b9578d0 -size 530 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__rust_divan_unwind_data.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__rust_divan_unwind_data.snap deleted file mode 100644 index 7da415b7..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__rust_divan_unwind_data.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f0bf28f6ceacb7ea6d6304d2b0a6054e8dfd555fbb423d6d0998881322fa0e6d -size 537 diff --git a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__the_algorithms_unwind_data.snap b/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__the_algorithms_unwind_data.snap deleted file mode 100644 index b8414ef9..00000000 --- a/src/run/runner/wall_time/perf/snapshots/codspeed__run__runner__wall_time__perf__unwind_data__tests__the_algorithms_unwind_data.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7deaa26473956fbb506cea18022aae5c103794449fc631c0db0460a400cf238a -size 533 diff --git a/src/run/uploader/interfaces.rs b/src/run/uploader/interfaces.rs index a25446af..6d885f1b 100644 --- a/src/run/uploader/interfaces.rs +++ b/src/run/uploader/interfaces.rs @@ -1,11 +1,9 @@ use serde::{Deserialize, Serialize}; -use crate::run::{ - check_system::SystemInfo, - instruments::InstrumentName, - run_environment::{RepositoryProvider, RunEnvironment, RunEnvironmentMetadata, RunPart}, - runner::ExecutorName, -}; +use crate::executor::ExecutorName; +use crate::instruments::InstrumentName; +use crate::run::check_system::SystemInfo; +use crate::run_environment::{RepositoryProvider, RunEnvironment, RunEnvironmentMetadata, RunPart}; pub const LATEST_UPLOAD_METADATA_VERSION: u32 = 7; diff --git a/src/run/uploader/snapshots/codspeed__run__uploader__upload_metadata__tests__get_metadata_hash-2.snap b/src/run/uploader/snapshots/codspeed__run__uploader__upload_metadata__tests__get_metadata_hash-2.snap index 07374191..c98b73f1 100644 --- a/src/run/uploader/snapshots/codspeed__run__uploader__upload_metadata__tests__get_metadata_hash-2.snap +++ b/src/run/uploader/snapshots/codspeed__run__uploader__upload_metadata__tests__get_metadata_hash-2.snap @@ -1,56 +1,3 @@ ---- -source: src/run/uploader/upload_metadata.rs -expression: upload_metadata ---- -{ - "repositoryProvider": "GITHUB", - "version": 7, - "tokenless": true, - "profileMd5": "jp/k05RKuqP3ERQuIIvx4Q==", - "profileEncoding": "gzip", - "runner": { - "name": "codspeed-runner", - "version": "2.1.0", - "instruments": [ - "MongoDB" - ], - "executor": "valgrind", - "os": "ubuntu", - "osVersion": "20.04", - "arch": "x86_64", - "host": "host", - "user": "user", - "cpuBrand": "Intel(R) Xeon(R) CPU E5-2686 v4 @ 2.30GHz", - "cpuName": "cpu0", - "cpuVendorId": "GenuineIntel", - "cpuCores": 2, - "totalMemoryGb": 8 - }, - "runEnvironment": "GITHUB_ACTIONS", - "runPart": { - "runId": "7044765741", - "runPartId": "benchmarks_3.2.2", - "jobName": "codspeed", - "metadata": { - "anotherKey": "anotherValue", - "someKey": "someValue" - } - }, - "commitHash": "5bd77cb0da72bef094893ed45fb793ff16ecfbe3", - "ref": "refs/pull/29/merge", - "headRef": "chore/native-action-runner", - "baseRef": "main", - "owner": "CodSpeedHQ", - "repository": "codspeed-node", - "event": "pull_request", - "sender": { - "id": "19605940", - "login": "adriencaccia" - }, - "ghData": { - "runId": "7044765741", - "job": "codspeed" - }, - "glData": null, - "repositoryRootPath": "/home/runner/work/codspeed-node/codspeed-node/" -} +version https://git-lfs.github.com/spec/v1 +oid sha256:5967ad62545b7a2816a10a2c92436d84b14b9eb7f4a55d841d1f8c4f669945c6 +size 1349 diff --git a/src/run/uploader/upload.rs b/src/run/uploader/upload.rs index 119360d5..70ee50c4 100644 --- a/src/run/uploader/upload.rs +++ b/src/run/uploader/upload.rs @@ -1,10 +1,9 @@ +use crate::executor::Config; use crate::run::{ - check_system::SystemInfo, - config::Config, - run_environment::{RunEnvironment, RunEnvironmentProvider}, - runner::{ExecutorName, RunData}, + executor::{ExecutionContext, ExecutorName}, uploader::{UploadError, profile_archive::ProfileArchiveContent}, }; +use crate::run_environment::RunEnvironment; use crate::{ prelude::*, request_client::{REQUEST_CLIENT, STREAMING_CLIENT}, @@ -53,7 +52,7 @@ async fn calculate_folder_size(path: &std::path::Path) -> Result { /// For WallTime, we check the folder size and create either a compressed or uncompressed tar archive /// based on the MAX_UNCOMPRESSED_PROFILE_SIZE_BYTES threshold. async fn create_profile_archive( - run_data: &RunData, + execution_context: &ExecutionContext, executor_name: ExecutorName, ) -> Result { let time_start = std::time::Instant::now(); @@ -62,7 +61,7 @@ async fn create_profile_archive( debug!("Creating compressed tar archive for Valgrind"); let enc = GzipEncoder::new(Vec::new()); let mut tar = Builder::new(enc); - tar.append_dir_all(".", run_data.profile_folder.clone()) + tar.append_dir_all(".", execution_context.profile_folder.clone()) .await?; let mut gzip_encoder = tar.into_inner().await?; gzip_encoder.shutdown().await?; @@ -71,7 +70,8 @@ async fn create_profile_archive( } ExecutorName::WallTime => { // Check folder size to decide on compression - let folder_size_bytes = calculate_folder_size(&run_data.profile_folder).await?; + let folder_size_bytes = + calculate_folder_size(&execution_context.profile_folder).await?; let should_compress = folder_size_bytes >= MAX_UNCOMPRESSED_PROFILE_SIZE_BYTES; let temp_file = tempfile::NamedTempFile::new()?; @@ -91,7 +91,7 @@ async fn create_profile_archive( ); let enc = GzipEncoder::new(file); let mut tar = Builder::new(enc); - tar.append_dir_all(".", run_data.profile_folder.clone()) + tar.append_dir_all(".", execution_context.profile_folder.clone()) .await?; let mut gzip_encoder = tar.into_inner().await?; gzip_encoder.shutdown().await?; @@ -105,7 +105,7 @@ async fn create_profile_archive( bytes_to_mib(MAX_UNCOMPRESSED_PROFILE_SIZE_BYTES) ); let mut tar = Builder::new(file); - tar.append_dir_all(".", run_data.profile_folder.clone()) + tar.append_dir_all(".", execution_context.profile_folder.clone()) .await?; tar.into_inner().await?.sync_all().await?; @@ -238,29 +238,32 @@ pub struct UploadResult { pub run_id: String, } -#[allow(clippy::borrowed_box)] pub async fn upload( - config: &mut Config, - system_info: &SystemInfo, - provider: &Box, - run_data: &RunData, + execution_context: &mut ExecutionContext, executor_name: ExecutorName, ) -> Result { - let profile_archive = create_profile_archive(run_data, executor_name.clone()).await?; + let profile_archive = create_profile_archive(execution_context, executor_name.clone()).await?; debug!( "Run Environment provider detected: {:?}", - provider.get_run_environment() + execution_context.provider.get_run_environment() ); - if provider.get_run_environment() != RunEnvironment::Local { + if !execution_context.is_local() { // If relevant, set the OIDC token for authentication // Note: OIDC tokens can expire quickly, so we set it just before the upload - provider.set_oidc_token(config).await?; + execution_context + .provider + .set_oidc_token(&mut execution_context.config) + .await?; } - let upload_metadata = - provider.get_upload_metadata(config, system_info, &profile_archive, executor_name)?; + let upload_metadata = execution_context.provider.get_upload_metadata( + &execution_context.config, + &execution_context.system_info, + &profile_archive, + executor_name, + )?; debug!("Upload metadata: {upload_metadata:#?}"); info!( "Linked repository: {}\n", @@ -277,7 +280,7 @@ pub async fn upload( } info!("Preparing upload..."); - let upload_data = retrieve_upload_data(config, &upload_metadata).await?; + let upload_data = retrieve_upload_data(&execution_context.config, &upload_metadata).await?; debug!("runId: {}", upload_data.run_id); info!("Uploading performance data..."); @@ -299,25 +302,23 @@ mod tests { use url::Url; use super::*; + use crate::config::CodSpeedConfig; use std::path::PathBuf; // TODO: remove the ignore when implementing network mocking #[ignore] #[tokio::test] async fn test_upload() { - let mut config = Config { + let config = Config { command: "pytest tests/ --codspeed".into(), upload_url: Url::parse("change me").unwrap(), token: Some("change me".into()), - ..Config::test() - }; - let run_data = RunData { - profile_folder: PathBuf::from(format!( + profile_folder: Some(PathBuf::from(format!( "{}/src/uploader/samples/adrien-python-test", env!("CARGO_MANIFEST_DIR") - )), + ))), + ..Config::test() }; - let system_info = SystemInfo::test(); async_with_vars( [ ("GITHUB_ACTIONS", Some("true")), @@ -347,16 +348,12 @@ mod tests { ("VERSION", Some("0.1.0")), ], async { - let provider = crate::run::run_environment::get_provider(&config).unwrap(); - upload( - &mut config, - &system_info, - &provider, - &run_data, - ExecutorName::Valgrind, - ) - .await - .unwrap(); + let codspeed_config = CodSpeedConfig::default(); + let mut execution_context = ExecutionContext::try_from((config, &codspeed_config)) + .expect("Failed to create ExecutionContext for test"); + upload(&mut execution_context, ExecutorName::Valgrind) + .await + .unwrap(); }, ) .await; diff --git a/src/run/uploader/upload_metadata.rs b/src/run/uploader/upload_metadata.rs index 56ba4a7b..62ddd815 100644 --- a/src/run/uploader/upload_metadata.rs +++ b/src/run/uploader/upload_metadata.rs @@ -15,16 +15,16 @@ mod tests { use insta::{assert_json_snapshot, assert_snapshot}; + use crate::executor::ExecutorName; + use crate::instruments::InstrumentName; use crate::run::{ check_system::SystemInfo, - instruments::InstrumentName, - run_environment::{ - GhData, RepositoryProvider, RunEnvironment, RunEnvironmentMetadata, RunEvent, RunPart, - Sender, - }, - runner::ExecutorName, uploader::{LATEST_UPLOAD_METADATA_VERSION, Runner, UploadMetadata}, }; + use crate::run_environment::{ + GhData, RepositoryProvider, RunEnvironment, RunEnvironmentMetadata, RunEvent, RunPart, + Sender, + }; #[test] fn test_get_metadata_hash() { diff --git a/src/run/run_environment/buildkite/logger.rs b/src/run_environment/buildkite/logger.rs similarity index 97% rename from src/run/run_environment/buildkite/logger.rs rename to src/run_environment/buildkite/logger.rs index 69c6d459..ed744e1d 100644 --- a/src/run/run_environment/buildkite/logger.rs +++ b/src/run_environment/buildkite/logger.rs @@ -1,6 +1,6 @@ use crate::{ logger::{GroupEvent, get_announcement_event, get_group_event, get_json_event}, - run::run_environment::logger::should_provider_logger_handle_record, + run_environment::logger::should_provider_logger_handle_record, }; use log::*; use simplelog::SharedLogger; diff --git a/src/run/run_environment/buildkite/mod.rs b/src/run_environment/buildkite/mod.rs similarity index 100% rename from src/run/run_environment/buildkite/mod.rs rename to src/run_environment/buildkite/mod.rs diff --git a/src/run/run_environment/buildkite/provider.rs b/src/run_environment/buildkite/provider.rs similarity index 96% rename from src/run/run_environment/buildkite/provider.rs rename to src/run_environment/buildkite/provider.rs index c144f5b9..d2b8e3d9 100644 --- a/src/run/run_environment/buildkite/provider.rs +++ b/src/run_environment/buildkite/provider.rs @@ -3,17 +3,12 @@ use std::env; use async_trait::async_trait; use simplelog::SharedLogger; +use crate::executor::Config; use crate::prelude::*; -use crate::run::helpers::{GitRemote, parse_git_remote}; -use crate::run::run_environment::{RunEnvironment, RunPart}; -use crate::run::{ - config::Config, - helpers::{find_repository_root, get_env_variable}, - run_environment::{ - interfaces::{RepositoryProvider, RunEnvironmentMetadata, RunEvent}, - provider::{RunEnvironmentDetector, RunEnvironmentProvider}, - }, -}; +use crate::run::helpers::{GitRemote, find_repository_root, get_env_variable, parse_git_remote}; +use crate::run_environment::interfaces::{RepositoryProvider, RunEnvironmentMetadata, RunEvent}; +use crate::run_environment::provider::{RunEnvironmentDetector, RunEnvironmentProvider}; +use crate::run_environment::{RunEnvironment, RunPart}; use super::logger::BuildkiteLogger; diff --git a/src/run_environment/buildkite/snapshots/codspeed__run_environment__buildkite__provider__tests__pull_request_run_environment_metadata.snap b/src/run_environment/buildkite/snapshots/codspeed__run_environment__buildkite__provider__tests__pull_request_run_environment_metadata.snap new file mode 100644 index 00000000..991bd3d1 --- /dev/null +++ b/src/run_environment/buildkite/snapshots/codspeed__run_environment__buildkite__provider__tests__pull_request_run_environment_metadata.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce961d12e4698cb3b5923fec598bb940a1ef9c14cd4d385629ccb67d4d1267e5 +size 410 diff --git a/src/run/run_environment/github_actions/logger.rs b/src/run_environment/github_actions/logger.rs similarity index 98% rename from src/run/run_environment/github_actions/logger.rs rename to src/run_environment/github_actions/logger.rs index 4822ac33..04a91e10 100644 --- a/src/run/run_environment/github_actions/logger.rs +++ b/src/run_environment/github_actions/logger.rs @@ -1,6 +1,6 @@ use crate::{ logger::{GroupEvent, get_announcement_event, get_group_event, get_json_event}, - run::run_environment::logger::should_provider_logger_handle_record, + run_environment::logger::should_provider_logger_handle_record, }; use log::*; use simplelog::SharedLogger; diff --git a/src/run/run_environment/github_actions/mod.rs b/src/run_environment/github_actions/mod.rs similarity index 100% rename from src/run/run_environment/github_actions/mod.rs rename to src/run_environment/github_actions/mod.rs diff --git a/src/run/run_environment/github_actions/provider.rs b/src/run_environment/github_actions/provider.rs similarity index 97% rename from src/run/run_environment/github_actions/provider.rs rename to src/run_environment/github_actions/provider.rs index 7b5b7c61..fba87abc 100644 --- a/src/run/run_environment/github_actions/provider.rs +++ b/src/run_environment/github_actions/provider.rs @@ -8,17 +8,15 @@ use simplelog::SharedLogger; use std::collections::BTreeMap; use std::{env, fs}; +use crate::executor::Config; use crate::prelude::*; use crate::request_client::OIDC_CLIENT; -use crate::run::run_environment::{RunEnvironment, RunPart}; -use crate::run::{ - config::Config, - helpers::{find_repository_root, get_env_variable}, - run_environment::{ - interfaces::{GhData, RepositoryProvider, RunEnvironmentMetadata, RunEvent, Sender}, - provider::{RunEnvironmentDetector, RunEnvironmentProvider}, - }, +use crate::run::helpers::{find_repository_root, get_env_variable}; +use crate::run_environment::interfaces::{ + GhData, RepositoryProvider, RunEnvironmentMetadata, RunEvent, Sender, }; +use crate::run_environment::provider::{RunEnvironmentDetector, RunEnvironmentProvider}; +use crate::run_environment::{RunEnvironment, RunPart}; use super::logger::GithubActionLogger; @@ -424,7 +422,7 @@ mod tests { "GITHUB_EVENT_PATH", Some( format!( - "{}/src/run/run_environment/github_actions/samples/push-event.json", + "{}/src/run_environment/github_actions/samples/push-event.json", env!("CARGO_MANIFEST_DIR") ) .as_str(), @@ -476,7 +474,7 @@ mod tests { "GITHUB_EVENT_PATH", Some( format!( - "{}/src/run/run_environment/github_actions/samples/pr-event.json", + "{}/src/run_environment/github_actions/samples/pr-event.json", env!("CARGO_MANIFEST_DIR") ) .as_str(), @@ -531,7 +529,7 @@ mod tests { "GITHUB_EVENT_PATH", Some( format!( - "{}/src/run/run_environment/github_actions/samples/fork-pr-event.json", + "{}/src/run_environment/github_actions/samples/fork-pr-event.json", env!("CARGO_MANIFEST_DIR") ) .as_str(), @@ -595,7 +593,7 @@ mod tests { "GITHUB_EVENT_PATH", Some( format!( - "{}/src/run/run_environment/github_actions/samples/pr-event.json", + "{}/src/run_environment/github_actions/samples/pr-event.json", env!("CARGO_MANIFEST_DIR") ) .as_str(), diff --git a/src/run/run_environment/github_actions/samples/fork-pr-event.json b/src/run_environment/github_actions/samples/fork-pr-event.json similarity index 100% rename from src/run/run_environment/github_actions/samples/fork-pr-event.json rename to src/run_environment/github_actions/samples/fork-pr-event.json diff --git a/src/run/run_environment/github_actions/samples/pr-event.json b/src/run_environment/github_actions/samples/pr-event.json similarity index 100% rename from src/run/run_environment/github_actions/samples/pr-event.json rename to src/run_environment/github_actions/samples/pr-event.json diff --git a/src/run/run_environment/github_actions/samples/push-event.json b/src/run_environment/github_actions/samples/push-event.json similarity index 100% rename from src/run/run_environment/github_actions/samples/push-event.json rename to src/run_environment/github_actions/samples/push-event.json diff --git a/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__fork_pull_request_run_environment_metadata-2.snap b/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__fork_pull_request_run_environment_metadata-2.snap new file mode 100644 index 00000000..820a19b5 --- /dev/null +++ b/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__fork_pull_request_run_environment_metadata-2.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab568ca261f80ba87270fb4ea13105e7c58afe241f4dd97e9326ee760ba9d9f9 +size 180 diff --git a/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__fork_pull_request_run_environment_metadata.snap b/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__fork_pull_request_run_environment_metadata.snap new file mode 100644 index 00000000..87d7c9bc --- /dev/null +++ b/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__fork_pull_request_run_environment_metadata.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51d02dd35e2e5e7baf7c4f8eb10e3cb5a42e1e17cbdbcacd6da3e1a359781f16 +size 527 diff --git a/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__matrix_job_run_environment_metadata-2.snap b/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__matrix_job_run_environment_metadata-2.snap new file mode 100644 index 00000000..7a380877 --- /dev/null +++ b/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__matrix_job_run_environment_metadata-2.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e280bf3e174a7c1787589910f914403ce2cbd6d95faaa57d64c2710365b98dd4 +size 378 diff --git a/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__matrix_job_run_environment_metadata.snap b/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__matrix_job_run_environment_metadata.snap new file mode 100644 index 00000000..e03bcc13 --- /dev/null +++ b/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__matrix_job_run_environment_metadata.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b812b341347175110729c744e6f07d8122eda0079ac9daf95f88dda9044699c0 +size 516 diff --git a/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__pull_request_run_environment_metadata-2.snap b/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__pull_request_run_environment_metadata-2.snap new file mode 100644 index 00000000..820a19b5 --- /dev/null +++ b/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__pull_request_run_environment_metadata-2.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab568ca261f80ba87270fb4ea13105e7c58afe241f4dd97e9326ee760ba9d9f9 +size 180 diff --git a/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__pull_request_run_environment_metadata.snap b/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__pull_request_run_environment_metadata.snap new file mode 100644 index 00000000..e03bcc13 --- /dev/null +++ b/src/run_environment/github_actions/snapshots/codspeed__run_environment__github_actions__provider__tests__pull_request_run_environment_metadata.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b812b341347175110729c744e6f07d8122eda0079ac9daf95f88dda9044699c0 +size 516 diff --git a/src/run/run_environment/gitlab_ci/logger.rs b/src/run_environment/gitlab_ci/logger.rs similarity index 98% rename from src/run/run_environment/gitlab_ci/logger.rs rename to src/run_environment/gitlab_ci/logger.rs index 35e2c36e..4c5af6f5 100644 --- a/src/run/run_environment/gitlab_ci/logger.rs +++ b/src/run_environment/gitlab_ci/logger.rs @@ -12,7 +12,7 @@ use std::{ use crate::{ logger::{GroupEvent, get_announcement_event, get_group_event, get_json_event}, - run::run_environment::logger::should_provider_logger_handle_record, + run_environment::logger::should_provider_logger_handle_record, }; lazy_static! { diff --git a/src/run/run_environment/gitlab_ci/mod.rs b/src/run_environment/gitlab_ci/mod.rs similarity index 100% rename from src/run/run_environment/gitlab_ci/mod.rs rename to src/run_environment/gitlab_ci/mod.rs diff --git a/src/run/run_environment/gitlab_ci/provider.rs b/src/run_environment/gitlab_ci/provider.rs similarity index 98% rename from src/run/run_environment/gitlab_ci/provider.rs rename to src/run_environment/gitlab_ci/provider.rs index 797e7bb0..e3564113 100644 --- a/src/run/run_environment/gitlab_ci/provider.rs +++ b/src/run_environment/gitlab_ci/provider.rs @@ -3,14 +3,14 @@ use simplelog::SharedLogger; use std::collections::BTreeMap; use std::env; +use crate::executor::Config; use crate::prelude::*; -use crate::run::config::Config; use crate::run::helpers::get_env_variable; -use crate::run::run_environment::interfaces::{ +use crate::run_environment::interfaces::{ GlData, RepositoryProvider, RunEnvironment, RunEnvironmentMetadata, RunEvent, Sender, }; -use crate::run::run_environment::provider::RunEnvironmentDetector; -use crate::run::run_environment::{RunEnvironmentProvider, RunPart}; +use crate::run_environment::provider::RunEnvironmentDetector; +use crate::run_environment::{RunEnvironmentProvider, RunPart}; use super::logger::GitLabCILogger; diff --git a/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__fork_merge_request_run_environment_metadata-2.snap b/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__fork_merge_request_run_environment_metadata-2.snap new file mode 100644 index 00000000..47c7daf1 --- /dev/null +++ b/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__fork_merge_request_run_environment_metadata-2.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ed6e5480b717ba83767966db9122503ccfd59d596a4a5c1ea79ee74fbb6314d +size 179 diff --git a/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__fork_merge_request_run_environment_metadata.snap b/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__fork_merge_request_run_environment_metadata.snap new file mode 100644 index 00000000..7b6eaf82 --- /dev/null +++ b/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__fork_merge_request_run_environment_metadata.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af84cf9aa603ba88103a781849161d5d0a09c7896790655806ce5053b9da224a +size 475 diff --git a/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__merge_request_run_environment_metadata-2.snap b/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__merge_request_run_environment_metadata-2.snap new file mode 100644 index 00000000..47c7daf1 --- /dev/null +++ b/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__merge_request_run_environment_metadata-2.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ed6e5480b717ba83767966db9122503ccfd59d596a4a5c1ea79ee74fbb6314d +size 179 diff --git a/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__merge_request_run_environment_metadata.snap b/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__merge_request_run_environment_metadata.snap new file mode 100644 index 00000000..a28da143 --- /dev/null +++ b/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__merge_request_run_environment_metadata.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8ed03d10f8ae7ac7091815d23fd48300067f133ff53aced1b367be6248da349 +size 464 diff --git a/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__push_main_run_environment_metadata-2.snap b/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__push_main_run_environment_metadata-2.snap new file mode 100644 index 00000000..1df90001 --- /dev/null +++ b/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__push_main_run_environment_metadata-2.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1421a01f35dc96a4e2ac342492a84f6270714d9cf83f00b4ea4cc6c570f171fb +size 167 diff --git a/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__push_main_run_environment_metadata.snap b/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__push_main_run_environment_metadata.snap new file mode 100644 index 00000000..911bbf7e --- /dev/null +++ b/src/run_environment/gitlab_ci/snapshots/codspeed__run_environment__gitlab_ci__provider__tests__push_main_run_environment_metadata.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3872df94992d882b782d50ea6610479e639399c68a591e5661dc7ff4bc0feabd +size 432 diff --git a/src/run/run_environment/interfaces.rs b/src/run_environment/interfaces.rs similarity index 100% rename from src/run/run_environment/interfaces.rs rename to src/run_environment/interfaces.rs diff --git a/src/run/run_environment/local/mod.rs b/src/run_environment/local/mod.rs similarity index 100% rename from src/run/run_environment/local/mod.rs rename to src/run_environment/local/mod.rs diff --git a/src/run/run_environment/local/provider.rs b/src/run_environment/local/provider.rs similarity index 95% rename from src/run/run_environment/local/provider.rs rename to src/run_environment/local/provider.rs index db5849e8..8ec8e5f9 100644 --- a/src/run/run_environment/local/provider.rs +++ b/src/run_environment/local/provider.rs @@ -2,24 +2,18 @@ use async_trait::async_trait; use git2::Repository; use simplelog::SharedLogger; +use crate::executor::config::RepositoryOverride; +use crate::executor::{Config, ExecutorName}; use crate::local_logger::get_local_logger; use crate::prelude::*; use crate::run::check_system::SystemInfo; -use crate::run::config::RepositoryOverride; -use crate::run::helpers::{GitRemote, parse_git_remote}; -use crate::run::run_environment::{RunEnvironment, RunPart}; -use crate::run::runner::ExecutorName; +use crate::run::helpers::{GitRemote, find_repository_root, parse_git_remote}; use crate::run::uploader::{ LATEST_UPLOAD_METADATA_VERSION, ProfileArchive, Runner, UploadMetadata, }; -use crate::run::{ - config::Config, - helpers::find_repository_root, - run_environment::{ - interfaces::{RepositoryProvider, RunEnvironmentMetadata, RunEvent}, - provider::{RunEnvironmentDetector, RunEnvironmentProvider}, - }, -}; +use crate::run_environment::interfaces::{RepositoryProvider, RunEnvironmentMetadata, RunEvent}; +use crate::run_environment::provider::{RunEnvironmentDetector, RunEnvironmentProvider}; +use crate::run_environment::{RunEnvironment, RunPart}; static FAKE_COMMIT_REF: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; diff --git a/src/run/run_environment/logger.rs b/src/run_environment/logger.rs similarity index 83% rename from src/run/run_environment/logger.rs rename to src/run_environment/logger.rs index 5858fb35..30da45bf 100644 --- a/src/run/run_environment/logger.rs +++ b/src/run_environment/logger.rs @@ -1,4 +1,4 @@ -use crate::run::runner::EXECUTOR_TARGET; +use crate::executor::EXECUTOR_TARGET; pub(super) fn should_provider_logger_handle_record(record: &log::Record) -> bool { // Provider logger should handle all records except the ones from the executor target diff --git a/src/run_environment/mod.rs b/src/run_environment/mod.rs new file mode 100644 index 00000000..7e9472bc --- /dev/null +++ b/src/run_environment/mod.rs @@ -0,0 +1,46 @@ +pub mod interfaces; +pub mod logger; +mod provider; + +use buildkite::BuildkiteProvider; +use github_actions::GitHubActionsProvider; +use gitlab_ci::GitLabCIProvider; +use local::LocalProvider; +use provider::RunEnvironmentDetector; + +use crate::executor::Config; +use crate::prelude::*; + +pub use self::interfaces::*; +pub use self::provider::RunEnvironmentProvider; + +// RunEnvironment Provider implementations +mod buildkite; +mod github_actions; +mod gitlab_ci; +mod local; + +pub fn get_provider(config: &Config) -> Result> { + let mut provider: Box = { + if BuildkiteProvider::detect() { + let provider = BuildkiteProvider::try_from(config)?; + Box::new(provider) + } else if GitHubActionsProvider::detect() { + let provider = GitHubActionsProvider::try_from(config)?; + Box::new(provider) + } else if GitLabCIProvider::detect() { + let provider = GitLabCIProvider::try_from(config)?; + Box::new(provider) + } else if LocalProvider::detect() { + let provider = LocalProvider::try_from(config)?; + Box::new(provider) + } else { + // By design, this should not happen as the `LocalProvider` is a fallback + bail!("No RunEnvironment provider detected") + } + }; + + provider.check_oidc_configuration(config)?; + + Ok(provider) +} diff --git a/src/run/run_environment/provider.rs b/src/run_environment/provider.rs similarity index 98% rename from src/run/run_environment/provider.rs rename to src/run_environment/provider.rs index 9b0d0c7d..6aff25d0 100644 --- a/src/run/run_environment/provider.rs +++ b/src/run_environment/provider.rs @@ -2,10 +2,9 @@ use async_trait::async_trait; use git2::Repository; use simplelog::SharedLogger; +use crate::executor::{Config, ExecutorName}; use crate::prelude::*; use crate::run::check_system::SystemInfo; -use crate::run::config::Config; -use crate::run::runner::ExecutorName; use crate::run::uploader::{ LATEST_UPLOAD_METADATA_VERSION, ProfileArchive, Runner, UploadMetadata, }; diff --git a/src/runner_mode.rs b/src/runner_mode.rs new file mode 100644 index 00000000..af143f87 --- /dev/null +++ b/src/runner_mode.rs @@ -0,0 +1,12 @@ +use clap::ValueEnum; +use serde::Serialize; + +#[derive(ValueEnum, Clone, Debug, Serialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum RunnerMode { + #[deprecated(note = "Use `RunnerMode::Simulation` instead")] + Instrumentation, + Simulation, + Walltime, + Memory, +} diff --git a/src/setup.rs b/src/setup.rs index f59b3911..3629e392 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -1,6 +1,6 @@ +use crate::executor::get_all_executors; use crate::prelude::*; use crate::run::check_system::SystemInfo; -use crate::run::runner::get_all_executors; use std::path::Path; pub async fn setup(setup_cache_dir: Option<&Path>) -> Result<()> {