blob: 0558d2cfe76937a2f1494ac06b97a1b1d0136fd4 [file] [log] [blame]
//! Parse diagnostic records from streams, returning FIDL-generated structs that match expected
//! diagnostic service APIs.
use {
crate::{ArgType, Header, StreamError, StringRef},
fidl_fuchsia_diagnostics::Severity,
fidl_fuchsia_diagnostics_stream::{Argument, Record, Value},
nom::{
bytes::complete::take,
multi::many0,
number::complete::{le_f64, le_i64, le_u64},
Err, IResult,
},
std::convert::TryFrom,
};
pub(crate) type ParseResult<'a, T> = IResult<&'a [u8], T, StreamError>;
/// Error for parsed records.
pub type ParseError = Err<StreamError>;
/// Attempt to parse a diagnostic record from the head of this buffer.
pub fn parse_record(buf: &[u8]) -> ParseResult<'_, Record> {
let (after_header, header) = parse_header(buf)?;
if header.raw_type() != crate::TRACING_FORMAT_LOG_RECORD_TYPE {
return Err(nom::Err::Failure(StreamError::ValueOutOfValidRange));
}
let (var_len, timestamp) = le_i64(after_header)?;
// Remove two word lengths for header and timestamp.
let remaining_record_len = if header.size_words() >= 2 {
(header.size_words() - 2) as usize * 8
} else {
return Err(nom::Err::Failure(StreamError::ValueOutOfValidRange));
};
let severity = Severity::from_primitive(header.severity())
.ok_or(nom::Err::Failure(StreamError::Unsupported))?;
let (after_record, args_buf) = take(remaining_record_len)(var_len)?;
let (_, arguments) = many0(parse_argument)(args_buf)?;
Ok((after_record, Record { timestamp, severity, arguments }))
}
fn parse_header(buf: &[u8]) -> ParseResult<'_, Header> {
let (after, header) = le_u64(buf)?;
let header = Header(header);
Ok((after, header))
}
pub(super) fn parse_argument(buf: &[u8]) -> ParseResult<'_, Argument> {
let (after_header, header) = parse_header(buf)?;
let arg_ty = ArgType::try_from(header.raw_type()).map_err(nom::Err::Failure)?;
let (after_name, name) = string_ref(header.name_ref(), after_header)?;
let (value, after_value) = match arg_ty {
ArgType::Null => (Value::UnsignedInt(1), after_name),
ArgType::I64 => {
let (rem, n) = le_i64(after_name)?;
(Value::SignedInt(n), rem)
}
ArgType::U64 => {
let (rem, n) = le_u64(after_name)?;
(Value::UnsignedInt(n), rem)
}
ArgType::F64 => {
let (rem, n) = le_f64(after_name)?;
(Value::Floating(n), rem)
}
ArgType::String => {
let (rem, s) = string_ref(header.value_ref(), after_name)?;
(Value::Text(s.to_string()), rem)
}
ArgType::Pointer | ArgType::Koid | ArgType::I32 | ArgType::U32 => {
return Err(Err::Failure(StreamError::Unsupported))
}
};
Ok((after_value, Argument { name: name.to_string(), value }))
}
fn string_ref(ref_mask: u16, buf: &[u8]) -> ParseResult<'_, StringRef<'_>> {
Ok(if ref_mask == 0 {
(buf, StringRef::Empty)
} else if (ref_mask & 1 << 15) == 0 {
return Err(Err::Failure(StreamError::Unsupported));
} else {
// zero out the top bit
let name_len = (ref_mask & !(1 << 15)) as usize;
let (after_name, name) = take(name_len)(buf)?;
let name = std::str::from_utf8(name).map_err(|e| nom::Err::Error(StreamError::from(e)))?;
let (_padding, after_padding) = after_name.split_at(after_name.len() % 8);
(after_padding, StringRef::Inline(name))
})
}