Skip to content

Commit 90409e0

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 2ffcfb3 commit 90409e0

4 files changed

Lines changed: 76 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
@@ -72,6 +72,7 @@ tabled = { version = "0.20.0", features = ["ansi"] }
7272
shell-words = "1.1.0"
7373
rmp-serde = "1.3.0"
7474
uuid = { version = "1.21.0", features = ["v4"] }
75+
which = "8.0.2"
7576

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

crates/memtrack/src/allocators/mod.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,45 @@ 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!(
104+
"Skipping env-specified binary {}: {e}",
105+
path.display()
106+
);
107+
None
108+
}
109+
}
110+
})
111+
.collect()
112+
}
77113
}
78114

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

128163
if !orchestrator.is_local() {

0 commit comments

Comments
 (0)