- enhancement: modify values in the memory view while debugging

- enhancement: auto update watch, local and memory view after expression evaluated
  - enhancement: auto update watch, local and memory view after memory modified
This commit is contained in:
Roy Qu 2022-01-11 22:29:03 +08:00
parent 8edace1c1d
commit 3ca1a9fc4c
8 changed files with 234 additions and 24 deletions

View File

@ -5,6 +5,11 @@ Red Panda C++ Version 0.13.2
- enhancement: if there's no selection when copy/cut, select currect line by default
- enhancement: support ligatures in fonts like fira code ( disabled by default, can be turned on in options dialog's editor font page)
- enhancement: add "minimum id length required to show code completion" to the options dialog's editor code completion page
- enhancement: modify values in the memory view while debugging
- enhancement: auto update watch, local and memory view after expression evaluated
- enhancement: auto update watch, local and memory view after memory modified
Red Panda C++ Version 0.13.1
- enhancement: suppoort localization info in project templates

View File

@ -39,6 +39,9 @@ Debugger::Debugger(QObject *parent) : QObject(parent)
mBacktraceModel=new BacktraceModel(this);
mWatchModel = new WatchModel(this);
mRegisterModel = new RegisterModel(this);
mMemoryModel = new MemoryModel(8,this);
connect(mMemoryModel,&MemoryModel::setMemoryData,
this, &Debugger::setMemoryData);
mExecuting = false;
mReader = nullptr;
mTarget = nullptr;
@ -98,6 +101,7 @@ bool Debugger::start(const QString& inferior)
}
}
mMemoryModel->reset();
mWatchModel->resetAllVarInfos();
if (pSettings->debugger().useGDBServer()) {
mTarget = new DebugTarget(inferior,compilerSet->debugServer(),pSettings->debugger().GDBServerPort());
@ -114,6 +118,7 @@ bool Debugger::start(const QString& inferior)
mReader = new DebugReader(this);
mReader->setDebuggerPath(debuggerPath);
connect(mReader, &QThread::finished,this,&Debugger::cleanUpReader);
connect(mReader, &QThread::finished,mMemoryModel,&MemoryModel::reset);
connect(mReader, &DebugReader::parseFinished,this,&Debugger::syncFinishedParsing,Qt::BlockingQueuedConnection);
connect(mReader, &DebugReader::changeDebugConsoleLastLine,this,&Debugger::onChangeDebugConsoleLastline);
connect(mReader, &DebugReader::cmdStarted,pMainWindow, &MainWindow::disableDebugActions);
@ -147,10 +152,9 @@ bool Debugger::start(const QString& inferior)
connect(mReader, &DebugReader::inferiorStopped,pMainWindow,
&MainWindow::setActiveBreakpoint);
connect(mReader, &DebugReader::inferiorStopped,this,
&Debugger::refreshWatchVars);
&Debugger::refreshAll);
mReader->registerInferiorStoppedCommand("-stack-list-frames","");
mReader->registerInferiorStoppedCommand("-stack-list-variables", "--all-values");
mReader->start();
mReader->waitStart();
@ -210,6 +214,14 @@ void Debugger::updateRegisterValues(const QHash<int, QString> &values)
mRegisterModel->updateValues(values);
}
void Debugger::refreshAll()
{
refreshWatchVars();
sendCommand("-stack-list-variables", "--all-values");
if (memoryModel()->startAddress()>0)
sendCommand("-data-read-memory",QString("%1 x 1 8 8 ").arg(memoryModel()->startAddress()));
}
RegisterModel *Debugger::registerModel() const
{
return mRegisterModel;
@ -407,6 +419,11 @@ void Debugger::fetchVarChildren(const QString &varName)
}
}
MemoryModel *Debugger::memoryModel() const
{
return mMemoryModel;
}
void Debugger::removeWatchVars(bool deleteparent)
{
if (deleteparent) {
@ -567,8 +584,15 @@ void Debugger::syncFinishedParsing()
}
}
void Debugger::setMemoryData(qulonglong address, unsigned char data)
{
sendCommand("-data-write-memory-bytes", QString("%1 \"%2\"").arg(address).arg(data,2,16,QChar('0')));
refreshAll();
}
void Debugger::updateMemory(const QStringList &value)
{
mMemoryModel->updateMemory(value);
emit memoryExamineReady(value);
}
@ -2339,3 +2363,132 @@ void DebugTarget::run()
emit processError(mProcess->error());
}
}
MemoryModel::MemoryModel(int dataPerLine, QObject *parent):
QAbstractTableModel(parent),
mDataPerLine(dataPerLine),
mStartAddress(0)
{
}
void MemoryModel::updateMemory(const QStringList &value)
{
QRegExp delimiter("(\\s+)");
QList<PMemoryLine> newModel;
for (int i=0;i<value.length();i++) {
QString line = value[i].trimmed();
QStringList dataLst = line.split(delimiter,QString::SkipEmptyParts);
PMemoryLine memoryLine = std::make_shared<MemoryLine>();
memoryLine->startAddress = -1;
if (dataLst.length()>0) {
memoryLine->startAddress = stringToHex(dataLst[0],0);
for (int j=1;j<dataLst.length();j++) {
qulonglong data = stringToHex(dataLst[j],-1);
if (data>=0)
memoryLine->datas.append((unsigned char)data);
}
}
newModel.append(memoryLine);
}
if (newModel.count()>0 && newModel.count()== mLines.count() &&
newModel[0]->startAddress == mLines[0]->startAddress) {
for (int i=0;i<newModel.count();i++) {
PMemoryLine newLine = newModel[i];
PMemoryLine oldLine = mLines[i];
for (int j=0;j<newLine->datas.count();j++) {
if (j>=oldLine->datas.count())
break;
if (newLine->datas[j]!=oldLine->datas[j])
newLine->changedDatas.insert(j);
}
}
mLines = newModel;
emit dataChanged(createIndex(0,0),
createIndex(mLines.count()-1,mDataPerLine-1));
} else {
beginResetModel();
mLines = newModel;
endResetModel();
}
if (mLines.count()>0) {
mStartAddress = mLines[0]->startAddress;
} else {
mStartAddress = 0;
}
}
int MemoryModel::rowCount(const QModelIndex &parent) const
{
return mLines.count();
}
int MemoryModel::columnCount(const QModelIndex &parent) const
{
return mDataPerLine;
}
QVariant MemoryModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row()<0 || index.row()>=mLines.count())
return QVariant();
PMemoryLine line = mLines[index.row()];
int col = index.column();
if (col<0 || col>=line->datas.count())
return QVariant();
if (role == Qt::DisplayRole)
return QString("%1").arg(line->datas[col],2,16,QChar('0'));
return QVariant();
}
QVariant MemoryModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical && role == Qt::DisplayRole) {
if (section<0 || section>=mLines.count())
return QVariant();
PMemoryLine line = mLines[section];
return QString("0x%1").arg(line->startAddress,0,16,QChar('0'));
}
return QVariant();
}
bool MemoryModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid())
return false;
if (index.row()<0 || index.row()>=mLines.count())
return false;
PMemoryLine line = mLines[index.row()];
int col = index.column();
if (col<0 || col>=line->datas.count())
return false;
if (role == Qt::EditRole && mStartAddress>0) {
bool ok;
unsigned char val = ("0x"+value.toString()).toUInt(&ok,16);
if (!ok || val>255)
return false;
emit setMemoryData(mStartAddress+mDataPerLine*index.row()+col,val);
return true;
}
return false;
}
Qt::ItemFlags MemoryModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
if (mStartAddress!=0)
flags |= Qt::ItemIsEditable;
return flags;
}
qulonglong MemoryModel::startAddress() const
{
return mStartAddress;
}
void MemoryModel::reset()
{
mStartAddress=0;
mLines.clear();
}

