perry compile creates a per-process scratch directory under the system temp dir and never deletes it. On a dev box doing repeated compiles this accumulates until the disk fills.
Impact observed
During a refactor session on macOS this filled the volume to literally 0 bytes free — builds in unrelated shells started failing, and even writing tool output failed. Cleanup evidence from that host:
- First sweep: 340 orphaned
perry_strip_<pid> directories, every one of them belonging to a dead PID. Removing only those took free space from 0 → 20 GB (~60 MB each).
- Roughly two hours later, after ordinary local build/test activity: 75 more, again all dead PIDs, another 8 GB (11 GB → 19 GB free, ~107 MB each).
So this is not a one-off from a crash — it accumulates during normal use, at roughly 60–110 MB per compile that goes through the strip/dedup path.
Cause
crates/perry/src/commands/compile/strip_dedup.rs and .../strip_dedup/stub_symbols.rs build the scratch path at 8 separate sites, all identical:
let tmp_base = std::env::temp_dir().join(format!("perry_strip_{}", std::process::id()));
std::fs::create_dir_all(&tmp_base).ok();
let trimmed_lib = tmp_base.join(format!("_{lib_name}_trimmed.lib"));
let extract_dir = tmp_base.join(format!("_{lib_name}_extract"));
let _ = std::fs::remove_dir_all(&extract_dir);
std::fs::create_dir_all(&extract_dir)?;
Sites: strip_dedup.rs:677, 896, 1089, 1231, 1438 and stub_symbols.rs:170, 346, 473.
There are zero remove_dir_all(&tmp_base) calls anywhere in either file. The only removal is of the inner extract_dir, and it runs before creation as a stale-state guard rather than after use — so both the extracted objects and the trimmed .lib survive process exit.
Because the name is keyed on std::process::id(), every compile invocation gets a fresh directory rather than reusing one, so nothing is ever overwritten either.
Suggested fix
Any of these would work; the first is probably cleanest given there are 8 duplicated sites:
- Factor the scratch-dir setup into one helper returning a guard type whose
Drop does remove_dir_all(&tmp_base). That fixes all 8 sites at once and removes the copy-paste. (Note Drop won't run on process::exit/panic-abort, so pair it with 2.)
- Add a best-effort sweep at compile startup: scan
temp_dir() for perry_strip_*, parse the trailing PID, and remove directories whose PID is no longer alive. This is exactly what I did by hand to recover the disk, and it self-heals hosts that already have a backlog.
- At minimum, delete
tmp_base at the end of the strip/dedup path on the success route.
Worth checking whether perry_llvm_scratch_* (same temp dir, ~21 GB on this host) has the same lifecycle problem — I left those alone since they may have belonged to a live session, but the naming pattern suggests the same design.
Reproduce
ls -d "${TMPDIR}"perry_strip_* | wc -l # before
perry some.ts -o /tmp/out # any compile hitting strip/dedup
ls -d "${TMPDIR}"perry_strip_* | wc -l # one more, and it never goes away
perry compilecreates a per-process scratch directory under the system temp dir and never deletes it. On a dev box doing repeated compiles this accumulates until the disk fills.Impact observed
During a refactor session on macOS this filled the volume to literally 0 bytes free — builds in unrelated shells started failing, and even writing tool output failed. Cleanup evidence from that host:
perry_strip_<pid>directories, every one of them belonging to a dead PID. Removing only those took free space from 0 → 20 GB (~60 MB each).So this is not a one-off from a crash — it accumulates during normal use, at roughly 60–110 MB per compile that goes through the strip/dedup path.
Cause
crates/perry/src/commands/compile/strip_dedup.rsand.../strip_dedup/stub_symbols.rsbuild the scratch path at 8 separate sites, all identical:Sites:
strip_dedup.rs:677, 896, 1089, 1231, 1438andstub_symbols.rs:170, 346, 473.There are zero
remove_dir_all(&tmp_base)calls anywhere in either file. The only removal is of the innerextract_dir, and it runs before creation as a stale-state guard rather than after use — so both the extracted objects and the trimmed.libsurvive process exit.Because the name is keyed on
std::process::id(), every compile invocation gets a fresh directory rather than reusing one, so nothing is ever overwritten either.Suggested fix
Any of these would work; the first is probably cleanest given there are 8 duplicated sites:
Dropdoesremove_dir_all(&tmp_base). That fixes all 8 sites at once and removes the copy-paste. (NoteDropwon't run onprocess::exit/panic-abort, so pair it with 2.)temp_dir()forperry_strip_*, parse the trailing PID, and remove directories whose PID is no longer alive. This is exactly what I did by hand to recover the disk, and it self-heals hosts that already have a backlog.tmp_baseat the end of the strip/dedup path on the success route.Worth checking whether
perry_llvm_scratch_*(same temp dir, ~21 GB on this host) has the same lifecycle problem — I left those alone since they may have belonged to a live session, but the naming pattern suggests the same design.Reproduce