Gav Wood
10 years ago
8 changed files with 793 additions and 27 deletions
@ -0,0 +1,26 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Debugger.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
#include "Context.h" |
|||
|
|||
Context::~Context() |
|||
{ |
|||
} |
@ -0,0 +1,41 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Debugger.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <string> |
|||
#include <QString> |
|||
#include <libethcore/CommonEth.h> |
|||
|
|||
namespace dev { namespace eth { class StateDiff; } } |
|||
|
|||
class Context |
|||
{ |
|||
public: |
|||
virtual ~Context(); |
|||
|
|||
virtual QString pretty(dev::Address _a) const = 0; |
|||
virtual QString prettyU256(dev::u256 _n) const = 0; |
|||
virtual QString render(dev::Address _a) const = 0; |
|||
virtual dev::Address fromString(QString const& _a) const = 0; |
|||
virtual std::string renderDiff(dev::eth::StateDiff const& _d) const = 0; |
|||
}; |
|||
|
@ -0,0 +1,372 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Debugger.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
#include "Debugger.h" |
|||
|
|||
#include <fstream> |
|||
#include <QFileDialog> |
|||
#include <libevm/VM.h> |
|||
#include <libethereum/ExtVM.h> |
|||
#include <libethereum/Executive.h> |
|||
#include "ui_Debugger.h" |
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
|
|||
Debugger::Debugger(Context* _c, QWidget* _parent): |
|||
QDialog(_parent), |
|||
ui(new Ui::Debugger), |
|||
m_context(_c) |
|||
{ |
|||
ui->setupUi(this); |
|||
} |
|||
|
|||
Debugger::~Debugger() |
|||
{ |
|||
delete ui; |
|||
} |
|||
|
|||
void Debugger::init() |
|||
{ |
|||
if (m_session.history.size()) |
|||
{ |
|||
alterDebugStateGroup(true); |
|||
ui->debugCode->setEnabled(false); |
|||
ui->debugTimeline->setMinimum(0); |
|||
ui->debugTimeline->setMaximum(m_session.history.size()); |
|||
ui->debugTimeline->setValue(0); |
|||
} |
|||
} |
|||
|
|||
void Debugger::populate(dev::eth::Executive& _executive, dev::bytesConstRef _transactionRLP) |
|||
{ |
|||
finished(); |
|||
if (m_session.populate(_executive, _transactionRLP)) |
|||
init(); |
|||
update(); |
|||
} |
|||
|
|||
bool DebugSession::populate(dev::eth::Executive& _executive, dev::bytesConstRef _transactionRLP) |
|||
{ |
|||
if (_executive.setup(_transactionRLP)) |
|||
return false; |
|||
vector<WorldState const*> levels; |
|||
bytes lastExtCode; |
|||
bytesConstRef lastData; |
|||
h256 lastHash; |
|||
h256 lastDataHash; |
|||
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, VM* voidVM, ExtVMFace const* voidExt) |
|||
{ |
|||
VM& vm = *voidVM; |
|||
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt); |
|||
if (ext.code != lastExtCode) |
|||
{ |
|||
lastExtCode = ext.code; |
|||
lastHash = sha3(lastExtCode); |
|||
if (!codes.count(lastHash)) |
|||
codes[lastHash] = ext.code; |
|||
} |
|||
if (ext.data != lastData) |
|||
{ |
|||
lastData = ext.data; |
|||
lastDataHash = sha3(lastData); |
|||
if (!codes.count(lastDataHash)) |
|||
codes[lastDataHash] = ext.data.toBytes(); |
|||
} |
|||
if (levels.size() < ext.depth) |
|||
levels.push_back(&history.back()); |
|||
else |
|||
levels.resize(ext.depth); |
|||
history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); |
|||
}; |
|||
_executive.go(onOp); |
|||
_executive.finalize(); |
|||
return true; |
|||
} |
|||
|
|||
void Debugger::finished() |
|||
{ |
|||
m_session = DebugSession(); |
|||
ui->callStack->clear(); |
|||
ui->debugCode->clear(); |
|||
ui->debugStack->clear(); |
|||
ui->debugMemory->setHtml(""); |
|||
ui->debugStorage->setHtml(""); |
|||
ui->debugStateInfo->setText(""); |
|||
alterDebugStateGroup(false); |
|||
} |
|||
|
|||
void Debugger::update() |
|||
{ |
|||
if (m_session.history.size()) |
|||
{ |
|||
WorldState const& nws = m_session.history[min((int)m_session.history.size() - 1, ui->debugTimeline->value())]; |
|||
WorldState const& ws = ui->callStack->currentRow() > 0 ? *nws.levels[nws.levels.size() - ui->callStack->currentRow()] : nws; |
|||
|
|||
if (ui->debugTimeline->value() >= m_session.history.size()) |
|||
{ |
|||
if (ws.gasCost > ws.gas) |
|||
ui->debugMemory->setHtml("<h3>OUT-OF-GAS</h3>"); |
|||
else if (ws.inst == Instruction::RETURN && ws.stack.size() >= 2) |
|||
{ |
|||
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("<h3>RETURN</h3>" + QString::fromStdString(dev::memDump(out, 16, true))); |
|||
} |
|||
else if (ws.inst == Instruction::STOP) |
|||
ui->debugMemory->setHtml("<h3>STOP</h3>"); |
|||
else if (ws.inst == Instruction::SUICIDE && ws.stack.size() >= 1) |
|||
ui->debugMemory->setHtml("<h3>SUICIDE</h3>0x" + QString::fromStdString(toString(right160(ws.stack.back())))); |
|||
else |
|||
ui->debugMemory->setHtml("<h3>EXCEPTION</h3>"); |
|||
|
|||
ostringstream ss; |
|||
ss << dec << "EXIT | GAS: " << dec << max<dev::bigint>(0, (dev::bigint)ws.gas - ws.gasCost); |
|||
ui->debugStateInfo->setText(QString::fromStdString(ss.str())); |
|||
ui->debugStorage->setHtml(""); |
|||
ui->debugCallData->setHtml(""); |
|||
m_session.currentData = h256(); |
|||
ui->callStack->clear(); |
|||
m_session.currentLevels.clear(); |
|||
ui->debugCode->clear(); |
|||
m_session.currentCode = h256(); |
|||
ui->debugStack->setHtml(""); |
|||
} |
|||
else |
|||
{ |
|||
if (m_session.currentLevels != nws.levels || !ui->callStack->count()) |
|||
{ |
|||
m_session.currentLevels = nws.levels; |
|||
ui->callStack->clear(); |
|||
for (unsigned i = 0; i <= nws.levels.size(); ++i) |
|||
{ |
|||
WorldState const& s = i ? *nws.levels[nws.levels.size() - i] : nws; |
|||
ostringstream out; |
|||
out << s.cur.abridged(); |
|||
if (i) |
|||
out << " " << instructionInfo(s.inst).name << " @0x" << hex << s.curPC; |
|||
ui->callStack->addItem(QString::fromStdString(out.str())); |
|||
} |
|||
} |
|||
|
|||
if (ws.code != m_session.currentCode) |
|||
{ |
|||
m_session.currentCode = ws.code; |
|||
bytes const& code = m_session.codes[ws.code]; |
|||
QListWidget* dc = ui->debugCode; |
|||
dc->clear(); |
|||
m_session.pcWarp.clear(); |
|||
for (unsigned i = 0; i <= code.size(); ++i) |
|||
{ |
|||
byte b = i < code.size() ? code[i] : 0; |
|||
try |
|||
{ |
|||
QString s = QString::fromStdString(instructionInfo((Instruction)b).name); |
|||
ostringstream out; |
|||
out << hex << setw(4) << setfill('0') << i; |
|||
m_session.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 (...) |
|||
{ |
|||
cerr << "Unhandled exception!" << endl << boost::current_exception_diagnostic_information(); |
|||
break; // probably hit data segment
|
|||
} |
|||
} |
|||
} |
|||
|
|||
if (ws.callData != m_session.currentData) |
|||
{ |
|||
m_session.currentData = ws.callData; |
|||
if (ws.callData) |
|||
{ |
|||
assert(m_session.codes.count(ws.callData)); |
|||
ui->debugCallData->setHtml(QString::fromStdString(dev::memDump(m_session.codes[ws.callData], 16, true))); |
|||
} |
|||
else |
|||
ui->debugCallData->setHtml(""); |
|||
} |
|||
|
|||
QString stack; |
|||
for (auto i: ws.stack) |
|||
stack.prepend("<div>" + m_context->prettyU256(i) + "</div>"); |
|||
ui->debugStack->setHtml(stack); |
|||
ui->debugMemory->setHtml(QString::fromStdString(dev::memDump(ws.memory, 16, true))); |
|||
assert(m_session.codes.count(ws.code)); |
|||
|
|||
if (m_session.codes[ws.code].size() >= (unsigned)ws.curPC) |
|||
{ |
|||
int l = m_session.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); |
|||
} |
|||
else |
|||
cwarn << "PC (" << (unsigned)ws.curPC << ") is after code range (" << m_session.codes[ws.code].size() << ")"; |
|||
|
|||
ostringstream ss; |
|||
ss << dec << "STEP: " << ws.steps << " | PC: 0x" << hex << ws.curPC << " : " << instructionInfo(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 << "@" << m_context->prettyU256(i.first).toStdString() << " " << m_context->prettyU256(i.second).toStdString() << "<br/>"; |
|||
ui->debugStorage->setHtml(QString::fromStdString(s.str())); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void Debugger::on_callStack_currentItemChanged() |
|||
{ |
|||
update(); |
|||
} |
|||
|
|||
void Debugger::alterDebugStateGroup(bool _enable) const |
|||
{ |
|||
ui->stepOver->setEnabled(_enable); |
|||
ui->stepInto->setEnabled(_enable); |
|||
ui->stepOut->setEnabled(_enable); |
|||
ui->backOver->setEnabled(_enable); |
|||
ui->backInto->setEnabled(_enable); |
|||
ui->backOut->setEnabled(_enable); |
|||
ui->dump->setEnabled(_enable); |
|||
ui->dumpStorage->setEnabled(_enable); |
|||
ui->dumpPretty->setEnabled(_enable); |
|||
} |
|||
|
|||
void Debugger::on_debugTimeline_valueChanged() |
|||
{ |
|||
update(); |
|||
} |
|||
|
|||
void Debugger::on_stepOver_triggered() |
|||
{ |
|||
if (ui->debugTimeline->value() < m_session.history.size()) { |
|||
auto l = m_session.history[ui->debugTimeline->value()].levels.size(); |
|||
if ((ui->debugTimeline->value() + 1) < m_session.history.size() && m_session.history[ui->debugTimeline->value() + 1].levels.size() > l) |
|||
{ |
|||
on_stepInto_triggered(); |
|||
if (m_session.history[ui->debugTimeline->value()].levels.size() > l) |
|||
on_stepOut_triggered(); |
|||
} |
|||
else |
|||
on_stepInto_triggered(); |
|||
} |
|||
} |
|||
|
|||
void Debugger::on_stepInto_triggered() |
|||
{ |
|||
ui->debugTimeline->setValue(ui->debugTimeline->value() + 1); |
|||
ui->callStack->setCurrentRow(0); |
|||
} |
|||
|
|||
void Debugger::on_stepOut_triggered() |
|||
{ |
|||
if (ui->debugTimeline->value() < m_session.history.size()) |
|||
{ |
|||
auto ls = m_session.history[ui->debugTimeline->value()].levels.size(); |
|||
auto l = ui->debugTimeline->value(); |
|||
for (; l < m_session.history.size() && m_session.history[l].levels.size() >= ls; ++l) {} |
|||
ui->debugTimeline->setValue(l); |
|||
ui->callStack->setCurrentRow(0); |
|||
} |
|||
} |
|||
|
|||
void Debugger::on_backInto_triggered() |
|||
{ |
|||
ui->debugTimeline->setValue(ui->debugTimeline->value() - 1); |
|||
ui->callStack->setCurrentRow(0); |
|||
} |
|||
|
|||
void Debugger::on_backOver_triggered() |
|||
{ |
|||
auto l = m_session.history[ui->debugTimeline->value()].levels.size(); |
|||
if (ui->debugTimeline->value() > 0 && m_session.history[ui->debugTimeline->value() - 1].levels.size() > l) |
|||
{ |
|||
on_backInto_triggered(); |
|||
if (m_session.history[ui->debugTimeline->value()].levels.size() > l) |
|||
on_backOut_triggered(); |
|||
} |
|||
else |
|||
on_backInto_triggered(); |
|||
} |
|||
|
|||
void Debugger::on_backOut_triggered() |
|||
{ |
|||
if (ui->debugTimeline->value() > 0 && m_session.history.size() > 0) |
|||
{ |
|||
auto ls = m_session.history[min(ui->debugTimeline->value(), m_session.history.size() - 1)].levels.size(); |
|||
int l = ui->debugTimeline->value(); |
|||
for (; l > 0 && m_session.history[l].levels.size() >= ls; --l) {} |
|||
ui->debugTimeline->setValue(l); |
|||
ui->callStack->setCurrentRow(0); |
|||
} |
|||
} |
|||
|
|||
void Debugger::on_dump_triggered() |
|||
{ |
|||
QString fn = QFileDialog::getSaveFileName(this, "Select file to output EVM trace"); |
|||
ofstream f(fn.toStdString()); |
|||
if (f.is_open()) |
|||
for (WorldState const& ws: m_session.history) |
|||
f << ws.cur << " " << hex << toHex(dev::toCompactBigEndian(ws.curPC, 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)ws.inst, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)ws.gas, 1)) << endl; |
|||
} |
|||
|
|||
void Debugger::on_dumpPretty_triggered() |
|||
{ |
|||
QString fn = QFileDialog::getSaveFileName(this, "Select file to output EVM trace"); |
|||
ofstream f(fn.toStdString()); |
|||
if (f.is_open()) |
|||
for (WorldState const& ws: m_session.history) |
|||
{ |
|||
f << endl << " STACK" << endl; |
|||
for (auto i: ws.stack) |
|||
f << (h256)i << endl; |
|||
f << " MEMORY" << endl << dev::memDump(ws.memory); |
|||
f << " STORAGE" << endl; |
|||
for (auto const& i: ws.storage) |
|||
f << showbase << hex << i.first << ": " << i.second << endl; |
|||
f << dec << ws.levels.size() << " | " << ws.cur << " | #" << ws.steps << " | " << hex << setw(4) << setfill('0') << ws.curPC << " : " << instructionInfo(ws.inst).name << " | " << dec << ws.gas << " | -" << dec << ws.gasCost << " | " << ws.newMemSize << "x32"; |
|||
} |
|||
} |
|||
|
|||
void Debugger::on_dumpStorage_triggered() |
|||
{ |
|||
QString fn = QFileDialog::getSaveFileName(this, "Select file to output EVM trace"); |
|||
ofstream f(fn.toStdString()); |
|||
if (f.is_open()) |
|||
for (WorldState const& ws: m_session.history) |
|||
{ |
|||
if (ws.inst == Instruction::STOP || ws.inst == Instruction::RETURN || ws.inst == Instruction::SUICIDE) |
|||
for (auto i: ws.storage) |
|||
f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl; |
|||
f << ws.cur << " " << hex << toHex(dev::toCompactBigEndian(ws.curPC, 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)ws.inst, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)ws.gas, 1)) << endl; |
|||
} |
|||
} |
@ -0,0 +1,102 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file Debugger.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <libdevcore/RLP.h> |
|||
#include <libethcore/CommonEth.h> |
|||
#include <libethereum/State.h> |
|||
#include <libethereum/Executive.h> |
|||
#include <QDialog> |
|||
#include <QMap> |
|||
#include <QList> |
|||
#include "Context.h" |
|||
|
|||
namespace Ui { class Debugger; } |
|||
|
|||
struct WorldState |
|||
{ |
|||
uint64_t steps; |
|||
dev::Address cur; |
|||
dev::u256 curPC; |
|||
dev::eth::Instruction inst; |
|||
dev::bigint newMemSize; |
|||
dev::u256 gas; |
|||
dev::h256 code; |
|||
dev::h256 callData; |
|||
dev::u256s stack; |
|||
dev::bytes memory; |
|||
dev::bigint gasCost; |
|||
std::map<dev::u256, dev::u256> storage; |
|||
std::vector<WorldState const*> levels; |
|||
}; |
|||
|
|||
struct DebugSession |
|||
{ |
|||
DebugSession() {} |
|||
|
|||
bool populate(dev::eth::Executive& _executive, dev::bytesConstRef _transactionRLP); |
|||
|
|||
dev::h256 currentCode; |
|||
dev::h256 currentData; |
|||
std::vector<WorldState const*> currentLevels; |
|||
|
|||
QMap<unsigned, unsigned> pcWarp; |
|||
QList<WorldState> history; |
|||
|
|||
std::map<dev::u256, dev::bytes> codes; |
|||
}; |
|||
|
|||
class Debugger: public QDialog |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
explicit Debugger(Context* _context, QWidget* _parent = 0); |
|||
~Debugger(); |
|||
|
|||
void populate(dev::eth::Executive& _executive, dev::bytesConstRef _transactionRLP); |
|||
|
|||
protected slots: |
|||
void on_callStack_currentItemChanged(); |
|||
void on_debugTimeline_valueChanged(); |
|||
void on_stepOver_triggered(); |
|||
void on_stepInto_triggered(); |
|||
void on_stepOut_triggered(); |
|||
void on_backOver_triggered(); |
|||
void on_backInto_triggered(); |
|||
void on_backOut_triggered(); |
|||
void on_dump_triggered(); |
|||
void on_dumpPretty_triggered(); |
|||
void on_dumpStorage_triggered(); |
|||
|
|||
private: |
|||
void init(); |
|||
void update(); |
|||
void finished(); |
|||
|
|||
void alterDebugStateGroup(bool _enable) const; |
|||
|
|||
Ui::Debugger* ui; |
|||
|
|||
DebugSession m_session; |
|||
Context* m_context; |
|||
}; |
@ -0,0 +1,238 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<ui version="4.0"> |
|||
<class>Debugger</class> |
|||
<widget class="QDialog" name="Debugger"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>0</x> |
|||
<y>0</y> |
|||
<width>946</width> |
|||
<height>690</height> |
|||
</rect> |
|||
</property> |
|||
<property name="windowTitle"> |
|||
<string>Dialog</string> |
|||
</property> |
|||
<layout class="QVBoxLayout" name="verticalLayout"> |
|||
<item> |
|||
<widget class="QFrame" name="frame"> |
|||
<property name="frameShape"> |
|||
<enum>QFrame::StyledPanel</enum> |
|||
</property> |
|||
<property name="frameShadow"> |
|||
<enum>QFrame::Raised</enum> |
|||
</property> |
|||
<layout class="QHBoxLayout" name="horizontalLayout"> |
|||
<item> |
|||
<widget class="QToolButton" name="stepOver"> |
|||
<property name="text"> |
|||
<string>Step Over</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QToolButton" name="stepInto"> |
|||
<property name="text"> |
|||
<string>Step Into</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QToolButton" name="stepOut"> |
|||
<property name="text"> |
|||
<string>Step Out</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QToolButton" name="backOver"> |
|||
<property name="text"> |
|||
<string>Back Over</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QToolButton" name="backInto"> |
|||
<property name="text"> |
|||
<string>Back Into</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QToolButton" name="backOut"> |
|||
<property name="text"> |
|||
<string>Back Out</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QToolButton" name="dump"> |
|||
<property name="text"> |
|||
<string>Dump</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QToolButton" name="dumpPretty"> |
|||
<property name="text"> |
|||
<string>Dump Pretty</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QToolButton" name="dumpStorage"> |
|||
<property name="text"> |
|||
<string>Dump Storage</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<spacer name="horizontalSpacer"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>40</width> |
|||
<height>20</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QSplitter" name="splitter_6"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
<widget class="QSplitter" name="splitter_42"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding"> |
|||
<horstretch>1</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="orientation"> |
|||
<enum>Qt::Vertical</enum> |
|||
</property> |
|||
<widget class="QListWidget" name="debugCode"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="frameShape"> |
|||
<enum>QFrame::NoFrame</enum> |
|||
</property> |
|||
<property name="lineWidth"> |
|||
<number>0</number> |
|||
</property> |
|||
</widget> |
|||
<widget class="QListWidget" name="callStack"> |
|||
<property name="font"> |
|||
<font> |
|||
<family>Ubuntu Mono</family> |
|||
</font> |
|||
</property> |
|||
<property name="frameShape"> |
|||
<enum>QFrame::NoFrame</enum> |
|||
</property> |
|||
<property name="lineWidth"> |
|||
<number>0</number> |
|||
</property> |
|||
</widget> |
|||
</widget> |
|||
<widget class="QSplitter" name="splitter_4"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding"> |
|||
<horstretch>1</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="orientation"> |
|||
<enum>Qt::Vertical</enum> |
|||
</property> |
|||
<widget class="QTextEdit" name="debugStack"> |
|||
<property name="frameShape"> |
|||
<enum>QFrame::NoFrame</enum> |
|||
</property> |
|||
<property name="lineWidth"> |
|||
<number>0</number> |
|||
</property> |
|||
<property name="readOnly"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="textInteractionFlags"> |
|||
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> |
|||
</property> |
|||
</widget> |
|||
<widget class="QTextEdit" name="debugMemory"> |
|||
<property name="frameShape"> |
|||
<enum>QFrame::NoFrame</enum> |
|||
</property> |
|||
<property name="lineWidth"> |
|||
<number>0</number> |
|||
</property> |
|||
<property name="readOnly"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="textInteractionFlags"> |
|||
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> |
|||
</property> |
|||
</widget> |
|||
<widget class="QTextEdit" name="debugStorage"> |
|||
<property name="frameShape"> |
|||
<enum>QFrame::NoFrame</enum> |
|||
</property> |
|||
<property name="lineWidth"> |
|||
<number>0</number> |
|||
</property> |
|||
<property name="readOnly"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="textInteractionFlags"> |
|||
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> |
|||
</property> |
|||
</widget> |
|||
<widget class="QTextEdit" name="debugCallData"> |
|||
<property name="frameShape"> |
|||
<enum>QFrame::NoFrame</enum> |
|||
</property> |
|||
<property name="readOnly"> |
|||
<bool>true</bool> |
|||
</property> |
|||
</widget> |
|||
</widget> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QLabel" name="debugStateInfo"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="text"> |
|||
<string/> |
|||
</property> |
|||
<property name="alignment"> |
|||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QSlider" name="debugTimeline"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
<resources/> |
|||
<connections/> |
|||
</ui> |
Loading…
Reference in new issue