Skip to content

Commit f2f73ae

Browse files
committed
refactor(walltime): share Linux profiler sysctl setup
Move the Linux profiling sysctl setup out of the perf profiler and into a shared walltime profiler helper. This lets both perf and samply prepare kernel symbol access and non-root profiling consistently while preserving the existing sudo-based behavior.
1 parent ed91edd commit f2f73ae

4 files changed

Lines changed: 80 additions & 25 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use crate::prelude::*;
2+
3+
#[cfg(target_os = "linux")]
4+
use crate::executor::helpers::run_with_sudo::run_with_sudo;
5+
#[cfg(any(test, target_os = "linux"))]
6+
use anyhow::Context;
7+
#[cfg(target_os = "linux")]
8+
use std::process::Command;
9+
10+
pub fn ensure_linux_profiling_sysctls() -> Result<()> {
11+
#[cfg(target_os = "linux")]
12+
{
13+
ensure_sysctl("kernel.kptr_restrict", 0)?;
14+
ensure_sysctl("kernel.perf_event_paranoid", -1)?;
15+
}
16+
17+
Ok(())
18+
}
19+
20+
#[cfg(target_os = "linux")]
21+
fn ensure_sysctl(name: &str, target_value: i64) -> Result<()> {
22+
if sysctl_read(name)? == target_value {
23+
return Ok(());
24+
}
25+
26+
let assignment = format!("{name}={target_value}");
27+
run_with_sudo("sysctl", ["-w", assignment.as_str()])
28+
}
29+
30+
#[cfg(target_os = "linux")]
31+
fn sysctl_read(name: &str) -> Result<i64> {
32+
let output = Command::new("sysctl").arg(name).output()?;
33+
let output = String::from_utf8(output.stdout)?;
34+
35+
parse_sysctl_value(&output)
36+
}
37+
38+
#[cfg(any(test, target_os = "linux"))]
39+
fn parse_sysctl_value(output: &str) -> Result<i64> {
40+
let (_, value) = output
41+
.split_once('=')
42+
.context("Couldn't find the value in sysctl output")?;
43+
44+
Ok(value.trim().parse::<i64>()?)
45+
}
46+
47+
#[cfg(test)]
48+
mod tests {
49+
use super::*;
50+
51+
#[test]
52+
fn parses_sysctl_value() {
53+
assert_eq!(parse_sysctl_value("kernel.kptr_restrict = 0\n").unwrap(), 0);
54+
}
55+
56+
#[test]
57+
fn parses_negative_sysctl_value() {
58+
assert_eq!(
59+
parse_sysctl_value("kernel.perf_event_paranoid = -1\n").unwrap(),
60+
-1
61+
);
62+
}
63+
64+
#[test]
65+
fn rejects_sysctl_output_without_value_separator() {
66+
assert!(parse_sysctl_value("kernel.kptr_restrict 0\n").is_err());
67+
}
68+
}

src/executor/wall_time/profiler/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! (perf, samply, instruments, ...) and produces a unified set of artifacts
55
//! in the profile folder.
66
7+
mod linux_sysctl;
78
pub mod perf;
89
pub mod samply;
910

src/executor/wall_time/profiler/perf/mod.rs

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ use crate::executor::helpers::detect_executable::command_has_executable;
88
use crate::executor::helpers::env::is_codspeed_debug_enabled;
99
use crate::executor::helpers::env::suppress_go_perf_unwinding_warning;
1010
use crate::executor::helpers::harvest_perf_maps_for_pids::harvest_perf_maps_for_pids;
11-
use crate::executor::helpers::run_with_sudo::run_with_sudo;
1211
use crate::executor::helpers::run_with_sudo::wrap_with_sudo;
1312
use crate::executor::shared::fifo::FifoBenchmarkData;
1413
use crate::executor::wall_time::profiler::Profiler;
1514
use crate::executor::wall_time::profiler::WALLTIME_METADATA_CURRENT_VERSION;
15+
use crate::executor::wall_time::profiler::linux_sysctl::ensure_linux_profiling_sysctls;
1616
use crate::executor::wall_time::profiler::perf::perf_executable::get_working_perf_executable;
1717
use crate::prelude::*;
1818
use crate::system::SystemInfo;
@@ -75,30 +75,7 @@ impl Profiler for PerfProfiler {
7575
setup_cache_dir: Option<&Path>,
7676
) -> anyhow::Result<()> {
7777
setup::install_perf(system_info, setup_cache_dir).await?;
78-
79-
let sysctl_read = |name: &str| -> anyhow::Result<i64> {
80-
let output = std::process::Command::new("sysctl").arg(name).output()?;
81-
let output = String::from_utf8(output.stdout)?;
82-
83-
Ok(output
84-
.split(" = ")
85-
.last()
86-
.context("Couldn't find the value in sysctl output")?
87-
.trim()
88-
.parse::<i64>()?)
89-
};
90-
91-
// Allow access to kernel symbols
92-
if sysctl_read("kernel.kptr_restrict")? != 0 {
93-
run_with_sudo("sysctl", ["-w", "kernel.kptr_restrict=0"])?;
94-
}
95-
96-
// Allow non-root profiling
97-
if sysctl_read("kernel.perf_event_paranoid")? != -1 {
98-
run_with_sudo("sysctl", ["-w", "kernel.perf_event_paranoid=-1"])?;
99-
}
100-
101-
Ok(())
78+
ensure_linux_profiling_sysctls()
10279
}
10380

10481
async fn wrap(

src/executor/wall_time/profiler/samply/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::executor::ExecutorConfig;
44
use crate::executor::helpers::command::CommandBuilder;
55
use crate::executor::shared::fifo::FifoBenchmarkData;
66
use crate::executor::wall_time::profiler::Profiler;
7+
use crate::executor::wall_time::profiler::linux_sysctl::ensure_linux_profiling_sysctls;
78
use crate::prelude::*;
89
use async_trait::async_trait;
910
use runner_shared::artifacts::ArtifactExt;
@@ -32,6 +33,14 @@ impl SamplyProfiler {
3233

3334
#[async_trait(?Send)]
3435
impl Profiler for SamplyProfiler {
36+
async fn setup(
37+
&self,
38+
_system_info: &SystemInfo,
39+
_setup_cache_dir: Option<&Path>,
40+
) -> anyhow::Result<()> {
41+
ensure_linux_profiling_sysctls()
42+
}
43+
3544
async fn wrap(
3645
&mut self,
3746
mut cmd_builder: CommandBuilder,

0 commit comments

Comments
 (0)