-
Notifications
You must be signed in to change notification settings - Fork 294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Histogram tracer #323
Histogram tracer #323
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
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() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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(); | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this valid in CSV? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. But this is yaml document start mark. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The CSV can only have optional header line with column names. You cannot have any title line. The fancier YAML wrapper would be --- # HISTOGRAM
depth: 0
csv: |
opcode,count
ADD,1
PUSH1,2 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So why not output YAML and then whoever wants CSV can do the conversion themselves (or in worst-case by skipping the top 3 lines) ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because nobody wants YAML. I used |
||
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 | ||
)"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would add a comment (or on the class) explaining what this does, i.e. outputs a histogram of opcodes in a CSV format.