- 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:
parent
8edace1c1d
commit
3ca1a9fc4c
5
NEWS.md
5
NEWS.md
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue