Browse Source

debugging into calls

cl-refactor
arkpar 10 years ago
parent
commit
c820b2597e
  1. 7
      mix/AssemblyDebuggerControl.cpp
  2. 4
      mix/AssemblyDebuggerControl.h
  3. 40
      mix/ClientModel.cpp
  4. 10
      mix/ClientModel.h
  5. 2
      mix/CodeModel.cpp
  6. 54
      mix/DebuggingStateWrapper.cpp
  7. 84
      mix/DebuggingStateWrapper.h
  8. 48
      mix/MixClient.cpp
  9. 9
      mix/MixClient.h
  10. 14
      mix/qml/Debugger.qml
  11. 110
      mix/qml/js/Debugger.js

7
mix/AssemblyDebuggerControl.cpp

@ -29,7 +29,6 @@ using namespace dev::mix;
AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context): AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context):
Extension(_context, ExtensionDisplayBehavior::RightView) Extension(_context, ExtensionDisplayBehavior::RightView)
{ {
connect(_context->clientModel(), &ClientModel::showDebuggerWindow, this, &AssemblyDebuggerControl::showDebugger, Qt::QueuedConnection);
} }
QString AssemblyDebuggerControl::contentUrl() const QString AssemblyDebuggerControl::contentUrl() const
@ -45,9 +44,3 @@ QString AssemblyDebuggerControl::title() const
void AssemblyDebuggerControl::start() const void AssemblyDebuggerControl::start() const
{ {
} }
void AssemblyDebuggerControl::showDebugger()
{
QObject* debugPanel = m_view->findChild<QObject*>("debugPanel", Qt::FindChildrenRecursively);
QMetaObject::invokeMethod(debugPanel, "update", Q_ARG(QVariant, true));
}

4
mix/AssemblyDebuggerControl.h

@ -42,10 +42,6 @@ public:
void start() const override; void start() const override;
QString title() const override; QString title() const override;
QString contentUrl() const override; QString contentUrl() const override;
private slots:
/// Update UI with machine states result. Displayed in the right side tab.
void showDebugger();
}; };
} }

40
mix/ClientModel.cpp

