Skip to main content

fxt/
metadata.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::string::parse_padded_string;
6use crate::{METADATA_RECORD_TYPE, ParseError, ParseResult, trace_header};
7use flyweights::FlyStr;
8use nom::Parser;
9use nom::combinator::all_consuming;
10
11const PROVIDER_INFO_METADATA_TYPE: u8 = 1;
12const PROVIDER_SECTION_METADATA_TYPE: u8 = 2;
13const PROVIDER_EVENT_METADATA_TYPE: u8 = 3;
14const TRACE_INFO_METADATA_TYPE: u8 = 4;
15
16#[derive(Clone, Debug, PartialEq)]
17pub struct Provider {
18    pub id: u32,
19    pub name: FlyStr,
20}
21
22#[derive(Debug, PartialEq)]
23pub(super) enum MetadataRecord {
24    ProviderInfo(ProviderInfoMetadataRecord),
25    ProviderSection(ProviderSectionMetadataRecord),
26    ProviderEvent(ProviderEventMetadataRecord),
27    TraceInfo(TraceInfoMetadataRecord),
28    Unknown { raw_type: u8 },
29}
30
31impl MetadataRecord {
32    pub(super) fn parse(buf: &[u8]) -> ParseResult<'_, Self> {
33        use nom::combinator::map;
34        match BaseMetadataHeader::parse(buf)?.1.metadata_type() {
35            PROVIDER_INFO_METADATA_TYPE => {
36                map(ProviderInfoMetadataRecord::parse, |i| Self::ProviderInfo(i)).parse(buf)
37            }
38            PROVIDER_SECTION_METADATA_TYPE => {
39                map(ProviderSectionMetadataRecord::parse, |s| Self::ProviderSection(s)).parse(buf)
40            }
41            PROVIDER_EVENT_METADATA_TYPE => {
42                map(ProviderEventMetadataRecord::parse, |e| Self::ProviderEvent(e)).parse(buf)
43            }
44            TRACE_INFO_METADATA_TYPE => {
45                map(TraceInfoMetadataRecord::parse, |t| Self::TraceInfo(t)).parse(buf)
46            }
47            unknown => Ok((buf, Self::Unknown { raw_type: unknown })),
48        }
49    }
50}
51
52macro_rules! metadata_header {
53    ($name:ident $(($metadata_ty:expr))? { $($record_specific:tt)* }) => {
54        trace_header! {
55            $name (METADATA_RECORD_TYPE) {
56                $($record_specific)*
57                u8, metadata_type: 16, 19;
58            } => |_h: &$name| {
59                $(if _h.metadata_type() != $metadata_ty {
60                    return Err(ParseError::WrongType {
61                        context: stringify!($name),
62                        expected: $metadata_ty,
63                        observed: _h.metadata_type(),
64                    });
65                })?
66                Ok(())
67            }
68        }
69    };
70}
71
72metadata_header! { BaseMetadataHeader {} }
73
74#[derive(Debug, PartialEq)]
75pub(super) struct ProviderInfoMetadataRecord {
76    pub provider_id: u32,
77    pub name: FlyStr,
78}
79
80metadata_header! {
81    ProviderInfoMetadataHeader (PROVIDER_INFO_METADATA_TYPE) {
82        u32, provider_id: 20, 51;
83        u8, name_len: 52, 59;
84    }
85}
86
87impl ProviderInfoMetadataRecord {
88    fn parse(buf: &[u8]) -> ParseResult<'_, Self> {
89        let (buf, header) = ProviderInfoMetadataHeader::parse(buf)?;
90        let (rem, payload) = header.take_payload(buf)?;
91        let (empty, name) =
92            all_consuming(|p| parse_padded_string(header.name_len() as usize, p)).parse(payload)?;
93        assert!(empty.is_empty(), "all_consuming must not return any remaining buffer");
94        Ok((rem, Self { provider_id: header.provider_id(), name: name.into() }))
95    }
96}
97
98#[derive(Debug, PartialEq)]
99pub(super) struct ProviderSectionMetadataRecord {
100    pub provider_id: u32,
101}
102
103metadata_header! {
104    ProviderSectionMetadataHeader (PROVIDER_SECTION_METADATA_TYPE) {
105        u32, provider_id: 20, 51;
106    }
107}
108
109impl ProviderSectionMetadataRecord {
110    fn parse(buf: &[u8]) -> ParseResult<'_, Self> {
111        let (buf, header) = ProviderSectionMetadataHeader::parse(buf)?;
112        let (rem, payload) = header.take_payload(buf)?;
113        if !payload.is_empty() {
114            return Err(nom::Err::Failure(ParseError::InvalidSize));
115        }
116        Ok((rem, Self { provider_id: header.provider_id() }))
117    }
118}
119
120const PROVIDER_EVENT_BUFFER_FULL: u8 = 0;
121
122#[derive(Debug, PartialEq)]
123pub(super) struct ProviderEventMetadataRecord {
124    pub provider_id: u32,
125    pub event: ProviderEvent,
126}
127
128metadata_header! {
129    ProviderEventMetadataHeader (PROVIDER_EVENT_METADATA_TYPE) {
130        u32, provider_id: 20, 51;
131        u8, event_id: 52, 55;
132    }
133}
134
135impl ProviderEventMetadataRecord {
136    fn parse(buf: &[u8]) -> ParseResult<'_, Self> {
137        let (buf, header) = ProviderEventMetadataHeader::parse(buf)?;
138        let provider_id = header.provider_id();
139        let event = match header.event_id() {
140            PROVIDER_EVENT_BUFFER_FULL => ProviderEvent::BufferFull,
141            unknown => ProviderEvent::Unknown { raw_type: unknown },
142        };
143        let (rem, payload) = header.take_payload(buf)?;
144        if !payload.is_empty() {
145            return Err(nom::Err::Failure(ParseError::InvalidSize));
146        }
147        Ok((rem, Self { provider_id, event }))
148    }
149}
150
151#[derive(Clone, Debug, PartialEq)]
152pub enum ProviderEvent {
153    BufferFull,
154    Unknown { raw_type: u8 },
155}
156
157const MAGIC_NUMBER_TRACE_INFO_TYPE: u8 = 0;
158const MAGIC_NUMBER: u32 = 0x16547846;
159
160/// The 8-byte FXT magic number record (`0x0016547846040010` in little-endian).
161pub const FXT_MAGIC_BYTES: [u8; 8] = [0x10, 0x00, 0x04, 0x46, 0x78, 0x54, 0x16, 0x00];
162
163/// Returns `true` if `bytes` begins with the 8-byte FXT magic number record.
164pub fn is_magic(bytes: &[u8]) -> bool {
165    bytes.starts_with(&FXT_MAGIC_BYTES)
166}
167
168#[derive(Debug, PartialEq)]
169pub(super) enum TraceInfoMetadataRecord {
170    MagicNumber,
171}
172
173impl TraceInfoMetadataRecord {
174    fn parse(buf: &[u8]) -> ParseResult<'_, Self> {
175        // The magic number record is the only trace info record currently specified, so we can
176        // parse it directly here without confusing it with other record types.
177        let (buf, header) = MagicNumberHeader::parse(buf)?;
178        if header.trace_info_type() != MAGIC_NUMBER_TRACE_INFO_TYPE {
179            return Err(nom::Err::Error(ParseError::WrongType {
180                context: "MagicNumber",
181                expected: MAGIC_NUMBER_TRACE_INFO_TYPE,
182                observed: header.trace_info_type(),
183            }));
184        }
185        if header.magic_number() != MAGIC_NUMBER {
186            return Err(nom::Err::Failure(ParseError::InvalidMagicNumber {
187                observed: header.magic_number(),
188            }));
189        }
190
191        let (rem, payload) = header.take_payload(buf)?;
192        if !payload.is_empty() {
193            return Err(nom::Err::Failure(ParseError::InvalidSize));
194        }
195
196        Ok((rem, Self::MagicNumber))
197    }
198}
199
200metadata_header! {
201    MagicNumberHeader (TRACE_INFO_METADATA_TYPE) {
202        u8, trace_info_type: 20, 23;
203        u32, magic_number: 24, 55;
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210    use crate::RawTraceRecord;
211    use crate::fxt_builder::FxtBuilder;
212
213    #[test]
214    fn basic_provider_info() {
215        let name = "hello";
216        let mut header = ProviderInfoMetadataHeader::empty();
217        header.set_metadata_type(PROVIDER_INFO_METADATA_TYPE);
218        header.set_provider_id(16);
219        header.set_name_len(name.len() as _);
220
221        assert_parses_to_record!(
222            FxtBuilder::new(header).atom(name).build(),
223            RawTraceRecord::Metadata(MetadataRecord::ProviderInfo(ProviderInfoMetadataRecord {
224                provider_id: 16,
225                name: "hello".into(),
226            })),
227        );
228    }
229
230    #[test]
231    fn basic_provider_section() {
232        let mut header = ProviderSectionMetadataHeader::empty();
233        header.set_metadata_type(PROVIDER_SECTION_METADATA_TYPE);
234        header.set_provider_id(16);
235
236        assert_parses_to_record!(
237            FxtBuilder::new(header).build(),
238            RawTraceRecord::Metadata(MetadataRecord::ProviderSection(
239                ProviderSectionMetadataRecord { provider_id: 16 }
240            )),
241        );
242    }
243
244    #[test]
245    fn basic_provider_event() {
246        let mut header = ProviderEventMetadataHeader::empty();
247        header.set_metadata_type(PROVIDER_EVENT_METADATA_TYPE);
248        header.set_provider_id(16);
249        header.set_event_id(PROVIDER_EVENT_BUFFER_FULL);
250
251        assert_parses_to_record!(
252            FxtBuilder::new(header).build(),
253            RawTraceRecord::Metadata(MetadataRecord::ProviderEvent(ProviderEventMetadataRecord {
254                provider_id: 16,
255                event: ProviderEvent::BufferFull,
256            })),
257        );
258    }
259
260    #[test]
261    fn magic_number_literal() {
262        let mut buf = 0x0016547846040010u64.to_le_bytes().to_vec(); // header
263        buf.extend([1, 1, 1, 1]); // trailing
264
265        let (trailing, parsed) = RawTraceRecord::parse(&buf).unwrap();
266        assert_eq!(
267            parsed.parsed,
268            RawTraceRecord::Metadata(MetadataRecord::TraceInfo(
269                TraceInfoMetadataRecord::MagicNumber
270            )),
271        );
272        assert_eq!(trailing, [1, 1, 1, 1]);
273    }
274
275    #[test]
276    fn is_magic_check() {
277        assert!(is_magic(&FXT_MAGIC_BYTES));
278        assert!(is_magic(&[0x10, 0x00, 0x04, 0x46, 0x78, 0x54, 0x16, 0x00, 0xff]));
279        assert!(!is_magic(&FXT_MAGIC_BYTES[..7]));
280        assert!(!is_magic(b"NOT_FXT!"));
281        assert!(!is_magic(&[]));
282    }
283}