Skip to content

Commit

Permalink
Add tracing output for t8n
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet committed Aug 7, 2023
1 parent 095f83a commit eacb66d
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 105 deletions.
70 changes: 37 additions & 33 deletions lib/evmone/tracing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "execution_state.hpp"
#include "instructions_traits.hpp"
#include <evmc/hex.hpp>
#include <fstream>
#include <sstream>
#include <stack>

namespace evmone
Expand Down Expand Up @@ -70,39 +72,36 @@ class InstructionTracer : public Tracer
{
struct Context
{
const int32_t depth;
const uint8_t* const code; ///< Reference to the code being executed.
const int64_t start_gas;

Context(const uint8_t* c, int64_t g) noexcept : code{c}, start_gas{g} {}
Context(int32_t d, const uint8_t* c, int64_t g) noexcept : depth{d}, code{c}, start_gas{g}
{}
};

std::stack<Context> m_contexts;
std::ostream& m_out; ///< Output stream.
std::ostringstream m_buf;

void output_stack(const intx::uint256* stack_top, int stack_height)
{
m_out << R"(,"stack":[)";
m_buf << R"(,"stack":[)";
const auto stack_end = stack_top + 1;
const auto stack_begin = stack_end - stack_height;
for (auto it = stack_begin; it != stack_end; ++it)
{
if (it != stack_begin)
m_out << ',';
m_out << R"("0x)" << to_string(*it, 16) << '"';
m_buf << ',';
m_buf << R"("0x)" << to_string(*it, 16) << '"';
}
m_out << ']';
m_buf << ']';
}

void on_execution_start(
evmc_revision rev, const evmc_message& msg, bytes_view code) noexcept override
evmc_revision /*rev*/, const evmc_message& msg, bytes_view code) noexcept override
{
m_contexts.emplace(code.data(), msg.gas);

m_out << "{";
m_out << R"("depth":)" << msg.depth;
m_out << R"(,"rev":")" << rev << '"';
m_out << R"(,"static":)" << (((msg.flags & EVMC_STATIC) != 0) ? "true" : "false");
m_out << "}\n";
m_contexts.emplace(msg.depth, code.data(), msg.gas);
}

void on_instruction_start(uint32_t pc, const intx::uint256* stack_top, int stack_height,
Expand All @@ -111,34 +110,39 @@ class InstructionTracer : public Tracer
const auto& ctx = m_contexts.top();

const auto opcode = ctx.code[pc];
m_out << "{";
m_out << R"("pc":)" << std::dec << pc;
m_out << R"(,"op":)" << std::dec << int{opcode};
m_out << R"(,"opName":")" << get_name(opcode) << '"';
m_out << R"(,"gas":0x)" << std::hex << gas;
output_stack(stack_top, stack_height);
m_buf << "{";
m_buf << R"("pc":)" << std::dec << pc;
m_buf << R"(,"op":)" << std::dec << int{opcode};
m_buf << R"(,"gas":"0x)" << std::hex << gas << '"';
m_buf << R"(,"gasCost":"0x)" << std::hex << instr::gas_costs[state.rev][opcode] << '"';

// Full memory can be dumped as evmc::hex({state.memory.data(), state.memory.size()}),
// but this should not be done by default. Adding --tracing=+memory option would be nice.
m_out << R"(,"memorySize":)" << std::dec << state.memory.size();
m_buf << R"(,"memSize":)" << std::dec << state.memory.size();

output_stack(stack_top, stack_height);
if (!state.return_data.empty())
m_buf << R"("returnData":)"
<< "0x" << evmc::hex(state.return_data);
m_buf << R"(,"depth":)" << std::dec << (ctx.depth + 1);
m_buf << R"(,"refund":)" << std::dec << state.gas_refund;
m_buf << R"(,"opName":")" << get_name(opcode) << '"';

m_out << "}\n";
m_buf << "}\n";

m_out << m_buf.str();
m_buf.str({});
}

void on_execution_end(const evmc_result& result) noexcept override
{
const auto& ctx = m_contexts.top();

m_out << "{";
m_out << R"("error":)";
if (result.status_code == EVMC_SUCCESS)
m_out << "null";
else
m_out << '"' << result.status_code << '"';
m_out << R"(,"gas":)" << std::hex << "0x" << result.gas_left;
m_out << R"(,"gasUsed":)" << std::hex << "0x" << (ctx.start_gas - result.gas_left);
m_out << R"(,"output":")" << evmc::hex({result.output_data, result.output_size}) << '"';
m_out << "}\n";
if (result.status_code != EVMC_SUCCESS)
{
m_buf << R"({"error":)" << '"' << result.status_code << '"' << '}';
m_buf << "\n";
m_out << m_buf.str();
m_buf.str({});
}

