Skip to main content

Binary Trace Format Specification

Overview

Chronon's binary trace format (.ctrace) provides compact, self-describing trace storage using FlatBuffers for schema and zstd for compression.

File Structure

┌─────────────────────────────────────┐
│ File Header (64 bytes, fixed) │
├─────────────────────────────────────┤
│ Schema Section (FlatBuffers) │
├─────────────────────────────────────┤
│ Event Block 0 │
│ ├── Block Header (28 bytes) │
│ └── Compressed Event Data │
├─────────────────────────────────────┤
│ Event Block 1...N │
├─────────────────────────────────────┤
│ Footer (FlatBuffers) │
└─────────────────────────────────────┘

File Header (64 bytes)

OffsetSizeFieldDescription
04magic0x43545243 ("CTRC" little-endian)
42version_majorFormat version major (1)
62version_minorFormat version minor (1)
88schema_offsetByte offset to schema section
168schema_sizeSize of schema in bytes
248first_block_offsetByte offset to first event block
328footer_offsetByte offset to footer
408footer_sizeSize of footer in bytes
488flagsFeature flags (see below)
568reservedReserved for future use

Flags

BitNameDescription
0FLAG_COMPRESSEDEvent data is zstd compressed
1FLAG_HAS_INDEXFooter contains block index
2FLAG_HAS_SCHEMASchema section present

Schema Section

FlatBuffers-encoded schema containing:

table TraceSchema {
version: uint32;
formats: [FormatEntry];
categories: [CategoryEntry];
units: [UnitEntry];
simulation_name: string;
config_hash: uint64;
}

table FormatEntry {
id: uint32;
format_string: string;
file: string;
line: uint32;
arg_types: [ArgType];
is_log: bool;
log_level: LogLevel;
}

table CategoryEntry {
name: string;
description: string;
mask: uint64;
}

table UnitEntry {
id: uint16;
name: string;
type_name: string;
}

Event Block

Each block contains multiple events with metadata for seeking and compression.

Block Header Dual Representation

The block header exists in two forms:

  1. Inline Binary (28 bytes): Written before each compressed block for streaming parsing
  2. FlatBuffers BlockHeader: Stored in the file footer for indexed random access

Both representations contain the same information but serve different purposes:

  • Inline binary enables sequential streaming without parsing the footer
  • FlatBuffers footer enables efficient cycle-based seeking

Inline Block Header (28 bytes)

OffsetSizeFieldDescription
04event_countNumber of events in block
48min_cycleMinimum cycle in block
128max_cycleMaximum cycle in block
204uncompressed_sizeSize before compression
244compressed_sizeSize after compression

Event Data

After the block header, compressed event data follows. Each event is stored as:

[args_size:2][StructuredRecord:24][args:N]

Where args_size is a 2-byte prefix (added in v1.1) indicating the size of the argument data that follows the record.

Important: The in-memory runtime representation (StructuredRecord in Types.hpp) differs slightly from the FlatBuffers schema representation (EventRecord in TraceSchema.fbs). Both are 24 bytes but use different field names. The binary format uses StructuredRecord layout.

StructuredRecord (24 bytes)

OffsetSizeFieldDescription
08cycleSimulation cycle
84format_idIndex into schema formats
124categoryCategory bitmask (truncated to 32 bits)
162source_idUnit ID
181arg_countNumber of arguments
191paddingAlignment padding

Argument Types

ValueTypeSize
1Int81
2Int162
3Int324
4Int648
5UInt81
6UInt162
7UInt324
8UInt648
9Float4
10Double8
11Pointer8
12StringVariable (null-terminated)
13Bool1

FlatBuffers-encoded index for random access:

table FileFooter {
blocks: [BlockHeader];
total_events: uint64;
min_cycle: uint64;
max_cycle: uint64;
created_timestamp: uint64;
}

table BlockHeader {
block_index: uint32;
event_count: uint32;
min_cycle: uint64;
max_cycle: uint64;
uncompressed_size: uint32;
compressed_size: uint32;
data_offset: uint64;
}

Version History

VersionChanges
1.0Initial format
1.1Added per-event args_size prefix for robust parsing

Reading Traces

Using trace_reader CLI

The trace_reader tool provides several commands for analyzing trace files:

# Show trace file information and statistics
trace_reader info events.ctrace

# Show verbose info with all registered formats and units
trace_reader info events.ctrace --verbose

# Dump all events as text
trace_reader dump events.ctrace

# Filter events by cycle range
trace_reader filter events.ctrace --cycles 1000-2000

# Filter by unit name
trace_reader filter events.ctrace --unit "cpu.Fetch"

# Limit output to first N events
trace_reader dump events.ctrace --limit 100

# Convert to text file
trace_reader convert events.ctrace -o output.log

# Combine filters
trace_reader filter events.ctrace --cycles 5000-10000 --unit "Decode" --limit 50 -o filtered.log

Using BinaryTraceReader API

For programmatic access:

#include "chronon/Chronon.hpp"

BinaryTraceReader reader;
if (reader.open("events.ctrace")) {
while (auto event = reader.readEvent()) {
std::cout << "[" << event->cycle << "] "
<< reader.reconstructMessage(*event) << "\n";
}
}

Note: The default output file is events.ctrace (not trace.ctrace). Both trace events and log events can appear in the binary file if their channel format is set to binary or both.

Compression

  • Algorithm: zstd (level 3 default)
  • Block size: 64KB before compression (configurable)
  • Typical ratio: ~20% (5x compression)

Compression is optional and indicated by FLAG_COMPRESSED.