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
// Copyright (c) 2020-2023 MicroDoc Software GmbH.
// See the "LICENSE.txt" file at the top-level directory of this distribution.
//
// Licensed under the MIT license. This file may not be copied, modified,
// or distributed except according to those terms.
/*! Error reporting. */
use std::os::raw::c_int;
use std::sync::{Arc, Mutex};
use std::{fmt, io};
/// A result of a fallible operation.
pub(crate) type Result<T> = std::result::Result<T, Error>;
/// Actual storage for an error.
#[derive(Debug, Clone)]
#[non_exhaustive]
#[allow(variant_size_differences)]
pub enum ErrorKind {
/// Virtual memory address range contains too many pages.
TooManyVMPages,
/// Some [`io::Error`](std::io::Error) occurred.
#[non_exhaustive]
Io {
/// Name of the I/O operation that generated the error.
operation: &'static str,
/// The [`io::Error`](std::io::Error) that occurred.
error: Arc<io::Error>,
/// Identifier of the process that was the target of the I/O.
process_id: Option<libc::pid_t>,
},
/// Casting an integer caused data loss.
#[non_exhaustive]
IntegerCast(std::num::TryFromIntError),
}
/// Call stack back trace where the `Error` object was created.
struct ErrorBackTrace {
backtrace: backtrace::Backtrace,
resolved: bool,
}
impl ErrorBackTrace {
/// Resolve the call stack back trace to resolve all addresses
/// to their symbolic names.
fn resolve(&mut self) -> bool {
if !self.resolved {
self.resolved = true;
self.backtrace.resolve();
true
} else {
false
}
}
}
impl fmt::Debug for ErrorBackTrace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.backtrace, f)
}
}
/// Data describing an `Error` that occurred.
#[derive(Clone)]
struct ErrorData {
kind: ErrorKind,
backtrace: Arc<Mutex<ErrorBackTrace>>,
}
impl ErrorData {
/// Resolve the call stack back trace to resolve all addresses
/// to their symbolic names.
fn resolve_back_trace(&self) -> bool {
let mut back_trace_lock = self.backtrace.lock().unwrap();
back_trace_lock.resolve()
}
}
impl fmt::Debug for ErrorData {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
if self.resolve_back_trace() {
fmt::Debug::fmt(self, _f)
} else {
Ok(())
}
}
}
/// An error is a pointer that allocates when an error happens.
#[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::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 std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.0.kind {
// Errors that are self-descriptive.
ErrorKind::TooManyVMPages => None,
ErrorKind::Io { .. } => None,
// Errors that defer description to the inner error.
ErrorKind::IntegerCast(err) => Some(err),
}
}
}
/// Convert an `ErrorKind` into an `Error`.
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,
})),
}))
}
}
/// Wrap another error into an instance of `Error`.
impl From<std::num::TryFromIntError> for Error {
fn from(err: std::num::TryFromIntError) -> Self {
Self::from(ErrorKind::IntegerCast(err))
}
}
impl Error {
/// Wrap an `io::Error` into an instance of `Error`, with an associated process ID.
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()
}
/// Returns the actual kind of this error.
pub fn kind(&self) -> &ErrorKind {
&self.0.kind
}
/// Returns the errno code for a given `Error`, if such a code has been
/// reported by the operating system.
pub fn os_error_code(&self) -> Option<c_int> {
match &self.0.kind {
ErrorKind::TooManyVMPages { .. } => None,
ErrorKind::Io { error, .. } => error.raw_os_error(),
ErrorKind::IntegerCast { .. } => None,
}
}
}