use std::borrow::Cow;
use std::mem::MaybeUninit;
use perf_event_open_sys::bindings;
use crate::endian::Endian;
use crate::parsebuf::{ParseBufCursor, TrackingParseBuf};
use crate::util::cow::CowSliceExt;
use crate::{Record, RecordMetadata, SampleId, Visitor};
used_in_docs!(Record);
pub use crate::config::ParseConfig;
pub use crate::error::{ErrorKind, ParseError, ParseResult};
pub use crate::parsebuf::{ParseBuf, ParseBufChunk};
pub trait Parse<'p>: Sized {
fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
where
E: Endian,
B: ParseBuf<'p>;
}
#[derive(Clone)]
pub struct Parser<B, E> {
config: ParseConfig<E>,
data: TrackingParseBuf<B>,
}
impl<'p, B, E> Parser<B, E>
where
E: Endian,
B: ParseBuf<'p>,
{
pub fn new(data: B, config: ParseConfig<E>) -> Self {
Self {
config,
data: TrackingParseBuf::new(data),
}
}
#[inline]
pub fn config(&self) -> &ParseConfig<E> {
&self.config
}
#[inline]
pub fn endian(&self) -> &E {
self.config.endian()
}
pub(crate) fn split_at(&mut self, offset: usize) -> ParseResult<Parser<ParseBufCursor<'p>, E>> {
let cursor = ParseBufCursor::new(&mut self.data, offset)?;
Ok(Parser::new(cursor, self.config().clone()))
}
fn safe_capacity_bound<T>(&self) -> usize {
const DEFAULT_LEN: usize = 16384;
let size = std::mem::size_of::<T>();
if size == 0 {
return usize::MAX;
}
self.data.remaining_hint().unwrap_or(DEFAULT_LEN) / size
}
fn parse_bytes_direct(&mut self, len: usize) -> ParseResult<Option<&'p [u8]>> {
let chunk = match self.data.chunk()? {
ParseBufChunk::External(chunk) => chunk,
_ => return Ok(None),
};
if chunk.len() < len {
return Ok(None);
}
self.data.advance(len);
Ok(Some(&chunk[..len]))
}
#[cold]
fn parse_bytes_slow(&mut self, mut len: usize) -> ParseResult<Vec<u8>> {
let mut bytes = Vec::with_capacity(self.safe_capacity_bound::<u8>().min(len));
while len > 0 {
let mut chunk = self.data.chunk()?;
chunk.truncate(len);
bytes.extend_from_slice(&chunk);
let chunk_len = chunk.len();
len -= chunk_len;
self.data.advance(chunk_len);
}
Ok(bytes)
}
pub fn parse_bytes(&mut self, len: usize) -> ParseResult<Cow<'p, [u8]>> {
if let Some(bytes) = self.parse_bytes_direct(len)? {
return Ok(Cow::Borrowed(bytes));
}
match self.data.remaining_hint() {
Some(hint) if hint >= len => (),
_ => return Ok(Cow::Owned(self.parse_bytes_slow(len)?)),
}
let mut bytes = Vec::with_capacity(len);
self.parse_to_slice(bytes.spare_capacity_mut())?;
unsafe { bytes.set_len(len) };
Ok(Cow::Owned(bytes))
}
fn parse_bytes_ignored(&mut self, mut len: usize) -> ParseResult<()> {
while len > 0 {
let chunk = self.data.chunk()?;
let consumed = chunk.len().min(len);
len -= consumed;
self.data.advance(consumed);
}
Ok(())
}
fn parse_to_slice(&mut self, slice: &mut [MaybeUninit<u8>]) -> ParseResult<()> {
let mut dst = slice.as_mut_ptr() as *mut u8;
let mut len = slice.len();
while len > 0 {
let chunk = self.data.chunk()?;
let chunk_len = chunk.len().min(len);
unsafe {
std::ptr::copy_nonoverlapping(chunk.as_ptr(), dst, chunk_len);
dst = dst.add(chunk_len);
len -= chunk_len;
};
self.data.advance(chunk_len);
}
Ok(())
}
#[cold]
fn parse_array_slow<const N: usize>(&mut self) -> ParseResult<[u8; N]> {
let mut array = [0u8; N];
self.parse_to_slice(unsafe { array.align_to_mut().1 })?;
Ok(array)
}
pub(crate) fn parse_array<const N: usize>(&mut self) -> ParseResult<[u8; N]> {
let chunk = self.data.chunk()?;
if chunk.len() < N {
return self.parse_array_slow();
}
let mut array = [0u8; N];
array.copy_from_slice(&chunk[..N]);
self.data.advance(N);
Ok(array)
}
pub fn parse<P: Parse<'p>>(&mut self) -> ParseResult<P> {
P::parse(self)
}
pub fn parse_with<F, R>(&mut self, func: F) -> ParseResult<R>
where
F: FnOnce(&mut Self) -> ParseResult<R>,
{
func(self)
}
pub fn parse_if<P: Parse<'p>>(&mut self, parse: bool) -> ParseResult<Option<P>> {
self.parse_if_with(parse, P::parse)
}
pub fn parse_if_with<F, R>(&mut self, parse: bool, func: F) -> ParseResult<Option<R>>
where
F: FnOnce(&mut Self) -> ParseResult<R>,
{
match parse {
true => self.parse_with(func).map(Some),
false => Ok(None),
}
}
pub(crate) fn parse_padded<F, R>(&mut self, padding: usize, func: F) -> ParseResult<R>
where
F: FnOnce(&mut Self) -> ParseResult<R>,
{
assert_ne!(padding, 0);
let offset = self.data.offset();
let value = func(self)?;
let parsed = self.data.offset() - offset;
match parsed % padding {
0 => (),
n => self.parse_bytes_ignored(padding - n)?,
}
Ok(value)
}
pub fn parse_u8(&mut self) -> ParseResult<u8> {
let [byte] = self.parse_array()?;
Ok(byte)
}
pub fn parse_u16(&mut self) -> ParseResult<u16> {
let array = self.parse_array()?;
Ok(self.endian().convert_u16(array))
}
pub fn parse_u32(&mut self) -> ParseResult<u32> {
let array = self.parse_array()?;
Ok(self.endian().convert_u32(array))
}
pub fn parse_u64(&mut self) -> ParseResult<u64> {
let array = self.parse_array()?;
Ok(self.endian().convert_u64(array))
}
pub fn parse_rest(&mut self) -> ParseResult<Cow<'p, [u8]>> {
let mut bytes = self.data.chunk()?.to_cow();
self.data.advance(bytes.len());
loop {
match self.data.chunk() {
Ok(chunk) => {
bytes.to_mut().extend_from_slice(&chunk);
let len = chunk.len();
self.data.advance(len);
}
Err(e) if e.kind() == ErrorKind::Eof => break,
Err(e) => return Err(e),
}
}
Ok(bytes)
}
pub fn parse_rest_trim_nul(&mut self) -> ParseResult<Cow<'p, [u8]>> {
let mut bytes = self.parse_rest()?;
let mut rest = &*bytes;
while let Some((b'\0', head)) = rest.split_last() {
rest = head;
}
bytes.truncate(rest.len());
Ok(bytes)
}
pub unsafe fn parse_slice_direct<T>(&mut self, len: usize) -> ParseResult<Option<&'p [T]>>
where
T: Copy,
{
if !self.endian().is_native() {
return Ok(None);
}
let byte_len = len.checked_mul(std::mem::size_of::<T>()).ok_or_else(|| {
ParseError::custom(
ErrorKind::InvalidRecord,
"array length in bytes larger than usize::MAX",
)
})?;
let bytes = match self.parse_bytes_direct(byte_len)? {
Some(bytes) => bytes,
None => return Ok(None),
};
let (head, slice, tail) = bytes.align_to();
if !head.is_empty() || !tail.is_empty() {
return Ok(None);
}
Ok(Some(slice))
}
pub unsafe fn parse_slice<T>(&mut self, len: usize) -> ParseResult<Cow<'p, [T]>>
where
T: Parse<'p> + Copy,
{
Ok(match self.parse_slice_direct(len)? {
Some(slice) => Cow::Borrowed(slice),
None => Cow::Owned(self.parse_repeated(len)?),
})
}
pub fn parse_repeated<T: Parse<'p>>(&mut self, len: usize) -> ParseResult<Vec<T>> {
let mut vec = Vec::with_capacity(len.min(self.safe_capacity_bound::<T>()));
for _ in 0..len {
vec.push(self.parse()?);
}
Ok(vec)
}
pub fn parse_metadata(
&mut self,
) -> ParseResult<(Parser<impl ParseBuf<'p>, E>, RecordMetadata)> {
let header = self.parse()?;
self.parse_metadata_with_header(header)
}
fn parse_metadata_with_header_impl(
&mut self,
header: bindings::perf_event_header,
) -> ParseResult<(Parser<ParseBufCursor<'p>, E>, RecordMetadata)> {
use perf_event_open_sys::bindings::*;
use std::mem;
let data_len = (header.size as usize)
.checked_sub(mem::size_of_val(&header))
.ok_or_else(|| {
ParseError::custom(
ErrorKind::InvalidRecord,
"header size was too small to be valid",
)
})?;
let mut rp = self.split_at(data_len)?;
let (p, sample_id) = match header.type_ {
PERF_RECORD_MMAP | PERF_RECORD_SAMPLE => (rp, SampleId::default()),
_ => {
let remaining_len = data_len
.checked_sub(SampleId::estimate_len(rp.config()))
.ok_or_else(|| ParseError::custom(
ErrorKind::InvalidRecord,
"config has sample_id_all bit set but record does not have enough data to store the sample_id"
))?;
let p = rp.split_at(remaining_len)?;
(p, rp.parse()?)
}
};
let metadata = RecordMetadata::new(header, sample_id);
Ok((p, metadata))
}
pub fn parse_metadata_with_header(
&mut self,
header: bindings::perf_event_header,
) -> ParseResult<(Parser<impl ParseBuf<'p>, E>, RecordMetadata)> {
self.parse_metadata_with_header_impl(header)
}
pub fn parse_record<V: Visitor<'p>>(&mut self, visitor: V) -> ParseResult<V::Output> {
let header = self.parse()?;
self.parse_record_with_header(visitor, header)
}
fn parse_record_impl<V: Visitor<'p>>(
self,
visitor: V,
metadata: RecordMetadata,
) -> ParseResult<V::Output> {
use perf_event_open_sys::bindings::*;
let mut p = Parser::new(self.data, self.config.with_misc(metadata.misc()));
Ok(match metadata.ty() {
PERF_RECORD_MMAP => visitor.visit_mmap(p.parse()?, metadata),
PERF_RECORD_LOST => visitor.visit_lost(p.parse()?, metadata),
PERF_RECORD_COMM => visitor.visit_comm(p.parse()?, metadata),
PERF_RECORD_EXIT => visitor.visit_exit(p.parse()?, metadata),
PERF_RECORD_THROTTLE => visitor.visit_throttle(p.parse()?, metadata),
PERF_RECORD_UNTHROTTLE => visitor.visit_unthrottle(p.parse()?, metadata),
PERF_RECORD_FORK => visitor.visit_fork(p.parse()?, metadata),
PERF_RECORD_READ => visitor.visit_read(p.parse()?, metadata),
PERF_RECORD_SAMPLE => visitor.visit_sample(p.parse()?, metadata),
PERF_RECORD_MMAP2 => visitor.visit_mmap2(p.parse()?, metadata),
PERF_RECORD_AUX => visitor.visit_aux(p.parse()?, metadata),
PERF_RECORD_ITRACE_START => visitor.visit_itrace_start(p.parse()?, metadata),
PERF_RECORD_LOST_SAMPLES => visitor.visit_lost_samples(p.parse()?, metadata),
PERF_RECORD_SWITCH_CPU_WIDE => visitor.visit_switch_cpu_wide(p.parse()?, metadata),
PERF_RECORD_NAMESPACES => visitor.visit_namespaces(p.parse()?, metadata),
PERF_RECORD_KSYMBOL => visitor.visit_ksymbol(p.parse()?, metadata),
PERF_RECORD_BPF_EVENT => visitor.visit_bpf_event(p.parse()?, metadata),
PERF_RECORD_CGROUP => visitor.visit_cgroup(p.parse()?, metadata),
PERF_RECORD_TEXT_POKE => visitor.visit_text_poke(p.parse()?, metadata),
PERF_RECORD_AUX_OUTPUT_HW_ID => visitor.visit_aux_output_hw_id(p.parse()?, metadata),
_ => visitor.visit_unknown(p.parse_rest()?, metadata),
})
}
pub fn parse_record_with_header<V: Visitor<'p>>(
&mut self,
visitor: V,
header: bindings::perf_event_header,
) -> ParseResult<V::Output> {
let (p, metadata) = self.parse_metadata_with_header_impl(header)?;
match p.data.as_slice() {
Some(data) => {
let p = Parser::new(data, p.config);
p.parse_record_impl(visitor, metadata)
}
None => p.parse_record_impl(visitor, metadata),
}
}
}
impl<'p> Parse<'p> for u8 {
fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
where
E: Endian,
B: ParseBuf<'p>,
{
p.parse_u8()
}
}
impl<'p> Parse<'p> for u16 {
fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
where
E: Endian,
B: ParseBuf<'p>,
{
p.parse_u16()
}
}
impl<'p> Parse<'p> for u32 {
fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
where
E: Endian,
B: ParseBuf<'p>,
{
p.parse_u32()
}
}
impl<'p> Parse<'p> for u64 {
fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
where
E: Endian,
B: ParseBuf<'p>,
{
p.parse_u64()
}
}
impl<'p, const N: usize> Parse<'p> for [u8; N] {
fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
where
E: Endian,
B: ParseBuf<'p>,
{
p.parse_array()
}
}
impl<'p> Parse<'p> for bindings::perf_event_header {
fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
where
E: Endian,
B: ParseBuf<'p>,
{
Ok(Self {
type_: p.parse()?,
misc: p.parse()?,
size: p.parse()?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::endian::Native;
#[test]
fn parse_rest() {
let data: &[u8] = &[1, 2, 3, 4, 5];
let mut parser = Parser::new(data, ParseConfig::<Native>::default());
let rest = parser.parse_rest().unwrap();
assert_eq!(data, &*rest);
}
}