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;
7use crate::init::Ticks;
8use crate::session::ResolveCtx;
9use crate::string::{StringRef, STRING_REF_INLINE_BIT};
10use crate::thread::{ProcessKoid, ProcessRef, ThreadKoid, ThreadRef};
11use crate::{trace_header, ParseResult, Provider, EVENT_RECORD_TYPE};
12use flyweights::FlyStr;
13use nom::number::complete::le_u64;
14use nom::Parser;
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    event_type: u8,
90    ticks: Ticks,
91    process: ProcessRef,
92    thread: ThreadRef,
93    category: StringRef<'a>,
94    pub name: StringRef<'a>,
95    pub args: Vec<RawArg<'a>>,
96    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) => id.into(),
127            StringRef::Inline(category_stream) => {
128                category_stream.len() as u16 | STRING_REF_INLINE_BIT
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) => id.into(),
136            StringRef::Inline(name_stream) => name_stream.len() as u16 | STRING_REF_INLINE_BIT,
137            StringRef::Empty => 0u16,
138        };
139        header.set_name_ref(name_ref);
140        header
141    }
142
143    pub fn serialize(&self) -> Result<Vec<u8>, String> {
144        let mut event_record = FxtBuilder::new(self.make_header());
145
146        event_record = event_record.atom(self.ticks.0.to_le_bytes());
147
148        if let ProcessRef::Inline(process_koid) = self.process {
149            event_record = event_record.atom(process_koid.0.to_le_bytes());
150        }
151
152        if let ThreadRef::Inline(thread_koid) = self.thread {
153            event_record = event_record.atom(thread_koid.0.to_le_bytes());
154        }
155
156        if let StringRef::Inline(category_stream) = self.category {
157            event_record = event_record.atom(category_stream);
158        }
159
160        if let StringRef::Inline(name_stream) = self.name {
161            event_record = event_record.atom(name_stream);
162        }
163
164        for arg in &self.args {
165            event_record = event_record.atom(arg.serialize()?);
166        }
167
168        match &self.payload {
169            EventPayload::Instant | EventPayload::DurationBegin | EventPayload::DurationEnd => {}
170
171            EventPayload::Counter { id }
172            | EventPayload::AsyncBegin { id }
173            | EventPayload::AsyncInstant { id }
174            | EventPayload::AsyncEnd { id }
175            | EventPayload::FlowBegin { id }
176            | EventPayload::FlowStep { id }
177            | EventPayload::FlowEnd { id } => {
178                event_record = event_record.atom(id.to_le_bytes());
179            }
180
181            EventPayload::DurationComplete { end_timestamp } => {
182                event_record = event_record.atom(end_timestamp.0.to_le_bytes());
183            }
184
185            EventPayload::Unknown { raw_type: _, bytes } => {
186                event_record = event_record.atom(bytes);
187            }
188        }
189        Ok(event_record.build())
190    }
191
192    pub fn set_flow_step_payload(&mut self, id: u64) {
193        self.event_type = FLOW_STEP_EVENT_TYPE;
194        self.payload = EventPayload::FlowStep { id };
195    }
196}
197
198trace_header! {
199    EventHeader (EVENT_RECORD_TYPE) {
200        u8, event_type: 16, 19;
201        u8, num_args: 20, 23;
202        u8, thread_ref: 24, 31;
203        u16, category_ref: 32, 47;
204        u16, name_ref: 48, 63;
205    }
206}
207
208#[derive(Clone, Debug, PartialEq)]
209pub enum EventPayload<Time> {
210    Instant,
211    Counter { id: u64 },
212    DurationBegin,
213    DurationEnd,
214    DurationComplete { end_timestamp: Time },
215    AsyncBegin { id: u64 },
216    AsyncInstant { id: u64 },
217    AsyncEnd { id: u64 },
218    FlowBegin { id: u64 },
219    FlowStep { id: u64 },
220    FlowEnd { id: u64 },
221    Unknown { raw_type: u8, bytes: Vec<u8> },
222}
223
224impl EventPayload<Ticks> {
225    pub(crate) fn resolve(self, ctx: &ResolveCtx) -> EventPayload<i64> {
226        match self {
227            EventPayload::Instant => EventPayload::Instant,
228            EventPayload::Counter { id } => EventPayload::Counter { id },
229            EventPayload::DurationBegin => EventPayload::DurationBegin,
230            EventPayload::DurationEnd => EventPayload::DurationEnd,
231            EventPayload::DurationComplete { end_timestamp } => {
232                EventPayload::DurationComplete { end_timestamp: ctx.resolve_ticks(end_timestamp) }
233            }
234            EventPayload::AsyncBegin { id } => EventPayload::AsyncBegin { id },
235            EventPayload::AsyncInstant { id } => EventPayload::AsyncInstant { id },
236            EventPayload::AsyncEnd { id } => EventPayload::AsyncEnd { id },
237            EventPayload::FlowBegin { id } => EventPayload::FlowBegin { id },
238            EventPayload::FlowStep { id } => EventPayload::FlowStep { id },
239            EventPayload::FlowEnd { id } => EventPayload::FlowEnd { id },
240            EventPayload::Unknown { raw_type, bytes } => EventPayload::Unknown { raw_type, bytes },
241        }
242    }
243}
244
245impl EventPayload<Ticks> {
246    fn parse(event_type: u8, buf: &[u8]) -> ParseResult<'_, Self> {
247        use nom::combinator::map;
248        match event_type {
249            INSTANT_EVENT_TYPE => Ok((buf, EventPayload::Instant)),
250            COUNTER_EVENT_TYPE => map(le_u64, |id| EventPayload::Counter { id }).parse(buf),
251            DURATION_BEGIN_EVENT_TYPE => Ok((buf, EventPayload::DurationBegin)),
252            DURATION_END_EVENT_TYPE => Ok((buf, EventPayload::DurationEnd)),
253            DURATION_COMPLETE_EVENT_TYPE => {
254                map(Ticks::parse, |end_timestamp| EventPayload::DurationComplete { end_timestamp })
255                    .parse(buf)
256            }
257            ASYNC_BEGIN_EVENT_TYPE => map(le_u64, |id| EventPayload::AsyncBegin { id }).parse(buf),
258            ASYNC_INSTANT_EVENT_TYPE => {
259                map(le_u64, |id| EventPayload::AsyncInstant { id }).parse(buf)
260            }
261            ASYNC_END_EVENT_TYPE => map(le_u64, |id| EventPayload::AsyncEnd { id }).parse(buf),
262            FLOW_BEGIN_EVENT_TYPE => map(le_u64, |id| EventPayload::FlowBegin { id }).parse(buf),
263            FLOW_STEP_EVENT_TYPE => map(le_u64, |id| EventPayload::FlowStep { id }).parse(buf),
264            FLOW_END_EVENT_TYPE => map(le_u64, |id| EventPayload::FlowEnd { id }).parse(buf),
265            unknown => {
266                Ok((&[][..], EventPayload::Unknown { raw_type: unknown, bytes: buf.to_vec() }))
267            }
268        }
269    }
270}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275    use crate::RawTraceRecord;
276    use std::num::{NonZeroU16, NonZeroU8};
277
278    #[test]
279    fn event_no_args() {
280        let mut header = EventHeader::empty();
281        header.set_thread_ref(11);
282        header.set_category_ref(27);
283        header.set_name_ref(93);
284        header.set_num_args(0);
285        header.set_event_type(INSTANT_EVENT_TYPE);
286
287        let event_record_bytes = FxtBuilder::new(header).atom(2048u64.to_le_bytes()).build();
288        let raw_event_record = RawEventRecord {
289            event_type: INSTANT_EVENT_TYPE,
290            ticks: Ticks(2048),
291            process: ProcessRef::Index(NonZeroU8::new(11).unwrap()),
292            thread: ThreadRef::Index(NonZeroU8::new(11).unwrap()),
293            category: StringRef::Index(NonZeroU16::new(27).unwrap()),
294            name: StringRef::Index(NonZeroU16::new(93).unwrap()),
295            args: vec![],
296            payload: EventPayload::Instant,
297        };
298
299        assert_eq!(raw_event_record.serialize().unwrap(), event_record_bytes);
300        assert_parses_to_record!(event_record_bytes, RawTraceRecord::Event(raw_event_record));
301    }
302
303    #[test]
304    fn event_with_args() {
305        let mut header = EventHeader::empty();
306        header.set_event_type(DURATION_COMPLETE_EVENT_TYPE);
307        header.set_category_ref("event_category".len() as u16 | STRING_REF_INLINE_BIT);
308        header.set_name_ref("event_name".len() as u16 | STRING_REF_INLINE_BIT);
309        header.set_num_args(2);
310
311        let first_arg_name = "arg1";
312        let first_arg_value = "val1";
313        let mut first_arg_header = crate::args::StringHeader::empty();
314        first_arg_header.set_name_ref(first_arg_name.len() as u16 | STRING_REF_INLINE_BIT);
315        first_arg_header.set_value_ref(first_arg_value.len() as u16 | STRING_REF_INLINE_BIT);
316
317        let second_arg_name = "arg2";
318        let mut second_arg_header = crate::args::BaseArgHeader::empty();
319        second_arg_header.set_raw_type(crate::args::PTR_ARG_TYPE);
320        second_arg_header.set_name_ref(second_arg_name.len() as u16 | STRING_REF_INLINE_BIT);
321
322        let event_record_bytes = FxtBuilder::new(header)
323            // begin ticks
324            .atom(2048u64.to_le_bytes())
325            // process
326            .atom(345u64.to_le_bytes())
327            // thread
328            .atom(678u64.to_le_bytes())
329            // category
330            .atom("event_category")
331            // name
332            .atom("event_name")
333            // first arg
334            .atom(
335                FxtBuilder::new(first_arg_header)
336                    .atom(first_arg_name)
337                    .atom(first_arg_value)
338                    .build(),
339            )
340            // second arg
341            .atom(
342                FxtBuilder::new(second_arg_header)
343                    .atom(second_arg_name)
344                    .atom(123456u64.to_le_bytes())
345                    .build(),
346            )
347            // end ticks
348            .atom(4096u64.to_le_bytes())
349            .build();
350
351        let raw_event_record = RawEventRecord {
352            event_type: DURATION_COMPLETE_EVENT_TYPE,
353            ticks: Ticks(2048),
354            process: ProcessRef::Inline(ProcessKoid(345)),
355            thread: ThreadRef::Inline(ThreadKoid(678)),
356            category: StringRef::Inline("event_category"),
357            name: StringRef::Inline("event_name"),
358            args: vec![
359                RawArg {
360                    name: StringRef::Inline(first_arg_name),
361                    value: crate::args::RawArgValue::String(StringRef::Inline(first_arg_value)),
362                },
363                RawArg {
364                    name: StringRef::Inline(second_arg_name),
365                    value: crate::args::RawArgValue::Pointer(123456),
366                },
367            ],
368            payload: EventPayload::DurationComplete { end_timestamp: Ticks(4096) },
369        };
370
371        assert_eq!(raw_event_record.serialize().unwrap(), event_record_bytes);
372        assert_parses_to_record!(event_record_bytes, RawTraceRecord::Event(raw_event_record));
373    }
374
375    #[test]
376    fn symbolize_event() {
377        let ordinal: u64 = 123456;
378        let method_name = "fidl_method";
379        let raw_event_record = RawEventRecord {
380            event_type: INSTANT_EVENT_TYPE,
381            ticks: Ticks(2048),
382            process: ProcessRef::Inline(ProcessKoid(345)),
383            thread: ThreadRef::Inline(ThreadKoid(678)),
384            category: StringRef::Inline("event_category"),
385            name: StringRef::Inline("event_name"),
386            args: vec![
387                RawArg {
388                    name: StringRef::Inline("arg1"),
389                    value: crate::args::RawArgValue::Pointer(123456),
390                },
391                RawArg {
392                    name: StringRef::Index(NonZeroU16::new(8).unwrap()),
393                    value: crate::args::RawArgValue::Unsigned64(ordinal),
394                },
395            ],
396            payload: EventPayload::DurationComplete { end_timestamp: Ticks(4096) },
397        };
398
399        assert_eq!(
400            symbolize(ordinal, method_name, &raw_event_record),
401            RawEventRecord {
402                event_type: INSTANT_EVENT_TYPE,
403                ticks: Ticks(2048),
404                process: ProcessRef::Inline(ProcessKoid(345)),
405                thread: ThreadRef::Inline(ThreadKoid(678)),
406                category: StringRef::Inline("event_category"),
407                name: StringRef::Inline("event_name"),
408                args: vec![
409                    RawArg {
410                        name: StringRef::Inline("arg1"),
411                        value: crate::args::RawArgValue::Pointer(123456),
412                    },
413                    RawArg {
414                        name: StringRef::Inline("method"),
415                        value: crate::args::RawArgValue::String(StringRef::Inline(method_name)),
416                    }
417                ],
418                payload: EventPayload::DurationComplete { end_timestamp: Ticks(4096) },
419            }
420        );
421    }
422}