Skip to content

Commit ae7bc14

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

4 files changed

Lines changed: 129 additions & 1 deletion

File tree

src/run/runner/valgrind/executor.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::run::runner::{ExecutorName, RunData};
77
use crate::run::{check_system::SystemInfo, config::Config};
88

99
use super::setup::install_valgrind;
10-
use super::{helpers::perf_maps::harvest_perf_maps, measure};
10+
use super::{helpers::perf_maps::harvest_perf_maps, helpers::venv_compat, measure};
1111

1212
pub struct ValgrindExecutor;
1313

@@ -19,6 +19,13 @@ impl Executor for ValgrindExecutor {
1919

2020
async fn setup(&self, system_info: &SystemInfo) -> Result<()> {
2121
install_valgrind(system_info).await?;
22+
23+
if let Err(error) = venv_compat::symlink_libpython(None) {
24+
error!("Failed to symlink libpython: {}", error);
25+
} else {
26+
info!("Successfully added symlink for libpython in the venv");
27+
}
28+
2229
Ok(())
2330
}
2431

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod ignored_objects_path;
22
pub mod introspected_nodejs;
33
pub mod perf_maps;
4+
pub mod venv_compat;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//! When creating a virtual environment, only `python3` is symlinked or copied which makes
2+
//! lookups to `.venv/lib/libpython{version}.so.1.0` fail. This isn't an issue for most distributions
3+
//! since they use absolute paths in the `python3` executable.
4+
//!
5+
//! However, uv uses relative paths which causes the lookups to fail:
6+
//! ```no_run
7+
//! > ldd .venv/bin/python3
8+
//! /home/project/.venv/bin/../lib/libpython3.13.so.1.0 => not found
9+
//! ```
10+
//!
11+
//! The solution to this is to add the symlink of the `libpython` shared object in the
12+
//! virtual environment (`.venv/lib`) to make the symlink work correctly.
13+
14+
use crate::prelude::*;
15+
use std::{io::Write, os::unix::fs::PermissionsExt, process::Command};
16+
17+
/// This scripts tries to find the virtual environment using `uv python find` and by finding the
18+
/// `python3` executable in the activated virtual environment.
19+
const VENV_COMPAT_SCRIPT: &str = include_str!("venv_compat.sh");
20+
21+
pub fn symlink_libpython(cwd: Option<&String>) -> anyhow::Result<()> {
22+
let rwx = std::fs::Permissions::from_mode(0o777);
23+
let mut script_file = tempfile::Builder::new()
24+
.suffix(".sh")
25+
.permissions(rwx)
26+
.tempfile()?;
27+
script_file.write_all(VENV_COMPAT_SCRIPT.as_bytes())?;
28+
29+
let mut cmd = Command::new("bash");
30+
cmd.arg(script_file.path());
31+
32+
if let Some(cwd) = cwd {
33+
cmd.current_dir(cwd);
34+
}
35+
36+
debug!("Running the venv compat script");
37+
let output = cmd.output()?;
38+
39+
let stdout = String::from_utf8(output.stdout)?;
40+
debug!("Script output: {stdout}");
41+
42+
if !output.status.success() {
43+
let stderr = String::from_utf8(output.stderr)?;
44+
bail!("Failed to execute script: {stdout} {stderr}");
45+
}
46+
47+
Ok(())
48+
}
49+
50+
#[cfg(test)]
51+
mod tests {
52+
use super::*;
53+
54+
#[test]
55+
fn test_venv_compat_no_crash() {
56+
assert!(symlink_libpython(None).is_ok());
57+
}
58+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env bash
2+
3+
function add_symlink() {
4+
local venv_python="$1"
5+
6+
system_python="$(readlink -f "$venv_python")"
7+
if [ -z "$system_python" ]; then
8+
echo "Error: Failed to resolve real path for $venv_python" >&2
9+
return 1
10+
fi
11+
12+
system_path="$(dirname $(dirname "$system_python"))"
13+
venv_path="$(dirname $(dirname "$venv_python"))"
14+
echo "Python installation (system): $system_path"
15+
echo "Python installation (venv): $venv_path"
16+
17+
# Find libpython in the system python dir
18+
mapfile -t libpython_array < <(find "$system_path" -maxdepth 3 -type f -name "libpython*.so.1.0" 2>/dev/null)
19+
if [ ${#libpython_array[@]} -eq 0 ]; then
20+
echo "Error: libpython*.so.1.0 not found in $system_path"
21+
exit 1
22+
fi
23+
24+
libpython="${libpython_array[0]}"
25+
libpython_name=$(basename "$libpython")
26+
echo "Found libpython: $libpython"
27+
28+
# Create the symlink in the virtual environment
29+
venv_link="$venv_path/lib/$libpython_name"
30+
if [ -e "$venv_link" ]; then
31+
echo "Symlink already exists: $venv_link"
32+
else
33+
echo "Creating symlink: $venv_link -> $libpython"
34+
ln -s "$libpython" "$venv_link"
35+
fi
36+
}
37+
38+
##
39+
##
40+
##
41+
42+
uv_python="$(uv python find 2>/dev/null || true)"
43+
if [ -n "$uv_python" ]; then
44+
add_symlink "$uv_python"
45+
else
46+
echo "Didn't find uv venv, continuing..."
47+
fi
48+
49+
##
50+
##
51+
##
52+
53+
python3_path="$(which python3 2>/dev/null || true)"
54+
if [ -n "$python3_path" ]; then
55+
echo "Found system Python: $python3_path"
56+
57+
if ldd "$python3_path" | grep -q "libpython.*not found"; then
58+
add_symlink "$python3_path"
59+
else
60+
echo "System python is already correctly linked, continuing..."
61+
fi
62+
fi

0 commit comments

Comments
 (0)