Skip to content

Commit bdc3101

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 b958a5b commit bdc3101

4 files changed

Lines changed: 70 additions & 18 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::Path::new(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: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,42 @@ pub async fn run(
118118
/// Sets up the orchestrator and drives execution. Exec-harness installation is handled
119119
/// by the orchestrator when exec targets are present.
120120
pub async fn execute_config(
121-
config: OrchestratorConfig,
121+
mut config: OrchestratorConfig,
122122
api_client: &CodSpeedAPIClient,
123123
codspeed_config: &CodSpeedConfig,
124124
setup_cache_dir: Option<&Path>,
125125
) -> Result<()> {
126+
// Resolve exec target binary paths so memtrack can discover statically linked
127+
// allocators (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+
config.extra_env.insert(
152+
"CODSPEED_MEMTRACK_BINARIES".into(),
153+
memtrack_binaries.join(":"),
154+
);
155+
}
156+
126157
let orchestrator = executor::Orchestrator::new(config, codspeed_config, api_client).await?;
127158

128159
if !orchestrator.is_local() {

0 commit comments

Comments
 (0)