Skip to content

Commit 0aba799

Browse files
authored
Merge pull request #323 from ethereum/histogram_tracer
Histogram tracer
2 parents 2c1f473 + 174cc7e commit 0aba799

File tree

8 files changed

+166
-11
lines changed

8 files changed

+166
-11
lines changed

circle.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ jobs:
268268
executor: linux-gcc-latest
269269
environment:
270270
BUILD_TYPE: Coverage
271-
TESTS_FILTER: unittests
271+
TESTS_FILTER: unittests|integration
272272
steps:
273273
- build
274274
- test
@@ -290,8 +290,6 @@ jobs:
290290
environment:
291291
CMAKE_OPTIONS: -DSANITIZE=undefined,implicit-conversion,nullability
292292
UBSAN_OPTIONS: halt_on_error=1
293-
# TODO: There is unresolved __ubsan_vptr_type_cache in evmone.so
294-
TESTS_FILTER: unittests
295293
steps:
296294
- build
297295
- test
@@ -300,7 +298,7 @@ jobs:
300298
executor: linux-clang-latest
301299
environment:
302300
BUILD_TYPE: Coverage
303-
TESTS_FILTER: unittests
301+
TESTS_FILTER: unittests|integration
304302
steps:
305303
- build
306304
- run:

lib/evmone/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ add_library(evmone
2121
instructions_calls.cpp
2222
limits.hpp
2323
opcodes_helpers.h
24+
tracing.cpp
2425
tracing.hpp
2526
vm.cpp
2627
vm.hpp

lib/evmone/tracing.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// evmone: Fast Ethereum Virtual Machine implementation
2+
// Copyright 2021 The evmone Authors.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include "tracing.hpp"
6+
#include <evmc/hex.hpp>
7+
#include <stack>
8+
9+
namespace evmone
10+
{
11+
namespace
12+
{
13+
std::string get_name(const char* const* names, uint8_t opcode)
14+
{
15+
const auto name = names[opcode];
16+
return (name != nullptr) ? name : "0x" + evmc::hex(opcode);
17+
}
18+
19+
/// @see create_histogram_tracer()
20+
class HistogramTracer : public Tracer
21+
{
22+
struct Context
23+
{
24+
const int32_t depth;
25+
const uint8_t* const code;
26+
const char* const* const opcode_names;
27+
uint32_t counts[256]{};
28+
29+
Context(int32_t _depth, const uint8_t* _code, const char* const* _opcode_names) noexcept
30+
: depth{_depth}, code{_code}, opcode_names{_opcode_names}
31+
{}
32+
};
33+
34+
std::stack<Context> m_contexts;
35+
std::ostream& m_out;
36+
37+
void on_execution_start(
38+
evmc_revision rev, const evmc_message& msg, bytes_view code) noexcept override
39+
{
40+
m_contexts.emplace(msg.depth, code.data(), evmc_get_instruction_names_table(rev));
41+
}
42+
43+
void on_instruction_start(uint32_t pc) noexcept override
44+
{
45+
auto& ctx = m_contexts.top();
46+
++ctx.counts[ctx.code[pc]];
47+
}
48+
49+
void on_execution_end(const evmc_result& /*result*/) noexcept override
50+
{
51+
const auto& ctx = m_contexts.top();
52+
const auto names = ctx.opcode_names;
53+
54+
m_out << "--- # HISTOGRAM depth=" << ctx.depth << "\nopcode,count\n";
55+
for (size_t i = 0; i < std::size(ctx.counts); ++i)
56+
{
57+
if (ctx.counts[i] != 0)
58+
m_out << get_name(names, static_cast<uint8_t>(i)) << ',' << ctx.counts[i] << '\n';
59+
}
60+
61+
m_contexts.pop();
62+
}
63+
64+
public:
65+
explicit HistogramTracer(std::ostream& out) noexcept : m_out{out} {}
66+
};
67+
} // namespace
68+
69+
std::unique_ptr<Tracer> create_histogram_tracer(std::ostream& out)
70+
{
71+
return std::make_unique<HistogramTracer>(out);
72+
}
73+
} // namespace evmone

lib/evmone/tracing.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <evmc/instructions.h>
77
#include <memory>
8+
#include <ostream>
89
#include <string_view>
910

1011
namespace evmone
@@ -48,4 +49,11 @@ class Tracer
4849
virtual void on_execution_end(const evmc_result& result) noexcept = 0;
4950
};
5051

52+
/// Creates the "histogram" tracer which counts occurrences of individual opcodes during execution
53+
/// and reports this data in CSV format.
54+
///
55+
/// @param out Report output stream.
56+
/// @return Histogram tracer object.
57+
EVMC_EXPORT std::unique_ptr<Tracer> create_histogram_tracer(std::ostream& out);
58+
5159
} // namespace evmone

lib/evmone/vm.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "baseline.hpp"
1010
#include "execution.hpp"
1111
#include <evmone/evmone.h>
12+
#include <iostream>
1213

1314
namespace evmone
1415
{
@@ -25,22 +26,31 @@ constexpr evmc_capabilities_flagset get_capabilities(evmc_vm* /*vm*/) noexcept
2526
return EVMC_CAPABILITY_EVM1;
2627
}
2728

