diff --git a/RedPandaIDE/debugger/debugger.cpp b/RedPandaIDE/debugger/debugger.cpp
index d6342d62..044b6add 100644
--- a/RedPandaIDE/debugger/debugger.cpp
+++ b/RedPandaIDE/debugger/debugger.cpp
@@ -15,6 +15,7 @@
* along with this program. If not, see .
*/
#include "debugger.h"
+#include "gdbmidebugger.h"
#include "utils.h"
#include "utils/parsearg.h"
#include "mainwindow.h"
@@ -158,7 +159,7 @@ bool Debugger::start(int compilerSetIndex, const QString& inferior, const QStrin
mTarget->waitStart();
}
//delete when thread finished
- mClient = new DebuggerClient(this);
+ mClient = new GDBMIDebuggerClient(this);
mClient->addBinDirs(binDirs);
mClient->addBinDir(pSettings->dirs().appDir());
mClient->setDebuggerPath(debuggerPath);
@@ -999,9 +1000,7 @@ DebuggerClient::DebuggerClient(Debugger* debugger, QObject *parent) : QThread(pa
mStartSemaphore(0)
{
mDebugger = debugger;
- mProcess = std::make_shared();
mCmdRunning = false;
- mAsyncUpdated = false;
}
void DebuggerClient::clearCmdQueue()
@@ -1010,358 +1009,6 @@ void DebuggerClient::clearCmdQueue()
mCmdQueue.clear();
}
-void DebuggerClient::processConsoleOutput(const QByteArray& line)
-{
- if (line.length()>3 && line.startsWith("~\"") && line.endsWith("\"")) {
- QByteArray s=line.mid(2,line.length()-3);
- QByteArray stringValue;
- const char *p=s.data();
- while (*p!=0) {
- if (*p=='\\' && *(p+1)!=0) {
- p++;
- switch (*p) {
- case '\'':
- stringValue+=0x27;
- p++;
- break;
- case '"':
- stringValue+=0x22;
- p++;
- break;
- case '?':
- stringValue+=0x3f;
- p++;
- break;
- case '\\':
- stringValue+=0x5c;
- p++;
- break;
- case 'a':
- stringValue+=0x07;
- p++;
- break;
- case 'b':
- stringValue+=0x08;
- p++;
- break;
- case 'f':
- stringValue+=0x0c;
- p++;
- break;
- case 'n':
- stringValue+=0x0a;
- p++;
- break;
- case 'r':
- stringValue+=0x0d;
- p++;
- break;
- case 't':
- stringValue+=0x09;
- p++;
- break;
- case 'v':
- stringValue+=0x0b;
- p++;
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- {
- int i=0;
- for (i=0;i<3;i++) {
- if (*(p+i)<'0' || *(p+i)>'7')
- break;
- }
- QByteArray numStr(p,i);
- bool ok;
- unsigned char ch = numStr.toInt(&ok,8);
- stringValue+=ch;
- p+=i;
- break;
- }
- }
- } else {
- stringValue+=*p;
- p++;
- }
- }
- //mConsoleOutput.append(QString::fromLocal8Bit(stringValue));
- mConsoleOutput.append(QString::fromUtf8(stringValue));
- }
-}
-
-void DebuggerClient::processLogOutput(const QByteArray &line)
-{
- if (mDebugger->debugInfosUsingUTF8() && line.endsWith(": No such file or directory.\n\"")) {
- QByteArray newLine = line;
- newLine[0]='~';
- int p=newLine.lastIndexOf(':');
- if (p>0) {
- newLine=newLine.left(p);
- //qDebug()<command, resultType,multiValues);
- if (!parseOk)
- return;
- switch(resultType) {
- case GDBMIResultType::BreakpointTable:
- case GDBMIResultType::Locals:
- break;
- case GDBMIResultType::Breakpoint:
- handleBreakpoint(multiValues["bkpt"].object());
- return;
- case GDBMIResultType::Frame:
- handleFrame(multiValues["frame"]);
- return;
- case GDBMIResultType::FrameStack:
- handleStack(multiValues["stack"].array());
- return;
- case GDBMIResultType::LocalVariables:
- handleLocalVariables(multiValues["variables"].array());
- return;
- case GDBMIResultType::Evaluation:
- handleEvaluation(multiValues["value"].value());
- return;
- case GDBMIResultType::Memory:
- handleMemory(multiValues["memory"].array());
- return;
- case GDBMIResultType::RegisterNames:
- handleRegisterNames(multiValues["register-names"].array());
- return;
- case GDBMIResultType::RegisterValues:
- handleRegisterValue(multiValues["register-values"].array());
- return;
- case GDBMIResultType::CreateVar:
- handleCreateVar(multiValues);
- return;
- case GDBMIResultType::ListVarChildren:
- handleListVarChildren(multiValues);
- return;
- case GDBMIResultType::UpdateVarValue:
- handleUpdateVarValue(multiValues["changelist"].array());
- return;
- default:
- return;
- }
-
-}
-
-void DebuggerClient::processExecAsyncRecord(const QByteArray &line)
-{
- QByteArray result;
- GDBMIResultParser::ParseObject multiValues;
- GDBMIResultParser parser;
- if (!parser.parseAsyncResult(line,result,multiValues))
- return;
- if (result == "running") {
- mInferiorRunning = true;
- mCurrentAddress=0;
- mCurrentFile.clear();
- mCurrentLine=-1;
- mCurrentFunc.clear();
- emit inferiorContinued();
- return;
- }
- if (result == "stopped") {
- mInferiorRunning = false;
- QByteArray reason = multiValues["reason"].value();
- if (reason == "exited") {
- //inferior exited, gdb should terminate too
- mProcessExited = true;
- return;
- }
- if (reason == "exited-normally") {
- //inferior exited, gdb should terminate too
- mProcessExited = true;
- return;
- }
- if (reason == "exited-signalled") {
- //inferior exited, gdb should terminate too
- mProcessExited = true;
- mSignalReceived = true;
- return;
- }
- mUpdateCPUInfo = true;
- handleFrame(multiValues["frame"]);
- if (reason == "signal-received") {
- mSignalReceived = true;
- mSignalName = multiValues["signal-name"].value();
- mSignalMeaning = multiValues["signal-meaning"].value();
- } else if (reason == "watchpoint-trigger") {
- QString var,oldVal,newVal;
- GDBMIResultParser::ParseValue wpt=multiValues["wpt"];
- if (wpt.isValid()) {
- GDBMIResultParser::ParseObject wptObj = wpt.object();
- var=wptObj["exp"].value();
- }
- GDBMIResultParser::ParseValue varValue=multiValues["value"];
- if (varValue.isValid()) {
- GDBMIResultParser::ParseObject valueObj = varValue.object();
- oldVal=valueObj["old"].value();
- newVal=valueObj["new"].value();
- }
- if (!var.isEmpty()) {
- emit watchpointHitted(var,oldVal,newVal);
- }
- }
- runInferiorStoppedHook();
- if (reason.isEmpty()) {
- QMutexLocker locker(&mCmdQueueMutex);
- foreach (const PDebugCommand& cmd, mCmdQueue) {
- //gdb-server connected, just ignore it
- if (cmd->command=="-exec-continue")
- return;
- }
- }
- if (mCurrentCmd && mCurrentCmd->source == DebugCommandSource::Console)
- emit inferiorStopped(mCurrentFile, mCurrentLine, false);
- else
- emit inferiorStopped(mCurrentFile, mCurrentLine, true);
- }
-}
-
-void DebuggerClient::processError(const QByteArray &errorLine)
-{
- QString s = QString::fromLocal8Bit(errorLine);
- mConsoleOutput.append(s);
- int idx=s.indexOf(",msg=\"No symbol table is loaded");
- if (idx>0) {
- emit errorNoSymbolTable();
- return;
- }
-}
-static QRegularExpression reGdbSourceLine("^(\\d)+\\s+in\\s+(.+)$");
-
-void DebuggerClient::processResultRecord(const QByteArray &line)
-{
- if (line.startsWith("^exit")) {
- mProcessExited = true;
- return;
- }
- if (line.startsWith("^error")) {
- processError(line);
- return;
- }
- if (line.startsWith("^done")
- || line.startsWith("^running")) {
- int pos = line.indexOf(',');
- if (pos>=0) {
- QByteArray result = line.mid(pos+1);
- processResult(result);
- } else if (mCurrentCmd && !(mCurrentCmd->command.startsWith('-'))) {
- if (mCurrentCmd->command == "disas") {
- QStringList disOutput = mConsoleOutput;
- if (disOutput.length()>=3) {
- disOutput.pop_back();
- disOutput.pop_front();
- disOutput.pop_front();
- }
- if (mDebugger->debugInfosUsingUTF8()) {
- QStringList newOutput;
- foreach(const QString& s, disOutput) {
- QString line = s;
- if (!s.isEmpty() && s.front().isDigit()) {
- QRegularExpressionMatch match = reGdbSourceLine.match(s);
-// qDebug()<editorList()->getContentFromOpenedEditor(filename,contents))
- contents = readFileToLines(filename);
- mFileCache[filename]=contents;
- }
- if (lineno>=0 && lineno lines = splitByteArrayToLines(debugOutput);
-
- for (int i=0;idebugger().showDetailLog())
- mFullOutput.append(line);
- line = removeToken(line);
- if (line.isEmpty()) {
- continue;
- }
- switch (line[0]) {
- case '~': // console stream output
- processConsoleOutput(line);
- break;
- case '@': // target stream output
- break;
- case '&': // log stream output
- processLogOutput(line);
- break;
- case '^': // result record
- processResultRecord(line);
- break;
- case '*': // exec async output
- processExecAsyncRecord(line);
- break;
- case '+': // status async output
- case '=': // notify async output
- break;
- }
- }
- emit parseFinished();
- mConsoleOutput.clear();
- mFullOutput.clear();
-}
-
void DebuggerClient::runInferiorStoppedHook()
{
QMutexLocker locker(&mCmdQueueMutex);
@@ -1370,376 +1017,8 @@ void DebuggerClient::runInferiorStoppedHook()
}
}
-void DebuggerClient::runNextCmd()
-{
- QMutexLocker locker(&mCmdQueueMutex);
- if (mCurrentCmd) {
- DebugCommandSource commandSource = mCurrentCmd->source;
- mCurrentCmd=nullptr;
- if (commandSource!=DebugCommandSource::HeartBeat)
- emit cmdFinished();
- }
- if (mCmdQueue.isEmpty()) {
- if (mDebugger->useDebugServer() && mInferiorRunning && !mAsyncUpdated) {
- mAsyncUpdated = true;
- QTimer::singleShot(50,this,&DebuggerClient::asyncUpdate);
- }
- return;
- }
- PDebugCommand pCmd = mCmdQueue.dequeue();
- mCmdRunning = true;
- mCurrentCmd = pCmd;
- if (pCmd->source!=DebugCommandSource::HeartBeat)
- emit cmdStarted();
-
- QByteArray s;
- QByteArray params;
- s=pCmd->command.toLocal8Bit();
- if (!pCmd->params.isEmpty()) {
- params = pCmd->params.toLocal8Bit();
- }
-
- //clang compatibility
- if (mDebugger->forceUTF8()) {
- params = pCmd->params.toUtf8();
- } else if (mDebugger->debugInfosUsingUTF8() &&
- (pCmd->command=="-break-insert"
- || pCmd->command=="-var-create"
- || pCmd->command=="-data-read-memory"
- || pCmd->command=="-data-evaluate-expression"
- )) {
- params = pCmd->params.toUtf8();
- }
- if (pCmd->command == "-var-create") {
- //hack for variable creation,to easy remember var expression
- if (mDebugger->debuggerType()==DebuggerType::LLDB_MI)
- params = " - * "+params;
- else
- params = " - @ "+params;
- } else if (pCmd->command == "-var-list-children") {
- //hack for list variable children,to easy remember var expression
- params = " --all-values \"" + params+'\"';
- }
- s+=" "+params;
- s+= "\n";
- if (mProcess->write(s)<0) {
- emit writeToDebugFailed();
- }
-
- if (pSettings->debugger().enableDebugConsole() ) {
- //update debug console
- if (pSettings->debugger().showDetailLog()
- && pCmd->source != DebugCommandSource::Console) {
- emit changeDebugConsoleLastLine(pCmd->command + ' ' + params);
- }
- }
-}
-
-QStringList DebuggerClient::tokenize(const QString &s)
-{
- QStringList result;
- int tStart,tEnd;
- int i=0;
- while (i') {
- i++;
- break;
- }
- i++;
- }
- tEnd = std::min(i,s.length());
- result.append(s.mid(tStart,tEnd-tStart));
- } else if (ch == '(') {
- tStart = i;
- i++;
- while (iforceUTF8() || mDebugger->debugInfosUsingUTF8())
- filename = breakpoint["fullname"].utf8PathValue();
- else
- filename = breakpoint["fullname"].pathValue();
- int line = breakpoint["line"].intValue();
- int number = breakpoint["number"].intValue();
- emit breakpointInfoGetted(filename, line , number);
-}
-
-void DebuggerClient::handleFrame(const GDBMIResultParser::ParseValue &frame)
-{
- if (frame.isValid()) {
- GDBMIResultParser::ParseObject frameObj = frame.object();
- bool ok;
- mCurrentAddress = frameObj["addr"].hexValue(ok);
- if (!ok)
- mCurrentAddress=0;
- mCurrentLine = frameObj["line"].intValue();
- if (mDebugger->forceUTF8()
- || mDebugger->debugInfosUsingUTF8())
- mCurrentFile = frameObj["fullname"].utf8PathValue();
- else
- mCurrentFile = frameObj["fullname"].pathValue();
- mCurrentFunc = frameObj["func"].value();
- }
-}
-
-void DebuggerClient::handleStack(const QList & stack)
-{
- mDebugger->backtraceModel()->clear();
- foreach (const GDBMIResultParser::ParseValue& frameValue, stack) {
- GDBMIResultParser::ParseObject frameObject = frameValue.object();
- PTrace trace = std::make_shared();
- trace->funcname = frameObject["func"].value();
- if (mDebugger->forceUTF8() || mDebugger->debugInfosUsingUTF8())
- trace->filename = frameObject["fullname"].utf8PathValue();
- else
- trace->filename = frameObject["fullname"].pathValue();
- trace->line = frameObject["line"].intValue();
- trace->level = frameObject["level"].intValue(0);
- trace->address = frameObject["addr"].value();
- mDebugger->backtraceModel()->addTrace(trace);
- }
-}
-
-void DebuggerClient::handleLocalVariables(const QList &variables)
-{
- QStringList locals;
- foreach (const GDBMIResultParser::ParseValue& varValue, variables) {
- GDBMIResultParser::ParseObject varObject = varValue.object();
- QString name = QString(varObject["name"].value());
- QString value = QString(varObject["value"].value());
- locals.append(
- QString("%1 = %2")
- .arg(
- name,
- value
- ));
- }
- emit localsUpdated(locals);
-}
-
-void DebuggerClient::handleEvaluation(const QString &value)
-{
- emit evalUpdated(value);
-}
-
-void DebuggerClient::handleMemory(const QList &rows)
-{
- QStringList memory;
- foreach (const GDBMIResultParser::ParseValue& row, rows) {
- GDBMIResultParser::ParseObject rowObject = row.object();
- QList data = rowObject["data"].array();
- QStringList values;
- foreach (const GDBMIResultParser::ParseValue& val, data) {
- values.append(val.value());
- }
- memory.append(QString("%1 %2")
- .arg(rowObject["addr"].value(),values.join(" ")));
- }
- emit memoryUpdated(memory);
-}
-
-void DebuggerClient::handleRegisterNames(const QList &names)
-{
- QStringList nameList;
- foreach (const GDBMIResultParser::ParseValue& nameValue, names) {
-// QString text = nameValue.value().trimmed();
-// if (!text.isEmpty())
- nameList.append(nameValue.value());
- }
- emit registerNamesUpdated(nameList);
-}
-
-void DebuggerClient::handleRegisterValue(const QList &values)
-{
- QHash result;
- foreach (const GDBMIResultParser::ParseValue& val, values) {
- GDBMIResultParser::ParseObject obj = val.object();
- int number = obj["number"].intValue();
- QString value = obj["value"].value();
- bool ok;
- long long intVal;
- intVal = value.toLongLong(&ok,10);
- if (ok) {
- value = QString("0x%1").arg(intVal,0,16);
- }
- result.insert(number,value);
- }
- emit registerValuesUpdated(result);
-}
-
-void DebuggerClient::handleCreateVar(const GDBMIResultParser::ParseObject &multiVars)
-{
- if (!mCurrentCmd)
- return;
- QString expression = mCurrentCmd->params;
- QString name = multiVars["name"].value();
- int numChild = multiVars["numchild"].intValue(0);
- QString value = multiVars["value"].value();
- QString type = multiVars["type"].value();
- bool hasMore = multiVars["has_more"].value() != "0";
- emit varCreated(expression,name,numChild,value,type,hasMore);
-}
-
-void DebuggerClient::handleListVarChildren(const GDBMIResultParser::ParseObject &multiVars)
-{
- if (!mCurrentCmd)
- return;
- QString parentName = mCurrentCmd->params;
- int parentNumChild = multiVars["numchild"].intValue(0);
- QList children = multiVars["children"].array();
- bool hasMore = multiVars["has_more"].value()!="0";
- emit prepareVarChildren(parentName,parentNumChild,hasMore);
- foreach(const GDBMIResultParser::ParseValue& child, children) {
- GDBMIResultParser::ParseObject childObj = child.object();
- QString name = childObj["name"].value();
- QString exp = childObj["exp"].value();
- int numChild = childObj["numchild"].intValue(0);
- QString value = childObj["value"].value();
- QString type = childObj["type"].value();
- bool hasMore = childObj["has_more"].value() != "0";
- emit addVarChild(parentName,
- name,
- exp,
- numChild,
- value,
- type,
- hasMore);
- }
-}
-
-void DebuggerClient::handleUpdateVarValue(const QList &changes)
-{
- foreach (const GDBMIResultParser::ParseValue& value, changes) {
- GDBMIResultParser::ParseObject obj = value.object();
- QString name = obj["name"].value();
- QString val = obj["value"].value();
- QString inScope = obj["in_scope"].value();
- bool typeChanged = (obj["type_changed"].value()=="true");
- QString newType = obj["new_type"].value();
- int newNumChildren = obj["new_num_children"].intValue(-1);
- bool hasMore = (obj["has_more"].value() == "1");
- emit varValueUpdated(name,val,inScope,typeChanged,newType,newNumChildren,
- hasMore);
- }
- //todo: -var-list-children will freeze if the var is not correctly initialized
- //emit varsValueUpdated();
-}
-
-QByteArray DebuggerClient::removeToken(const QByteArray &line)
-{
- int p=0;
- while (p'9') {
- break;
- }
- p++;
- }
- if (perror());
+ emit processFailed(mProcess->error());
}
}
diff --git a/RedPandaIDE/debugger/debugger.h b/RedPandaIDE/debugger/debugger.h
index 1ef40299..5562212e 100644
--- a/RedPandaIDE/debugger/debugger.h
+++ b/RedPandaIDE/debugger/debugger.h
@@ -41,7 +41,8 @@ enum class DebugCommandSource {
enum class DebuggerType {
GDB,
- LLDB_MI
+ LLDB_MI,
+ DAP
};
struct DebugCommand{
@@ -444,7 +445,7 @@ public:
void addBinDirs(const QStringList &binDirs);
void addBinDir(const QString &binDir);
signals:
- void processError(QProcess::ProcessError error);
+ void processFailed(QProcess::ProcessError error);
private:
QString mInferior;
QStringList mArguments;
@@ -499,12 +500,13 @@ public:
void addBinDirs(const QStringList &binDirs);
void addBinDir(const QString &binDir);
+ Debugger* debugger() { return mDebugger; }
signals:
void parseStarted();
void invalidateAllVars();
void parseFinished();
void writeToDebugFailed();
- void processError(QProcess::ProcessError error);
+ void processFailed(QProcess::ProcessError error);
void changeDebugConsoleLastLine(const QString& text);
void cmdStarted();
void cmdFinished();
@@ -536,36 +538,10 @@ signals:
const QString& newType, int newNumChildren,
bool hasMore);
void varsValueUpdated();
-
-private:
+protected:
+ virtual void runNextCmd() = 0;
void clearCmdQueue();
-
- void runNextCmd();
- QStringList tokenize(const QString& s);
-
- bool outputTerminated(const QByteArray& text);
- void handleBreakpoint(const GDBMIResultParser::ParseObject& breakpoint);
- void handleFrame(const GDBMIResultParser::ParseValue &frame);
- void handleStack(const QList & stack);
- void handleLocalVariables(const QList & variables);
- void handleEvaluation(const QString& value);
- void handleMemory(const QList & rows);
- void handleRegisterNames(const QList & names);
- void handleRegisterValue(const QList & values);
- void handleCreateVar(const GDBMIResultParser::ParseObject& multiVars);
- void handleListVarChildren(const GDBMIResultParser::ParseObject& multiVars);
- void handleUpdateVarValue(const QList &changes);
- void processConsoleOutput(const QByteArray& line);
- void processLogOutput(const QByteArray& line);
- void processResult(const QByteArray& result);
- void processExecAsyncRecord(const QByteArray& line);
- void processError(const QByteArray& errorLine);
- void processResultRecord(const QByteArray& line);
- void processDebugOutput(const QByteArray& debugOutput);
void runInferiorStoppedHook();
- QByteArray removeToken(const QByteArray& line);
-private slots:
- void asyncUpdate();
protected:
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
QRecursiveMutex mCmdQueueMutex;
@@ -573,39 +549,27 @@ protected:
QMutex mCmdQueueMutex;
#endif
QQueue mCmdQueue;
+ bool mCmdRunning;
+
QList mInferiorStoppedHookCommands;
+ bool mInferiorRunning;
+ bool mProcessExited;
+ PDebugCommand mCurrentCmd;
+ QStringList mConsoleOutput;
+ QStringList mFullOutput;
+ QSemaphore mStartSemaphore;
+ bool mSignalReceived;
+ bool mUpdateCPUInfo;
+
+ QString mSignalName;
+ QString mSignalMeaning;
+ bool mReceivedSFWarning;
private:
Debugger *mDebugger;
QString mDebuggerPath;
- QSemaphore mStartSemaphore;
- bool mErrorOccured;
- bool mAsyncUpdated;
- //fOnInvalidateAllVars: TInvalidateAllVarsEvent;
- bool mCmdRunning;
- PDebugCommand mCurrentCmd;
QStringList mBinDirs;
- QMap mFileCache;
- //fWatchView: TTreeView;
-
- QString mSignalName;
- QString mSignalMeaning;
-
- //
- bool mInferiorRunning;
- bool mProcessExited;
-
- bool mSignalReceived;
- bool mUpdateCPUInfo;
- bool mReceivedSFWarning;
-
- int mCurrentLine;
- qulonglong mCurrentAddress;
- QString mCurrentFunc;
- QString mCurrentFile;
- QStringList mConsoleOutput;
- QStringList mFullOutput;
};
#endif // DEBUGGER_H
diff --git a/RedPandaIDE/debugger/gdbmidebugger.cpp b/RedPandaIDE/debugger/gdbmidebugger.cpp
index 88346437..fb5610e0 100644
--- a/RedPandaIDE/debugger/gdbmidebugger.cpp
+++ b/RedPandaIDE/debugger/gdbmidebugger.cpp
@@ -15,14 +15,22 @@
* along with this program. If not, see .
*/
#include "gdbmidebugger.h"
+#include "../mainwindow.h"
+#include "../editorlist.h"
#include "../utils.h"
+#include "../systemconsts.h"
+#include "../settings.h"
#include
+
+const QRegularExpression GDBMIDebuggerClient::REGdbSourceLine("^(\\d)+\\s+in\\s+(.+)$");
+
GDBMIDebuggerClient::GDBMIDebuggerClient(Debugger *debugger, QObject *parent):
DebuggerClient(debugger, parent)
{
-
+ mProcess = std::make_shared();
+ mAsyncUpdated = false;
}
void GDBMIDebuggerClient::postCommand(const QString &Command, const QString &Params,
@@ -56,9 +64,9 @@ void GDBMIDebuggerClient::stopDebug()
void GDBMIDebuggerClient::run()
{
mStop = false;
+ bool errorOccured = false;
mInferiorRunning = false;
mProcessExited = false;
- mErrorOccured = false;
QString cmd = debuggerPath();
// QString arguments = "--annotate=2";
QStringList arguments{"--interpret=mi", "--silent"};
@@ -91,7 +99,7 @@ void GDBMIDebuggerClient::run()
connect(mProcess.get(), &QProcess::errorOccurred,
[&](){
- mErrorOccured= true;
+ errorOccured= true;
});
QByteArray buffer;
QByteArray readed;
@@ -109,7 +117,7 @@ void GDBMIDebuggerClient::run()
mProcess->kill();
break;
}
- if (mErrorOccured)
+ if (errorOccured)
break;
readed = mProcess->readAll();
buffer += readed;
@@ -125,8 +133,731 @@ void GDBMIDebuggerClient::run()
msleep(1);
}
}
- if (mErrorOccured) {
- emit processError(mProcess->error());
+ if (errorOccured) {
+ emit processFailed(mProcess->error());
+ }
+}
+void GDBMIDebuggerClient::runNextCmd()
+{
+ QMutexLocker locker(&mCmdQueueMutex);
+
+ if (mCurrentCmd) {
+ DebugCommandSource commandSource = mCurrentCmd->source;
+ mCurrentCmd=nullptr;
+ if (commandSource!=DebugCommandSource::HeartBeat)
+ emit cmdFinished();
+ }
+ if (mCmdQueue.isEmpty()) {
+ if (debugger()->useDebugServer() && mInferiorRunning && !mAsyncUpdated) {
+ mAsyncUpdated = true;
+ QTimer::singleShot(50,this,&GDBMIDebuggerClient::asyncUpdate);
+ }
+ return;
+ }
+
+ PDebugCommand pCmd = mCmdQueue.dequeue();
+ mCmdRunning = true;
+ mCurrentCmd = pCmd;
+ if (pCmd->source!=DebugCommandSource::HeartBeat)
+ emit cmdStarted();
+
+ QByteArray s;
+ QByteArray params;
+ s=pCmd->command.toLocal8Bit();
+ if (!pCmd->params.isEmpty()) {
+ params = pCmd->params.toLocal8Bit();
+ }
+
+ //clang compatibility
+ if (debugger()->forceUTF8()) {
+ params = pCmd->params.toUtf8();
+ } else if (debugger()->debugInfosUsingUTF8() &&
+ (pCmd->command=="-break-insert"
+ || pCmd->command=="-var-create"
+ || pCmd->command=="-data-read-memory"
+ || pCmd->command=="-data-evaluate-expression"
+ )) {
+ params = pCmd->params.toUtf8();
+ }
+ if (pCmd->command == "-var-create") {
+ //hack for variable creation,to easy remember var expression
+ if (debugger()->debuggerType()==DebuggerType::LLDB_MI)
+ params = " - * "+params;
+ else
+ params = " - @ "+params;
+ } else if (pCmd->command == "-var-list-children") {
+ //hack for list variable children,to easy remember var expression
+ params = " --all-values \"" + params+'\"';
+ }
+ s+=" "+params;
+ s+= "\n";
+ if (mProcess->write(s)<0) {
+ emit writeToDebugFailed();
+ }
+
+ if (pSettings->debugger().enableDebugConsole() ) {
+ //update debug console
+ if (pSettings->debugger().showDetailLog()
+ && pCmd->source != DebugCommandSource::Console) {
+ emit changeDebugConsoleLastLine(pCmd->command + ' ' + params);
+ }
}
}
+QStringList GDBMIDebuggerClient::tokenize(const QString &s) const
+{
+ QStringList result;
+ int tStart,tEnd;
+ int i=0;
+ while (i') {
+ i++;
+ break;
+ }
+ i++;
+ }
+ tEnd = std::min(i,s.length());
+ result.append(s.mid(tStart,tEnd-tStart));
+ } else if (ch == '(') {
+ tStart = i;
+ i++;
+ while (iforceUTF8() || debugger()->debugInfosUsingUTF8())
+ filename = breakpoint["fullname"].utf8PathValue();
+ else
+ filename = breakpoint["fullname"].pathValue();
+ int line = breakpoint["line"].intValue();
+ int number = breakpoint["number"].intValue();
+ emit breakpointInfoGetted(filename, line , number);
+}
+
+void GDBMIDebuggerClient::handleFrame(const GDBMIResultParser::ParseValue &frame)
+{
+ if (frame.isValid()) {
+ GDBMIResultParser::ParseObject frameObj = frame.object();
+ bool ok;
+ mCurrentAddress = frameObj["addr"].hexValue(ok);
+ if (!ok)
+ mCurrentAddress=0;
+ mCurrentLine = frameObj["line"].intValue();
+ if (debugger()->forceUTF8()
+ || debugger()->debugInfosUsingUTF8())
+ mCurrentFile = frameObj["fullname"].utf8PathValue();
+ else
+ mCurrentFile = frameObj["fullname"].pathValue();
+ mCurrentFunc = frameObj["func"].value();
+ }
+}
+
+void GDBMIDebuggerClient::handleStack(const QList & stack)
+{
+ debugger()->backtraceModel()->clear();
+ foreach (const GDBMIResultParser::ParseValue& frameValue, stack) {
+ GDBMIResultParser::ParseObject frameObject = frameValue.object();
+ PTrace trace = std::make_shared();
+ trace->funcname = frameObject["func"].value();
+ if (debugger()->forceUTF8() || debugger()->debugInfosUsingUTF8())
+ trace->filename = frameObject["fullname"].utf8PathValue();
+ else
+ trace->filename = frameObject["fullname"].pathValue();
+ trace->line = frameObject["line"].intValue();
+ trace->level = frameObject["level"].intValue(0);
+ trace->address = frameObject["addr"].value();
+ debugger()->backtraceModel()->addTrace(trace);
+ }
+}
+
+void GDBMIDebuggerClient::handleLocalVariables(const QList &variables)
+{
+ QStringList locals;
+ foreach (const GDBMIResultParser::ParseValue& varValue, variables) {
+ GDBMIResultParser::ParseObject varObject = varValue.object();
+ QString name = QString(varObject["name"].value());
+ QString value = QString(varObject["value"].value());
+ locals.append(
+ QString("%1 = %2")
+ .arg(
+ name,
+ value
+ ));
+ }
+ emit localsUpdated(locals);
+}
+
+void GDBMIDebuggerClient::handleEvaluation(const QString &value)
+{
+ emit evalUpdated(value);
+}
+
+void GDBMIDebuggerClient::handleMemory(const QList &rows)
+{
+ QStringList memory;
+ foreach (const GDBMIResultParser::ParseValue& row, rows) {
+ GDBMIResultParser::ParseObject rowObject = row.object();
+ QList data = rowObject["data"].array();
+ QStringList values;
+ foreach (const GDBMIResultParser::ParseValue& val, data) {
+ values.append(val.value());
+ }
+ memory.append(QString("%1 %2")
+ .arg(rowObject["addr"].value(),values.join(" ")));
+ }
+ emit memoryUpdated(memory);
+}
+
+void GDBMIDebuggerClient::handleRegisterNames(const QList &names)
+{
+ QStringList nameList;
+ foreach (const GDBMIResultParser::ParseValue& nameValue, names) {
+// QString text = nameValue.value().trimmed();
+// if (!text.isEmpty())
+ nameList.append(nameValue.value());
+ }
+ emit registerNamesUpdated(nameList);
+}
+
+void GDBMIDebuggerClient::handleRegisterValue(const QList &values)
+{
+ QHash result;
+ foreach (const GDBMIResultParser::ParseValue& val, values) {
+ GDBMIResultParser::ParseObject obj = val.object();
+ int number = obj["number"].intValue();
+ QString value = obj["value"].value();
+ bool ok;
+ long long intVal;
+ intVal = value.toLongLong(&ok,10);
+ if (ok) {
+ value = QString("0x%1").arg(intVal,0,16);
+ }
+ result.insert(number,value);
+ }
+ emit registerValuesUpdated(result);
+}
+
+void GDBMIDebuggerClient::handleListVarChildren(const GDBMIResultParser::ParseObject &multiVars)
+{
+ if (!mCurrentCmd)
+ return;
+ QString parentName = mCurrentCmd->params;
+ int parentNumChild = multiVars["numchild"].intValue(0);
+ QList children = multiVars["children"].array();
+ bool hasMore = multiVars["has_more"].value()!="0";
+ emit prepareVarChildren(parentName,parentNumChild,hasMore);
+ foreach(const GDBMIResultParser::ParseValue& child, children) {
+ GDBMIResultParser::ParseObject childObj = child.object();
+ QString name = childObj["name"].value();
+ QString exp = childObj["exp"].value();
+ int numChild = childObj["numchild"].intValue(0);
+ QString value = childObj["value"].value();
+ QString type = childObj["type"].value();
+ bool hasMore = childObj["has_more"].value() != "0";
+ emit addVarChild(parentName,
+ name,
+ exp,
+ numChild,
+ value,
+ type,
+ hasMore);
+ }
+}
+
+
+void GDBMIDebuggerClient::handleCreateVar(const GDBMIResultParser::ParseObject &multiVars)
+{
+ if (!mCurrentCmd)
+ return;
+ QString expression = mCurrentCmd->params;
+ QString name = multiVars["name"].value();
+ int numChild = multiVars["numchild"].intValue(0);
+ QString value = multiVars["value"].value();
+ QString type = multiVars["type"].value();
+ bool hasMore = multiVars["has_more"].value() != "0";
+ emit varCreated(expression,name,numChild,value,type,hasMore);
+}
+
+void GDBMIDebuggerClient::handleUpdateVarValue(const QList &changes)
+{
+ foreach (const GDBMIResultParser::ParseValue& value, changes) {
+ GDBMIResultParser::ParseObject obj = value.object();
+ QString name = obj["name"].value();
+ QString val = obj["value"].value();
+ QString inScope = obj["in_scope"].value();
+ bool typeChanged = (obj["type_changed"].value()=="true");
+ QString newType = obj["new_type"].value();
+ int newNumChildren = obj["new_num_children"].intValue(-1);
+ bool hasMore = (obj["has_more"].value() == "1");
+ emit varValueUpdated(name,val,inScope,typeChanged,newType,newNumChildren,
+ hasMore);
+ }
+ //todo: -var-list-children will freeze if the var is not correctly initialized
+ //emit varsValueUpdated();
+}
+
+void GDBMIDebuggerClient::processConsoleOutput(const QByteArray& line)
+{
+ if (line.length()>3 && line.startsWith("~\"") && line.endsWith("\"")) {
+ QByteArray s=line.mid(2,line.length()-3);
+ QByteArray stringValue;
+ const char *p=s.data();
+ while (*p!=0) {
+ if (*p=='\\' && *(p+1)!=0) {
+ p++;
+ switch (*p) {
+ case '\'':
+ stringValue+=0x27;
+ p++;
+ break;
+ case '"':
+ stringValue+=0x22;
+ p++;
+ break;
+ case '?':
+ stringValue+=0x3f;
+ p++;
+ break;
+ case '\\':
+ stringValue+=0x5c;
+ p++;
+ break;
+ case 'a':
+ stringValue+=0x07;
+ p++;
+ break;
+ case 'b':
+ stringValue+=0x08;
+ p++;
+ break;
+ case 'f':
+ stringValue+=0x0c;
+ p++;
+ break;
+ case 'n':
+ stringValue+=0x0a;
+ p++;
+ break;
+ case 'r':
+ stringValue+=0x0d;
+ p++;
+ break;
+ case 't':
+ stringValue+=0x09;
+ p++;
+ break;
+ case 'v':
+ stringValue+=0x0b;
+ p++;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int i=0;
+ for (i=0;i<3;i++) {
+ if (*(p+i)<'0' || *(p+i)>'7')
+ break;
+ }
+ QByteArray numStr(p,i);
+ bool ok;
+ unsigned char ch = numStr.toInt(&ok,8);
+ stringValue+=ch;
+ p+=i;
+ break;
+ }
+ }
+ } else {
+ stringValue+=*p;
+ p++;
+ }
+ }
+ //mConsoleOutput.append(QString::fromLocal8Bit(stringValue));
+ mConsoleOutput.append(QString::fromUtf8(stringValue));
+ }
+}
+
+void GDBMIDebuggerClient::processLogOutput(const QByteArray &line)
+{
+ if (debugger()->debugInfosUsingUTF8() && line.endsWith(": No such file or directory.\n\"")) {
+ QByteArray newLine = line;
+ newLine[0]='~';
+ int p=newLine.lastIndexOf(':');
+ if (p>0) {
+ newLine=newLine.left(p);
+ //qDebug()<command, resultType,multiValues);
+ if (!parseOk)
+ return;
+ switch(resultType) {
+ case GDBMIResultType::BreakpointTable:
+ case GDBMIResultType::Locals:
+ break;
+ case GDBMIResultType::Breakpoint:
+ handleBreakpoint(multiValues["bkpt"].object());
+ return;
+ case GDBMIResultType::Frame:
+ handleFrame(multiValues["frame"]);
+ return;
+ case GDBMIResultType::FrameStack:
+ handleStack(multiValues["stack"].array());
+ return;
+ case GDBMIResultType::LocalVariables:
+ handleLocalVariables(multiValues["variables"].array());
+ return;
+ case GDBMIResultType::Evaluation:
+ handleEvaluation(multiValues["value"].value());
+ return;
+ case GDBMIResultType::Memory:
+ handleMemory(multiValues["memory"].array());
+ return;
+ case GDBMIResultType::RegisterNames:
+ handleRegisterNames(multiValues["register-names"].array());
+ return;
+ case GDBMIResultType::RegisterValues:
+ handleRegisterValue(multiValues["register-values"].array());
+ return;
+ case GDBMIResultType::CreateVar:
+ handleCreateVar(multiValues);
+ return;
+ case GDBMIResultType::ListVarChildren:
+ handleListVarChildren(multiValues);
+ return;
+ case GDBMIResultType::UpdateVarValue:
+ handleUpdateVarValue(multiValues["changelist"].array());
+ return;
+ default:
+ return;
+ }
+}
+
+void GDBMIDebuggerClient::processExecAsyncRecord(const QByteArray &line)
+{
+ QByteArray result;
+ GDBMIResultParser::ParseObject multiValues;
+ GDBMIResultParser parser;
+ if (!parser.parseAsyncResult(line,result,multiValues))
+ return;
+ if (result == "running") {
+ mInferiorRunning = true;
+ mCurrentAddress=0;
+ mCurrentFile.clear();
+ mCurrentLine=-1;
+ mCurrentFunc.clear();
+ emit inferiorContinued();
+ return;
+ }
+ if (result == "stopped") {
+ mInferiorRunning = false;
+ QByteArray reason = multiValues["reason"].value();
+ if (reason == "exited") {
+ //inferior exited, gdb should terminate too
+ mProcessExited = true;
+ return;
+ }
+ if (reason == "exited-normally") {
+ //inferior exited, gdb should terminate too
+ mProcessExited = true;
+ return;
+ }
+ if (reason == "exited-signalled") {
+ //inferior exited, gdb should terminate too
+ mProcessExited = true;
+ mSignalReceived = true;
+ return;
+ }
+ mUpdateCPUInfo = true;
+ handleFrame(multiValues["frame"]);
+ if (reason == "signal-received") {
+ mSignalReceived = true;
+ mSignalName = multiValues["signal-name"].value();
+ mSignalMeaning = multiValues["signal-meaning"].value();
+ } else if (reason == "watchpoint-trigger") {
+ QString var,oldVal,newVal;
+ GDBMIResultParser::ParseValue wpt=multiValues["wpt"];
+ if (wpt.isValid()) {
+ GDBMIResultParser::ParseObject wptObj = wpt.object();
+ var=wptObj["exp"].value();
+ }
+ GDBMIResultParser::ParseValue varValue=multiValues["value"];
+ if (varValue.isValid()) {
+ GDBMIResultParser::ParseObject valueObj = varValue.object();
+ oldVal=valueObj["old"].value();
+ newVal=valueObj["new"].value();
+ }
+ if (!var.isEmpty()) {
+ emit watchpointHitted(var,oldVal,newVal);
+ }
+ }
+ runInferiorStoppedHook();
+ if (reason.isEmpty()) {
+ QMutexLocker locker(&mCmdQueueMutex);
+ foreach (const PDebugCommand& cmd, mCmdQueue) {
+ //gdb-server connected, just ignore it
+ if (cmd->command=="-exec-continue")
+ return;
+ }
+ }
+ if (mCurrentCmd && mCurrentCmd->source == DebugCommandSource::Console)
+ emit inferiorStopped(mCurrentFile, mCurrentLine, false);
+ else
+ emit inferiorStopped(mCurrentFile, mCurrentLine, true);
+ }
+}
+
+void GDBMIDebuggerClient::processError(const QByteArray &errorLine)
+{
+ QString s = QString::fromLocal8Bit(errorLine);
+ mConsoleOutput.append(s);
+ int idx=s.indexOf(",msg=\"No symbol table is loaded");
+ if (idx>0) {
+ emit errorNoSymbolTable();
+ return;
+ }
+}
+
+void GDBMIDebuggerClient::processResultRecord(const QByteArray &line)
+{
+ if (line.startsWith("^exit")) {
+ mProcessExited = true;
+ return;
+ }
+ if (line.startsWith("^error")) {
+ processError(line);
+ return;
+ }
+ if (line.startsWith("^done")
+ || line.startsWith("^running")) {
+ int pos = line.indexOf(',');
+ if (pos>=0) {
+ QByteArray result = line.mid(pos+1);
+ processResult(result);
+ } else if (mCurrentCmd && !(mCurrentCmd->command.startsWith('-'))) {
+ if (mCurrentCmd->command == "disas") {
+ QStringList disOutput = mConsoleOutput;
+ if (disOutput.length()>=3) {
+ disOutput.pop_back();
+ disOutput.pop_front();
+ disOutput.pop_front();
+ }
+ if (debugger()->debugInfosUsingUTF8()) {
+ QStringList newOutput;
+ foreach(const QString& s, disOutput) {
+ QString line = s;
+ if (!s.isEmpty() && s.front().isDigit()) {
+ QRegularExpressionMatch match = REGdbSourceLine.match(s);
+// qDebug()<editorList()->getContentFromOpenedEditor(filename,contents))
+ contents = readFileToLines(filename);
+ mFileCache[filename]=contents;
+ }
+ if (lineno>=0 && lineno lines = splitByteArrayToLines(debugOutput);
+
+ for (int i=0;idebugger().showDetailLog())
+ mFullOutput.append(line);
+ line = removeToken(line);
+ if (line.isEmpty()) {
+ continue;
+ }
+ switch (line[0]) {
+ case '~': // console stream output
+ processConsoleOutput(line);
+ break;
+ case '@': // target stream output
+ break;
+ case '&': // log stream output
+ processLogOutput(line);
+ break;
+ case '^': // result record
+ processResultRecord(line);
+ break;
+ case '*': // exec async output
+ processExecAsyncRecord(line);
+ break;
+ case '+': // status async output
+ case '=': // notify async output
+ break;
+ }
+ }
+ emit parseFinished();
+ mConsoleOutput.clear();
+ mFullOutput.clear();
+}
+
+
+QByteArray GDBMIDebuggerClient::removeToken(const QByteArray &line) const
+{
+ int p=0;
+ while (p'9') {
+ break;
+ }
+ p++;
+ }
+ if (p
+#include
+#include
+#include
+#include
+#include
#include
+#include
class GDBMIDebuggerClient: public DebuggerClient {
Q_OBJECT
@@ -34,9 +40,43 @@ public:
// QThread interface
protected:
void run() override;
+ void runNextCmd() override;
+private:
+ QStringList tokenize(const QString& s) const;
+ bool outputTerminated(const QByteArray& text) const;
+ void handleBreakpoint(const GDBMIResultParser::ParseObject& breakpoint);
+ void handleCreateVar(const GDBMIResultParser::ParseObject &multiVars);
+ void handleFrame(const GDBMIResultParser::ParseValue &frame);
+ void handleStack(const QList & stack);
+ void handleLocalVariables(const QList & variables);
+ void handleEvaluation(const QString& value);
+ void handleMemory(const QList & rows);
+ void handleRegisterNames(const QList & names);
+ void handleRegisterValue(const QList & values);
+ void handleListVarChildren(const GDBMIResultParser::ParseObject& multiVars);
+ void handleUpdateVarValue(const QList &changes);
+ void processConsoleOutput(const QByteArray& line);
+ void processLogOutput(const QByteArray& line);
+ void processResult(const QByteArray& result);
+ void processExecAsyncRecord(const QByteArray& line);
+ void processError(const QByteArray& errorLine);
+ void processResultRecord(const QByteArray& line);
+ void processDebugOutput(const QByteArray& debugOutput);
+ QByteArray removeToken(const QByteArray& line) const;
+private slots:
+ void asyncUpdate();
private:
bool mStop;
std::shared_ptr mProcess;
+ QMap mFileCache;
+ int mCurrentLine;
+ qulonglong mCurrentAddress;
+ QString mCurrentFunc;
+ QString mCurrentFile;
+
+ bool mAsyncUpdated;
+
+ static const QRegularExpression REGdbSourceLine;
};
#endif // GDBMI_DEBUGGER_H