[trace] introduce trace failed to Ext (#11019)

* [trace] add trace_failed to Ext, manage stack of trace data

* [evm] call trace_failed only if self.do_trace

* [evm] add a comment about do_trace set to true

* [vm] improve the doc in trace_prepare_execute

* [trace] add the bounds check back
This commit is contained in:
Andronik Ordian 2019-09-05 16:11:51 +02:00 committed by David
parent 11d3cb3f08
commit 44c00b1f74
5 changed files with 42 additions and 20 deletions

View File

@ -279,6 +279,8 @@ impl<Cost: CostType> Interpreter<Cost> {
cache, params, reader, informant, cache, params, reader, informant,
valid_jump_destinations, gasometer, stack, valid_jump_destinations, gasometer, stack,
done: false, done: false,
// Overridden in `step_inner` based on
// the result of `ext.trace_next_instruction`.
do_trace: true, do_trace: true,
mem: Vec::new(), mem: Vec::new(),
return_data: ReturnData::empty(), return_data: ReturnData::empty(),
@ -353,6 +355,9 @@ impl<Cost: CostType> Interpreter<Cost> {
ext.trace_prepare_execute(self.reader.position - 1, opcode, requirements.gas_cost.as_u256(), Self::mem_written(instruction, &self.stack), Self::store_written(instruction, &self.stack)); ext.trace_prepare_execute(self.reader.position - 1, opcode, requirements.gas_cost.as_u256(), Self::mem_written(instruction, &self.stack), Self::store_written(instruction, &self.stack));
} }
if let Err(e) = self.gasometer.as_mut().expect(GASOMETER_PROOF).verify_gas(&requirements.gas_cost) { if let Err(e) = self.gasometer.as_mut().expect(GASOMETER_PROOF).verify_gas(&requirements.gas_cost) {
if self.do_trace {
ext.trace_failed();
}
return InterpreterResult::Done(Err(e)); return InterpreterResult::Done(Err(e));
} }
self.mem.expand(requirements.memory_required_size); self.mem.expand(requirements.memory_required_size);
@ -366,7 +371,12 @@ impl<Cost: CostType> Interpreter<Cost> {
let result = match self.exec_instruction( let result = match self.exec_instruction(
current_gas, ext, instruction, requirements.provide_gas current_gas, ext, instruction, requirements.provide_gas
) { ) {
Err(x) => return InterpreterResult::Done(Err(x)), Err(x) => {
if self.do_trace {
ext.trace_failed();
}
return InterpreterResult::Done(Err(x));
},
Ok(x) => x, Ok(x) => x,
}; };
evm_debug!({ self.informant.after_instruction(instruction) }); evm_debug!({ self.informant.after_instruction(instruction) });

View File

@ -440,6 +440,10 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost, mem_written, store_written) self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost, mem_written, store_written)
} }
fn trace_failed(&mut self) {
self.vm_tracer.trace_failed();
}
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) { fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) {
self.vm_tracer.trace_executed(gas_used, stack_push, mem) self.vm_tracer.trace_executed(gas_used, stack_push, mem)
} }

View File

@ -16,6 +16,7 @@
//! Simple executive tracer. //! Simple executive tracer.
use std::cmp::min;
use ethereum_types::{U256, Address}; use ethereum_types::{U256, Address};
use vm::{Error as VmError, ActionParams}; use vm::{Error as VmError, ActionParams};
use log::{debug, warn}; use log::{debug, warn};
@ -196,12 +197,16 @@ impl Tracer for ExecutiveTracer {
} }
} }
struct TraceData {
mem_written: Option<(usize, usize)>,
store_written: Option<(U256, U256)>,
}
/// Simple VM tracer. Traces all operations. /// Simple VM tracer. Traces all operations.
pub struct ExecutiveVMTracer { pub struct ExecutiveVMTracer {
data: VMTrace, data: VMTrace,
depth: usize, depth: usize,
last_mem_written: Option<(usize, usize)>, trace_stack: Vec<TraceData>,
last_store_written: Option<(U256, U256)>,
} }
impl ExecutiveVMTracer { impl ExecutiveVMTracer {
@ -215,8 +220,7 @@ impl ExecutiveVMTracer {
subs: vec![], subs: vec![],
}, },
depth: 0, depth: 0,
last_mem_written: None, trace_stack: vec![],
last_store_written: None,
} }
} }
@ -243,30 +247,27 @@ impl VMTracer for ExecutiveVMTracer {
executed: None, executed: None,
}); });
}); });
self.last_mem_written = mem_written; self.trace_stack.push(TraceData { mem_written, store_written });
self.last_store_written = store_written; }
fn trace_failed(&mut self) {
let _ = self.trace_stack.pop().expect("pushed in trace_prepare_execute; qed");
} }
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) { fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) {
let mem_diff = self.last_mem_written.take().map(|(o, s)| { let TraceData { mem_written, store_written } = self.trace_stack.pop().expect("pushed in trace_prepare_execute; qed");
let mem_diff = mem_written.map(|(o, s)| {
if o + s > mem.len() { if o + s > mem.len() {
warn!( warn!(target: "trace", "mem_written is out of bounds");
target: "trace",
"Last mem written is out of bounds {} (mem is {})",
o + s,
mem.len(),
);
(o, &[][..])
} else {
(o, &(mem[o..o+s]))
} }
(o, &mem[min(mem.len(), o)..min(o + s, mem.len())])
}); });
let store_diff = self.last_store_written.take(); let store_diff = store_written;
Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| { Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| {
let ex = VMExecutedOperation { let ex = VMExecutedOperation {
gas_used: gas_used, gas_used: gas_used,
stack_push: stack_push.iter().cloned().collect(), stack_push: stack_push.to_vec(),
mem_diff: mem_diff.map(|(s, r)| MemoryDiff { offset: s, data: r.iter().cloned().collect() }), mem_diff: mem_diff.map(|(s, r)| MemoryDiff { offset: s, data: r.to_vec() }),
store_diff: store_diff.map(|(l, v)| StorageDiff { location: l, value: v }), store_diff: store_diff.map(|(l, v)| StorageDiff { location: l, value: v }),
}; };
trace.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute; trace.operations cannot be empty; qed").executed = Some(ex); trace.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute; trace.operations cannot be empty; qed").executed = Some(ex);

View File

@ -93,6 +93,9 @@ pub trait VMTracer: Send {
/// Trace the preparation to execute a single valid instruction. /// Trace the preparation to execute a single valid instruction.
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, _store_written: Option<(U256, U256)>) {} fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, _store_written: Option<(U256, U256)>) {}
/// Trace the execution failure of a single instruction.
fn trace_failed(&mut self) {}
/// Trace the finalised execution of a single valid instruction. /// Trace the finalised execution of a single valid instruction.
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {} fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {}

View File

@ -164,8 +164,12 @@ pub trait Ext {
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false } fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false }
/// Prepare to trace an operation. Passthrough for the VM trace. /// Prepare to trace an operation. Passthrough for the VM trace.
/// For each call of `trace_prepare_execute` either `trace_failed` or `trace_executed` MUST be called.
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, _store_written: Option<(U256, U256)>) {} fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, _store_written: Option<(U256, U256)>) {}
/// Trace the execution failure of a single instruction.
fn trace_failed(&mut self) {}
/// Trace the finalised execution of a single instruction. /// Trace the finalised execution of a single instruction.
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {} fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem: &[u8]) {}