28-
evmc_set_option_result set_option(evmc_vm* vm, char const* name, char const* value) noexcept
29+
evmc_set_option_result set_option(evmc_vm* c_vm, char const* c_name, char const* c_value) noexcept
2930
{
30-
if (name[0] == 'O' && name[1] == '\0')
31+
const auto name = (c_name != nullptr) ? std::string_view{c_name} : std::string_view{};
32+
const auto value = (c_value != nullptr) ? std::string_view{c_value} : std::string_view{};
33+
auto& vm = *static_cast<VM*>(c_vm);
34+
35+
if (name == "O")
3136
{
32-
if (value[0] == '0' && value[1] == '\0') // O=0
37+
if (value == "0")
3338
{
34-
vm->execute = evmone::baseline::execute;
39+
c_vm->execute = evmone::baseline::execute;
3540
return EVMC_SET_OPTION_SUCCESS;
3641
}
37-
else if (value[0] == '2' && value[1] == '\0') // O=2
42+
else if (value == "2")
3843
{
39-
vm->execute = evmone::execute;
44+
c_vm->execute = evmone::execute;
4045
return EVMC_SET_OPTION_SUCCESS;
4146
}
4247
return EVMC_SET_OPTION_INVALID_VALUE;
4348
}
49+
else if (name == "histogram")
50+
{
51+
vm.add_tracer(create_histogram_tracer(std::cerr));
52+
return EVMC_SET_OPTION_SUCCESS;
53+
}
4454
return EVMC_SET_OPTION_INVALID_NAME;
4555
}
4656

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ find_package(benchmark CONFIG REQUIRED)
1212

1313
add_subdirectory(utils)
1414
add_subdirectory(bench)
15+
add_subdirectory(integration)
1516
add_subdirectory(internal_benchmarks)
1617
add_subdirectory(unittests)
1718

test/integration/CMakeLists.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# evmone: Fast Ethereum Virtual Machine implementation
2+
# Copyright 2021 The evmone Authors.
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
set(PREFIX ${PROJECT_NAME}/integration)
6+
7+
get_target_property(EVMONE_LIB_TYPE evmone TYPE)
8+
if(EVMONE_LIB_TYPE STREQUAL SHARED_LIBRARY)
9+
10+
add_test(NAME ${PREFIX}/histogram COMMAND $<TARGET_FILE:evmc::tool> --vm $<TARGET_FILE:evmone>,O=0,histogram run 6000808080800101010200)
11+
set_tests_properties(
12+
${PREFIX}/histogram PROPERTIES PASS_REGULAR_EXPRESSION
13+
"--- # HISTOGRAM depth=0
14+
opcode,count
15+
STOP,1
16+
ADD,3
17+
MUL,1
18+
PUSH1,1
19+
DUP1,4
20+
")
21+
22+
get_property(ALL_TESTS DIRECTORY PROPERTY TESTS)
23+
set_tests_properties("${ALL_TESTS}" PROPERTIES ENVIRONMENT LLVM_PROFILE_FILE=${CMAKE_BINARY_DIR}/integration-%p.profraw)
24+
endif()

test/unittests/tracing_test.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ class tracing : public Test
2727
vm{*static_cast<evmone::VM*>(m_baseline_vm.get_raw_pointer())}
2828
{}
2929

30-
std::string trace(bytes_view code)
30+
std::string trace(bytes_view code, int32_t depth = 0)
3131
{
3232
evmc::MockedHost host;
3333
evmc_message msg{};
34+
msg.depth = depth;
3435
msg.gas = 1000000;
3536
m_baseline_vm.execute(host, EVMC_BERLIN, msg, code.data(), code.size());
3637
auto result = trace_stream.str();
@@ -95,3 +96,42 @@ TEST_F(tracing, three_tracers)
9596

9697
EXPECT_EQ(trace(dup1(0)), "A0:PUSH1 B0:PUSH1 C0:PUSH1 A2:DUP1 B2:DUP1 C2:DUP1 ");
9798
}
99+
100+
TEST_F(tracing, histogram)
101+
{
102+
vm.add_tracer(evmone::create_histogram_tracer(trace_stream));
103+
104+
trace_stream << '\n';
105+
EXPECT_EQ(trace(add(0, 0)), R"(
106+
--- # HISTOGRAM depth=0
107+
opcode,count
108+
ADD,1
109+
PUSH1,2
110+
)");
111+
}
112+
113+
TEST_F(tracing, histogram_undefined_instruction)
114+
{
115+
vm.add_tracer(evmone::create_histogram_tracer(trace_stream));
116+
117+
trace_stream << '\n';
118+
EXPECT_EQ(trace(bytecode{"EF"}), R"(
119+
--- # HISTOGRAM depth=0
120+
opcode,count
121+
0xef,1
122+
)");
123+
}
124+
125+
TEST_F(tracing, histogram_internal_call)
126+
{
127+
vm.add_tracer(evmone::create_histogram_tracer(trace_stream));
128+
trace_stream << '\n';
129+
EXPECT_EQ(trace(push(0) + OP_DUP1 + OP_SWAP1 + OP_POP + OP_POP, 1), R"(
130+
--- # HISTOGRAM depth=1
131+
opcode,count
132+
POP,2
133+
PUSH1,1
134+
DUP1,1
135+
SWAP1,1
136+
)");
137+
}

0 commit comments

Comments
 (0)