ATF Format Specification
The ADA Trace Format (ATF) v2 is the binary format used to store function-level tracing data. It uses a dual-lane architecture: a lightweight Index lane that is always written, and an optional Detail lane for richer event data.
Per-Thread File Organization
Each thread gets its own directory under the trace session:
thread_<id>/
├── index.atf # Index lane (always present)
└── detail.atf # Detail lane (optional)
Index File (.atf / .ati2)
The index lane records fixed-size 32-byte events for every function call and return. It is designed for fast sequential scanning and O(1) random access via memory mapping.
Index Header (64 bytes)
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 4 | magic | "ATI2" |
| 4 | 1 | endian | 0x01 = little-endian |
| 5 | 1 | version | 2 |
| 6 | 1 | arch | 1 = x86_64, 2 = arm64 |
| 7 | 1 | os | 1 = iOS, 2 = Android, 3 = macOS, 4 = Linux, 5 = Windows |
| 8 | 4 | flags | Bit 0: has_detail_file |
| 12 | 4 | thread_id | Thread ID for this file |
| 16 | 1 | clock_type | 1 = mach_continuous, 2 = qpc, 3 = boottime |
| 17 | 3 | reserved | |
| 20 | 4 | event_size | 32 (fixed) |
| 24 | 8 | event_count | Total number of events |
| 32 | 8 | events_offset | Byte offset to first event (typically 64) |
| 40 | 8 | footer_offset | Byte offset to footer |
| 48 | 8 | time_start_ns | First event timestamp |
| 56 | 8 | time_end_ns | Last event timestamp |
Index Event (32 bytes)
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 8 | timestamp_ns | Platform continuous clock (nanoseconds) |
| 8 | 8 | function_id | (module_id << 32) | symbol_index |
| 16 | 8 | detail_seq | Forward link to detail event (u64::MAX = none) |
| 24 | 1 | event_kind | 1 = CALL, 2 = RETURN, 3 = EXCEPTION |
| 25 | 7 | reserved | Padding to 32 bytes |
The function_id encodes two pieces of information: the upper 32 bits identify the module (0 = main executable, 1+ = dylibs), and the lower 32 bits are the symbol index within that module.
Index Footer (64 bytes)
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 4 | magic | "2ITA" (reversed) |
| 4 | 4 | checksum | CRC32-C of events section (0 = legacy/unchecked) |
| 8 | 8 | event_count | Authoritative event count |
| 16 | 8 | time_start_ns | First event timestamp |
| 24 | 8 | time_end_ns | Last event timestamp |
| 32 | 8 | bytes_written | Total bytes in events section |
| 40 | 24 | reserved |
The footer’s event_count is authoritative — if both header and footer exist, the footer count takes precedence. This supports crash recovery: the footer is written last, so if present it confirms a clean write.
Detail File (.atf / .atd2)
The detail lane stores variable-length events with richer payload data. It is only present when detail recording is enabled (e.g. during voice capture with --pre-roll-ms / --post-roll-ms).
Detail Header (64 bytes)
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 4 | magic | "ATD2" |
| 4 | 1 | endian | 0x01 = little-endian |
| 5 | 1 | version | 2 |
| 6 | 1 | arch | Same encoding as index |
| 7 | 1 | os | Same encoding as index |
| 8 | 4 | flags | Reserved |
| 12 | 4 | thread_id | Thread ID |
| 16 | 4 | reserved | |
| 20 | 8 | events_offset | Byte offset to first event (typically 64) |
| 28 | 8 | event_count | Number of detail events |
| 36 | 8 | bytes_length | Total bytes in events section |
| 44 | 8 | index_seq_start | First index sequence number covered |
| 52 | 8 | index_seq_end | Last index sequence number covered |
| 60 | 4 | reserved |
Detail Event (variable length)
Each detail event has a 24-byte header followed by a variable-length payload:
Detail Event Header (24 bytes):
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 4 | total_length | Total bytes including header and payload |
| 4 | 2 | event_type | 3 = FUNCTION_CALL, 4 = FUNCTION_RETURN |
| 6 | 2 | flags | Event-specific flags |
| 8 | 8 | index_seq | Backward link to index event position |
| 16 | 8 | timestamp | Monotonic nanoseconds (same clock as index) |
The payload immediately follows the header. Its length is total_length - 24 bytes.
Detail Footer (64 bytes)
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 4 | magic | "2DTA" (reversed) |
| 4 | 4 | checksum | CRC32-C of events section |
| 8 | 8 | event_count | Actual event count |
| 16 | 8 | bytes_length | Actual bytes in events section |
| 24 | 8 | time_start_ns | First event timestamp |
| 32 | 8 | time_end_ns | Last event timestamp |
| 40 | 24 | reserved |
Cross-Lane Linking
Index and detail events are linked bidirectionally:
- Index → Detail: The
detail_seqfield in an index event is the sequence number of the corresponding detail event.u64::MAXmeans no detail event exists. - Detail → Index: The
index_seqfield in a detail event header is the sequence number of the corresponding index event.
This allows efficient lookup in either direction: given an index event you can jump directly to its detail, and given a detail event you can find its position in the index timeline.
Constants
| Name | Value | Description |
|---|---|---|
ATF_NO_DETAIL_SEQ | u64::MAX | Sentinel: no linked detail event |
ATF_INDEX_FLAG_HAS_DETAIL_FILE | 1 << 0 | Index header flag: detail file exists |
ATF_EVENT_KIND_CALL | 1 | Function call event |
ATF_EVENT_KIND_RETURN | 2 | Function return event |
ATF_EVENT_KIND_EXCEPTION | 3 | Exception event |
ATF_DETAIL_EVENT_FUNCTION_CALL | 3 | Detail event type: function call |
ATF_DETAIL_EVENT_FUNCTION_RETURN | 4 | Detail event type: function return |
Crash Recovery
ATF files are designed to be readable even after a crash:
- No footer — If the footer is missing, the reader calculates event count from file size:
(file_size - events_offset) / event_size - Footer checksum = 0 — Treated as a legacy file; validation passes without checking
- Footer present — The footer’s
event_countis authoritative and CRC32-C checksum can be validated
This means partially-written files from interrupted traces are still useful — you get all complete events up to the crash point.
Byte Order
ATF v2 only supports little-endian (endian = 0x01). Big-endian files are rejected at read time.
Implementation
The reference reader implementation is in the atf Rust crate:
atf::index::IndexReader— memory-mapped index file reader with O(1) event accessatf::detail::DetailReader— memory-mapped detail file reader with indexed lookupatf::session::SessionReader— multi-thread session reader with merge-sort iterator