Skip to content

Commit efa8e53

Browse files
committed
feat(memtrack): support CODSPEED_MEMTRACK_BINARIES for static allocator discovery
Memtrack discovers allocators by scanning known build directories, but exec CLI binaries may not live there. This causes memory profiling to miss allocations when the binary has a statically linked allocator (common in Rust). Add CODSPEED_MEMTRACK_BINARIES env var (colon-separated paths) that tells memtrack additional binaries to scan. The exec CLI auto-sets it by resolving exec target binary paths before execution. COD-2347
1 parent 106e731 commit efa8e53

4 files changed

Lines changed: 73 additions & 17 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ tabled = { version = "0.20.0", features = ["ansi"] }
7171
shell-words = "1.1.0"
7272
rmp-serde = "1.3.0"
7373
uuid = { version = "1.21.0", features = ["v4"] }
74+
which = "8.0.2"
7475

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

crates/memtrack/src/allocators/mod.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,42 @@ pub struct AllocatorLib {
7171
impl AllocatorLib {
7272
pub fn find_all() -> anyhow::Result<Vec<AllocatorLib>> {
7373
let mut allocators = static_linked::find_all()?;
74+
allocators.extend(Self::find_from_env());
7475
allocators.extend(dynamic::find_all()?);
7576
Ok(allocators)
7677
}
78+
79+
/// Discover allocators from binaries listed in the `CODSPEED_MEMTRACK_BINARIES` env var.
80+
///
81+
/// The variable is a colon-separated list of paths. Each path is checked for
82+
/// a statically linked allocator via [`AllocatorLib::from_path_static`].
83+
/// Invalid or missing paths are silently skipped (with a debug log).
84+
fn find_from_env() -> Vec<AllocatorLib> {
85+
let Ok(raw) = std::env::var("CODSPEED_MEMTRACK_BINARIES") else {
86+
return vec![];
87+
};
88+
89+
raw.split(':')
90+
.filter(|p| !p.is_empty())
91+
.filter_map(|p| {
92+
let path = std::path::PathBuf::from(p);
93+
match Self::from_path_static(&path) {
94+
Ok(alloc) => {
95+
log::debug!(
96+
"Found {} allocator in env-specified binary: {}",
97+
alloc.kind.name(),
98+
path.display()
99+
);
100+
Some(alloc)
101+
}
102+
Err(e) => {
103+
log::debug!("Skipping env-specified binary {}: {e}", path.display());
104+
None
105+
}
106+
}
107+
})
108+
.collect()
109+
}
77110
}
78111

79112
/// Check if a file is an ELF binary by reading its magic bytes.

src/cli/exec/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,41 @@ pub async fn execute_config(
121121
codspeed_config: &CodSpeedConfig,
122122
setup_cache_dir: Option<&Path>,
123123
) -> Result<()> {
124+
// Set CODSPEED_MEMTRACK_BINARIES so memtrack can discover statically linked
125+
// allocators in exec target binaries (which may not live in known build dirs).
126+
let memtrack_binaries: Vec<String> = config
127+
.targets
128+
.iter()
129+
.filter_map(|t| match t {
130+
executor::BenchmarkTarget::Exec { command, .. } => command.first().cloned(),
131+
_ => None,
132+
})
133+
.filter_map(|bin| {
134+
let result = match &config.working_directory {
135+
Some(cwd) => which::which_in(&bin, std::env::var_os("PATH"), cwd),
136+
None => which::which(&bin),
137+
};
138+
match result {
139+
Ok(path) => Some(path.to_string_lossy().into_owned()),
140+
Err(e) => {
141+
debug!("Could not resolve exec binary {bin:?}: {e}");
142+
None
143+
}
144+
}
145+
})
146+
.collect();
147+
148+
if !memtrack_binaries.is_empty() {
149+
let mut all_paths = memtrack_binaries;
150+
if let Ok(existing) = std::env::var("CODSPEED_MEMTRACK_BINARIES") {
151+
if !existing.is_empty() {
152+
all_paths.push(existing);
153+
}
154+
}
155+
// SAFETY: The runner uses single-threaded tokio, so no concurrent env access.
156+
unsafe { std::env::set_var("CODSPEED_MEMTRACK_BINARIES", all_paths.join(":")) };
157+
}
158+
124159
let orchestrator = executor::Orchestrator::new(config, codspeed_config, api_client).await?;
125160

126161
if !orchestrator.is_local() {

0 commit comments

Comments
 (0)