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
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ 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/CodSpeedHQ/samply-codspeed", branch = "codspeed" }
samply = { git = "https://github.com/CodSpeedHQ/samply-codspeed", rev = "72d93a3a" } # branch = "codspeed"

[target.'cfg(target_os = "linux")'.dependencies]
procfs = "0.17.0"
Expand Down
13 changes: 12 additions & 1 deletion src/executor/helpers/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ impl CommandBuilder {
self
}

#[cfg(target_os = "macos")]
pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: AsRef<OsStr>,
Expand All @@ -59,6 +58,18 @@ impl CommandBuilder {
self
}

pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
for (k, v) in vars {
self.env(k, v);
}
self
}

pub fn current_dir<D>(&mut self, dir: D)
where
D: AsRef<OsStr>,
Expand Down
50 changes: 39 additions & 11 deletions src/executor/wall_time/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,24 +88,41 @@ pub struct WallTimeExecutor {
benchmark_state: OnceCell<(FifoBenchmarkData, ExecutionTimestamps)>,
}

fn select_profiler() -> Option<Box<dyn Profiler>> {
const PROFILER_OVERRIDE_ENV: &str = "CODSPEED_WALLTIME_PROFILER";

match std::env::var(PROFILER_OVERRIDE_ENV).ok().as_deref() {
Some("perf") => return Some(Box::new(PerfProfiler::new())),
Some("samply") => return Some(Box::new(SamplyProfiler::new())),
Some(other) => {
warn!(
"Ignoring unknown {PROFILER_OVERRIDE_ENV}={other:?}; expected `perf` or `samply`."
);
}
None => {}
}

if cfg!(target_os = "linux") {
Some(Box::new(PerfProfiler::new()))
} else if cfg!(target_os = "macos") {
Some(Box::new(SamplyProfiler::new()))
} else {
None
}
}

impl WallTimeExecutor {
pub fn new() -> Self {
let profiler: Option<Box<dyn Profiler>> = if cfg!(target_os = "linux") {
Some(Box::new(PerfProfiler::new()))
} else if cfg!(target_os = "macos") {
Some(Box::new(SamplyProfiler::new()))
} else {
None
};
Self {
profiler,
profiler: select_profiler(),
benchmark_state: OnceCell::new(),
}
}

fn walltime_bench_cmd(
config: &ExecutorConfig,
execution_context: &ExecutionContext,
isolate: bool,
) -> Result<(NamedTempFile, NamedTempFile, CommandBuilder)> {
let path_value = build_path_env(config.enable_introspection)?;

Expand All @@ -130,7 +147,11 @@ impl WallTimeExecutor {
bench_cmd.current_dir(abs_cwd);
}

let bench_cmd = wrap_with_isolation(bench_cmd)?;
let bench_cmd = if isolate {
wrap_with_isolation(bench_cmd)?
} else {
bench_cmd
};

Ok((env_file, script_file, bench_cmd))
}
Expand Down Expand Up @@ -168,8 +189,15 @@ impl Executor for WallTimeExecutor {
) -> Result<()> {
let _guard = HookScriptsGuard::setup();

let (_env_file, _script_file, cmd_builder) =
WallTimeExecutor::walltime_bench_cmd(&execution_context.config, execution_context)?;
let isolate = self
.profiler
.as_ref()
.is_none_or(|p| p.requires_isolation());
let (_env_file, _script_file, cmd_builder) = WallTimeExecutor::walltime_bench_cmd(
&execution_context.config,
execution_context,
isolate,
)?;

// Split-borrow `self` so the closure inside `run_with_profiler` can
// capture `benchmark_state` while we hold `&mut profiler`.
Expand Down
6 changes: 6 additions & 0 deletions src/executor/wall_time/profiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ pub trait Profiler {
None
}

/// Whether the benchmark command should be wrapped with the Linux
/// systemd-run isolation scope.
fn requires_isolation(&self) -> bool {
true
}

/// One-time system setup (install tool, tweak sysctls, ...).
async fn setup(
&self,
Expand Down
15 changes: 15 additions & 0 deletions src/executor/wall_time/profiler/samply/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ impl SamplyProfiler {

#[async_trait(?Send)]
impl Profiler for SamplyProfiler {
fn requires_isolation(&self) -> bool {
false
}

async fn setup(
&self,
_system_info: &SystemInfo,
Expand Down Expand Up @@ -101,6 +105,17 @@ impl Profiler for SamplyProfiler {

cmd_builder.wrap_with(samply_builder);

cmd_builder.envs([
// Disable JIT classification as it produces duplicates symbols in
// Node.js for the Interpreter frames.
("SAMPLY_DISABLE_JIT_CLASSIFICATION", "1"),
("SAMPLY_USE_DEBUGINFOD", "1"),
(
"SAMPLY_BREAKPAD_SYMBOL_SERVER",
"https://symbols.mozilla.org/,https://symbols.electronjs.org/",
),
]);

// If `setup` decided the bash on PATH is Apple-signed, prepend brew's
// bin so samply's spawned shell resolves to the ad-hoc-signed brew bash
// instead. Only the samply child's PATH is touched.
Expand Down