diff --git a/alethzero/Main.ui b/alethzero/Main.ui index f2d44b040..76a057453 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -162,6 +162,8 @@ Deb&ug + + @@ -1445,7 +1447,7 @@ font-size: 14pt false - &Single Step + &Step Over F10 @@ -1513,7 +1515,7 @@ font-size: 14pt false - Single Step &Backwards + Step &Backwards Shift+F10 @@ -1532,6 +1534,28 @@ font-size: 14pt &Dump Standard Trace + + + false + + + Step &Into + + + F11 + + + + + false + + + Step &Out + + + Shift+F11 + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index b7f7e626d..6ab50e9d8 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1297,11 +1297,34 @@ void Main::on_create_triggered() } void Main::on_debugStep_triggered() +{ + if (ui->debugTimeline->value() < m_history.size() && (m_history[ui->debugTimeline->value()].inst == Instruction::CALL || m_history[ui->debugTimeline->value()].inst == Instruction::CREATE)) + { + on_debugStepInto_triggered(); + on_debugStepOut_triggered(); + } + else + on_debugStepInto_triggered(); +} + +void Main::on_debugStepInto_triggered() { ui->debugTimeline->setValue(ui->debugTimeline->value() + 1); ui->callStack->setCurrentRow(0); } +void Main::on_debugStepOut_triggered() +{ + if (ui->debugTimeline->value() < m_history.size()) + { + auto ls = m_history[ui->debugTimeline->value()].levels.size(); + auto l = ui->debugTimeline->value(); + for (; l < m_history.size() && m_history[l].levels.size() >= ls; ++l) {} + ui->debugTimeline->setValue(l); + ui->callStack->setCurrentRow(0); + } +} + void Main::on_debugStepback_triggered() { ui->debugTimeline->setValue(ui->debugTimeline->value() - 1); @@ -1337,6 +1360,8 @@ void Main::debugFinished() ui->debugStateInfo->setText(""); // ui->send->setEnabled(true); ui->debugStep->setEnabled(false); + ui->debugStepInto->setEnabled(false); + ui->debugStepOut->setEnabled(false); ui->dumpTrace->setEnabled(false); ui->debugStepback->setEnabled(false); ui->debugPanel->setEnabled(false); @@ -1349,11 +1374,13 @@ void Main::initDebugger() { ui->debugStep->setEnabled(true); ui->dumpTrace->setEnabled(true); + ui->debugStepInto->setEnabled(true); + ui->debugStepOut->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->setMaximum(m_history.size()); ui->debugTimeline->setValue(0); } } @@ -1363,6 +1390,20 @@ void Main::on_debugTimeline_valueChanged() updateDebugger(); } +QString prettyU256(eth::u256 _n) +{ + ostringstream s; + if (_n >> 32 == 0) + s << hex << "0x" << (unsigned)_n; + else if (_n >> 200 == 0) + s << "0x" << (h160)right160(_n); + else if (fromRaw((h256)_n).size()) + s << "\"" << fromRaw((h256)_n).toStdString() << "\""; + else + s << "0x" << (h256)_n; + return QString::fromStdString(s.str()); +} + void Main::updateDebugger() { if (m_history.size()) @@ -1370,91 +1411,114 @@ void Main::updateDebugger() QListWidget* ds = ui->debugStack; ds->clear(); - WorldState const& nws = m_history[ui->debugTimeline->value()]; + WorldState const& nws = m_history[min((int)m_history.size() - 1, ui->debugTimeline->value())]; + WorldState const& ws = ui->callStack->currentRow() > 0 ? *nws.levels[nws.levels.size() - ui->callStack->currentRow()] : nws; - if (m_lastLevels != nws.levels || !ui->callStack->count()) + if (ui->debugTimeline->value() >= m_history.size()) { - m_lastLevels = nws.levels; - ui->callStack->clear(); - for (unsigned i = 0; i <= nws.levels.size(); ++i) + if (ws.gasCost > ws.gas) + ui->debugMemory->setHtml("

OUT-OF-GAS

"); + else if (ws.inst == Instruction::RETURN && ws.stack.size() >= 2) { - 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())); + unsigned from = (unsigned)ws.stack.back(); + unsigned size = (unsigned)ws.stack[ws.stack.size() - 2]; + unsigned o = 0; + bytes out(size, 0); + for (; o < size && from + o < ws.memory.size(); ++o) + out[o] = ws.memory[from + o]; + ui->debugMemory->setHtml("

RETURN

" + QString::fromStdString(eth::memDump(out, 16, true))); } + else if (ws.inst == Instruction::STOP) + ui->debugMemory->setHtml("

STOP

"); + else if (ws.inst == Instruction::SUICIDE && ws.stack.size() >= 1) + ui->debugMemory->setHtml("

SUICIDE

0x" + QString::fromStdString(toString(right160(ws.stack.back())))); + else + ui->debugMemory->setHtml("

EXCEPTION

"); + + ostringstream ss; + ss << dec << "EXIT | GAS: " << dec << max(0, (eth::bigint)ws.gas - ws.gasCost); + ui->debugStateInfo->setText(QString::fromStdString(ss.str())); + ui->debugStorage->setHtml(""); + ui->debugCallData->setHtml(""); + m_lastData = h256(); + ui->callStack->clear(); + m_lastLevels.clear(); + ui->debugCode->clear(); + m_lastCode = h256(); } - - WorldState const& ws = ui->callStack->currentRow() > 0 ? *nws.levels[nws.levels.size() - ui->callStack->currentRow()] : nws; - - if (ws.code != m_lastCode) + else { - bytes const& code = m_codes[ws.code]; - QListWidget* dc = ui->debugCode; - dc->clear(); - m_pcWarp.clear(); - for (unsigned i = 0; i <= code.size(); ++i) + if (m_lastLevels != nws.levels || !ui->callStack->count()) { - byte b = i < code.size() ? code[i] : 0; - try + m_lastLevels = nws.levels; + ui->callStack->clear(); + for (unsigned i = 0; i <= nws.levels.size(); ++i) { - QString s = c_instructionInfo.at((Instruction)b).name; + WorldState const& s = i ? *nws.levels[nws.levels.size() - i] : nws; 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(&code[i + 1], bc))); - i += bc; - } - dc->addItem(QString::fromStdString(out.str()) + " " + s); + out << s.cur.abridged(); + if (i) + out << " @0x" << hex << s.curPC; + ui->callStack->addItem(QString::fromStdString(out.str())); } - catch (...) + } + + if (ws.code != m_lastCode) + { + bytes const& code = m_codes[ws.code]; + QListWidget* dc = ui->debugCode; + dc->clear(); + m_pcWarp.clear(); + for (unsigned i = 0; i <= code.size(); ++i) { - break; // probably hit data segment + byte b = i < code.size() ? code[i] : 0; + try + { + QString s = c_instructionInfo.at((Instruction)b).name; + 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(&code[i + 1], bc))); + i += bc; + } + dc->addItem(QString::fromStdString(out.str()) + " " + s); + } + catch (...) + { + break; // probably hit data segment + } } + m_lastCode = ws.code; } - m_lastCode = ws.code; - } - if (ws.callData != m_lastData) - { - m_lastData = ws.callData; - assert(m_codes.count(ws.callData)); - ui->debugCallData->setHtml(QString::fromStdString(eth::memDump(m_codes[ws.callData], 16, true))); - } + if (ws.callData != m_lastData) + { + m_lastData = ws.callData; + assert(m_codes.count(ws.callData)); + ui->debugCallData->setHtml(QString::fromStdString(eth::memDump(m_codes[ws.callData], 16, true))); + } - for (auto i: ws.stack) - { - ostringstream s; - if (i >> 32 == 0) - s << hex << "0x" << (unsigned)i; - else if (i >> 200 == 0) - s << "0x" << (h160)right160(i); - else if (fromRaw((h256)i).size()) - s << "\"" << fromRaw((h256)i).toStdString() << "\""; - else - s << "0x" << (h256)i; - ds->insertItem(0, QString::fromStdString(s.str())); + for (auto i: ws.stack) + ds->insertItem(0, prettyU256(i)); + ui->debugMemory->setHtml(QString::fromStdString(eth::memDump(ws.memory, 16, true))); + 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 << 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; + for (auto const& i: ws.storage) + s << "@" << prettyU256(i.first).toStdString() << "    " << prettyU256(i.second).toStdString() << "
"; + ui->debugStorage->setHtml(QString::fromStdString(s.str())); } - ui->debugMemory->setHtml(QString::fromStdString(eth::memDump(ws.memory, 16, true))); - 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 << 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; - for (auto const& i: ws.storage) - s << "@" << showbase << hex << i.first << "    " << showbase << hex << i.second << "
"; - ui->debugStorage->setHtml(QString::fromStdString(s.str())); } } diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 53b7a2677..dd4001fc1 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -124,6 +124,8 @@ private slots: void on_blockChainFilter_textChanged(); void on_clearPending_triggered(); void on_dumpTrace_triggered(); + void on_debugStepInto_triggered(); + void on_debugStepOut_triggered(); void on_callStack_currentItemChanged(); void refresh(bool _override = false);