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