Added VM trace information for post-execution stuff.
This commit is contained in:
@@ -106,7 +106,9 @@ pub trait Ext {
|
||||
/// Increments sstore refunds count by 1.
|
||||
fn inc_sstore_clears(&mut self);
|
||||
|
||||
// TODO work out a way of not having this here but go via .
|
||||
/// Prepare to trace an operation. Passthrough for the VM trace.
|
||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256, _stack: &[U256]) {}
|
||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
|
||||
|
||||
/// Trace the finalised execution of a single instruction.
|
||||
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
use common::*;
|
||||
use trace::VMTracer;
|
||||
use super::instructions as instructions;
|
||||
use super::instructions::Instruction;
|
||||
use super::instructions::{Instruction, get_info};
|
||||
use std::marker::Copy;
|
||||
use evm::{self, MessageCallResult, ContractCreateResult};
|
||||
|
||||
@@ -71,7 +71,7 @@ trait Stack<T> {
|
||||
/// Get number of elements on Stack
|
||||
fn size(&self) -> usize;
|
||||
/// Returns all data on stack.
|
||||
fn peek_all(&mut self) -> &[T];
|
||||
fn peek_top(&mut self, no_of_elems: usize) -> &[T];
|
||||
}
|
||||
|
||||
struct VecStack<S> {
|
||||
@@ -135,8 +135,9 @@ impl<S : fmt::Display> Stack<S> for VecStack<S> {
|
||||
self.stack.len()
|
||||
}
|
||||
|
||||
fn peek_all(&mut self) -> &[S] {
|
||||
&self.stack
|
||||
fn peek_top(&mut self, no_from_top: usize) -> &[S] {
|
||||
assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist.");
|
||||
&self.stack[self.stack.len() - no_from_top .. self.stack.len()]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +307,7 @@ impl evm::Evm for Interpreter {
|
||||
let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &mut mem, &stack));
|
||||
|
||||
// TODO: make compile-time removable if too much of a performance hit.
|
||||
ext.trace_prepare_execute(reader.position, instruction, &gas_cost, stack.peek_all());
|
||||
let trace_executed = ext.trace_prepare_execute(reader.position, instruction, &gas_cost);
|
||||
|
||||
try!(self.verify_gas(¤t_gas, &gas_cost));
|
||||
mem.expand(mem_size);
|
||||
@@ -322,10 +323,19 @@ impl evm::Evm for Interpreter {
|
||||
);
|
||||
});
|
||||
|
||||
let (mem_written, store_written) = match trace_executed {
|
||||
true => (Self::mem_written(instruction, &stack), Self::store_written(instruction, &stack)),
|
||||
false => (None, None),
|
||||
};
|
||||
|
||||
// Execute instruction
|
||||
let result = try!(self.exec_instruction(
|
||||
current_gas, ¶ms, ext, instruction, &mut reader, &mut mem, &mut stack
|
||||
));
|
||||
));
|
||||
|
||||
if trace_executed {
|
||||
ext.trace_executed(current_gas, stack.peek_top(get_info(instruction).ret), mem_written.map(|(o, s)| (o, &(mem[o..(o + s)]))), store_written);
|
||||
}
|
||||
|
||||
// Advance
|
||||
match result {
|
||||
@@ -496,6 +506,31 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
fn mem_written(
|
||||
instruction: Instruction,
|
||||
stack: &Stack<U256>
|
||||
) -> Option<(usize, usize)> {
|
||||
match instruction {
|
||||
instructions::MSTORE | instructions::MLOAD => Some((stack.peek(0).low_u64() as usize, 32)),
|
||||
instructions::MSTORE8 => Some((stack.peek(0).low_u64() as usize, 1)),
|
||||
instructions::CALLDATACOPY | instructions::CODECOPY => Some((stack.peek(0).low_u64() as usize, stack.peek(2).low_u64() as usize)),
|
||||
instructions::EXTCODECOPY => Some((stack.peek(1).low_u64() as usize, stack.peek(3).low_u64() as usize)),
|
||||
instructions::CALL | instructions::CALLCODE => Some((stack.peek(5).low_u64() as usize, stack.peek(6).low_u64() as usize)),
|
||||
instructions::DELEGATECALL => Some((stack.peek(4).low_u64() as usize, stack.peek(5).low_u64() as usize)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn store_written(
|
||||
instruction: Instruction,
|
||||
stack: &Stack<U256>
|
||||
) -> Option<(U256, U256)> {
|
||||
match instruction {
|
||||
instructions::SSTORE => Some((stack.peek(0).clone(), stack.peek(1).clone())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> Result<(U256, usize), evm::Error> {
|
||||
let gas_for_mem = |mem_size: U256| {
|
||||
let s = mem_size >> 5;
|
||||
|
||||
@@ -289,8 +289,12 @@ impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMT
|
||||
self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one();
|
||||
}
|
||||
|
||||
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256, stack: &[U256]) {
|
||||
self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost, stack);
|
||||
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool {
|
||||
self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost)
|
||||
}
|
||||
|
||||
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
|
||||
self.vm_tracer.trace_executed(gas_used, stack_push, mem_diff, store_diff)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
use util::{Bytes, Address, U256};
|
||||
use action_params::ActionParams;
|
||||
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation};
|
||||
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff};
|
||||
use trace::{Tracer, VMTracer};
|
||||
|
||||
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
|
||||
@@ -113,13 +113,24 @@ pub struct ExecutiveVMTracer {
|
||||
}
|
||||
|
||||
impl VMTracer for ExecutiveVMTracer {
|
||||
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256, stack: &[U256]) {
|
||||
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool {
|
||||
self.data.operations.push(VMOperation {
|
||||
pc: pc,
|
||||
instruction: instruction,
|
||||
gas_cost: gas_cost.clone(),
|
||||
stack: stack.iter().cloned().collect(),
|
||||
})
|
||||
executed: None,
|
||||
});
|
||||
true
|
||||
}
|
||||
|
||||
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
|
||||
let ex = VMExecutedOperation {
|
||||
gas_used: gas_used,
|
||||
stack_push: stack_push.iter().cloned().collect(),
|
||||
mem_diff: mem_diff.map(|(s, r)| MemoryDiff{ offset: s, data: r.iter().cloned().collect() }),
|
||||
store_diff: store_diff.map(|(l, v)| StorageDiff{ location: l, value: v }),
|
||||
};
|
||||
self.data.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute").executed = Some(ex);
|
||||
}
|
||||
|
||||
fn prepare_subtrace(&self, code: &Bytes) -> Self {
|
||||
|
||||
@@ -91,7 +91,11 @@ pub trait Tracer: Send {
|
||||
/// Used by executive to build VM traces.
|
||||
pub trait VMTracer: Send {
|
||||
/// Trace the preparation to execute a single instruction.
|
||||
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256, stack: &[U256]);
|
||||
/// @returns true if `trace_executed` should be called.
|
||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
|
||||
|
||||
/// Trace the finalised execution of a single instruction.
|
||||
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
||||
|
||||
/// Spawn subtracer which will be used to trace deeper levels of execution.
|
||||
fn prepare_subtrace(&self, code: &Bytes) -> Self where Self: Sized;
|
||||
|
||||
@@ -69,7 +69,10 @@ pub struct NoopVMTracer;
|
||||
|
||||
impl VMTracer for NoopVMTracer {
|
||||
/// Trace the preparation to execute a single instruction.
|
||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256, _stack: &[U256]) {}
|
||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
|
||||
|
||||
/// Trace the finalised execution of a single instruction.
|
||||
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
||||
|
||||
/// Spawn subtracer which will be used to trace deeper levels of execution.
|
||||
fn prepare_subtrace(&self, _code: &Bytes) -> Self { NoopVMTracer }
|
||||
|
||||
@@ -349,16 +349,94 @@ impl Trace {
|
||||
}
|
||||
}
|
||||
|
||||
/*pub struct VMExecutedOperation {
|
||||
#[derive(Debug, Clone, PartialEq, Binary)]
|
||||
/// A diff of some chunk of memory.
|
||||
pub struct MemoryDiff {
|
||||
/// Offset into memory the change begins.
|
||||
pub offset: usize,
|
||||
/// The changed data.
|
||||
pub data: Bytes,
|
||||
}
|
||||
|
||||
impl Encodable for MemoryDiff {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(2);
|
||||
s.append(&self.offset);
|
||||
s.append(&self.data);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for MemoryDiff {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
Ok(MemoryDiff {
|
||||
offset: try!(d.val_at(0)),
|
||||
data: try!(d.val_at(1)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Binary)]
|
||||
/// A diff of some storage value.
|
||||
pub struct StorageDiff {
|
||||
/// Which key in storage is changed.
|
||||
pub location: U256,
|
||||
/// What the value has been changed to.
|
||||
pub value: U256,
|
||||
}
|
||||
|
||||
impl Encodable for StorageDiff {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(2);
|
||||
s.append(&self.location);
|
||||
s.append(&self.value);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for StorageDiff {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
Ok(StorageDiff {
|
||||
location: try!(d.val_at(0)),
|
||||
value: try!(d.val_at(1)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Binary)]
|
||||
/// A record of an executed VM operation.
|
||||
pub struct VMExecutedOperation {
|
||||
/// The total gas used.
|
||||
pub gas_used: U256,
|
||||
/// Altered storage value.
|
||||
pub storage_diff: Option<(U256, U256)>,
|
||||
/// If altered, the new memory image.
|
||||
pub new_memory: Option<Bytes>,
|
||||
}*/
|
||||
/// Information concerning the execution of the operation.
|
||||
// pub executed: Option<VMExecutedOperation>,
|
||||
/// The stack item placed, if any.
|
||||
pub stack_push: Vec<U256>,
|
||||
/// If altered, the memory delta, given as (offset, new bytes).
|
||||
pub mem_diff: Option<MemoryDiff>,
|
||||
/// The altered storage value.
|
||||
pub store_diff: Option<StorageDiff>,
|
||||
}
|
||||
|
||||
impl Encodable for VMExecutedOperation {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(4);
|
||||
s.append(&self.gas_used);
|
||||
s.append(&self.stack_push);
|
||||
s.append(&self.mem_diff);
|
||||
s.append(&self.store_diff);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for VMExecutedOperation {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
let d = decoder.as_rlp();
|
||||
Ok(VMExecutedOperation {
|
||||
gas_used: try!(d.val_at(0)),
|
||||
stack_push: try!(d.val_at(1)),
|
||||
mem_diff: try!(d.val_at(2)),
|
||||
store_diff: try!(d.val_at(3)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Binary)]
|
||||
/// A record of the execution of a single VM operation.
|
||||
@@ -369,8 +447,8 @@ pub struct VMOperation {
|
||||
pub instruction: u8,
|
||||
/// The gas cost for this instruction.
|
||||
pub gas_cost: U256,
|
||||
/// The stack.
|
||||
pub stack: Vec<U256>,
|
||||
/// Information concerning the execution of the operation.
|
||||
pub executed: Option<VMExecutedOperation>,
|
||||
}
|
||||
|
||||
impl Encodable for VMOperation {
|
||||
@@ -379,7 +457,7 @@ impl Encodable for VMOperation {
|
||||
s.append(&self.pc);
|
||||
s.append(&self.instruction);
|
||||
s.append(&self.gas_cost);
|
||||
s.append(&self.stack);
|
||||
s.append(&self.executed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,7 +468,7 @@ impl Decodable for VMOperation {
|
||||
pc: try!(d.val_at(0)),
|
||||
instruction: try!(d.val_at(1)),
|
||||
gas_cost: try!(d.val_at(2)),
|
||||
stack: try!(d.val_at(3)),
|
||||
executed: try!(d.val_at(3)),
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
|
||||
Reference in New Issue
Block a user