Rearchitected VM tracing to reflect existing tracing.
Should more or less work now.
This commit is contained in:
parent
d4a06b27ed
commit
86fdcabd0e
@ -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]) {}
|
||||||
}
|
}
|
||||||
|
@ -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(¤t_gas, &gas_cost));
|
try!(self.verify_gas(¤t_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, ¶ms, ext, instruction, &mut reader, &mut mem, &mut stack
|
current_gas, ¶ms, ext, instruction, &mut reader, &mut mem, &mut stack
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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(¶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);
|
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(¶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 || {
|
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(¶ms);
|
let trace_info = tracer.prepare_trace_call(¶ms);
|
||||||
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(¶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 = {
|
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,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -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)]
|
||||||
|
@ -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) }
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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 }
|
||||||
|
}
|
||||||
|
@ -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)),
|
||||||
|
Loading…
Reference in New Issue
Block a user