Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #16 from eosnetworkfoundation/backport-boxed-eos-vm
Browse files Browse the repository at this point in the history
backport cbbab05 eos-vm
tbfleming authored Jan 20, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents c3e89c6 + b0c41f3 commit fb5d7b5
Showing 12 changed files with 174 additions and 47 deletions.
7 changes: 6 additions & 1 deletion libraries/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
add_subdirectory( fc )
add_subdirectory( builtins )

# Suppress warnings on 3rdParty Library
add_definitions( -w )
add_subdirectory( softfloat )
add_subdirectory( chainbase )
add_subdirectory( wasm-jit )
remove_definitions( -w )

add_subdirectory( chainbase )
add_subdirectory( appbase )
add_subdirectory( chain )
add_subdirectory( testing )
6 changes: 5 additions & 1 deletion libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
@@ -308,7 +308,7 @@ struct controller_impl {
cfg.reversible_cache_size, false, cfg.db_map_mode ),
blog( cfg.blocks_dir ),
fork_db( cfg.state_dir ),
wasmif( cfg.wasm_runtime, cfg.eosvmoc_tierup, db, cfg.state_dir, cfg.eosvmoc_config ),
wasmif( cfg.wasm_runtime, cfg.eosvmoc_tierup, db, cfg.state_dir, cfg.eosvmoc_config, !cfg.profile_accounts.empty() ),
resource_limits( db ),
authorization( s, db ),
protocol_features( std::move(pfs) ),
@@ -3104,6 +3104,10 @@ bool controller::contracts_console()const {
return my->conf.contracts_console;
}

bool controller::is_profiling(account_name account) const {
return my->conf.profile_accounts.find(account) != my->conf.profile_accounts.end();
}

