Skip to content

Commit 72d5f05

Browse files
committed
fix: valgrind crash for unresolved libpython
1 parent d3c5540 commit 72d5f05

File tree

1 file changed

+88
-1
lines changed

1 file changed

+88
-1
lines changed

src/run/runner/valgrind/measure.rs

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::fs::canonicalize;
1212
use std::io::Write;
1313
use std::os::unix::fs::PermissionsExt;
1414
use std::os::unix::process::ExitStatusExt;
15-
use std::path::Path;
15+
use std::path::{Path, PathBuf};
1616
use std::{env::consts::ARCH, process::Command};
1717
use tempfile::TempPath;
1818

@@ -73,11 +73,98 @@ fn create_run_script() -> anyhow::Result<TempPath> {
7373
Ok(script_file.into_temp_path())
7474
}
7575

76+
/// By default, uv only symlinks the python3 executable which will cause the library lookups of
77+
/// valgrind or ldd to fail:
78+
/// ```no_run
79+
/// > ldd .venv/bin/python3
80+
/// /home/project/.venv/bin/../lib/libpython3.13.so.1.0 => not found
81+
/// ```
82+
///
83+
/// To fix this, we have to also add the symlink to the `libpython` shared object.
84+
fn symlink_libcpython(cwd: Option<&String>) -> anyhow::Result<()> {
85+
let run_cmd = |cmd: &str, args: &[&str]| -> anyhow::Result<String> {
86+
let mut cmd = Command::new(cmd);
87+
88+
if let Some(cwd) = cwd {
89+
cmd.current_dir(cwd);
90+
}
91+
92+
let output = cmd.args(args).output()?;
93+
if !output.status.success() {
94+
let stderr = String::from_utf8(output.stderr)?;
95+
bail!("Failed to execute command: {}", stderr);
96+
}
97+
98+
let output = String::from_utf8(output.stdout)?;
99+
Ok(output.trim().to_string())
100+
};
101+
102+
// 1. find the python installation and version
103+
let venv_python =
104+
run_cmd("uv", &["python", "find"]).context("Failed to find uv python executable")?;
105+
let system_python = std::fs::canonicalize(&venv_python)?;
106+
debug!(
107+
"Python executables: {} (venv), {} (system)",
108+
venv_python,
109+
system_python.display()
110+
);
111+
112+
// 2. Resolve the symlink, then find the python dir
113+
let python_dir = |py_exe: &Path| {
114+
py_exe
115+
.parent()
116+
.and_then(|p| p.parent())
117+
.context("Failed to find python dir")
118+
.map(|p| p.to_path_buf())
119+
};
120+
121+
let system_python_dir = python_dir(&system_python)?;
122+
let venv_python_dir = python_dir(&PathBuf::from(venv_python))?;
123+
debug!(
124+
"Python dirs: {} (venv), {} (system)",
125+
venv_python_dir.display(),
126+
system_python_dir.display(),
127+
);
128+
129+
// 3. Find the libpython in the system dir
130+
let mut libpython_name = None;
131+
for entry in std::fs::read_dir(system_python_dir.join("lib"))?.filter_map(Result::ok) {
132+
let filename = entry.file_name().to_string_lossy().to_string();
133+
if filename.starts_with("libpython") && filename.ends_with(".so.1.0") {
134+
libpython_name = Some(filename);
135+
break;
136+
}
137+
}
138+
let libpython_name = libpython_name.context("Failed to find libpython shared object")?;
139+
140+
// Ensure the lib directory exists
141+
let venv_lib = venv_python_dir.join("lib");
142+
std::fs::create_dir_all(&venv_lib)?;
143+
144+
// 4. Create a symlink in .venv/lib/
145+
let libpython_path = |python_dir: &Path| python_dir.join("lib").join(&*libpython_name);
146+
let original_path = libpython_path(&system_python_dir);
147+
let link_path = libpython_path(&venv_python_dir);
148+
match std::os::unix::fs::symlink(original_path, link_path) {
149+
Err(error) if error.kind() != std::io::ErrorKind::AlreadyExists => {
150+
bail!("Failed to create symlink: {}", error)
151+
}
152+
_ => {}
153+
}
154+
155+
Ok(())
156+
}
157+
76158
pub async fn measure(
77159
config: &Config,
78160
profile_folder: &Path,
79161
mongo_tracer: &Option<MongoTracer>,
80162
) -> Result<()> {
163+
// Ensure that valgrind can find the libpython library when using uv:
164+
if config.command.contains("uv run") {
165+
symlink_libcpython(config.working_directory.as_ref())?;
166+
}
167+
81168
// Create the command
82169
let mut cmd = Command::new("setarch");
83170
cmd.arg(ARCH).arg("-R");

0 commit comments

Comments
 (0)