Skip to content

Commit

Permalink
Change execution tracing to use it's own memory diffs
Browse files Browse the repository at this point in the history
  • Loading branch information
Dentosal committed Dec 11, 2024
1 parent eb07ba9 commit 2cd645d
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 10 deletions.
1 change: 1 addition & 0 deletions fuel-vm/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub use memory::{
Memory,
MemoryInstance,
MemoryRange,
MemorySliceChange,
};

use crate::checked_transaction::{
Expand Down
75 changes: 71 additions & 4 deletions fuel-vm/src/interpreter/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ use crate::error::{
IoResult,
RuntimeError,
};
use alloc::vec::Vec;
use alloc::{
vec,
vec::Vec,
};
use fuel_storage::{
Mappable,
StorageRead,
Expand Down Expand Up @@ -130,6 +133,15 @@ impl MemoryInstance {
self.hp = MEM_SIZE;
}

/// Make memory equal to another instance, keeping the original allocations.
pub fn make_equal(&mut self, other: &Self) {
self.stack.truncate(0);
self.stack.extend_from_slice(&other.stack);
self.hp = other.hp;
self.heap.truncate(0);
self.heap.extend_from_slice(&other.heap);
}

/// Offset of the heap section
fn heap_offset(&self) -> usize {
MEM_SIZE.saturating_sub(self.heap.len())
Expand Down Expand Up @@ -403,6 +415,58 @@ impl MemoryInstance {
&self.heap
}

/// Diff of from `self` to `new` memory state.
/// Panics if new instance is not possible to reach from the current one,
/// for instance if it has smaller stack or heap allocation.
pub fn diff_patches(&self, new: &MemoryInstance) -> Vec<MemorySliceChange> {
assert!(self.stack.len() <= new.stack.len());
assert!(self.hp >= new.hp);

let mut changes = Vec::new();

let mut current_change: Option<MemorySliceChange> = None;
for (i, new_value) in new.stack.iter().copied().enumerate().take(new.hp) {
if self.stack.get(i).copied().unwrap_or(0) != new_value {
if let Some(change) = current_change.as_mut() {
change.data.push(new_value);
} else {
current_change = Some(MemorySliceChange {
global_start: i,
data: vec![new_value],
});
}
} else if let Some(change) = current_change.take() {
changes.push(change);
}
}
if let Some(change) = current_change.take() {
changes.push(change);
}

let heap_start_diff = self.heap_offset().saturating_sub(new.heap_offset());
for (i, new_value) in new.heap.iter().copied().enumerate() {
let global_i = new.heap_offset() + i;

if self.stack.get(i - heap_start_diff).copied().unwrap_or(0) != new_value {
if let Some(change) = current_change.as_mut() {
change.data.push(new_value);
} else {
current_change = Some(MemorySliceChange {
global_start: global_i,
data: vec![new_value],
});
}
} else if let Some(change) = current_change.take() {
changes.push(change);
}
}
if let Some(change) = current_change.take() {
changes.push(change);
}

changes
}

/// Returns a `MemoryRollbackData` that can be used to achieve the state of the
/// `desired_memory_state` instance.
pub fn collect_rollback_data(
Expand Down Expand Up @@ -499,11 +563,14 @@ fn get_changes(
changes
}

/// Memory change at a specific location.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
struct MemorySliceChange {
global_start: usize,
data: Vec<u8>,
pub struct MemorySliceChange {
/// Start address of the change. Global address.
pub global_start: usize,
/// Data that was changed.
pub data: Vec<u8>,
}

/// The container for the data used to rollback memory changes.
Expand Down
14 changes: 8 additions & 6 deletions fuel-vm/src/interpreter/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ use fuel_asm::Word;
use alloc::vec::Vec;

use super::{
memory::MemoryRollbackData,
memory::MemorySliceChange,
Interpreter,
Memory,
VM_REGISTER_COUNT,
};

/// When to record a new snapshot
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Trigger {
/// Capture state after an instruction adds a new receipt
OnReceipt,
Expand All @@ -39,7 +40,7 @@ pub struct Frame {
#[cfg_attr(feature = "serde", serde(with = "serde_big_array::BigArray"))]
pub registers: [Word; VM_REGISTER_COUNT],
/// Memory delta from the previous snapshot
pub memory_diff: Option<MemoryRollbackData>,
pub memory_diff: Vec<MemorySliceChange>,
/// How many of the receipts have been added by now
pub receipt_count: usize,
}
Expand Down Expand Up @@ -78,10 +79,11 @@ where
let memory_diff = trace
.previous_memory
.as_ref()
.collect_rollback_data(self.memory.as_ref());
if let Some(diff) = memory_diff.as_ref() {
trace.previous_memory.as_mut().rollback(&diff);
}
.diff_patches(self.memory.as_ref());
trace
.previous_memory
.as_mut()
.make_equal(self.memory.as_ref());

trace.frames.push(Frame {
memory_diff,
Expand Down

0 comments on commit 2cd645d

Please sign in to comment.