perf_event_data/
error.rs

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