Skip to main content

fxt/
event.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
5use crate::args::{Arg, RawArg, RawArgValue};
6use crate::fxt_builder::{FxtBuilder, SerializeError};
7use crate::init::Ticks;
8use crate::session::ResolveCtx;
9use crate::string::StringRef;
10use crate::thread::{ProcessKoid, ProcessRef, ThreadKoid, ThreadRef};
11use crate::{EVENT_RECORD_TYPE, ParseResult, Provider, trace_header};
12use flyweights::FlyStr;
13use nom::Parser;
14use nom::number::complete::le_u64;
15
16pub(crate) const INSTANT_EVENT_TYPE: u8 = 0;
17pub(crate) const COUNTER_EVENT_TYPE: u8 = 1;
18pub(crate) const DURATION_BEGIN_EVENT_TYPE: u8 = 2;
19pub(crate) const DURATION_END_EVENT_TYPE: u8 = 3;
20pub(crate) const DURATION_COMPLETE_EVENT_TYPE: u8 = 4;
21pub(crate) const ASYNC_BEGIN_EVENT_TYPE: u8 = 5;
22pub(crate) const ASYNC_INSTANT_EVENT_TYPE: u8 = 6;
23pub(crate) const ASYNC_END_EVENT_TYPE: u8 = 7;
24pub(crate) const FLOW_BEGIN_EVENT_TYPE: u8 = 8;
25pub(crate) const FLOW_STEP_EVENT_TYPE: u8 = 9;
26pub(crate) const FLOW_END_EVENT_TYPE: u8 = 10;
27
28pub fn symbolize<'a>(
29    ordinal: u64,
30    method: &'a str,
31    raw_record: &RawEventRecord<'a>,
32) -> RawEventRecord<'a> {
33    let mut new_args = vec![];
34    for arg in &raw_record.args {
35        if let &RawArgValue::Unsigned64(arg_value) = &arg.value {
36            if arg_value == ordinal {
37                let symbolized_arg = RawArg {
38                    name: StringRef::Inline("method"),
39                    value: RawArgValue::String(StringRef::Inline(method)),
40                };
41                new_args.push(symbolized_arg);
42                continue;
43            }
44        }
45        new_args.push(arg.clone());
46    }
47
48    RawEventRecord {
49        event_type: raw_record.event_type,
50        ticks: raw_record.ticks.clone(),
51        process: raw_record.process.clone(),
52        thread: raw_record.thread.clone(),
53        category: raw_record.category.clone(),
54        name: raw_record.name.clone(),
55        args: new_args,
56        payload: raw_record.payload.clone(),
57    }
58}
59
60#[derive(Clone, Debug, PartialEq)]
61pub struct EventRecord {
62    pub provider: Option<Provider>,
63    pub timestamp: i64,
64    pub process: ProcessKoid,
65    pub thread: ThreadKoid,
66    pub category: FlyStr,
67    pub name: FlyStr,
68    pub args: Vec<Arg>,
69    pub payload: EventPayload<i64>,
70}
71
72impl EventRecord {
73    pub(super) fn resolve(ctx: &mut ResolveCtx, raw: RawEventRecord<'_>) -> Self {
74        Self {
75            provider: ctx.current_provider(),
76            timestamp: ctx.resolve_ticks(raw.ticks),
77            process: ctx.resolve_process(raw.process),
78            thread: ctx.resolve_thread(raw.thread),
79            category: ctx.resolve_str(raw.category),
80            name: ctx.resolve_str(raw.name),
81            args: Arg::resolve_n(ctx, raw.args),
82            payload: raw.payload.resolve(ctx),
83        }
84    }
85}
86
87#[derive(Debug, PartialEq)]
88pub struct RawEventRecord<'a> {
89    pub(crate) event_type: u8,
90    pub(crate) ticks: Ticks,
91    pub(crate) process: ProcessRef,
92    pub(crate) thread: ThreadRef,
93    pub(crate) category: StringRef<'a>,
94    pub name: StringRef<'a>,
95    pub args: Vec<RawArg<'a>>,
96    pub(crate) payload: EventPayload<Ticks>,
97}
98
99impl<'a> RawEventRecord<'a> {
100    pub fn parse(buf: &'a [u8]) -> ParseResult<'a, Self> {
101        let (buf, header) = EventHeader::parse(buf)?;
102        let (rem, payload) = header.take_payload(buf)?;
103        let event_type = header.event_type();
104        let (payload, ticks) = Ticks::parse(payload)?;
105        let (payload, process) = ProcessRef::parse(header.thread_ref(), payload)?;
106        let (payload, thread) = ThreadRef::parse(header.thread_ref(), payload)?;
107        let (payload, category) = StringRef::parse(header.category_ref(), payload)?;
108        let (payload, name) = StringRef::parse(header.name_ref(), payload)?;
109        let (payload, args) = RawArg::parse_n(header.num_args(), payload)?;
110
111        // Some trace events attach an undocumented "scope" word on instant events for chrome trace
112        // viewer compatibility that we don't need to return, so don't use all_consuming here.
113        let (_empty, payload) = EventPayload::parse(event_type, payload)?;
114        Ok((rem, Self { event_type, ticks, process, thread, category, name, args, payload }))
115    }
116
117    pub fn make_header(&self) -> EventHeader {
118        let mut header = EventHeader::empty();
119        header.set_event_type(self.event_type);
120        header.set_num_args(self.args.len() as u8);
121
122        if let ProcessRef::Index(id) = self.process {
123            header.set_thread_ref(id.into());
124        }
125        let category_ref: u16 = match self.category {
126            StringRef::Index(id) => fxt_layout::StringRefHeader::indexed(id.into()).bits(),
127            StringRef::Inline(category_stream) => {
128                fxt_layout::StringRefHeader::inline(category_stream.len() as u16).bits()
129            }
130            StringRef::Empty => 0u16,
131        };
132        header.set_category_ref(category_ref);
133
134        let name_ref: u16 = match self.name {
135            StringRef::Index(id) => fxt_layout::StringRefHeader::indexed(id.into()).bits(),
136            StringRef::Inline(name_stream) => {
137                fxt_layout::StringRefHeader::inline(name_stream.len() as u16).bits()
138            }
139            StringRef::Empty => 0u16,
140        };
141        header.set_name_ref(name_ref);
142        header
143    }
144
145    pub fn serialize(&self) -> Result<Vec<u8>, SerializeError> {
146        let mut event_record = FxtBuilder::new(self.make_header());
147
148        event_record = event_record.atom(self.ticks.0.to_le_bytes());
149
150        if let ProcessRef::Inline(process_koid) = self.process {
151            event_record = event_record.atom(process_koid.0.to_le_bytes());
152        }
153
154        if let ThreadRef::Inline(thread_koid) = self.thread {
155            event_record = event_record.atom(thread_koid.0.to_le_bytes());
156        }
157
158        if let StringRef::Inline(category_stream) = self.category {
159            event_record = event_record.atom(category_stream);
160        }
161
162        if let StringRef::Inline(name_stream) = self.name {
163            event_record = event_record.atom(name_stream);
164        }
165
166        for arg in &self.args {
167            event_record = event_record.atom(arg.serialize()?);
168        }
169
170        match &self.payload {
171            EventPayload::Instant | EventPayload::DurationBegin | EventPayload::DurationEnd => {}
172
173            EventPayload::Counter { id }
174            | EventPayload::AsyncBegin { id }
175            | EventPayload::AsyncInstant { id }
176            | EventPayload::AsyncEnd { id }
177            | EventPayload::FlowBegin { id }
178            | EventPayload::FlowStep { id }
179            | EventPayload::FlowEnd { id } => {
180                event_record = event_record.atom(id.to_le_bytes());
181            }
182
183            EventPayload::DurationComplete { end_timestamp } => {
184                event_record = event_record.atom(end_timestamp.0.to_le_bytes());
185            }
186
187            EventPayload::Unknown { raw_type: _, bytes } => {
188                event_record = event_record.atom(bytes);
189            }
190        }
191        Ok(event_record.build())
192    }
193
194    pub fn set_flow_step_payload(&mut self, id: u64) {
195        self.event_type = FLOW_STEP_EVENT_TYPE;
196        self.payload = EventPayload::FlowStep { id };
197    }
198}
199
200trace_header! {
201    EventHeader (EVENT_RECORD_TYPE) {
202        u8, event_type: 16, 19;
203        u8, num_args: 20, 23;
204        u8, thread_ref: 24, 31;
205        u16, category_ref: 32, 47;
206        u16, name_ref: 48, 63;
207    }
208}
209
210#[derive(Clone, Debug, PartialEq)]
211pub enum EventPayload<Time> {
212    Instant,
213    Counter { id: u64 },
214    DurationBegin,
215    DurationEnd,
216    DurationComplete { end_timestamp: Time },
217    AsyncBegin { id: u64 },
218    AsyncInstant { id: u64 },
219    AsyncEnd { id: u64 },
220    FlowBegin { id: u64 },
221    FlowStep { id: u64 },
222    FlowEnd { id: u64 },
223    Unknown { raw_type: u8, bytes: Vec<u8> },
224}
225
226impl EventPayload<Ticks> {
227    pub(crate) fn resolve(self, ctx: &ResolveCtx) -> EventPayload<i64> {
228        match self {
229            EventPayload::Instant => EventPayload::Instant,
230            EventPayload::Counter { id } => EventPayload::Counter { id },
231            EventPayload::DurationBegin => EventPayload::DurationBegin,
232            EventPayload::DurationEnd => EventPayload::DurationEnd,
233            EventPayload::DurationComplete { end_timestamp } => {
234                EventPayload::DurationComplete { end_timestamp: ctx.resolve_ticks(end_timestamp) }
235            }
236            EventPayload::AsyncBegin { id } => EventPayload::AsyncBegin { id },
237            EventPayload::AsyncInstant { id } => EventPayload::AsyncInstant { id },
238            EventPayload::AsyncEnd { id } => EventPayload::AsyncEnd { id },
239            EventPayload::FlowBegin { id } => EventPayload::FlowBegin { id },
240            EventPayload::FlowStep { id } => EventPayload::FlowStep { id },
241            EventPayload::FlowEnd { id } => EventPayload::FlowEnd { id },
242            EventPayload::Unknown { raw_type, bytes } => EventPayload::Unknown { raw_type, bytes },
243        }
244    }
245}
246
247impl EventPayload<Ticks> {
248    fn parse(event_type: u8, buf: &[u8]) -> ParseResult<'_, Self> {
249        use nom::combinator::map;
250        match event_type {
251            INSTANT_EVENT_TYPE => Ok((buf, EventPayload::Instant)),
252            COUNTER_EVENT_TYPE => map(le_u64, |id| EventPayload::Counter { id }).parse(buf),
253            DURATION_BEGIN_EVENT_TYPE => Ok((buf, EventPayload::DurationBegin)),
254            DURATION_END_EVENT_TYPE => Ok((buf, EventPayload::DurationEnd)),
255            DURATION_COMPLETE_EVENT_TYPE => {
256                map(Ticks::parse, |end_timestamp| EventPayload::DurationComplete { end_timestamp })
257                    .parse(buf)
258            }
259            ASYNC_BEGIN_EVENT_TYPE => map(le_u64, |id| EventPayload::AsyncBegin { id }).parse(buf),
260            ASYNC_INSTANT_EVENT_TYPE => {
261                map(le_u64, |id| EventPayload::AsyncInstant { id }).parse(buf)
262            }
263            ASYNC_END_EVENT_TYPE => map(le_u64, |id| EventPayload::AsyncEnd { id }).parse(buf),
264            FLOW_BEGIN_EVENT_TYPE => map(le_u64, |id| EventPayload::FlowBegin { id }).parse(buf),
265            FLOW_STEP_EVENT_TYPE => map(le_u64, |id| EventPayload::FlowStep { id }).parse(buf),
266            FLOW_END_EVENT_TYPE => map(le_u64, |id| EventPayload::FlowEnd { id }).parse(buf),
267            unknown => {
268                Ok((&[][..], EventPayload::Unknown { raw_type: unknown, bytes: buf.to_vec() }))
269            }
270        }
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use super::*;
277    use crate::RawTraceRecord;
278    use std::num::{NonZeroU8, NonZeroU16};
279
280    #[test]
281    fn event_no_args() {
282        let mut header = EventHeader::empty();
283        header.set_thread_ref(11);
284        header.set_category_ref(27);
285        header.set_name_ref(93);
286        header.set_num_args(0);
287        header.set_event_type(INSTANT_EVENT_TYPE);
288
289        let event_record_bytes = FxtBuilder::new(header).atom(2048u64.to_le_bytes()).build();
290        let raw_event_record = RawEventRecord {
291            event_type: INSTANT_EVENT_TYPE,
292            ticks: Ticks(2048),
293            process: ProcessRef::Index(NonZeroU8::new(11).unwrap()),
294            thread: ThreadRef::Index(NonZeroU8::new(11).unwrap()),
295            category: StringRef::Index(NonZeroU16::new(27).unwrap()),
296            name: StringRef::Index(NonZeroU16::new(93).unwrap()),
297            args: vec![],
298            payload: EventPayload::Instant,
299        };
300
301        assert_eq!(raw_event_record.serialize().unwrap(), event_record_bytes);
302        assert_parses_to_record!(event_record_bytes, RawTraceRecord::Event(raw_event_record));
303    }
304
305    #[test]
306    fn event_with_args() {
307        let mut header = EventHeader::empty();
308        header.set_event_type(DURATION_COMPLETE_EVENT_TYPE);
309        header.set_category_ref(
310            fxt_layout::StringRefHeader::inline("event_category".len() as u16).bits(),
311        );
312        header.set_name_ref(fxt_layout::StringRefHeader::inline("event_name".len() as u16).bits());
313        header.set_num_args(2);
314
315        let first_arg_name = "arg1";
316        let first_arg_value = "val1";
317        let mut first_arg_header = crate::args::StringHeader::empty();
318        first_arg_header
319            .set_name_ref(fxt_layout::StringRefHeader::inline(first_arg_name.len() as u16).bits());
320        first_arg_header.set_value_ref(
321            fxt_layout::StringRefHeader::inline(first_arg_value.len() as u16).bits(),
322        );
323
324        let second_arg_name = "arg2";
325        let mut second_arg_header = crate::args::BaseArgHeader::empty();
326        second_arg_header.set_raw_type(crate::args::PTR_ARG_TYPE);
327        second_arg_header
328            .set_name_ref(fxt_layout::StringRefHeader::inline(second_arg_name.len() as u16).bits());
329
330        let event_record_bytes = FxtBuilder::new(header)
331            // begin ticks
332            .atom(2048u64.to_le_bytes())
333            // process
334            .atom(345u64.to_le_bytes())
335            // thread
336            .atom(678u64.to_le_bytes())
337            // category
338            .atom("event_category")
339            // name
340            .atom("event_name")
341            // first arg
342            .atom(
343                FxtBuilder::new(first_arg_header)
344                    .atom(first_arg_name)
345                    .atom(first_arg_value)
346                    .build(),
347            )
348            // second arg
349            .atom(
350                FxtBuilder::new(second_arg_header)
351                    .atom(second_arg_name)
352                    .atom(123456u64.to_le_bytes())
353                    .build(),
354            )
355            // end ticks
356            .atom(4096u64.to_le_bytes())
357            .build();
358
359        let raw_event_record = RawEventRecord {
360            event_type: DURATION_COMPLETE_EVENT_TYPE,
361            ticks: Ticks(2048),
362            process: ProcessRef::Inline(ProcessKoid(345)),
363            thread: ThreadRef::Inline(ThreadKoid(678)),
364            category: StringRef::Inline("event_category"),
365            name: StringRef::Inline("event_name"),
366            args: vec![
367                RawArg {
368                    name: StringRef::Inline(first_arg_name),
369                    value: crate::args::RawArgValue::String(StringRef::Inline(first_arg_value)),
370                },
371                RawArg {
372                    name: StringRef::Inline(second_arg_name),
373                    value: crate::args::RawArgValue::Pointer(123456),
374                },
375            ],
376            payload: EventPayload::DurationComplete { end_timestamp: Ticks(4096) },
377        };
378
379        assert_eq!(raw_event_record.serialize().unwrap(), event_record_bytes);
380        assert_parses_to_record!(event_record_bytes, RawTraceRecord::Event(raw_event_record));
381    }
382
383    #[test]
384    fn symbolize_event() {
385        let ordinal: u64 = 123456;
386        let method_name = "fidl_method";
387        let raw_event_record = RawEventRecord {
388            event_type: INSTANT_EVENT_TYPE,
389            ticks: Ticks(2048),
390            process: ProcessRef::Inline(ProcessKoid(345)),
391            thread: ThreadRef::Inline(ThreadKoid(678)),
392            category: StringRef::Inline("event_category"),
393            name: StringRef::Inline("event_name"),
394            args: vec![
395                RawArg {
396                    name: StringRef::Inline("arg1"),
397                    value: crate::args::RawArgValue::Pointer(123456),
398                },
399                RawArg {
400                    name: StringRef::Index(NonZeroU16::new(8).unwrap()),
401                    value: crate::args::RawArgValue::Unsigned64(ordinal),
402                },
403            ],
404            payload: EventPayload::DurationComplete { end_timestamp: Ticks(4096) },
405        };
406
407        assert_eq!(
408            symbolize(ordinal, method_name, &raw_event_record),
409            RawEventRecord {
410                event_type: INSTANT_EVENT_TYPE,
411                ticks: Ticks(2048),
412                process: ProcessRef::Inline(ProcessKoid(345)),
413                thread: ThreadRef::Inline(ThreadKoid(678)),
414                category: StringRef::Inline("event_category"),
415                name: StringRef::Inline("event_name"),
416                args: vec![
417                    RawArg {
418                        name: StringRef::Inline("arg1"),
419                        value: crate::args::RawArgValue::Pointer(123456),
420                    },
421                    RawArg {
422                        name: StringRef::Inline("method"),
423                        value: crate::args::RawArgValue::String(StringRef::Inline(method_name)),
424                    }
425                ],
426                payload: EventPayload::DurationComplete { end_timestamp: Ticks(4096) },
427            }
428        );
429    }
430}