chain_id_type controller::get_chain_id()const {
return my->chain_id;
}
4 changes: 4 additions & 0 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
@@ -96,6 +96,8 @@ namespace eosio { namespace chain {
flat_set<account_name> resource_greylist;
flat_set<account_name> trusted_producers;
uint32_t greylist_limit = chain::config::maximum_elastic_resource_multiplier;

flat_set<account_name> profile_accounts;
};

enum class block_status {
@@ -284,6 +286,8 @@ namespace eosio { namespace chain {

bool contracts_console()const;

bool is_profiling(account_name name) const;

chain_id_type get_chain_id()const;

db_read_mode get_read_mode()const;
2 changes: 1 addition & 1 deletion libraries/chain/include/eosio/chain/wasm_interface.hpp
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ namespace eosio { namespace chain {
}
}

wasm_interface(vm_type vm, bool eosvmoc_tierup, const chainbase::database& d, const boost::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config);
wasm_interface(vm_type vm, bool eosvmoc_tierup, const chainbase::database& d, const boost::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile);
~wasm_interface();

//call before dtor to skip what can be minutes of dtor overhead with some runtimes; can cause leaks
10 changes: 7 additions & 3 deletions libraries/chain/include/eosio/chain/wasm_interface_private.hpp
Original file line number Diff line number Diff line change
@@ -58,13 +58,17 @@ namespace eosio { namespace chain {
};
#endif

wasm_interface_impl(wasm_interface::vm_type vm, bool eosvmoc_tierup, const chainbase::database& d, const boost::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config) : db(d), wasm_runtime_time(vm) {
wasm_interface_impl(wasm_interface::vm_type vm, bool eosvmoc_tierup, const chainbase::database& d, const boost::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile) : db(d), wasm_runtime_time(vm) {
#ifdef EOSIO_EOS_VM_RUNTIME_ENABLED
if(vm == wasm_interface::vm_type::eos_vm)
runtime_interface = std::make_unique<webassembly::eos_vm_runtime::eos_vm_runtime<eosio::vm::interpreter>>();
#endif
#ifdef EOSIO_EOS_VM_JIT_RUNTIME_ENABLED
if(vm == wasm_interface::vm_type::eos_vm_jit)
if(vm == wasm_interface::vm_type::eos_vm_jit && profile) {
eosio::vm::set_profile_interval_us(200);
runtime_interface = std::make_unique<webassembly::eos_vm_runtime::eos_vm_profile_runtime>();
}
if(vm == wasm_interface::vm_type::eos_vm_jit && !profile)
runtime_interface = std::make_unique<webassembly::eos_vm_runtime::eos_vm_runtime<eosio::vm::jit>>();
#endif
#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED
@@ -86,7 +90,7 @@ namespace eosio { namespace chain {
if(is_shutting_down)
for(wasm_cache_index::iterator it = wasm_instantiation_cache.begin(); it != wasm_instantiation_cache.end(); ++it)
wasm_instantiation_cache.modify(it, [](wasm_cache_entry& e) {
e.module.release();
e.module.release()->fast_shutdown();
});
}

72 changes: 35 additions & 37 deletions libraries/chain/include/eosio/chain/webassembly/common.hpp
Original file line number Diff line number Diff line change
@@ -7,8 +7,6 @@
#include <eosio/vm/span.hpp>
#include <eosio/vm/types.hpp>

using namespace fc;

namespace eosio { namespace chain { namespace webassembly {
// forward declaration
class interface;
@@ -33,10 +31,9 @@ namespace eosio { namespace chain {
template <typename T, std::size_t Align = alignof(T)>
using legacy_span = eosio::vm::argument_proxy<eosio::vm::span<T>, Align>;

struct null_terminated_ptr {
null_terminated_ptr(const char* ptr) : ptr(ptr) {}
const char* data() const { return ptr; }
const char* ptr;
struct null_terminated_ptr : eosio::vm::span<const char> {
using base_type = eosio::vm::span<const char>;
null_terminated_ptr(const char* ptr) : base_type(ptr, strlen(ptr)) {}
};

struct memcpy_params {
@@ -58,64 +55,65 @@ namespace eosio { namespace chain {
};

// define the type converter for eosio
struct type_converter : public eosio::vm::type_converter<webassembly::interface, eosio::vm::execution_interface> {
using base_type = eosio::vm::type_converter<webassembly::interface, eosio::vm::execution_interface>;
using base_type::type_converter;
template<typename Interface>
struct basic_type_converter : public eosio::vm::type_converter<webassembly::interface, Interface> {
using base_type = eosio::vm::type_converter<webassembly::interface, Interface>;
using eosio::vm::type_converter<webassembly::interface, Interface>::type_converter;
using base_type::from_wasm;
using base_type::to_wasm;
using base_type::as_value;
using base_type::as_result;
using base_type::elem_type;
using base_type::get_host;

EOS_VM_FROM_WASM(memcpy_params, (void* dst, const void* src, vm::wasm_size_t size)) {
validate_pointer<char>(dst, size);
validate_pointer<char>(src, size);
validate_pointer<char>(dst, 1);
return { dst, src, size };

EOS_VM_FROM_WASM(bool, (uint32_t value)) { return value ? 1 : 0; }

EOS_VM_FROM_WASM(memcpy_params, (vm::wasm_ptr_t dst, vm::wasm_ptr_t src, vm::wasm_size_t size)) {
auto d = this->template validate_pointer<char>(dst, size);
auto s = this->template validate_pointer<char>(src, size);
this->template validate_pointer<char>(dst, 1);
return { d, s, size };
}

EOS_VM_FROM_WASM(memcmp_params, (const void* lhs, const void* rhs, vm::wasm_size_t size)) {
validate_pointer<char>(lhs, size);
validate_pointer<char>(rhs, size);
return { lhs, rhs, size };
EOS_VM_FROM_WASM(memcmp_params, (vm::wasm_ptr_t lhs, vm::wasm_ptr_t rhs, vm::wasm_size_t size)) {
auto l = this->template validate_pointer<char>(lhs, size);
auto r = this->template validate_pointer<char>(rhs, size);
return { l, r, size };
}

EOS_VM_FROM_WASM(memset_params, (void* dst, int32_t val, vm::wasm_size_t size)) {
validate_pointer<char>(dst, size);
validate_pointer<char>(dst, 1);
return { dst, val, size };
EOS_VM_FROM_WASM(memset_params, (vm::wasm_ptr_t dst, int32_t val, vm::wasm_size_t size)) {
auto d = this->template validate_pointer<char>(dst, size);
this->template validate_pointer<char>(dst, 1);
return { d, val, size };
}

template <typename T>
auto from_wasm(void* ptr) const
auto from_wasm(vm::wasm_ptr_t ptr) const
-> std::enable_if_t< std::is_pointer_v<T>,
vm::argument_proxy<T> > {
validate_pointer<std::remove_pointer_t<T>>(ptr, 1);
return {ptr};
auto p = this->template validate_pointer<std::remove_pointer_t<T>>(ptr, 1);
return {p};
}

template <typename T>
auto from_wasm(void* ptr, vm::tag<T> = {}) const
auto from_wasm(vm::wasm_ptr_t ptr, vm::tag<T> = {}) const
-> std::enable_if_t< vm::is_argument_proxy_type_v<T> &&
std::is_pointer_v<typename T::proxy_type>, T> {
if constexpr(T::is_legacy()) {
EOS_ASSERT(ptr != this->get_interface().get_memory(), wasm_execution_error, "references cannot be created for null pointers");
EOS_ASSERT(ptr != 0, wasm_execution_error, "references cannot be created for null pointers");
}
this->template validate_pointer<typename T::pointee_type>(ptr, 1);
return {ptr};
auto p = this->template validate_pointer<typename T::pointee_type>(ptr, 1);
return {p};
}

EOS_VM_FROM_WASM(null_terminated_ptr, (const void* ptr)) {
validate_null_terminated_pointer(ptr);
return {static_cast<const char*>(ptr)};
EOS_VM_FROM_WASM(null_terminated_ptr, (vm::wasm_ptr_t ptr)) {
auto p = this->validate_null_terminated_pointer(ptr);
return {static_cast<const char*>(p)};
}
EOS_VM_FROM_WASM(name, (uint64_t e)) { return name{e}; }
uint64_t to_wasm(name&& n) { return n.to_uint64_t(); }
EOS_VM_FROM_WASM(float32_t, (float f)) { return ::to_softfloat32(f); }
EOS_VM_FROM_WASM(float64_t, (double f)) { return ::to_softfloat64(f); }
};

using type_converter = basic_type_converter<eosio::vm::execution_interface>;

using eos_vm_host_functions_t = eosio::vm::registered_host_functions<webassembly::interface,
eosio::vm::execution_interface,
eosio::chain::type_converter>;
17 changes: 16 additions & 1 deletion libraries/chain/include/eosio/chain/webassembly/eos-vm.hpp
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@

//eos-vm includes
#include <eosio/vm/backend.hpp>
#include <eosio/vm/profile.hpp>

namespace eosio { namespace chain { namespace webassembly { namespace eos_vm_runtime {

@@ -18,7 +19,7 @@ struct apply_options;
}}

template <typename Impl>
using eos_vm_backend_t = eosio::vm::backend<eos_vm_host_functions_t, Impl, webassembly::eos_vm_runtime::apply_options>;
using eos_vm_backend_t = eosio::vm::backend<eos_vm_host_functions_t, Impl, webassembly::eos_vm_runtime::apply_options, vm::profile_instr_map>;

template <typename Options>
using eos_vm_null_backend_t = eosio::vm::backend<eos_vm_host_functions_t, eosio::vm::null_backend, Options>;
@@ -34,6 +35,10 @@ void validate(const bytes& code, const wasm_config& cfg, const whitelisted_intri

struct apply_options;

struct profile_config {
boost::container::flat_set<name> accounts_to_profile;
};

template<typename Backend>
class eos_vm_runtime : public eosio::chain::wasm_runtime_interface {
public:
@@ -54,4 +59,14 @@ class eos_vm_runtime : public eosio::chain::wasm_runtime_interface {
friend class eos_vm_instantiated_module;
};

class eos_vm_profile_runtime : public eosio::chain::wasm_runtime_interface {
public:
eos_vm_profile_runtime();
bool inject_module(IR::Module&) override;
std::unique_ptr<wasm_instantiated_module_interface> instantiate_module(const char* code_bytes, size_t code_size, std::vector<uint8_t>,
const digest_type& code_hash, const uint8_t& vm_type, const uint8_t& vm_version) override;

void immediately_exit_currently_running_module() override;
};

}}}}// eosio::chain::webassembly::eos_vm_runtime
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ class apply_context;
class wasm_instantiated_module_interface {
public:
virtual void apply(apply_context& context) = 0;
virtual void fast_shutdown() {}

virtual ~wasm_instantiated_module_interface();
};
4 changes: 2 additions & 2 deletions libraries/chain/wasm_interface.cpp
Original file line number Diff line number Diff line change
@@ -33,8 +33,8 @@

namespace eosio { namespace chain {

wasm_interface::wasm_interface(vm_type vm, bool eosvmoc_tierup, const chainbase::database& d, const boost::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config)
: my( new wasm_interface_impl(vm, eosvmoc_tierup, d, data_dir, eosvmoc_config) ) {}
wasm_interface::wasm_interface(vm_type vm, bool eosvmoc_tierup, const chainbase::database& d, const boost::filesystem::path data_dir, const eosvmoc::config& eosvmoc_config, bool profile)
: my( new wasm_interface_impl(vm, eosvmoc_tierup, d, data_dir, eosvmoc_config, profile) ) {}

wasm_interface::~wasm_interface() {}

92 changes: 92 additions & 0 deletions libraries/chain/webassembly/runtimes/eos-vm.cpp
Original file line number Diff line number Diff line change
@@ -162,6 +162,72 @@ class eos_vm_instantiated_module : public wasm_instantiated_module_interface {
std::unique_ptr<backend_t> _instantiated_module;
};

class eos_vm_profiling_module : public wasm_instantiated_module_interface {
using backend_t = eosio::vm::backend<eos_vm_host_functions_t, eosio::vm::jit_profile, webassembly::eos_vm_runtime::apply_options, vm::profile_instr_map>;
public:
eos_vm_profiling_module(std::unique_ptr<backend_t> mod, const char * code, std::size_t code_size) :
_instantiated_module(std::move(mod)),
_original_code(code, code + code_size) {}


void apply(apply_context& context) override {
_instantiated_module->set_wasm_allocator(&context.control.get_wasm_allocator());
apply_options opts;
if(context.control.is_builtin_activated(builtin_protocol_feature_t::configurable_wasm_limits)) {
const wasm_config& config = context.control.get_global_properties().wasm_configuration;
opts = {config.max_pages, config.max_call_depth};
}
auto fn = [&]() {
eosio::chain::webassembly::interface iface(context);
_instantiated_module->initialize(&iface, opts);
_instantiated_module->call(
iface, "env", "apply",
context.get_receiver().to_uint64_t(),
context.get_action().account.to_uint64_t(),
context.get_action().name.to_uint64_t());
};
profile_data* prof = start(context);
try {
scoped_profile profile_runner(prof);
checktime_watchdog wd(context.trx_context.transaction_timer);
_instantiated_module->timed_run(wd, fn);
} catch(eosio::vm::timeout_exception&) {
context.trx_context.checktime();
} catch(eosio::vm::wasm_memory_exception& e) {
FC_THROW_EXCEPTION(wasm_execution_error, "access violation");
} catch(eosio::vm::exception& e) {
FC_THROW_EXCEPTION(wasm_execution_error, "eos-vm system failure");
}
}

void fast_shutdown() override {
_prof.clear();
}

profile_data* start(apply_context& context) {
name account = context.get_receiver();
if(!context.control.is_profiling(account)) return nullptr;
if(auto it = _prof.find(account); it != _prof.end()) {
return it->second.get();
} else {
auto code_sequence = context.control.db().get<account_metadata_object, by_name>(account).code_sequence;
std::string basename = account.to_string() + "." + std::to_string(code_sequence);
auto prof = std::make_unique<profile_data>(basename + ".profile", *_instantiated_module);
auto [pos,_] = _prof.insert(std::pair{ account, std::move(prof)});
std::ofstream outfile(basename + ".wasm");
outfile.write(_original_code.data(), _original_code.size());
return pos->second.get();
}
return nullptr;
}

private:

std::unique_ptr<backend_t> _instantiated_module;
boost::container::flat_map<name, std::unique_ptr<profile_data>> _prof;
std::vector<char> _original_code;
};

template<typename Impl>
eos_vm_runtime<Impl>::eos_vm_runtime() {}

@@ -195,6 +261,32 @@ std::unique_ptr<wasm_instantiated_module_interface> eos_vm_runtime<Impl>::instan
template class eos_vm_runtime<eosio::vm::interpreter>;
template class eos_vm_runtime<eosio::vm::jit>;

eos_vm_profile_runtime::eos_vm_profile_runtime() {}

void eos_vm_profile_runtime::immediately_exit_currently_running_module() {
throw wasm_exit{};
}

bool eos_vm_profile_runtime::inject_module(IR::Module& module) {
return false;
}

std::unique_ptr<wasm_instantiated_module_interface> eos_vm_profile_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector<uint8_t>,
const digest_type&, const uint8_t&, const uint8_t&) {

using backend_t = eosio::vm::backend<eos_vm_host_functions_t, eosio::vm::jit_profile, webassembly::eos_vm_runtime::apply_options, vm::profile_instr_map>;
try {
wasm_code_ptr code((uint8_t*)code_bytes, code_size);
apply_options options = { .max_pages = 65536,
.max_call_depth = 0 };
std::unique_ptr<backend_t> bkend = std::make_unique<backend_t>(code, code_size, nullptr, options);
eos_vm_host_functions_t::resolve(bkend->get_module());
return std::make_unique<eos_vm_profiling_module>(std::move(bkend), code_bytes, code_size);
} catch(eosio::vm::exception& e) {
FC_THROW_EXCEPTION(wasm_execution_error, "Error building eos-vm interp: ${e}", ("e", e.what()));
}
}

}

template <auto HostFunction, typename... Preconditions>
4 changes: 4 additions & 0 deletions plugins/chain_plugin/chain_plugin.cpp
Original file line number Diff line number Diff line change
@@ -241,6 +241,8 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip
#endif
})->default_value(eosio::chain::config::default_wasm_runtime, default_wasm_runtime_str), wasm_runtime_opt.c_str()
)
("profile-account", boost::program_options::value<vector<string>>()->composing(),
"The name of an account whose code will be profiled")
("abi-serializer-max-time-ms", bpo::value<uint32_t>()->default_value(config::default_abi_serializer_max_time_us / 1000),
"Override default maximum ABI serialization time allowed in ms")
("chain-state-db-size-mb", bpo::value<uint64_t>()->default_value(config::default_state_size / (1024 * 1024)), "Maximum size (in MiB) of the chain state database")
@@ -726,6 +728,8 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
if( options.count( "wasm-runtime" ))
my->wasm_runtime = options.at( "wasm-runtime" ).as<vm_type>();

LOAD_VALUE_SET( options, "profile-account", my->chain_config->profile_accounts );

if(options.count("abi-serializer-max-time-ms"))
my->abi_serializer_max_time_us = fc::microseconds(options.at("abi-serializer-max-time-ms").as<uint32_t>() * 1000);

0 comments on commit fb5d7b5

Please sign in to comment.