use std::fmt::Display;
use fvm_shared::address::Address;
use fvm_shared::error::{ErrorNumber, ExitCode};
use fvm_shared::{ActorID, MethodNum};
use crate::kernel::SyscallError;
#[derive(Debug, Default, Clone)]
pub struct Backtrace {
pub frames: Vec<Frame>,
pub cause: Option<Cause>,
}
impl Display for Backtrace {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (i, frame) in self.frames.iter().rev().enumerate() {
writeln!(f, "{:02}: {}", i, frame)?;
}
if let Some(cause) = &self.cause {
writeln!(f, "--> caused by: {}", cause)?;
}
Ok(())
}
}
impl Backtrace {
pub fn is_empty(&self) -> bool {
self.frames.is_empty() && self.cause.is_none()
}
pub fn clear(&mut self) {
self.cause = None;
self.frames.clear();
}
pub fn begin(&mut self, cause: Cause) {
self.cause = Some(cause);
self.frames.clear();
}
pub fn set_cause(&mut self, cause: Cause) {
self.cause = Some(cause);
}
pub fn push_frame(&mut self, frame: Frame) {
self.frames.push(frame)
}
}
#[derive(Clone, Debug)]
pub struct Frame {
pub source: ActorID,
pub method: MethodNum,
pub code: ExitCode,
pub message: String,
}
impl Display for Frame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} (method {}) -- {} ({})",
Address::new_id(self.source),
self.method,
&self.message,
self.code,
)
}
}
#[derive(Clone, Debug)]
pub enum Cause {
Syscall {
module: &'static str,
function: &'static str,
error: ErrorNumber,
message: String,
},
Fatal {
error_msg: String,
backtrace: String,
},
}
impl Cause {
pub fn from_syscall(module: &'static str, function: &'static str, err: SyscallError) -> Self {
Self::Syscall {
module,
function,
error: err.1,
message: err.0,
}
}
pub fn from_fatal(err: anyhow::Error) -> Self {
Self::Fatal {
error_msg: format!("{:#}", err),
backtrace: err.backtrace().to_string(),
}
}
}
impl Display for Cause {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Cause::Syscall {
module,
function,
error,
message,
} => {
write!(
f,
"{}::{} -- {} ({}: {})",
module, function, &message, *error as u32, error,
)
}
Cause::Fatal {
error_msg,
backtrace,
} => {
write!(f, "[FATAL] Error: {}, Backtrace:\n{}", error_msg, backtrace)
}
}
}
}