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
311layout!({
312    /// Header word for an FXT Context Switch record (RecordType = 8, SchedulerEventType = 1).
313    pub struct ContextSwitchRecordHeader(u64);
314    {
315        let event_type @ 63..60: SchedulerEventType = SchedulerEventType::ContextSwitch;
316        let thread_state @ 39..36;
317        let cpu_number @ 35..20;
318        let arg_count @ 19..16;
319        let record_size @ 15..4;
320        let record_type @ 3..0: RecordType = RecordType::Scheduler;
321    }
322});
323
324layout!({
325    /// Header word for an FXT Thread Wakeup record (RecordType = 8, SchedulerEventType = 2).
326    pub struct ThreadWakeupRecordHeader(u64);
327    {
328        let event_type @ 63..60: SchedulerEventType = SchedulerEventType::ThreadWakeup;
329        let cpu_number @ 35..20;
330        let arg_count @ 19..16;
331        let record_size @ 15..4;
332        let record_type @ 3..0: RecordType = RecordType::Scheduler;
333    }
334});
335
336#[cfg(test)]
337mod tests {
338    use super::*;
339
340    #[test]
341    fn test_record_header() {
342        let mut rec = RecordHeader::new();
343        rec.set_record_size(12).set_record_type(RecordType::Event);
344        assert_eq!(rec.record_type(), RecordType::Event);
345        assert_eq!(rec.record_size(), 12);
346        let raw = rec.bits();
347        assert_eq!(raw & 0xf, 4);
348        assert_eq!((raw >> 4) & 0xfff, 12);
349    }
350
351    #[test]
352    fn test_large_record_header() {
353        let mut lrec = LargeRecordHeader::default();
354        lrec.set_record_size(0x1000).set_large_type(2);
355        assert_eq!(lrec.record_type(), RecordType::LargeBlob);
356        assert_eq!(lrec.record_size(), 0x1000);
357        assert_eq!(lrec.large_type(), 2);
358        let raw = lrec.bits();
359        assert_eq!(raw & 0xf, 15);
360        assert_eq!((raw >> 4) & 0xffffffff, 0x1000);
361        assert_eq!((raw >> 36) & 0xf, 2);
362    }
363
364    #[test]
365    fn test_string_ref_header() {
366        let mut interned = StringRefHeader::new();
367        interned.set_id_or_len(42);
368        assert_eq!(interned.id_or_len(), 42);
369        assert!(!interned.is_inline());
370        assert_eq!(interned.bits(), 42);
371
372        let mut inline = StringRefHeader::new();
373        inline.set_is_inline(true).set_id_or_len(15);
374        assert!(inline.is_inline());
375        assert_eq!(inline.id_or_len(), 15);
376        assert_eq!(inline.bits(), 0x800f);
377    }
378
379    #[test]
380    fn test_thread_ref_header() {
381        let mut interned = ThreadRefHeader::new();
382        interned.set_id_or_len(5);
383        assert_eq!(interned.id_or_len(), 5);
384        assert!(!interned.is_inline());
385        assert_eq!(interned.bits(), 5);
386
387        let mut inline = ThreadRefHeader::new();
388        inline.set_is_inline(true).set_id_or_len(3);
389        assert!(inline.is_inline());
390        assert_eq!(inline.id_or_len(), 3);
391        assert_eq!(inline.bits(), 0x83);
392    }
393
394    #[test]
395    fn test_argument_header() {
396        let mut arg = ArgumentHeader::for_argument(10, 2, ArgumentType::Uint64);
397        assert_eq!(arg.arg_type(), ArgumentType::Uint64);
398        assert_eq!(arg.size_words(), 2);
399        assert_eq!(arg.name_ref(), 10);
400        assert_eq!(arg.value_bits(), 0);
401
402        arg.set_value_bits(0x12345678);
403        assert_eq!(arg.value_bits(), 0x12345678);
404        let raw = arg.bits();
405        assert_eq!(raw & 0xf, 4);
406        assert_eq!((raw >> 4) & 0xfff, 2);
407        assert_eq!((raw >> 16) & 0xffff, 10);
408        assert_eq!((raw >> 32) & 0xffffffff, 0x12345678);
409    }
410
411    #[test]
412    fn test_kernel_object_record_header() {
413        let mut ko = KernelObjectRecordHeader::default();
414        ko.set_record_size(6).set_obj_type(1).set_name_ref(0x8009).set_arg_count(1);
415        assert_eq!(ko.record_type(), RecordType::KernelObject);
416        assert_eq!(ko.record_size(), 6);
417        assert_eq!(ko.obj_type(), 1);
418        assert_eq!(ko.name_ref(), 0x8009);
419        assert_eq!(ko.arg_count(), 1);
420        let raw = ko.bits();
421        assert_eq!(raw & 0xf, 7);
422        assert_eq!((raw >> 4) & 0xfff, 6);
423        assert_eq!((raw >> 16) & 0xff, 1);
424        assert_eq!((raw >> 24) & 0xffff, 0x8009);
425        assert_eq!((raw >> 40) & 0xf, 1);
426    }
427
428    #[test]
429    fn test_event_record_header() {
430        let mut ev = EventRecordHeader::default();
431        ev.set_record_size(5)
432            .set_event_type(EventType::Instant)
433            .set_arg_count(1)
434            .set_thread_ref(0)
435            .set_category_ref(10)
436            .set_name_ref(20);
437        assert_eq!(ev.record_type(), RecordType::Event);
438        assert_eq!(ev.record_size(), 5);
439        assert_eq!(ev.event_type(), EventType::Instant);
440        assert_eq!(ev.arg_count(), 1);
441        assert_eq!(ev.thread_ref(), 0);
442        assert_eq!(ev.category_ref(), 10);
443        assert_eq!(ev.name_ref(), 20);
444        let raw = ev.bits();
445        assert_eq!(raw & 0xf, 4);
446        assert_eq!((raw >> 4) & 0xfff, 5);
447        assert_eq!((raw >> 16) & 0xf, 0);
448        assert_eq!((raw >> 20) & 0xf, 1);
449        assert_eq!((raw >> 24) & 0xff, 0);
450        assert_eq!((raw >> 32) & 0xffff, 10);
451        assert_eq!((raw >> 48) & 0xffff, 20);
452    }
453
454    #[test]
455    fn test_context_switch_record_header() {
456        let mut cs = ContextSwitchRecordHeader::default();
457        cs.set_record_size(6).set_arg_count(2).set_cpu_number(0x1234).set_thread_state(3);
458        assert_eq!(cs.record_type(), RecordType::Scheduler);
459        assert_eq!(cs.record_size(), 6);
460        assert_eq!(cs.arg_count(), 2);
461        assert_eq!(cs.cpu_number(), 0x1234);
462        assert_eq!(cs.thread_state(), 3);
463        assert_eq!(cs.event_type(), SchedulerEventType::ContextSwitch);
464        let raw = cs.bits();
465        assert_eq!(raw & 0xf, 8);
466        assert_eq!((raw >> 4) & 0xfff, 6);
467        assert_eq!((raw >> 16) & 0xf, 2);
468        assert_eq!((raw >> 20) & 0xffff, 0x1234);
469        assert_eq!((raw >> 36) & 0xf, 3);
470        assert_eq!((raw >> 60) & 0xf, 1);
471    }
472
473    #[test]
474    fn test_thread_wakeup_record_header() {
475        let mut tw = ThreadWakeupRecordHeader::default();
476        tw.set_record_size(4).set_arg_count(1).set_cpu_number(0x5678);
477        assert_eq!(tw.record_type(), RecordType::Scheduler);
478        assert_eq!(tw.record_size(), 4);
479        assert_eq!(tw.arg_count(), 1);
480        assert_eq!(tw.cpu_number(), 0x5678);
481        assert_eq!(tw.event_type(), SchedulerEventType::ThreadWakeup);
482        let raw = tw.bits();
483        assert_eq!(raw & 0xf, 8);
484        assert_eq!((raw >> 4) & 0xfff, 4);
485        assert_eq!((raw >> 16) & 0xf, 1);
486        assert_eq!((raw >> 20) & 0xffff, 0x5678);
487        assert_eq!((raw >> 60) & 0xf, 2);
488    }
489}