13#include <sys/syscall.h>
15#include <linux/memfd.h>
25namespace sysio {
namespace chain {
namespace eosvmoc {
27static constexpr size_t header_offset = 512u;
28static constexpr size_t header_size = 512u;
29static constexpr size_t total_header_size = header_offset + header_size;
30static constexpr uint64_t header_id = 0x32434f4d56534f45ULL;
37static constexpr size_t header_dirty_bit_offset_from_file_start = header_offset + offsetof(
code_cache_header,
dirty);
40static_assert(
sizeof(
code_cache_header) <= header_size,
"code_cache_header too big");
44 _result_queue(eosvmoc_config.threads * 2),
45 _threads(eosvmoc_config.threads)
47 FC_ASSERT(_threads,
"SYS VM OC requires at least 1 compile thread");
49 wait_on_compile_monitor_message();
51 _monitor_reply_thread = std::thread([
this]() {
59 _monitor_reply_thread.join();
60 consume_compile_thread_queue();
64void code_cache_async::wait_on_compile_monitor_message() {
72 if(!success || !std::holds_alternative<wasm_compilation_result_message>(message)) {
77 _result_queue.push(std::get<wasm_compilation_result_message>(message));
79 wait_on_compile_monitor_message();
85std::tuple<size_t, size_t> code_cache_async::consume_compile_thread_queue() {
86 size_t bytes_remaining = 0;
87 size_t gotsome = _result_queue.consume_all([&](
const wasm_compilation_result_message& result) {
90 [&](
const code_descriptor& cd) {
93 [&](
const compilation_result_unknownfailure&) {
94 wlog(
"code ${c} failed to tier-up with SYS VM OC", (
"c", result.code.code_id));
95 _blacklist.emplace(result.code);
97 [&](
const compilation_result_toofull&) {
103 bytes_remaining = result.cache_free_bytes;
106 return {gotsome, bytes_remaining};
112 auto [count_processed, bytes_remaining] = consume_compile_thread_queue();
125 std::vector<wrapped_fd> fds_to_pass;
135 code_cache_index::index<by_hash>::type::iterator it =
_cache_index.get<by_hash>().find(boost::make_tuple(code_id, vm_version));
143 if(_blacklist.find(ct) != _blacklist.end())
162 std::vector<wrapped_fd> fds_to_pass;
174 elog(
"unexpected response from SYS VM OC compile monitor during shutdown");
179 code_cache_index::index<by_hash>::type::iterator it =
_cache_index.get<by_hash>().find(boost::make_tuple(code_id, vm_version));
189 std::vector<wrapped_fd> fds_to_pass;
194 SYS_ASSERT(success, wasm_execution_error,
"failed to read response from monitor process");
195 SYS_ASSERT(std::holds_alternative<wasm_compilation_result_message>(message), wasm_execution_error,
"unexpected response from monitor process");
198 SYS_ASSERT(std::holds_alternative<code_descriptor>(result.result), wasm_execution_error,
"failed to compile wasm");
202 return &*
_cache_index.push_front(std::move(std::get<code_descriptor>(result.result))).first;
207 _cache_file_path(data_dir/
"code_cache.bin")
209 static_assert(
sizeof(
allocator_t) <= header_offset,
"header offset intersects with allocator");
211 bfs::create_directories(data_dir);
218 bip::file_mapping creation_mapping(
_cache_file_path.generic_string().c_str(), bip::read_write);
219 bip::mapped_region creation_region(creation_mapping, bip::read_write);
226 char header_buff[total_header_size];
228 hs.read(header_buff,
sizeof(header_buff));
229 SYS_ASSERT(!hs.fail(), bad_database_version_exception,
"failed to read code cache header");
230 memcpy((
char*)&cache_header, header_buff + header_offset,
sizeof(cache_header));
233 SYS_ASSERT(cache_header.
id == header_id, bad_database_version_exception,
"existing SYS VM OC code cache not compatible with this version");
239 if(eosvmoc_config.
cache_size > existing_file_size) {
242 bip::file_mapping resize_mapping(
_cache_file_path.generic_string().c_str(), bip::read_write);
243 bip::mapped_region resize_region(resize_mapping, bip::read_write);
246 resize_allocator->grow(eosvmoc_config.
cache_size - existing_file_size);
253 char* code_mapping = (
char*)mmap(
nullptr, eosvmoc_config.
cache_size, PROT_READ|PROT_WRITE, MAP_SHARED,
_cache_fd, 0);
260 unsigned number_entries;
262 for(
unsigned i = 0; i < number_entries; ++i) {
274 ilog(
"SYS VM Optimized Compiler code cache loaded with ${c} entries; ${f} of ${t} bytes free", (
"c", number_entries)(
"f",
allocator->get_free_memory())(
"t",
allocator->get_size()));
276 munmap(code_mapping, eosvmoc_config.
cache_size);
284 int duped = dup(compile_monitor_conn);
290 bip::file_mapping dirty_mapping(
_cache_file_path.generic_string().c_str(), bip::read_write);
291 bip::mapped_region dirty_region(dirty_mapping, bip::read_write);
293 *((
char*)dirty_region.get_address()+header_dirty_bit_offset_from_file_start) =
dirty;
294 if(dirty_region.flush(0, 0,
false) ==
false)
295 elog(
"Syncing code cache failed");
311 char* code_mapping = (
char*)mmap(
nullptr, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED,
_cache_fd, 0);
312 if(code_mapping == MAP_FAILED)
320 size_t sz = dssz.tellp();
329 for(
unsigned int i = 0; i < 25 &&
_cache_index.size(); ++i) {
341 *((
uintptr_t*)(code_mapping+descriptor_ptr_from_file_start)) = ptr_offset;
344 *((
uintptr_t*)(code_mapping+descriptor_ptr_from_file_start)) = 0;
346 msync(code_mapping,
allocator->get_size(), MS_SYNC);
347 munmap(code_mapping,
allocator->get_size());
354 code_cache_index::index<by_hash>::type::iterator it =
_cache_index.get<by_hash>().find(boost::make_tuple(code_id, vm_version));
368 compiling_it->second =
true;
373 for(
unsigned int i = 0; i < 25 &&
_cache_index.size() > 1; ++i) {
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
const ObjectType * find(CompatibleKey &&key) const
code_cache_async(const bfs::path data_dir, const eosvmoc::config &eosvmoc_config, const chainbase::database &db)
const code_descriptor *const get_descriptor_for_code(const digest_type &code_id, const uint8_t &vm_version)
void serialize_cache_index(fc::datastream< T > &ds)
local::datagram_protocol::socket _compile_monitor_write_socket
void check_eviction_threshold(size_t free_bytes)
local::datagram_protocol::socket _compile_monitor_read_socket
std::unordered_set< code_tuple > _queued_compiles
void run_eviction_round()
std::unordered_map< code_tuple, bool > _outstanding_compiles_and_poison
void free_code(const digest_type &code_id, const uint8_t &vm_version)
void set_on_disk_region_dirty(bool)
size_t _free_bytes_eviction_threshold
code_cache_base(const bfs::path data_dir, const eosvmoc::config &eosvmoc_config, const chainbase::database &db)
bfs::path _cache_file_path
const chainbase::database & _db
code_cache_index _cache_index
const code_descriptor *const get_descriptor_for_code_sync(const digest_type &code_id, const uint8_t &vm_version)
uintptr_t serialized_descriptor_index
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
void close(T *e, websocketpp::connection_hdl hdl)
bip::allocator< T, pinnable_mapped_file::segment_manager > allocator
void unpack(Stream &s, std::deque< T > &value)
void pack(Stream &s, const std::deque< T > &value)
void set_os_thread_name(const string &name)
bip::rbtree_best_fit< bip::null_mutex_family, bip::offset_ptr< void >, alignof(std::max_align_t)> allocator_t
bool write_message_with_fds(boost::asio::local::datagram_protocol::socket &s, const eosvmoc_message &message, const std::vector< wrapped_fd > &fds=std::vector< wrapped_fd >())
std::tuple< bool, eosvmoc_message, std::vector< wrapped_fd > > read_message_with_fds(boost::asio::local::datagram_protocol::socket &s)
struct sysio::chain::eosvmoc::code_cache_header __attribute__((packed))
wrapped_fd get_connection_to_compile_monitor(int cache_fd)
wrapped_fd memfd_for_bytearray(const T &bytes)
key Invalid authority Invalid transaction Invalid block ID Invalid packed transaction Invalid chain ID Invalid symbol Signature type is not a currently activated type Block can not be found Unlinkable block Block does not guarantee concurrent execution without conflicts Block exhausted allowed resources Block is from the future Block is not signed by expected producer Block includes an ill formed protocol feature activation extension Block includes an ill formed additional block signature extension Error decompressing transaction Transaction should have at least one required authority Expired Transaction Invalid Reference Block Duplicate deferred transaction The transaction can not be found Transaction is too big Invalid transaction extension Transaction includes disallowed Transaction exceeded transient resource limit Account name already exists sysio_assert_message assertion failure Action can not be found Attempt to use unaccessible API Inline Action exceeds maximum size limit sysio_assert_code assertion failure uses restricted error code value action return value size too big database_exception
overloaded(Ts...) -> overloaded< Ts... >
_W64 unsigned int uintptr_t
unsigned __int64 uint64_t
std::vector< code_descriptor > codes
memcpy((char *) pInfo->slotDescription, s, l)