From 289755fd0e273dd6cb122614a1c87e98518aee27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 5 Jun 2018 18:21:21 +0200 Subject: [PATCH 01/19] EVMC tracing for interpreter --- libaleth-interpreter/VM.cpp | 54 ++++++++++++++++++++++++++++++------- libaleth-interpreter/VM.h | 3 ++- libevm/EVMC.cpp | 42 +++++++++++++++++++++++++++++ libevm/EVMC.h | 7 ++++- 4 files changed, 94 insertions(+), 12 deletions(-) diff --git a/libaleth-interpreter/VM.cpp b/libaleth-interpreter/VM.cpp index ca618702eed..d605a73b245 100644 --- a/libaleth-interpreter/VM.cpp +++ b/libaleth-interpreter/VM.cpp @@ -22,6 +22,9 @@ namespace { +evmc_trace_callback g_traceCallback = nullptr; +evmc_tracer_context* g_traceContext = nullptr; + void destroy(evmc_instance* _instance) { (void)_instance; @@ -39,7 +42,7 @@ void delete_output(const evmc_result* result) } evmc_result execute(evmc_instance* _instance, evmc_context* _context, evmc_revision _rev, - const evmc_message* _msg, uint8_t const* _code, size_t _codeSize) noexcept + evmc_message const* _msg, uint8_t const* _code, size_t _codeSize) noexcept { (void)_instance; std::unique_ptr vm{new dev::eth::VM}; @@ -108,6 +111,13 @@ evmc_result execute(evmc_instance* _instance, evmc_context* _context, evmc_revis return result; } + +void setTracer(evmc_instance* /*_instance*/, evmc_trace_callback _callback, + evmc_tracer_context* _context) noexcept +{ + g_traceCallback = _callback; + g_traceContext = _context; +} } // namespace extern "C" evmc_instance* evmc_create_interpreter() noexcept @@ -120,17 +130,33 @@ extern "C" evmc_instance* evmc_create_interpreter() noexcept ::destroy, ::execute, getCapabilities, - nullptr, // set_tracer + ::setTracer, nullptr, // set_option }; return &s_instance; } - namespace dev { namespace eth { +void VM::trace() noexcept +{ + if (g_traceCallback) + { + auto const& metrics = c_metrics[static_cast(m_OP)]; + evmc_uint256be topStackItem; + evmc_uint256be const* pushedStackItem = nullptr; + if (metrics.num_stack_returned_items == 1) + { + topStackItem = toEvmC(m_SPP[0]); + pushedStackItem = &topStackItem; + } + g_traceCallback(g_traceContext, m_PC, EVMC_SUCCESS, m_io_gas, m_stackEnd - m_SPP, + pushedStackItem, m_mem.size(), 0, 0, nullptr); + } +} + uint64_t VM::memNeed(u256 _offset, u256 _size) { return toInt63(_size ? u512(_offset) + _size : u512(0)); @@ -411,6 +437,7 @@ void VM::interpretCases() updateIOGas(); m_SPP[0] = (u256)*(h256 const*)(m_mem.data() + (unsigned)m_SP[0]); + trace(); } NEXT @@ -421,6 +448,7 @@ void VM::interpretCases() updateIOGas(); *(h256*)&m_mem[(unsigned)m_SP[0]] = (h256)m_SP[1]; + trace(); } NEXT @@ -1162,11 +1190,14 @@ void VM::interpretCases() // get val at two-byte offset into const pool and advance pc by one-byte remainder TRACE_OP(2, m_PC, m_OP); unsigned off; - ++m_PC; - off = m_code[m_PC++] << 8; - off |= m_code[m_PC++]; - m_PC += m_code[m_PC]; + uint64_t pc = m_PC; + ++pc; + off = m_code[pc++] << 8; + off |= m_code[pc++]; + pc += m_code[pc]; m_SPP[0] = m_pool[off]; + trace(); + m_PC = pc; TRACE_VAL(2, "Retrieved pooled const", m_SPP[0]); #else throwBadInstruction(); @@ -1178,9 +1209,9 @@ void VM::interpretCases() { ON_OP(); updateIOGas(); - ++m_PC; - m_SPP[0] = m_code[m_PC]; - ++m_PC; + m_SPP[0] = m_code[m_PC + 1]; + trace(); + m_PC += 2; } CONTINUE @@ -1226,6 +1257,8 @@ void VM::interpretCases() // bytes to handle "out of code" push data here. for (++m_PC; numBytes--; ++m_PC) m_SPP[0] = (m_SPP[0] << 8) | m_code[m_PC]; + + trace(); } CONTINUE @@ -1370,6 +1403,7 @@ void VM::interpretCases() } updateIOGas(); + trace(); } NEXT diff --git a/libaleth-interpreter/VM.h b/libaleth-interpreter/VM.h index 30f20bfe6fa..4fef4a4b476 100644 --- a/libaleth-interpreter/VM.h +++ b/libaleth-interpreter/VM.h @@ -81,7 +81,6 @@ class VM void copyCode(int); typedef void (VM::*MemFnPtr)(); MemFnPtr m_bounce = nullptr; - uint64_t m_nSteps = 0; // return bytes owning_bytes_ref m_output; @@ -116,6 +115,8 @@ class VM uint64_t m_newMemSize = 0; uint64_t m_copyMemSize = 0; + void trace() noexcept; + // initialize interpreter void initEntry(); void optimize(); diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index 8afd2984a12..4a5db96d458 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -3,6 +3,7 @@ #include "EVMC.h" +#include #include #include @@ -49,6 +50,36 @@ EVM::Result EVM::execute(ExtVMFace& _ext, int64_t gas) evmc_execute(m_instance, &_ext, mode, &msg, _ext.code.data(), _ext.code.size())}; } +EVMC::EVMC(evmc_instance* _instance) : EVM(_instance) +{ + static const auto tracer = [](evmc_tracer_context * context, size_t code_offset, + evmc_status_code status_code, int64_t gas_left, size_t stack_num_items, + const evmc_uint256be* pushed_stack_item, size_t memory_size, size_t changed_memory_offset, + size_t changed_memory_size, const uint8_t* changed_memory) noexcept + { + EVMC* evmc = reinterpret_cast(context); + + // TODO: It might be easier to just pass instruction from VM. + char const* name = evmc->m_instructionNames[evmc->m_code[code_offset]]; + + std::cerr << "EVMC " + << " " << evmc->m_step++ << " " << code_offset << " " << name << " " + << status_code << " " << gas_left << " " << stack_num_items; + + if (pushed_stack_item) + std::cerr << " +[" << fromEvmC(*pushed_stack_item) << "]"; + + std::cerr << " " << memory_size << "\n"; + + (void)changed_memory_offset; + (void)changed_memory_size; + (void)changed_memory_size; + (void)changed_memory; + }; + + _instance->set_tracer(_instance, tracer, reinterpret_cast(this)); +} + owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp) { assert(_ext.envInfo().number() >= 0); @@ -63,8 +94,19 @@ owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp assert(_ext.envInfo().gasLimit() <= int64max); assert(_ext.depth <= static_cast(std::numeric_limits::max())); + m_code = bytesConstRef{&_ext.code}; + m_step = 0; + + // FIXME: EVMC revision found twice. + m_instructionNames = evmc_get_instruction_names_table(toRevision(_ext.evmSchedule())); + + auto gas = static_cast(io_gas); + std::cerr << "EVMC message START " << _ext.depth << " " << _ext.caller << " -> " + << _ext.myAddress << " gas: " << gas << "\n"; EVM::Result r = execute(_ext, gas); + std::cerr << "EVMC message END " << _ext.depth << " status: " << r.status() + << " gas left: " << r.gasLeft() << "\n"; switch (r.status()) { diff --git a/libevm/EVMC.h b/libevm/EVMC.h index 8d317f4616b..af4fd7becf5 100644 --- a/libevm/EVMC.h +++ b/libevm/EVMC.h @@ -86,9 +86,14 @@ class EVM class EVMC : public EVM, public VMFace { public: - explicit EVMC(evmc_instance* _instance) : EVM(_instance) {} + explicit EVMC(evmc_instance* _instance); owning_bytes_ref exec(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) final; + +private: + bytesConstRef m_code; + int m_step = 0; + char const* const* m_instructionNames = nullptr; }; } } From a53eaf702994800257bfe2dfb730d0e45a172a21 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Thu, 2 Aug 2018 12:10:07 +0200 Subject: [PATCH 02/19] Formatting fixes in tracer function --- libevm/EVMC.cpp | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index 4a5db96d458..53a7296fe3d 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -52,29 +52,25 @@ EVM::Result EVM::execute(ExtVMFace& _ext, int64_t gas) EVMC::EVMC(evmc_instance* _instance) : EVM(_instance) { - static const auto tracer = [](evmc_tracer_context * context, size_t code_offset, - evmc_status_code status_code, int64_t gas_left, size_t stack_num_items, - const evmc_uint256be* pushed_stack_item, size_t memory_size, size_t changed_memory_offset, - size_t changed_memory_size, const uint8_t* changed_memory) noexcept + static const auto tracer = [](evmc_tracer_context * _context, size_t _codeOffset, + evmc_status_code _statusCode, int64_t _gasLeft, size_t _stackNumItems, + evmc_uint256be const* _pushedStackItem, size_t _memorySize, + size_t /*changed_memory_offset*/, size_t /*changed_memory_size*/, + uint8_t const* /*changed_memory*/) noexcept { - EVMC* evmc = reinterpret_cast(context); + EVMC* evmc = reinterpret_cast(_context); // TODO: It might be easier to just pass instruction from VM. - char const* name = evmc->m_instructionNames[evmc->m_code[code_offset]]; + char const* name = evmc->m_instructionNames[evmc->m_code[_codeOffset]]; std::cerr << "EVMC " - << " " << evmc->m_step++ << " " << code_offset << " " << name << " " - << status_code << " " << gas_left << " " << stack_num_items; + << " " << evmc->m_step++ << " " << _codeOffset << " " << name << " " + << _statusCode << " " << _gasLeft << " " << _stackNumItems; - if (pushed_stack_item) - std::cerr << " +[" << fromEvmC(*pushed_stack_item) << "]"; + if (_pushedStackItem) + std::cerr << " +[" << fromEvmC(*_pushedStackItem) << "]"; - std::cerr << " " << memory_size << "\n"; - - (void)changed_memory_offset; - (void)changed_memory_size; - (void)changed_memory_size; - (void)changed_memory; + std::cerr << " " << _memorySize << "\n"; }; _instance->set_tracer(_instance, tracer, reinterpret_cast(this)); From 07fc1cc13d472d9a67813a9ab6ccb3f786084432 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Thu, 2 Aug 2018 12:15:38 +0200 Subject: [PATCH 03/19] Log EVMC trace messages to vmtrace channel --- libevm/EVMC.cpp | 21 ++++++++++++--------- libevm/EVMC.h | 2 ++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index 53a7296fe3d..45ce4cc62f0 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -63,14 +63,17 @@ EVMC::EVMC(evmc_instance* _instance) : EVM(_instance) // TODO: It might be easier to just pass instruction from VM. char const* name = evmc->m_instructionNames[evmc->m_code[_codeOffset]]; - std::cerr << "EVMC " - << " " << evmc->m_step++ << " " << _codeOffset << " " << name << " " - << _statusCode << " " << _gasLeft << " " << _stackNumItems; + std::ostringstream logMessage; + logMessage << "EVMC " + << " " << evmc->m_step++ << " " << _codeOffset << " " << name << " " + << _statusCode << " " << _gasLeft << " " << _stackNumItems; if (_pushedStackItem) - std::cerr << " +[" << fromEvmC(*_pushedStackItem) << "]"; + logMessage << " +[" << fromEvmC(*_pushedStackItem) << "]"; - std::cerr << " " << _memorySize << "\n"; + logMessage << " " << _memorySize; + + LOG(evmc->m_vmTraceLogger) << logMessage.str(); }; _instance->set_tracer(_instance, tracer, reinterpret_cast(this)); @@ -98,11 +101,11 @@ owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp auto gas = static_cast(io_gas); - std::cerr << "EVMC message START " << _ext.depth << " " << _ext.caller << " -> " - << _ext.myAddress << " gas: " << gas << "\n"; + LOG(m_vmTraceLogger) << "EVMC message START " << _ext.depth << " " << _ext.caller << " -> " + << _ext.myAddress << " gas: " << gas << "\n"; EVM::Result r = execute(_ext, gas); - std::cerr << "EVMC message END " << _ext.depth << " status: " << r.status() - << " gas left: " << r.gasLeft() << "\n"; + LOG(m_vmTraceLogger) << "EVMC message END " << _ext.depth << " status: " << r.status() + << " gas left: " << r.gasLeft() << "\n"; switch (r.status()) { diff --git a/libevm/EVMC.h b/libevm/EVMC.h index af4fd7becf5..0d4f1d65e4b 100644 --- a/libevm/EVMC.h +++ b/libevm/EVMC.h @@ -94,6 +94,8 @@ class EVMC : public EVM, public VMFace bytesConstRef m_code; int m_step = 0; char const* const* m_instructionNames = nullptr; + + Logger m_vmTraceLogger{createLogger(VerbosityTrace, "vmtrace")}; }; } } From fd2659ba642fe5cd9ebe6a41adbaef94f543cec2 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Thu, 2 Aug 2018 13:07:19 +0200 Subject: [PATCH 04/19] Minor fixes in tracing --- libaleth-interpreter/VM.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libaleth-interpreter/VM.cpp b/libaleth-interpreter/VM.cpp index d605a73b245..4a4aa5dadda 100644 --- a/libaleth-interpreter/VM.cpp +++ b/libaleth-interpreter/VM.cpp @@ -147,7 +147,7 @@ void VM::trace() noexcept auto const& metrics = c_metrics[static_cast(m_OP)]; evmc_uint256be topStackItem; evmc_uint256be const* pushedStackItem = nullptr; - if (metrics.num_stack_returned_items == 1) + if (metrics.num_stack_returned_items >= 1) { topStackItem = toEvmC(m_SPP[0]); pushedStackItem = &topStackItem; @@ -1255,10 +1255,12 @@ void VM::interpretCases() // Construct a number out of PUSH bytes. // This requires the code has been copied and extended by 32 zero // bytes to handle "out of code" push data here. - for (++m_PC; numBytes--; ++m_PC) - m_SPP[0] = (m_SPP[0] << 8) | m_code[m_PC]; + uint64_t codeOffset = m_PC + 1; + for (; numBytes--; ++codeOffset) + m_SPP[0] = (m_SPP[0] << 8) | m_code[codeOffset]; trace(); + m_PC = codeOffset; } CONTINUE From 4ad84c4e6cf7624a9224e83832606de7613b97b2 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Mon, 6 Aug 2018 16:01:24 +0200 Subject: [PATCH 05/19] Get rid of global variables for trace function and context. Store them as member variables of a class wrapping evmc_instance. --- libaleth-interpreter/VM.cpp | 186 +++++++++++++++++++----------------- libaleth-interpreter/VM.h | 5 +- 2 files changed, 103 insertions(+), 88 deletions(-) diff --git a/libaleth-interpreter/VM.cpp b/libaleth-interpreter/VM.cpp index 4a4aa5dadda..d4d94cdd6af 100644 --- a/libaleth-interpreter/VM.cpp +++ b/libaleth-interpreter/VM.cpp @@ -22,13 +22,6 @@ namespace { -evmc_trace_callback g_traceCallback = nullptr; -evmc_tracer_context* g_traceContext = nullptr; - -void destroy(evmc_instance* _instance) -{ - (void)_instance; -} evmc_capabilities_flagset getCapabilities(evmc_instance* _instance) noexcept { @@ -41,99 +34,115 @@ void delete_output(const evmc_result* result) delete[] result->output_data; } -evmc_result execute(evmc_instance* _instance, evmc_context* _context, evmc_revision _rev, - evmc_message const* _msg, uint8_t const* _code, size_t _codeSize) noexcept +class InterpreterEvmcInstance : public evmc_instance { - (void)_instance; - std::unique_ptr vm{new dev::eth::VM}; +public: + static InterpreterEvmcInstance* create() { return new InterpreterEvmcInstance{}; } - evmc_result result = {}; - dev::eth::owning_bytes_ref output; +private: + InterpreterEvmcInstance() + : evmc_instance{ + EVMC_ABI_VERSION, "interpreter", aleth_get_buildinfo()->project_version, destroy, + execute, getCapabilities, setTracer, + nullptr, // set_option + } + {} - try - { - output = vm->exec(_context, _rev, _msg, _code, _codeSize); - result.status_code = EVMC_SUCCESS; - result.gas_left = vm->m_io_gas; - } - catch (dev::eth::RevertInstruction& ex) + static void destroy(evmc_instance* _instance) { - result.status_code = EVMC_REVERT; - result.gas_left = vm->m_io_gas; - output = ex.output(); // This moves the output from the exception! + delete static_cast(_instance); } - catch (dev::eth::BadInstruction const&) - { - result.status_code = EVMC_UNDEFINED_INSTRUCTION; - } - catch (dev::eth::OutOfStack const&) - { - result.status_code = EVMC_STACK_OVERFLOW; - } - catch (dev::eth::StackUnderflow const&) - { - result.status_code = EVMC_STACK_UNDERFLOW; - } - catch (dev::eth::BufferOverrun const&) - { - result.status_code = EVMC_INVALID_MEMORY_ACCESS; - } - catch (dev::eth::OutOfGas const&) - { - result.status_code = EVMC_OUT_OF_GAS; - } - catch (dev::eth::BadJumpDestination const&) - { - result.status_code = EVMC_BAD_JUMP_DESTINATION; - } - catch (dev::eth::DisallowedStateChange const&) - { - result.status_code = EVMC_STATIC_MODE_VIOLATION; - } - catch (dev::eth::VMException const&) - { - result.status_code = EVMC_FAILURE; - } - catch (...) + + static evmc_result execute(evmc_instance* _instance, evmc_context* _context, evmc_revision _rev, + evmc_message const* _msg, uint8_t const* _code, size_t _codeSize) noexcept { - result.status_code = EVMC_INTERNAL_ERROR; + std::unique_ptr vm{new dev::eth::VM}; + + evmc_result result = {}; + dev::eth::owning_bytes_ref output; + + auto evmc = static_cast(_instance); + try + { + output = vm->exec(_context, _rev, _msg, _code, _codeSize, evmc->m_traceCallback, + evmc->m_traceContext); + result.status_code = EVMC_SUCCESS; + result.gas_left = vm->m_io_gas; + } + catch (dev::eth::RevertInstruction& ex) + { + result.status_code = EVMC_REVERT; + result.gas_left = vm->m_io_gas; + output = ex.output(); // This moves the output from the exception! + } + catch (dev::eth::BadInstruction const&) + { + result.status_code = EVMC_UNDEFINED_INSTRUCTION; + } + catch (dev::eth::OutOfStack const&) + { + result.status_code = EVMC_STACK_OVERFLOW; + } + catch (dev::eth::StackUnderflow const&) + { + result.status_code = EVMC_STACK_UNDERFLOW; + } + catch (dev::eth::BufferOverrun const&) + { + result.status_code = EVMC_INVALID_MEMORY_ACCESS; + } + catch (dev::eth::OutOfGas const&) + { + result.status_code = EVMC_OUT_OF_GAS; + } + catch (dev::eth::BadJumpDestination const&) + { + result.status_code = EVMC_BAD_JUMP_DESTINATION; + } + catch (dev::eth::DisallowedStateChange const&) + { + result.status_code = EVMC_STATIC_MODE_VIOLATION; + } + catch (dev::eth::VMException const&) + { + result.status_code = EVMC_FAILURE; + } + catch (...) + { + result.status_code = EVMC_INTERNAL_ERROR; + } + + if (!output.empty()) + { + // Make a copy of the output. + auto outputData = new uint8_t[output.size()]; + std::memcpy(outputData, output.data(), output.size()); + result.output_data = outputData; + result.output_size = output.size(); + result.release = delete_output; + } + + return result; } - if (!output.empty()) + static void setTracer(evmc_instance* _instance, evmc_trace_callback _callback, + evmc_tracer_context* _context) noexcept { - // Make a copy of the output. - auto outputData = new uint8_t[output.size()]; - std::memcpy(outputData, output.data(), output.size()); - result.output_data = outputData; - result.output_size = output.size(); - result.release = delete_output; + auto evmc = static_cast(_instance); + + evmc->m_traceCallback = _callback; + evmc->m_traceContext = _context; } - return result; -} + evmc_trace_callback m_traceCallback = nullptr; + evmc_tracer_context* m_traceContext = nullptr; +}; -void setTracer(evmc_instance* /*_instance*/, evmc_trace_callback _callback, - evmc_tracer_context* _context) noexcept -{ - g_traceCallback = _callback; - g_traceContext = _context; -} } // namespace extern "C" evmc_instance* evmc_create_interpreter() noexcept { - // TODO: Allow creating multiple instances with different configurations. - static evmc_instance s_instance{ - EVMC_ABI_VERSION, - "interpreter", - aleth_get_buildinfo()->project_version, - ::destroy, - ::execute, - getCapabilities, - ::setTracer, - nullptr, // set_option - }; - return &s_instance; + return InterpreterEvmcInstance::create(); } namespace dev @@ -142,7 +151,7 @@ namespace eth { void VM::trace() noexcept { - if (g_traceCallback) + if (m_traceCallback) { auto const& metrics = c_metrics[static_cast(m_OP)]; evmc_uint256be topStackItem; @@ -152,7 +161,7 @@ void VM::trace() noexcept topStackItem = toEvmC(m_SPP[0]); pushedStackItem = &topStackItem; } - g_traceCallback(g_traceContext, m_PC, EVMC_SUCCESS, m_io_gas, m_stackEnd - m_SPP, + m_traceCallback(m_traceContext, m_PC, EVMC_SUCCESS, m_io_gas, m_stackEnd - m_SPP, pushedStackItem, m_mem.size(), 0, 0, nullptr); } } @@ -288,7 +297,8 @@ evmc_tx_context const& VM::getTxContext() // interpreter entry point owning_bytes_ref VM::exec(evmc_context* _context, evmc_revision _rev, const evmc_message* _msg, - uint8_t const* _code, size_t _codeSize) + uint8_t const* _code, size_t _codeSize, evmc_trace_callback _traceCallback, + evmc_tracer_context* _traceContext) { m_context = _context; m_rev = _rev; @@ -297,6 +307,8 @@ owning_bytes_ref VM::exec(evmc_context* _context, evmc_revision _rev, const evmc m_PC = 0; m_pCode = _code; m_codeSize = _codeSize; + m_traceCallback = _traceCallback; + m_traceContext = _traceContext; // trampoline to minimize depth of call stack when calling out m_bounce = &VM::initEntry; diff --git a/libaleth-interpreter/VM.h b/libaleth-interpreter/VM.h index 4fef4a4b476..84fd2100850 100644 --- a/libaleth-interpreter/VM.h +++ b/libaleth-interpreter/VM.h @@ -66,7 +66,8 @@ class VM VM() = default; owning_bytes_ref exec(evmc_context* _context, evmc_revision _rev, const evmc_message* _msg, - uint8_t const* _code, size_t _codeSize); + uint8_t const* _code, size_t _codeSize, evmc_trace_callback _traceCallback, + evmc_tracer_context* _traceContext); uint64_t m_io_gas = 0; private: @@ -115,6 +116,8 @@ class VM uint64_t m_newMemSize = 0; uint64_t m_copyMemSize = 0; + evmc_trace_callback m_traceCallback = nullptr; + evmc_tracer_context* m_traceContext = nullptr; void trace() noexcept; // initialize interpreter From 95fc2c6e9cfaf73ee6cca0fc3450c0c689cd0d8b Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Tue, 7 Aug 2018 14:49:18 +0200 Subject: [PATCH 06/19] Move trace() call to common place (NEXT / CONTINUE / BREAK macros) instead of repeating it in every opcode implementation. --- libaleth-interpreter/VM.cpp | 20 ++++++------------- libaleth-interpreter/VM.h | 2 +- libaleth-interpreter/VMConfig.h | 35 ++++++++++++++++++++++----------- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/libaleth-interpreter/VM.cpp b/libaleth-interpreter/VM.cpp index d4d94cdd6af..0dfff5a49c2 100644 --- a/libaleth-interpreter/VM.cpp +++ b/libaleth-interpreter/VM.cpp @@ -149,7 +149,7 @@ namespace dev { namespace eth { -void VM::trace() noexcept +void VM::trace(uint64_t _pc) noexcept { if (m_traceCallback) { @@ -161,7 +161,7 @@ void VM::trace() noexcept topStackItem = toEvmC(m_SPP[0]); pushedStackItem = &topStackItem; } - m_traceCallback(m_traceContext, m_PC, EVMC_SUCCESS, m_io_gas, m_stackEnd - m_SPP, + m_traceCallback(m_traceContext, _pc, EVMC_SUCCESS, m_io_gas, m_stackEnd - m_SPP, pushedStackItem, m_mem.size(), 0, 0, nullptr); } } @@ -449,7 +449,6 @@ void VM::interpretCases() updateIOGas(); m_SPP[0] = (u256)*(h256 const*)(m_mem.data() + (unsigned)m_SP[0]); - trace(); } NEXT @@ -460,7 +459,6 @@ void VM::interpretCases() updateIOGas(); *(h256*)&m_mem[(unsigned)m_SP[0]] = (h256)m_SP[1]; - trace(); } NEXT @@ -1202,14 +1200,11 @@ void VM::interpretCases() // get val at two-byte offset into const pool and advance pc by one-byte remainder TRACE_OP(2, m_PC, m_OP); unsigned off; - uint64_t pc = m_PC; - ++pc; - off = m_code[pc++] << 8; - off |= m_code[pc++]; - pc += m_code[pc]; + ++m_PC; + off = m_code[m_PC++] << 8; + off |= m_code[m_PC++]; + m_PC += m_code[m_PC]; m_SPP[0] = m_pool[off]; - trace(); - m_PC = pc; TRACE_VAL(2, "Retrieved pooled const", m_SPP[0]); #else throwBadInstruction(); @@ -1222,7 +1217,6 @@ void VM::interpretCases() ON_OP(); updateIOGas(); m_SPP[0] = m_code[m_PC + 1]; - trace(); m_PC += 2; } CONTINUE @@ -1271,7 +1265,6 @@ void VM::interpretCases() for (; numBytes--; ++codeOffset) m_SPP[0] = (m_SPP[0] << 8) | m_code[codeOffset]; - trace(); m_PC = codeOffset; } CONTINUE @@ -1417,7 +1410,6 @@ void VM::interpretCases() } updateIOGas(); - trace(); } NEXT diff --git a/libaleth-interpreter/VM.h b/libaleth-interpreter/VM.h index 84fd2100850..87746d78a86 100644 --- a/libaleth-interpreter/VM.h +++ b/libaleth-interpreter/VM.h @@ -118,7 +118,7 @@ class VM evmc_trace_callback m_traceCallback = nullptr; evmc_tracer_context* m_traceContext = nullptr; - void trace() noexcept; + void trace(uint64_t _pc) noexcept; // initialize interpreter void initEntry(); diff --git a/libaleth-interpreter/VMConfig.h b/libaleth-interpreter/VMConfig.h index b94ac62316a..13c56b89d14 100644 --- a/libaleth-interpreter/VMConfig.h +++ b/libaleth-interpreter/VMConfig.h @@ -124,18 +124,24 @@ namespace eth #if EVM_SWITCH_DISPATCH #define INIT_CASES -#define DO_CASES \ - for (;;) \ - { \ - fetchInstruction(); \ - switch (m_OP) \ +#define DO_CASES \ + for (;;) \ + { \ + fetchInstruction(); \ + auto startPC = m_PC; \ + switch (m_OP) \ { #define CASE(name) case Instruction::name: -#define NEXT \ - ++m_PC; \ +#define NEXT \ + trace(startPC); \ + ++m_PC; \ break; -#define CONTINUE continue; -#define BREAK return; +#define CONTINUE \ + trace(startPC); \ + continue; +#define BREAK \ + trace(startPC); \ + return; #define DEFAULT default: #define WHILE_CASES \ } \ @@ -410,19 +416,24 @@ namespace eth &&SUICIDE, \ }; -#define DO_CASES \ - fetchInstruction(); \ +#define DO_CASES \ + fetchInstruction(); \ + auto startPC = m_PC; \ goto* jumpTable[(int)m_OP]; #define CASE(name) \ name: #define NEXT \ + trace(startPC); \ ++m_PC; \ fetchInstruction(); \ goto* jumpTable[(int)m_OP]; #define CONTINUE \ + trace(startPC); \ fetchInstruction(); \ goto* jumpTable[(int)m_OP]; -#define BREAK return; +#define BREAK \ + trace(startPC); \ + return; #define DEFAULT #define WHILE_CASES From 97153628d692fa2a359449d681b5deaa6b726b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 13 Nov 2018 17:18:09 +0100 Subject: [PATCH 07/19] tracing: random dump --- libevm/EVMC.cpp | 55 +++++++++++++++++++++++++++---------------------- libevm/EVMC.h | 26 +++++++++++++++++++++-- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index 45ce4cc62f0..e5bc93dad66 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -53,27 +53,13 @@ EVM::Result EVM::execute(ExtVMFace& _ext, int64_t gas) EVMC::EVMC(evmc_instance* _instance) : EVM(_instance) { static const auto tracer = [](evmc_tracer_context * _context, size_t _codeOffset, - evmc_status_code _statusCode, int64_t _gasLeft, size_t _stackNumItems, - evmc_uint256be const* _pushedStackItem, size_t _memorySize, + evmc_status_code _statusCode, int64_t _gasLeft, size_t /*_stackNumItems*/, + evmc_uint256be const* /*_pushedStackItem*/, size_t /*_memorySize*/, size_t /*changed_memory_offset*/, size_t /*changed_memory_size*/, uint8_t const* /*changed_memory*/) noexcept { EVMC* evmc = reinterpret_cast(_context); - - // TODO: It might be easier to just pass instruction from VM. - char const* name = evmc->m_instructionNames[evmc->m_code[_codeOffset]]; - - std::ostringstream logMessage; - logMessage << "EVMC " - << " " << evmc->m_step++ << " " << _codeOffset << " " << name << " " - << _statusCode << " " << _gasLeft << " " << _stackNumItems; - - if (_pushedStackItem) - logMessage << " +[" << fromEvmC(*_pushedStackItem) << "]"; - - logMessage << " " << _memorySize; - - LOG(evmc->m_vmTraceLogger) << logMessage.str(); + evmc->trace.emplace_back(InstructionTrace{evmc->m_code[_codeOffset], _codeOffset, _statusCode, _gasLeft}); }; _instance->set_tracer(_instance, tracer, reinterpret_cast(this)); @@ -94,18 +80,18 @@ owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp assert(_ext.depth <= static_cast(std::numeric_limits::max())); m_code = bytesConstRef{&_ext.code}; - m_step = 0; - // FIXME: EVMC revision found twice. - m_instructionNames = evmc_get_instruction_names_table(toRevision(_ext.evmSchedule())); + auto gas = static_cast(io_gas); + calls.emplace_back(CallTrace{static_cast(_ext.depth), + _ext.isCreate ? EVMC_CREATE : EVMC_CALL, trace.size(), trace.size(), gas}); - auto gas = static_cast(io_gas); - LOG(m_vmTraceLogger) << "EVMC message START " << _ext.depth << " " << _ext.caller << " -> " - << _ext.myAddress << " gas: " << gas << "\n"; EVM::Result r = execute(_ext, gas); - LOG(m_vmTraceLogger) << "EVMC message END " << _ext.depth << " status: " << r.status() - << " gas left: " << r.gasLeft() << "\n"; + + calls.back().end = trace.size(); + + if (_ext.depth == 0) + dumpTrace(); switch (r.status()) { @@ -171,5 +157,24 @@ evmc_revision EVM::toRevision(EVMSchedule const& _schedule) return EVMC_HOMESTEAD; return EVMC_FRONTIER; } + +void EVMC::dumpTrace() +{ + auto names = evmc_get_instruction_names_table(EVMC_LATEST_REVISION); + + for (auto& c : calls) + { + std::cout << "call " << c.kind << " " << c.gas << "\n"; + for (size_t i = c.begin; i < c.end; ++i) + { + std::cout << names[trace[i].opcode] << "\n"; + } + std::cout << "endcall \n"; + } + + calls.clear(); + trace.clear(); +} + } // namespace eth } // namespace dev diff --git a/libevm/EVMC.h b/libevm/EVMC.h index 0d4f1d65e4b..d924b413492 100644 --- a/libevm/EVMC.h +++ b/libevm/EVMC.h @@ -82,6 +82,25 @@ class EVM }; + +struct InstructionTrace +{ + uint8_t opcode; + size_t codeOffset; + evmc_status_code statusCode; + int64_t gasLeft; +}; + +struct CallTrace +{ + int depth; + evmc_call_kind kind; + size_t begin; + size_t end; + int64_t gas; +}; + + /// The wrapper implementing the VMFace interface with a EVMC VM as a backend. class EVMC : public EVM, public VMFace { @@ -92,10 +111,13 @@ class EVMC : public EVM, public VMFace private: bytesConstRef m_code; - int m_step = 0; - char const* const* m_instructionNames = nullptr; Logger m_vmTraceLogger{createLogger(VerbosityTrace, "vmtrace")}; + + std::vector calls; + std::vector trace; + + void dumpTrace(); }; } } From b58625f6012b9c63ed8ba9885c9728691e5cc1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 20 Nov 2018 12:24:37 +0100 Subject: [PATCH 08/19] interpreter: Fix tracing PC --- libaleth-interpreter/VM.cpp | 10 +++++++--- libaleth-interpreter/VM.h | 3 ++- libaleth-interpreter/VMConfig.h | 16 ++++++++-------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/libaleth-interpreter/VM.cpp b/libaleth-interpreter/VM.cpp index 0dfff5a49c2..97763734e3b 100644 --- a/libaleth-interpreter/VM.cpp +++ b/libaleth-interpreter/VM.cpp @@ -149,10 +149,14 @@ namespace dev { namespace eth { -void VM::trace(uint64_t _pc) noexcept +void VM::trace() noexcept { if (m_traceCallback) { + // Skip the code extension added by the optimizer. + if (m_tracePC >= m_codeSize) + return; + auto const& metrics = c_metrics[static_cast(m_OP)]; evmc_uint256be topStackItem; evmc_uint256be const* pushedStackItem = nullptr; @@ -161,7 +165,7 @@ void VM::trace(uint64_t _pc) noexcept topStackItem = toEvmC(m_SPP[0]); pushedStackItem = &topStackItem; } - m_traceCallback(m_traceContext, _pc, EVMC_SUCCESS, m_io_gas, m_stackEnd - m_SPP, + m_traceCallback(m_traceContext, m_tracePC, EVMC_SUCCESS, m_io_gas, m_stackEnd - m_SPP, pushedStackItem, m_mem.size(), 0, 0, nullptr); } } @@ -306,7 +310,7 @@ owning_bytes_ref VM::exec(evmc_context* _context, evmc_revision _rev, const evmc m_io_gas = uint64_t(_msg->gas); m_PC = 0; m_pCode = _code; - m_codeSize = _codeSize; + m_codeSize = _codeSize; ///< The size of the original code. m_traceCallback = _traceCallback; m_traceContext = _traceContext; diff --git a/libaleth-interpreter/VM.h b/libaleth-interpreter/VM.h index 87746d78a86..b5bdc374e9c 100644 --- a/libaleth-interpreter/VM.h +++ b/libaleth-interpreter/VM.h @@ -108,6 +108,7 @@ class VM // interpreter state Instruction m_OP; // current operation uint64_t m_PC = 0; // program counter + uint64_t m_tracePC = 0; // program counter for tracing u256* m_SP = m_stackEnd; // stack pointer u256* m_SPP = m_SP; // stack pointer prime (next SP) @@ -118,7 +119,7 @@ class VM evmc_trace_callback m_traceCallback = nullptr; evmc_tracer_context* m_traceContext = nullptr; - void trace(uint64_t _pc) noexcept; + void trace() noexcept; // initialize interpreter void initEntry(); diff --git a/libaleth-interpreter/VMConfig.h b/libaleth-interpreter/VMConfig.h index 13c56b89d14..95950e3c940 100644 --- a/libaleth-interpreter/VMConfig.h +++ b/libaleth-interpreter/VMConfig.h @@ -416,23 +416,23 @@ namespace eth &&SUICIDE, \ }; -#define DO_CASES \ - fetchInstruction(); \ - auto startPC = m_PC; \ +#define DO_CASES \ + fetchInstruction(); \ goto* jumpTable[(int)m_OP]; #define CASE(name) \ - name: + name: \ + m_tracePC = m_PC; // Safe the current PC for tracing. #define NEXT \ - trace(startPC); \ + trace(); \ ++m_PC; \ fetchInstruction(); \ goto* jumpTable[(int)m_OP]; #define CONTINUE \ - trace(startPC); \ + trace(); \ fetchInstruction(); \ goto* jumpTable[(int)m_OP]; -#define BREAK \ - trace(startPC); \ +#define BREAK \ + trace(); \ return; #define DEFAULT #define WHILE_CASES From 505992f119b6e31730ac6546fd20eb780d65f9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 20 Nov 2018 12:46:07 +0100 Subject: [PATCH 09/19] evm: Tune EVMC tracing dump --- libevm/EVMC.cpp | 40 +++++++++++++++++++++++++++++++++++----- libevm/EVMC.h | 1 + 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index e5bc93dad66..3fdaba86697 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -54,12 +54,16 @@ EVMC::EVMC(evmc_instance* _instance) : EVM(_instance) { static const auto tracer = [](evmc_tracer_context * _context, size_t _codeOffset, evmc_status_code _statusCode, int64_t _gasLeft, size_t /*_stackNumItems*/, - evmc_uint256be const* /*_pushedStackItem*/, size_t /*_memorySize*/, + evmc_uint256be const* _pushedStackItem, size_t /*_memorySize*/, size_t /*changed_memory_offset*/, size_t /*changed_memory_size*/, uint8_t const* /*changed_memory*/) noexcept { EVMC* evmc = reinterpret_cast(_context); - evmc->trace.emplace_back(InstructionTrace{evmc->m_code[_codeOffset], _codeOffset, _statusCode, _gasLeft}); + boost::optional pushedStackItem; + if (_pushedStackItem) + pushedStackItem = *_pushedStackItem; + evmc->trace.emplace_back(InstructionTrace{evmc->m_code[_codeOffset], _codeOffset, + _statusCode, _gasLeft, pushedStackItem}); }; _instance->set_tracer(_instance, tracer, reinterpret_cast(this)); @@ -158,18 +162,44 @@ evmc_revision EVM::toRevision(EVMSchedule const& _schedule) return EVMC_FRONTIER; } +static char const* to_string(evmc_call_kind _kind) +{ + switch (_kind) + { + case EVMC_CALL: + return "CALL"; + case EVMC_CALLCODE: + return "CALLCODE"; + case EVMC_DELEGATECALL: + return "DELEGATECALL"; + case EVMC_CREATE: + return "CREATE"; + case EVMC_CREATE2: + return "CREATE2"; + } + return ""; +} + void EVMC::dumpTrace() { auto names = evmc_get_instruction_names_table(EVMC_LATEST_REVISION); for (auto& c : calls) { - std::cout << "call " << c.kind << " " << c.gas << "\n"; + std::string indent; + for (int i = 0; i <= c.depth; ++i) + indent.push_back(' '); + + std::cout << ">>>>" << indent << to_string(c.kind) << " gas: " << c.gas << "\n"; for (size_t i = c.begin; i < c.end; ++i) { - std::cout << names[trace[i].opcode] << "\n"; + std::cout << std::hex << std::setfill('0') << std::setw(4) << trace[i].codeOffset + << indent << " " << names[trace[i].opcode]; + if (trace[i].pushedStackItem) + std::cout << " (" << fromEvmC(*trace[i].pushedStackItem) << ")"; + std::cout << "\n"; } - std::cout << "endcall \n"; + std::cout << "<<<<\n"; } calls.clear(); diff --git a/libevm/EVMC.h b/libevm/EVMC.h index d924b413492..3cd915924c0 100644 --- a/libevm/EVMC.h +++ b/libevm/EVMC.h @@ -89,6 +89,7 @@ struct InstructionTrace size_t codeOffset; evmc_status_code statusCode; int64_t gasLeft; + boost::optional pushedStackItem; }; struct CallTrace From ea6fedc7c60f6a4164e1b0366677a582e605f2f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 20 Nov 2018 13:52:25 +0100 Subject: [PATCH 10/19] evm: Make interpreter a singleton --- libevm/VMFactory.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libevm/VMFactory.cpp b/libevm/VMFactory.cpp index 72c27f00c3a..e3d07d88e31 100644 --- a/libevm/VMFactory.cpp +++ b/libevm/VMFactory.cpp @@ -187,7 +187,10 @@ VMPtr VMFactory::create(VMKind _kind) switch (_kind) { case VMKind::Interpreter: - return {new EVMC{evmc_create_interpreter()}, default_delete}; + { + static std::unique_ptr s_interpreter{new EVMC{evmc_create_interpreter()}}; + return {s_interpreter.get(), null_delete}; + } case VMKind::DLL: assert(g_evmcDll != nullptr); // Return "fake" owning pointer to global EVMC DLL VM. From 72391b5cdaa0fa6f9e72776b7ebbd7f5ae6bfa4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 20 Nov 2018 15:33:36 +0100 Subject: [PATCH 11/19] evm: Proper call nesting in tracing --- libevm/EVMC.cpp | 66 ++++++++++++++++++++++++++++++++----------------- libevm/EVMC.h | 12 +++++---- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index 3fdaba86697..37e4319d17e 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -62,8 +62,10 @@ EVMC::EVMC(evmc_instance* _instance) : EVM(_instance) boost::optional pushedStackItem; if (_pushedStackItem) pushedStackItem = *_pushedStackItem; - evmc->trace.emplace_back(InstructionTrace{evmc->m_code[_codeOffset], _codeOffset, - _statusCode, _gasLeft, pushedStackItem}); + + auto opcode = evmc->m_code[_codeOffset]; + evmc->m_calls[evmc->m_currentCall].trace.emplace_back(InstructionTrace{ + opcode, _codeOffset, _statusCode, _gasLeft, pushedStackItem, -1}); }; _instance->set_tracer(_instance, tracer, reinterpret_cast(this)); @@ -87,12 +89,21 @@ owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp auto gas = static_cast(io_gas); - calls.emplace_back(CallTrace{static_cast(_ext.depth), - _ext.isCreate ? EVMC_CREATE : EVMC_CALL, trace.size(), trace.size(), gas}); + m_calls.emplace_back( + CallTrace{static_cast(_ext.depth), _ext.isCreate ? EVMC_CREATE : EVMC_CALL, gas, {}}); + auto parentCall = m_currentCall; + m_currentCall = m_calls.size() - 1; + if (_ext.depth > 0) + { + // The parent trace should have a call instructions. + // TODO: Create this instruction after the call. + m_calls[parentCall].trace.back().callIndex = m_currentCall; + } EVM::Result r = execute(_ext, gas); - calls.back().end = trace.size(); + m_prevCall = m_currentCall; + m_currentCall = parentCall; if (_ext.depth == 0) dumpTrace(); @@ -180,30 +191,41 @@ static char const* to_string(evmc_call_kind _kind) return ""; } +static void dumpCallTrace( + const std::vector& calls, size_t index, char const* const* names) +{ + auto& c = calls[index]; + + std::string indent; + for (int i = 0; i <= c.depth; ++i) + indent.push_back(' '); + + std::cout << ">>>>" << indent << to_string(c.kind) << " gas: " << std::dec << c.gas << "\n"; + for (auto& trace : c.trace) + { + std::cout << std::hex << std::setfill('0') << std::setw(4) << trace.codeOffset + << indent << " " << names[trace.opcode]; + if (trace.pushedStackItem) + std::cout << " (" << fromEvmC(*trace.pushedStackItem) << ")"; + std::cout << "\n"; + + if (trace.callIndex >= 0) + dumpCallTrace(calls, trace.callIndex, names); + } + std::cout << "<<<<\n"; +} + void EVMC::dumpTrace() { auto names = evmc_get_instruction_names_table(EVMC_LATEST_REVISION); - for (auto& c : calls) + for (size_t i = 0; i < m_calls.size(); ++i) { - std::string indent; - for (int i = 0; i <= c.depth; ++i) - indent.push_back(' '); - - std::cout << ">>>>" << indent << to_string(c.kind) << " gas: " << c.gas << "\n"; - for (size_t i = c.begin; i < c.end; ++i) - { - std::cout << std::hex << std::setfill('0') << std::setw(4) << trace[i].codeOffset - << indent << " " << names[trace[i].opcode]; - if (trace[i].pushedStackItem) - std::cout << " (" << fromEvmC(*trace[i].pushedStackItem) << ")"; - std::cout << "\n"; - } - std::cout << "<<<<\n"; + if (m_calls[i].depth == 0) + dumpCallTrace(m_calls, i, names); } - calls.clear(); - trace.clear(); + m_calls.clear(); } } // namespace eth diff --git a/libevm/EVMC.h b/libevm/EVMC.h index 3cd915924c0..fd89cb6d964 100644 --- a/libevm/EVMC.h +++ b/libevm/EVMC.h @@ -81,7 +81,7 @@ class EVM evmc_instance* m_instance = nullptr; }; - +struct CallTrace; struct InstructionTrace { @@ -90,15 +90,16 @@ struct InstructionTrace evmc_status_code statusCode; int64_t gasLeft; boost::optional pushedStackItem; + int callIndex; }; struct CallTrace { int depth; evmc_call_kind kind; - size_t begin; - size_t end; int64_t gas; + + std::vector trace; }; @@ -115,8 +116,9 @@ class EVMC : public EVM, public VMFace Logger m_vmTraceLogger{createLogger(VerbosityTrace, "vmtrace")}; - std::vector calls; - std::vector trace; + std::vector m_calls; + size_t m_currentCall = 0; + size_t m_prevCall = 0; void dumpTrace(); }; From 4818a99efa6a893ccbb5029ffff55dc2ae02d823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 20 Nov 2018 15:39:53 +0100 Subject: [PATCH 12/19] evm: Restore previous code for tracing --- libevm/EVMC.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index 37e4319d17e..6e6cbd8ee63 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -85,6 +85,7 @@ owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp assert(_ext.envInfo().gasLimit() <= int64max); assert(_ext.depth <= static_cast(std::numeric_limits::max())); + auto prevCode = m_code; m_code = bytesConstRef{&_ext.code}; auto gas = static_cast(io_gas); @@ -105,6 +106,8 @@ owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp m_prevCall = m_currentCall; m_currentCall = parentCall; + m_code = prevCode; + if (_ext.depth == 0) dumpTrace(); From 75732d2cef39133ddb76afbb6550c1815f2fe056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 20 Nov 2018 15:51:25 +0100 Subject: [PATCH 13/19] evm: Remove unused field from EVMC --- libevm/EVMC.cpp | 1 - libevm/EVMC.h | 1 - 2 files changed, 2 deletions(-) diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index 6e6cbd8ee63..050fe9771eb 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -103,7 +103,6 @@ owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp EVM::Result r = execute(_ext, gas); - m_prevCall = m_currentCall; m_currentCall = parentCall; m_code = prevCode; diff --git a/libevm/EVMC.h b/libevm/EVMC.h index fd89cb6d964..4613a197b85 100644 --- a/libevm/EVMC.h +++ b/libevm/EVMC.h @@ -118,7 +118,6 @@ class EVMC : public EVM, public VMFace std::vector m_calls; size_t m_currentCall = 0; - size_t m_prevCall = 0; void dumpTrace(); }; From 868258a7c3541aff980a23b7bc5078246f6ae9cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 28 Nov 2018 13:49:31 +0100 Subject: [PATCH 14/19] evm: In tracing handle call after they are finished --- libaleth-interpreter/VM.cpp | 7 +++++-- libaleth-interpreter/VMCalls.cpp | 2 ++ libaleth-interpreter/VMConfig.h | 1 - libevm/EVMC.cpp | 20 +++++++++++--------- libevm/EVMC.h | 3 ++- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/libaleth-interpreter/VM.cpp b/libaleth-interpreter/VM.cpp index 97763734e3b..4ddd28a6c60 100644 --- a/libaleth-interpreter/VM.cpp +++ b/libaleth-interpreter/VM.cpp @@ -383,7 +383,8 @@ void VM::interpretCases() uint64_t b = (uint64_t)m_SP[0]; uint64_t s = (uint64_t)m_SP[1]; m_output = owning_bytes_ref{std::move(m_mem), b, s}; - m_bounce = 0; + m_bounce = nullptr; + trace(); } BREAK @@ -430,6 +431,7 @@ void VM::interpretCases() updateIOGas(); m_context->host->selfdestruct(m_context, &m_message->destination, &destination); m_bounce = nullptr; + trace(); } BREAK @@ -437,7 +439,8 @@ void VM::interpretCases() { ON_OP(); updateIOGas(); - m_bounce = 0; + m_bounce = nullptr; + trace(); } BREAK diff --git a/libaleth-interpreter/VMCalls.cpp b/libaleth-interpreter/VMCalls.cpp index 1ffb818e949..47b0a302907 100644 --- a/libaleth-interpreter/VMCalls.cpp +++ b/libaleth-interpreter/VMCalls.cpp @@ -163,6 +163,7 @@ void VM::caseCreate() } else m_SPP[0] = 0; + trace(); ++m_PC; } @@ -194,6 +195,7 @@ void VM::caseCall() m_SPP[0] = 0; m_io_gas += msg.gas; } + trace(); ++m_PC; } diff --git a/libaleth-interpreter/VMConfig.h b/libaleth-interpreter/VMConfig.h index 95950e3c940..c4cef7ed420 100644 --- a/libaleth-interpreter/VMConfig.h +++ b/libaleth-interpreter/VMConfig.h @@ -432,7 +432,6 @@ namespace eth fetchInstruction(); \ goto* jumpTable[(int)m_OP]; #define BREAK \ - trace(); \ return; #define DEFAULT #define WHILE_CASES diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index 050fe9771eb..9af852296ac 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -50,6 +50,12 @@ EVM::Result EVM::execute(ExtVMFace& _ext, int64_t gas) evmc_execute(m_instance, &_ext, mode, &msg, _ext.code.data(), _ext.code.size())}; } +static inline bool isCall(uint8_t _opcode) noexcept +{ + return _opcode == OP_CALL || _opcode == OP_CALLCODE || _opcode == OP_DELEGATECALL || + _opcode == OP_CREATE || _opcode == OP_CREATE2; +} + EVMC::EVMC(evmc_instance* _instance) : EVM(_instance) { static const auto tracer = [](evmc_tracer_context * _context, size_t _codeOffset, @@ -64,8 +70,9 @@ EVMC::EVMC(evmc_instance* _instance) : EVM(_instance) pushedStackItem = *_pushedStackItem; auto opcode = evmc->m_code[_codeOffset]; + auto callIndex = isCall(opcode) ? evmc->m_prevCall : -1; evmc->m_calls[evmc->m_currentCall].trace.emplace_back(InstructionTrace{ - opcode, _codeOffset, _statusCode, _gasLeft, pushedStackItem, -1}); + opcode, _codeOffset, _statusCode, _gasLeft, pushedStackItem, callIndex}); }; _instance->set_tracer(_instance, tracer, reinterpret_cast(this)); @@ -94,15 +101,10 @@ owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp CallTrace{static_cast(_ext.depth), _ext.isCreate ? EVMC_CREATE : EVMC_CALL, gas, {}}); auto parentCall = m_currentCall; m_currentCall = m_calls.size() - 1; - if (_ext.depth > 0) - { - // The parent trace should have a call instructions. - // TODO: Create this instruction after the call. - m_calls[parentCall].trace.back().callIndex = m_currentCall; - } EVM::Result r = execute(_ext, gas); + m_prevCall = m_currentCall; m_currentCall = parentCall; m_code = prevCode; @@ -205,8 +207,8 @@ static void dumpCallTrace( std::cout << ">>>>" << indent << to_string(c.kind) << " gas: " << std::dec << c.gas << "\n"; for (auto& trace : c.trace) { - std::cout << std::hex << std::setfill('0') << std::setw(4) << trace.codeOffset - << indent << " " << names[trace.opcode]; + std::cout << std::hex << std::setfill('0') << std::setw(4) << trace.codeOffset << indent + << " " << names[trace.opcode]; if (trace.pushedStackItem) std::cout << " (" << fromEvmC(*trace.pushedStackItem) << ")"; std::cout << "\n"; diff --git a/libevm/EVMC.h b/libevm/EVMC.h index 4613a197b85..61780182228 100644 --- a/libevm/EVMC.h +++ b/libevm/EVMC.h @@ -117,7 +117,8 @@ class EVMC : public EVM, public VMFace Logger m_vmTraceLogger{createLogger(VerbosityTrace, "vmtrace")}; std::vector m_calls; - size_t m_currentCall = 0; + int m_currentCall = -1; + int m_prevCall = -1; void dumpTrace(); }; From 1a37110ca2a68bbd9b3c48592469849f288fcf2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 28 Nov 2018 14:09:35 +0100 Subject: [PATCH 15/19] evm: Better text output of trace --- libevm/EVMC.cpp | 12 +++++++----- libevm/EVMC.h | 3 +++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index 9af852296ac..17f49f002f2 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -97,13 +97,14 @@ owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp auto gas = static_cast(io_gas); - m_calls.emplace_back( - CallTrace{static_cast(_ext.depth), _ext.isCreate ? EVMC_CREATE : EVMC_CALL, gas, {}}); + m_calls.emplace_back(CallTrace{static_cast(_ext.depth), + _ext.isCreate ? EVMC_CREATE : EVMC_CALL, gas, -1, _ext.caller, _ext.myAddress, {}}); auto parentCall = m_currentCall; m_currentCall = m_calls.size() - 1; EVM::Result r = execute(_ext, gas); + m_calls[m_currentCall].gasLeft = r.gasLeft(); m_prevCall = m_currentCall; m_currentCall = parentCall; @@ -204,11 +205,12 @@ static void dumpCallTrace( for (int i = 0; i <= c.depth; ++i) indent.push_back(' '); - std::cout << ">>>>" << indent << to_string(c.kind) << " gas: " << std::dec << c.gas << "\n"; + std::cout << ">>>>" << indent << "> " << to_string(c.kind) << " " << c.sender << " > " + << c.destination << " gas: " << std::dec << c.gas << "\n"; for (auto& trace : c.trace) { std::cout << std::hex << std::setfill('0') << std::setw(4) << trace.codeOffset << indent - << " " << names[trace.opcode]; + << " " << std::left << std::setfill(' ') << std::setw(7) << names[trace.opcode]; if (trace.pushedStackItem) std::cout << " (" << fromEvmC(*trace.pushedStackItem) << ")"; std::cout << "\n"; @@ -216,7 +218,7 @@ static void dumpCallTrace( if (trace.callIndex >= 0) dumpCallTrace(calls, trace.callIndex, names); } - std::cout << "<<<<\n"; + std::cout << "<<<<" << indent << "< gas left: " << std::dec << c.gasLeft << "\n"; } void EVMC::dumpTrace() diff --git a/libevm/EVMC.h b/libevm/EVMC.h index 61780182228..73d9c03f58e 100644 --- a/libevm/EVMC.h +++ b/libevm/EVMC.h @@ -98,6 +98,9 @@ struct CallTrace int depth; evmc_call_kind kind; int64_t gas; + int64_t gasLeft; + Address sender; + Address destination; std::vector trace; }; From c27cfb882546648b4a5659337d4f3055caab5692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 28 Nov 2018 14:38:50 +0100 Subject: [PATCH 16/19] evm: Report status code in EVMC tracing --- libaleth-interpreter/VM.cpp | 2 +- libaleth-interpreter/VM.h | 1 + libaleth-interpreter/VMCalls.cpp | 17 ++++++++++++++++- libevm/EVMC.cpp | 11 ++++++++--- libevm/EVMC.h | 1 + 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/libaleth-interpreter/VM.cpp b/libaleth-interpreter/VM.cpp index 4ddd28a6c60..17d78138c5d 100644 --- a/libaleth-interpreter/VM.cpp +++ b/libaleth-interpreter/VM.cpp @@ -165,7 +165,7 @@ void VM::trace() noexcept topStackItem = toEvmC(m_SPP[0]); pushedStackItem = &topStackItem; } - m_traceCallback(m_traceContext, m_tracePC, EVMC_SUCCESS, m_io_gas, m_stackEnd - m_SPP, + m_traceCallback(m_traceContext, m_tracePC, m_traceStatus, m_io_gas, m_stackEnd - m_SPP, pushedStackItem, m_mem.size(), 0, 0, nullptr); } } diff --git a/libaleth-interpreter/VM.h b/libaleth-interpreter/VM.h index b5bdc374e9c..de52da2791e 100644 --- a/libaleth-interpreter/VM.h +++ b/libaleth-interpreter/VM.h @@ -109,6 +109,7 @@ class VM Instruction m_OP; // current operation uint64_t m_PC = 0; // program counter uint64_t m_tracePC = 0; // program counter for tracing + evmc_status_code m_traceStatus = EVMC_SUCCESS; // the status of current instruction u256* m_SP = m_stackEnd; // stack pointer u256* m_SPP = m_SP; // stack pointer prime (next SP) diff --git a/libaleth-interpreter/VMCalls.cpp b/libaleth-interpreter/VMCalls.cpp index 47b0a302907..047065d472f 100644 --- a/libaleth-interpreter/VMCalls.cpp +++ b/libaleth-interpreter/VMCalls.cpp @@ -41,21 +41,29 @@ void VM::copyDataToMemory(bytesConstRef _data, u256*_sp) void VM::throwOutOfGas() { + m_traceStatus = EVMC_OUT_OF_GAS; + trace(); BOOST_THROW_EXCEPTION(OutOfGas()); } void VM::throwBadInstruction() { + m_traceStatus = EVMC_UNDEFINED_INSTRUCTION; + trace(); BOOST_THROW_EXCEPTION(BadInstruction()); } void VM::throwBadJumpDestination() { + m_traceStatus = EVMC_BAD_JUMP_DESTINATION; + trace(); BOOST_THROW_EXCEPTION(BadJumpDestination()); } void VM::throwDisallowedStateChange() { + m_traceStatus = EVMC_STATIC_MODE_VIOLATION; + trace(); BOOST_THROW_EXCEPTION(DisallowedStateChange()); } @@ -65,7 +73,10 @@ void VM::throwDisallowedStateChange() void VM::throwBadStack(int _removed, int _added) { bigint size = m_stackEnd - m_SPP; - if (size < _removed) + bool underflow = size < _removed; + m_traceStatus = underflow ? EVMC_STACK_UNDERFLOW : EVMC_STACK_OVERFLOW; + trace(); + if (underflow) BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_removed, size)); else BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_added - _removed), size)); @@ -75,11 +86,15 @@ void VM::throwRevertInstruction(owning_bytes_ref&& _output) { // We can't use BOOST_THROW_EXCEPTION here because it makes a copy of exception inside and // RevertInstruction has no copy constructor + m_traceStatus = EVMC_REVERT; + trace(); throw RevertInstruction(std::move(_output)); } void VM::throwBufferOverrun(bigint const& _endOfAccess) { + m_traceStatus = EVMC_INVALID_MEMORY_ACCESS; + trace(); BOOST_THROW_EXCEPTION(BufferOverrun() << RequirementError(_endOfAccess, bigint(m_returnData.size()))); } diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index 17f49f002f2..374a1818b4d 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -97,13 +97,15 @@ owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp auto gas = static_cast(io_gas); - m_calls.emplace_back(CallTrace{static_cast(_ext.depth), - _ext.isCreate ? EVMC_CREATE : EVMC_CALL, gas, -1, _ext.caller, _ext.myAddress, {}}); + m_calls.emplace_back( + CallTrace{static_cast(_ext.depth), _ext.isCreate ? EVMC_CREATE : EVMC_CALL, + EVMC_SUCCESS, gas, -1, _ext.caller, _ext.myAddress, {}}); auto parentCall = m_currentCall; m_currentCall = m_calls.size() - 1; EVM::Result r = execute(_ext, gas); + m_calls[m_currentCall].status = r.status(); m_calls[m_currentCall].gasLeft = r.gasLeft(); m_prevCall = m_currentCall; m_currentCall = parentCall; @@ -213,12 +215,15 @@ static void dumpCallTrace( << " " << std::left << std::setfill(' ') << std::setw(7) << names[trace.opcode]; if (trace.pushedStackItem) std::cout << " (" << fromEvmC(*trace.pushedStackItem) << ")"; + if (trace.statusCode != EVMC_SUCCESS) + std::cout << " <" << trace.statusCode << ">"; std::cout << "\n"; if (trace.callIndex >= 0) dumpCallTrace(calls, trace.callIndex, names); } - std::cout << "<<<<" << indent << "< gas left: " << std::dec << c.gasLeft << "\n"; + std::cout << "<<<<" << indent << "< <" << c.status << "> " + << "gas left: " << std::dec << c.gasLeft << "\n"; } void EVMC::dumpTrace() diff --git a/libevm/EVMC.h b/libevm/EVMC.h index 73d9c03f58e..72b78990466 100644 --- a/libevm/EVMC.h +++ b/libevm/EVMC.h @@ -97,6 +97,7 @@ struct CallTrace { int depth; evmc_call_kind kind; + evmc_status_code status; int64_t gas; int64_t gasLeft; Address sender; From 9a3925394d3ff4e709102d1a61c7d56543b73d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 29 Nov 2018 10:37:20 +0100 Subject: [PATCH 17/19] evm: Properly reset prev call --- libevm/EVMC.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index 374a1818b4d..f71c212238a 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -97,6 +97,9 @@ owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp auto gas = static_cast(io_gas); + if (_ext.depth == 0) + m_prevCall = -1; // Reset prev call. + m_calls.emplace_back( CallTrace{static_cast(_ext.depth), _ext.isCreate ? EVMC_CREATE : EVMC_CALL, EVMC_SUCCESS, gas, -1, _ext.caller, _ext.myAddress, {}}); From 01ce909fbd11bd41e0d1d6a925411bf3007795f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 29 Nov 2018 12:38:45 +0100 Subject: [PATCH 18/19] evm: Only provide top stack item in trace with execution was successful --- libaleth-interpreter/VM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libaleth-interpreter/VM.cpp b/libaleth-interpreter/VM.cpp index 17d78138c5d..bd225ddef9c 100644 --- a/libaleth-interpreter/VM.cpp +++ b/libaleth-interpreter/VM.cpp @@ -160,7 +160,7 @@ void VM::trace() noexcept auto const& metrics = c_metrics[static_cast(m_OP)]; evmc_uint256be topStackItem; evmc_uint256be const* pushedStackItem = nullptr; - if (metrics.num_stack_returned_items >= 1) + if (m_traceStatus == EVMC_SUCCESS && metrics.num_stack_returned_items >= 1) { topStackItem = toEvmC(m_SPP[0]); pushedStackItem = &topStackItem; From cb39b51a97656e6788edf14efa48bca71ab9d5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 29 Nov 2018 14:05:54 +0100 Subject: [PATCH 19/19] evm: Handle undefined opcodes in tracing --- libevm/EVMC.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index f71c212238a..7002541c1c3 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -214,8 +214,13 @@ static void dumpCallTrace( << c.destination << " gas: " << std::dec << c.gas << "\n"; for (auto& trace : c.trace) { - std::cout << std::hex << std::setfill('0') << std::setw(4) << trace.codeOffset << indent - << " " << std::left << std::setfill(' ') << std::setw(7) << names[trace.opcode]; + auto* name = names[trace.opcode]; + std::cout << std::hex << std::right << std::setfill('0') << std::setw(4) << trace.codeOffset + << indent << " "; + if (name) + std::cout << std::left << std::setfill(' ') << std::setw(7) << name; + else + std::cout << "<" << std::hex << std::right << std::setfill('0') << std::setw(2) << int(trace.opcode) << "> "; if (trace.pushedStackItem) std::cout << " (" << fromEvmC(*trace.pushedStackItem) << ")"; if (trace.statusCode != EVMC_SUCCESS)