Skip to main content

fxt_layout/
layout.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Typed bitfields and enums for the Fuchsia Trace Format (FXT).
6//!
7//! Defined according to `docs/reference/tracing/trace-format.md` and matching
8//! C++ definitions in `<lib/fxt/record_types.h>` and `<lib/fxt/fields.h>`.
9
10#![no_std]
11
12use bitrs::{bitfield_repr, layout};
13
14/// The four byte value present in a magic number record.
15pub const MAGIC_VALUE: u32 = 0x16547846;
16
17/// Enumerates all known record types.
18#[bitfield_repr(u8)]
19pub enum RecordType {
20    Metadata = 0,
21    Initialization = 1,
22    String = 2,
23    Thread = 3,
24    Event = 4,
25    Blob = 5,
26    UserspaceObject = 6,
27    KernelObject = 7,
28    Scheduler = 8,
29    Log = 9,
30    Profiler = 10,
31    /// The kLargeRecord uses a 32-bit size field.
32    LargeBlob = 15,
33}
34
35/// ProfilerRecordType enumerates all known profiler record sub-types.
36#[bitfield_repr(u8)]
37pub enum ProfilerRecordType {
38    Module = 0,
39    Mmap = 1,
40    Backtrace = 2,
41}
42
43#[bitfield_repr(u8)]
44pub enum LargeRecordType {
45    Blob = 0,
46}
47
48/// MetadataType enumerates all known trace metadata types.
49#[bitfield_repr(u8)]
50pub enum MetadataType {
51    ProviderInfo = 1,
52    ProviderSection = 2,
53    ProviderEvent = 3,
54    TraceInfo = 4,
55}
56
57/// Enumerates all provider events.
58#[bitfield_repr(u8)]
59pub enum ProviderEventType {
60    BufferOverflow = 0,
61}
62
63/// Enumerates all known trace info types.
64#[bitfield_repr(u8)]
65pub enum TraceInfoType {
66    MagicNumber = 0,
67}
68
69/// Whether a String/Thread Ref is inline or referenced as an id.
70#[derive(Debug, Copy, Clone, Eq, PartialEq)]
71pub enum RefType {
72    Inline,
73    Id,
74}
75
76/// Enumerates all known argument types.
77#[bitfield_repr(u8)]
78pub enum ArgumentType {
79    Null = 0,
80    Int32 = 1,
81    Uint32 = 2,
82    Int64 = 3,
83    Uint64 = 4,
84    Double = 5,
85    String = 6,
86    Pointer = 7,
87    Koid = 8,
88    Bool = 9,
89    Blob = 10,
90}
91
92/// EventType enumerates all known trace event types.
93#[bitfield_repr(u8)]
94pub enum EventType {
95    Instant = 0,
96    Counter = 1,
97    DurationBegin = 2,
98    DurationEnd = 3,
99    DurationComplete = 4,
100    AsyncBegin = 5,
101    AsyncInstant = 6,
102    AsyncEnd = 7,
103    FlowBegin = 8,
104    FlowStep = 9,
105    FlowEnd = 10,
106}
107
108/// SchedulerEventType enumerates all known scheduler event types.
109#[bitfield_repr(u8)]
110pub enum SchedulerEventType {
111    LegacyContextSwitch = 0,
112    ContextSwitch = 1,
113    ThreadWakeup = 2,
114}
115
116/// Specifies the scope of instant events.
117#[bitfield_repr(u8)]
118pub enum EventScope {
119    Thread = 0,
120    Process = 1,
121    Global = 2,
122}
123
124/// Trace provider id in a trace session.
125pub type ProviderId = u32;
126
127#[bitfield_repr(u8)]
128pub enum BlobType {
129    Data = 1,
130    LastBranch = 2,
131    Perfetto = 3,
132}
133
134#[bitfield_repr(u8)]
135pub enum LargeBlobFormat {
136    Metadata = 0,
137    NoMetadata = 1,
138}
139
140layout!({
141    /// Header word for a standard FXT record.
142    pub struct RecordHeader(u64);
143    {
144        let record_size @ 15..4;
145        let record_type @ 3..0: RecordType;
146    }
147});
148
149layout!({
150    /// Header word for a large FXT record (RecordType = 15).
151    pub struct LargeRecordHeader(u64);
152    {
153        let large_type @ 39..36;
154        let record_size @ 35..4;
155        let record_type @ 3..0: RecordType = RecordType::LargeBlob;
156    }
157});
158
159layout!({
160    /// 16-bit String Reference header entry.
161    pub struct StringRefHeader(u16);
162    {
163        let is_inline @ 15;
164        let id_or_len @ 14..0;
165    }
166});
167
168impl StringRefHeader {
169    /// Constructs a `StringRefHeader` for an indexed string ref.
170    pub fn indexed(id: u16) -> Self {
171        let mut hdr = Self::new();
172        hdr.set_id_or_len(id);
173        hdr
174    }
175
176    /// Constructs a `StringRefHeader` for an inline string ref.
177    pub fn inline(len: u16) -> Self {
178        let mut hdr = Self::new();
179        hdr.set_is_inline(true).set_id_or_len(len);
180        hdr
181    }
182}
183
184layout!({
185    /// 8-bit Thread Reference header entry.
186    pub struct ThreadRefHeader(u8);
187    {
188        let is_inline @ 7;
189        let id_or_len @ 6..0;
190    }
191});
192
193impl ThreadRefHeader {
194    /// Constructs a `ThreadRefHeader` for an indexed thread ref.
195    pub fn indexed(id: u8) -> Self {
196        let mut hdr = Self::new();
197        hdr.set_id_or_len(id);
198        hdr
199    }
200
201    /// Constructs a `ThreadRefHeader` for an inline thread ref.
202    pub fn inline(len: u8) -> Self {
203        let mut hdr = Self::new();
204        hdr.set_is_inline(true).set_id_or_len(len);
205        hdr
206    }
207}
208
209layout!({
210    /// Header word for an FXT Argument.
211    pub struct ArgumentHeader(u64);
212    {
213        let value_bits @ 63..32;
214        let name_ref @ 31..16;
215        let size_words @ 15..4;
216        let arg_type @ 3..0: ArgumentType;
217    }
218});
219
220impl ArgumentHeader {
221    /// Constructs an `ArgumentHeader` for an argument with the given name reference, size in words, and argument type.
222    pub fn for_argument(name_ref: u16, size_words: u16, arg_type: ArgumentType) -> Self {
223        let mut hdr = Self::new();
224        hdr.set_arg_type(arg_type).set_size_words(size_words).set_name_ref(name_ref);
225        hdr
226    }
227}
228
229layout!({
230    /// Header word for an FXT Kernel Object record (RecordType = 7).
231    pub struct KernelObjectRecordHeader(u64);
232    {
233        let arg_count @ 43..40;
234        let name_ref @ 39..24;
235        let obj_type @ 23..16;
236        let record_size @ 15..4;
237        let record_type @ 3..0: RecordType = RecordType::KernelObject;
238    }
239});
240
241layout!({
242    /// Header word for an FXT Userspace Object record (RecordType = 6).
243    pub struct UserspaceObjectRecordHeader(u64);
244    {
245        let arg_count @ 43..40;
246        let name_ref @ 39..24;
247        let process_thread_ref @ 23..16;
248        let record_size @ 15..4;
249        let record_type @ 3..0: RecordType = RecordType::UserspaceObject;
250    }
251});
252
253layout!({
254    /// Header word for an FXT Event record (RecordType = 4).
255    pub struct EventRecordHeader(u64);
256    {
257        let name_ref @ 63..48;
258        let category_ref @ 47..32;
259        let thread_ref @ 31..24;
260        let arg_count @ 23..20;
261        let event_type @ 19..16: EventType;
262        let record_size @ 15..4;
263        let record_type @ 3..0: RecordType = RecordType::Event;
264    }
265});
266
267layout!({
268    /// Header word for an FXT String record (RecordType = 2).
269    pub struct StringRecordHeader(u64);
270    {
271        let string_len @ 46..32;
272        let string_index @ 30..16;
273        let record_size @ 15..4;
274        let record_type @ 3..0: RecordType = RecordType::String;
275    }
276});
277
278layout!({
279    /// Header word for an FXT Thread record (RecordType = 3).
280    pub struct ThreadRecordHeader(u64);
281    {
282        let thread_index @ 23..16;
283        let record_size @ 15..4;
284        let record_type @ 3..0: RecordType = RecordType::Thread;
285    }
286});
287
288layout!({
289    /// Header word for an FXT Blob record (RecordType = 5).
290    pub struct BlobRecordHeader(u64);
291    {
292        let blob_type @ 55..48: BlobType;
293        let blob_size @ 46..32;
294        let name_ref @ 31..16;
295        let record_size @ 15..4;
296        let record_type @ 3..0: RecordType = RecordType::Blob;
297    }
298});
299
300layout!({
301    /// Header word for an FXT Log record (RecordType = 9).
302    pub struct LogRecordHeader(u64);
303    {
304        let thread_ref @ 39..32;
305        let log_message_len @ 30..16;
306        let record_size @ 15..4;
307        let record_type @ 3..0: RecordType = RecordType::Log;
308    }
309});
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314
315    #[test]
316    fn test_record_header() {
317        let mut rec = RecordHeader::new();
318        rec.set_record_size(12).set_record_type(RecordType::Event);
319        assert_eq!(rec.record_type(), RecordType::Event);
320        assert_eq!(rec.record_size(), 12);
321        let raw = rec.bits();
322        assert_eq!(raw & 0xf, 4);
323        assert_eq!((raw >> 4) & 0xfff, 12);
324    }
325
326    #[test]
327    fn test_large_record_header() {
328        let mut lrec = LargeRecordHeader::default();
329        lrec.set_record_size(0x1000).set_large_type(2);
330        assert_eq!(lrec.record_type(), RecordType::LargeBlob);
331        assert_eq!(lrec.record_size(), 0x1000);
332        assert_eq!(lrec.large_type(), 2);
333        let raw = lrec.bits();
334        assert_eq!(raw & 0xf, 15);
335        assert_eq!((raw >> 4) & 0xffffffff, 0x1000);
336        assert_eq!((raw >> 36) & 0xf, 2);
337    }
338
339    #[test]
340    fn test_string_ref_header() {
341        let mut interned = StringRefHeader::new();
342        interned.set_id_or_len(42);
343        assert_eq!(interned.id_or_len(), 42);
344        assert!(!interned.is_inline());
345        assert_eq!(interned.bits(), 42);
346
347        let mut inline = StringRefHeader::new();
348        inline.set_is_inline(true).set_id_or_len(15);
349        assert!(inline.is_inline());
350        assert_eq!(inline.id_or_len(), 15);
351        assert_eq!(inline.bits(), 0x800f);
352    }
353
354    #[test]
355    fn test_thread_ref_header() {
356        let mut interned = ThreadRefHeader::new();
357        interned.set_id_or_len(5);
358        assert_eq!(interned.id_or_len(), 5);
359        assert!(!interned.is_inline());
360        assert_eq!(interned.bits(), 5);
361
362        let mut inline = ThreadRefHeader::new();
363        inline.set_is_inline(true).set_id_or_len(3);
364        assert!(inline.is_inline());
365        assert_eq!(inline.id_or_len(), 3);
366        assert_eq!(inline.bits(), 0x83);
367    }
368
369    #[test]
370    fn test_argument_header() {
371        let mut arg = ArgumentHeader::for_argument(10, 2, ArgumentType::Uint64);
372        assert_eq!(arg.arg_type(), ArgumentType::Uint64);
373        assert_eq!(arg.size_words(), 2);
374        assert_eq!(arg.name_ref(), 10);
375        assert_eq!(arg.value_bits(), 0);
376
377        arg.set_value_bits(0x12345678);
378        assert_eq!(arg.value_bits(), 0x12345678);
379        let raw = arg.bits();
380        assert_eq!(raw & 0xf, 4);
381        assert_eq!((raw >> 4) & 0xfff, 2);
382        assert_eq!((raw >> 16) & 0xffff, 10);
383        assert_eq!((raw >> 32) & 0xffffffff, 0x12345678);
384    }
385
386    #[test]
387    fn test_kernel_object_record_header() {
388        let mut ko = KernelObjectRecordHeader::default();
389        ko.set_record_size(6).set_obj_type(1).set_name_ref(0x8009).set_arg_count(1);
390        assert_eq!(ko.record_type(), RecordType::KernelObject);
391        assert_eq!(ko.record_size(), 6);
392        assert_eq!(ko.obj_type(), 1);
393        assert_eq!(ko.name_ref(), 0x8009);
394        assert_eq!(ko.arg_count(), 1);
395        let raw = ko.bits();
396        assert_eq!(raw & 0xf, 7);
397        assert_eq!((raw >> 4) & 0xfff, 6);
398        assert_eq!((raw >> 16) & 0xff, 1);
399        assert_eq!((raw >> 24) & 0xffff, 0x8009);
400        assert_eq!((raw >> 40) & 0xf, 1);
401    }
402
403    #[test]
404    fn test_event_record_header() {
405        let mut ev = EventRecordHeader::default();
406        ev.set_record_size(5)
407            .set_event_type(EventType::Instant)
408            .set_arg_count(1)
409            .set_thread_ref(0)
410            .set_category_ref(10)
411            .set_name_ref(20);
412        assert_eq!(ev.record_type(), RecordType::Event);
413        assert_eq!(ev.record_size(), 5);
414        assert_eq!(ev.event_type(), EventType::Instant);
415        assert_eq!(ev.arg_count(), 1);
416        assert_eq!(ev.thread_ref(), 0);
417        assert_eq!(ev.category_ref(), 10);
418        assert_eq!(ev.name_ref(), 20);
419        let raw = ev.bits();
420        assert_eq!(raw & 0xf, 4);
421        assert_eq!((raw >> 4) & 0xfff, 5);
422        assert_eq!((raw >> 16) & 0xf, 0);
423        assert_eq!((raw >> 20) & 0xf, 1);
424        assert_eq!((raw >> 24) & 0xff, 0);
425        assert_eq!((raw >> 32) & 0xffff, 10);
426        assert_eq!((raw >> 48) & 0xffff, 20);
427    }
428}