m_contexts.pop();
}
Expand Down
6 changes: 4 additions & 2 deletions lib/evmone/vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "baseline.hpp"
#include <evmone/evmone.h>
#include <cassert>
#include <filesystem>
#include <iostream>

namespace evmone
Expand Down Expand Up @@ -53,12 +54,13 @@ evmc_set_option_result set_option(evmc_vm* c_vm, char const* c_name, char const*
}
else if (name == "trace")
{
vm.add_tracer(create_instruction_tracer(std::cerr));
vm.add_tracer(create_instruction_tracer(std::clog));

return EVMC_SET_OPTION_SUCCESS;
}
else if (name == "histogram")
{
vm.add_tracer(create_histogram_tracer(std::cerr));
vm.add_tracer(create_histogram_tracer(std::clog));
return EVMC_SET_OPTION_SUCCESS;
}
return EVMC_SET_OPTION_INVALID_NAME;
Expand Down
34 changes: 28 additions & 6 deletions test/t8n/t8n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ int main(int argc, const char* argv[])
fs::path output_body_file;
std::optional<uint64_t> block_reward;
uint64_t chain_id = 0;
bool tracing_enabled = false;

try
{
Expand Down Expand Up @@ -63,6 +64,8 @@ int main(int argc, const char* argv[])
chain_id = intx::from_string<uint64_t>(argv[i]);
else if (arg == "--output.body" && ++i < argc)
output_body_file = argv[i];
else if (arg == "--trace")
tracing_enabled = true;
}

state::BlockInfo block;
Expand Down Expand Up @@ -93,7 +96,12 @@ int main(int argc, const char* argv[])
{
const auto j_txs = json::json::parse(std::ifstream{txs_file});

evmc::VM vm{evmc_create_evmone(), {{"O", "0"}}};
std::initializer_list<std::pair<const char*, const char*>> options = {{"O", "0"}};

if (tracing_enabled)
options = {{"trace", ""}};

evmc::VM vm{evmc_create_evmone(), options};

std::vector<state::Log> txs_logs;

Expand All @@ -107,9 +115,8 @@ int main(int argc, const char* argv[])
auto tx = test::from_json<state::Transaction>(j_txs[i]);
tx.chain_id = chain_id;

auto res = state::transition(state, block, tx, rev, vm);

const auto computed_tx_hash = keccak256(rlp::encode(tx));
const auto computed_tx_hash_str = hex0x(computed_tx_hash);

if (j_txs[i].contains("hash"))
{
Expand All @@ -118,15 +125,30 @@ int main(int argc, const char* argv[])

if (loaded_tx_hash_opt != computed_tx_hash)
throw std::logic_error("transaction hash mismatched: computed " +
hex0x(computed_tx_hash) + ", expected " +
computed_tx_hash_str + ", expected " +
hex0x(loaded_tx_hash_opt.value()));
}

std::ofstream trace_file_output;
if (tracing_enabled)
{
auto output_filename =
fs::path(output_dir / ("trace-" + std::to_string(i) + "-" +
computed_tx_hash_str + ".jsonl"));

// `trace` flag enables trace logging to std::clog.
// Redirect std::clog to the output file.
trace_file_output.open(output_filename);
std::clog.rdbuf(trace_file_output.rdbuf());
}

auto res = state::transition(state, block, tx, rev, vm);

if (holds_alternative<std::error_code>(res))
{
const auto ec = std::get<std::error_code>(res);
json::json j_rejected_tx;
j_rejected_tx["hash"] = hex0x(computed_tx_hash);
j_rejected_tx["hash"] = computed_tx_hash_str;
j_rejected_tx["index"] = i;
j_rejected_tx["error"] = ec.message();
j_result["rejected"].push_back(j_rejected_tx);
Expand All @@ -140,7 +162,7 @@ int main(int argc, const char* argv[])
txs_logs.insert(txs_logs.end(), tx_logs.begin(), tx_logs.end());
auto& j_receipt = j_result["receipts"][j_result["receipts"].size()];

j_receipt["transactionHash"] = hex0x(computed_tx_hash);
j_receipt["transactionHash"] = computed_tx_hash_str;
j_receipt["gasUsed"] = hex0x(static_cast<uint64_t>(receipt.gas_used));
cumulative_gas_used += receipt.gas_used;
j_receipt["cumulativeGasUsed"] = hex0x(cumulative_gas_used);
Expand Down
1 change: 1 addition & 0 deletions test/unittests/evmone_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <evmone/evmone.h>
#include <evmone/vm.hpp>
#include <gtest/gtest.h>
#include <filesystem>

TEST(evmone, info)
{
Expand Down
Loading

0 comments on commit eacb66d

Please sign in to comment.