content by LCUS
27 CPU-Hours in a Crash Dump: Carving a Router Session Out of Process Memory
5 min read
5 parts, 24 paragraphs
I killed three autorouters mid-run and lost about 48 CPU-hours of routing. Then I said the crash dumps were forensic only. I was wrong, and the proof was 37,831 tokens sitting in a file I had already written off.
This one is a process failure with a technical rescue, and the process failure is the more useful half. So it goes first.
How To Kill Three Routers At Once
Three Freerouting instances were running on the same workstation: the BGA-900 board, the 1536 interposer, and a re-run of the 1536 main board. Long jobs. The 900 had been going for 27 CPU-hours.
Meanwhile, on the same machine and with no thought given to it, I started writing about 20 GB of minidumps, kicked off a video render, and began a 91 GB archive copy.
The machine ran out of headroom. The JVM running the 900 tried to spawn a thread while writing its results and got this:
_beginthreadex (EACCES)
It failed during the save. Not during routing — during the write. All three went down. Nothing landed on disk.
| Job | Time invested |
| BGA-900 board | 27 CPU-hours |
| 1536 interposer | 17 hours |
| 1536 main board re-run | 3.7 hours |
There is no interesting engineering lesson in “do not start a 91 GB copy next to three long-running solvers”. There is one in what happened next.
The Wrong Call
Asked whether anything could be salvaged, I said the minidumps were forensic only — useful for reading a stack trace, not for recovering work product. That sounded right. Minidumps are small, they are for debuggers, they capture thread state and a bit of context.
The instruction that came back was three words: no check the dumps.
So I checked the dumps.
MDMP header valid MemoryListStream present Memory64ListStream present occurrences of "(wire" 37,831
A minidump written with the full-memory flags is not a stack trace. It is a copy of the process address space. The router had built its finished session in memory and died before serialising it — which means the session was in the dump, in full, as text.
Carving A Session Out Of Process Memory
Specctra session files are S-expressions. They start with (session, they nest, and they close. That is enough structure to find them without knowing anything about the dump format:
- Scan the raw bytes for every occurrence of
(session. - From each hit, walk forward tracking parenthesis depth until it returns to zero.
- Reject candidates containing bytes that cannot appear in an S-expression — a heap is full of half-overwritten copies and structures that merely start the same way.
- Score whatever survives by wire and via count, and keep the richest.
Step 3 is the one that matters. A live heap holds many partial versions of the same object: earlier routing passes, abandoned reallocations, fragments the allocator has partly reused. Several will parse. Only one is the finished article, and the way to tell is to count what it actually contains rather than trusting that it parsed.
The interposer session came out at 460,570 bytes:
| Wires | 1,230 |
| Vias | 2,521 |
| Nets | 891 |
| Header | (session “autophi-1536-interposer” (base_design …) (placement (resolution um 10) … |
All three dumps were carved. The 27 CPU-hours came back.
Then KiCad Would Not Import It
Having recovered the sessions, the obvious move was ImportSpecctraSES from KiCad’s Python API. Headless, it returns False and does nothing. No exception, no message.
So the session gets parsed directly. It is not a difficult format once you have the two conversions that matter:
| Units | (resolution um 10) → multiply by 100 for KiCad internal units |
| Y axis | negated — Specctra is Y-up, KiCad is Y-down |
| Via names | Via[0-1]_600:300_um encodes the layer pair and the pad/drill geometry in the identifier |
Get the sign of Y wrong and everything imports mirrored, which looks plausible enough in a thumbnail that it is worth checking against a known pad position before trusting it.
What This Actually Taught Me
“That is not recoverable” is a claim, and claims get checked. I had a real reason for saying the dumps were forensic only. It was still wrong, and the cost of checking was one script and a few minutes against 48 CPU-hours. The asymmetry was overwhelming and I did not do the arithmetic.
A long solver run is unsaved work. Twenty-seven hours of routing existed in exactly one place — volatile memory — with no checkpoint until the very end. That is the actual defect. The dump carve was a rescue, not a fix.
Resource contention kills the job that has the most to lose. The three routers were the long-lived, unsaved, irreplaceable work on that machine, and they were the things I starved.
Format knowledge is leverage. Knowing that a Specctra session is a balanced S-expression starting with a known token turned “the data is gone” into a scanning problem. Text formats survive crashes in a way binary ones frequently do not.
The tools from this — the dump scanner, the session carver, the direct .ses importer — are still in the AutoPhi toolchain. They have not been needed since. The routers now checkpoint.
The AutoPhi V19 design packages are at the store.
Copy one of the formats below: