Skip to content

perry compile leaks its perry_strip_<pid> temp dir on every run (filled a dev volume to 0 bytes) #7261

Description

@proggeramlug

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:

  1. 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.)
  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.
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions