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