Skip to main content

fxt/
lib.rs

1// Copyright 2023 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#[cfg(test)]
6#[macro_use]
7mod testing;
8
9pub mod args;
10pub mod bitfields {
11    pub use fxt_layout::*;
12}
13pub mod blob;
14pub mod error;
15pub mod event;
16pub mod fxt_builder;
17pub mod header;
18pub mod init;
19pub mod log;
20pub mod metadata;
21pub mod objects;
22pub mod profiler;
23pub mod scheduling;
24pub mod session;
25pub mod string;
26pub mod thread;
27
28pub use args::{Arg, ArgValue, RawArg, RawArgValue};
29pub use blob::{BlobRecord, BlobType, LargeBlobMetadata, LargeBlobRecord};
30pub use error::{ParseError, ParseWarning};
31pub use event::{EventPayload, EventRecord, RawEventRecord, symbolize};
32pub use log::LogRecord;
33pub use metadata::{Provider, ProviderEvent};
34pub use objects::{KernelObjRecord, UserspaceObjRecord};
35pub use scheduling::{
36    ContextSwitchEvent, LegacyContextSwitchEvent, SchedulingRecord, ThreadState, ThreadWakeupEvent,
37};
38pub use session::{SessionParser, parse_full_session};
39pub use string::StringRef;
40pub use thread::{ProcessKoid, ThreadKoid};
41
42use crate::blob::{RawBlobRecord, RawLargeBlobRecord};
43use crate::error::ParseResult;
44use crate::init::InitRecord;
45use crate::log::RawLogRecord;
46use crate::metadata::{MetadataRecord, TraceInfoMetadataRecord};
47use crate::objects::{RawKernelObjRecord, RawUserspaceObjRecord};
48use crate::profiler::{ProfilerRecord, RawProfilerRecordType};
49use crate::scheduling::RawSchedulingRecord;
50use crate::session::ResolveCtx;
51use crate::string::StringRecord;
52use crate::thread::ThreadRecord;
53use nom::Parser;
54use std::num::NonZero;
55
56#[derive(Clone, Debug, PartialEq)]
57pub enum TraceRecord {
58    Event(EventRecord),
59    Blob(BlobRecord),
60    UserspaceObj(UserspaceObjRecord),
61    KernelObj(KernelObjRecord),
62    Scheduling(SchedulingRecord),
63    Log(LogRecord),
64    Profiler(ProfilerRecord),
65    LargeBlob(LargeBlobRecord),
66    ProviderEvent { provider: Provider, event: ProviderEvent },
67}
68
69impl TraceRecord {
70    pub fn process(&self) -> Option<ProcessKoid> {
71        match self {
72            Self::Event(EventRecord { process, .. })
73            | Self::Log(LogRecord { process, .. })
74            | Self::UserspaceObj(UserspaceObjRecord { process, .. }) => Some(*process),
75            Self::Scheduling(s) => s.process(),
76            Self::KernelObj(k) => k.process(),
77            Self::Profiler(..)
78            | Self::Blob(..)
79            | Self::LargeBlob(..)
80            | Self::ProviderEvent { .. } => None,
81        }
82    }
83
84    pub fn thread(&self) -> Option<ThreadKoid> {
85        match self {
86            Self::Event(EventRecord { thread, .. }) | Self::Log(LogRecord { thread, .. }) => {
87                Some(*thread)
88            }
89            Self::Scheduling(s) => Some(s.thread()),
90            Self::Blob(..)
91            | Self::Profiler(..)
92            | Self::KernelObj(..)
93            | Self::LargeBlob(..)
94            | Self::ProviderEvent { .. }
95            | Self::UserspaceObj(..) => None,
96        }
97    }
98
99    fn resolve(ctx: &mut ResolveCtx, raw: RawTraceRecord<'_>) -> Result<Option<Self>, ParseError> {
100        Ok(match raw {
101            // Callers who want to handle unknown record types should use the raw record types.
102            RawTraceRecord::Unknown { raw_type } => {
103                ctx.add_warning(ParseWarning::UnknownTraceRecordType(raw_type));
104                None
105            }
106            RawTraceRecord::Profiler(p) => ProfilerRecord::resolve(ctx, p).map(Self::Profiler),
107            RawTraceRecord::Metadata(m) => ctx.on_metadata_record(m)?,
108            RawTraceRecord::Init(i) => {
109                ctx.on_init_record(i);
110                None
111            }
112            RawTraceRecord::String(s) => {
113                ctx.on_string_record(s);
114                None
115            }
116            RawTraceRecord::Thread(t) => {
117                ctx.on_thread_record(t);
118                None
119            }
120            RawTraceRecord::Event(e) => Some(Self::Event(EventRecord::resolve(ctx, e))),
121            RawTraceRecord::Blob(b) => Some(Self::Blob(BlobRecord::resolve(ctx, b))),
122            RawTraceRecord::UserspaceObj(u) => {
123                Some(Self::UserspaceObj(UserspaceObjRecord::resolve(ctx, u)))
124            }
125            RawTraceRecord::KernelObj(k) => Some(Self::KernelObj(KernelObjRecord::resolve(ctx, k))),
126            RawTraceRecord::Scheduling(s) => {
127                SchedulingRecord::resolve(ctx, s).map(Self::Scheduling)
128            }
129            RawTraceRecord::Log(l) => Some(Self::Log(LogRecord::resolve(ctx, l))),
130            RawTraceRecord::LargeBlob(lb) => LargeBlobRecord::resolve(ctx, lb).map(Self::LargeBlob),
131        })
132    }
133}
134
135#[derive(Debug, PartialEq)]
136enum RawTraceRecord<'a> {
137    Metadata(MetadataRecord),
138    Profiler(RawProfilerRecordType<'a>),
139    Init(InitRecord),
140    String(StringRecord<'a>),
141    Thread(ThreadRecord),
142    Event(RawEventRecord<'a>),
143    Blob(RawBlobRecord<'a>),
144    UserspaceObj(RawUserspaceObjRecord<'a>),
145    KernelObj(RawKernelObjRecord<'a>),
146    Scheduling(RawSchedulingRecord<'a>),
147    Log(RawLogRecord<'a>),
148    LargeBlob(RawLargeBlobRecord<'a>),
149    Unknown { raw_type: u8 },
150}
151
152trace_header! {
153    BaseTraceHeader {}
154}
155
156#[derive(Debug, PartialEq)]
157pub(crate) struct ParsedWithOriginalBytes<'a, T> {
158    pub parsed: T,
159    pub bytes: &'a [u8],
160}
161
162const METADATA_RECORD_TYPE: u8 = 0;
163const INIT_RECORD_TYPE: u8 = 1;
164const STRING_RECORD_TYPE: u8 = 2;
165const THREAD_RECORD_TYPE: u8 = 3;
166const EVENT_RECORD_TYPE: u8 = 4;
167const BLOB_RECORD_TYPE: u8 = 5;
168const USERSPACE_OBJ_RECORD_TYPE: u8 = 6;
169const KERNEL_OBJ_RECORD_TYPE: u8 = 7;
170const SCHEDULING_RECORD_TYPE: u8 = 8;
171const LOG_RECORD_TYPE: u8 = 9;
172const PROFILER_RECORD_TYPE: u8 = 10;
173const LARGE_RECORD_TYPE: u8 = 15;
174
175impl<'a> RawTraceRecord<'a> {
176    fn parse(buf: &'a [u8]) -> ParseResult<'a, ParsedWithOriginalBytes<'a, Self>> {
177        use nom::combinator::map;
178        let base_header = BaseTraceHeader::parse(buf)?.1;
179        let size_bytes = if base_header.raw_type() == LARGE_RECORD_TYPE {
180            crate::blob::LargeBlobHeader::parse(buf)?.1.size_words() as usize * 8
181        } else {
182            base_header.size_words() as usize * 8
183        };
184        if size_bytes == 0 {
185            return Err(nom::Err::Failure(ParseError::InvalidSize));
186        }
187        if size_bytes > buf.len() {
188            return Err(nom::Err::Incomplete(nom::Needed::Size(
189                NonZero::new(size_bytes - buf.len()).unwrap(),
190            )));
191        }
192
193        let (buf, rem) = buf.split_at(size_bytes);
194        let (_, parsed) = match base_header.raw_type() {
195            METADATA_RECORD_TYPE => map(MetadataRecord::parse, |m| Self::Metadata(m)).parse(buf),
196            PROFILER_RECORD_TYPE => {
197                map(RawProfilerRecordType::parse, |p| Self::Profiler(p)).parse(buf)
198            }
199            INIT_RECORD_TYPE => map(InitRecord::parse, |i| Self::Init(i)).parse(buf),
200            STRING_RECORD_TYPE => map(StringRecord::parse, |s| Self::String(s)).parse(buf),
201            THREAD_RECORD_TYPE => map(ThreadRecord::parse, |t| Self::Thread(t)).parse(buf),
202            EVENT_RECORD_TYPE => map(RawEventRecord::parse, |e| Self::Event(e)).parse(buf),
203            BLOB_RECORD_TYPE => map(RawBlobRecord::parse, |b| Self::Blob(b)).parse(buf),
204            USERSPACE_OBJ_RECORD_TYPE => {
205                map(RawUserspaceObjRecord::parse, |u| Self::UserspaceObj(u)).parse(buf)
206            }
207            KERNEL_OBJ_RECORD_TYPE => {
208                map(RawKernelObjRecord::parse, |k| Self::KernelObj(k)).parse(buf)
209            }
210            SCHEDULING_RECORD_TYPE => {
211                map(RawSchedulingRecord::parse, |s| Self::Scheduling(s)).parse(buf)
212            }
213            LOG_RECORD_TYPE => map(RawLogRecord::parse, |l| Self::Log(l)).parse(buf),
214            LARGE_RECORD_TYPE => map(RawLargeBlobRecord::parse, |l| Self::LargeBlob(l)).parse(buf),
215            raw_type => Ok((&[][..], Self::Unknown { raw_type })),
216        }?;
217
218        Ok((rem, ParsedWithOriginalBytes { parsed, bytes: buf }))
219    }
220
221    fn is_magic_number(&self) -> bool {
222        matches!(
223            self,
224            Self::Metadata(MetadataRecord::TraceInfo(TraceInfoMetadataRecord::MagicNumber)),
225        )
226    }
227}
228
229/// Take the first `unpadded_len` bytes from a buffer, returning a suffix beginning at the next
230/// world-aligned region and discarding padding bytes.
231fn take_n_padded<'a>(unpadded_len: usize, buf: &'a [u8]) -> ParseResult<'a, &'a [u8]> {
232    let padded_len = unpadded_len + word_padding(unpadded_len);
233    if padded_len > buf.len() {
234        return Err(nom::Err::Incomplete(nom::Needed::Size(
235            NonZero::new(padded_len - buf.len()).unwrap(),
236        )));
237    }
238    let (with_padding, rem) = buf.split_at(padded_len);
239    let (unpadded, _padding) = with_padding.split_at(unpadded_len);
240    Ok((rem, unpadded))
241}
242
243fn word_padding(unpadded_len: usize) -> usize {
244    match unpadded_len % 8 {
245        0 | 8 => 0,
246        nonzero => 8 - nonzero,
247    }
248}
249
250#[cfg(test)]
251mod tests {
252    use super::*;
253
254    #[test]
255    fn take_empty_bytes() {
256        let (trailing, parsed) = take_n_padded(0, &[1, 1, 1, 1]).unwrap();
257        assert_eq!(parsed, [] as [u8; 0]);
258        assert_eq!(trailing, [1, 1, 1, 1]);
259    }
260
261    #[test]
262    fn take_unpadded_bytes() {
263        let (trailing, parsed) = take_n_padded(8, &[5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1]).unwrap();
264        assert_eq!(parsed, [5, 5, 5, 5, 5, 5, 5, 5]);
265        assert_eq!(trailing, [1, 1, 1, 1]);
266    }
267
268    #[test]
269    fn take_padded_bytes() {
270        let (trailing, parsed) = take_n_padded(6, &[5, 5, 5, 5, 5, 5, 0, 0, 1, 1, 1, 1]).unwrap();
271        assert_eq!(parsed, [5, 5, 5, 5, 5, 5]);
272        assert_eq!(trailing, [1, 1, 1, 1],);
273    }
274}