Skip to content

Commit

Permalink
Add tracing output for t8n (#616)
Browse files Browse the repository at this point in the history
Add `--trace` option for `evmone-t8n` to output traces to specific files
compatible with `retesteth`.

Modify the tracing format to follow closer the specification at
[EIP-3155: EVM trace
specification](https://eips.ethereum.org/EIPS/eip-3155).
  • Loading branch information
chfast authored Aug 22, 2023
2 parents 35f4141 + 69bc40f commit 6608cf3
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 110 deletions.
46 changes: 16 additions & 30 deletions lib/evmone/tracing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@ 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;
Expand All @@ -94,15 +96,9 @@ class InstructionTracer : public Tracer
}

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 @@ -114,35 +110,25 @@ class InstructionTracer : public Tracer
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_out << R"(,"gas":"0x)" << std::hex << gas << '"';
m_out << 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_out << "}\n";
}
m_out << R"(,"memSize":)" << std::dec << state.memory.size();

void on_execution_end(const evmc_result& result) noexcept override
{
const auto& ctx = m_contexts.top();
output_stack(stack_top, stack_height);
if (!state.return_data.empty())
m_out << R"(,"returnData":"0x)" << evmc::hex(state.return_data) << '"';
m_out << R"(,"depth":)" << std::dec << (ctx.depth + 1);
m_out << R"(,"refund":)" << std::dec << state.gas_refund;
m_out << R"(,"opName":")" << get_name(opcode) << '"';

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";

m_contexts.pop();
}

void on_execution_end(const evmc_result& /*result*/) noexcept override { m_contexts.pop(); }

public:
explicit InstructionTracer(std::ostream& out) noexcept : m_out{out}
{
Expand Down
4 changes: 2 additions & 2 deletions lib/evmone/vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ 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
12 changes: 5 additions & 7 deletions test/integration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,11 @@ DUP1,4
add_test(NAME ${PREFIX}/trace COMMAND $<TARGET_FILE:evmc::tool> --vm $<TARGET_FILE:evmone>,trace run 60006002800103)
set_tests_properties(
${PREFIX}/trace PROPERTIES PASS_REGULAR_EXPRESSION
"{\"depth\":0,\"rev\":\"Shanghai\",\"static\":false}
{\"pc\":0,\"op\":96,\"opName\":\"PUSH1\",\"gas\":0xf4240,\"stack\":\\[\\],\"memorySize\":0}
{\"pc\":2,\"op\":96,\"opName\":\"PUSH1\",\"gas\":0xf423d,\"stack\":\\[\"0x0\"\\],\"memorySize\":0}
{\"pc\":4,\"op\":128,\"opName\":\"DUP1\",\"gas\":0xf423a,\"stack\":\\[\"0x0\",\"0x2\"\\],\"memorySize\":0}
{\"pc\":5,\"op\":1,\"opName\":\"ADD\",\"gas\":0xf4237,\"stack\":\\[\"0x0\",\"0x2\",\"0x2\"\\],\"memorySize\":0}
{\"pc\":6,\"op\":3,\"opName\":\"SUB\",\"gas\":0xf4234,\"stack\":\\[\"0x0\",\"0x4\"\\],\"memorySize\":0}
{\"error\":null,\"gas\":0xf4231,\"gasUsed\":0xf,\"output\":\"\"}
"\"pc\":0,\"op\":96,\"gas\":\"0xf4240\",\"gasCost\":\"0x3\",\"memSize\":0,\"stack\":\\[\\],\"depth\":1,\"refund\":0,\"opName\":\"PUSH1\"}
{\"pc\":2,\"op\":96,\"gas\":\"0xf423d\",\"gasCost\":\"0x3\",\"memSize\":0,\"stack\":\\[\"0x0\"\\],\"depth\":1,\"refund\":0,\"opName\":\"PUSH1\"}
{\"pc\":4,\"op\":128,\"gas\":\"0xf423a\",\"gasCost\":\"0x3\",\"memSize\":0,\"stack\":\\[\"0x0\",\"0x2\"\\],\"depth\":1,\"refund\":0,\"opName\":\"DUP1\"}
{\"pc\":5,\"op\":1,\"gas\":\"0xf4237\",\"gasCost\":\"0x3\",\"memSize\":0,\"stack\":\\[\"0x0\",\"0x2\",\"0x2\"\\],\"depth\":1,\"refund\":0,\"opName\":\"ADD\"}
{\"pc\":6,\"op\":3,\"gas\":\"0xf4234\",\"gasCost\":\"0x3\",\"memSize\":0,\"stack\":\\[\"0x0\",\"0x4\"\\],\"depth\":1,\"refund\":0,\"opName\":\"SUB\"}
")

endif()
Expand Down
2 changes: 1 addition & 1 deletion test/integration/statetest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ add_test(
)
set_tests_properties(
${PREFIX}/trace PROPERTIES
PASS_REGULAR_EXPRESSION [=[\{"pc":4,"op":1,"opName":"ADD","gas":0x5c872,"stack":\["0x1","0x1"\],"memorySize":0\}]=]
PASS_REGULAR_EXPRESSION [=[\{"pc":4,"op":1,"gas":"0x5c872","gasCost":"0x3","memSize":0,"stack":\["0x1","0x1"\],"depth":1,"refund":0,"opName":"ADD"\}]=]
)

add_test(
Expand Down
35 changes: 30 additions & 5 deletions test/t8n/t8n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,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 trace = false;

try
{
Expand Down Expand Up @@ -64,6 +65,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")
trace = true;
}

state::BlockInfo block;
Expand Down Expand Up @@ -97,6 +100,9 @@ int main(int argc, const char* argv[])

evmc::VM vm{evmc_create_evmone(), {{"O", "0"}}};

if (trace)
vm.set_option("trace", "0");

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

if (j_txs.is_array())
Expand All @@ -110,8 +116,7 @@ int main(int argc, const char* argv[])
tx.chain_id = chain_id;

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

auto res = state::transition(state, block, tx, rev, vm, block_gas_left);
const auto computed_tx_hash_str = hex0x(computed_tx_hash);

if (j_txs[i].contains("hash"))
{
Expand All @@ -120,15 +125,31 @@ 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;
const auto orig_clog_buf = std::clog.rdbuf();
if (trace)
{
const auto output_filename =
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, block_gas_left);

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 @@ -142,7 +163,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;
receipt.cumulative_gas_used = cumulative_gas_used;
Expand All @@ -161,6 +182,10 @@ int main(int argc, const char* argv[])
block_gas_left -= receipt.gas_used;
receipts.emplace_back(std::move(receipt));
}

// Restore original std::clog buffer (otherwise std::clog crashes at exit).
if (trace)
std::clog.rdbuf(orig_clog_buf);
}
}

Expand Down
Loading

0 comments on commit 6608cf3

Please sign in to comment.