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)

OffsetSizeFieldDescription
04magic"ATI2"
41endian0x01 = little-endian
51version2
61arch1 = x86_64, 2 = arm64
71os1 = iOS, 2 = Android, 3 = macOS, 4 = Linux, 5 = Windows
84flagsBit 0: has_detail_file
124thread_idThread ID for this file
161clock_type1 = mach_continuous, 2 = qpc, 3 = boottime
173reserved
204event_size32 (fixed)
248event_countTotal number of events
328events_offsetByte offset to first event (typically 64)
408footer_offsetByte offset to footer
488time_start_nsFirst event timestamp
568time_end_nsLast event timestamp

Index Event (32 bytes)

OffsetSizeFieldDescription
08timestamp_nsPlatform continuous clock (nanoseconds)
88function_id(module_id << 32) | symbol_index
168detail_seqForward link to detail event (u64::MAX = none)
241event_kind1 = CALL, 2 = RETURN, 3 = EXCEPTION
257reservedPadding 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.

OffsetSizeFieldDescription
04magic"2ITA" (reversed)
44checksumCRC32-C of events section (0 = legacy/unchecked)
88event_countAuthoritative event count
168time_start_nsFirst event timestamp
248time_end_nsLast event timestamp
328bytes_writtenTotal bytes in events section
4024reserved

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)

OffsetSizeFieldDescription
04magic"ATD2"
41endian0x01 = little-endian
51version2
61archSame encoding as index
71osSame encoding as index
84flagsReserved
124thread_idThread ID
164reserved
208events_offsetByte offset to first event (typically 64)
288event_countNumber of detail events
368bytes_lengthTotal bytes in events section
448index_seq_startFirst index sequence number covered
528index_seq_endLast index sequence number covered
604reserved

Detail Event (variable length)

Each detail event has a 24-byte header followed by a variable-length payload:

Detail Event Header (24 bytes):

OffsetSizeFieldDescription
04total_lengthTotal bytes including header and payload
42event_type3 = FUNCTION_CALL, 4 = FUNCTION_RETURN
62flagsEvent-specific flags
88index_seqBackward link to index event position
168timestampMonotonic nanoseconds (same clock as index)

The payload immediately follows the header. Its length is total_length - 24 bytes.

OffsetSizeFieldDescription
04magic"2DTA" (reversed)
44checksumCRC32-C of events section
88event_countActual event count
168bytes_lengthActual bytes in events section
248time_start_nsFirst event timestamp
328time_end_nsLast event timestamp
4024reserved

Cross-Lane Linking

Index and detail events are linked bidirectionally:

  • Index → Detail: The detail_seq field in an index event is the sequence number of the corresponding detail event. u64::MAX means no detail event exists.
  • Detail → Index: The index_seq field 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

NameValueDescription
ATF_NO_DETAIL_SEQu64::MAXSentinel: no linked detail event
ATF_INDEX_FLAG_HAS_DETAIL_FILE1 << 0Index header flag: detail file exists
ATF_EVENT_KIND_CALL1Function call event
ATF_EVENT_KIND_RETURN2Function return event
ATF_EVENT_KIND_EXCEPTION3Exception event
ATF_DETAIL_EVENT_FUNCTION_CALL3Detail event type: function call
ATF_DETAIL_EVENT_FUNCTION_RETURN4Detail event type: function return

Crash Recovery

ATF files are designed to be readable even after a crash:

  1. No footer — If the footer is missing, the reader calculates event count from file size: (file_size - events_offset) / event_size
  2. Footer checksum = 0 — Treated as a legacy file; validation passes without checking
  3. Footer present — The footer’s event_count is 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 access
  • atf::detail::DetailReader — memory-mapped detail file reader with indexed lookup
  • atf::session::SessionReader — multi-thread session reader with merge-sort iterator