use alloc::sync::Arc;
use core::fmt;
use std::io;
use std::os::raw::c_int;
use std::sync::Mutex;
pub(crate) type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum ErrorKind {
TooManyVMPages,
UnknownPageSize,
InvalidPageSize(u64),
#[non_exhaustive]
Io {
operation: &'static str,
error: Arc<io::Error>,
process_id: Option<libc::pid_t>,
},
#[non_exhaustive]
IntegerCast(core::num::TryFromIntError),
}
struct ErrorBackTrace {
backtrace: backtrace::Backtrace,
resolved: bool,
}
impl ErrorBackTrace {
fn resolve(&mut self) {
if !self.resolved {
self.resolved = true;
self.backtrace.resolve();
}
}
}
impl fmt::Debug for ErrorBackTrace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.backtrace, f)
}
}
#[derive(Clone)]
struct ErrorData {
kind: ErrorKind,
backtrace: Arc<Mutex<ErrorBackTrace>>,
}
impl fmt::Debug for ErrorData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut back_trace_lock = self.backtrace.lock().unwrap();
back_trace_lock.resolve();
write!(f, "Error: {:?}. At:\n{:?}", self.kind, *back_trace_lock)
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Error(Box<ErrorData>);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.0.kind {
ErrorKind::TooManyVMPages => {
write!(f, "virtual memory address range contains too many pages")
}
ErrorKind::UnknownPageSize => {
write!(f, "failed to query the system page size")
}
ErrorKind::InvalidPageSize(size) => {
write!(f, "invalid system page size: {size} bytes")
}
ErrorKind::Io {
operation,
error,
process_id,
} => match process_id {
None => write!(f, "{operation}: {error}"),
Some(process_id) => write!(f, "{operation}({process_id}): {error}"),
},
ErrorKind::IntegerCast(err) => err.fmt(f),
}
}
}
impl core::error::Error for Error {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match &self.0.kind {
ErrorKind::TooManyVMPages
| ErrorKind::UnknownPageSize
| ErrorKind::InvalidPageSize(_)
| ErrorKind::Io { .. } => None,
ErrorKind::IntegerCast(err) => Some(err),
}
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
let backtrace = backtrace::Backtrace::new_unresolved();
Self(Box::new(ErrorData {
kind,
backtrace: Arc::new(Mutex::new(ErrorBackTrace {
backtrace,
resolved: false,
})),
}))
}
}
impl From<core::num::TryFromIntError> for Error {
fn from(err: core::num::TryFromIntError) -> Self {
Self::from(ErrorKind::IntegerCast(err))
}
}
impl Error {
pub(crate) fn from_io3(
error: io::Error,
operation: &'static str,
process_id: libc::pid_t,
) -> Self {
ErrorKind::Io {
operation,
error: Arc::new(error),
process_id: Some(process_id),
}
.into()
}
#[must_use]
pub fn kind(&self) -> &ErrorKind {
&self.0.kind
}
#[must_use]
pub fn os_error_code(&self) -> Option<c_int> {
match &self.0.kind {
ErrorKind::TooManyVMPages
| ErrorKind::UnknownPageSize
| ErrorKind::InvalidPageSize(_)
| ErrorKind::IntegerCast { .. } => None,
ErrorKind::Io { error, .. } => error.raw_os_error(),
}
}
}