Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,127 changes: 1,088 additions & 39 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,14 @@ rmp-serde = "1.3.0"
uuid = { version = "1.21.0", features = ["v4"] }
which = "8.0.2"
crc32fast = "1.5.0"
samply = { git = "https://github.com/AvalancheHQ/samply", branch = "lib-entrypoint" }

[target.'cfg(target_os = "linux")'.dependencies]
procfs = "0.17.0"

[target.'cfg(target_os = "macos")'.dependencies]
mach2 = "0.4"

[dev-dependencies]
temp-env = { version = "0.3.6", features = ["async_closure"] }
insta = { version = "1.29.0", features = ["json", "redactions"] }
Expand Down
4 changes: 2 additions & 2 deletions crates/runner-shared/src/fifo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub enum MarkerType {

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntegrationMode {
Perf,
Walltime,
Simulation,
Analysis,
}
Expand All @@ -46,7 +46,7 @@ pub enum Command {
StopBenchmark,
Ack,
#[deprecated(note = "Use `GetIntegrationMode` instead")]
PingPerf,
PingProfiler,
SetIntegration {
name: String,
version: String,
Expand Down
8 changes: 4 additions & 4 deletions crates/runner-shared/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::module_symbols::MappedProcessModuleSymbols;
use crate::unwind_data::MappedProcessUnwindData;

#[derive(Serialize, Deserialize, Default)]
pub struct PerfMetadata {
pub struct WalltimeMetadata {
/// The version of this metadata format.
pub version: u64,

Expand Down Expand Up @@ -71,13 +71,13 @@ pub struct PerfMetadata {
pub debug_info_by_pid: HashMap<pid_t, Vec<ModuleDebugInfo>>,
}

impl PerfMetadata {
impl WalltimeMetadata {
pub fn from_reader<R: std::io::Read>(reader: R) -> anyhow::Result<Self> {
serde_json::from_reader(reader).context("Could not parse perf metadata from JSON")
serde_json::from_reader(reader).context("Could not parse walltime metadata from JSON")
}

pub fn save_to<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
let file = std::fs::File::create(path.as_ref().join("perf.metadata"))?;
let file = std::fs::File::create(path.as_ref().join("walltime.metadata"))?;
const BUFFER_SIZE: usize = 256 * 1024 /* 256 KB */;

let writer = BufWriter::with_capacity(BUFFER_SIZE, file);
Expand Down
2 changes: 1 addition & 1 deletion src/cli/exec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ fn build_orchestrator_config(
modes,
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,
enable_profiler: args.shared.perf_run_args.resolve_enable_profiler(),
simulation_tool: args.shared.simulation_tool.unwrap_or_default(),
profile_folder: args.shared.profile_folder,
skip_upload: args.shared.skip_upload,
Expand Down
7 changes: 6 additions & 1 deletion src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod auth;
pub(crate) mod exec;
pub(crate) mod experimental;
pub(crate) mod run;
mod samply;
mod setup;
mod shared;
mod show;
Expand Down Expand Up @@ -117,6 +118,9 @@ enum Commands {
Show,
/// Update the CodSpeed CLI to the latest version
Update,
/// Run the bundled samply profiler. Args are forwarded to samply.
#[command(disable_help_flag = true, disable_help_subcommand = true)]
Samply(samply::SamplyArgs),
}
Comment on lines +121 to 124
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we segregate these subcommands into a dedicated enum so we now at glance which commands are real entrypoints by users and which are actually just the cli re-invoking itself?


pub async fn run() -> Result<()> {
Expand All @@ -141,7 +145,7 @@ pub async fn run() -> Result<()> {
let setup_cache_dir = setup_cache_dir.as_deref();

match cli.command {
Commands::Run(_) | Commands::Exec(_) => {} // Run and Exec are responsible for their own logger initialization
Commands::Run(_) | Commands::Exec(_) | Commands::Samply(_) => {} // these are responsible for their own logger initialization
_ => {
init_local_logger()?;
}
Expand Down Expand Up @@ -176,6 +180,7 @@ pub async fn run() -> Result<()> {
Commands::Use(args) => use_mode::run(args)?,
Commands::Show => show::run()?,
Commands::Update => update::run().await?,
Commands::Samply(args) => samply::run(args)?,
}
Ok(())
}
Expand Down
5 changes: 3 additions & 2 deletions src/cli/run/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ impl RunArgs {
show_full_output: false,
base: None,
perf_run_args: PerfRunArgs {
enable_perf: false,
enable_profiler: false,
enable_perf: None,
perf_unwinding_mode: None,
},
experimental: ExperimentalArgs {
Expand Down Expand Up @@ -112,7 +113,7 @@ fn build_orchestrator_config(
modes,
instruments,
perf_unwinding_mode: args.shared.perf_run_args.perf_unwinding_mode,
enable_perf: args.shared.perf_run_args.enable_perf,
enable_profiler: args.shared.perf_run_args.resolve_enable_profiler(),
simulation_tool: args.shared.simulation_tool.unwrap_or_default(),
profile_folder: args.shared.profile_folder,
skip_upload: args.shared.skip_upload,
Expand Down
37 changes: 37 additions & 0 deletions src/cli/samply.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use clap::Parser;

use crate::prelude::*;

/// Run the bundled samply profiler. Arguments after `samply` are forwarded
/// verbatim to samply's own CLI parser.
#[derive(Debug, clap::Args)]
pub struct SamplyArgs {
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<std::ffi::OsString>,
}

pub fn run(args: SamplyArgs) -> Result<()> {
use ::samply::cli;

let argv = std::iter::once(std::ffi::OsString::from("samply")).chain(args.args);
let opt = cli::Opt::parse_from(argv);

// samply spins up its own tokio runtime internally, so it must run on a
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixup with previous commit

// thread that isn't already inside our `#[tokio::main]` runtime.
std::thread::scope(|s| {
s.spawn(|| match opt.action {
#[cfg(any(
target_os = "android",
target_os = "macos",
target_os = "linux",
target_os = "windows"
))]
cli::Action::Record(a) => ::samply::do_record_action(a),
_ => unimplemented!("Only `samply record` is supported"),
})
.join()
.map_err(|_| anyhow::anyhow!("samply thread panicked"))
})?;

Ok(())
}
24 changes: 21 additions & 3 deletions src/cli/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,34 @@ pub enum UnwindingMode {

#[derive(Args, Debug, Clone)]
pub struct PerfRunArgs {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be renamed and the unwinding mode argument should be scoped to perf, could be done using a generic to avoid having to handle this at runtime

/// Enable the linux perf profiler to collect granular performance data.
/// Enable a profiler to collect granular performance data.
/// This is only supported on Linux.
#[arg(long, env = "CODSPEED_PERF_ENABLED", default_value_t = true)]
pub enable_perf: bool,
#[arg(long, env = "CODSPEED_PROFILER_ENABLED", default_value_t = true)]
pub enable_profiler: bool,

/// Deprecated alias for --enable-profiler / CODSPEED_PROFILER_ENABLED.
#[arg(long, env = "CODSPEED_PERF_ENABLED", hide = true)]
pub enable_perf: Option<bool>,

/// The unwinding mode that should be used with perf to collect the call stack.
#[arg(long, env = "CODSPEED_PERF_UNWINDING_MODE")]
pub perf_unwinding_mode: Option<UnwindingMode>,
}

impl PerfRunArgs {
/// Resolves the effective `enable_profiler` value, honoring the deprecated
/// `--enable-perf` / `CODSPEED_PERF_ENABLED` flag with a warning.
pub fn resolve_enable_profiler(&self) -> bool {
let Some(legacy) = self.enable_perf else {
return self.enable_profiler;
};
log::warn!(
"CODSPEED_PERF_ENABLED / --enable-perf is deprecated; use CODSPEED_PROFILER_ENABLED / --enable-profiler instead."
);
legacy
}
}

/// Parser for go-runner version that validates semver format
fn parse_version(s: &str) -> Result<semver::Version, String> {
semver::Version::parse(s).map_err(|e| format!("Invalid semantic version: {e}"))
Expand Down
8 changes: 4 additions & 4 deletions src/executor/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub struct OrchestratorConfig {

pub modes: Vec<RunnerMode>,
pub instruments: Instruments,
pub enable_perf: bool,
pub enable_profiler: bool,
/// Stack unwinding mode for perf (if enabled)
pub perf_unwinding_mode: Option<UnwindingMode>,

Expand Down Expand Up @@ -96,7 +96,7 @@ pub struct ExecutorConfig {
pub command: String,

pub instruments: Instruments,
pub enable_perf: bool,
pub enable_profiler: bool,
/// Stack unwinding mode for perf (if enabled)
pub perf_unwinding_mode: Option<UnwindingMode>,

Expand Down Expand Up @@ -181,7 +181,7 @@ impl OrchestratorConfig {
working_directory: self.working_directory.clone(),
command,
instruments: self.instruments.clone(),
enable_perf: self.enable_perf,
enable_profiler: self.enable_profiler,
perf_unwinding_mode: self.perf_unwinding_mode,
simulation_tool: self.simulation_tool,
skip_run: self.skip_run,
Expand Down Expand Up @@ -217,7 +217,7 @@ impl OrchestratorConfig {
modes: vec![RunnerMode::Simulation],
instruments: Instruments::test(),
perf_unwinding_mode: None,
enable_perf: false,
enable_profiler: false,
simulation_tool: SimulationTool::default(),
profile_folder: None,
skip_upload: false,
Expand Down
4 changes: 3 additions & 1 deletion src/executor/helpers/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ pub fn get_base_injected_env(
};
let mut env = HashMap::from([
("PYTHONHASHSEED".into(), "0".into()),
// IMPORTANT: We must not enable this, otherwise we'll have many unresolved addresses on the stack on MacOS
// TODO: Investigate why this happens
(
"PYTHON_PERF_JIT_SUPPORT".into(),
if mode == RunnerMode::Walltime {
if mode == RunnerMode::Walltime && !cfg!(target_os = "macos") {
"1".into()
} else {
"0".into()
Expand Down
2 changes: 1 addition & 1 deletion src/executor/memory/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ impl Executor for MemoryExecutor {
}

async fn run(
&self,
&mut self,
execution_context: &ExecutionContext,
_mongo_tracer: &Option<MongoTracer>,
) -> Result<()> {
Expand Down
4 changes: 2 additions & 2 deletions src/executor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ pub trait Executor {

/// Runs the executor
async fn run(
&self,
&mut self,
execution_context: &ExecutionContext,
// TODO: use Instruments instead of directly passing the mongodb tracer
mongo_tracer: &Option<MongoTracer>,
Expand All @@ -118,7 +118,7 @@ pub trait Executor {
/// Run a single executor: setup → run → teardown → persist logs.
/// Does NOT upload.
pub async fn run_executor(
executor: &dyn Executor,
executor: &mut dyn Executor,
orchestrator: &Orchestrator,
execution_context: &ExecutionContext,
setup_cache_dir: Option<&Path>,
Expand Down
4 changes: 2 additions & 2 deletions src/executor/orchestrator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ impl Orchestrator {
let config = self
.config
.executor_config_for_command(part.command, !part.uses_exec_harness);
let executor = get_executor_from_mode(part.mode);
let mut executor = get_executor_from_mode(part.mode);
let profile_folder =
self.resolve_profile_folder(&executor.name(), run_part_index, total_parts)?;

Expand All @@ -167,7 +167,7 @@ impl Orchestrator {
activate_rolling_buffer(&part.label);
}

run_executor(executor.as_ref(), self, &ctx, setup_cache_dir).await?;
run_executor(executor.as_mut(), self, &ctx, setup_cache_dir).await?;

if !self.config.show_full_output {
deactivate_rolling_buffer();
Expand Down
Loading
Loading