Skip to content

Commit 61c032e

Browse files
committed
oh boy, backtraces.
1 parent acad70a commit 61c032e

File tree

3 files changed

+50
-19
lines changed

3 files changed

+50
-19
lines changed

fvm/src/call_manager/backtrace.rs

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use fvm_shared::{ActorID, MethodNum};
77

88
use crate::kernel::SyscallError;
99

10-
/// A call backtrace records _why_ an actor exited with a specific error code.
10+
/// A call backtrace records the actors an error was propagated through, from
11+
/// the moment it was emitted. The original error is the _cause_. Backtraces are
12+
/// useful for identifying the root cause of an error.
1113
#[derive(Debug, Default, Clone)]
1214
pub struct Backtrace {
1315
/// The actors through which this error was propagated from bottom (source) to top.
@@ -34,22 +36,26 @@ impl Backtrace {
3436
self.frames.is_empty() && self.cause.is_none()
3537
}
3638

37-
/// Clear the backtrace. This should be called:
38-
///
39-
/// 1. Before all syscalls except "abort"
40-
/// 2. After an actor returns with a 0 exit code.
39+
/// Clear the backtrace.
4140
pub fn clear(&mut self) {
4241
self.cause = None;
4342
self.frames.clear();
4443
}
4544

46-
/// Set the backtrace cause. If there is an existing backtrace, this will clear it.
47-
pub fn set_cause(&mut self, cause: Cause) {
45+
/// Begins a new backtrace. If there is an existing backtrace, this will clear it.
46+
///
47+
/// Note: Backtraces are populated _backwards_. That is, a frame is inserted
48+
/// every time an actor returns. That's why `begin()` resets any currently
49+
/// accumulated state, as once an error occurs, we want to track its
50+
/// propagation all the way up.
51+
pub fn begin(&mut self, cause: Cause) {
4852
self.cause = Some(cause);
4953
self.frames.clear();
5054
}
5155

5256
/// Push a "frame" (actor exit) onto the backtrace.
57+
///
58+
/// This should be called every time an actor exits.
5359
pub fn push_frame(&mut self, frame: Frame) {
5460
self.frames.push(frame)
5561
}
@@ -83,9 +89,7 @@ impl Display for Frame {
8389
}
8490
}
8591

86-
/// The ultimate "cause" of a failed message.
87-
#[derive(Clone, Debug)]
88-
pub struct Cause {
92+
struct SyscallCause {
8993
/// The syscall "module".
9094
pub module: &'static str,
9195
/// The syscall function name.
@@ -96,23 +100,45 @@ pub struct Cause {
96100
pub message: String,
97101
}
98102

103+
/// The ultimate "cause" of a failed message.
104+
#[derive(Clone, Debug)]
105+
pub enum Cause {
106+
/// The original cause was a syscall error.
107+
Syscall(SyscallCause),
108+
/// The original cause was a fatal error.
109+
Fatal(String),
110+
}
111+
99112
impl Cause {
100-
pub fn new(module: &'static str, function: &'static str, err: SyscallError) -> Self {
101-
Self {
113+
/// Records a failing syscall as the cause of a backtrace.
114+
pub fn from_syscall(module: &'static str, function: &'static str, err: SyscallError) -> Self {
115+
Self::Syscall(SyscallCause {
102116
module,
103117
function,
104118
error: err.1,
105119
message: err.0,
106-
}
120+
})
121+
}
122+
123+
/// Records a fatal error as the cause of a backtrace.
124+
pub fn from_fatal(err: anyhow::Error) -> Self {
125+
Self::Fatal(err.into())
107126
}
108127
}
109128

110129
impl Display for Cause {
111130
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112-
write!(
113-
f,
114-
"{}::{} -- {} ({}: {})",
115-
self.module, self.function, &self.message, self.error as u32, self.error,
116-
)
131+
match self {
132+
Cause::Syscall(cause) => {
133+
write!(
134+
f,
135+
"{}::{} -- {} ({}: {})",
136+
cause.module, cause.function, &cause.message, cause.error as u32, cause.error,
137+
)
138+
}
139+
Cause::Fatal(msg) => {
140+
write!(f, "[FATAL] {}", msg)
141+
}
142+
}
117143
}
118144
}

fvm/src/call_manager/default.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,11 @@ where
392392
let ret = match result {
393393
Ok(value) => Ok(InvocationResult::Return(value)),
394394
Err(abort) => {
395+
let cause = match abort {
396+
Abort::Exit(_, _) => {}
397+
Abort::OutOfGas => {}
398+
Abort::Fatal(_) => {}
399+
};
395400
if let Some(err) = last_error {
396401
cm.backtrace.set_cause(err);
397402
}

fvm/src/executor/default.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ where
130130
ErrorNumber::Forbidden => ExitCode::SYS_ASSERTION_FAILED,
131131
};
132132

133-
backtrace.set_cause(backtrace::Cause::new("send", "send", err));
133+
backtrace.begin(backtrace::Cause::from_syscall("send", "send", err));
134134
Receipt {
135135
exit_code,
136136
return_data: Default::default(),

0 commit comments

Comments
 (0)