Rearchitected VM tracing to reflect existing tracing.

Should more or less work now.
This commit is contained in:
Gav Wood 2016-05-28 23:57:16 +02:00
parent d4a06b27ed
commit 86fdcabd0e
10 changed files with 173 additions and 89 deletions

View File

@ -40,9 +40,6 @@ pub enum MessageCallResult {
Failed Failed
} }
/// The trace function callback for VM tracing (*not* transaction tracing - that's different).
pub type VMTraceFunctionBox = Box<FnMut(usize, u8, U256, U256) + Send>;
/// Externalities interface for EVMs /// Externalities interface for EVMs
pub trait Ext { pub trait Ext {
/// Returns a value for given key. /// Returns a value for given key.
@ -109,6 +106,7 @@ pub trait Ext {
/// Increments sstore refunds count by 1. /// Increments sstore refunds count by 1.
fn inc_sstore_clears(&mut self); fn inc_sstore_clears(&mut self);
/// Provide a tracer for VM tracing if the VM implementation supports it. // TODO work out a way of not having this here but go via .
fn vm_tracer(&mut self) -> Option<&mut VMTraceFunctionBox>; /// 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]) {}
} }

View File

@ -17,6 +17,7 @@
///! Rust VM implementation ///! Rust VM implementation
use common::*; use common::*;
use trace::VMTracer;
use super::instructions as instructions; use super::instructions as instructions;
use super::instructions::Instruction; use super::instructions::Instruction;
use std::marker::Copy; use std::marker::Copy;
@ -69,6 +70,8 @@ trait Stack<T> {
fn push(&mut self, elem: T); fn push(&mut self, elem: T);
/// Get number of elements on Stack /// Get number of elements on Stack
fn size(&self) -> usize; fn size(&self) -> usize;
/// Returns all data on stack.
fn peek_all(&mut self) -> &[T];
} }
struct VecStack<S> { struct VecStack<S> {
@ -131,6 +134,10 @@ impl<S : fmt::Display> Stack<S> for VecStack<S> {
fn size(&self) -> usize { fn size(&self) -> usize {
self.stack.len() self.stack.len()
} }
fn peek_all(&mut self) -> &[S] {
&self.stack
}
} }
trait Memory { trait Memory {
@ -297,6 +304,10 @@ impl evm::Evm for Interpreter {
// Calculate gas cost // Calculate gas cost
let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &mut mem, &stack)); 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());
try!(self.verify_gas(&current_gas, &gas_cost)); try!(self.verify_gas(&current_gas, &gas_cost));
mem.expand(mem_size); mem.expand(mem_size);
current_gas = current_gas - gas_cost; //TODO: use operator -= current_gas = current_gas - gas_cost; //TODO: use operator -=
@ -311,12 +322,6 @@ impl evm::Evm for Interpreter {
); );
}); });
// Call trace
// TODO: allow to be disabled at build time for max speed
if let Some(ref mut trace_instruction) = ext.vm_tracer() {
(*trace_instruction.deref_mut())(reader.position, instruction, gas_cost, current_gas);
}
// Execute instruction // Execute instruction
let result = try!(self.exec_instruction( let result = try!(self.exec_instruction(
current_gas, &params, ext, instruction, &mut reader, &mut mem, &mut stack current_gas, &params, ext, instruction, &mut reader, &mut mem, &mut stack

View File

@ -16,6 +16,7 @@
//! Just in time compiler execution environment. //! Just in time compiler execution environment.
use common::*; use common::*;
use trace::VMTracer;
use evmjit; use evmjit;
use evm; use evm;

View File

@ -30,6 +30,6 @@ mod jit;
mod tests; mod tests;
pub use self::evm::{Evm, Error, Result}; pub use self::evm::{Evm, Error, Result};
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult, VMTraceFunctionBox}; pub use self::ext::{Ext, ContractCreateResult, MessageCallResult};
pub use self::factory::{Factory, VMType}; pub use self::factory::{Factory, VMType};
pub use self::schedule::Schedule; pub use self::schedule::Schedule;

View File

@ -21,7 +21,7 @@ use engine::*;
use evm::{self, Ext, Factory}; use evm::{self, Ext, Factory};
use externalities::*; use externalities::*;
use substate::*; use substate::*;
use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
use crossbeam; use crossbeam;
pub use types::executed::{Executed, ExecutionResult}; pub use types::executed::{Executed, ExecutionResult};
@ -82,21 +82,40 @@ impl<'a> Executive<'a> {
} }
/// Creates `Externalities` from `Executive`. /// Creates `Externalities` from `Executive`.
pub fn as_externalities<'_, T>(&'_ mut self, origin_info: OriginInfo, substate: &'_ mut Substate, output: OutputPolicy<'_, '_>, tracer: &'_ mut T) -> Externalities<'_, T> where T: Tracer { pub fn as_externalities<'_, T, V>(
Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer) &'_ mut self,
origin_info: OriginInfo,
substate: &'_ mut Substate,
output: OutputPolicy<'_, '_>,
tracer: &'_ mut T,
vm_tracer: &'_ mut V
) -> Externalities<'_, T, V> where T: Tracer, V: VMTracer {
Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer, vm_tracer)
} }
/// This function should be used to execute transaction. /// This function should be used to execute transaction.
pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> { pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result<Executed, ExecutionError> {
let check = options.check_nonce; let check = options.check_nonce;
match options.tracing { match options.tracing {
true => self.transact_with_tracer(t, check, ExecutiveTracer::default()), true => match options.vm_tracing {
false => self.transact_with_tracer(t, check, NoopTracer), true => self.transact_with_tracer(t, check, ExecutiveTracer::default(), ExecutiveVMTracer::default()),
false => self.transact_with_tracer(t, check, ExecutiveTracer::default(), NoopVMTracer),
},
false => match options.vm_tracing {
true => self.transact_with_tracer(t, check, NoopTracer, ExecutiveVMTracer::default()),
false => self.transact_with_tracer(t, check, NoopTracer, NoopVMTracer),
},
} }
} }
/// Execute transaction/call with tracing enabled /// Execute transaction/call with tracing enabled
pub fn transact_with_tracer<T>(&'a mut self, t: &SignedTransaction, check_nonce: bool, mut tracer: T) -> Result<Executed, ExecutionError> where T: Tracer { pub fn transact_with_tracer<T, V>(
&'a mut self,
t: &SignedTransaction,
check_nonce: bool,
mut tracer: T,
mut vm_tracer: V
) -> Result<Executed, ExecutionError> where T: Tracer, V: VMTracer {
let sender = try!(t.sender().map_err(|e| { let sender = try!(t.sender().map_err(|e| {
let message = format!("Transaction malformed: {:?}", e); let message = format!("Transaction malformed: {:?}", e);
ExecutionError::TransactionMalformed(message) ExecutionError::TransactionMalformed(message)
@ -156,7 +175,7 @@ impl<'a> Executive<'a> {
code: Some(t.data.clone()), code: Some(t.data.clone()),
data: None, data: None,
}; };
(self.create(params, &mut substate, &mut tracer), vec![]) (self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![])
}, },
Action::Call(ref address) => { Action::Call(ref address) => {
let params = ActionParams { let params = ActionParams {
@ -172,20 +191,26 @@ impl<'a> Executive<'a> {
}; };
// TODO: move output upstream // TODO: move output upstream
let mut out = vec![]; let mut out = vec![];
(self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer), out) (self.call(params, &mut substate, BytesRef::Flexible(&mut out), &mut tracer, &mut vm_tracer), out)
} }
}; };
// finalize here! // finalize here!
Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop()))) Ok(try!(self.finalize(t, substate, gas_left, output, tracer.traces().pop(), vm_tracer.drain())))
} }
fn exec_vm<T>(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy, tracer: &mut T) fn exec_vm<T, V>(
-> evm::Result where T: Tracer { &mut self,
params: ActionParams,
unconfirmed_substate: &mut Substate,
output_policy: OutputPolicy,
tracer: &mut T,
vm_tracer: &mut V
) -> evm::Result where T: Tracer, V: VMTracer {
// Ordinary execution - keep VM in same thread // Ordinary execution - keep VM in same thread
if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 { if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 {
let vm_factory = self.vm_factory; let vm_factory = self.vm_factory;
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer); let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer);
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
return vm_factory.create().exec(params, &mut ext); return vm_factory.create().exec(params, &mut ext);
} }
@ -195,7 +220,7 @@ impl<'a> Executive<'a> {
// https://github.com/aturon/crossbeam/issues/16 // https://github.com/aturon/crossbeam/issues/16
crossbeam::scope(|scope| { crossbeam::scope(|scope| {
let vm_factory = self.vm_factory; let vm_factory = self.vm_factory;
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer); let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer);
scope.spawn(move || { scope.spawn(move || {
vm_factory.create().exec(params, &mut ext) vm_factory.create().exec(params, &mut ext)
@ -207,8 +232,14 @@ impl<'a> Executive<'a> {
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate and the output. /// Modifies the substate and the output.
/// Returns either gas_left or `evm::Error`. /// Returns either gas_left or `evm::Error`.
pub fn call<T>(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef, tracer: &mut T) pub fn call<T, V>(
-> evm::Result where T: Tracer { &mut self,
params: ActionParams,
substate: &mut Substate,
mut output: BytesRef,
tracer: &mut T,
vm_tracer: &mut V
) -> evm::Result where T: Tracer, V: VMTracer {
// backup used in case of running out of gas // backup used in case of running out of gas
self.state.snapshot(); self.state.snapshot();
@ -266,16 +297,22 @@ impl<'a> Executive<'a> {
let trace_info = tracer.prepare_trace_call(&params); let trace_info = tracer.prepare_trace_call(&params);
let mut trace_output = tracer.prepare_trace_output(); let mut trace_output = tracer.prepare_trace_output();
let mut subtracer = tracer.subtracer(); let mut subtracer = tracer.subtracer();
let gas = params.gas; let gas = params.gas;
if params.code.is_some() { if params.code.is_some() {
// part of substate that may be reverted // part of substate that may be reverted
let mut unconfirmed_substate = Substate::new(); let mut unconfirmed_substate = Substate::new();
// TODO: make ActionParams pass by ref then avoid copy altogether.
let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("scope is protected by params.code.is_some condition"));
let res = { let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer) self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
}; };
vm_tracer.done_subtrace(subvmtracer);
trace!(target: "executive", "res={:?}", res); trace!(target: "executive", "res={:?}", res);
let traces = subtracer.traces(); let traces = subtracer.traces();
@ -309,8 +346,13 @@ impl<'a> Executive<'a> {
/// Creates contract with given contract params. /// Creates contract with given contract params.
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate. /// Modifies the substate.
pub fn create<T>(&mut self, params: ActionParams, substate: &mut Substate, tracer: &mut T) -> evm::Result where T: pub fn create<T, V>(
Tracer { &mut self,
params: ActionParams,
substate: &mut Substate,
tracer: &mut T,
vm_tracer: &mut V
) -> evm::Result where T: Tracer, V: VMTracer {
// backup used in case of running out of gas // backup used in case of running out of gas
self.state.snapshot(); self.state.snapshot();
@ -332,10 +374,14 @@ impl<'a> Executive<'a> {
let gas = params.gas; let gas = params.gas;
let created = params.address.clone(); let created = params.address.clone();
let mut subvmtracer = vm_tracer.prepare_subtrace(&params.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed"));
let res = { let res = {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer) self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
}; };
vm_tracer.done_subtrace(subvmtracer);
match res { match res {
Ok(gas_left) => tracer.trace_create( Ok(gas_left) => tracer.trace_create(
trace_info, trace_info,
@ -353,7 +399,15 @@ impl<'a> Executive<'a> {
} }
/// Finalizes the transaction (does refunds and suicides). /// Finalizes the transaction (does refunds and suicides).
fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result, output: Bytes, trace: Option<Trace>) -> ExecutionResult { fn finalize(
&mut self,
t: &SignedTransaction,
substate: Substate,
result: evm::Result,
output: Bytes,
trace: Option<Trace>,
vm_trace: Option<VMTrace>
) -> ExecutionResult {
let schedule = self.engine.schedule(self.info); let schedule = self.engine.schedule(self.info);
// refunds from SSTORE nonzero -> zero // refunds from SSTORE nonzero -> zero
@ -396,7 +450,7 @@ impl<'a> Executive<'a> {
contracts_created: vec![], contracts_created: vec![],
output: output, output: output,
trace: trace, trace: trace,
vm_trace: None, vm_trace: vm_trace,
}) })
}, },
_ => { _ => {
@ -409,7 +463,7 @@ impl<'a> Executive<'a> {
contracts_created: substate.contracts_created, contracts_created: substate.contracts_created,
output: output, output: output,
trace: trace, trace: trace,
vm_trace: None, vm_trace: vm_trace,
}) })
}, },
} }

View File

@ -19,9 +19,9 @@ use common::*;
use state::*; use state::*;
use engine::*; use engine::*;
use executive::*; use executive::*;
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory, VMTraceFunctionBox}; use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory};
use substate::*; use substate::*;
use trace::Tracer; use trace::{Tracer, VMTracer};
/// Policy for handling output data on `RETURN` opcode. /// Policy for handling output data on `RETURN` opcode.
pub enum OutputPolicy<'a, 'b> { pub enum OutputPolicy<'a, 'b> {
@ -55,7 +55,7 @@ impl OriginInfo {
} }
/// Implementation of evm Externalities. /// Implementation of evm Externalities.
pub struct Externalities<'a, T> where T: 'a + Tracer { pub struct Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
state: &'a mut State, state: &'a mut State,
env_info: &'a EnvInfo, env_info: &'a EnvInfo,
engine: &'a Engine, engine: &'a Engine,
@ -66,10 +66,10 @@ pub struct Externalities<'a, T> where T: 'a + Tracer {
schedule: Schedule, schedule: Schedule,
output: OutputPolicy<'a, 'a>, output: OutputPolicy<'a, 'a>,
tracer: &'a mut T, tracer: &'a mut T,
vm_tracer: Option<VMTraceFunctionBox>, vm_tracer: &'a mut V,
} }
impl<'a, T> Externalities<'a, T> where T: 'a + Tracer { impl<'a, T, V> Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
#[cfg_attr(feature="dev", allow(too_many_arguments))] #[cfg_attr(feature="dev", allow(too_many_arguments))]
/// Basic `Externalities` constructor. /// Basic `Externalities` constructor.
pub fn new(state: &'a mut State, pub fn new(state: &'a mut State,
@ -81,6 +81,7 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer {
substate: &'a mut Substate, substate: &'a mut Substate,
output: OutputPolicy<'a, 'a>, output: OutputPolicy<'a, 'a>,
tracer: &'a mut T, tracer: &'a mut T,
vm_tracer: &'a mut V,
) -> Self { ) -> Self {
Externalities { Externalities {
state: state, state: state,
@ -93,40 +94,12 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer {
schedule: engine.schedule(env_info), schedule: engine.schedule(env_info),
output: output, output: output,
tracer: tracer, tracer: tracer,
vm_tracer: None, vm_tracer: vm_tracer,
}
}
#[cfg_attr(feature="dev", allow(too_many_arguments))]
/// Basic `Externalities` constructor.
pub fn with_vm_tracer(state: &'a mut State,
env_info: &'a EnvInfo,
engine: &'a Engine,
vm_factory: &'a Factory,
depth: usize,
origin_info: OriginInfo,
substate: &'a mut Substate,
output: OutputPolicy<'a, 'a>,
tracer: &'a mut T,
vm_tracer: VMTraceFunctionBox,
) -> Self {
Externalities {
state: state,
env_info: env_info,
engine: engine,
vm_factory: vm_factory,
depth: depth,
origin_info: origin_info,
substate: substate,
schedule: engine.schedule(env_info),
output: output,
tracer: tracer,
vm_tracer: Some(vm_tracer),
} }
} }
} }
impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer { impl<'a, T, V> Ext for Externalities<'a, T, V> where T: 'a + Tracer, V: 'a + VMTracer {
fn storage_at(&self, key: &H256) -> H256 { fn storage_at(&self, key: &H256) -> H256 {
self.state.storage_at(&self.origin_info.address, key) self.state.storage_at(&self.origin_info.address, key)
} }
@ -181,7 +154,7 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth); let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth);
// TODO: handle internal error separately // TODO: handle internal error separately
match ex.create(params, self.substate, self.tracer) { match ex.create(params, self.substate, self.tracer, self.vm_tracer) {
Ok(gas_left) => { Ok(gas_left) => {
self.substate.contracts_created.push(address.clone()); self.substate.contracts_created.push(address.clone());
ContractCreateResult::Created(address, gas_left) ContractCreateResult::Created(address, gas_left)
@ -219,7 +192,7 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth); let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth);
match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer) { match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) {
Ok(gas_left) => MessageCallResult::Success(gas_left), Ok(gas_left) => MessageCallResult::Success(gas_left),
_ => MessageCallResult::Failed _ => MessageCallResult::Failed
} }
@ -316,7 +289,9 @@ impl<'a, T> Ext for Externalities<'a, T> where T: 'a + Tracer {
self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one(); self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one();
} }
fn vm_tracer(&mut self) -> Option<&mut VMTraceFunctionBox> { self.vm_tracer.as_mut() } 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);
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -18,13 +18,13 @@
use util::{Bytes, Address, U256}; use util::{Bytes, Address, U256};
use action_params::ActionParams; use action_params::ActionParams;
use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult}; use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation};
use trace::Tracer; use trace::{Tracer, VMTracer};
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
#[derive(Default)] #[derive(Default)]
pub struct ExecutiveTracer { pub struct ExecutiveTracer {
traces: Vec<Trace> traces: Vec<Trace>,
} }
impl Tracer for ExecutiveTracer { impl Tracer for ExecutiveTracer {
@ -105,3 +105,35 @@ impl Tracer for ExecutiveTracer {
self.traces self.traces
} }
} }
/// Simple VM tracer. Traces all operations. Ignores delegatecalls.
#[derive(Default)]
pub struct ExecutiveVMTracer {
data: VMTrace,
}
impl VMTracer for ExecutiveVMTracer {
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256, stack: &[U256]) {
self.data.operations.push(VMOperation {
pc: pc,
instruction: instruction,
gas_cost: gas_cost.clone(),
stack: stack.iter().cloned().collect(),
})
}
fn prepare_subtrace(&self, code: &Bytes) -> Self {
ExecutiveVMTracer { data: VMTrace {
parent_step: self.data.operations.len(),
code: code.clone(),
operations: vec![],
subs: vec![],
}}
}
fn done_subtrace(&mut self, sub: Self) {
self.data.subs.push(sub.data);
}
fn drain(self) -> Option<VMTrace> { Some(self.data) }
}

