diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs index 278b22e23..574a11773 100644 --- a/ethcore/src/evm/ext.rs +++ b/ethcore/src/evm/ext.rs @@ -40,9 +40,6 @@ pub enum MessageCallResult { Failed } -/// The trace function callback for VM tracing (*not* transaction tracing - that's different). -pub type VMTraceFunctionBox = Box; - /// Externalities interface for EVMs pub trait Ext { /// Returns a value for given key. @@ -109,6 +106,7 @@ pub trait Ext { /// Increments sstore refunds count by 1. fn inc_sstore_clears(&mut self); - /// Provide a tracer for VM tracing if the VM implementation supports it. - fn vm_tracer(&mut self) -> Option<&mut VMTraceFunctionBox>; + // 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]) {} } diff --git a/ethcore/src/evm/interpreter.rs b/ethcore/src/evm/interpreter.rs index 3fb59374b..d4948919f 100644 --- a/ethcore/src/evm/interpreter.rs +++ b/ethcore/src/evm/interpreter.rs @@ -17,6 +17,7 @@ ///! Rust VM implementation use common::*; +use trace::VMTracer; use super::instructions as instructions; use super::instructions::Instruction; use std::marker::Copy; @@ -69,6 +70,8 @@ trait Stack { fn push(&mut self, elem: T); /// Get number of elements on Stack fn size(&self) -> usize; + /// Returns all data on stack. + fn peek_all(&mut self) -> &[T]; } struct VecStack { @@ -131,6 +134,10 @@ impl Stack for VecStack { fn size(&self) -> usize { self.stack.len() } + + fn peek_all(&mut self) -> &[S] { + &self.stack + } } trait Memory { @@ -297,6 +304,10 @@ impl evm::Evm for Interpreter { // Calculate gas cost 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(¤t_gas, &gas_cost)); mem.expand(mem_size); 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 let result = try!(self.exec_instruction( current_gas, ¶ms, ext, instruction, &mut reader, &mut mem, &mut stack diff --git a/ethcore/src/evm/jit.rs b/ethcore/src/evm/jit.rs index 6a22a1306..694c1668a 100644 --- a/ethcore/src/evm/jit.rs +++ b/ethcore/src/evm/jit.rs @@ -16,6 +16,7 @@ //! Just in time compiler execution environment. use common::*; +use trace::VMTracer; use evmjit; use evm; diff --git a/ethcore/src/evm/mod.rs b/ethcore/src/evm/mod.rs index e72328d92..b7816b99c 100644 --- a/ethcore/src/evm/mod.rs +++ b/ethcore/src/evm/mod.rs @@ -30,6 +30,6 @@ mod jit; mod tests; 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::schedule::Schedule; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 666371770..f2be36df4 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -21,7 +21,7 @@ use engine::*; use evm::{self, Ext, Factory}; use externalities::*; use substate::*; -use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; +use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; use crossbeam; pub use types::executed::{Executed, ExecutionResult}; @@ -82,21 +82,40 @@ impl<'a> Executive<'a> { } /// 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 { - Externalities::new(self.state, self.info, self.engine, self.vm_factory, self.depth, origin_info, substate, output, tracer) + pub fn as_externalities<'_, T, V>( + &'_ 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. pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result { let check = options.check_nonce; match options.tracing { - true => self.transact_with_tracer(t, check, ExecutiveTracer::default()), - false => self.transact_with_tracer(t, check, NoopTracer), + true => match options.vm_tracing { + 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 - pub fn transact_with_tracer(&'a mut self, t: &SignedTransaction, check_nonce: bool, mut tracer: T) -> Result where T: Tracer { + pub fn transact_with_tracer( + &'a mut self, + t: &SignedTransaction, + check_nonce: bool, + mut tracer: T, + mut vm_tracer: V + ) -> Result where T: Tracer, V: VMTracer { let sender = try!(t.sender().map_err(|e| { let message = format!("Transaction malformed: {:?}", e); ExecutionError::TransactionMalformed(message) @@ -156,7 +175,7 @@ impl<'a> Executive<'a> { code: Some(t.data.clone()), 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) => { let params = ActionParams { @@ -172,20 +191,26 @@ impl<'a> Executive<'a> { }; // TODO: move output upstream 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! - 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(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy, tracer: &mut T) - -> evm::Result where T: Tracer { + fn exec_vm( + &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 if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 { let vm_factory = self.vm_factory; - let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer); + let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); return vm_factory.create().exec(params, &mut ext); } @@ -195,7 +220,7 @@ impl<'a> Executive<'a> { // https://github.com/aturon/crossbeam/issues/16 crossbeam::scope(|scope| { let vm_factory = self.vm_factory; - let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer); + let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer); scope.spawn(move || { 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). /// Modifies the substate and the output. /// Returns either gas_left or `evm::Error`. - pub fn call(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef, tracer: &mut T) - -> evm::Result where T: Tracer { + pub fn call( + &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 self.state.snapshot(); @@ -266,16 +297,22 @@ impl<'a> Executive<'a> { let trace_info = tracer.prepare_trace_call(¶ms); let mut trace_output = tracer.prepare_trace_output(); let mut subtracer = tracer.subtracer(); + let gas = params.gas; if params.code.is_some() { // part of substate that may be reverted 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 = { - 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); let traces = subtracer.traces(); @@ -309,8 +346,13 @@ impl<'a> Executive<'a> { /// Creates contract with given contract params. /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// Modifies the substate. - pub fn create(&mut self, params: ActionParams, substate: &mut Substate, tracer: &mut T) -> evm::Result where T: - Tracer { + pub fn create( + &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 self.state.snapshot(); @@ -332,10 +374,14 @@ impl<'a> Executive<'a> { let gas = params.gas; let created = params.address.clone(); + let mut subvmtracer = vm_tracer.prepare_subtrace(¶ms.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed")); + 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 { Ok(gas_left) => tracer.trace_create( trace_info, @@ -353,7 +399,15 @@ impl<'a> Executive<'a> { } /// Finalizes the transaction (does refunds and suicides). - fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result, output: Bytes, trace: Option) -> ExecutionResult { + fn finalize( + &mut self, + t: &SignedTransaction, + substate: Substate, + result: evm::Result, + output: Bytes, + trace: Option, + vm_trace: Option + ) -> ExecutionResult { let schedule = self.engine.schedule(self.info); // refunds from SSTORE nonzero -> zero @@ -396,7 +450,7 @@ impl<'a> Executive<'a> { contracts_created: vec![], output: output, trace: trace, - vm_trace: None, + vm_trace: vm_trace, }) }, _ => { @@ -409,7 +463,7 @@ impl<'a> Executive<'a> { contracts_created: substate.contracts_created, output: output, trace: trace, - vm_trace: None, + vm_trace: vm_trace, }) }, } diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index c78ac09a8..e7a5adb89 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -19,9 +19,9 @@ use common::*; use state::*; use engine::*; use executive::*; -use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory, VMTraceFunctionBox}; +use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; use substate::*; -use trace::Tracer; +use trace::{Tracer, VMTracer}; /// Policy for handling output data on `RETURN` opcode. pub enum OutputPolicy<'a, 'b> { @@ -55,7 +55,7 @@ impl OriginInfo { } /// 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, env_info: &'a EnvInfo, engine: &'a Engine, @@ -66,10 +66,10 @@ pub struct Externalities<'a, T> where T: 'a + Tracer { schedule: Schedule, output: OutputPolicy<'a, 'a>, tracer: &'a mut T, - vm_tracer: Option, + 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))] /// Basic `Externalities` constructor. 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, output: OutputPolicy<'a, 'a>, tracer: &'a mut T, + vm_tracer: &'a mut V, ) -> Self { Externalities { state: state, @@ -93,40 +94,12 @@ impl<'a, T> Externalities<'a, T> where T: 'a + Tracer { schedule: engine.schedule(env_info), output: output, tracer: tracer, - vm_tracer: None, - } - } - - #[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), + vm_tracer: 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 { 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); // 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) => { self.substate.contracts_created.push(address.clone()); 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); - 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), _ => 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(); } - 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)] diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index 41d8c2389..34d8a6936 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -18,13 +18,13 @@ use util::{Bytes, Address, U256}; use action_params::ActionParams; -use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult}; -use trace::Tracer; +use trace::trace::{Trace, Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation}; +use trace::{Tracer, VMTracer}; /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. #[derive(Default)] pub struct ExecutiveTracer { - traces: Vec + traces: Vec, } impl Tracer for ExecutiveTracer { @@ -105,3 +105,35 @@ impl Tracer for ExecutiveTracer { 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 { Some(self.data) } +} diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 21827dd58..ccf9e192e 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -32,8 +32,8 @@ pub use self::config::{Config, Switch}; pub use self::db::TraceDB; pub use self::error::Error; pub use types::trace_types::trace::{Trace, VMTrace}; -pub use self::noop_tracer::NoopTracer; -pub use self::executive_tracer::ExecutiveTracer; +pub use self::noop_tracer::{NoopTracer, NoopVMTracer}; +pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}; pub use types::trace_types::filter::{Filter, AddressesFilter}; pub use self::import::ImportRequest; pub use self::localized::LocalizedTrace; @@ -91,13 +91,16 @@ 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(pc: usize, instruction: u8, gas_cost: &U256, stack: &Vec); + 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 subtracer(&self) -> Self where Self: Sized; + fn prepare_subtrace(&self, code: &Bytes) -> Self where Self: Sized; - /// Consumes self and returns all VM traces. - fn traces(self) -> Vec; + /// Spawn subtracer which will be used to trace deeper levels of execution. + fn done_subtrace(&mut self, sub: Self) where Self: Sized; + + /// Consumes self and returns the VM trace. + fn drain(self) -> Option; } /// `DbExtras` provides an interface to query extra data which is not stored in tracesdb, diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/src/trace/noop_tracer.rs index 2581e692b..08eff3c4e 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/src/trace/noop_tracer.rs @@ -18,8 +18,8 @@ use util::{Bytes, Address, U256}; use action_params::ActionParams; -use trace::Tracer; -use trace::trace::{Trace, Call, Create}; +use trace::{Tracer, VMTracer}; +use trace::trace::{Trace, Call, Create, VMTrace}; /// Nonoperative tracer. Does not trace anything. pub struct NoopTracer; @@ -63,3 +63,20 @@ impl Tracer for NoopTracer { 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 { None } +} diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index 5b5132643..5c5c9af15 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -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. pub struct VMTrace { - /// The number of EVM execution environments active when this action happened; 0 if it's - /// the outer action of the transaction. - pub depth: usize, + /// The step (i.e. index into operations) at which this trace corresponds. + pub parent_step: usize, /// The code to be executed. pub code: Bytes, /// The operations executed. @@ -415,7 +414,7 @@ pub struct VMTrace { impl Encodable for VMTrace { fn rlp_append(&self, s: &mut RlpStream) { s.begin_list(4); - s.append(&self.depth); + s.append(&self.parent_step); s.append(&self.code); s.append(&self.operations); s.append(&self.subs); @@ -426,7 +425,7 @@ impl Decodable for VMTrace { fn decode(decoder: &D) -> Result where D: Decoder { let d = decoder.as_rlp(); let res = VMTrace { - depth: try!(d.val_at(0)), + parent_step: try!(d.val_at(0)), code: try!(d.val_at(1)), operations: try!(d.val_at(2)), subs: try!(d.val_at(3)),