@ -72,7 +72,10 @@ ClientModel::ClientModel(AppContext* _context):
qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>"); qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>");
qRegisterMetaType<QList<QVariableDeclaration*>>("QList<QVariableDeclaration*>"); qRegisterMetaType<QList<QVariableDeclaration*>>("QList<QVariableDeclaration*>");
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*"); qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*");
qRegisterMetaType<AssemblyDebuggerData>("AssemblyDebuggerData"); qRegisterMetaType<QMachineState*>("QMachineState");
qRegisterMetaType<QInstruction*>("QInstruction");
qRegisterMetaType<QCode*>("QCode");
qRegisterMetaType<QCallData*>("QCallData");
qRegisterMetaType<TransactionLogEntry*>("TransactionLogEntry"); qRegisterMetaType<TransactionLogEntry*>("TransactionLogEntry");
connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection); connect(this, &ClientModel::runComplete, this, &ClientModel::showDebugger, Qt::QueuedConnection);
@ -251,24 +254,27 @@ void ClientModel::showDebugger()
void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t) void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
{ {
//we need to wrap states in a QObject before sending to QML. //we need to wrap states in a QObject before sending to QML.
QList<QObject*> wStates; QDebugData* debugData = new QDebugData();
for (unsigned i = 0; i < _t.machineStates.size(); i++) QQmlEngine::setObjectOwnership(debugData, QQmlEngine::JavaScriptOwnership);
{ QList<QCode*> codes;
QPointer<DebuggingStateWrapper> s(new DebuggingStateWrapper(_t.executionCode, _t.executionData.toBytes())); for (bytes const& code: _t.executionCode)
s->setState(_t.machineStates[i]); codes.push_back(QMachineState::getHumanReadableCode(debugData, code));
wStates.append(s);
} QList<QCallData*> data;
for (bytes const& d: _t.transactionData)
data.push_back(QMachineState::getDebugCallData(debugData, d));
QVariantList states;
for (MachineState const& s: _t.machineStates)
states.append(QVariant::fromValue(new QMachineState(debugData, s, codes[s.codeIndex], data[s.dataIndex])));
debugData->setStates(std::move(states));
QList<QVariableDefinition*> returnParameters; //QList<QVariableDefinition*> returnParameters;
//returnParameters = encoder.decode(f->returnParameters(), debuggingContent.returnValue); //returnParameters = encoder.decode(f->returnParameters(), debuggingContent.returnValue);
//collect states for last transaction //collect states for last transaction
AssemblyDebuggerData code = DebuggingStateWrapper::getHumanReadableCode(_t.executionCode); debugDataReady(debugData);
m_context->appEngine()->rootContext()->setContextProperty("debugStates", QVariant::fromValue(wStates));
m_context->appEngine()->rootContext()->setContextProperty("humanReadableExecutionCode", QVariant::fromValue(std::get<0>(code)));
m_context->appEngine()->rootContext()->setContextProperty("bytesCodeMapping", QVariant::fromValue(std::get<1>(code)));
m_context->appEngine()->rootContext()->setContextProperty("contractCallReturnParameters", QVariant::fromValue(new QVariableDefinitionList(returnParameters)));
showDebuggerWindow();
} }
@ -339,9 +345,9 @@ void ClientModel::onNewTransaction()
else else
{ {
//call //call
if (tr.transactionData.size() >= 4) if (tr.transactionData.size() > 0 && tr.transactionData.front().size() >= 4)
{ {
functionHash = FixedHash<4>(tr.transactionData.data(), FixedHash<4>::ConstructFromPointer); functionHash = FixedHash<4>(tr.transactionData.front().data(), FixedHash<4>::ConstructFromPointer);
function = QString::fromStdString(toJS(functionHash)); function = QString::fromStdString(toJS(functionHash));
call = true; call = true;
} }

10
mix/ClientModel.h

@ -25,14 +25,9 @@
#include <atomic> #include <atomic>
#include <map> #include <map>
#include "DebuggingStateWrapper.h" #include <QString>
#include "MixClient.h" #include "MixClient.h"
using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>;
Q_DECLARE_METATYPE(AssemblyDebuggerData)
Q_DECLARE_METATYPE(dev::mix::ExecutionResult)
namespace dev namespace dev
{ {
namespace mix namespace mix
@ -42,6 +37,7 @@ class AppContext;
class Web3Server; class Web3Server;
class RpcConnector; class RpcConnector;
class QEther; class QEther;
class QDebugData;
/// Backend transaction config class /// Backend transaction config class
struct TransactionSettings struct TransactionSettings
@ -156,7 +152,7 @@ signals:
/// Execution state changed /// Execution state changed
void runStateChanged(); void runStateChanged();
/// Show debugger window request /// Show debugger window request
void showDebuggerWindow(); void debugDataReady(QObject* _debugData);
/// ethereum.js RPC response ready /// ethereum.js RPC response ready
/// @param _message RPC response in Json format /// @param _message RPC response in Json format
void apiResponse(QString const& _message); void apiResponse(QString const& _message);

2
mix/CodeModel.cpp

@ -133,7 +133,7 @@ void CodeModel::runCompilationJob(int _jobId, QString const& _code)
if (_jobId != m_backgroundJobId) if (_jobId != m_backgroundJobId)
return; //obsolete job return; //obsolete job
solidity::CompilerStack cs; solidity::CompilerStack cs(true);
std::unique_ptr<CompilationResult> result; std::unique_ptr<CompilationResult> result;
std::string source = _code.toStdString(); std::string source = _code.toStdString();

54
mix/DebuggingStateWrapper.cpp

@ -24,6 +24,7 @@
#include <QDebug> #include <QDebug>
#include <QPointer> #include <QPointer>
#include <QQmlEngine> #include <QQmlEngine>
#include <QVariantList>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libdevcore/CommonJS.h> #include <libdevcore/CommonJS.h>
#include <libdevcrypto/Common.h> #include <libdevcrypto/Common.h>
@ -35,10 +36,9 @@ using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace dev::mix; using namespace dev::mix;
std::tuple<QList<QObject*>, QQMLMap*> DebuggingStateWrapper::getHumanReadableCode(const bytes& _code) QCode* QMachineState::getHumanReadableCode(QObject* _owner, const bytes& _code)
{ {
QList<QObject*> codeStr; QVariantList codeStr;
QMap<int, int> codeMapping;
for (unsigned i = 0; i <= _code.size(); ++i) for (unsigned i = 0; i <= _code.size(); ++i)
{ {
byte b = i < _code.size() ? _code[i] : 0; byte b = i < _code.size() ? _code[i] : 0;
@ -47,7 +47,6 @@ std::tuple<QList<QObject*>, QQMLMap*> DebuggingStateWrapper::getHumanReadableCod
QString s = QString::fromStdString(instructionInfo((Instruction)b).name); QString s = QString::fromStdString(instructionInfo((Instruction)b).name);
std::ostringstream out; std::ostringstream out;
out << std::hex << std::setw(4) << std::setfill('0') << i; out << std::hex << std::setw(4) << std::setfill('0') << i;
codeMapping[i] = codeStr.size();
int line = i; int line = i;
if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32) if (b >= (byte)Instruction::PUSH1 && b <= (byte)Instruction::PUSH32)
{ {
@ -55,8 +54,7 @@ std::tuple<QList<QObject*>, QQMLMap*> DebuggingStateWrapper::getHumanReadableCod
s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&_code[i + 1], bc))); s = "PUSH 0x" + QString::fromStdString(toHex(bytesConstRef(&_code[i + 1], bc)));
i += bc; i += bc;
} }
QPointer<HumanReadableCode> humanCode(new HumanReadableCode(QString::fromStdString(out.str()) + " " + s, line)); codeStr.append(QVariant::fromValue(new QInstruction(_owner, QString::fromStdString(out.str()) + " " + s, line)));
codeStr.append(humanCode);
} }
catch (...) catch (...)
{ {
@ -65,25 +63,25 @@ std::tuple<QList<QObject*>, QQMLMap*> DebuggingStateWrapper::getHumanReadableCod
break; // probably hit data segment break; // probably hit data segment
} }
} }
return std::make_tuple(codeStr, QPointer<QQMLMap>(new QQMLMap(codeMapping))); return new QCode(_owner, std::move(codeStr));
} }
QBigInt* DebuggingStateWrapper::gasCost() QBigInt* QMachineState::gasCost()
{ {
return new QBigInt(m_state.gasCost); return new QBigInt(m_state.gasCost);
} }
QBigInt* DebuggingStateWrapper::gas() QBigInt* QMachineState::gas()
{ {
return new QBigInt(m_state.gas); return new QBigInt(m_state.gas);
} }
QBigInt* DebuggingStateWrapper::newMemSize() QBigInt* QMachineState::newMemSize()
{ {
return new QBigInt(m_state.newMemSize); return new QBigInt(m_state.newMemSize);
} }
QStringList DebuggingStateWrapper::debugStack() QStringList QMachineState::debugStack()
{ {
QStringList stack; QStringList stack;
for (std::vector<u256>::reverse_iterator i = m_state.stack.rbegin(); i != m_state.stack.rend(); ++i) for (std::vector<u256>::reverse_iterator i = m_state.stack.rbegin(); i != m_state.stack.rend(); ++i)
@ -91,7 +89,7 @@ QStringList DebuggingStateWrapper::debugStack()
return fillList(stack, ""); return fillList(stack, "");
} }
QStringList DebuggingStateWrapper::debugStorage() QStringList QMachineState::debugStorage()
{ {
QStringList storage; QStringList storage;
for (auto const& i: m_state.storage) for (auto const& i: m_state.storage)
@ -103,7 +101,7 @@ QStringList DebuggingStateWrapper::debugStorage()
return fillList(storage, "@ -"); return fillList(storage, "@ -");
} }
QVariantList DebuggingStateWrapper::debugMemory() QVariantList QMachineState::debugMemory()
{ {
std::vector<std::vector<std::string>> dump = memDumpToList(m_state.memory, 16); std::vector<std::vector<std::string>> dump = memDumpToList(m_state.memory, 16);
QStringList filled; QStringList filled;
@ -113,17 +111,17 @@ QVariantList DebuggingStateWrapper::debugMemory()
return fillList(qVariantDump(dump), QVariant(filled)); return fillList(qVariantDump(dump), QVariant(filled));
} }
QVariantList DebuggingStateWrapper::debugCallData() QCallData* QMachineState::getDebugCallData(QObject* _owner, bytes const& _data)
{ {
std::vector<std::vector<std::string>> dump = memDumpToList(m_data, 16); std::vector<std::vector<std::string>> dump = memDumpToList(_data, 16);
QStringList filled; QStringList filled;
filled.append(" "); filled.append(" ");
filled.append(" "); filled.append(" ");
filled.append(" "); filled.append(" ");
return fillList(qVariantDump(dump), QVariant(filled)); return new QCallData(_owner, fillList(qVariantDump(dump), QVariant(filled)));
} }
std::vector<std::vector<std::string>> DebuggingStateWrapper::memDumpToList(bytes const& _bytes, unsigned _width) std::vector<std::vector<std::string>> QMachineState::memDumpToList(bytes const& _bytes, unsigned _width)
{ {
std::vector<std::vector<std::string>> dump; std::vector<std::vector<std::string>> dump;
for (unsigned i = 0; i < _bytes.size(); i += _width) for (unsigned i = 0; i < _bytes.size(); i += _width)
@ -155,7 +153,7 @@ std::vector<std::vector<std::string>> DebuggingStateWrapper::memDumpToList(bytes
return dump; return dump;
} }
QVariantList DebuggingStateWrapper::qVariantDump(std::vector<std::vector<std::string>> const& _dump) QVariantList QMachineState::qVariantDump(std::vector<std::vector<std::string>> const& _dump)
{ {
QVariantList ret; QVariantList ret;
for (std::vector<std::string> const& line: _dump) for (std::vector<std::string> const& line: _dump)
@ -168,7 +166,7 @@ QVariantList DebuggingStateWrapper::qVariantDump(std::vector<std::vector<std::st
return ret; return ret;
} }
QStringList DebuggingStateWrapper::fillList(QStringList& _list, QString const& _emptyValue) QStringList QMachineState::fillList(QStringList& _list, QString const& _emptyValue)
{ {
if (_list.size() < 20) if (_list.size() < 20)
{ {
@ -178,7 +176,7 @@ QStringList DebuggingStateWrapper::fillList(QStringList& _list, QString const& _
return _list; return _list;
} }
QVariantList DebuggingStateWrapper::fillList(QVariantList _list, QVariant const& _emptyValue) QVariantList QMachineState::fillList(QVariantList _list, QVariant const& _emptyValue)
{ {
if (_list.size() < 20) if (_list.size() < 20)
{ {
@ -188,14 +186,13 @@ QVariantList DebuggingStateWrapper::fillList(QVariantList _list, QVariant const&
return _list; return _list;
} }
QStringList QMachineState::levels()
QStringList DebuggingStateWrapper::levels()
{ {
QStringList levelsStr; QStringList levelsStr;
for (unsigned i = 0; i <= m_state.levels.size(); ++i) for (unsigned i = 0; i <= m_state.levels.size(); ++i)
{ {
std::ostringstream out; std::ostringstream out;
out << m_state.cur.abridged(); out << m_state.address.abridged();
if (i) if (i)
out << " " << instructionInfo(m_state.inst).name << " @0x" << std::hex << m_state.curPC; out << " " << instructionInfo(m_state.inst).name << " @0x" << std::hex << m_state.curPC;
levelsStr.append(QString::fromStdString(out.str())); levelsStr.append(QString::fromStdString(out.str()));
@ -203,19 +200,12 @@ QStringList DebuggingStateWrapper::levels()
return levelsStr; return levelsStr;
} }
QString DebuggingStateWrapper::headerInfo() QString QMachineState::instruction()
{
std::ostringstream ss;
ss << std::dec << " " << QApplication::tr("STEP").toStdString() << " : " << m_state.steps << " | PC: 0x" << std::hex << m_state.curPC << " : " << dev::eth::instructionInfo(m_state.inst).name << " | ADDMEM: " << std::dec << m_state.newMemSize << " " << QApplication::tr("words").toStdString() << " | " << QApplication::tr("COST").toStdString() << " : " << std::dec << m_state.gasCost << " | " << QApplication::tr("GAS").toStdString() << " : " << std::dec << m_state.gas;
return QString::fromStdString(ss.str());
}
QString DebuggingStateWrapper::instruction()
{ {
return QString::fromStdString(dev::eth::instructionInfo(m_state.inst).name); return QString::fromStdString(dev::eth::instructionInfo(m_state.inst).name);
} }
QString DebuggingStateWrapper::endOfDebug() QString QMachineState::endOfDebug()
{ {
if (m_state.gasCost > m_state.gas) if (m_state.gasCost > m_state.gas)
return QApplication::tr("OUT-OF-GAS"); return QApplication::tr("OUT-OF-GAS");

84
mix/DebuggingStateWrapper.h

@ -39,45 +39,61 @@ namespace mix
/** /**
* @brief Contains the line nb of the assembly code and the corresponding index in the code bytes array. * @brief Contains the line nb of the assembly code and the corresponding index in the code bytes array.
*/ */
class HumanReadableCode: public QObject class QInstruction: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString line READ line CONSTANT) Q_PROPERTY(QString line MEMBER m_line CONSTANT)
Q_PROPERTY(int processIndex READ processIndex CONSTANT) Q_PROPERTY(int processIndex MEMBER m_processIndex CONSTANT)
public: public:
HumanReadableCode(QString _line, int _processIndex): QObject(), m_line(_line), m_processIndex(_processIndex) {} QInstruction(QObject* _owner, QString _line, int _processIndex): QObject(_owner), m_line(_line), m_processIndex(_processIndex) {}
/// Get the assembly code line.
QString line() { return m_line; }
/// Get corresponding index.
int processIndex() { return m_processIndex; }
private: private:
QString m_line; QString m_line;
int m_processIndex; int m_processIndex;
}; };
class QCode: public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList instructions MEMBER m_instructions CONSTANT)
/** public:
* @brief Publish QMap type to QML. QCode(QObject* _owner, QVariantList&& _instrunctions): QObject(_owner), m_instructions(_instrunctions) {}
*/
class QQMLMap: public QObject private:
QVariantList m_instructions;
};
class QCallData: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QVariantList items MEMBER m_items CONSTANT)
public: public:
QQMLMap(QMap<int, int> _map): QObject(), m_map(_map) { } QCallData(QObject* _owner, QVariantList&& _items): QObject(_owner), m_items(_items) {}
/// Get the value associated with _key store in n_map.
Q_INVOKABLE int getValue(int _key) { return m_map.value(_key); }
private: private:
QMap<int, int> m_map; QVariantList m_items;
};
class QDebugData: public QObject
{
Q_OBJECT
Q_PROPERTY(QVariantList states MEMBER m_states CONSTANT)
public:
QDebugData() { }
void setStates(QVariantList&& _states) { m_states = _states; }
private:
QVariantList m_states;
}; };
/** /**
* @brief Wrap DebuggingState in QObject * @brief Wrap DebuggingState in QObject
*/ */
class DebuggingStateWrapper: public QObject class QMachineState: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int step READ step CONSTANT) Q_PROPERTY(int step READ step CONSTANT)
@ -88,18 +104,25 @@ class DebuggingStateWrapper: public QObject
Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT) Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT)
Q_PROPERTY(QStringList debugStorage READ debugStorage CONSTANT) Q_PROPERTY(QStringList debugStorage READ debugStorage CONSTANT)
Q_PROPERTY(QVariantList debugMemory READ debugMemory CONSTANT) Q_PROPERTY(QVariantList debugMemory READ debugMemory CONSTANT)
Q_PROPERTY(QVariantList debugCallData READ debugCallData CONSTANT) Q_PROPERTY(QObject* code MEMBER m_code CONSTANT)
Q_PROPERTY(QString headerInfo READ headerInfo CONSTANT) Q_PROPERTY(QObject* callData MEMBER m_callData CONSTANT)
Q_PROPERTY(QString endOfDebug READ endOfDebug CONSTANT) Q_PROPERTY(QString endOfDebug READ endOfDebug CONSTANT)
Q_PROPERTY(QBigInt* newMemSize READ newMemSize CONSTANT) Q_PROPERTY(QBigInt* newMemSize READ newMemSize CONSTANT)
Q_PROPERTY(QStringList levels READ levels CONSTANT) Q_PROPERTY(QStringList levels READ levels CONSTANT)
Q_PROPERTY(unsigned codeIndex READ codeIndex CONSTANT)
Q_PROPERTY(unsigned dataIndex READ dataIndex CONSTANT)
public: public:
DebuggingStateWrapper(bytes _code, bytes _data): QObject(), m_code(_code), m_data(_data) {} QMachineState(QObject* _owner, MachineState const& _state, QCode* _code, QCallData* _callData):
QObject(_owner), m_state(_state), m_code(_code), m_callData(_callData) {}
/// Get the step of this machine states. /// Get the step of this machine states.
int step() { return (int)m_state.steps; } int step() { return (int)m_state.steps; }
/// Get the proccessed code index. /// Get the proccessed code index.
int curPC() { return (int)m_state.curPC; } int curPC() { return (int)m_state.curPC; }
/// Get the code id
unsigned codeIndex() { return m_state.codeIndex; }
/// Get the call data id
unsigned dataIndex() { return m_state.dataIndex; }
/// Get gas cost. /// Get gas cost.
QBigInt* gasCost(); QBigInt* gasCost();
/// Get gas used. /// Get gas used.
@ -111,8 +134,6 @@ public:
/// Get memory. /// Get memory.
QVariantList debugMemory(); QVariantList debugMemory();
/// Get call data. /// Get call data.
QVariantList debugCallData();
/// Get info to be displayed in the header.
QString headerInfo(); QString headerInfo();
/// get end of debug information. /// get end of debug information.
QString endOfDebug(); QString endOfDebug();
@ -126,20 +147,21 @@ public:
MachineState state() { return m_state; } MachineState state() { return m_state; }
/// Set the current processed machine state. /// Set the current processed machine state.
void setState(MachineState _state) { m_state = _state; } void setState(MachineState _state) { m_state = _state; }
/// Convert all machine state in human readable code. /// Convert all machine states in human readable code.
static std::tuple<QList<QObject*>, QQMLMap*> getHumanReadableCode(bytes const& _code); static QCode* getHumanReadableCode(QObject* _owner, bytes const& _code);
/// Convert call data into human readable form
static QCallData* getDebugCallData(QObject* _owner, bytes const& _data);
private: private:
MachineState m_state; MachineState m_state;
bytes m_code; QCode* m_code;
bytes m_data; QCallData* m_callData;
QStringList fillList(QStringList& _list, QString const& _emptyValue); static QStringList fillList(QStringList& _list, QString const& _emptyValue);
QVariantList fillList(QVariantList _list, QVariant const& _emptyValue); static QVariantList fillList(QVariantList _list, QVariant const& _emptyValue);
QVariantList qVariantDump(std::vector<std::vector<std::string>> const& _dump); static QVariantList qVariantDump(std::vector<std::vector<std::string>> const& _dump);
/// Nicely renders the given bytes to a string, store the content in an array. /// Nicely renders the given bytes to a string, store the content in an array.
/// @a _bytes: bytes array to be rendered as string. @a _width of a bytes line. /// @a _bytes: bytes array to be rendered as string. @a _width of a bytes line.
std::vector<std::vector<std::string>> memDumpToList(bytes const& _bytes, unsigned _width); static std::vector<std::vector<std::string>> memDumpToList(bytes const& _bytes, unsigned _width);
}; };
} }