View File

@ -32,8 +32,8 @@ pub use self::config::{Config, Switch};
pub use self::db::TraceDB; pub use self::db::TraceDB;
pub use self::error::Error; pub use self::error::Error;
pub use types::trace_types::trace::{Trace, VMTrace}; pub use types::trace_types::trace::{Trace, VMTrace};
pub use self::noop_tracer::NoopTracer; pub use self::noop_tracer::{NoopTracer, NoopVMTracer};
pub use self::executive_tracer::ExecutiveTracer; pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer};
pub use types::trace_types::filter::{Filter, AddressesFilter}; pub use types::trace_types::filter::{Filter, AddressesFilter};
pub use self::import::ImportRequest; pub use self::import::ImportRequest;
pub use self::localized::LocalizedTrace; pub use self::localized::LocalizedTrace;
@ -91,13 +91,16 @@ pub trait Tracer: Send {
/// Used by executive to build VM traces. /// Used by executive to build VM traces.
pub trait VMTracer: Send { pub trait VMTracer: Send {
/// Trace the preparation to execute a single instruction. /// Trace the preparation to execute a single instruction.
fn trace_prepare_execute(pc: usize, instruction: u8, gas_cost: &U256, stack: &Vec<U256>); fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256, stack: &[U256]);
/// Spawn subtracer which will be used to trace deeper levels of execution. /// Spawn subtracer which will be used to trace deeper levels of execution.
fn subtracer(&self) -> Self where Self: Sized; fn prepare_subtrace(&self, code: &Bytes) -> Self where Self: Sized;
/// Consumes self and returns all VM traces. /// Spawn subtracer which will be used to trace deeper levels of execution.
fn traces(self) -> Vec<VMTrace>; fn done_subtrace(&mut self, sub: Self) where Self: Sized;
/// Consumes self and returns the VM trace.
fn drain(self) -> Option<VMTrace>;
} }
/// `DbExtras` provides an interface to query extra data which is not stored in tracesdb, /// `DbExtras` provides an interface to query extra data which is not stored in tracesdb,

