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, 36;
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_name_inline() {
262 let name = "foo_blob";
263 let payload = &[
264 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
265 25,
266 ][..];
267
268 let mut header = BlobHeader::empty();
269 header.set_name_ref(name.len() as u16 | crate::string::STRING_REF_INLINE_BIT);
270 header.set_payload_len(payload.len() as u16);
271 header.set_blob_format_type(BLOB_TYPE_PERFETTO);
272
273 assert_parses_to_record!(
274 FxtBuilder::new(header).atom(name).atom(payload).build(),
275 RawTraceRecord::Blob(RawBlobRecord {
276 name: StringRef::Inline(name),
277 ty: BlobType::Perfetto,
278 bytes: payload,
279 }),
280 );
281 }
282
283 #[test]
284 fn large_blob_no_metadata() {
285 let payload = &[
286 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
287 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
288 ][..];
289
290 let mut header = LargeBlobHeader::empty();
291 header.set_blob_format_type(BLOB_TYPE_DATA);
292 header.set_large_record_type(LARGE_BLOB_NO_METADATA_TYPE);
293
294 let mut format_header = LargeBlobFormatHeader(0);
295 format_header.set_category_ref(7);
296 format_header.set_name_ref(8);
297
298 assert_parses_to_record!(
299 FxtBuilder::new(header)
300 .atom(format_header.0.to_le_bytes())
301 .atom(payload.len().to_le_bytes())
302 .atom(payload)
303 .build(),
304 RawTraceRecord::LargeBlob(RawLargeBlobRecord {
305 ty: BlobType::Data,
306 category: StringRef::Index(NonZeroU16::new(7).unwrap()),
307 name: StringRef::Index(NonZeroU16::new(8).unwrap()),
308 payload: RawLargeBlobPayload::BytesOnly(payload),
309 })
310 );
311 }
312
313 #[test]
314 fn large_blob_with_metadata() {
315 let payload = &[
316 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
317 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
318 ][..];
319
320 let mut header = LargeBlobHeader::empty();
321 header.set_blob_format_type(BLOB_TYPE_DATA);
322 header.set_large_record_type(LARGE_BLOB_WITH_METADATA_TYPE);
323
324 let mut format_header = LargeBlobFormatHeader(0);
325 format_header.set_category_ref(7);
326 format_header.set_name_ref(8);
327 format_header.set_thread_ref(31);
328 format_header.set_num_args(1);
329
330 let mut arg_header = crate::args::U32Header::empty();
331 arg_header.set_name_ref(10);
332 arg_header.set_value(100);
333
334 assert_parses_to_record!(
335 FxtBuilder::new(header)
336 .atom(format_header.0.to_le_bytes())
337 .atom(1024u64.to_le_bytes())
338 .atom(FxtBuilder::new(arg_header).build())
339 .atom(payload.len().to_le_bytes())
340 .atom(payload)
341 .build(),
342 RawTraceRecord::LargeBlob(RawLargeBlobRecord {
343 ty: BlobType::Data,
344 category: StringRef::Index(NonZeroU16::new(7).unwrap()),
345 name: StringRef::Index(NonZeroU16::new(8).unwrap()),
346 payload: RawLargeBlobPayload::BytesAndMetadata(
347 payload,
348 RawLargeBlobMetadata {
349 ticks: Ticks(1024),
350 process: ProcessRef::Index(NonZeroU8::new(31).unwrap()),
351 thread: ThreadRef::Index(NonZeroU8::new(31).unwrap()),
352 args: vec![RawArg {
353 name: StringRef::Index(NonZeroU16::new(10).unwrap()),
354 value: crate::args::RawArgValue::Unsigned32(100),
355 }],
356 },
357 )
358 })
359 );
360 }
361}