diff --git a/NEWS.md b/NEWS.md index fe6f265c..9c7b676f 100644 --- a/NEWS.md +++ b/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 diff --git a/RedPandaIDE/debugger.cpp b/RedPandaIDE/debugger.cpp index 9654bf85..2237fe33 100644 --- a/RedPandaIDE/debugger.cpp +++ b/RedPandaIDE/debugger.cpp @@ -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 &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 newModel; + for (int i=0;i(); + memoryLine->startAddress = -1; + if (dataLst.length()>0) { + memoryLine->startAddress = stringToHex(dataLst[0],0); + for (int j=1;j=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;idatas.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(); +} diff --git a/RedPandaIDE/debugger.h b/RedPandaIDE/debugger.h index 680b73d0..d5f0efc6 100644 --- a/RedPandaIDE/debugger.h +++ b/RedPandaIDE/debugger.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -204,6 +205,39 @@ private: int mUpdateCount; }; +struct MemoryLine { + uintptr_t startAddress; + QList datas; + QSet changedDatas; +}; + +using PMemoryLine = std::shared_ptr; + +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 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; diff --git a/RedPandaIDE/mainwindow.cpp b/RedPandaIDE/mainwindow.cpp index 8ab21ebb..5887da2a 100644 --- a/RedPandaIDE/mainwindow.cpp +++ b/RedPandaIDE/mainwindow.cpp @@ -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) diff --git a/RedPandaIDE/mainwindow.ui b/RedPandaIDE/mainwindow.ui index cd649490..d343b303 100644 --- a/RedPandaIDE/mainwindow.ui +++ b/RedPandaIDE/mainwindow.ui @@ -702,7 +702,7 @@ QTabWidget::North - 2 + 4 @@ -850,6 +850,13 @@ + + + + false + + + @@ -866,16 +873,6 @@ - - - - QPlainTextEdit::NoWrap - - - true - - - diff --git a/RedPandaIDE/settingsdialog/projectversioninfowidget.cpp b/RedPandaIDE/settingsdialog/projectversioninfowidget.cpp index be20360a..9af746da 100644 --- a/RedPandaIDE/settingsdialog/projectversioninfowidget.cpp +++ b/RedPandaIDE/settingsdialog/projectversioninfowidget.cpp @@ -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) diff --git a/RedPandaIDE/utils.cpp b/RedPandaIDE/utils.cpp index b8171516..ab0ed3ce 100644 --- a/RedPandaIDE/utils.cpp +++ b/RedPandaIDE/utils.cpp @@ -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; +} diff --git a/RedPandaIDE/utils.h b/RedPandaIDE/utils.h index 0a7871ce..c31c5488 100644 --- a/RedPandaIDE/utils.h +++ b/RedPandaIDE/utils.h @@ -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);