From d91198a335b68d932175eb141ad653085b65ed96 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 9 Jul 2014 19:36:00 +0100 Subject: [PATCH] Proper debugger. --- alethzero/Main.ui | 76 ++++++++++++++++++- alethzero/MainWin.cpp | 149 +++++++++++++++++++++++++++----------- alethzero/MainWin.h | 17 ++++- libethereum/Executive.cpp | 54 ++++++-------- libethereum/Executive.h | 9 ++- libethereum/ExtVM.h | 18 +++-- libethereum/State.cpp | 12 +-- libethereum/State.h | 4 +- libevm/ExtVMFace.h | 3 + libevm/VM.h | 38 ++-------- test/vm.cpp | 4 +- 11 files changed, 257 insertions(+), 127 deletions(-) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 68b6adae6..532333deb 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -163,6 +163,7 @@ + @@ -518,11 +519,24 @@ Qt::Vertical - + + + QFrame::NoFrame + + + 0 + + Qt::NoFocus + + QFrame::NoFrame + + + 0 + @@ -930,6 +944,21 @@ false + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + @@ -942,6 +971,12 @@ 0 + + QFrame::NoFrame + + + 0 + @@ -954,11 +989,23 @@ Qt::Vertical + + QFrame::NoFrame + + + 0 + QAbstractItemView::NoSelection + + QFrame::NoFrame + + + 0 + true @@ -967,6 +1014,12 @@ + + QFrame::NoFrame + + + 0 + true @@ -975,6 +1028,19 @@ + + + + Ubuntu Mono + + + + QFrame::NoFrame + + + 0 + + @@ -1450,6 +1516,14 @@ font-size: 14pt &Clear Pending + + + false + + + &Dump Standard Trace + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index b5018e1a6..c3dffaa39 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -898,16 +898,7 @@ void Main::on_blocks_currentItemChanged() Transaction t = st.pending()[txi]; auto r = t.rlp(); - bool done = m_currentExecution->setup(&r); - if (!done) - { - debugFinished(); - auto startGas = m_currentExecution->vm().gas(); - for (; !done; done = m_currentExecution->go(1)) - m_history.append(WorldState({m_currentExecution->vm().curPC(), m_currentExecution->vm().gas(), startGas - m_currentExecution->vm().gas(), m_currentExecution->vm().stack(), m_currentExecution->vm().memory(), m_currentExecution->state().storage(m_currentExecution->ext().myAddress)})); - initDebugger(); - updateDebugger(); - } + populateDebugger(&r); m_currentExecution.reset(); } @@ -916,6 +907,39 @@ void Main::on_blocks_currentItemChanged() } } +void Main::populateDebugger(eth::bytesConstRef _r) +{ + bool done = m_currentExecution->setup(_r); + if (!done) + { + debugFinished(); + vector levels; + m_codes.clear(); + bytesConstRef lastExtCode; + h256 lastHash; + auto onOp = [&](uint64_t steps, Instruction inst, unsigned newMemSize, eth::bigint gasCost, void* voidVM, void const* voidExt) + { + eth::VM& vm = *(eth::VM*)voidVM; + eth::ExtVM const& ext = *(eth::ExtVM const*)voidExt; + if (ext.code != lastExtCode) + { + lastExtCode = ext.code; + lastHash = sha3(lastExtCode); + if (!m_codes.count(lastHash)) + m_codes[lastHash] = ext.code.toBytes(); + } + if (levels.size() < ext.level) + levels.push_back(&m_history.back()); + else + levels.resize(ext.level); + m_history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), lastHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); + }; + m_currentExecution->go(onOp); + initDebugger(); + updateDebugger(); + } +} + void Main::on_contracts_currentItemChanged() { ui->contractInfo->clear(); @@ -1236,15 +1260,7 @@ void Main::on_debug_clicked() t.receiveAddress = isCreation() ? Address() : fromString(ui->destination->currentText()); t.sign(s); auto r = t.rlp(); - bool done = m_currentExecution->setup(&r); - if (!done) - { - auto startGas = m_currentExecution->vm().gas(); - for (; !done; done = m_currentExecution->go(1)) - m_history.append(WorldState({m_currentExecution->vm().curPC(), m_currentExecution->vm().gas(), startGas - m_currentExecution->vm().gas(), m_currentExecution->vm().stack(), m_currentExecution->vm().memory(), m_currentExecution->state().storage(m_currentExecution->ext().myAddress)})); - initDebugger(); - updateDebugger(); - } + populateDebugger(&r); m_currentExecution.reset(); return; } @@ -1265,17 +1281,35 @@ void Main::on_create_triggered() void Main::on_debugStep_triggered() { ui->debugTimeline->setValue(ui->debugTimeline->value() + 1); + ui->callStack->setCurrentRow(0); } void Main::on_debugStepback_triggered() { ui->debugTimeline->setValue(ui->debugTimeline->value() - 1); + ui->callStack->setCurrentRow(0); +} + +void Main::on_dumpTrace_triggered() +{ + for (auto i: m_history) + { + } +} + +void Main::on_callStack_currentItemChanged() +{ + updateDebugger(); } void Main::debugFinished() { + m_codes.clear(); m_pcWarp.clear(); m_history.clear(); + m_lastLevels.clear(); + m_inDebug = h256(); + ui->callStack->clear(); ui->debugCode->clear(); ui->debugStack->clear(); ui->debugMemory->setHtml(""); @@ -1283,6 +1317,7 @@ void Main::debugFinished() ui->debugStateInfo->setText(""); // ui->send->setEnabled(true); ui->debugStep->setEnabled(false); + ui->dumpTrace->setEnabled(false); ui->debugStepback->setEnabled(false); ui->debugPanel->setEnabled(false); } @@ -1293,30 +1328,66 @@ void Main::initDebugger() if (m_history.size()) { ui->debugStep->setEnabled(true); + ui->dumpTrace->setEnabled(true); ui->debugStepback->setEnabled(true); ui->debugPanel->setEnabled(true); ui->debugCode->setEnabled(false); ui->debugTimeline->setMinimum(0); ui->debugTimeline->setMaximum(m_history.size() - 1); ui->debugTimeline->setValue(0); + } +} + +void Main::on_debugTimeline_valueChanged() +{ + updateDebugger(); +} + +void Main::updateDebugger() +{ + if (m_history.size()) + { + QListWidget* ds = ui->debugStack; + ds->clear(); - QListWidget* dc = ui->debugCode; - dc->clear(); - if (m_currentExecution) + WorldState const& nws = m_history[ui->debugTimeline->value()]; + + if (m_lastLevels != nws.levels || !ui->callStack->count()) { - for (unsigned i = 0; i <= m_currentExecution->ext().code.size(); ++i) + m_lastLevels = nws.levels; + ui->callStack->clear(); + for (unsigned i = 0; i <= nws.levels.size(); ++i) { - byte b = i < m_currentExecution->ext().code.size() ? m_currentExecution->ext().code[i] : 0; + WorldState const& s = i ? *nws.levels[nws.levels.size() - i] : nws; + ostringstream out; + out << s.cur.abridged(); + if (i) + out << " @0x" << hex << s.curPC; + ui->callStack->addItem(QString::fromStdString(out.str())); + } + } + + WorldState const& ws = ui->callStack->currentRow() > 0 ? *nws.levels[nws.levels.size() - ui->callStack->currentRow()] : nws; + + if (ws.code != m_inDebug) + { + bytes const& code = m_codes[ws.code]; + QListWidget* dc = ui->debugCode; + dc->clear(); + m_pcWarp.clear(); + for (unsigned i = 0; i <= code.size(); ++i) + { + byte b = i < code.size() ? code[i] : 0; try { QString s = c_instructionInfo.at((Instruction)b).name; - m_pcWarp[i] = dc->count(); ostringstream out; out << hex << setw(4) << setfill('0') << i; + m_pcWarp[i] = dc->count(); if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32) { unsigned bc = b - (byte)Instruction::PUSH1 + 1; - s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&m_currentExecution->ext().code[i + 1], bc))); + s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&code[i + 1], bc))); i += bc; } dc->addItem(QString::fromStdString(out.str()) + " " + s); @@ -1326,30 +1397,20 @@ void Main::initDebugger() break; // probably hit data segment } } + m_inDebug = ws.code; } - } -} - -void Main::on_debugTimeline_valueChanged() -{ - updateDebugger(); -} - -void Main::updateDebugger() -{ - if (m_history.size()) - { - QListWidget* ds = ui->debugStack; - ds->clear(); - - WorldState const& ws = m_history[ui->debugTimeline->value()]; for (auto i: ws.stack) ds->insertItem(0, QString::fromStdString(toHex(((h256)i).asArray()))); ui->debugMemory->setHtml(QString::fromStdString(eth::memDump(ws.memory, 16, true))); - ui->debugCode->setCurrentRow(m_pcWarp[(unsigned)ws.curPC]); + assert(m_codes.count(ws.code)); + assert(m_codes[ws.code].size() > (unsigned)ws.curPC); + int l = m_pcWarp[(unsigned)ws.curPC]; + ui->debugCode->setCurrentRow(max(0, l - 5)); + ui->debugCode->setCurrentRow(min(ui->debugCode->count() - 1, l + 5)); + ui->debugCode->setCurrentRow(l); ostringstream ss; - ss << hex << "PC: 0x" << ws.curPC << " | GAS: " << dec << ws.gas << " | (- " << dec << ws.gasUsed << ")"; + ss << dec << "STEP: " << ws.steps << " | PC: 0x" << hex << ws.curPC << " : " << c_instructionInfo.at(ws.inst).name << " | ADDMEM: " << dec << ws.newMemSize << " words | COST: " << dec << ws.gasCost << " | GAS: " << dec << ws.gas; ui->debugStateInfo->setText(QString::fromStdString(ss.str())); stringstream s; diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 6cd1043fe..cf9e74ed5 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -21,7 +21,8 @@ #pragma once -//#include + +#include #include #include #include @@ -44,12 +45,18 @@ class QQuickView; struct WorldState { + uint64_t steps; + eth::Address cur; eth::u256 curPC; + eth::Instruction inst; + unsigned newMemSize; eth::u256 gas; - eth::u256 gasUsed; + eth::h256 code; eth::u256s stack; eth::bytes memory; + eth::bigint gasCost; std::map storage; + std::vector levels; }; class Main : public QMainWindow @@ -115,6 +122,8 @@ private slots: void on_loadJS_triggered(); void on_blockChainFilter_textChanged(); void on_clearPending_triggered(); + void on_dumpTrace_triggered(); + void on_callStack_currentItemChanged(); void refresh(bool _override = false); void refreshNetwork(); @@ -129,6 +138,7 @@ private: QString pretty(eth::Address _a) const; + void populateDebugger(eth::bytesConstRef r); void initDebugger(); void updateDebugger(); void debugFinished(); @@ -167,9 +177,12 @@ private: eth::State m_executiveState; std::unique_ptr m_currentExecution; + eth::h256 m_inDebug; + std::vector m_lastLevels; QMap m_pcWarp; QList m_history; + std::map m_codes; // and pcWarps QNetworkAccessManager m_webCtrl; diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 35f3bc156..fefcd55af 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -140,44 +140,34 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g return _init.empty(); } -bool Executive::go(uint64_t _steps) +OnOpFunc Executive::simpleTrace() +{ + return [](uint64_t steps, Instruction inst, unsigned newMemSize, bigint gasCost, void* voidVM, void const* voidExt) + { + ExtVM const& ext = *(ExtVM const*)voidExt; + VM& vm = *(VM*)voidVM; + + ostringstream o; + o << endl << " STACK" << endl; + for (auto i: vm.stack()) + o << (h256)i << endl; + o << " MEMORY" << endl << memDump(vm.memory()); + o << " STORAGE" << endl; + for (auto const& i: ext.state().storage(ext.myAddress)) + o << showbase << hex << i.first << ": " << i.second << endl; + eth::LogOutputStream(true) << o.str(); + eth::LogOutputStream(false) << " | " << dec << ext.level << " | " << ext.myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << c_instructionInfo.at(inst).name << " | " << dec << vm.gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32" << " ]"; + }; +} + +bool Executive::go(OnOpFunc const& _onOp) { if (m_vm) { bool revert = false; try { -#if ETH_VMTRACE - /* - if (_steps == (uint64_t)0 - 1) - for (uint64_t s = 0;; ++s) - { - ostringstream o; - o << endl << " STACK" << endl; - for (auto i: vm().stack()) - o << (h256)i << endl; - o << " MEMORY" << endl << memDump(vm().memory()); - o << " STORAGE" << endl; - for (auto const& i: state().storage(ext().myAddress)) - o << showbase << hex << i.first << ": " << i.second << endl; - eth::LogOutputStream(true) << o.str(); - eth::LogOutputStream(false) << dec << " | #" << s << " | " << hex << setw(4) << setfill('0') << vm().curPC() << " : " << c_instructionInfo.at((Instruction)ext().getCode(vm().curPC())).name << " | " << dec << vm().gas() << " ]"; - if (s >= _steps) - break; - try - { - m_out = m_vm->go(*m_ext, 1); - break; - } - catch (StepsDone const&) {} - } - else - m_out = m_vm->go(*m_ext, _steps);*/ - auto s = state().storage(m_ext->myAddress); - m_out = m_vm->go(*m_ext, _steps, &s); -#else - m_out = m_vm->go(*m_ext, _steps); -#endif + m_out = m_vm->go(*m_ext, _onOp); m_endGas = m_vm->gas(); } catch (StepsDone const&) diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 91b7f1e2f..696072090 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -21,8 +21,11 @@ #pragma once +#include #include +#include #include +#include #include "Transaction.h" namespace eth @@ -35,6 +38,8 @@ class State; struct Manifest; using Manifests = std::vector; +struct VMTraceChannel: public LogChannel { static const char* name() { return "EVM"; } static const int verbosity = 11; }; + /** * @brief A record of the state-interaction of a transaction/call/create. */ @@ -57,10 +62,12 @@ public: bool setup(bytesConstRef _transaction); bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress); bool call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress); - bool go(uint64_t _steps = (uint64_t)-1); + bool go(OnOpFunc const& _onOp = OnOpFunc()); void finalize(); u256 gasUsed() const; + static OnOpFunc simpleTrace(); + Transaction const& t() const { return m_t; } u256 gas() const; diff --git a/libethereum/ExtVM.h b/libethereum/ExtVM.h index 446ff8efe..b980d6f01 100644 --- a/libethereum/ExtVM.h +++ b/libethereum/ExtVM.h @@ -22,6 +22,7 @@ #pragma once #include +#include #include #include #include "State.h" @@ -36,8 +37,8 @@ class ExtVM: public ExtVMFace { public: /// Full constructor. - ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, Manifest* o_ms): - ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code, _s.m_previousBlock, _s.m_currentBlock), m_s(_s), m_origCache(_s.m_cache), m_ms(o_ms) + ExtVM(State& _s, Address _myAddress, Address _caller, Address _origin, u256 _value, u256 _gasPrice, bytesConstRef _data, bytesConstRef _code, Manifest* o_ms, unsigned _level = 0): + ExtVMFace(_myAddress, _caller, _origin, _value, _gasPrice, _data, _code, _s.m_previousBlock, _s.m_currentBlock), level(_level), m_s(_s), m_origCache(_s.m_cache), m_ms(o_ms) { m_s.ensureCached(_myAddress, true, true); } @@ -49,24 +50,24 @@ public: void setStore(u256 _n, u256 _v) { m_s.setStorage(myAddress, _n, _v); if (m_ms) m_ms->altered.push_back(_n); } /// Create a new contract. - h160 create(u256 _endowment, u256* _gas, bytesConstRef _code) + h160 create(u256 _endowment, u256* _gas, bytesConstRef _code, OnOpFunc const& _onOp = OnOpFunc()) { // Increment associated nonce for sender. m_s.noteSending(myAddress); if (m_ms) m_ms->internal.resize(m_ms->internal.size() + 1); - auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr); + auto ret = m_s.create(myAddress, _endowment, gasPrice, _gas, _code, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, level + 1); if (m_ms && !m_ms->internal.back().from) m_ms->internal.pop_back(); return ret; } /// Create a new message call. - bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out) + bool call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData, u256* _gas, bytesRef _out, OnOpFunc const& _onOp = OnOpFunc()) { if (m_ms) m_ms->internal.resize(m_ms->internal.size() + 1); - auto ret = m_s.call(_receiveAddress, myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr); + auto ret = m_s.call(_receiveAddress, myAddress, _txValue, gasPrice, _txData, _gas, _out, origin, &suicides, m_ms ? &(m_ms->internal.back()) : nullptr, _onOp, level + 1); if (m_ms && !m_ms->internal.back().from) m_ms->internal.pop_back(); return ret; @@ -92,6 +93,11 @@ public: /// @TODO check call site for the parent manifest being discarded. void revert() { if (m_ms) *m_ms = Manifest(); m_s.m_cache = m_origCache; } + State& state() const { return m_s; } + + /// @note not a part of the main API; just for use by tracing/debug stuff. + unsigned level = 0; + private: State& m_s; ///< A reference to the base state. std::map m_origCache; ///< The cache of the address states (i.e. the externalities) as-was prior to the execution. diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 43e00f1e8..c3380f76a 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -1021,7 +1021,7 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit, Manifest* return e.gasUsed(); } -bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, std::set
* o_suicides, Manifest* o_ms) +bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256* _gas, bytesRef _out, Address _originAddress, std::set
* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) { if (!_originAddress) _originAddress = _senderAddress; @@ -1039,12 +1039,12 @@ bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u if (addressHasCode(_receiveAddress)) { VM vm(*_gas); - ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_receiveAddress), o_ms); + ExtVM evm(*this, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &code(_receiveAddress), o_ms, _level); bool revert = false; try { - auto out = vm.go(evm); + auto out = vm.go(evm, _onOp); memcpy(_out.data(), out.data(), std::min(out.size(), _out.size())); if (o_suicides) for (auto i: evm.suicides) @@ -1081,7 +1081,7 @@ bool State::call(Address _receiveAddress, Address _senderAddress, u256 _value, u return true; } -h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set
* o_suicides, Manifest* o_ms) +h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _origin, std::set
* o_suicides, Manifest* o_ms, OnOpFunc const& _onOp, unsigned _level) { if (!_origin) _origin = _sender; @@ -1102,13 +1102,13 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas, // Execute init code. VM vm(*_gas); - ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, o_ms); + ExtVM evm(*this, newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _code, o_ms, _level); bool revert = false; bytesConstRef out; try { - out = vm.go(evm); + out = vm.go(evm, _onOp); if (o_ms) o_ms->output = out.toBytes(); if (o_suicides) diff --git a/libethereum/State.h b/libethereum/State.h index 2f07d7696..28af31da9 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -304,12 +304,12 @@ private: // We assume all instrinsic fees are paid up before this point. /// Execute a contract-creation transaction. - h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set
* o_suicides = nullptr, Manifest* o_ms = nullptr); + h160 create(Address _txSender, u256 _endowment, u256 _gasPrice, u256* _gas, bytesConstRef _code, Address _originAddress = Address(), std::set
* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); /// Execute a call. /// @a _gas points to the amount of gas to use for the call, and will lower it accordingly. /// @returns false if the call ran out of gas before completion. true otherwise. - bool call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), std::set
* o_suicides = nullptr, Manifest* o_ms = nullptr); + bool call(Address _myAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256* _gas, bytesRef _out, Address _originAddress = Address(), std::set
* o_suicides = nullptr, Manifest* o_ms = nullptr, OnOpFunc const& _onOp = OnOpFunc(), unsigned _level = 0); /// Sets m_currentBlock to a clean state, (i.e. no change from m_previousBlock). void resetCurrent(); diff --git a/libevm/ExtVMFace.h b/libevm/ExtVMFace.h index 04b758eb5..4890a5979 100644 --- a/libevm/ExtVMFace.h +++ b/libevm/ExtVMFace.h @@ -22,6 +22,7 @@ #pragma once #include +#include #include #include @@ -82,4 +83,6 @@ public: std::set
suicides; ///< Any accounts that have suicided. }; +typedef std::function OnOpFunc; + } diff --git a/libevm/VM.h b/libevm/VM.h index e3d67ff86..15f05fd26 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -21,13 +21,7 @@ #pragma once -#define ETH_VMTRACE 1 - #include -#if ETH_VMTRACE -#include -#include -#endif #include #include #include @@ -73,7 +67,7 @@ public: void reset(u256 _gas = 0); template - bytesConstRef go(Ext& _ext, uint64_t _steps = (uint64_t)-1, std::map const* _storage = nullptr); + bytesConstRef go(Ext& _ext, OnOpFunc const& _onOp = OnOpFunc(), uint64_t _steps = (uint64_t)-1); void require(u256 _n) { if (m_stack.size() < _n) throw StackTooSmall(_n, m_stack.size()); } void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } } @@ -90,36 +84,15 @@ private: u256s m_stack; }; -struct VMTraceChannel: public LogChannel { static const char* name() { return "EVM"; } static const int verbosity = 11; }; - } // INLINE: -template eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps, std::map const* _storage) +template eth::bytesConstRef eth::VM::go(Ext& _ext, OnOpFunc const& _onOp, uint64_t _steps) { u256 nextPC = m_curPC + 1; auto osteps = _steps; for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1) { - // TRACE -#if ETH_VMTRACE - { - std::ostringstream o; - o << std::endl << " STACK" << std::endl; - for (auto i: stack()) - o << (h256)i << std::endl; - o << " MEMORY" << std::endl << memDump(memory()); - if (_storage) - { - o << " STORAGE" << std::endl; - for (auto const& i: *_storage) - o << std::showbase << std::hex << i.first << ": " << i.second << std::endl; - } - eth::LogOutputStream(true) << o.str(); - eth::LogOutputStream(false) << std::dec << " | #" << (osteps - _steps) << " | " << std::hex << std::setw(4) << std::setfill('0') << curPC() << " : " << c_instructionInfo.at((Instruction)_ext.getCode(curPC())).name << " | " << std::dec << gas() << " ]"; - } -#endif - // INSTRUCTION... Instruction inst = (Instruction)_ext.getCode(m_curPC); @@ -209,6 +182,9 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps, if (newTempSize > m_temp.size()) runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32; + if (_onOp) + _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : 0, runGas, this, &_ext); + if (m_gas < runGas) { // Out of gas! @@ -564,7 +540,7 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps, if (_ext.balance(_ext.myAddress) >= endowment) { _ext.subBalance(endowment); - m_stack.push_back((u160)_ext.create(endowment, &m_gas, bytesConstRef(m_temp.data() + initOff, initSize))); + m_stack.push_back((u160)_ext.create(endowment, &m_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp)); } else m_stack.push_back(0); @@ -593,7 +569,7 @@ template eth::bytesConstRef eth::VM::go(Ext& _ext, uint64_t _steps, if (_ext.balance(_ext.myAddress) >= value) { _ext.subBalance(value); - m_stack.push_back(_ext.call(receiveAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), &gas, bytesRef(m_temp.data() + outOff, outSize))); + m_stack.push_back(_ext.call(receiveAddress, value, bytesConstRef(m_temp.data() + inOff, inSize), &gas, bytesRef(m_temp.data() + outOff, outSize), _onOp)); } else m_stack.push_back(0); diff --git a/test/vm.cpp b/test/vm.cpp index 8f128c2e2..b91ed4207 100644 --- a/test/vm.cpp +++ b/test/vm.cpp @@ -62,7 +62,7 @@ public: get<0>(addresses[_a]) += get<0>(addresses[myAddress]); addresses.erase(myAddress); } - h160 create(u256 _endowment, u256* _gas, bytesConstRef _init) + h160 create(u256 _endowment, u256* _gas, bytesConstRef _init, OnOpFunc) { Address na = right160(sha3(rlpList(myAddress, get<1>(addresses[myAddress])))); /* if (get<0>(addresses[myAddress]) >= _endowment) @@ -80,7 +80,7 @@ public: return na; } - bool call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out) + bool call(Address _receiveAddress, u256 _value, bytesConstRef _data, u256* _gas, bytesRef _out, OnOpFunc) { /* if (get<0>(addresses[myAddress]) >= _value) {