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