Skip to content

Commit c7ad8fb

Browse files
committed
test: avoid ETXTBSY in entry-value launcher
Sync the generated wrapper script before executing it and retry only when spawning fails with ETXTBSY. This keeps the entry-value runtime tests stable on slower or overlay-backed CI filesystems without hiding unrelated launch failures.
1 parent 95d277a commit c7ad8fb

1 file changed

Lines changed: 38 additions & 5 deletions

File tree

e2e-tests/tests/entry_value_recovery_execution.rs

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use ghostscope_dwarf::{ComputeStep, MemoryAccessSize};
1010
use regex::Regex;
1111
use std::collections::HashMap;
1212
use std::fs;
13+
use std::io::Write;
1314
use std::path::{Path, PathBuf};
1415
use std::process::Command as StdCommand;
1516
use std::sync::OnceLock;
@@ -101,18 +102,15 @@ exec {binary} >>{stdout} 2>>{stderr}
101102
stdout = shell_quote(&stdout_log),
102103
stderr = shell_quote(&stderr_log),
103104
);
104-
fs::write(&wrapper_path, wrapper)?;
105+
write_wrapper_script(&wrapper_path, &wrapper)?;
105106
#[cfg(unix)]
106107
{
107108
use std::os::unix::fs::PermissionsExt;
108109
let mut perms = fs::metadata(&wrapper_path)?.permissions();
109110
perms.set_mode(0o755);
110111
fs::set_permissions(&wrapper_path, perms)?;
111112
}
112-
let target = TargetLauncher::binary(&wrapper_path)
113-
.current_dir(base)
114-
.spawn()
115-
.await?;
113+
let target = spawn_wrapper_target(&wrapper_path, base).await?;
116114
tokio::time::sleep(Duration::from_millis(500)).await;
117115
Ok(LoggedTarget {
118116
target,
@@ -129,6 +127,41 @@ fn create_runtime_log_dir(base: &Path) -> anyhow::Result<PathBuf> {
129127
Ok(path)
130128
}
131129

130+
fn write_wrapper_script(path: &Path, contents: &str) -> anyhow::Result<()> {
131+
let mut file = fs::File::create(path)?;
132+
file.write_all(contents.as_bytes())?;
133+
file.sync_all()?;
134+
Ok(())
135+
}
136+
137+
async fn spawn_wrapper_target(wrapper_path: &Path, base: &Path) -> anyhow::Result<TargetHandle> {
138+
let mut delay = Duration::from_millis(50);
139+
for attempt in 0..3 {
140+
match TargetLauncher::binary(wrapper_path)
141+
.current_dir(base)
142+
.spawn()
143+
.await
144+
{
145+
Ok(target) => return Ok(target),
146+
Err(err) if attempt < 2 && is_text_file_busy(&err) => {
147+
tokio::time::sleep(delay).await;
148+
delay *= 2;
149+
}
150+
Err(err) => return Err(err),
151+
}
152+
}
153+
unreachable!("spawn retry loop should return on success or the final error")
154+
}
155+
156+
fn is_text_file_busy(err: &anyhow::Error) -> bool {
157+
err.chain().any(|cause| {
158+
cause
159+
.downcast_ref::<std::io::Error>()
160+
.and_then(std::io::Error::raw_os_error)
161+
== Some(libc::ETXTBSY)
162+
})
163+
}
164+
132165
fn read_log_file(path: &Path) -> anyhow::Result<String> {
133166
match fs::read_to_string(path) {
134167
Ok(contents) => Ok(contents),

0 commit comments

Comments
 (0)