48
mix/MixClient.cpp

@ -63,21 +63,46 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state)
bytes rlp = _t.rlp(); bytes rlp = _t.rlp();
Executive execution(_state, LastHashes(), 0); Executive execution(_state, LastHashes(), 0);
execution.setup(&rlp); execution.setup(&rlp);
bytes code;
bytesConstRef data;
bool firstIteration = true;
std::vector<MachineState> machineStates; std::vector<MachineState> machineStates;
std::vector<MachineState const*> levels; std::vector<MachineState const*> levels;
std::vector<bytes> codes;
std::map<bytes const*, unsigned> codeIndexes;
std::vector<bytes> data;
std::map<bytesConstRef const*, unsigned> dataIndexes;
bytes const* lastCode = nullptr;
bytesConstRef const* lastData = nullptr;
unsigned codeIndex = 0;
unsigned dataIndex = 0;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt) auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt)
{ {
VM& vm = *(VM*)voidVM; VM& vm = *(VM*)voidVM;
ExtVM const& ext = *(ExtVM const*)voidExt; ExtVM const& ext = *(ExtVM const*)voidExt;
if (lastCode == nullptr || lastCode != &ext.code)
{
auto const& iter = codeIndexes.find(&ext.code);
if (iter != codeIndexes.end())
codeIndex = iter->second;
else
{
codeIndex = codes.size();
codes.push_back(ext.code);
codeIndexes[&ext.code] = codeIndex;
}
lastCode = &ext.code;
}
if (firstIteration) if (lastData == nullptr || lastData != &ext.data)
{ {
code = ext.code; auto const& iter = dataIndexes.find(&ext.data);
data = ext.data; if (iter != dataIndexes.end())
firstIteration = false; dataIndex = iter->second;
else
{
dataIndex = data.size();
data.push_back(ext.data.toBytes());
dataIndexes[&ext.data] = dataIndex;
}
lastData = &ext.data;
} }
if (levels.size() < ext.depth) if (levels.size() < ext.depth)
@ -85,8 +110,8 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state)
else else
levels.resize(ext.depth); levels.resize(ext.depth);
machineStates.push_back(MachineState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), machineStates.emplace_back(MachineState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(),
vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels})); vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels, codeIndex, dataIndex}));
}; };
execution.go(onOp); execution.go(onOp);
@ -95,9 +120,8 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state)
ExecutionResult d; ExecutionResult d;
d.returnValue = execution.out().toVector(); d.returnValue = execution.out().toVector();
d.machineStates = machineStates; d.machineStates = machineStates;
d.executionCode = code; d.executionCode = std::move(codes);
d.executionData = data; d.transactionData = std::move(data);
d.transactionData = _t.data();
d.address = _t.receiveAddress(); d.address = _t.receiveAddress();
d.sender = _t.sender(); d.sender = _t.sender();
d.value = _t.value(); d.value = _t.value();