View File

@ -27,6 +27,7 @@
#include <QQueue>
#include <QQueue>
#include <QSemaphore>
#include <QSet>
#include <QThread>
#include <QTimer>
#include <memory>
@ -204,6 +205,39 @@ private:
int mUpdateCount;
};
struct MemoryLine {
uintptr_t startAddress;
QList<unsigned char> datas;
QSet<int> changedDatas;
};
using PMemoryLine = std::shared_ptr<MemoryLine>;
class MemoryModel: public QAbstractTableModel{
Q_OBJECT
public:
explicit MemoryModel(int dataPerLine,QObject* parent=nullptr);
void updateMemory(const QStringList& value);
qulonglong startAddress() const;
void reset();
// QAbstractItemModel interface
signals:
void setMemoryData(qlonglong address, unsigned char data);
public:
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
private:
int mDataPerLine;
QList<PMemoryLine> mLines;
qulonglong mStartAddress;
};
class DebugReader;
class DebugTarget;
@ -259,12 +293,15 @@ public:
RegisterModel *registerModel() const;
MemoryModel *memoryModel() const;
signals:
void evalValueReady(const QString& s);
void memoryExamineReady(const QStringList& s);
void localsReady(const QStringList& s);
public slots:
void stop();
void refreshAll();
private:
void sendWatchCommand(PWatchVar var);
@ -275,6 +312,7 @@ private:
private slots:
void syncFinishedParsing();
void setMemoryData(qulonglong address, unsigned char data);
void updateMemory(const QStringList& value);
void updateEval(const QString& value);
void updateDisassembly(const QString& file, const QString& func,const QStringList& value);
@ -291,6 +329,7 @@ private:
BacktraceModel *mBacktraceModel;
WatchModel *mWatchModel;
RegisterModel *mRegisterModel;
MemoryModel *mMemoryModel;
DebugReader *mReader;
DebugTarget *mTarget;
int mLeftPageIndexBackup;

