From 480b0588c5725e16129eded3ab7b4c6c6bb63532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 13 May 2021 20:41:53 +0200 Subject: [PATCH 1/5] Add Histogram tracer --- lib/evmone/CMakeLists.txt | 1 + lib/evmone/tracing.cpp | 73 +++++++++++++++++++++++++++++++++ lib/evmone/tracing.hpp | 8 ++++ test/unittests/tracing_test.cpp | 42 ++++++++++++++++++- 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 lib/evmone/tracing.cpp diff --git a/lib/evmone/CMakeLists.txt b/lib/evmone/CMakeLists.txt index c0e1229854..d345bdd538 100644 --- a/lib/evmone/CMakeLists.txt +++ b/lib/evmone/CMakeLists.txt @@ -21,6 +21,7 @@ add_library(evmone instructions_calls.cpp limits.hpp opcodes_helpers.h + tracing.cpp tracing.hpp vm.cpp vm.hpp diff --git a/lib/evmone/tracing.cpp b/lib/evmone/tracing.cpp new file mode 100644 index 0000000000..63cbd3d740 --- /dev/null +++ b/lib/evmone/tracing.cpp @@ -0,0 +1,73 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2021 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "tracing.hpp" +#include +#include + +namespace evmone +{ +namespace +{ +std::string get_name(const char* const* names, uint8_t opcode) +{ + const auto name = names[opcode]; + return (name != nullptr) ? name : "0x" + evmc::hex(opcode); +} + +/// @see create_histogram_tracer() +class HistogramTracer : public Tracer +{ + struct Context + { + const int32_t depth; + const uint8_t* const code; + const char* const* const opcode_names; + uint32_t counts[256]{}; + + Context(int32_t _depth, const uint8_t* _code, const char* const* _opcode_names) noexcept + : depth{_depth}, code{_code}, opcode_names{_opcode_names} + {} + }; + + std::stack m_contexts; + std::ostream& m_out; + + void on_execution_start( + evmc_revision rev, const evmc_message& msg, bytes_view code) noexcept override + { + m_contexts.emplace(msg.depth, code.data(), evmc_get_instruction_names_table(rev)); + } + + void on_instruction_start(uint32_t pc) noexcept override + { + auto& ctx = m_contexts.top(); + ++ctx.counts[ctx.code[pc]]; + } + + void on_execution_end(const evmc_result& /*result*/) noexcept override + { + const auto& ctx = m_contexts.top(); + const auto names = ctx.opcode_names; + + m_out << "--- # HISTOGRAM depth=" << ctx.depth << "\nopcode,count\n"; + for (size_t i = 0; i < std::size(ctx.counts); ++i) + { + if (ctx.counts[i] != 0) + m_out << get_name(names, static_cast(i)) << ',' << ctx.counts[i] << '\n'; + } + + m_contexts.pop(); + } + +public: + explicit HistogramTracer(std::ostream& out) noexcept : m_out{out} {} +}; +} // namespace + +std::unique_ptr create_histogram_tracer(std::ostream& out) +{ + return std::make_unique(out); +} +} // namespace evmone diff --git a/lib/evmone/tracing.hpp b/lib/evmone/tracing.hpp index e331539407..64c76d2b93 100644 --- a/lib/evmone/tracing.hpp +++ b/lib/evmone/tracing.hpp @@ -5,6 +5,7 @@ #include #include +#include #include namespace evmone @@ -48,4 +49,11 @@ class Tracer virtual void on_execution_end(const evmc_result& result) noexcept = 0; }; +/// Creates the "histogram" tracer which counts occurrences of individual opcodes during execution +/// and reports this data in CSV format. +/// +/// @param out Report output stream. +/// @return Histogram tracer object. +EVMC_EXPORT std::unique_ptr create_histogram_tracer(std::ostream& out); + } // namespace evmone diff --git a/test/unittests/tracing_test.cpp b/test/unittests/tracing_test.cpp index 6af270d94f..018d54b015 100644 --- a/test/unittests/tracing_test.cpp +++ b/test/unittests/tracing_test.cpp @@ -27,10 +27,11 @@ class tracing : public Test vm{*static_cast(m_baseline_vm.get_raw_pointer())} {} - std::string trace(bytes_view code) + std::string trace(bytes_view code, int32_t depth = 0) { evmc::MockedHost host; evmc_message msg{}; + msg.depth = depth; msg.gas = 1000000; m_baseline_vm.execute(host, EVMC_BERLIN, msg, code.data(), code.size()); auto result = trace_stream.str(); @@ -95,3 +96,42 @@ TEST_F(tracing, three_tracers) EXPECT_EQ(trace(dup1(0)), "A0:PUSH1 B0:PUSH1 C0:PUSH1 A2:DUP1 B2:DUP1 C2:DUP1 "); } + +TEST_F(tracing, histogram) +{ + vm.add_tracer(evmone::create_histogram_tracer(trace_stream)); + + trace_stream << '\n'; + EXPECT_EQ(trace(add(0, 0)), R"( +--- # HISTOGRAM depth=0 +opcode,count +ADD,1 +PUSH1,2 +)"); +} + +TEST_F(tracing, histogram_undefined_instruction) +{ + vm.add_tracer(evmone::create_histogram_tracer(trace_stream)); + + trace_stream << '\n'; + EXPECT_EQ(trace(bytecode{"EF"}), R"( +--- # HISTOGRAM depth=0 +opcode,count +0xef,1 +)"); +} + +TEST_F(tracing, histogram_internal_call) +{ + vm.add_tracer(evmone::create_histogram_tracer(trace_stream)); + trace_stream << '\n'; + EXPECT_EQ(trace(push(0) + OP_DUP1 + OP_SWAP1 + OP_POP + OP_POP, 1), R"( +--- # HISTOGRAM depth=1 +opcode,count +POP,2 +PUSH1,1 +DUP1,1 +SWAP1,1 +)"); +} From 8c507be9f3dc027a75e8dce4ef6d8d123020b586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 13 May 2021 21:13:24 +0200 Subject: [PATCH 2/5] Refactor set_option() implementation --- lib/evmone/vm.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/evmone/vm.cpp b/lib/evmone/vm.cpp index 870b93b397..b54975c033 100644 --- a/lib/evmone/vm.cpp +++ b/lib/evmone/vm.cpp @@ -25,16 +25,19 @@ constexpr evmc_capabilities_flagset get_capabilities(evmc_vm* /*vm*/) noexcept return EVMC_CAPABILITY_EVM1; } -evmc_set_option_result set_option(evmc_vm* vm, char const* name, char const* value) noexcept +evmc_set_option_result set_option(evmc_vm* vm, char const* c_name, char const* c_value) noexcept { - if (name[0] == 'O' && name[1] == '\0') + const auto name = (c_name != nullptr) ? std::string_view{c_name} : std::string_view{}; + const auto value = (c_value != nullptr) ? std::string_view{c_value} : std::string_view{}; + + if (name == "O") { - if (value[0] == '0' && value[1] == '\0') // O=0 + if (value == "0") { vm->execute = evmone::baseline::execute; return EVMC_SET_OPTION_SUCCESS; } - else if (value[0] == '2' && value[1] == '\0') // O=2 + else if (value == "2") { vm->execute = evmone::execute; return EVMC_SET_OPTION_SUCCESS; From fb41bc685c9e3c7205e6deb9a2b158414520c872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 13 May 2021 21:22:24 +0200 Subject: [PATCH 3/5] Add "histogram" option --- lib/evmone/vm.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/evmone/vm.cpp b/lib/evmone/vm.cpp index b54975c033..cf860cf226 100644 --- a/lib/evmone/vm.cpp +++ b/lib/evmone/vm.cpp @@ -9,6 +9,7 @@ #include "baseline.hpp" #include "execution.hpp" #include +#include namespace evmone { @@ -25,25 +26,31 @@ constexpr evmc_capabilities_flagset get_capabilities(evmc_vm* /*vm*/) noexcept return EVMC_CAPABILITY_EVM1; } -evmc_set_option_result set_option(evmc_vm* vm, char const* c_name, char const* c_value) noexcept +evmc_set_option_result set_option(evmc_vm* c_vm, char const* c_name, char const* c_value) noexcept { const auto name = (c_name != nullptr) ? std::string_view{c_name} : std::string_view{}; const auto value = (c_value != nullptr) ? std::string_view{c_value} : std::string_view{}; + auto& vm = *static_cast(c_vm); if (name == "O") { if (value == "0") { - vm->execute = evmone::baseline::execute; + c_vm->execute = evmone::baseline::execute; return EVMC_SET_OPTION_SUCCESS; } else if (value == "2") { - vm->execute = evmone::execute; + c_vm->execute = evmone::execute; return EVMC_SET_OPTION_SUCCESS; } return EVMC_SET_OPTION_INVALID_VALUE; } + else if (name == "histogram") + { + vm.add_tracer(create_histogram_tracer(std::cerr)); + return EVMC_SET_OPTION_SUCCESS; + } return EVMC_SET_OPTION_INVALID_NAME; } From a7dbbcfc367089ec547c29c86df586da4c989a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 14 May 2021 12:46:23 +0200 Subject: [PATCH 4/5] test: Add histogram integration test --- test/CMakeLists.txt | 1 + test/integration/CMakeLists.txt | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 test/integration/CMakeLists.txt diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 229582a9b2..af8eb1dba1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,6 +12,7 @@ find_package(benchmark CONFIG REQUIRED) add_subdirectory(utils) add_subdirectory(bench) +add_subdirectory(integration) add_subdirectory(internal_benchmarks) add_subdirectory(unittests) diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt new file mode 100644 index 0000000000..6d26d4a664 --- /dev/null +++ b/test/integration/CMakeLists.txt @@ -0,0 +1,24 @@ +# evmone: Fast Ethereum Virtual Machine implementation +# Copyright 2021 The evmone Authors. +# SPDX-License-Identifier: Apache-2.0 + +set(PREFIX ${PROJECT_NAME}/integration) + +get_target_property(EVMONE_LIB_TYPE evmone TYPE) +if(EVMONE_LIB_TYPE STREQUAL SHARED_LIBRARY) + + add_test(NAME ${PREFIX}/histogram COMMAND $ --vm $,O=0,histogram run 6000808080800101010200) + set_tests_properties( + ${PREFIX}/histogram PROPERTIES PASS_REGULAR_EXPRESSION + "--- # HISTOGRAM depth=0 +opcode,count +STOP,1 +ADD,3 +MUL,1 +PUSH1,1 +DUP1,4 +") + + get_property(ALL_TESTS DIRECTORY PROPERTY TESTS) + set_tests_properties("${ALL_TESTS}" PROPERTIES ENVIRONMENT LLVM_PROFILE_FILE=${CMAKE_BINARY_DIR}/integration-%p.profraw) +endif() From 174cc7eff9242a218703dbd747b568d7337bf0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 14 May 2021 13:43:12 +0200 Subject: [PATCH 5/5] ci: Include integration tests in coverage --- circle.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/circle.yml b/circle.yml index 0d90bb2064..e4312be3ab 100644 --- a/circle.yml +++ b/circle.yml @@ -268,7 +268,7 @@ jobs: executor: linux-gcc-latest environment: BUILD_TYPE: Coverage - TESTS_FILTER: unittests + TESTS_FILTER: unittests|integration steps: - build - test @@ -290,8 +290,6 @@ jobs: environment: CMAKE_OPTIONS: -DSANITIZE=undefined,implicit-conversion,nullability UBSAN_OPTIONS: halt_on_error=1 - # TODO: There is unresolved __ubsan_vptr_type_cache in evmone.so - TESTS_FILTER: unittests steps: - build - test @@ -300,7 +298,7 @@ jobs: executor: linux-clang-latest environment: BUILD_TYPE: Coverage - TESTS_FILTER: unittests + TESTS_FILTER: unittests|integration steps: - build - run: