# SPULSE spool format
A spool file is the on-disk profile that `Recorder` produces: an
append-only binary stream designed so that recording only ever issues small
writes. If recording dies partway through the final record, readers keep the
complete prefix that precedes it. A finished file can be read fully in memory
through `Snapshot` or replayed sequentially with bounded memory through
`Replay`. `Tail` reads the complete prefix incrementally while the writer is
still appending records.
## Recovery
A reader accepts a file that ends partway through its final record. It retains
the complete prefix and reports recovery through
`Snapshot::recovered_from_truncated_tail` or
`Replay::recovered_from_truncated_tail`. Corruption within the complete prefix
remains an error.
`Tail` handles an incomplete final record differently: it leaves the reader
position before that record and retries it after the file grows. The writer
must not modify or truncate bytes that were already visible to the reader.
## Stream header
Every file begins with:
1. The eight-byte magic value `SPULSE3\0`.
2. The profile start timestamp in microseconds, encoded as an unsigned varint.
3. The requested sample interval in microseconds, encoded as an unsigned
varint.
Integers after the magic use the `integer-encoding` varint representation.
Signed values use that crate's signed encoding.
## Record types
Each record starts with a one-byte tag.
| 1 | module | Defines an executable mapping and its stable module id. |
| 2 | frame | Interns a module-relative or absolute instruction address. |
| 3 | stack | Interns one prefix-linked stack node. |
| 4 | thread | Interns a process and thread identity. |
| 5 | sample | Associates a timestamp delta, thread, and stack. |
| 6 | Python runtime | Marks a process as entering or leaving Python-runtime mode. |
| 7 | process module deactivation | Retires active mappings for one process. |
| 8 | module deactivation | Retires one mapping generation. |
Definitions are ordered and ids are dense. A record may only refer to an id
defined earlier in the stream. Readers reject forward references, duplicate
definition ids, overflowing address arithmetic, and references outside a
module's mapped span.
## Frame encoding
A frame matched to a known module stores that module's id and a file-relative
address. A frame with no matching module stores an absolute address and a
user/kernel mode bit. A reserved tag encodes a truncated-stack marker with a
zero payload.
Return addresses are normalized to the calling instruction before a frame is
written. Module-relative addresses use checked subtraction and addition;
overflow or an address outside the mapping is rejected.
## Replay and resource bounds
`Snapshot` retains decoded samples for random access.
`Replay` retains definitions and a bounded sample-range index,
then decodes sample records sequentially. Once the range index reaches its
configured limit, replay scans the validated suffix without retaining one
index entry per sample.
`Tail` retains definitions but returns samples in batches of at most 16,384.
The next poll clears and reuses the previous sample storage. Definitions stay
resident because later records can refer to any earlier module, frame, stack,
or thread definition.
`Snapshot` and `Replay` validate the complete retained prefix when opened.
`Tail` performs the same checks as each complete record becomes visible.
Sequential iteration only visits records and references that passed those
checks.