1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
use std::borrow::Cow;
use std::error::Error;
use std::fmt::{self, Display};
use perf_event_open_sys::bindings::perf_event_attr;
use crate::parse::{Parse, ParseBuf, ParseConfig, Parser};
used_in_docs!(Parse, Parser, ParseBuf, ParseConfig);
used_in_docs!(perf_event_attr);
type BoxedError = Box<dyn Error + Send + Sync + 'static>;
/// A specialized result type used by [`Parse`] and [`Parser`].
pub type ParseResult<T> = std::result::Result<T, ParseError>;
/// The error type for parsing errors as returned by [`Parser`].
///
/// The format used by perf events doesn't give many opportunities for error
/// checking so most parsing errors will likely result in an error with [`kind`]
/// [`ErrorKind::Eof`]. Otherwise, this type can be used to wrap errors emitted
/// by the [`ParseBuf`] type.
///
/// [`kind`]: ParseError::kind
#[derive(Debug)]
pub struct ParseError {
code: ErrorKind,
source: Option<BoxedError>,
}
impl ParseError {
/// Create a new `ParseError` from an arbitrary error payload.
#[cold]
pub fn new<E>(error: E) -> Self
where
E: Into<BoxedError>,
{
Self {
code: ErrorKind::External,
source: Some(error.into()),
}
}
/// Create a new `ParseError` with a custom message.
#[cold]
pub(crate) fn custom(kind: ErrorKind, msg: impl Message) -> Self {
Self::new(CustomMessageError::new(msg)).with_kind(kind)
}
/// Get the [`ErrorKind`] of this error.
#[inline]
pub fn kind(&self) -> ErrorKind {
self.code
}
#[inline]
const fn from_code(code: ErrorKind) -> Self {
Self { code, source: None }
}
pub(crate) fn with_kind(self, code: ErrorKind) -> Self {
Self { code, ..self }
}
/// More input was needed before the item could be successfully parsed.
#[cold]
pub fn eof() -> Self {
Self::from_code(ErrorKind::Eof)
}
}
/// A list specifying general categories of parse error.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[non_exhaustive]
pub enum ErrorKind {
/// There was no more data in the [`ParseBuf`] but more is required in
/// in order to parse the record.
///
/// Should be returned by [`ParseBuf::chunk`] when there is no data left to
/// be returned.
Eof,
/// A record was parsed, but it was invalid.
///
/// This is for validation errors that occur when parsing the record. Most
/// errors will result either leftover unparsed data or
/// [`Eof`](ErrorKind::Eof) errors.
InvalidRecord,
/// The [`ParseConfig`] had options that are not yet supported by this
/// library.
///
/// This is only emitted when the lack of support for said option would
/// cause parsing to to return incorrect results.
UnsupportedConfig,
/// The data type that was being parsed by this library contains data that
/// is not yet supported by this library.
///
/// This is used when attempting to parse a [`perf_event_attr`] that has
/// fields from versions of the kernel that this crate does not support.
UnsupportedData,
/// An external error, forwarded from the [`ParseBuf`] implementation.
///
/// This error will never be emitted by a parse method in this crate.
External,
}
impl Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.code {
ErrorKind::Eof => f.write_str("unexpected EOF during parsing")?,
ErrorKind::InvalidRecord => f.write_str("invalid record")?,
ErrorKind::UnsupportedData => f.write_str("unsupported serialized data")?,
ErrorKind::UnsupportedConfig => f.write_str("unsupported config")?,
ErrorKind::External => {
// This type should always have a source, but, however, if it doesn't then we
// still need to provide a default message.
if self.source.is_none() {
f.write_str("user-provided error")?;
}
}
}
if let Some(source) = &self.source {
if matches!(self.code, ErrorKind::External) {
f.write_str(": ")?;
}
source.fmt(f)?;
}
Ok(())
}
}
impl Error for ParseError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.source {
Some(source) => Some(&**source),
None => None,
}
}
}
impl From<std::io::Error> for ParseError {
#[cold]
fn from(error: std::io::Error) -> Self {
match error.kind() {
std::io::ErrorKind::UnexpectedEof => Self::new(error).with_kind(ErrorKind::Eof),
_ => Self::new(error),
}
}
}
impl From<BoxedError> for ParseError {
#[cold]
fn from(error: BoxedError) -> Self {
Self {
code: ErrorKind::External,
source: Some(error),
}
}
}
pub(crate) trait Message: Display {
fn as_str(&self) -> Option<&'static str>;
}
impl Message for &'static str {
fn as_str(&self) -> Option<&'static str> {
Some(self)
}
}
impl Message for fmt::Arguments<'_> {
fn as_str(&self) -> Option<&'static str> {
self.as_str()
}
}
#[derive(Debug)]
struct CustomMessageError(Cow<'static, str>);
impl CustomMessageError {
fn new(msg: impl Message) -> Self {
Self(match msg.as_str() {
Some(s) => Cow::Borrowed(s),
None => msg.to_string().into(),
})
}
}
impl fmt::Display for CustomMessageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl Error for CustomMessageError {}