9
mix/MixClient.h

@ -38,7 +38,7 @@ namespace mix
struct MachineState struct MachineState
{ {
uint64_t steps; uint64_t steps;
dev::Address cur; dev::Address address;
dev::u256 curPC; dev::u256 curPC;
dev::eth::Instruction inst; dev::eth::Instruction inst;
dev::bigint newMemSize; dev::bigint newMemSize;
@ -48,6 +48,8 @@ struct MachineState
dev::bigint gasCost; dev::bigint gasCost;
std::map<dev::u256, dev::u256> storage; std::map<dev::u256, dev::u256> storage;
std::vector<MachineState const*> levels; std::vector<MachineState const*> levels;
unsigned codeIndex;
unsigned dataIndex;
}; };
/** /**
@ -58,9 +60,8 @@ struct ExecutionResult
ExecutionResult(): receipt(dev::h256(), dev::h256(), dev::eth::LogEntries()) {} ExecutionResult(): receipt(dev::h256(), dev::h256(), dev::eth::LogEntries()) {}
std::vector<MachineState> machineStates; std::vector<MachineState> machineStates;
bytes transactionData; std::vector<bytes> transactionData;
bytes executionCode; std::vector<bytes> executionCode;
bytesConstRef executionData;
bytes returnValue; bytes returnValue;
dev::Address address; dev::Address address;
dev::Address sender; dev::Address sender;

14
mix/qml/Debugger.qml

@ -27,11 +27,11 @@ Rectangle {
forceActiveFocus(); forceActiveFocus();
} }
function update(giveFocus) function update(data, giveFocus)
{ {
if (statusPane.result.successful) if (statusPane.result.successful)
{ {
Debugger.init(); Debugger.init(data);
debugScrollArea.visible = true; debugScrollArea.visible = true;
compilationErrorArea.visible = false; compilationErrorArea.visible = false;
machineStates.visible = true; machineStates.visible = true;
@ -50,9 +50,16 @@ Rectangle {
forceActiveFocus(); forceActiveFocus();
} }
Connections {
target: clientModel
onDebugDataReady: {
update(_debugData, true);
}
}
Connections { Connections {
target: codeModel target: codeModel
onCompilationComplete: update(false) onCompilationComplete: update(null, false);
} }
Rectangle Rectangle
@ -277,6 +284,7 @@ Rectangle {
delegate: renderDelegate delegate: renderDelegate
highlight: highlightBar highlight: highlightBar
highlightFollowsCurrentItem: false highlightFollowsCurrentItem: false
model: ListModel {}
} }
Component { Component {

110
mix/qml/js/Debugger.js

@ -1,35 +1,53 @@
//humanReadableExecutionCode => contain human readable code. //debugData => contain all debug states.
//debugStates => contain all debug states.
//bytesCodeMapping => mapping between humanReadableExecutionCode and bytesCode.
//statesList => ListView //statesList => ListView
var currentSelectedState = null; var currentSelectedState = null;
var jumpStartingPoint = null; var jumpStartingPoint = null;
function init() var debugData = null;
var codeMap = null;
function init(data)
{ {
if (typeof(debugStates) === "undefined") jumpOutBackAction.enabled(false);
jumpIntoBackAction.enabled(false);
jumpIntoForwardAction.enabled(false);
jumpOutForwardAction.enabled(false);
if (data === null) {
statesList.model.clear();
statesSlider.maximumValue = 0;
statesSlider.value = 0;
currentSelectedState = null;
jumpStartingPoint = null;
debugData = null;
return; return;
}
statesSlider.maximumValue = debugStates.length - 1; debugData = data;
statesSlider.maximumValue = data.states.length - 1;
statesSlider.value = 0; statesSlider.value = 0;
statesList.model = humanReadableExecutionCode;
currentSelectedState = 0; currentSelectedState = 0;
setupInstructions(currentSelectedState);
select(currentSelectedState); select(currentSelectedState);
}
jumpOutBackAction.enabled(false); function setupInstructions(stateIndex) {
jumpIntoBackAction.enabled(false);
jumpIntoForwardAction.enabled(false); var instructions = debugData.states[stateIndex].code.instructions;
jumpOutForwardAction.enabled(false); codeMap = {};
statesList.model.clear();
for (var i = 0; i < instructions.length; i++) {
statesList.model.append(instructions[i]);
codeMap[instructions[i].processIndex] = i;
}
} }
function moveSelection(incr) function moveSelection(incr)
{ {
if (typeof(debugStates) === "undefined") var prevState = currentSelectedState;
return;
if (currentSelectedState + incr >= 0) if (currentSelectedState + incr >= 0)
{ {
if (currentSelectedState + incr < debugStates.length) if (currentSelectedState + incr < debugData.states.length)
select(currentSelectedState + incr); select(currentSelectedState + incr);
statesSlider.value = currentSelectedState; statesSlider.value = currentSelectedState;
} }
@ -37,16 +55,15 @@ function moveSelection(incr)
function select(stateIndex) function select(stateIndex)
{ {
if (typeof(debugStates) === "undefined") if (debugData.states[stateIndex].codeIndex !== debugData.states[currentSelectedState].codeIndex)
return; setupInstructions(stateIndex);
currentSelectedState = stateIndex;
var codeLine = codeStr(stateIndex); var codeLine = codeStr(stateIndex);
var state = debugStates[stateIndex]; var state = debugData.states[stateIndex];
highlightSelection(codeLine); highlightSelection(codeLine);
currentSelectedState = stateIndex;
completeCtxInformation(state); completeCtxInformation(state);
if (state.instruction === "JUMP") if (state.instruction === "CALL" || state.instruction === "CREATE")
jumpIntoForwardAction.enabled(true); jumpIntoForwardAction.enabled(true);
else else
jumpIntoForwardAction.enabled(false); jumpIntoForwardAction.enabled(false);
@ -59,11 +76,8 @@ function select(stateIndex)
function codeStr(stateIndex) function codeStr(stateIndex)
{ {
if (typeof(debugStates) === "undefined") var state = debugData.states[stateIndex];
return; return codeMap[state.curPC];
var state = debugStates[stateIndex];
return bytesCodeMapping.getValue(state.curPC);
} }
function highlightSelection(index) function highlightSelection(index)
@ -73,13 +87,10 @@ function highlightSelection(index)
function completeCtxInformation(state) function completeCtxInformation(state)
{ {
if (typeof(debugStates) === "undefined")
return;
currentStep.update(state.step); currentStep.update(state.step);
mem.update(state.newMemSize.value() + " " + qsTr("words")); mem.update(state.newMemSize.value() + " " + qsTr("words"));
stepCost.update(state.gasCost.value()); stepCost.update(state.gasCost.value());
gasSpent.update(debugStates[0].gas.subtract(state.gas).value()); gasSpent.update(debugData.states[0].gas.subtract(state.gas).value());
stack.listModel = state.debugStack; stack.listModel = state.debugStack;
storage.listModel = state.debugStorage; storage.listModel = state.debugStorage;
@ -87,17 +98,8 @@ function completeCtxInformation(state)
callDataDump.listModel = state.debugCallData; callDataDump.listModel = state.debugCallData;
} }
function displayReturnValue()
{
headerReturnList.model = contractCallReturnParameters;
headerReturnList.update();
}
function stepOutBack() function stepOutBack()
{ {
if (typeof(debugStates) === "undefined")
return;
if (jumpStartingPoint != null) if (jumpStartingPoint != null)
{ {
select(jumpStartingPoint); select(jumpStartingPoint);
@ -114,15 +116,12 @@ function stepIntoBack()
function stepOverBack() function stepOverBack()
{ {
if (typeof(debugStates) === "undefined") var state = debugData.states[currentSelectedState];
return; if (state.instruction === "CALL" || state.instruction === "CREATE")
var state = debugStates[currentSelectedState];
if (state.instruction === "JUMPDEST")
{ {
for (var k = currentSelectedState; k > 0; k--) for (var k = currentSelectedState; k > 0; k--)
{ {
var line = bytesCodeMapping.getValue(debugStates[k].curPC); var line = codeMap[debugData.states[k].curPC];
if (line === statesList.currentIndex - 2) if (line === statesList.currentIndex - 2)
{ {
select(k); select(k);
@ -136,15 +135,12 @@ function stepOverBack()
function stepOverForward() function stepOverForward()
{ {
if (typeof(debugStates) === "undefined") var state = debugData.states[currentSelectedState];
return; if (state.instruction === "CALL" || state.instruction === "CREATE")
var state = debugStates[currentSelectedState];
if (state.instruction === "JUMP")
{ {
for (var k = currentSelectedState; k < debugStates.length; k++) for (var k = currentSelectedState; k < debugData.states.length; k++)
{ {
var line = bytesCodeMapping.getValue(debugStates[k].curPC); var line = codeMap[debugData.states[k].curPC];
if (line === statesList.currentIndex + 2) if (line === statesList.currentIndex + 2)
{ {
select(k); select(k);
@ -158,11 +154,8 @@ function stepOverForward()
function stepIntoForward() function stepIntoForward()
{ {
if (typeof(debugStates) === "undefined") var state = debugData.states[currentSelectedState];
return; if (state.instruction === "CALL" || state.instruction === "CREATE")
var state = debugStates[currentSelectedState];
if (state.instruction === "JUMP")
{ {
jumpStartingPoint = currentSelectedState; jumpStartingPoint = currentSelectedState;
moveSelection(1); moveSelection(1);
@ -184,6 +177,5 @@ function stepOutForward()
function jumpTo(value) function jumpTo(value)
{ {
currentSelectedState = value; select(value);
select(currentSelectedState);
} }

Loading…
Cancel
Save