1use crate::args::{Arg, RawArg};
6use crate::error::ParseWarning;
7use crate::init::Ticks;
8use crate::session::ResolveCtx;
9use crate::string::StringRef;
10use crate::thread::{ProcessKoid, ProcessRef, ThreadKoid, ThreadRef};
11use crate::{
12 take_n_padded, trace_header, ParseResult, Provider, BLOB_RECORD_TYPE, LARGE_RECORD_TYPE,
13};
14use flyweights::FlyStr;
15use nom::combinator::all_consuming;
16use nom::number::complete::le_u64;
17use nom::Parser;
18
19const BLOB_TYPE_DATA: u8 = 0x01;
20const BLOB_TYPE_LAST_BRANCH: u8 = 0x02;
21const BLOB_TYPE_PERFETTO: u8 = 0x03;
22
23const LARGE_BLOB_WITH_METADATA_TYPE: u8 = 0;
24const LARGE_BLOB_NO_METADATA_TYPE: u8 = 1;
25
26#[derive(Clone, Debug, PartialEq)]
27pub struct BlobRecord {
28 pub provider: Option<Provider>,
29 pub name: FlyStr,
30 pub ty: BlobType,
31 pub bytes: Vec<u8>,
32}
33
34impl BlobRecord {
35 pub(super) fn resolve(ctx: &mut ResolveCtx, raw: RawBlobRecord<'_>) -> Self {
36 Self {
37 provider: ctx.current_provider(),
38 name: ctx.resolve_str(raw.name),
39 ty: raw.ty,
40 bytes: raw.bytes.to_owned(),
41 }
42 }
43}
44
45#[derive(Debug, PartialEq)]
46pub(super) struct RawBlobRecord<'a> {
47 name: StringRef<'a>,
48 ty: BlobType,
49 bytes: &'a [u8],
50}
51
52impl<'a> RawBlobRecord<'a> {
53 pub(super) fn parse(buf: &'a [u8]) -> ParseResult<'a, Self> {
54 let (buf, header) = BlobHeader::parse(buf)?;
55 let ty = BlobType::from(header.blob_format_type());
56 let (rem, payload) = header.take_payload(buf)?;
57 let (payload, name) = StringRef::parse(header.name_ref(), payload)?;
58
59 let (_should_be_empty, bytes) = take_n_padded(header.payload_len() as usize, payload)?;
63 Ok((rem, Self { name, ty, bytes }))
64 }
65}
66
67#[derive(Clone, Debug, PartialEq)]
68pub enum BlobType {
69 Data,
70 LastBranch,
71 Perfetto,
72 Unknown { raw: u8 },
73}
74
75impl From<u8> for BlobType {
76 fn from(raw: u8) -> Self {
77 match raw {
78 BLOB_TYPE_DATA => BlobType::Data,
79 BLOB_TYPE_LAST_BRANCH => BlobType::LastBranch,
80 BLOB_TYPE_PERFETTO => BlobType::Perfetto,
81 raw => BlobType::Unknown { raw },
82 }
83 }
84}
85
86trace_header! {
87 BlobHeader (BLOB_RECORD_TYPE) {
88 u16, name_ref: 16, 31;
89 u16, payload_len: 32, 46;
90 u8, blob_format_type: 48, 55;
91 }
92}
93
94#[derive(Clone, Debug, PartialEq)]
95pub struct LargeBlobRecord {
96 pub provider: Option<Provider>,
97 pub ty: BlobType,
98 pub category: FlyStr,
99 pub name: FlyStr,
100 pub bytes: Vec<u8>,
101 pub metadata: Option<LargeBlobMetadata>,
102}
103
104impl LargeBlobRecord {
105 pub(super) fn resolve(ctx: &mut ResolveCtx, raw: RawLargeBlobRecord<'_>) -> Option<Self> {
106 let (bytes, metadata) = match raw.payload {
107 RawLargeBlobPayload::BytesAndMetadata(bytes, metadata) => {
108 (bytes.to_owned(), Some(LargeBlobMetadata::resolve(ctx, metadata)))
109 }
110 RawLargeBlobPayload::BytesOnly(bytes) => (bytes.to_owned(), None),
111 RawLargeBlobPayload::UnknownLargeBlobType { raw_type, .. } => {
112 ctx.add_warning(ParseWarning::UnknownLargeBlobType(raw_type));
113 return None;
114 }
115 };
116 Some(Self {
117 provider: ctx.current_provider(),
118 ty: raw.ty,
119 category: ctx.resolve_str(raw.category),
120 name: ctx.resolve_str(raw.name),
121 bytes,
122 metadata,
123 })
124 }
125}
126
127#[derive(Debug, PartialEq)]
128pub(super) struct RawLargeBlobRecord<'a> {
129 ty: BlobType,
130 category: StringRef<'a>,
131 name: StringRef<'a>,
132 payload: RawLargeBlobPayload<'a>,
133}
134
135#[derive(Debug, PartialEq)]
136enum RawLargeBlobPayload<'a> {
137 BytesOnly(&'a [u8]),
138 BytesAndMetadata(&'a [u8], RawLargeBlobMetadata<'a>),
139 UnknownLargeBlobType { raw_type: u8, remaining_bytes: &'a [u8] },
140}
141
142impl<'a> RawLargeBlobRecord<'a> {
143 pub(super) fn parse(buf: &'a [u8]) -> ParseResult<'a, Self> {
144 let (buf, header) = LargeBlobHeader::parse(buf)?;
145 let ty = BlobType::from(header.blob_format_type());
146 let (rem, payload) = header.take_payload(buf)?;
147 let (payload, format_header) =
148 nom::combinator::map(le_u64, LargeBlobFormatHeader).parse(payload)?;
149 let (payload, category) = StringRef::parse(format_header.category_ref(), payload)?;
150 let (payload, name) = StringRef::parse(format_header.name_ref(), payload)?;
151 let (payload, metadata) = match header.large_record_type() {
152 LARGE_BLOB_WITH_METADATA_TYPE => {
153 let (payload, ticks) = Ticks::parse(payload)?;
154 let (payload, process) = ProcessRef::parse(format_header.thread_ref(), payload)?;
155 let (payload, thread) = ThreadRef::parse(format_header.thread_ref(), payload)?;
156 let (payload, args) = RawArg::parse_n(format_header.num_args(), payload)?;
157 (payload, Some(RawLargeBlobMetadata { ticks, process, thread, args }))
158 }
159 LARGE_BLOB_NO_METADATA_TYPE => (payload, None),
160 unknown => {
161 let payload = RawLargeBlobPayload::UnknownLargeBlobType {
164 raw_type: unknown,
165 remaining_bytes: payload,
166 };
167 return Ok((rem, Self { ty, category, name, payload }));
168 }
169 };
170 let (payload, blob_size) = le_u64(payload)?;
171
172 let (empty, bytes) =
173 all_consuming(|p| take_n_padded(blob_size as usize, p)).parse(payload)?;
174 assert_eq!(empty, [] as [u8; 0], "all_consuming must not return any trailing bytes");
175
176 let payload = if let Some(metadata) = metadata {
177 RawLargeBlobPayload::BytesAndMetadata(bytes, metadata)
178 } else {
179 RawLargeBlobPayload::BytesOnly(bytes)
180 };
181
182 Ok((rem, Self { ty, category, name, payload }))
183 }
184}
185
186#[derive(Clone, Debug, PartialEq)]
187pub struct LargeBlobMetadata {
188 pub timestamp: i64,
189 pub process: ProcessKoid,
190 pub thread: ThreadKoid,
191 pub args: Vec<Arg>,
192}
193
194impl LargeBlobMetadata {
195 fn resolve(ctx: &mut ResolveCtx, raw: RawLargeBlobMetadata<'_>) -> Self {
196 Self {
197 timestamp: ctx.resolve_ticks(raw.ticks),
198 process: ctx.resolve_process(raw.process),
199 thread: ctx.resolve_thread(raw.thread),
200 args: Arg::resolve_n(ctx, raw.args),
201 }
202 }
203}
204
205#[derive(Debug, PartialEq)]
206pub(super) struct RawLargeBlobMetadata<'a> {
207 ticks: Ticks,
208 process: ProcessRef,
209 thread: ThreadRef,
210 args: Vec<RawArg<'a>>,
211}
212
213trace_header! {
214 LargeBlobHeader (max_size_bit: 35) (u32) (LARGE_RECORD_TYPE) {
215 u8, large_record_type: 36, 39;
216 u8, blob_format_type: 40, 43;
217 }
218}
219
220bitfield::bitfield! {
221 struct LargeBlobFormatHeader(u64);
222 impl Debug;
223
224 u16, category_ref, set_category_ref: 15, 0;
225 u16, name_ref, set_name_ref: 31, 16;
226
227 u8, num_args, set_num_args: 35, 32;
229 u8, thread_ref, set_thread_ref: 43, 36;
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235 use crate::fxt_builder::FxtBuilder;
236 use crate::RawTraceRecord;
237 use std::num::{NonZeroU16, NonZeroU8};
238
239 #[test]
240 fn blob_name_index() {
241 let payload = &[
242 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
243 ][..];
244
245 let mut header = BlobHeader::empty();
246 header.set_name_ref(15);
247 header.set_payload_len(payload.len() as u16);
248 header.set_blob_format_type(BLOB_TYPE_DATA);
249
250 assert_parses_to_record!(
251 FxtBuilder::new(header).atom(payload).build(),
252 RawTraceRecord::Blob(RawBlobRecord {
253 name: StringRef::Index(NonZeroU16::new(15).unwrap()),
254 ty: BlobType::Data,
255 bytes: &payload,
256 }),
257 );
258 }
259
260 #[test]
261 fn blob_long_blob() {
262 let payload = &[0xEFu8; 1024][..];
263
264 let mut header = BlobHeader::empty();
265 header.set_name_ref(15);
266 header.set_payload_len(payload.len() as u16);
267 header.set_blob_format_type(BLOB_TYPE_DATA);
268
269 assert_parses_to_record!(
270 FxtBuilder::new(header).atom(payload).build(),
271 RawTraceRecord::Blob(RawBlobRecord {
272 name: StringRef::Index(NonZeroU16::new(15).unwrap()),
273 ty: BlobType::Data,
274 bytes: &payload,
275 }),
276 );
277 }
278
279 #[test]
280 fn blob_name_inline() {
281 let name = "foo_blob";
282 let payload = &[
283 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
284 25,
285 ][..];
286
287 let mut header = BlobHeader::empty();
288 header.set_name_ref(name.len() as u16 | crate::string::STRING_REF_INLINE_BIT);
289 header.set_payload_len(payload.len() as u16);
290 header.set_blob_format_type(BLOB_TYPE_PERFETTO);
291
292 assert_parses_to_record!(
293 FxtBuilder::new(header).atom(name).atom(payload).build(),
294 RawTraceRecord::Blob(RawBlobRecord {
295 name: StringRef::Inline(name),
296 ty: BlobType::Perfetto,
297 bytes: payload,
298 }),
299 );
300 }
301
302 #[test]
303 fn large_blob_no_metadata() {
304 let payload = &[
305 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
306 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
307 ][..];
308
309 let mut header = LargeBlobHeader::empty();
310 header.set_blob_format_type(BLOB_TYPE_DATA);
311 header.set_large_record_type(LARGE_BLOB_NO_METADATA_TYPE);
312
313 let mut format_header = LargeBlobFormatHeader(0);
314 format_header.set_category_ref(7);
315 format_header.set_name_ref(8);
316
317 assert_parses_to_record!(
318 FxtBuilder::new(header)
319 .atom(format_header.0.to_le_bytes())
320 .atom(payload.len().to_le_bytes())
321 .atom(payload)
322 .build(),
323 RawTraceRecord::LargeBlob(RawLargeBlobRecord {
324 ty: BlobType::Data,
325 category: StringRef::Index(NonZeroU16::new(7).unwrap()),
326 name: StringRef::Index(NonZeroU16::new(8).unwrap()),
327 payload: RawLargeBlobPayload::BytesOnly(payload),
328 })
329 );
330 }
331
332 #[test]
333 fn large_blob_with_metadata() {
334 let payload = &[
335 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
336 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
337 ][..];
338
339 let mut header = LargeBlobHeader::empty();
340 header.set_blob_format_type(BLOB_TYPE_DATA);
341 header.set_large_record_type(LARGE_BLOB_WITH_METADATA_TYPE);
342
343 let mut format_header = LargeBlobFormatHeader(0);
344 format_header.set_category_ref(7);
345 format_header.set_name_ref(8);
346 format_header.set_thread_ref(31);
347 format_header.set_num_args(1);
348
349 let mut arg_header = crate::args::U32Header::empty();
350 arg_header.set_name_ref(10);
351 arg_header.set_value(100);
352
353 assert_parses_to_record!(
354 FxtBuilder::new(header)
355 .atom(format_header.0.to_le_bytes())
356 .atom(1024u64.to_le_bytes())
357 .atom(FxtBuilder::new(arg_header).build())
358 .atom(payload.len().to_le_bytes())
359 .atom(payload)
360 .build(),
361 RawTraceRecord::LargeBlob(RawLargeBlobRecord {
362 ty: BlobType::Data,
363 category: StringRef::Index(NonZeroU16::new(7).unwrap()),
364 name: StringRef::Index(NonZeroU16::new(8).unwrap()),
365 payload: RawLargeBlobPayload::BytesAndMetadata(
366 payload,
367 RawLargeBlobMetadata {
368 ticks: Ticks(1024),
369 process: ProcessRef::Index(NonZeroU8::new(31).unwrap()),
370 thread: ThreadRef::Index(NonZeroU8::new(31).unwrap()),
371 args: vec![RawArg {
372 name: StringRef::Index(NonZeroU16::new(10).unwrap()),
373 value: crate::args::RawArgValue::Unsigned32(100),
374 }],
375 },
376 )
377 })
378 );
379 }
380}