View File

@ -135,6 +135,8 @@ MainWindow::MainWindow(QWidget *parent)
ui->tblBreakpoints->setModel(mDebugger->breakpointModel());
ui->tblStackTrace->setModel(mDebugger->backtraceModel());
ui->watchView->setModel(mDebugger->watchModel());
ui->tblMemoryView->setModel(mDebugger->memoryModel());
ui->tblMemoryView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
try {
mDebugger->breakpointModel()->load(includeTrailingPathDelimiter(pSettings->dirs().config())
@ -1116,7 +1118,8 @@ void MainWindow::updateDebuggerSettings()
QFont font(pSettings->debugger().fontName());
font.setPixelSize(pointToPixel(pSettings->debugger().fontSize()));
ui->debugConsole->setFont(font);
ui->txtMemoryView->setFont(font);
ui->tblMemoryView->setFont(font);
//ui->txtMemoryView->setFont(font);
ui->txtLocals->setFont(font);
int idx = findTabIndex(ui->debugViews,ui->tabDebugConsole);
@ -4421,6 +4424,7 @@ void MainWindow::onDebugEvaluateInput()
connect(mDebugger, &Debugger::evalValueReady,
this, &MainWindow::onEvalValueReady);
mDebugger->sendCommand("-data-evaluate-expression",s);
pMainWindow->debugger()->refreshAll();
}
}
@ -4428,8 +4432,8 @@ void MainWindow::onDebugMemoryAddressInput()
{
QString s=ui->cbMemoryAddress->currentText().trimmed();
if (!s.isEmpty()) {
connect(mDebugger, &Debugger::memoryExamineReady,
this, &MainWindow::onMemoryExamineReady);
// connect(mDebugger, &Debugger::memoryExamineReady,
// this, &MainWindow::onMemoryExamineReady);
mDebugger->sendCommand("-data-read-memory",QString("%1 x 1 8 8 ").arg(s));
}
}
@ -4487,14 +4491,15 @@ void MainWindow::onEvalValueReady(const QString& value)
void MainWindow::onMemoryExamineReady(const QStringList& value)
{
ui->txtMemoryView->clear();
foreach (QString s, value) {
s.replace("\t"," ");
ui->txtMemoryView->appendPlainText(s);
}
ui->txtMemoryView->moveCursor(QTextCursor::Start);
disconnect(mDebugger, &Debugger::memoryExamineReady,
this, &MainWindow::onMemoryExamineReady);
// ui->txtMemoryView->clear();
// foreach (QString s, value) {
// s.replace("\t"," ");
// ui->txtMemoryView->appendPlainText(s);
// }
// ui->txtMemoryView->moveCursor(QTextCursor::Start);
// disconnect(mDebugger, &Debugger::memoryExamineReady,
// this, &MainWindow::onMemoryExamineReady);
}
void MainWindow::onLocalsReady(const QStringList& value)

View File

@ -702,7 +702,7 @@
<enum>QTabWidget::North</enum>
</property>
<property name="currentIndex">
<number>2</number>
<number>4</number>
</property>
<widget class="QWidget" name="tabDebugConsole">
<attribute name="title">
@ -850,6 +850,13 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTableView" name="tblMemoryView">
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cbMemoryAddress">
<property name="sizePolicy">
@ -866,16 +873,6 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QPlainTextEdit" name="txtMemoryView">
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>

View File

@ -38,6 +38,7 @@ static BOOL CALLBACK localeEnumProc(
}
return TRUE;
}
ProjectVersionInfoWidget::ProjectVersionInfoWidget(const QString &name, const QString &group, QWidget *parent) :
SettingsWidget(name,group,parent),
ui(new Ui::ProjectVersionInfoWidget)

View File

@ -1077,3 +1077,12 @@ float desktopDpi()
{
return qApp->desktop()->logicalDpiY();
}
qulonglong stringToHex(const QString &str, qulonglong defaultValue)
{
bool isOk;
qulonglong value = str.toULongLong(&isOk,16);
if (isOk)
return value;
return defaultValue;
}

View File

@ -190,6 +190,7 @@ bool stringIsBlank(const QString& s);
int compareFileModifiedTime(const QString& filename1, const QString& filename2);
QByteArray getHTTPBody(const QByteArray& content);
bool haveGoodContrast(const QColor& c1, const QColor &c2);
qulonglong stringToHex(const QString& str, qulonglong defaultValue = 0);
//void changeTheme(const QString& themeName);