/* * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * 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, DebuggerType clientType, QObject *parent): DebuggerClient{debugger, parent}, mClientType{clientType} { mProcess = std::make_shared(); mAsyncUpdated = false; registerInferiorStoppedCommand("-stack-list-frames",""); } void GDBMIDebuggerClient::postCommand(const QString &command, const QString ¶ms, DebugCommandSource source) { QMutexLocker locker(&mCmdQueueMutex); PDebugCommand pCmd = std::make_shared(); pCmd->command = command; pCmd->params = params; pCmd->source = source; mCmdQueue.enqueue(pCmd); // if (!mCmdRunning) // runNextCmd(); } void GDBMIDebuggerClient::registerInferiorStoppedCommand(const QString &command, const QString ¶ms) { QMutexLocker locker(&mCmdQueueMutex); PDebugCommand pCmd = std::make_shared(); pCmd->command = command; pCmd->params = params; pCmd->source = DebugCommandSource::Other; mInferiorStoppedHookCommands.append(pCmd); } void GDBMIDebuggerClient::stopDebug() { mStop = true; } DebuggerType GDBMIDebuggerClient::clientType() { return mClientType; } void GDBMIDebuggerClient::run() { mStop = false; bool errorOccured = false; mInferiorRunning = false; mProcessExited = false; QString cmd = debuggerPath(); // QString arguments = "--annotate=2"; QStringList arguments{"--interpret=mi", "--silent"}; QString workingDir = QFileInfo(debuggerPath()).path(); mProcess = std::make_shared(); auto action = finally([&]{ mProcess.reset(); }); mProcess->setProgram(cmd); mProcess->setArguments(arguments); mProcess->setProcessChannelMode(QProcess::MergedChannels); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QString path = env.value("PATH"); QStringList pathAdded = binDirs(); if (!path.isEmpty()) { path = pathAdded.join(PATH_SEPARATOR) + PATH_SEPARATOR + path; } else { path = pathAdded.join(PATH_SEPARATOR); } QString cmdDir = extractFileDir(cmd); if (!cmdDir.isEmpty()) { path = cmdDir + PATH_SEPARATOR + path; } env.insert("PATH",path); mProcess->setProcessEnvironment(env); mProcess->setWorkingDirectory(workingDir); connect(mProcess.get(), &QProcess::errorOccurred, [&](){ errorOccured= true; }); QByteArray buffer; QByteArray readed; mProcess->start(); mProcess->waitForStarted(5000); mStartSemaphore.release(1); while (true) { mProcess->waitForFinished(1); if (mProcess->state()!=QProcess::Running) { break; } if (mStop) { mProcess->readAll(); mProcess->write("-gdb-exit\n"); msleep(50); mProcess->readAll(); msleep(50); mProcess->terminate(); mProcess->kill(); break; } if (errorOccured) break; readed = mProcess->readAll(); buffer += readed; if (readed.endsWith("\n")&& outputTerminated(buffer)) { processDebugOutput(buffer); buffer.clear(); // mCmdRunning = false; // runNextCmd(); } else if (!mCmdRunning && readed.isEmpty()){ runNextCmd(); } else if (readed.isEmpty()){ msleep(1); } } 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 (clientType()==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::handleMemoryBytes(const QList &rows) { QStringList memory; foreach (const GDBMIResultParser::ParseValue& row, rows) { GDBMIResultParser::ParseObject rowObject = row.object(); bool ok; qulonglong startAddr = rowObject["begin"].value().toLongLong(&ok, 16); qulonglong endAddr = rowObject["end"].value().toLongLong(&ok, 16); qulonglong offset = rowObject["offset"].value().toLongLong(&ok, 16); startAddr += offset; QByteArray contents = rowObject["contents"].value(); qulonglong addr = startAddr; QStringList values; int cols = pSettings->debugger().memoryViewColumns(); while (addr &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::handleDisassembly(const QList &instructions) { QStringList lines; foreach (const GDBMIResultParser::ParseValue& value, instructions) { QString line; GDBMIResultParser::ParseObject obj = value.object(); if (mCurrentCmd && mCurrentCmd->params.contains("--source")) { } else { bool ok; QString addr = obj["address"].value(); QString inst = obj["inst"].value(); QString offset = obj["offset"].value(); qulonglong addrVal = addr.toULongLong(&ok, 16); if (addrVal == mCurrentAddress) { line = "==> "+addr+ " " + inst; } else { line = " "+addr+ " " + inst; } lines.append(line); } } emit disassemblyUpdate(mCurrentFile, mCurrentFunc, lines); } 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()); break; case GDBMIResultType::Frame: handleFrame(multiValues["frame"]); break; case GDBMIResultType::FrameStack: handleStack(multiValues["stack"].array()); break; case GDBMIResultType::LocalVariables: handleLocalVariables(multiValues["variables"].array()); break; case GDBMIResultType::Evaluation: handleEvaluation(multiValues["value"].value()); break; case GDBMIResultType::Memory: handleMemory(multiValues["memory"].array()); break; case GDBMIResultType::MemoryBytes: handleMemoryBytes(multiValues["memory"].array()); break; case GDBMIResultType::RegisterNames: handleRegisterNames(multiValues["register-names"].array()); break; case GDBMIResultType::RegisterValues: handleRegisterValue(multiValues["register-values"].array()); break; case GDBMIResultType::CreateVar: handleCreateVar(multiValues); break; case GDBMIResultType::ListVarChildren: handleListVarChildren(multiValues); break; case GDBMIResultType::UpdateVarValue: handleUpdateVarValue(multiValues["changelist"].array()); break; case GDBMIResultType::Disassembly: handleDisassembly(multiValues["asm_insns"].array()); break; default: break; } } 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) { auto action = finally([this]() { if (!mProcessExited) { mCmdRunning = false; runNextCmd(); } }); 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& origLine, disOutput) { QStringList subLines = textToLines(origLine); foreach (const QString& s, subLines) { 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 (pdebugger().arrayElements())); // limit array elements to 30 postCommand("-gdb-set", QString("print characters %1").arg(pSettings->debugger().characters())); // limit array elements to 300 postCommand("-environment-cd", QString("\"%1\"").arg(extractFileDir(inferior))); // restore working directory if (clientType()==DebuggerType::GDB) postCommand("-data-list-register-names",""); if (hasSymbols) { postCommand("-file-exec-and-symbols", '"' + inferior + '"'); } else { postCommand("-file-exec-file", '"' + inferior + '"'); } } void GDBMIDebuggerClient::runInferior(bool hasBreakpoints) { if (debugger()->useDebugServer()) { postCommand("-target-select",QString("remote localhost:%1").arg(pSettings->debugger().GDBServerPort())); if (!hasBreakpoints) { postCommand("-break-insert","-t main"); } if (pSettings->executor().useParams()) { postCommand("-exec-arguments", pSettings->executor().params()); } postCommand("-exec-continue",""); } else { #ifdef Q_OS_WIN postCommand("-gdb-set", "new-console on"); #endif if (pSettings->executor().useParams()) { postCommand("-exec-arguments", pSettings->executor().params()); } if (clientType() == DebuggerType::LLDB_MI) { if (!hasBreakpoints) { postCommand("-break-insert","-t main"); } postCommand("-exec-run",""); } else { if (!hasBreakpoints) { postCommand("-exec-run","--start"); } else { postCommand("-exec-run",""); } } } } void GDBMIDebuggerClient::stepOver() { postCommand("-exec-next", ""); } void GDBMIDebuggerClient::stepInto() { postCommand("-exec-step", ""); } void GDBMIDebuggerClient::stepOut() { postCommand("-exec-finish", ""); } void GDBMIDebuggerClient::runTo(const QString &filename, int line) { postCommand("-exec-until", QString("\"%1\":%2") .arg(filename) .arg(line)); } void GDBMIDebuggerClient::resume() { postCommand("-exec-continue", ""); } void GDBMIDebuggerClient::stepOverInstruction() { postCommand("-exec-next-instruction",""); } void GDBMIDebuggerClient::stepIntoInstruction() { postCommand("-exec-step-instruction",""); } void GDBMIDebuggerClient::interrupt() { postCommand("-exec-interrupt", ""); } void GDBMIDebuggerClient::refreshStackVariables() { postCommand("-stack-list-variables", "--all-values"); } void GDBMIDebuggerClient::readMemory(const QString& startAddress, int rows, int cols) { // postCommand("-data-read-memory",QString("%1 x 1 %2 %3 ") // .arg(startAddress) // .arg(rows) // .arg(cols)); postCommand("-data-read-memory-bytes",QString("%1 %2") .arg(startAddress) .arg(rows * cols)); } void GDBMIDebuggerClient::writeMemory(qulonglong address, unsigned char data) { postCommand("-data-write-memory-bytes", QString("%1 \"%2\"").arg(address).arg(data,2,16,QChar('0'))); } void GDBMIDebuggerClient::addBreakpoint(PBreakpoint breakpoint) { if (breakpoint) { // break "filename":linenum QString condition; if (!breakpoint->condition.isEmpty()) { condition = " -c " + breakpoint->condition; } QString filename = breakpoint->filename; filename.replace('\\','/'); if (clientType()==DebuggerType::LLDB_MI) { postCommand("-break-insert", QString("%1 \"%2:%3\"") .arg(condition, filename) .arg(breakpoint->line)); } else { postCommand("-break-insert", QString("%1 --source \"%2\" --line %3") .arg(condition,filename) .arg(breakpoint->line)); } } } void GDBMIDebuggerClient::removeBreakpoint(PBreakpoint breakpoint) { if (breakpoint && breakpoint->number>=0) { //clear "filename":linenum QString filename = breakpoint->filename; filename.replace('\\','/'); postCommand("-break-delete", QString("%1").arg(breakpoint->number)); } } void GDBMIDebuggerClient::setBreakpointCondition(PBreakpoint breakpoint) { Q_ASSERT(breakpoint!=nullptr); QString condition = breakpoint->condition; if (condition.isEmpty()) { postCommand("-break-condition", QString("%1").arg(breakpoint->number)); } else { postCommand("-break-condition", QString("%1 %2").arg(breakpoint->number).arg(condition)); } } void GDBMIDebuggerClient::addWatch(const QString &expression) { postCommand("-var-create", expression); } void GDBMIDebuggerClient::removeWatch(PWatchVar watchVar) { postCommand("-var-delete",QString("%1").arg(watchVar->name)); } void GDBMIDebuggerClient::writeWatchVar(const QString &varName, const QString &value) { postCommand("-var-assign",QString("%1 %2").arg(varName, value)); } void GDBMIDebuggerClient::addWatchpoint(const QString &watchExp) { if (!watchExp.isEmpty()) postCommand("-break-watch", watchExp); } void GDBMIDebuggerClient::refreshWatch(PWatchVar var) { Q_ASSERT(var!=nullptr); postCommand("-var-update", QString(" --all-values %1").arg(var->name)); } void GDBMIDebuggerClient::refreshWatch() { postCommand("-var-update"," --all-values *"); } void GDBMIDebuggerClient::fetchWatchVarChildren(const QString& varName) { postCommand("-var-list-children", varName); } void GDBMIDebuggerClient::evalExpression(const QString &expression) { postCommand("-data-evaluate-expression", expression); } void GDBMIDebuggerClient::selectFrame(PTrace trace) { if (trace) postCommand("-stack-select-frame", QString("%1").arg(trace->level)); } void GDBMIDebuggerClient::refreshFrame() { postCommand("-stack-info-frame", ""); } void GDBMIDebuggerClient::refreshRegisters() { if (clientType()==DebuggerType::LLDB_MI) postCommand("-data-list-register-names",""); postCommand("-data-list-register-values", "N"); } void GDBMIDebuggerClient::disassembleCurrentFrame(bool blendMode) { if (blendMode && clientType()==DebuggerType::GDB) postCommand("disas", "/s"); else postCommand("disas", ""); // QString params=QString("-s 0x%1 -e 0x%2 -mode 0") // .arg(mCurrentAddress,0,16) // .arg(mCurrentAddress+1,0,16); // // if (blendMode) // // params += " --source"; // postCommand("-data-disassemble",params); } void GDBMIDebuggerClient::setDisassemblyLanguage(bool isIntel) { if (isIntel) { postCommand("-gdb-set", "disassembly-flavor intel"); } else { postCommand("-gdb-set", "disassembly-flavor att"); } } void GDBMIDebuggerClient::skipDirectoriesInSymbolSearch(const QStringList &lst) { foreach(const QString &dirName, lst) { postCommand( "skip", QString("-gfi \"%1/%2\"") .arg(dirName,"*.*")); } } void GDBMIDebuggerClient::addSymbolSearchDirectories(const QStringList &lst) { foreach(const QString &dirName, lst) { postCommand( "-environment-directory", QString("\"%1\"").arg(dirName)); } } void GDBMIDebuggerClient::runInferiorStoppedHook() { QMutexLocker locker(&mCmdQueueMutex); foreach (const PDebugCommand& cmd, mInferiorStoppedHookCommands) { mCmdQueue.push_front(cmd); } } void GDBMIDebuggerClient::clearCmdQueue() { QMutexLocker locker(&mCmdQueueMutex); mCmdQueue.clear(); } bool GDBMIDebuggerClient::commandRunning() { return !mCmdQueue.isEmpty(); }