fuel_vm/backtrace.rs
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
//! Backtrace implementation to track script errors.
//!
//! As of the moment, doesn't support predicates.
use alloc::{
borrow::ToOwned,
vec::Vec,
};
use crate::{
call::CallFrame,
consts::*,
interpreter::{
InitialBalances,
Interpreter,
},
};
use educe::Educe;
use crate::interpreter::{
Memory,
MemoryInstance,
};
use fuel_tx::ScriptExecutionResult;
use fuel_types::{
ContractId,
Word,
};
#[derive(Educe)]
#[educe(Debug)]
/// Runtime description derived from a VM error.
pub struct Backtrace {
call_stack: Vec<CallFrame>,
contract: ContractId,
registers: [Word; VM_REGISTER_COUNT],
memory: MemoryInstance,
result: ScriptExecutionResult,
initial_balances: InitialBalances,
}
impl Backtrace {
/// Create a backtrace from a vm instance and instruction result.
///
/// This isn't copy-free and shouldn't be provided by default.
pub fn from_vm_error<M, S, Tx, Ecal>(
vm: &Interpreter<M, S, Tx, Ecal>,
result: ScriptExecutionResult,
) -> Self
where
M: Memory,
{
let call_stack = vm.call_stack().to_owned();
let contract = vm.internal_contract().unwrap_or_default();
let memory = vm.memory().clone();
let initial_balances = vm.initial_balances().clone();
let mut registers = [0; VM_REGISTER_COUNT];
registers.copy_from_slice(vm.registers());
Self {
call_stack,
contract,
registers,
memory,
result,
initial_balances,
}
}
/// Call stack of the VM when the error occurred.
pub fn call_stack(&self) -> &[CallFrame] {
self.call_stack.as_slice()
}
/// Last contract of the context when the error occurred.
pub const fn contract(&self) -> &ContractId {
&self.contract
}
/// Register set when the error occurred.
pub const fn registers(&self) -> &[Word] {
&self.registers
}
/// Memory of the VM when the error occurred.
pub fn memory(&self) -> &MemoryInstance {
&self.memory
}
/// [`ScriptExecutionResult`] of the error that caused this backtrace.
pub const fn result(&self) -> &ScriptExecutionResult {
&self.result
}
/// The initial balances.
pub const fn initial_balances(&self) -> &InitialBalances {
&self.initial_balances
}
/// Expose the internal attributes of the backtrace.
pub fn into_inner(
self,
) -> (
Vec<CallFrame>,
ContractId,
[Word; VM_REGISTER_COUNT],
MemoryInstance,
ScriptExecutionResult,
InitialBalances,
) {
let Self {
call_stack,
contract,
registers,
memory,
result,
initial_balances,
} = self;
(
call_stack,
contract,
registers,
memory,
result,
initial_balances,
)
}
}