Skip to content

Commit

Permalink
Merge pull request #323 from ethereum/histogram_tracer
Browse files Browse the repository at this point in the history
Histogram tracer
  • Loading branch information
chfast authored May 21, 2021
2 parents 2c1f473 + 174cc7e commit 0aba799
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 11 deletions.
6 changes: 2 additions & 4 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ jobs:
executor: linux-gcc-latest
environment:
BUILD_TYPE: Coverage
TESTS_FILTER: unittests
TESTS_FILTER: unittests|integration
steps:
- build
- test
Expand All @@ -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
Expand All @@ -300,7 +298,7 @@ jobs:
executor: linux-clang-latest
environment:
BUILD_TYPE: Coverage
TESTS_FILTER: unittests
TESTS_FILTER: unittests|integration
steps:
- build
- run:
Expand Down
1 change: 1 addition & 0 deletions lib/evmone/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ add_library(evmone
instructions_calls.cpp
limits.hpp
opcodes_helpers.h
tracing.cpp
tracing.hpp
vm.cpp
vm.hpp
Expand Down
73 changes: 73 additions & 0 deletions lib/evmone/tracing.cpp
Original file line number Diff line number Diff line change
@@ -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 <evmc/hex.hpp>
#include <stack>

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<Context> 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<uint8_t>(i)) << ',' << ctx.counts[i] << '\n';
}

m_contexts.pop();
}

public:
explicit HistogramTracer(std::ostream& out) noexcept : m_out{out} {}
};
} // namespace

std::unique_ptr<Tracer> create_histogram_tracer(std::ostream& out)
{
return std::make_unique<HistogramTracer>(out);
}
} // namespace evmone
8 changes: 8 additions & 0 deletions lib/evmone/tracing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <evmc/instructions.h>
#include <memory>
#include <ostream>
#include <string_view>

namespace evmone
Expand Down Expand Up @@ -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<Tracer> create_histogram_tracer(std::ostream& out);

} // namespace evmone
22 changes: 16 additions & 6 deletions lib/evmone/vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "baseline.hpp"
#include "execution.hpp"
#include <evmone/evmone.h>
#include <iostream>

namespace evmone
{
Expand All @@ -25,22 +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* name, char const* value) noexcept
evmc_set_option_result set_option(evmc_vm* c_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{};
auto& vm = *static_cast<VM*>(c_vm);

if (name == "O")
{
if (value[0] == '0' && value[1] == '\0') // O=0
if (value == "0")
{
vm->execute = evmone::baseline::execute;
c_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;
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;
}

Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
24 changes: 24 additions & 0 deletions test/integration/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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 $<TARGET_FILE:evmc::tool> --vm $<TARGET_FILE:evmone>,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()
42 changes: 41 additions & 1 deletion test/unittests/tracing_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ class tracing : public Test
vm{*static_cast<evmone::VM*>(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();
Expand Down Expand Up @@ -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
)");
}

0 comments on commit 0aba799

Please sign in to comment.