diagnostics_log_encoding/
parse.rs
1use crate::{constants, ArgType, Argument, Header, RawSeverity, Record, Value};
9use std::borrow::Cow;
10use thiserror::Error;
11use zerocopy::FromBytes;
12
13pub fn basic_info(buf: &[u8]) -> Result<(zx::BootInstant, RawSeverity), ParseError> {
15 let (header, after_header) =
16 Header::read_from_prefix(buf).map_err(|_| ParseError::InvalidHeader)?;
17 if header.raw_type() != crate::TRACING_FORMAT_LOG_RECORD_TYPE {
18 return Err(ParseError::InvalidRecordType);
19 }
20 let (timestamp, _) =
21 i64::read_from_prefix(after_header).map_err(|_| ParseError::InvalidTimestamp)?;
22 Ok((zx::BootInstant::from_nanos(timestamp), header.severity()))
23}
24
25pub fn parse_record(buf: &[u8]) -> Result<(Record<'_>, &[u8]), ParseError> {
28 let (header, after_header) =
29 Header::read_from_prefix(buf).map_err(|_| ParseError::InvalidHeader)?;
30
31 if header.raw_type() != crate::TRACING_FORMAT_LOG_RECORD_TYPE {
32 return Err(ParseError::ValueOutOfValidRange);
33 }
34
35 let (timestamp, remaining_buffer) =
36 i64::read_from_prefix(after_header).map_err(|_| ParseError::InvalidTimestamp)?;
37
38 let arguments_length = if header.size_words() >= 2 {
39 (header.size_words() - 2) as usize * 8
41 } else {
42 return Err(ParseError::ValueOutOfValidRange);
43 };
44
45 let Some((mut arguments_buffer, remaining)) =
46 remaining_buffer.split_at_checked(arguments_length)
47 else {
48 return Err(ParseError::ValueOutOfValidRange);
49 };
50
51 let mut arguments = vec![];
52 let mut state = ParseState::Initial;
53 while !arguments_buffer.is_empty() {
54 let (argument, rem) = parse_argument_internal(arguments_buffer, &mut state)?;
55 arguments_buffer = rem;
56 arguments.push(argument);
57 }
58
59 Ok((
60 Record {
61 timestamp: zx::BootInstant::from_nanos(timestamp),
62 severity: header.severity(),
63 arguments,
64 },
65 remaining,
66 ))
67}
68
69enum ParseState {
72 Initial,
74 InMessage,
76 InArguments,
78}
79
80pub fn parse_argument(buf: &[u8]) -> Result<(Argument<'_>, &[u8]), ParseError> {
82 parse_argument_internal(buf, &mut ParseState::Initial)
83}
84
85fn parse_argument_internal<'a>(
86 buf: &'a [u8],
87 state: &mut ParseState,
88) -> Result<(Argument<'a>, &'a [u8]), ParseError> {
89 let (header, after_header) =
90 Header::read_from_prefix(buf).map_err(|_| ParseError::InvalidArgumentHeader)?;
91 let arg_ty = ArgType::try_from(header.raw_type())?;
92
93 let (name, after_name) = string_ref(header.name_ref(), after_header, false)?;
94 if matches!(state, ParseState::Initial) && name == Cow::Borrowed(constants::MESSAGE) {
95 *state = ParseState::InMessage;
96 }
97 let (value, after_value) = match arg_ty {
98 ArgType::Null => (Value::UnsignedInt(1), after_name),
99 ArgType::I64 => {
100 let (n, rem) =
101 i64::read_from_prefix(after_name).map_err(|_| ParseError::InvalidArgument)?;
102 (Value::SignedInt(n), rem)
103 }
104 ArgType::U64 => {
105 let (n, rem) =
106 u64::read_from_prefix(after_name).map_err(|_| ParseError::InvalidArgument)?;
107 (Value::UnsignedInt(n), rem)
108 }
109 ArgType::F64 => {
110 let (n, rem) =
111 f64::read_from_prefix(after_name).map_err(|_| ParseError::InvalidArgument)?;
112 (Value::Floating(n), rem)
113 }
114 ArgType::String => {
115 let (s, rem) =
116 string_ref(header.value_ref(), after_name, matches!(state, ParseState::InMessage))?;
117 (Value::Text(s), rem)
118 }
119 ArgType::Bool => (Value::Boolean(header.bool_val()), after_name),
120 ArgType::Pointer | ArgType::Koid | ArgType::I32 | ArgType::U32 => {
121 return Err(ParseError::Unsupported)
122 }
123 };
124 if matches!(state, ParseState::InMessage) {
125 *state = ParseState::InArguments;
126 }
127
128 Ok((Argument::new(name, value), after_value))
129}
130
131fn string_ref(
132 ref_mask: u16,
133 buf: &[u8],
134 support_invalid_utf8: bool,
135) -> Result<(Cow<'_, str>, &[u8]), ParseError> {
136 if ref_mask == 0 {
137 return Ok((Cow::Borrowed(""), buf));
138 }
139 if (ref_mask & (1 << 15)) == 0 {
140 return Err(ParseError::Unsupported);
141 }
142 let name_len = (ref_mask & !(1 << 15)) as usize;
144 let Some((name, after_name)) = buf.split_at_checked(name_len) else {
145 return Err(ParseError::ValueOutOfValidRange);
146 };
147 let parsed = if support_invalid_utf8 {
148 String::from_utf8_lossy(name)
149 } else {
150 let name = std::str::from_utf8(name)?;
151 Cow::Borrowed(name)
152 };
153 let (_padding, after_padding) = after_name.split_at(after_name.len() % 8);
154 Ok((parsed, after_padding))
155}
156
157#[derive(Debug, Clone, Error)]
159pub enum ParseError {
160 #[error("value out of range")]
162 ValueOutOfValidRange,
163
164 #[error("unsupported value type")]
167 Unsupported,
168
169 #[error("found invalid header")]
171 InvalidHeader,
172
173 #[error("found invalid header in an argument")]
175 InvalidArgumentHeader,
176
177 #[error("found invalid timestamp after header")]
179 InvalidTimestamp,
180
181 #[error("found invalid argument")]
183 InvalidArgument,
184
185 #[error("found invalid record type")]
187 InvalidRecordType,
188
189 #[error("parsing terminated early, remaining bytes: {0:?}")]
191 Incomplete(usize),
192}
193
194impl From<std::str::Utf8Error> for ParseError {
195 fn from(_: std::str::Utf8Error) -> Self {
196 ParseError::ValueOutOfValidRange
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203 use crate::encode::{Encoder, EncoderOpts};
204 use fidl_fuchsia_diagnostics::Severity;
205 use std::io::Cursor;
206
207 #[fuchsia::test]
208 fn basic_structured_info() {
209 let expected_timestamp = zx::BootInstant::from_nanos(72);
210 let record = Record {
211 timestamp: expected_timestamp,
212 severity: Severity::Error as u8,
213 arguments: vec![],
214 };
215 let mut buffer = Cursor::new(vec![0u8; 1000]);
216 let mut encoder = Encoder::new(&mut buffer, EncoderOpts::default());
217 encoder.write_record(record).unwrap();
218 let encoded = &buffer.get_ref().as_slice()[..buffer.position() as usize];
219
220 let (timestamp, severity) = basic_info(encoded).unwrap();
221 assert_eq!(timestamp, expected_timestamp);
222 assert_eq!(severity, Severity::Error.into_primitive());
223 }
224
225 #[fuchsia::test]
226 fn parse_record_with_zeros() {
227 let expected_timestamp = zx::BootInstant::from_nanos(72);
228 let record = Record {
229 timestamp: expected_timestamp,
230 severity: Severity::Error as u8,
231 arguments: vec![],
232 };
233 let mut buffer = Cursor::new(vec![0u8; 1000]);
234 let mut encoder = Encoder::new(&mut buffer, EncoderOpts::default());
235 encoder.write_record(record.clone()).unwrap();
236
237 let encoded = &buffer.get_ref().as_slice()[..buffer.position() as usize + 3];
239
240 let (result_record, rem) = parse_record(encoded).unwrap();
241 assert_eq!(rem.len(), 3);
242 assert_eq!(record, result_record);
243 }
244}