View File

@ -18,8 +18,8 @@
use util::{Bytes, Address, U256}; use util::{Bytes, Address, U256};
use action_params::ActionParams; use action_params::ActionParams;
use trace::Tracer; use trace::{Tracer, VMTracer};
use trace::trace::{Trace, Call, Create}; use trace::trace::{Trace, Call, Create, VMTrace};
/// Nonoperative tracer. Does not trace anything. /// Nonoperative tracer. Does not trace anything.
pub struct NoopTracer; pub struct NoopTracer;
@ -63,3 +63,20 @@ impl Tracer for NoopTracer {
vec![] vec![]
} }
} }
/// Nonoperative VM tracer. Does not trace anything.
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]) {}
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn prepare_subtrace(&self, _code: &Bytes) -> Self { NoopVMTracer }
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn done_subtrace(&mut self, _sub: Self) {}
/// Consumes self and returns all VM traces.
fn drain(self) -> Option<VMTrace> { None }
}

View File

@ -397,12 +397,11 @@ impl Decodable for VMOperation {
} }
} }
#[derive(Debug, Clone, PartialEq, Binary)] #[derive(Debug, Clone, PartialEq, Binary, Default)]
/// A record of a full VM trace for a CALL/CREATE. /// A record of a full VM trace for a CALL/CREATE.
pub struct VMTrace { pub struct VMTrace {
/// The number of EVM execution environments active when this action happened; 0 if it's /// The step (i.e. index into operations) at which this trace corresponds.
/// the outer action of the transaction. pub parent_step: usize,
pub depth: usize,
/// The code to be executed. /// The code to be executed.
pub code: Bytes, pub code: Bytes,
/// The operations executed. /// The operations executed.
@ -415,7 +414,7 @@ pub struct VMTrace {
impl Encodable for VMTrace { impl Encodable for VMTrace {
fn rlp_append(&self, s: &mut RlpStream) { fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4); s.begin_list(4);
s.append(&self.depth); s.append(&self.parent_step);
s.append(&self.code); s.append(&self.code);
s.append(&self.operations); s.append(&self.operations);
s.append(&self.subs); s.append(&self.subs);
@ -426,7 +425,7 @@ impl Decodable for VMTrace {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder { fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp(); let d = decoder.as_rlp();
let res = VMTrace { let res = VMTrace {
depth: try!(d.val_at(0)), parent_step: try!(d.val_at(0)),
code: try!(d.val_at(1)), code: try!(d.val_at(1)),
operations: try!(d.val_at(2)), operations: try!(d.val_at(2)),
subs: try!(d.val_at(3)), subs: try!(d.val_at(3)),