RedPanda-CPP/RedPandaIDE/debugger.cpp

1832 lines
49 KiB
C++
Raw Normal View History

2021-07-03 21:57:50 +08:00
#include "debugger.h"
2021-07-17 19:32:23 +08:00
#include "utils.h"
#include "mainwindow.h"
2021-07-24 11:18:25 +08:00
#include "editor.h"
2021-07-25 00:26:13 +08:00
#include "settings.h"
2021-08-01 23:24:37 +08:00
#include "widgets/cpudialog.h"
2021-07-25 00:26:13 +08:00
#include <QFile>
#include <QFileInfo>
#include <QMessageBox>
2021-08-01 10:00:27 +08:00
#include <QPlainTextEdit>
2021-08-01 01:06:43 +08:00
#include <QDebug>
2021-07-03 21:57:50 +08:00
Debugger::Debugger(QObject *parent) : QObject(parent)
{
2021-07-24 11:18:25 +08:00
mBreakpointModel=new BreakpointModel(this);
mBacktraceModel=new BacktraceModel(this);
2021-08-01 01:06:43 +08:00
mWatchModel = new WatchModel(this);
2021-08-01 23:24:37 +08:00
mRegisterModel = new RegisterModel(this);
2021-07-27 00:14:24 +08:00
mExecuting = false;
mUseUTF8 = false;
mReader = nullptr;
mCommandChanged = false;
mLeftPageIndexBackup = -1;
2021-07-24 11:18:25 +08:00
}
2021-08-20 12:43:01 +08:00
bool Debugger::start()
2021-07-25 00:26:13 +08:00
{
Settings::PCompilerSet compilerSet = pSettings->compilerSets().defaultSet();
if (!compilerSet) {
QMessageBox::critical(pMainWindow,
tr("No compiler set"),
tr("No compiler set is configured.")+tr("Can't start debugging."));
2021-08-20 12:43:01 +08:00
return false;
2021-07-25 00:26:13 +08:00
}
2021-07-26 22:29:47 +08:00
mExecuting = true;
2021-07-25 00:26:13 +08:00
QString debuggerPath = compilerSet->debugger();
QFile debuggerProgram(debuggerPath);
if (!debuggerProgram.exists()) {
2021-08-20 12:43:01 +08:00
mExecuting = false;
2021-07-25 00:26:13 +08:00
QMessageBox::critical(pMainWindow,
tr("Debugger not exists"),
tr("Can''t find debugger in : \"%1\"").arg(debuggerPath));
2021-08-20 12:43:01 +08:00
return false;
2021-07-25 00:26:13 +08:00
}
2021-07-26 00:22:08 +08:00
mReader = new DebugReader(this);
2021-07-25 00:26:13 +08:00
mReader->setDebuggerPath(debuggerPath);
2021-07-31 14:04:43 +08:00
connect(mReader, &QThread::finished,this,&Debugger::clearUpReader);
2021-07-26 11:47:54 +08:00
connect(mReader, &DebugReader::parseFinished,this,&Debugger::syncFinishedParsing,Qt::BlockingQueuedConnection);
2021-08-01 12:02:28 +08:00
connect(mReader, &DebugReader::changeDebugConsoleLastLine,this,&Debugger::onChangeDebugConsoleLastline);
2021-08-01 10:00:27 +08:00
connect(mReader, &DebugReader::addLocalWithLinebreak,this,&Debugger::onAddLocalWithLinebreak);
connect(mReader, &DebugReader::addLocalWithoutLinebreak,this,&Debugger::onAddLocalWithoutLinebreak);
connect(mReader, &DebugReader::clearLocals,this,&Debugger::onClearLocals);
2021-07-25 00:26:13 +08:00
mReader->start();
2021-07-27 00:14:24 +08:00
mReader->mStartSemaphore.acquire(1);
2021-07-25 00:26:13 +08:00
2021-07-26 00:22:08 +08:00
pMainWindow->updateAppTitle();
2021-07-25 00:26:13 +08:00
2021-07-25 13:03:46 +08:00
//Application.HintHidePause := 5000;
2021-08-20 12:43:01 +08:00
return true;
2021-07-25 13:03:46 +08:00
}
2021-07-31 14:04:43 +08:00
void Debugger::stop() {
if (mExecuting) {
mReader->stopDebug();
}
}
void Debugger::clearUpReader()
2021-07-25 13:03:46 +08:00
{
if (mExecuting) {
mExecuting = false;
2021-07-26 00:22:08 +08:00
//stop debugger
mReader->deleteLater();
mReader=nullptr;
2021-07-31 14:04:43 +08:00
2021-07-26 00:22:08 +08:00
// if WatchVarList.Count = 0 then // nothing worth showing, restore view
// MainForm.LeftPageControl.ActivePageIndex := LeftPageIndexBackup;
2021-07-25 13:03:46 +08:00
2021-07-26 00:22:08 +08:00
// // Close CPU window
2021-08-01 23:24:37 +08:00
if (pMainWindow->cpuDialog()!=nullptr) {
pMainWindow->cpuDialog()->close();
}
2021-07-25 13:03:46 +08:00
// Free resources
2021-07-26 00:22:08 +08:00
pMainWindow->removeActiveBreakpoints();
2021-07-25 13:03:46 +08:00
2021-08-01 10:00:27 +08:00
pMainWindow->txtLocals()->clear();
2021-07-26 00:22:08 +08:00
pMainWindow->updateAppTitle();
2021-07-25 13:03:46 +08:00
2021-08-01 23:24:37 +08:00
pMainWindow->updateDebugEval("");
2021-07-26 11:47:54 +08:00
mBacktraceModel->clear();
2021-07-25 13:03:46 +08:00
2021-08-01 01:06:43 +08:00
for(PWatchVar var:mWatchModel->watchVars()) {
invalidateWatchVar(var);
}
2021-08-01 12:02:28 +08:00
pMainWindow->updateEditorActions();
2021-07-25 13:03:46 +08:00
}
2021-07-25 00:26:13 +08:00
}
2021-08-01 10:00:27 +08:00
void Debugger::onAddLocalWithoutLinebreak(const QString &text)
{
pMainWindow->txtLocals()->insertPlainText(text);
}
void Debugger::onAddLocalWithLinebreak(const QString &text)
{
pMainWindow->txtLocals()->appendPlainText(text);
}
void Debugger::onClearLocals()
{
pMainWindow->txtLocals()->clear();
}
2021-08-01 23:24:37 +08:00
RegisterModel *Debugger::registerModel() const
{
return mRegisterModel;
}
2021-08-01 01:06:43 +08:00
WatchModel *Debugger::watchModel() const
{
return mWatchModel;
}
2021-07-24 11:18:25 +08:00
void Debugger::sendCommand(const QString &command, const QString &params, bool updateWatch, bool showInConsole, DebugCommandSource source)
{
if (mExecuting && mReader) {
mReader->postCommand(command,params,updateWatch,showInConsole,source);
}
}
2021-08-29 22:08:43 +08:00
bool Debugger::commandRunning()
{
if (mExecuting && mReader) {
return mReader->commandRunning();
}
return false;
}
2021-07-24 11:18:25 +08:00
void Debugger::addBreakpoint(int line, const Editor* editor)
{
addBreakpoint(line,editor->filename());
}
2021-07-03 21:57:50 +08:00
2021-07-24 11:18:25 +08:00
void Debugger::addBreakpoint(int line, const QString &filename)
{
PBreakpoint bp=std::make_shared<Breakpoint>();
bp->line = line;
bp->filename = filename;
bp->condition = "";
mBreakpointModel->addBreakpoint(bp);
2021-07-25 13:03:46 +08:00
if (mExecuting) {
sendBreakpointCommand(bp);
}
2021-07-24 11:18:25 +08:00
}
void Debugger::deleteBreakpoints(const QString &filename)
{
for (int i=mBreakpointModel->breakpoints().size()-1;i>=0;i--) {
PBreakpoint bp = mBreakpointModel->breakpoints()[i];
if (bp->filename == filename) {
mBreakpointModel->removeBreakpoint(i);
}
}
}
void Debugger::deleteBreakpoints(const Editor *editor)
{
deleteBreakpoints(editor->filename());
}
void Debugger::removeBreakpoint(int line, const Editor *editor)
{
removeBreakpoint(line,editor->filename());
}
void Debugger::removeBreakpoint(int line, const QString &filename)
{
for (int i=mBreakpointModel->breakpoints().size()-1;i>=0;i--) {
PBreakpoint bp = mBreakpointModel->breakpoints()[i];
if (bp->filename == filename && bp->line == line) {
removeBreakpoint(i);
}
}
}
void Debugger::removeBreakpoint(int index)
{
sendClearBreakpointCommand(index);
mBreakpointModel->removeBreakpoint(index);
}
void Debugger::setBreakPointCondition(int index, const QString &condition)
{
PBreakpoint breakpoint=mBreakpointModel->setBreakPointCondition(index,condition);
if (condition.isEmpty()) {
sendCommand("cond",
QString("%1").arg(breakpoint->line));
} else {
sendCommand("cond",
QString("%1 %2").arg(breakpoint->line).arg(condition));
}
2021-07-03 21:57:50 +08:00
}
2021-07-25 13:03:46 +08:00
void Debugger::sendAllBreakpointsToDebugger()
{
for (PBreakpoint breakpoint:mBreakpointModel->breakpoints()) {
sendBreakpointCommand(breakpoint);
}
}
2021-07-31 20:19:45 +08:00
void Debugger::addWatchVar(const QString &namein)
2021-07-26 18:22:09 +08:00
{
2021-07-31 20:19:45 +08:00
// Don't allow duplicates...
PWatchVar oldVar = mWatchModel->findWatchVar(namein);
if (oldVar)
return;
2021-07-26 18:22:09 +08:00
2021-07-31 20:19:45 +08:00
PWatchVar var = std::make_shared<WatchVar>();
var->parent= nullptr;
var->name = namein;
2021-08-01 01:06:43 +08:00
var->text = QString("%1 = %2").arg(var->name).arg(tr("Execute to evaluate"));
2021-07-31 20:19:45 +08:00
var->gdbIndex = -1;
2021-07-26 18:22:09 +08:00
2021-07-31 20:19:45 +08:00
mWatchModel->addWatchVar(var);
sendWatchCommand(var);
2021-07-26 18:22:09 +08:00
}
void Debugger::renameWatchVar(const QString &oldname, const QString &newname)
{
2021-07-31 20:19:45 +08:00
// check if name already exists;
PWatchVar var = mWatchModel->findWatchVar(newname);
if (var)
return;
var = mWatchModel->findWatchVar(oldname);
if (var) {
var->name = newname;
2021-08-01 01:06:43 +08:00
if (mExecuting && var->gdbIndex!=-1)
sendRemoveWatchCommand(var);
invalidateWatchVar(var);
2021-07-31 20:19:45 +08:00
if (mExecuting) {
sendWatchCommand(var);
}
}
2021-07-26 18:22:09 +08:00
}
void Debugger::refreshWatchVars()
{
2021-07-31 20:19:45 +08:00
for (PWatchVar var:mWatchModel->watchVars()) {
if (var->gdbIndex == -1)
sendWatchCommand(var);
}
2021-07-26 18:22:09 +08:00
}
void Debugger::deleteWatchVars(bool deleteparent)
{
2021-07-31 20:19:45 +08:00
if (deleteparent) {
mWatchModel->clear();
} else {
for(PWatchVar var:mWatchModel->watchVars()) {
sendRemoveWatchCommand(var);
2021-08-01 01:06:43 +08:00
invalidateWatchVar(var);
2021-07-31 20:19:45 +08:00
}
}
2021-07-26 18:22:09 +08:00
}
void Debugger::invalidateAllVars()
{
2021-07-31 20:19:45 +08:00
mReader->setInvalidateAllVars(true);
2021-07-26 18:22:09 +08:00
}
void Debugger::sendAllWatchvarsToDebugger()
{
2021-07-31 20:19:45 +08:00
for (PWatchVar var:mWatchModel->watchVars()) {
sendWatchCommand(var);
}
2021-07-26 18:22:09 +08:00
}
2021-08-01 01:06:43 +08:00
void Debugger::invalidateWatchVar(const QString &name)
{
PWatchVar var = mWatchModel->findWatchVar(name);
if (var) {
invalidateWatchVar(var);
}
}
2021-07-26 18:22:09 +08:00
void Debugger::invalidateWatchVar(PWatchVar var)
{
2021-08-01 01:06:43 +08:00
var->gdbIndex = -1;
QString value;
if (mExecuting) {
value = tr("Not found in current context");
} else {
value = tr("Execute to evaluate");
}
var->text = QString("%1 = %2").arg(var->name).arg(value);
var->children.clear();
mWatchModel->notifyUpdated(var);
}
PWatchVar Debugger::findWatchVar(const QString &name)
{
return mWatchModel->findWatchVar(name);
}
void Debugger::notifyWatchVarUpdated(PWatchVar var)
{
mWatchModel->notifyUpdated(var);
2021-07-26 18:22:09 +08:00
}
void Debugger::updateDebugInfo()
{
sendCommand("backtrace", "");
sendCommand("info locals", "");
sendCommand("info args", "");
}
2021-07-24 08:12:51 +08:00
bool Debugger::useUTF8() const
{
return mUseUTF8;
}
void Debugger::setUseUTF8(bool useUTF8)
{
mUseUTF8 = useUTF8;
}
2021-07-25 00:26:13 +08:00
BacktraceModel* Debugger::backtraceModel()
2021-07-24 08:12:51 +08:00
{
return mBacktraceModel;
}
2021-07-25 00:26:13 +08:00
BreakpointModel *Debugger::breakpointModel()
2021-07-24 11:18:25 +08:00
{
return mBreakpointModel;
}
2021-07-31 20:19:45 +08:00
void Debugger::sendWatchCommand(PWatchVar var)
2021-07-24 11:18:25 +08:00
{
2021-07-31 20:19:45 +08:00
sendCommand("display", var->name);
}
void Debugger::sendRemoveWatchCommand(PWatchVar var)
{
sendCommand("undisplay",QString("%1").arg(var->gdbIndex));
2021-07-25 13:03:46 +08:00
}
void Debugger::sendBreakpointCommand(PBreakpoint breakpoint)
{
if (breakpoint && mExecuting) {
// break "filename":linenum
QString condition;
if (!breakpoint->condition.isEmpty()) {
condition = " if " + breakpoint->condition;
}
QString filename = breakpoint->filename;
filename.replace('\\','/');
sendCommand("break",
QString("\"%1\":%2").arg(filename)
.arg(breakpoint->line)+condition);
2021-07-24 11:18:25 +08:00
}
}
void Debugger::sendClearBreakpointCommand(int index)
2021-07-25 13:03:46 +08:00
{
2021-07-26 11:47:54 +08:00
sendClearBreakpointCommand(mBreakpointModel->breakpoints()[index]);
2021-07-25 13:03:46 +08:00
}
void Debugger::sendClearBreakpointCommand(PBreakpoint breakpoint)
2021-07-24 11:18:25 +08:00
{
// Debugger already running? Remove it from GDB
2021-07-25 13:03:46 +08:00
if (breakpoint && mExecuting) {
2021-07-24 11:18:25 +08:00
//clear "filename":linenum
QString filename = breakpoint->filename;
filename.replace('\\','/');
sendCommand("clear",
2021-07-25 13:03:46 +08:00
QString("\"%1\":%2").arg(filename)
.arg(breakpoint->line));
2021-07-24 11:18:25 +08:00
}
}
2021-07-26 11:47:54 +08:00
void Debugger::syncFinishedParsing()
{
bool spawnedcpuform = false;
// GDB determined that the source code is more recent than the executable. Ask the user if he wants to rebuild.
if (mReader->doreceivedsfwarning) {
if (QMessageBox::question(pMainWindow,
tr("Compile"),
tr("Source file is more recent than executable.")+"<BR /><BR />" + tr("Recompile?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes
) == QMessageBox::Yes) {
stop();
pMainWindow->compile();
return;
}
}
// The program to debug has stopped. Stop the debugger
if (mReader->doprocessexited) {
stop();
return;
}
// An evaluation variable has been processed. Forward the results
if (mReader->doevalready) {
2021-08-29 22:08:43 +08:00
//pMainWindow->updateDebugEval(mReader->mEvalValue);
emit evalValueReady(mReader->mEvalValue);
2021-08-01 23:24:37 +08:00
mReader->mEvalValue="";
mReader->doevalready = false;
2021-07-26 11:47:54 +08:00
}
// show command output
if (pSettings->debugger().showCommandLog() ||
(mReader->mCurrentCmd && mReader->mCurrentCmd->showInConsole)) {
if (pSettings->debugger().showAnnotations()) {
QString strOutput = mReader->mOutput;
strOutput.replace(QChar(26),'>');
pMainWindow->addDebugOutput(strOutput);
pMainWindow->addDebugOutput("");
pMainWindow->addDebugOutput("");
} else {
QStringList strList = TextToLines(mReader->mOutput);
QStringList outStrList;
bool addToLastLine=false;
for (int i=0;i<strList.size();i++) {
QString strOutput=strList[i];
if (strOutput.startsWith("\032\032")) {
addToLastLine = true;
} else {
if (addToLastLine && outStrList.size()>0) {
outStrList[outStrList.size()-1]+=strOutput;
} else {
outStrList.append(strOutput);
}
addToLastLine = false;
}
}
for (const QString& line:outStrList) {
pMainWindow->addDebugOutput(line);
}
}
}
// Some part of the CPU form has been updated
2021-08-01 23:24:37 +08:00
if (pMainWindow->cpuDialog()!=nullptr && !mReader->doreceivedsignal) {
if (mReader->doregistersready) {
mRegisterModel->update(mReader->mRegisters);
mReader->mRegisters.clear();
mReader->doregistersready = false;
}
2021-07-26 11:47:54 +08:00
2021-08-01 23:24:37 +08:00
if (mReader->dodisassemblerready) {
pMainWindow->cpuDialog()->setDisassembly(mReader->mDisassembly);
mReader->mDisassembly.clear();
mReader->dodisassemblerready = false;
}
2021-07-26 11:47:54 +08:00
}
if (mReader->doupdateexecution) {
2021-07-26 18:22:09 +08:00
if (mReader->mCurrentCmd && mReader->mCurrentCmd->source == DebugCommandSource::Console) {
2021-07-26 11:47:54 +08:00
pMainWindow->setActiveBreakpoint(mReader->mBreakPointFile, mReader->mBreakPointLine,false);
} else {
pMainWindow->setActiveBreakpoint(mReader->mBreakPointFile, mReader->mBreakPointLine);
}
refreshWatchVars(); // update variable information
}
if (mReader->doreceivedsignal) {
//SignalDialog := CreateMessageDialog(fSignal, mtError, [mbOk]);
//SignalCheck := TCheckBox.Create(SignalDialog);
//// Display it on top of everything
//SignalDialog.FormStyle := fsStayOnTop;
//SignalDialog.Height := 150;
//with SignalCheck do begin
// Parent := SignalDialog;
// Caption := 'Show CPU window';
// Top := Parent.ClientHeight - 22;
// Left := 8;
// Width := Parent.ClientWidth - 16;
// Checked := devData.ShowCPUSignal;
//end;
//MessageBeep(MB_ICONERROR);
//if SignalDialog.ShowModal = ID_OK then begin
// devData.ShowCPUSignal := SignalCheck.Checked;
// if SignalCheck.Checked and not Assigned(CPUForm) then begin
// MainForm.ViewCPUItemClick(nil);
// spawnedcpuform := true;
// end;
//end;
//SignalDialog.Free;
}
// CPU form updates itself when spawned, don't update twice!
2021-08-01 23:24:37 +08:00
if ((mReader->doupdatecpuwindow && !spawnedcpuform) && (pMainWindow->cpuDialog()!=nullptr)) {
pMainWindow->cpuDialog()->updateInfo();
2021-07-26 11:47:54 +08:00
}
}
2021-07-30 23:28:58 +08:00
void Debugger::onChangeDebugConsoleLastline(const QString &text)
{
//pMainWindow->changeDebugOutputLastline(text);
pMainWindow->addDebugOutput(text);
}
2021-07-26 18:22:09 +08:00
int Debugger::leftPageIndexBackup() const
{
return mLeftPageIndexBackup;
}
void Debugger::setLeftPageIndexBackup(int leftPageIndexBackup)
{
mLeftPageIndexBackup = leftPageIndexBackup;
}
2021-07-26 11:47:54 +08:00
bool Debugger::executing() const
{
return mExecuting;
}
2021-07-27 00:14:24 +08:00
DebugReader::DebugReader(Debugger* debugger, QObject *parent) : QThread(parent),
mStartSemaphore(0)
2021-07-03 21:57:50 +08:00
{
2021-07-25 00:26:13 +08:00
mDebugger = debugger;
2021-07-27 00:14:24 +08:00
mProcess = nullptr;
mUseUTF8 = false;
2021-07-31 20:19:45 +08:00
mCmdRunning = false;
mInvalidateAllVars = false;
2021-07-03 21:57:50 +08:00
}
2021-07-19 23:02:32 +08:00
void DebugReader::postCommand(const QString &Command, const QString &Params, bool UpdateWatch, bool ShowInConsole, DebugCommandSource Source)
{
QMutexLocker locker(&mCmdQueueMutex);
if (mCmdQueue.isEmpty() && UpdateWatch) {
emit pauseWatchUpdate();
mUpdateCount++;
}
PDebugCommand pCmd = std::make_shared<DebugCommand>();
pCmd->command = Command;
pCmd->params = Params;
pCmd->updateWatch = UpdateWatch;
pCmd->showInConsole = ShowInConsole;
pCmd->source = Source;
mCmdQueue.enqueue(pCmd);
2021-07-30 23:28:58 +08:00
// if (!mCmdRunning)
// runNextCmd();
2021-07-19 23:02:32 +08:00
}
2021-07-03 21:57:50 +08:00
void DebugReader::clearCmdQueue()
{
2021-07-19 23:02:32 +08:00
QMutexLocker locker(&mCmdQueueMutex);
2021-07-03 21:57:50 +08:00
mCmdQueue.clear();
2021-07-19 23:02:32 +08:00
if (mUpdateCount>0) {
emit updateWatch();
mUpdateCount=0;
2021-07-03 21:57:50 +08:00
}
}
bool DebugReader::findAnnotation(AnnotationType annotation)
{
AnnotationType NextAnnotation;
do {
NextAnnotation = getNextAnnotation();
if (NextAnnotation == AnnotationType::TEOF)
return false;
} while (NextAnnotation != annotation);
return true;
}
AnnotationType DebugReader::getAnnotation(const QString &s)
{
if (s == "pre-prompt") {
return AnnotationType::TPrePrompt;
} else if (s == "prompt") {
return AnnotationType::TPrompt;
} else if (s == "post-prompt") {
2021-07-25 00:26:13 +08:00
AnnotationType result = AnnotationType::TPostPrompt;
int IndexBackup = mIndex;
QString t = getNextFilledLine();
mIndex = IndexBackup;
//hack to catch local
if ((mCurrentCmd) && (mCurrentCmd->command == "info locals")) {
result = AnnotationType::TLocal;
} else if ((mCurrentCmd) && (mCurrentCmd->command == "info args")) {
//hack to catch params
result = AnnotationType::TParam;
2021-08-01 23:24:37 +08:00
} else if ((mCurrentCmd) && (mCurrentCmd->command == "info") && (mCurrentCmd->params=="registers")) {
2021-07-25 00:26:13 +08:00
// Hack fix to catch register dump
result = AnnotationType::TInfoReg;
2021-08-01 23:24:37 +08:00
} else if ((mCurrentCmd) && (mCurrentCmd->command == "disas")) {
// Another hack to catch assembler
2021-07-25 00:26:13 +08:00
result = AnnotationType::TInfoAsm;
}
return result;
2021-07-03 21:57:50 +08:00
} else if (s == "error-begin") {
return AnnotationType::TErrorBegin;
} else if (s == "error-end") {
return AnnotationType::TErrorEnd;
} else if (s == "display-begin") {
return AnnotationType::TDisplayBegin;
} else if (s == "display-expression") {
return AnnotationType::TDisplayExpression;
} else if (s == "display-end") {
return AnnotationType::TDisplayEnd;
} else if (s == "frame-source-begin") {
return AnnotationType::TFrameSourceBegin;
} else if (s == "frame-source-file") {
return AnnotationType::TFrameSourceFile;
} else if (s == "frame-source-line") {
return AnnotationType::TFrameSourceLine;
} else if (s == "frame-function-name") {
return AnnotationType::TFrameFunctionName;
} else if (s == "frame-args") {
return AnnotationType::TFrameArgs;
} else if (s == "frame-begin") {
return AnnotationType::TFrameBegin;
} else if (s == "frame-end") {
return AnnotationType::TFrameEnd;
} else if (s == "frame-where") {
return AnnotationType::TFrameWhere;
} else if (s == "source") {
return AnnotationType::TSource;
} else if (s == "exited") {
return AnnotationType::TExit;
} else if (s == "arg-begin") {
return AnnotationType::TArgBegin;
} else if (s == "arg-name-end") {
return AnnotationType::TArgNameEnd;
} else if (s == "arg-value") {
return AnnotationType::TArgValue;
} else if (s == "arg-end") {
return AnnotationType::TArgEnd;
} else if (s == "array-section-begin") {
return AnnotationType::TArrayBegin;
} else if (s == "array-section-end") {
return AnnotationType::TArrayEnd;
} else if (s == "elt") {
return AnnotationType::TElt;
} else if (s == "elt-rep") {
return AnnotationType::TEltRep;
} else if (s == "elt-rep-end") {
return AnnotationType::TEltRepEnd;
} else if (s == "field-begin") {
return AnnotationType::TFieldBegin;
} else if (s == "field-name-end") {
return AnnotationType::TFieldNameEnd;
} else if (s == "field-value") {
return AnnotationType::TFieldValue;
} else if (s == "field-end") {
return AnnotationType::TFieldEnd;
} else if (s == "value-history-value") {
return AnnotationType::TValueHistoryValue;
} else if (s == "value-history-begin") {
return AnnotationType::TValueHistoryBegin;
} else if (s == "value-history-end") {
return AnnotationType::TValueHistoryEnd;
} else if (s == "signal") {
return AnnotationType::TSignal;
} else if (s == "signal-name") {
return AnnotationType::TSignalName;
} else if (s == "signal-name-end") {
return AnnotationType::TSignalNameEnd;
} else if (s == "signal-string") {
return AnnotationType::TSignalString;
} else if (s == "signal-string-end") {
return AnnotationType::TSignalStringEnd;
2021-07-17 19:32:23 +08:00
} else if (mIndex >= mOutput.length()) {
2021-07-03 21:57:50 +08:00
return AnnotationType::TEOF;
} else {
return AnnotationType::TUnknown;;
}
}
2021-07-19 23:02:32 +08:00
AnnotationType DebugReader::getLastAnnotation(const QByteArray &text)
2021-07-17 19:32:23 +08:00
{
2021-07-19 23:02:32 +08:00
int curpos = text.length()-1;
2021-07-17 19:32:23 +08:00
// Walk back until end of #26's
while ((curpos >= 0) && (text[curpos] != 26))
curpos--;
curpos++;
// Tiny rewrite of GetNextWord for special purposes
QString s = "";
2021-07-19 23:02:32 +08:00
while ((curpos < text.length()) && (text[curpos]>32)) {
2021-07-17 19:32:23 +08:00
s = s + text[curpos];
curpos++;
}
return getAnnotation(s);
}
AnnotationType DebugReader::getNextAnnotation()
{
// Skip until end of #26's, i.e. GDB formatted output
skipToAnnotation();
// Get part this line, after #26#26
return getAnnotation(getNextWord());
}
QString DebugReader::getNextFilledLine()
{
// Walk up to an enter sequence
2021-08-16 23:17:48 +08:00
while (mIndex<mOutput.length() && mOutput[mIndex]!='\r' && mOutput[mIndex]!='\n' && mOutput[mIndex]!=0)
2021-07-17 19:32:23 +08:00
mIndex++;
// Skip enter sequences (CRLF, CR, LF, etc.)
2021-08-16 23:17:48 +08:00
while (mIndex<mOutput.length() && mOutput[mIndex]=='\r' && mOutput[mIndex]=='\n' && mOutput[mIndex]==0)
2021-07-17 19:32:23 +08:00
mIndex++;
// Return next line
return getRemainingLine();
}
QString DebugReader::getNextLine()
{
// Walk up to an enter sequence
2021-08-16 23:17:48 +08:00
while (mIndex<mOutput.length() && mOutput[mIndex]!='\r' && mOutput[mIndex]!='\n' && mOutput[mIndex]!=0)
2021-07-17 19:32:23 +08:00
mIndex++;
// End of output. Exit
if (mIndex>=mOutput.length())
return "";
// Skip ONE enter sequence (CRLF, CR, LF, etc.)
2021-08-16 23:17:48 +08:00
if ((mOutput[mIndex] == '\r') && (mOutput[mIndex] == '\n')) // DOS
2021-07-17 19:32:23 +08:00
mIndex+=2;
2021-08-16 23:17:48 +08:00
else if (mOutput[mIndex] == '\r') // UNIX
2021-07-17 19:32:23 +08:00
mIndex++;
2021-08-16 23:17:48 +08:00
else if (mOutput[mIndex] == '\n') // MAC
2021-07-17 19:32:23 +08:00
mIndex++;
// Return next line
return getRemainingLine();
}
QString DebugReader::getNextWord()
{
QString Result;
// Called when at a space? Skip over
skipSpaces();
// Skip until a space
while (mIndex<mOutput.length() && mOutput[mIndex]>32) {
Result += mOutput[mIndex];
mIndex++;
}
return Result;
}
QString DebugReader::getRemainingLine()
{
QString Result;
// Return part of line still ahead of us
2021-08-16 23:17:48 +08:00
while (mIndex<mOutput.length() && mOutput[mIndex]!='\r' && mOutput[mIndex]!='\n' && mOutput[mIndex]!=0) {
2021-07-17 19:32:23 +08:00
Result += mOutput[mIndex];
mIndex++;
}
return Result;
}
void DebugReader::handleDisassembly()
{
// Get info message
QString s = getNextLine();
// the full function name will be saved at index 0
mDisassembly.append(s.mid(36));
s = getNextLine();
// Add lines of disassembly
while (s != "End of assembler dump.") {
if(!s.isEmpty())
mDisassembly.append(s);
2021-07-17 19:32:23 +08:00
s = getNextLine();
}
dodisassemblerready = true;
}
void DebugReader::handleDisplay()
{
QString s = getNextLine(); // watch index
if (!findAnnotation(AnnotationType::TDisplayExpression))
return;
QString watchName = getNextLine(); // watch name
2021-08-01 01:06:43 +08:00
// Find watchVar we're talking about
PWatchVar watchVar = mDebugger->findWatchVar(watchName);
if (watchVar) {
2021-07-17 19:32:23 +08:00
// Advance up to the value
if (!findAnnotation(AnnotationType::TDisplayExpression))
return;;
// Refresh GDB index so we can undisplay this by index
watchVar->gdbIndex = s.toInt();
processWatchOutput(watchVar);
2021-08-01 01:06:43 +08:00
mDebugger->notifyWatchVarUpdated(watchVar);
2021-07-17 19:32:23 +08:00
}
}
void DebugReader::handleError()
{
QString s = getNextLine(); // error text
if (s.startsWith("Cannot find bounds of current function")) {
//We have exited
handleExit();
} else if (s.startsWith("No symbol \"")) {
int head = s.indexOf('\"');
int tail = s.lastIndexOf('\"');
QString watchName = s.mid(head+1, tail-head-1);
2021-08-01 01:06:43 +08:00
// Update current..
mDebugger->invalidateWatchVar(watchName);
2021-07-17 19:32:23 +08:00
}
}
void DebugReader::handleExit()
{
doprocessexited=true;
}
void DebugReader::handleFrames()
{
QString s = getNextLine();
// Is this a backtrace dump?
if (s.startsWith("#")) {
2021-08-01 01:06:43 +08:00
if (s.startsWith("#0")) {
mDebugger->backtraceModel()->clear();
}
2021-07-17 19:32:23 +08:00
// Find function name
if (!findAnnotation(AnnotationType::TFrameFunctionName))
return;
PTrace trace = std::make_shared<Trace>();
trace->funcname = getNextLine();
// Find argument list start
if (!findAnnotation(AnnotationType::TFrameArgs))
return;
// Arguments are either () or detailed list
s = getNextLine();
while (peekNextAnnotation() == AnnotationType::TArgBegin) {
// argument name
if (!findAnnotation(AnnotationType::TArgBegin))
return;
s = s + getNextLine();
// =
if (!findAnnotation(AnnotationType::TArgNameEnd))
return;
s = s + ' ' + getNextLine() + ' '; // should be =
// argument value
if (!findAnnotation(AnnotationType::TArgValue))
return;
s = s + getNextLine();
// argument end
if (!findAnnotation(AnnotationType::TArgEnd))
return;
s = s + getNextLine();
}
trace->funcname = trace->funcname + s.trimmed();
// source info
if (peekNextAnnotation() == AnnotationType::TFrameSourceBegin) {
// Find filename
if (!findAnnotation(AnnotationType::TFrameSourceFile))
return;
trace->filename = getNextLine();
// find line
if (!findAnnotation(AnnotationType::TFrameSourceLine))
return;
trace->line = getNextLine().trimmed().toInt();
} else {
trace->filename = "";
trace->line = 0;
}
2021-07-25 00:26:13 +08:00
mDebugger->backtraceModel()->addTrace(trace);
2021-07-17 19:32:23 +08:00
// Skip over the remaining frame part...
if (!findAnnotation(AnnotationType::TFrameEnd))
return;
// Not another one coming? Done!
if (peekNextAnnotation() != AnnotationType::TFrameBegin) {
// End of stack trace dump!
dobacktraceready = true;
}
} else
doupdatecpuwindow = true;
}
void DebugReader::handleLocalOutput()
{
// name(spaces)hexvalue(tab)decimalvalue
QString s = getNextFilledLine();
2021-08-01 10:00:27 +08:00
bool nobreakLine = false;
2021-07-17 19:32:23 +08:00
while (true) {
2021-08-01 10:00:27 +08:00
if (!s.startsWith("\032\032")) {
2021-07-17 19:32:23 +08:00
s = TrimLeft(s);
if (s == "No locals.") {
return;
}
if (s == "No arguments.") {
return;
}
//todo: update local view
2021-08-16 23:17:48 +08:00
if (nobreakLine && pMainWindow->txtLocals()->document()->lineCount()>0) {
2021-08-01 10:00:27 +08:00
emit addLocalWithoutLinebreak(s);
} else {
emit addLocalWithLinebreak(s);
}
nobreakLine=false;
2021-07-17 19:32:23 +08:00
} else {
2021-08-01 10:00:27 +08:00
nobreakLine = true;
2021-07-17 19:32:23 +08:00
}
s = getNextLine();
2021-08-01 10:00:27 +08:00
if (!nobreakLine && s.isEmpty())
2021-07-17 19:32:23 +08:00
break;
}
}
void DebugReader::handleLocals()
{
2021-08-01 10:00:27 +08:00
emit clearLocals();
2021-07-17 19:32:23 +08:00
handleLocalOutput();
}
void DebugReader::handleParams(){
handleLocalOutput();
}
void DebugReader::handleRegisters()
{
// name(spaces)hexvalue(tab)decimalvalue
QString s = getNextFilledLine();
while (true) {
PRegister reg = std::make_shared<Register>();
// Cut name from 1 to first space
int x = s.indexOf(' ');
2021-08-01 23:24:37 +08:00
reg->name = s.mid(0,x);
2021-07-17 19:32:23 +08:00
s.remove(0,x);
// Remove spaces
s = TrimLeft(s);
// Cut hex value from 1 to first tab
x = s.indexOf('\t');
if (x<0)
x = s.indexOf(' ');
2021-08-01 23:24:37 +08:00
reg->hexValue = s.mid(0,x);
2021-07-17 19:32:23 +08:00
s.remove(0,x); // delete tab too
s = TrimLeft(s);
// Remaining part contains decimal value
reg->decValue = s;
2021-08-01 23:24:37 +08:00
if (!reg->name.trimmed().isEmpty())
mRegisters.append(reg);
2021-07-17 19:32:23 +08:00
s = getNextLine();
if (s.isEmpty())
break;
}
2021-07-18 00:18:07 +08:00
doregistersready = true;
}
void DebugReader::handleSignal()
{
mSignal = getNextFilledLine(); // Program received signal
if (!findAnnotation(AnnotationType::TSignalName))
return;
mSignal = mSignal + getNextFilledLine(); // signal code
if (!findAnnotation(AnnotationType::TSignalNameEnd))
return;
mSignal = mSignal + getNextFilledLine(); // comma
if (!findAnnotation(AnnotationType::TSignalString))
return;
mSignal = mSignal + getNextFilledLine(); // user friendly description
if (!findAnnotation(AnnotationType::TSignalStringEnd))
return;
mSignal = mSignal + getNextFilledLine(); // period
doreceivedsignal = true;
}
void DebugReader::handleSource()
{
// source filename:line:offset:beg/middle/end:addr
QString s = TrimLeft(getRemainingLine());
// remove offset, beg/middle/end, address
for (int i=0;i<3;i++) {
int delimPos = s.lastIndexOf(':');
if (delimPos >= 0)
s.remove(delimPos,INT_MAX);
else
return; // Wrong format. Don't bother to continue
}
// get line
int delimPos = s.lastIndexOf(':');
if (delimPos >= 0) {
mBreakPointLine = s.mid(delimPos+1).toInt();
s.remove(delimPos, INT_MAX);
}
// get file
mBreakPointFile = s;
doupdateexecution = true;
doupdatecpuwindow = true;
}
void DebugReader::handleValueHistoryValue()
{
mEvalValue = processEvalOutput();
doevalready = true;
}
AnnotationType DebugReader::peekNextAnnotation()
{
int indexBackup = mIndex; // do NOT modifiy curpos
AnnotationType result = getNextAnnotation();
mIndex = indexBackup;
return result;
}
void DebugReader::processDebugOutput()
{
// Only update once per update at most
2021-07-18 08:52:53 +08:00
//WatchView.Items.BeginUpdate;
if (mInvalidateAllVars) {
//invalidate all vars when there's first output
2021-07-31 20:19:45 +08:00
mDebugger->deleteWatchVars(false);
2021-07-18 08:52:53 +08:00
mInvalidateAllVars = false;
}
emit parseStarted();
//try
dobacktraceready = false;
dodisassemblerready = false;
doregistersready = false;
doevalready = false;
doprocessexited = false;
doupdateexecution = false;
doreceivedsignal = false;
doupdatecpuwindow = false;
doreceivedsfwarning = false;
// Global checks
if (mOutput.indexOf("warning: Source file is more recent than executable.") >= 0)
doreceivedsfwarning = true;
mIndex = 0;
AnnotationType nextAnnotation;
do {
nextAnnotation = getNextAnnotation();
switch(nextAnnotation) {
case AnnotationType::TValueHistoryValue:
handleValueHistoryValue();
break;
case AnnotationType::TSignal:
handleSignal();
break;
case AnnotationType::TExit:
handleExit();
break;
case AnnotationType::TFrameBegin:
handleFrames();
break;
case AnnotationType::TInfoAsm:
handleDisassembly();
break;
case AnnotationType::TInfoReg:
handleRegisters();
break;
case AnnotationType::TLocal:
handleLocals();
break;
case AnnotationType::TParam:
handleParams();
break;
case AnnotationType::TErrorBegin:
handleError();
break;
case AnnotationType::TDisplayBegin:
handleDisplay();
break;
case AnnotationType::TSource:
handleSource();
break;
}
} while (nextAnnotation != AnnotationType::TEOF);
// Only update once per update at most
//finally
//WatchView.Items.EndUpdate;
//end;
emit parseFinished();
}
QString DebugReader::processEvalOutput()
{
int indent = 0;
// First line gets special treatment
QString result = getNextLine();
if (result.startsWith('{'))
indent+=4;
// Collect all data, add formatting in between
AnnotationType nextAnnotation;
QString nextLine;
bool shouldExit = false;
do {
nextAnnotation = getNextAnnotation();
nextLine = getNextLine();
switch(nextAnnotation) {
// Change indent if { or } is found
case AnnotationType::TFieldBegin:
result += "\r\n" + QString(4,' ');
break;
case AnnotationType::TFieldValue:
if (nextLine.startsWith('{') && (peekNextAnnotation() !=
AnnotationType::TArrayBegin))
indent+=4;
break;
case AnnotationType::TFieldEnd:
if (nextLine.endsWith('}')) {
indent-=4;
result += "\r\n" + QString(4,' ');
}
break;
case AnnotationType::TEOF:
case AnnotationType::TValueHistoryEnd:
case AnnotationType::TDisplayEnd:
shouldExit = true;
}
result += nextLine;
} while (!shouldExit);
2021-08-01 01:06:43 +08:00
return result;
2021-07-18 08:52:53 +08:00
}
2021-08-01 01:06:43 +08:00
void DebugReader::processWatchOutput(PWatchVar watchVar)
2021-07-18 08:52:53 +08:00
{
2021-08-01 01:06:43 +08:00
// // Expand if it was expanded or if it didn't have any children
// bool ParentWasExpanded = false;
// Do not remove root node of watch variable
watchVar->children.clear();
watchVar->text = "";
// Process output parsed by ProcessEvalStruct
QString s = watchVar->name + " = " + processEvalOutput();
// add placeholder name for variable name so we can format structs using one rule
// Add children based on indent
QStringList lines = TextToLines(s);
PWatchVar parentVar=watchVar;
for (const QString& line:lines) {
// Format node text. Remove trailing comma
QString nodeText = line.trimmed();
if (nodeText.endsWith(',')) {
nodeText.remove(nodeText.length()-1);
}
if (nodeText.endsWith('{')) { // new member struct
if (parentVar->text.isEmpty()) { // root node, replace text only
parentVar->text = nodeText;
} else {
PWatchVar newVar = std::shared_ptr<WatchVar>();
newVar->parent = parentVar.get();
newVar->name = "";
newVar->text = nodeText;
newVar->gdbIndex = -1;
parentVar->children.append(newVar);
parentVar = newVar;
}
} else if (nodeText.startsWith('}')) { // end of struct, change parent
if (parentVar->parent!=nullptr) {
parentVar = std::shared_ptr<WatchVar>(parentVar->parent);
}
} else { // next parent member/child
if (parentVar->text.isEmpty()) { // root node, replace text only
parentVar->text = nodeText;
} else {
PWatchVar newVar = std::shared_ptr<WatchVar>();
newVar->parent = parentVar.get();
newVar->name = "";
newVar->text = nodeText;
newVar->gdbIndex = -1;
parentVar->children.append(newVar);
}
}
}
// TODO: remember expansion state
2021-07-17 19:32:23 +08:00
}
2021-07-19 23:02:32 +08:00
void DebugReader::runNextCmd()
{
bool doUpdate=false;
auto action = finally([this,&doUpdate] {
if (doUpdate) {
emit updateWatch();
}
});
QMutexLocker locker(&mCmdQueueMutex);
if (mCmdQueue.isEmpty()) {
if ((mCurrentCmd) && (mCurrentCmd->updateWatch)) {
doUpdate=true;
if (mUpdateCount>0) {
mUpdateCount=0;
}
}
return;
}
if (mCurrentCmd) {
mCurrentCmd.reset();
}
PDebugCommand pCmd = mCmdQueue.dequeue();
mCmdRunning = true;
mCurrentCmd = pCmd;
QByteArray s;
s=pCmd->command.toLocal8Bit();
if (!pCmd->params.isEmpty()) {
s+=' '+pCmd->params.toLocal8Bit();
}
s+= "\n";
2021-07-27 00:14:24 +08:00
if (mProcess->write(s)<0) {
2021-07-19 23:02:32 +08:00
emit writeToDebugFailed();
}
// if devDebugger.ShowCommandLog or pCmd^.ShowInConsole then begin
2021-07-30 23:28:58 +08:00
if (pSettings->debugger().showCommandLog() || pCmd->showInConsole) {
2021-07-19 23:02:32 +08:00
//update debug console
// if not devDebugger.ShowAnnotations then begin
2021-07-30 23:28:58 +08:00
if (!pSettings->debugger().showAnnotations()) {
2021-07-19 23:02:32 +08:00
// if MainForm.DebugOutput.Lines.Count>0 then begin
// MainForm.DebugOutput.Lines.Delete(MainForm.DebugOutput.Lines.Count-1);
// end;
2021-07-30 23:28:58 +08:00
emit changeDebugConsoleLastLine("(gdb)"+pCmd->command + ' ' + pCmd->params);
2021-07-19 23:02:32 +08:00
// MainForm.DebugOutput.Lines.Add('(gdb)'+pCmd^.Cmd + ' ' + pCmd^.params);
// MainForm.DebugOutput.Lines.Add('');
} else {
2021-07-30 23:28:58 +08:00
emit changeDebugConsoleLastLine("(gdb)"+pCmd->command + ' ' + pCmd->params);
2021-07-19 23:02:32 +08:00
// MainForm.DebugOutput.Lines.Add(pCmd^.Cmd + ' ' + pCmd^.params);
// MainForm.DebugOutput.Lines.Add('');
}
}
}
void DebugReader::skipSpaces()
{
while (mIndex < mOutput.length() &&
(mOutput[mIndex]=='\t' || mOutput[mIndex]==' '))
mIndex++;
}
void DebugReader::skipToAnnotation()
{
// Walk up to the next annotation
while (mIndex < mOutput.length() &&
(mOutput[mIndex]!=26))
mIndex++;
// Crawl through the remaining ->'s
while (mIndex < mOutput.length() &&
(mOutput[mIndex]==26))
mIndex++;
}
2021-07-31 20:19:45 +08:00
bool DebugReader::invalidateAllVars() const
{
return mInvalidateAllVars;
}
void DebugReader::setInvalidateAllVars(bool invalidateAllVars)
{
mInvalidateAllVars = invalidateAllVars;
}
2021-07-25 00:26:13 +08:00
QString DebugReader::debuggerPath() const
{
return mDebuggerPath;
}
void DebugReader::setDebuggerPath(const QString &debuggerPath)
{
mDebuggerPath = debuggerPath;
}
void DebugReader::stopDebug()
{
mStop = true;
}
2021-08-29 22:08:43 +08:00
bool DebugReader::commandRunning()
{
return !mCmdQueue.isEmpty();
}
2021-07-26 18:22:09 +08:00
2021-07-19 23:02:32 +08:00
void DebugReader::run()
{
mStop = false;
bool errorOccurred = false;
2021-07-25 00:26:13 +08:00
QString cmd = mDebuggerPath;
QString arguments = "--annotate=2 --silent";
QString workingDir = QFileInfo(mDebuggerPath).path();
2021-07-27 00:14:24 +08:00
mProcess = new QProcess();
mProcess->setProgram(cmd);
mProcess->setArguments(QProcess::splitCommand(arguments));
mProcess->setWorkingDirectory(workingDir);
2021-07-19 23:02:32 +08:00
2021-07-27 00:14:24 +08:00
connect(mProcess, &QProcess::errorOccurred,
2021-07-19 23:02:32 +08:00
[&](){
errorOccurred= true;
});
// mProcess.connect(&process, &QProcess::readyReadStandardError,[&process,this](){
// this->error(QString::fromLocal8Bit( process.readAllStandardError()));
// });
// mProcess.connect(&mProcess, &QProcess::readyReadStandardOutput,[&process,this](){
// this->log(QString::fromLocal8Bit( process.readAllStandardOutput()));
// });
// process.connect(&mProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),[&process,this](){
// this->error(COMPILE_PROCESS_END);
// });
2021-07-27 00:14:24 +08:00
mProcess->start();
mProcess->waitForStarted(5000);
mStartSemaphore.release(1);
2021-07-19 23:02:32 +08:00
QByteArray buffer;
2021-07-30 23:28:58 +08:00
QByteArray readed;
2021-07-19 23:02:32 +08:00
while (true) {
2021-08-01 01:06:43 +08:00
mProcess->waitForFinished(1);
2021-07-27 00:14:24 +08:00
if (mProcess->state()!=QProcess::Running) {
2021-07-19 23:02:32 +08:00
break;
}
if (mStop) {
2021-08-01 23:24:37 +08:00
mProcess->closeReadChannel(QProcess::StandardOutput);
mProcess->closeReadChannel(QProcess::StandardError);
mProcess->closeWriteChannel();
2021-07-27 00:14:24 +08:00
mProcess->terminate();
2021-08-01 23:24:37 +08:00
mProcess->kill();
2021-07-31 14:04:43 +08:00
break;
2021-07-19 23:02:32 +08:00
}
if (errorOccurred)
break;
2021-07-30 23:28:58 +08:00
readed = mProcess->readAll();
buffer += readed;
2021-07-19 23:02:32 +08:00
if (getLastAnnotation(buffer) == AnnotationType::TPrompt) {
mOutput = buffer;
processDebugOutput();
2021-07-30 23:28:58 +08:00
buffer.clear();
2021-07-19 23:02:32 +08:00
mCmdRunning = false;
runNextCmd();
2021-07-30 23:28:58 +08:00
} else if (!mCmdRunning && readed.isEmpty()){
runNextCmd();
2021-08-01 01:06:43 +08:00
} else if (readed.isEmpty()){
msleep(100);
2021-07-19 23:02:32 +08:00
}
}
if (errorOccurred) {
2021-07-27 00:14:24 +08:00
emit processError(mProcess->error());
2021-07-19 23:02:32 +08:00
}
}
2021-07-17 19:32:23 +08:00
2021-07-24 11:18:25 +08:00
BreakpointModel::BreakpointModel(QObject *parent):QAbstractTableModel(parent)
{
}
2021-07-17 19:32:23 +08:00
int BreakpointModel::rowCount(const QModelIndex &) const
{
return mList.size();
}
int BreakpointModel::columnCount(const QModelIndex &) const
{
return 3;
}
QVariant BreakpointModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row()<0 || index.row() >= static_cast<int>(mList.size()))
return QVariant();
PBreakpoint breakpoint = mList[index.row()];
if (!breakpoint)
return QVariant();
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case 0:
return breakpoint->filename;
case 1:
if (breakpoint->line>0)
return breakpoint->line;
else
return "";
case 2:
return breakpoint->condition;
default:
return QVariant();
}
default:
return QVariant();
}
}
QVariant BreakpointModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch(section) {
case 0:
return tr("Filename");
case 1:
return tr("Line");
case 2:
return tr("Condition");
}
}
return QVariant();
}
void BreakpointModel::addBreakpoint(PBreakpoint p)
{
beginInsertRows(QModelIndex(),mList.size(),mList.size());
mList.push_back(p);
endInsertRows();
}
void BreakpointModel::clear()
{
beginRemoveRows(QModelIndex(),0,mList.size()-1);
mList.clear();
endRemoveRows();
}
void BreakpointModel::removeBreakpoint(int row)
{
beginRemoveRows(QModelIndex(),row,row);
mList.removeAt(row);
endRemoveRows();
}
2021-07-24 11:18:25 +08:00
PBreakpoint BreakpointModel::setBreakPointCondition(int index, const QString &condition)
{
PBreakpoint breakpoint = mList[index];
breakpoint->condition = condition;
2021-07-31 20:19:45 +08:00
emit dataChanged(createIndex(index,0),createIndex(index,2));
2021-07-24 11:18:25 +08:00
return breakpoint;
}
const QList<PBreakpoint> &BreakpointModel::breakpoints() const
{
return mList;
}
BacktraceModel::BacktraceModel(QObject *parent):QAbstractTableModel(parent)
{
}
2021-07-17 19:32:23 +08:00
int BacktraceModel::rowCount(const QModelIndex &) const
{
return mList.size();
}
int BacktraceModel::columnCount(const QModelIndex &) const
{
return 3;
}
QVariant BacktraceModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row()<0 || index.row() >= static_cast<int>(mList.size()))
return QVariant();
PTrace trace = mList[index.row()];
if (!trace)
return QVariant();
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case 0:
return trace->funcname;
case 1:
return trace->filename;
case 2:
if (trace->line>0)
return trace->line;
else
return "";
default:
return QVariant();
}
default:
return QVariant();
}
}
QVariant BacktraceModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch(section) {
case 0:
return tr("Function");
case 1:
return tr("Filename");
case 2:
return tr("Line");
}
}
return QVariant();
}
void BacktraceModel::addTrace(PTrace p)
{
beginInsertRows(QModelIndex(),mList.size(),mList.size());
mList.push_back(p);
endInsertRows();
}
void BacktraceModel::clear()
{
beginRemoveRows(QModelIndex(),0,mList.size()-1);
mList.clear();
endRemoveRows();
}
void BacktraceModel::removeTrace(int row)
{
beginRemoveRows(QModelIndex(),row,row);
mList.removeAt(row);
endRemoveRows();
}
2021-07-24 11:18:25 +08:00
const QList<PTrace> &BacktraceModel::backtraces() const
{
return mList;
}
2021-07-31 14:04:43 +08:00
2021-07-31 20:19:45 +08:00
WatchModel::WatchModel(QObject *parent):QAbstractItemModel(parent)
{
}
QVariant WatchModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
WatchVar* item = static_cast<WatchVar*>(index.internalPointer());
switch (role) {
case Qt::DisplayRole:
2021-08-01 01:06:43 +08:00
qDebug()<<"item->text:"<<item->text;
2021-07-31 20:19:45 +08:00
return item->text;
}
return QVariant();
}
2021-07-31 14:04:43 +08:00
QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row,column,parent))
return QModelIndex();
WatchVar* parentItem;
PWatchVar pChild;
if (!parent.isValid()) {
parentItem = nullptr;
pChild = mWatchVars[row];
} else {
parentItem = static_cast<WatchVar*>(parent.internalPointer());
pChild = parentItem->children[row];
}
if (pChild)
return createIndex(row,column,pChild.get());
return QModelIndex();
}
static int getWatchIndex(WatchVar* var, const QList<PWatchVar> list) {
for (int i=0;i<list.size();i++) {
PWatchVar v = list[i];
if (v.get() == var) {
return i;
}
}
2021-08-27 23:51:42 +08:00
return -1;
2021-07-31 14:04:43 +08:00
}
QModelIndex WatchModel::parent(const QModelIndex &index) const
{
if (!index.isValid()) {
return QModelIndex();
}
WatchVar* childItem = static_cast<WatchVar*>(index.internalPointer());
2021-08-01 01:06:43 +08:00
WatchVar* parentItem = childItem->parent;
2021-07-31 14:04:43 +08:00
//parent is root
if (parentItem == nullptr) {
return QModelIndex();
}
int row;
WatchVar* grandItem = parentItem->parent;
if (grandItem == nullptr) {
row = getWatchIndex(parentItem,mWatchVars);
} else {
row = getWatchIndex(parentItem,grandItem->children);
}
return createIndex(row,0,parentItem);
}
int WatchModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return mWatchVars.count();
} else {
WatchVar* parentItem = static_cast<WatchVar*>(parent.internalPointer());
return parentItem->children.count();
}
}
2021-07-31 20:19:45 +08:00
int WatchModel::columnCount(const QModelIndex&) const
2021-07-31 14:04:43 +08:00
{
return 1;
}
void WatchModel::addWatchVar(PWatchVar watchVar)
{
for (PWatchVar var:mWatchVars) {
if (watchVar->name == var->name) {
return;
}
}
2021-07-31 20:19:45 +08:00
this->beginInsertRows(QModelIndex(),mWatchVars.size(),mWatchVars.size());
2021-07-31 14:04:43 +08:00
mWatchVars.append(watchVar);
2021-07-31 20:19:45 +08:00
this->endInsertRows();
2021-07-31 14:04:43 +08:00
}
void WatchModel::removeWatchVar(const QString &name)
{
2021-07-31 20:19:45 +08:00
for (int i=0;i<mWatchVars.size();i++) {
PWatchVar var = mWatchVars[i];
2021-07-31 14:04:43 +08:00
if (name == var->name) {
2021-07-31 20:19:45 +08:00
this->beginRemoveRows(QModelIndex(),i,i);
2021-07-31 14:04:43 +08:00
mWatchVars.removeOne(var);
2021-07-31 20:19:45 +08:00
this->endRemoveRows();
2021-07-31 14:04:43 +08:00
}
}
}
void WatchModel::removeWatchVar(int gdbIndex)
{
2021-07-31 20:19:45 +08:00
for (int i=0;i<mWatchVars.size();i++) {
PWatchVar var = mWatchVars[i];
2021-07-31 14:04:43 +08:00
if (gdbIndex == var->gdbIndex) {
2021-07-31 20:19:45 +08:00
this->beginRemoveRows(QModelIndex(),i,i);
2021-07-31 14:04:43 +08:00
mWatchVars.removeOne(var);
2021-07-31 20:19:45 +08:00
this->endRemoveRows();
2021-07-31 14:04:43 +08:00
}
}
}
void WatchModel::clear()
{
2021-07-31 20:19:45 +08:00
this->beginResetModel();
2021-07-31 14:04:43 +08:00
mWatchVars.clear();
2021-07-31 20:19:45 +08:00
this->endResetModel();
2021-07-31 14:04:43 +08:00
}
2021-07-31 20:19:45 +08:00
const QList<PWatchVar> &WatchModel::watchVars()
2021-07-31 14:04:43 +08:00
{
return mWatchVars;
}
PWatchVar WatchModel::findWatchVar(const QString &name)
{
for (PWatchVar var:mWatchVars) {
if (name == var->name) {
return var;
}
}
2021-07-31 20:19:45 +08:00
return PWatchVar();
2021-07-31 14:04:43 +08:00
}
PWatchVar WatchModel::findWatchVar(int gdbIndex)
{
for (PWatchVar var:mWatchVars) {
if (gdbIndex == var->gdbIndex) {
return var;
}
}
2021-07-31 20:19:45 +08:00
return PWatchVar();
}
void WatchModel::notifyUpdated(PWatchVar var)
{
if (!var)
return;
int row;
if (var->parent==nullptr) {
row = mWatchVars.indexOf(var);
} else {
row = var->parent->children.indexOf(var);
}
if (row<0)
return;
2021-08-01 01:06:43 +08:00
qDebug()<<"dataChanged"<<row<<":"<<var->text;
2021-07-31 20:19:45 +08:00
emit dataChanged(createIndex(row,0,var.get()),createIndex(row,0,var.get()));
2021-07-31 14:04:43 +08:00
}
2021-08-01 23:24:37 +08:00
RegisterModel::RegisterModel(QObject *parent):QAbstractTableModel(parent)
{
}
int RegisterModel::rowCount(const QModelIndex &parent) const
{
return mRegisters.count();
}
int RegisterModel::columnCount(const QModelIndex &parent) const
{
return 3;
}
QVariant RegisterModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row()<0 || index.row() >= static_cast<int>(mRegisters.size()))
return QVariant();
PRegister reg = mRegisters[index.row()];
if (!reg)
return QVariant();
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case 0:
return reg->name;
case 1:
return reg->hexValue;
case 2:
return reg->decValue;
default:
return QVariant();
}
default:
return QVariant();
}
}
QVariant RegisterModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch(section) {
case 0:
return tr("Register");
case 1:
return tr("Value(Hex)");
case 2:
return tr("Value(Dec)");
}
}
return QVariant();
}
void RegisterModel::update(const QList<PRegister> &regs)
{
beginResetModel();
mRegisters.clear();
mRegisters.append(regs);
endResetModel();
}
void RegisterModel::clear()
{
beginResetModel();
mRegisters.clear();
endResetModel();
}