Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Improve VM executor stack size estimation rules #8439

Merged
merged 4 commits into from
Apr 20, 2018
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions ethcore/src/executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,21 @@ use transaction::{Action, SignedTransaction};
use crossbeam;
pub use executed::{Executed, ExecutionResult};

/// Roughly estimate what stack size each level of evm depth will use
/// TODO [todr] We probably need some more sophisticated calculations here (limit on my machine 132)
/// Maybe something like here: `https://github.com/ethereum/libethereum/blob/4db169b8504f2b87f7d5a481819cfb959fc65f6c/libethereum/ExtVM.cpp`
const STACK_SIZE_PER_DEPTH: usize = 24*1024;
#[cfg(debug_assertions)]
/// Roughly estimate what stack size each level of evm depth will use. (Debug build)
const STACK_SIZE_PER_DEPTH: usize = 128 * 1024;

#[cfg(not(debug_assertions))]
/// Roughly estimate what stack size each level of evm depth will use.
const STACK_SIZE_PER_DEPTH: usize = 24 * 1024;

#[cfg(debug_assertions)]
/// Entry stack overhead prior to execution. (Debug build)
const STACK_SIZE_ENTRY_OVERHEAD: usize = 100 * 1024;

#[cfg(not(debug_assertions))]
/// Entry stack overhead prior to execution.
const STACK_SIZE_ENTRY_OVERHEAD: usize = 20 * 1024;

/// Returns new address created from address, nonce, and code hash
pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address, nonce: &U256, code: &[u8]) -> (Address, Option<H256>) {
Expand Down Expand Up @@ -332,30 +343,28 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
tracer: &mut T,
vm_tracer: &mut V
) -> vm::Result<FinalizationResult> where T: Tracer, V: VMTracer {

let depth_threshold = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get() / STACK_SIZE_PER_DEPTH);
let local_stack_size = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get());
let depth_threshold = (local_stack_size - STACK_SIZE_ENTRY_OVERHEAD) / STACK_SIZE_PER_DEPTH;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Watch out for overflow when subtracting

let static_call = params.call_type == CallType::StaticCall;

// Ordinary execution - keep VM in same thread
if (self.depth + 1) % depth_threshold != 0 {
if self.depth != depth_threshold {
let vm_factory = self.state.vm_factory();
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer, static_call);
trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call);
let mut vm = vm_factory.create(&params, &schedule);
return vm.exec(params, &mut ext).finalize(ext);
}

// Start in new thread to reset stack
// TODO [todr] No thread builder yet, so we need to reset once for a while
// https://github.com/aturon/crossbeam/issues/16
// Start in new thread with stack size needed up to max depth
crossbeam::scope(|scope| {
let vm_factory = self.state.vm_factory();
let mut ext = self.as_externalities(OriginInfo::from(&params), unconfirmed_substate, output_policy, tracer, vm_tracer, static_call);

scope.spawn(move || {
scope.builder().stack_size(::std::cmp::max((schedule.max_depth - depth_threshold) * STACK_SIZE_PER_DEPTH, local_stack_size)).spawn(move || {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Watch out for overflow if max_depth < depth_threshold

let mut vm = vm_factory.create(&params, &schedule);
vm.exec(params, &mut ext).finalize(ext)
})
}).expect("Sub-thread creation cannot fail; the host might run out of resources; qed")
}).join()
}

Expand Down