File indexing completed on 2024-04-28 05:48:33
0001 // 0002 // gdbbackend.cpp 0003 // 0004 // Description: Manages the interaction with GDB 0005 // 0006 // 0007 // SPDX-FileCopyrightText: 2008-2010 Ian Wakeling <ian.wakeling@ntlworld.com> 0008 // SPDX-FileCopyrightText: 2011 Kåre Särs <kare.sars@iki.fi> 0009 // 0010 // SPDX-License-Identifier: LGPL-2.0-only 0011 0012 #include "gdbbackend.h" 0013 #include "gdbmi/tokens.h" 0014 #include "hostprocess.h" 0015 0016 #include <QDir> 0017 #include <QFile> 0018 #include <QFileInfo> 0019 #include <QJsonArray> 0020 #include <QJsonDocument> 0021 #include <QJsonObject> 0022 #include <QRegularExpression> 0023 #include <QStandardPaths> 0024 #include <QTimer> 0025 0026 #include <KLocalizedString> 0027 #include <KMessageBox> 0028 0029 #include <algorithm> 0030 #include <signal.h> 0031 #include <stdlib.h> 0032 0033 // #define DEBUG_GDBMI 0034 0035 static const dap::Scope LocalScope(0, i18n("Locals")); 0036 static const dap::Scope ThisScope(1, QStringLiteral("*this")); 0037 static const dap::Scope RegistersScope(2, i18n("CPU registers")); 0038 0039 static constexpr int DATA_EVAL_THIS_CHECK = 0; 0040 static constexpr int DATA_EVAL_THIS_DEREF = 1; 0041 0042 static constexpr int MAX_RESPONSE_ERRORS = 5; 0043 0044 static QString printEvent(const QString &text) 0045 { 0046 return QStringLiteral("--> %1\n").arg(text); 0047 } 0048 0049 bool GdbCommand::isMachineInterface() const 0050 { 0051 return !arguments.isEmpty() && arguments.first().startsWith(QLatin1Char('-')); 0052 } 0053 0054 GdbCommand GdbCommand::parse(const QString &request) 0055 { 0056 GdbCommand cmd; 0057 0058 cmd.arguments = QProcess::splitCommand(request); 0059 if (!cmd.arguments.isEmpty()) { 0060 const auto parts = gdbmi::GdbmiParser::splitCommand(cmd.arguments.at(0)); 0061 if (parts.size() > 1) { 0062 cmd.arguments.replace(0, parts.last()); 0063 } 0064 } 0065 0066 return cmd; 0067 } 0068 0069 bool GdbCommand::check(const QString &command) const 0070 { 0071 return !arguments.isEmpty() && (arguments.first() == command); 0072 } 0073 0074 bool GdbCommand::check(const QString &part1, const QString &part2) const 0075 { 0076 return (arguments.size() >= 2) && (arguments.first() == part1) && (arguments.at(1) == part2); 0077 } 0078 0079 void GdbBackend::enqueue(const QString &command) 0080 { 0081 m_nextCommands << PendingCommand{command, std::nullopt, Default}; 0082 } 0083 0084 void GdbBackend::enqueue(const QString &command, const QJsonValue &data, uint8_t captureMode) 0085 { 0086 m_nextCommands << PendingCommand{command, data, captureMode}; 0087 } 0088 0089 void GdbBackend::prepend(const QString &command) 0090 { 0091 m_nextCommands.prepend({command, std::nullopt, Default}); 0092 } 0093 0094 GdbBackend::GdbBackend(QObject *parent) 0095 : BackendInterface(parent) 0096 , m_debugProcess(nullptr) 0097 , m_state(none) 0098 , m_debugLocationChanged(true) 0099 , m_queryLocals(false) 0100 , m_parser(new gdbmi::GdbmiParser(this)) 0101 { 0102 // variable parser 0103 connect(&m_variableParser, &GDBVariableParser::variable, this, &BackendInterface::variableInfo); 0104 0105 connect(m_parser, &gdbmi::GdbmiParser::outputProduced, this, &GdbBackend::processMIStreamOutput); 0106 connect(m_parser, &gdbmi::GdbmiParser::recordProduced, this, &GdbBackend::processMIRecord); 0107 connect(m_parser, &gdbmi::GdbmiParser::parserError, this, &GdbBackend::onMIParserError); 0108 } 0109 0110 GdbBackend::~GdbBackend() 0111 { 0112 if (m_debugProcess.state() != QProcess::NotRunning) { 0113 m_debugProcess.kill(); 0114 m_debugProcess.blockSignals(true); 0115 m_debugProcess.waitForFinished(); 0116 } 0117 disconnect(m_parser); 0118 m_parser->deleteLater(); 0119 } 0120 0121 bool GdbBackend::supportsMovePC() const 0122 { 0123 return m_capabilities.execJump.value_or(false) && canMove(); 0124 } 0125 0126 bool GdbBackend::supportsRunToCursor() const 0127 { 0128 return canMove(); 0129 } 0130 0131 bool GdbBackend::canSetBreakpoints() const 0132 { 0133 return m_gdbState != Disconnected; 0134 } 0135 0136 bool GdbBackend::canMove() const 0137 { 0138 return (m_gdbState == Connected) || (m_gdbState == Stopped); 0139 } 0140 0141 bool GdbBackend::canContinue() const 0142 { 0143 return canMove(); 0144 } 0145 0146 void GdbBackend::resetSession() 0147 { 0148 m_nextCommands.clear(); 0149 m_currentThread.reset(); 0150 m_stackFrames.clear(); 0151 m_registerNames.clear(); 0152 } 0153 0154 void GdbBackend::runDebugger(const GDBTargetConf &conf, const QStringList &ioFifos) 0155 { 0156 // TODO correct remote flow (connected, interrupt, etc.) 0157 if (conf.executable.isEmpty()) { 0158 const QString msg = i18n("Please set the executable in the 'Settings' tab in the 'Debug' panel."); 0159 Q_EMIT backendError(msg, KTextEditor::Message::Error); 0160 return; 0161 } 0162 0163 m_targetConf = conf; 0164 0165 // no chance if no debugger configured 0166 if (m_targetConf.gdbCmd.isEmpty()) { 0167 const QString msg = i18n("No debugger selected. Please select one in the 'Settings' tab in the 'Debug' panel."); 0168 Q_EMIT backendError(msg, KTextEditor::Message::Error); 0169 return; 0170 } 0171 0172 // only run debugger from PATH or the absolute executable path we specified 0173 const auto fullExecutable = QFileInfo(m_targetConf.gdbCmd).isAbsolute() ? m_targetConf.gdbCmd : safeExecutableName(m_targetConf.gdbCmd); 0174 if (fullExecutable.isEmpty()) { 0175 const QString msg = 0176 i18n("Debugger not found. Please make sure you have it installed in your system. The selected debugger is '%1'", m_targetConf.gdbCmd); 0177 Q_EMIT backendError(msg, KTextEditor::Message::Error); 0178 return; 0179 } 0180 0181 if (ioFifos.size() == 3) { 0182 // XXX only supported by GDB, not LLDB 0183 m_ioPipeString = QStringLiteral("< %1 1> %2 2> %3").arg(ioFifos[0], ioFifos[1], ioFifos[2]); 0184 } 0185 0186 if (m_state == none) { 0187 m_seq = 0; 0188 m_errorCounter = 0; 0189 resetSession(); 0190 updateInspectable(false); 0191 m_outBuffer.clear(); 0192 m_errBuffer.clear(); 0193 setGdbState(Disconnected); 0194 0195 // create a process to control GDB 0196 m_debugProcess.setWorkingDirectory(m_targetConf.workDir); 0197 0198 connect(&m_debugProcess, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::errorOccurred), this, &GdbBackend::slotError); 0199 0200 connect(&m_debugProcess, &QProcess::readyReadStandardError, this, &GdbBackend::slotReadDebugStdErr); 0201 0202 connect(&m_debugProcess, &QProcess::readyReadStandardOutput, this, &GdbBackend::slotReadDebugStdOut); 0203 0204 connect(&m_debugProcess, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &GdbBackend::slotDebugFinished); 0205 0206 startHostProcess(m_debugProcess, fullExecutable, {QLatin1String("--interpreter=mi3")}); 0207 0208 enqueue(QStringLiteral("-gdb-set pagination off")); 0209 // for the sake of simplicity we try to restrict to synchronous mode 0210 enqueue(QStringLiteral("-gdb-set mi-async off")); 0211 enqueueProtocolHandshake(); 0212 setState(ready, Connected); 0213 } else { 0214 enqueue(makeInitSequence()); 0215 } 0216 issueNextCommandLater(std::nullopt); 0217 } 0218 0219 void GdbBackend::issueNextCommandLater(const std::optional<State> &state) 0220 { 0221 if (state) { 0222 setState(*state); 0223 } 0224 0225 // On startup the gdb prompt will trigger the "nextCommands", 0226 // here we have to trigger it manually. 0227 QTimer::singleShot(0, this, &GdbBackend::issueNextCommand); 0228 } 0229 0230 void GdbBackend::enqueueProtocolHandshake() 0231 { 0232 m_capabilities.family = Unknown; 0233 m_capabilities.execRunStart.reset(); 0234 m_capabilities.threadInfo.reset(); 0235 m_capabilities.breakList.reset(); 0236 m_capabilities.pendingBreakpoints.reset(); 0237 m_capabilities.execJump.reset(); 0238 // "version" only exists in lldb 0239 // data added to recognise this request from anything entered by the user 0240 enqueue(QStringLiteral("version"), QJsonValue(true), CaptureConsole | MuteLog); 0241 enqueue(QStringLiteral("-list-features")); 0242 enqueue(QStringLiteral("-info-gdb-mi-command thread-info")); 0243 enqueue(QStringLiteral("-info-gdb-mi-command break-list")); 0244 enqueue(QStringLiteral("-info-gdb-mi-command exec-jump")); 0245 enqueue(QStringLiteral("-info-gdb-mi-command data-list-changed-registers")); 0246 enqueue(QStringLiteral("-kate-init"), QJsonValue(1)); 0247 } 0248 0249 void GdbBackend::enqueue(const QStringList &commands, bool prepend) 0250 { 0251 if (commands.isEmpty()) { 0252 return; 0253 } 0254 if (prepend) { 0255 for (int n = commands.size() - 1; n >= 0; --n) { 0256 m_nextCommands.prepend({commands[n], std::nullopt, Default}); 0257 } 0258 } else { 0259 for (const auto &cmd : commands) { 0260 enqueue(cmd); 0261 } 0262 } 0263 } 0264 0265 QStringList GdbBackend::makeInitSequence() 0266 { 0267 m_requests.clear(); 0268 QStringList sequence; 0269 // TODO adapt sequence for remote 0270 sequence << QStringLiteral("-file-exec-and-symbols \"%1\"").arg(m_targetConf.executable); 0271 if (m_capabilities.family != LLDB) { 0272 sequence << QStringLiteral("-exec-arguments %1 %2").arg(m_targetConf.arguments, m_ioPipeString); 0273 } else { 0274 sequence << QStringLiteral("-exec-arguments %1").arg(m_targetConf.arguments); 0275 } 0276 sequence << QStringLiteral("-inferior-tty-set /dev/null"); 0277 for (const auto &initPart : m_targetConf.customInit) { 0278 sequence << initPart; 0279 } 0280 if (m_capabilities.breakList.value_or(false)) { 0281 sequence << QStringLiteral("-break-list"); 0282 } 0283 return sequence; 0284 } 0285 0286 void GdbBackend::enqueueThreadInfo() 0287 { 0288 if (!m_inspectable) { 0289 return; 0290 } 0291 if (m_capabilities.threadInfo.value_or(true)) { 0292 enqueue(QStringLiteral("-thread-info")); 0293 } else { 0294 enqueue(QStringLiteral("-thread-list-ids")); 0295 } 0296 } 0297 0298 QStringList GdbBackend::makeRunSequence(bool stop) 0299 { 0300 QStringList sequence; 0301 if (stop) { 0302 if (m_capabilities.execRunStart.value_or(false)) { 0303 sequence << QStringLiteral("-exec-run --start"); 0304 } else { 0305 sequence << QStringLiteral("-break-insert -t main"); 0306 sequence << QStringLiteral("-exec-run"); 0307 } 0308 } else { 0309 sequence << QStringLiteral("-exec-run"); 0310 } 0311 if (m_capabilities.family == GDB) { 0312 sequence << QStringLiteral("-data-evaluate-expression \"setvbuf(stdout, 0, %1, 1024)\"").arg(_IOLBF); 0313 } 0314 return sequence; 0315 } 0316 0317 bool GdbBackend::debuggerRunning() const 0318 { 0319 return (m_state != none); 0320 } 0321 0322 bool GdbBackend::debuggerBusy() const 0323 { 0324 return (m_state == executingCmd) || !m_nextCommands.isEmpty(); 0325 } 0326 0327 int GdbBackend::findFirstBreakpoint(const QUrl &url, int line) const 0328 { 0329 for (auto it = m_breakpointTable.constBegin(); it != m_breakpointTable.constEnd(); ++it) { 0330 if ((url == it.value().file) && (line == it.value().line)) { 0331 return it.key(); 0332 } 0333 } 0334 return -1; 0335 } 0336 0337 QStringList GdbBackend::findAllBreakpoints(const QUrl &url, int line) const 0338 { 0339 QStringList out; 0340 for (auto it = m_breakpointTable.constBegin(); it != m_breakpointTable.constEnd(); ++it) { 0341 if ((url == it.value().file) && (line == it.value().line)) { 0342 out << QString::number(it.key()); 0343 } 0344 } 0345 return out; 0346 } 0347 0348 bool GdbBackend::hasBreakpoint(const QUrl &url, int line) const 0349 { 0350 return findFirstBreakpoint(url, line) >= 0; 0351 } 0352 0353 QString GdbBackend::makeCmdBreakInsert(const QUrl &url, int line, bool pending, bool temporal) const 0354 { 0355 QString flags = temporal ? QLatin1String("-t") : QString(); 0356 if (pending && m_capabilities.pendingBreakpoints.value_or(false)) { 0357 flags += QLatin1String(" -f"); 0358 } 0359 0360 return QStringLiteral("-break-insert %1 %2:%3").arg(flags).arg(url.path()).arg(line); 0361 } 0362 0363 void GdbBackend::toggleBreakpoint(QUrl const &url, int line) 0364 { 0365 if (m_state != ready) { 0366 return; 0367 } 0368 0369 QString cmd; 0370 const auto bpNumbers = findAllBreakpoints(url, line); 0371 if (bpNumbers.empty()) { 0372 cmd = makeCmdBreakInsert(url, line, true); 0373 } else { 0374 // delete all bpoints in that line 0375 cmd = QStringLiteral("-break-delete %1").arg(bpNumbers.join(QLatin1Char(' '))); 0376 } 0377 issueCommand(cmd); 0378 } 0379 0380 void GdbBackend::slotError() 0381 { 0382 Q_EMIT backendError(i18n("Could not start debugger process"), KTextEditor::Message::Error); 0383 } 0384 0385 void GdbBackend::slotReadDebugStdOut() 0386 { 0387 m_outBuffer += m_debugProcess.readAllStandardOutput(); 0388 0389 #ifdef DEBUG_GDBMI 0390 if (!m_outBuffer.isEmpty()) { 0391 Q_EMIT outputText(QStringLiteral("\n***(<gdbmi)\n%1\n***\n").arg(QString::fromLatin1(m_outBuffer))); 0392 } 0393 #endif 0394 do { 0395 int end = gdbmi::GdbmiParser::splitLines(m_outBuffer, false); 0396 0397 if (end < 0) { 0398 break; 0399 } 0400 ++end; 0401 const auto head = m_parser->parseResponse(m_outBuffer.mid(0, end)); 0402 if (!head.error) { 0403 m_outBuffer.remove(0, head.last); 0404 } else { 0405 m_outBuffer.remove(head.last, end); 0406 } 0407 #ifdef DEBUG_GDBMI 0408 if (!m_outBuffer.isEmpty()) { 0409 Q_EMIT outputText(QStringLiteral("\n***(<gdbmi)\n%1\n***\n").arg(QString::fromLatin1(m_outBuffer))); 0410 } 0411 #endif 0412 } while (!m_outBuffer.isEmpty()); 0413 } 0414 0415 void GdbBackend::slotReadDebugStdErr() 0416 { 0417 m_errBuffer += QString::fromLocal8Bit(m_debugProcess.readAllStandardError().data()); 0418 int end = 0; 0419 // add whole lines at a time to the error list 0420 do { 0421 end = m_errBuffer.indexOf(QLatin1Char('\n')); 0422 if (end < 0) { 0423 break; 0424 } 0425 m_errBuffer.remove(0, end + 1); 0426 } while (1); 0427 0428 Q_EMIT outputError(m_errBuffer + QLatin1String("\n")); 0429 } 0430 0431 void GdbBackend::clearDebugLocation() 0432 { 0433 m_debugLocationChanged = true; 0434 Q_EMIT debugLocationChanged(QUrl(), -1); 0435 } 0436 0437 void GdbBackend::slotDebugFinished(int /*exitCode*/, QProcess::ExitStatus status) 0438 { 0439 if (status != QProcess::NormalExit) { 0440 Q_EMIT outputText(i18n("*** gdb exited normally ***") + QLatin1Char('\n')); 0441 clearDebugLocation(); 0442 } 0443 0444 setState(none, Disconnected); 0445 0446 // remove all old breakpoints 0447 for (auto it = m_breakpointTable.constBegin(); it != m_breakpointTable.constEnd(); ++it) { 0448 Q_EMIT breakPointCleared(it.value().file, it.value().line - 1); 0449 } 0450 m_breakpointTable.clear(); 0451 0452 Q_EMIT gdbEnded(); 0453 } 0454 0455 void GdbBackend::movePC(QUrl const &url, int line) 0456 { 0457 if ((m_state == ready) && m_capabilities.execJump.value_or(false)) { 0458 // jump if inferior is running, or run inferrior and stop at start 0459 enqueue(QStringLiteral("-kate-try-run 1"), QJsonValue()); 0460 enqueue(QStringLiteral("-exec-jump %1:%2").arg(url.path()).arg(line)); 0461 issueCommand(makeCmdBreakInsert(url, line, false, true)); 0462 } 0463 } 0464 0465 void GdbBackend::runToCursor(QUrl const &url, int line) 0466 { 0467 if (m_state == ready) { 0468 // continue if inferior running, or run inferior 0469 enqueue(QStringLiteral("-kate-try-run 0"), QJsonValue(QStringLiteral("-exec-continue"))); 0470 issueCommand(makeCmdBreakInsert(url, line, true, true)); 0471 } 0472 } 0473 0474 void GdbBackend::slotInterrupt() 0475 { 0476 if (m_state == executingCmd) { 0477 m_debugLocationChanged = true; 0478 } 0479 if (!m_capabilities.async.value_or(false)) { 0480 const auto pid = m_debugProcess.processId(); 0481 if (pid != 0) { 0482 ::kill(pid, SIGINT); 0483 } 0484 } else { 0485 issueCommand(QStringLiteral("-exec-interrupt")); 0486 } 0487 } 0488 0489 void GdbBackend::slotKill() 0490 { 0491 if (inferiorRunning() && (m_state != ready)) { 0492 slotInterrupt(); 0493 setState(ready); 0494 } 0495 // FIXME couldn't find a replacement for "kill", only -gdb-exit 0496 if (inferiorRunning()) { 0497 issueCommand(QStringLiteral("kill")); 0498 } else if (m_gdbState == Connected) { 0499 issueCommand(QStringLiteral("-gdb-exit")); 0500 } 0501 } 0502 0503 void GdbBackend::slotReRun() 0504 { 0505 resetSession(); 0506 if (inferiorRunning()) { 0507 slotKill(); 0508 } 0509 enqueue(makeRunSequence(false)); 0510 issueNextCommandLater(std::nullopt); 0511 } 0512 0513 void GdbBackend::slotStepInto() 0514 { 0515 issueCommand(QStringLiteral("-kate-try-run 1"), QJsonValue(QStringLiteral("-exec-step"))); 0516 } 0517 0518 void GdbBackend::slotStepOver() 0519 { 0520 issueCommand(QStringLiteral("-kate-try-run 1"), QJsonValue(QStringLiteral("-exec-next"))); 0521 } 0522 0523 void GdbBackend::slotStepOut() 0524 { 0525 issueCommand(QStringLiteral("-kate-try-run 1"), QJsonValue(QStringLiteral("-exec-finish"))); 0526 } 0527 0528 void GdbBackend::slotContinue() 0529 { 0530 issueCommand(QStringLiteral("-kate-try-run 0"), QJsonValue(QStringLiteral("-exec-continue"))); 0531 } 0532 0533 void GdbBackend::processMIRecord(const gdbmi::Record &record) 0534 { 0535 m_errorCounter = 0; 0536 switch (record.category) { 0537 case gdbmi::Record::Status: 0538 break; 0539 case gdbmi::Record::Exec: 0540 processMIExec(record); 0541 break; 0542 case gdbmi::Record::Notify: 0543 processMINotify(record); 0544 break; 0545 case gdbmi::Record::Result: 0546 processMIResult(record); 0547 break; 0548 case gdbmi::Record::Prompt: 0549 processMIPrompt(); 0550 break; 0551 } 0552 } 0553 0554 void GdbBackend::processMIPrompt() 0555 { 0556 if ((m_state != ready) && (m_state != none)) { 0557 return; 0558 } 0559 if (m_captureOutput != Default) { 0560 // the last response has completely been processed at this point 0561 m_captureOutput = Default; 0562 m_capturedOutput.clear(); 0563 } 0564 // we get here after initialization 0565 issueNextCommandLater(ready); 0566 } 0567 0568 static QString formatRecordMessage(const gdbmi::Record &record) 0569 { 0570 return record.value[QLatin1String("msg")].toString() + QLatin1Char('\n'); 0571 } 0572 0573 static QString stoppedThreadsToString(const QJsonValue &value) 0574 { 0575 if (value.isString()) { 0576 return value.toString(); 0577 } else if (value.isArray()) { 0578 QStringList parts; 0579 for (const auto &item : value.toArray()) { 0580 parts << item.toString(); 0581 } 0582 return parts.join(QLatin1String(", ")); 0583 } 0584 return QString(); 0585 } 0586 0587 static QString getFilename(const QJsonObject &item) 0588 { 0589 QString file = item[QLatin1String("fullname")].toString(); 0590 0591 // lldb returns "??" and "??/???" when it is not resolved 0592 if (file.isEmpty() || file.startsWith(QLatin1Char('?'))) { 0593 file = item[QLatin1String("filename")].toString(); 0594 } 0595 0596 if (file.isEmpty() || file.startsWith(QLatin1Char('?'))) { 0597 file = item[QLatin1String("file")].toString(); 0598 } 0599 0600 if (file.startsWith(QLatin1Char('?'))) { 0601 file.clear(); 0602 } 0603 0604 return file; 0605 } 0606 0607 dap::StackFrame GdbBackend::parseFrame(const QJsonObject &object) 0608 { 0609 dap::StackFrame frame; 0610 frame.id = object[QLatin1String("level")].toString().toInt(); 0611 frame.instructionPointerReference = object[QLatin1String("addr")].toString(); 0612 frame.line = object[QLatin1String("line")].toString().toInt(); 0613 frame.column = 0; 0614 0615 auto file = getFilename(object); 0616 if (file.isEmpty()) { 0617 // not really an editable file, we mark with <> in order to avoid an opening attempt 0618 file = QLatin1String("<%1>").arg(object[QLatin1String("from")].toString()); 0619 } else if (!file.contains(QLatin1Char('?'))) { 0620 const auto resolvedFile = resolveFileName(file, true).toLocalFile(); 0621 if (!resolvedFile.isEmpty()) { 0622 file = resolvedFile; 0623 } 0624 } 0625 frame.source = dap::Source(file); 0626 const auto func = object[QLatin1String("func")].toString(); 0627 0628 frame.name = QStringLiteral("%1 at %2:%3").arg(!func.isEmpty() ? func : frame.instructionPointerReference.value()).arg(frame.source->path).arg(frame.line); 0629 0630 return frame; 0631 } 0632 0633 bool GdbBackend::inferiorRunning() const 0634 { 0635 return (m_gdbState == Running) || (m_gdbState == Stopped); 0636 } 0637 0638 void GdbBackend::processMIExec(const gdbmi::Record &record) 0639 { 0640 const auto threadId = stoppedThreadsToString(record.value.value(QLatin1String("thread-id"))); 0641 if (record.resultClass == QLatin1String("running")) { 0642 updateInspectable(false); 0643 // running 0644 setGdbState(Running); 0645 if (threadId == QLatin1String("all")) { 0646 Q_EMIT outputText(printEvent(i18n("all threads running"))); 0647 } else { 0648 Q_EMIT outputText(printEvent(i18n("thread(s) running: %1", threadId))); 0649 } 0650 } else if (record.resultClass == QLatin1String("stopped")) { 0651 const auto stoppedThreads = record.value.value(QLatin1String("stopped-threads")).toString(); 0652 const auto reason = record.value.value(QLatin1String("reason")).toString(); 0653 QStringList text = {i18n("stopped (%1).", reason)}; 0654 if (!threadId.isEmpty()) { 0655 text << QLatin1String(" "); 0656 if (stoppedThreads == QLatin1String("all")) { 0657 text << i18n("Active thread: %1 (all threads stopped).", threadId); 0658 } else { 0659 text << i18n("Active thread: %1.", threadId); 0660 } 0661 } 0662 0663 if (reason.startsWith(QLatin1String("exited"))) { 0664 // stopped by exit 0665 clearDebugLocation(); 0666 updateInspectable(false); 0667 m_nextCommands.clear(); 0668 setGdbState(Connected); 0669 Q_EMIT programEnded(); 0670 } else { 0671 // stopped by another reason 0672 updateInspectable(true); 0673 setGdbState(Stopped); 0674 0675 const auto frame = parseFrame(record.value[QLatin1String("frame")].toObject()); 0676 0677 if (frame.source) { 0678 text << QLatin1String(" ") << i18n("Current frame: %1:%2", frame.source->path, QString::number(frame.line)); 0679 } 0680 m_debugLocationChanged = true; 0681 Q_EMIT debugLocationChanged(resolveFileName(frame.source->path), frame.line - 1); 0682 } 0683 0684 Q_EMIT outputText(printEvent(text.join(QString()))); 0685 } 0686 } 0687 0688 void GdbBackend::processMINotify(const gdbmi::Record &record) 0689 { 0690 if (record.resultClass == QLatin1String("breakpoint-created")) { 0691 responseMIBreakInsert(record); 0692 } else if (record.resultClass == QLatin1String("breakpoint-deleted")) { 0693 notifyMIBreakpointDeleted(record); 0694 } else if (record.resultClass == QLatin1String("breakpoint-modified")) { 0695 notifyMIBreakpointModified(record); 0696 } else { 0697 QString data; 0698 if (record.resultClass.startsWith(QLatin1String("library-"))) { 0699 const auto target = record.value[QLatin1String("target-name")].toString(); 0700 const auto host = record.value[QLatin1String("host-name")].toString(); 0701 0702 if (target == host) { 0703 data = target; 0704 } else { 0705 data = i18n("Host: %1. Target: %1", host, target); 0706 } 0707 } else { 0708 data = QString::fromLocal8Bit(QJsonDocument(record.value).toJson(QJsonDocument::Compact)); 0709 } 0710 0711 const auto msg = QStringLiteral("(%1) %2").arg(record.resultClass).arg(data); 0712 Q_EMIT outputText(printEvent(msg)); 0713 } 0714 } 0715 0716 void GdbBackend::processMIResult(const gdbmi::Record &record) 0717 { 0718 auto reqType = GdbCommand::None; 0719 bool isMI = true; 0720 QStringList args; 0721 std::optional<QJsonValue> commandData = std::nullopt; 0722 if (record.token && m_requests.contains(record.token.value())) { 0723 const auto command = m_requests.take(record.token.value()); 0724 reqType = command.type; 0725 isMI = command.isMachineInterface(); 0726 args = command.arguments; 0727 commandData = command.data; 0728 } 0729 if (isMI && (record.resultClass == QLatin1String("error")) && !(m_captureOutput & MuteLog)) { 0730 Q_EMIT outputError(m_lastCommand + QLatin1String("\n")); 0731 Q_EMIT outputError(formatRecordMessage(record)); 0732 } 0733 0734 bool isReady = true; 0735 0736 switch (reqType) { 0737 case GdbCommand::BreakpointList: 0738 isReady = responseMIBreakpointList(record); 0739 break; 0740 case GdbCommand::ThreadInfo: 0741 isReady = responseMIThreadInfo(record); 0742 break; 0743 case GdbCommand::StackListFrames: 0744 isReady = responseMIStackListFrames(record); 0745 break; 0746 case GdbCommand::StackListVariables: 0747 isReady = responseMIStackListVariables(record); 0748 break; 0749 case GdbCommand::BreakInsert: 0750 isReady = responseMIBreakInsert(record); 0751 break; 0752 case GdbCommand::BreakDelete: 0753 isReady = responseMIBreakDelete(record, args); 0754 break; 0755 case GdbCommand::ListFeatures: 0756 isReady = responseMIListFeatures(record); 0757 break; 0758 case GdbCommand::DataEvaluateExpression: 0759 isReady = responseMIDataEvaluateExpression(record, commandData); 0760 break; 0761 case GdbCommand::Exit: 0762 isReady = responseMIExit(record); 0763 break; 0764 case GdbCommand::Kill: 0765 isReady = responseMIKill(record); 0766 break; 0767 case GdbCommand::InfoGdbMiCommand: 0768 isReady = responseMIInfoGdbCommand(record, args); 0769 break; 0770 case GdbCommand::LldbVersion: 0771 isReady = responseMILldbVersion(record); 0772 break; 0773 case GdbCommand::RegisterNames: 0774 isReady = responseMIRegisterNames(record); 0775 break; 0776 case GdbCommand::RegisterValues: 0777 isReady = responseMIRegisterValues(record); 0778 break; 0779 case GdbCommand::ChangedRegisters: 0780 isReady = responseMIChangedRegisters(record); 0781 break; 0782 case GdbCommand::Continue: 0783 case GdbCommand::Step: 0784 default: 0785 break; 0786 } 0787 if (isReady) { 0788 issueNextCommandLater(ready); 0789 } else { 0790 issueNextCommandLater(std::nullopt); 0791 } 0792 } 0793 0794 void GdbBackend::clearFrames() 0795 { 0796 // clear cached frames 0797 m_stackFrames.clear(); 0798 if (m_queryLocals) { 0799 // request empty panel 0800 Q_EMIT stackFrameInfo(-1, QString()); 0801 } 0802 0803 clearVariables(); 0804 } 0805 0806 void GdbBackend::clearVariables() 0807 { 0808 if (m_queryLocals) { 0809 Q_EMIT scopesInfo(QList<dap::Scope>{}, std::nullopt); 0810 Q_EMIT variableScopeOpened(); 0811 Q_EMIT variableScopeClosed(); 0812 } 0813 } 0814 0815 bool GdbBackend::responseMIThreadInfo(const gdbmi::Record &record) 0816 { 0817 if (record.resultClass == QLatin1String("error")) { 0818 if (!m_capabilities.threadInfo) { 0819 // now we know threadInfo is not supported 0820 m_capabilities.threadInfo = false; 0821 // try again 0822 enqueueThreadInfo(); 0823 } 0824 return true; 0825 } 0826 0827 // clear table 0828 Q_EMIT threadInfo(dap::Thread(-1), false); 0829 0830 int n_threads = 0; 0831 0832 bool ok = false; 0833 const int currentThread = record.value[QLatin1String("current-thread-id")].toString().toInt(&ok); 0834 if (ok) { 0835 // list loaded, but not selected yet 0836 m_currentThread = -1; 0837 } else { 0838 // unexpected, abort 0839 updateInspectable(false); 0840 return true; 0841 } 0842 0843 QString fCollection = QLatin1String("threads"); 0844 QString fId = QLatin1String("id"); 0845 bool hasName = true; 0846 if (!record.value.contains(fCollection)) { 0847 fCollection = QLatin1String("thread-ids"); 0848 fId = QLatin1String("thread-id"); 0849 hasName = false; 0850 } 0851 0852 for (const auto &item : record.value[fCollection].toArray()) { 0853 const auto thread = item.toObject(); 0854 dap::Thread info(thread[fId].toString().toInt()); 0855 if (hasName) { 0856 info.name = thread[QLatin1String("name")].toString(thread[QLatin1String("target-id")].toString()); 0857 } 0858 0859 Q_EMIT threadInfo(info, currentThread == info.id); 0860 ++n_threads; 0861 } 0862 0863 if (m_queryLocals) { 0864 if (n_threads > 0) { 0865 changeThread(currentThread); 0866 } else { 0867 updateInspectable(false); 0868 } 0869 } 0870 0871 return true; 0872 } 0873 0874 bool GdbBackend::responseMIExit(const gdbmi::Record &record) 0875 { 0876 if (record.resultClass != QLatin1String("exit")) { 0877 return true; 0878 } 0879 setState(none, Disconnected); 0880 0881 // not ready 0882 return false; 0883 } 0884 0885 bool GdbBackend::responseMIKill(const gdbmi::Record &record) 0886 { 0887 if (record.resultClass != QLatin1String("done")) { 0888 return true; 0889 } 0890 clearDebugLocation(); 0891 setState(none, Connected); 0892 Q_EMIT programEnded(); 0893 0894 return false; 0895 } 0896 0897 bool GdbBackend::responseMIInfoGdbCommand(const gdbmi::Record &record, const QStringList &args) 0898 { 0899 if (record.resultClass != QLatin1String("done")) { 0900 return true; 0901 } 0902 0903 if (args.size() < 2) { 0904 return true; 0905 } 0906 0907 const auto &command = args[1]; 0908 const bool exists = record.value[QLatin1String("command")].toObject()[QLatin1String("exists")].toString() == QLatin1String("true"); 0909 0910 if (command == QLatin1String("thread-info")) { 0911 m_capabilities.threadInfo = exists; 0912 } else if (command == QLatin1String("break-list")) { 0913 m_capabilities.breakList = exists; 0914 } else if (command == QLatin1String("exec-jump")) { 0915 m_capabilities.execJump = exists; 0916 } else if (command == QLatin1String("data-list-changed-registers")) { 0917 m_capabilities.changedRegisters = exists; 0918 } 0919 0920 return true; 0921 } 0922 0923 bool GdbBackend::responseMILldbVersion(const gdbmi::Record &record) 0924 { 0925 bool isLLDB = false; 0926 if (record.resultClass == QLatin1String("done")) { 0927 isLLDB = std::any_of(m_capturedOutput.cbegin(), m_capturedOutput.cend(), [](const QString &line) { 0928 return line.toLower().contains(QLatin1String("lldb")); 0929 }); 0930 } 0931 m_capabilities.family = isLLDB ? LLDB : GDB; 0932 // lldb-mi inherently uses the asynchronous mode 0933 m_capabilities.async = m_capabilities.family == LLDB; 0934 0935 return true; 0936 } 0937 0938 bool GdbBackend::responseMIStackListFrames(const gdbmi::Record &record) 0939 { 0940 if (record.resultClass == QLatin1String("error")) { 0941 return true; 0942 } 0943 0944 // clear table 0945 clearFrames(); 0946 0947 for (const auto &item : record.value[QLatin1String("stack")].toArray()) { 0948 m_stackFrames << parseFrame(item.toObject()[QLatin1String("frame")].toObject()); 0949 } 0950 0951 m_currentFrame = -1; 0952 m_currentScope.reset(); 0953 informStackFrame(); 0954 0955 if (!m_stackFrames.isEmpty()) { 0956 changeStackFrame(0); 0957 } 0958 0959 return true; 0960 } 0961 0962 bool GdbBackend::responseMIRegisterNames(const gdbmi::Record &record) 0963 { 0964 if (record.resultClass != QLatin1String("done")) { 0965 return true; 0966 } 0967 0968 const auto names = record.value[QLatin1String("register-names")].toArray(); 0969 m_registerNames.clear(); 0970 m_registerNames.reserve(names.size()); 0971 for (const auto &name : names) { 0972 m_registerNames << name.toString().trimmed(); 0973 } 0974 0975 return true; 0976 } 0977 0978 bool GdbBackend::responseMIRegisterValues(const gdbmi::Record &record) 0979 { 0980 if (record.resultClass != QLatin1String("done")) { 0981 return true; 0982 } 0983 0984 Q_EMIT variableScopeOpened(); 0985 for (const auto &item : record.value[QLatin1String("register-values")].toArray()) { 0986 const auto var = item.toObject(); 0987 bool ok = false; 0988 const int regIndex = var[QLatin1String("number")].toString().toInt(&ok); 0989 if (!ok || (regIndex < 0) || (regIndex >= m_registerNames.size())) { 0990 continue; 0991 } 0992 const auto &name = m_registerNames[regIndex]; 0993 if (name.isEmpty()) { 0994 continue; 0995 } 0996 m_variableParser.insertVariable(m_registerNames[regIndex], var[QLatin1String("value")].toString(), QString(), m_changedRegisters.contains(regIndex)); 0997 } 0998 Q_EMIT variableScopeClosed(); 0999 return true; 1000 } 1001 1002 bool GdbBackend::responseMIChangedRegisters(const gdbmi::Record &record) 1003 { 1004 if (record.resultClass != QLatin1String("done")) { 1005 return true; 1006 } 1007 for (const auto &item : record.value[QLatin1String("changed-registers")].toArray()) { 1008 bool ok = false; 1009 const int regIndex = item.toString().toInt(&ok); 1010 if (!ok) { 1011 continue; 1012 } 1013 m_changedRegisters.insert(regIndex); 1014 } 1015 return true; 1016 } 1017 1018 bool GdbBackend::responseMIStackListVariables(const gdbmi::Record &record) 1019 { 1020 if (record.resultClass == QLatin1String("error")) { 1021 return true; 1022 } 1023 1024 Q_EMIT variableScopeOpened(); 1025 for (const auto &item : record.value[QLatin1String("variables")].toArray()) { 1026 const auto var = item.toObject(); 1027 m_variableParser.insertVariable(var[QLatin1String("name")].toString(), var[QLatin1String("value")].toString(), var[QLatin1String("type")].toString()); 1028 } 1029 Q_EMIT variableScopeClosed(); 1030 1031 return true; 1032 } 1033 1034 bool GdbBackend::responseMIListFeatures(const gdbmi::Record &record) 1035 { 1036 if (record.resultClass != QLatin1String("done")) { 1037 return true; 1038 } 1039 1040 for (const auto &item : record.value[QLatin1String("features")].toArray()) { 1041 const auto capability = item.toString(); 1042 if (capability == QLatin1String("exec-run-start-option")) { 1043 // exec-run --start is not reliable in lldb 1044 m_capabilities.execRunStart = m_capabilities.family != LLDB; 1045 } else if (capability == QLatin1String("thread-info")) { 1046 m_capabilities.threadInfo = true; 1047 } else if (capability == QLatin1String("pending-breakpoints")) { 1048 m_capabilities.pendingBreakpoints = true; 1049 } 1050 } 1051 1052 return true; 1053 } 1054 1055 BreakPoint BreakPoint::parse(const QJsonObject &item) 1056 { 1057 const QString f_line = QLatin1String("line"); 1058 1059 BreakPoint breakPoint; 1060 breakPoint.number = item[QLatin1String("number")].toString(QLatin1String("1")).toInt(); 1061 breakPoint.line = item[f_line].toString(QLatin1String("-1")).toInt(); 1062 1063 // file 1064 auto file = getFilename(item); 1065 1066 if ((breakPoint.line < 0) || file.isEmpty()) { 1067 // try original-location 1068 QString pending = item[QLatin1String("original-location")].toString(); 1069 1070 // try pending 1071 const auto f_pending = QLatin1String("pending"); 1072 if (pending.isEmpty()) { 1073 const auto &v_pending = item[f_pending]; 1074 if (v_pending.isArray()) { 1075 const auto values = v_pending.toArray(); 1076 if (!values.isEmpty()) { 1077 pending = values.first().toString(); 1078 } 1079 } else { 1080 pending = v_pending.toString(); 1081 } 1082 } 1083 int sep = pending.lastIndexOf(QLatin1Char(':')); 1084 if (sep > 0) { 1085 if (breakPoint.line < 0) { 1086 breakPoint.line = pending.mid(sep + 1).toInt(); 1087 } 1088 if (file.isEmpty()) { 1089 file = pending.left(sep); 1090 if (file.startsWith(QLatin1Char('?'))) { 1091 file.clear(); 1092 } 1093 } 1094 } 1095 } 1096 1097 if ((breakPoint.line < 0) || file.isEmpty()) { 1098 // try locations 1099 const auto f_locations = QLatin1String("locations"); 1100 if (item.contains(f_locations)) { 1101 for (const auto item_loc : item[f_locations].toArray()) { 1102 const auto loc = item_loc.toObject(); 1103 if (breakPoint.line < 0) { 1104 breakPoint.line = loc[f_line].toString(QLatin1String("-1")).toInt(); 1105 } 1106 if (file.isEmpty()) { 1107 file = getFilename(loc); 1108 if (file.startsWith(QLatin1Char('?'))) { 1109 file.clear(); 1110 } 1111 } 1112 if ((breakPoint.line > -1) && !file.isEmpty()) { 1113 break; 1114 } 1115 } 1116 } 1117 } 1118 1119 if (!file.isEmpty()) { 1120 breakPoint.file = QUrl::fromLocalFile(file); 1121 } 1122 1123 return breakPoint; 1124 } 1125 1126 BreakPoint GdbBackend::parseBreakpoint(const QJsonObject &item) 1127 { 1128 // XXX in a breakpoint with multiple locations, only the first one is considered 1129 BreakPoint breakPoint = BreakPoint::parse(item); 1130 breakPoint.file = resolveFileName(breakPoint.file.toLocalFile()); 1131 1132 return breakPoint; 1133 } 1134 1135 void GdbBackend::insertBreakpoint(const QJsonObject &item) 1136 { 1137 const BreakPoint breakPoint = parseBreakpoint(item); 1138 Q_EMIT breakPointSet(breakPoint.file, breakPoint.line - 1); 1139 m_breakpointTable[breakPoint.number] = std::move(breakPoint); 1140 } 1141 1142 bool GdbBackend::responseMIBreakpointList(const gdbmi::Record &record) 1143 { 1144 if (record.resultClass != QLatin1String("done")) { 1145 return true; 1146 } 1147 1148 Q_EMIT clearBreakpointMarks(); 1149 m_breakpointTable.clear(); 1150 1151 for (const auto item : record.value[QLatin1String("body")].toArray()) { 1152 insertBreakpoint(item.toObject()); 1153 } 1154 1155 return true; 1156 } 1157 1158 bool GdbBackend::responseMIBreakInsert(const gdbmi::Record &record) 1159 { 1160 if (record.resultClass == QLatin1String("error")) { 1161 // cancel pending commands 1162 m_nextCommands.clear(); 1163 return true; 1164 } 1165 1166 const auto bkpt = record.value[QLatin1String("bkpt")].toObject(); 1167 if (bkpt.isEmpty()) { 1168 return true; 1169 } 1170 1171 insertBreakpoint(bkpt); 1172 1173 return true; 1174 } 1175 1176 void GdbBackend::notifyMIBreakpointModified(const gdbmi::Record &record) 1177 { 1178 const auto bkpt = record.value[QLatin1String("bkpt")].toObject(); 1179 if (bkpt.isEmpty()) { 1180 return; 1181 } 1182 1183 const BreakPoint newBp = parseBreakpoint(bkpt); 1184 if (!m_breakpointTable.contains(newBp.number)) { 1185 // may be a pending breakpoint 1186 responseMIBreakInsert(record); 1187 return; 1188 } 1189 1190 const auto &oldBp = m_breakpointTable[newBp.number]; 1191 1192 if ((oldBp.line != newBp.line) || (oldBp.file != newBp.file)) { 1193 const QUrl oldFile = oldBp.file; 1194 const int oldLine = oldBp.line; 1195 m_breakpointTable[newBp.number] = newBp; 1196 if (findFirstBreakpoint(oldFile, oldLine) < 0) { 1197 // this is the last bpoint in this line 1198 Q_EMIT breakPointCleared(oldFile, oldLine - 1); 1199 } 1200 Q_EMIT breakPointSet(newBp.file, newBp.line - 1); 1201 } 1202 } 1203 1204 void GdbBackend::deleteBreakpoint(const int bpNumber) 1205 { 1206 if (!m_breakpointTable.contains(bpNumber)) { 1207 return; 1208 } 1209 const auto bp = m_breakpointTable.take(bpNumber); 1210 if (findFirstBreakpoint(bp.file, bp.line) < 0) { 1211 // this is the last bpoint in this line 1212 Q_EMIT breakPointCleared(bp.file, bp.line - 1); 1213 } 1214 } 1215 1216 void GdbBackend::notifyMIBreakpointDeleted(const gdbmi::Record &record) 1217 { 1218 bool ok = false; 1219 const int bpNumber = record.value[QLatin1String("id")].toString().toInt(&ok); 1220 if (ok) { 1221 deleteBreakpoint(bpNumber); 1222 } 1223 } 1224 1225 bool GdbBackend::responseMIBreakDelete(const gdbmi::Record &record, const QStringList &args) 1226 { 1227 if (record.resultClass != QLatin1String("done")) { 1228 return true; 1229 } 1230 1231 // get breakpoints ids from arguments 1232 if (args.size() < 2) { 1233 return true; 1234 } 1235 1236 for (int idx = 1; idx < args.size(); ++idx) { 1237 bool ok = false; 1238 const int bpNumber = args[idx].toInt(&ok); 1239 if (!ok) { 1240 continue; 1241 } 1242 deleteBreakpoint(bpNumber); 1243 } 1244 1245 return true; 1246 } 1247 1248 QString GdbBackend::makeFrameFlags() const 1249 { 1250 if (!m_currentThread || !m_currentFrame) { 1251 return QString(); 1252 } 1253 1254 if ((*m_currentFrame >= m_stackFrames.size()) || (*m_currentFrame < 0)) { 1255 return QString(); 1256 } 1257 1258 const int frameId = m_stackFrames[*m_currentFrame].id; 1259 1260 return QLatin1String("--thread %1 --frame %2").arg(QString::number(*m_currentThread)).arg(frameId); 1261 } 1262 1263 void GdbBackend::enqueueScopes() 1264 { 1265 if (!m_currentFrame || !m_currentThread) { 1266 return; 1267 } 1268 enqueue(QLatin1String("-data-evaluate-expression %1 \"this\"").arg(makeFrameFlags()), QJsonValue(DATA_EVAL_THIS_CHECK), MuteLog); 1269 } 1270 1271 void GdbBackend::enqueueScopeVariables() 1272 { 1273 if (!m_currentFrame || !m_currentThread) { 1274 return; 1275 } 1276 if (m_pointerThis && (m_currentScope == ThisScope.variablesReference)) { 1277 // request *this 1278 enqueue(QLatin1String("-data-evaluate-expression %1 \"*this\"").arg(makeFrameFlags()), QJsonValue(DATA_EVAL_THIS_DEREF)); 1279 } else if (m_currentScope == RegistersScope.variablesReference) { 1280 if (m_registerNames.isEmpty()) { 1281 enqueue(QLatin1String("-data-list-register-names")); 1282 } 1283 if (m_capabilities.changedRegisters.value_or(false)) { 1284 m_changedRegisters.clear(); 1285 enqueue(QLatin1String("-data-list-changed-registers")); 1286 } 1287 enqueue(QLatin1String("-data-list-register-values --skip-unavailable r")); 1288 } else { 1289 // request locals 1290 enqueue(QLatin1String("-stack-list-variables %1 --all-values").arg(makeFrameFlags())); 1291 } 1292 } 1293 1294 void GdbBackend::responseMIScopes(const gdbmi::Record &record) 1295 { 1296 m_pointerThis = record.resultClass != QLatin1String("error"); 1297 if (!m_inspectable || !m_queryLocals) { 1298 return; 1299 } 1300 1301 QList<dap::Scope> scopes = {LocalScope}; 1302 if (m_pointerThis) { 1303 scopes << ThisScope; 1304 } 1305 scopes << RegistersScope; 1306 1307 const auto activeScope = std::find_if(scopes.cbegin(), scopes.cend(), [this](const auto &scope) { 1308 return !m_watchedScope || (*m_watchedScope == scope.variablesReference); 1309 }); 1310 if (activeScope != scopes.cend()) { 1311 m_watchedScope = activeScope->variablesReference; 1312 } else { 1313 m_watchedScope = scopes[0].variablesReference; 1314 } 1315 1316 m_currentScope.reset(); 1317 1318 if (m_queryLocals) { 1319 // preload variables 1320 Q_EMIT scopesInfo(scopes, m_watchedScope); 1321 slotQueryLocals(true); 1322 } 1323 } 1324 1325 void GdbBackend::responseMIThisScope(const gdbmi::Record &record) 1326 { 1327 if (record.resultClass == QLatin1String("error")) { 1328 return; 1329 } 1330 1331 const auto value = record.value[QStringLiteral("value")].toString(); 1332 const auto parent = dap::Variable(QString(), value, 0); 1333 Q_EMIT variableScopeOpened(); 1334 m_variableParser.insertVariable(QStringLiteral("*this"), value, QString()); 1335 Q_EMIT variableScopeClosed(); 1336 } 1337 1338 bool GdbBackend::responseMIDataEvaluateExpression(const gdbmi::Record &record, const std::optional<QJsonValue> &data) 1339 { 1340 if (data) { 1341 const int mode = data->toInt(-1); 1342 switch (mode) { 1343 case DATA_EVAL_THIS_CHECK: 1344 responseMIScopes(record); 1345 return true; 1346 break; 1347 case DATA_EVAL_THIS_DEREF: 1348 responseMIThisScope(record); 1349 return true; 1350 break; 1351 } 1352 } 1353 1354 if (record.resultClass != QLatin1String("done")) { 1355 return true; 1356 } 1357 1358 QString key; 1359 if (data) { 1360 key = data->toString(QLatin1String("$1")); 1361 } else { 1362 key = QLatin1String("$1"); 1363 } 1364 Q_EMIT outputText(QStringLiteral("%1 = %2\n").arg(key).arg(record.value[QLatin1String("value")].toString())); 1365 1366 return true; 1367 } 1368 1369 void GdbBackend::onMIParserError(const QString &errorMessage) 1370 { 1371 QString message; 1372 ++m_errorCounter; 1373 const bool overflow = m_errorCounter > MAX_RESPONSE_ERRORS; 1374 if (overflow) { 1375 message = i18n("gdb-mi: Could not parse last response: %1. Too many consecutive errors. Shutting down.", errorMessage); 1376 } else { 1377 message = i18n("gdb-mi: Could not parse last response: %1", errorMessage); 1378 } 1379 m_nextCommands.clear(); 1380 Q_EMIT backendError(message, KTextEditor::Message::Error); 1381 1382 if (overflow) { 1383 m_debugProcess.kill(); 1384 } 1385 } 1386 1387 QString GdbBackend::slotPrintVariable(const QString &variable) 1388 { 1389 const QString cmd = QStringLiteral("-data-evaluate-expression \"%1\"").arg(gdbmi::quotedString(variable)); 1390 issueCommand(cmd, QJsonValue(variable)); 1391 return cmd; 1392 } 1393 1394 void GdbBackend::issueCommand(QString const &cmd) 1395 { 1396 issueCommand(cmd, std::nullopt); 1397 } 1398 1399 void GdbBackend::cmdKateInit() 1400 { 1401 // enqueue full init sequence 1402 updateInputReady(!debuggerBusy() && canMove(), true); 1403 enqueue(makeInitSequence(), true); 1404 issueNextCommandLater(std::nullopt); 1405 } 1406 1407 void GdbBackend::cmdKateTryRun(const GdbCommand &command, const QJsonValue &data) 1408 { 1409 // enqueue command if running, or run inferior 1410 // 0 - run & continue 1411 // 1 - run & stop 1412 // command - data[str] 1413 if (!inferiorRunning()) { 1414 bool stop = false; 1415 if (command.arguments.size() > 1) { 1416 bool ok = false; 1417 const int val = command.arguments[1].toInt(&ok); 1418 if (ok) { 1419 stop = val > 0; 1420 } 1421 } 1422 enqueue(makeRunSequence(stop), true); 1423 } else { 1424 const auto altCmd = data.toString(); 1425 if (!altCmd.isEmpty()) { 1426 prepend(data.toString()); 1427 } 1428 } 1429 issueNextCommandLater(std::nullopt); 1430 } 1431 1432 void GdbBackend::issueCommand(const QString &cmd, const std::optional<QJsonValue> &data, uint8_t captureMode) 1433 { 1434 auto command = GdbCommand::parse(cmd); 1435 // macro command 1436 if (data) { 1437 if (command.check(QLatin1String("-kate-init"))) { 1438 cmdKateInit(); 1439 return; 1440 } else if (command.check(QLatin1String("-kate-try-run"))) { 1441 cmdKateTryRun(command, *data); 1442 return; 1443 } 1444 } 1445 // real command 1446 if (m_state == ready) { 1447 setState(executingCmd); 1448 1449 if (data) { 1450 command.data = data; 1451 } 1452 m_captureOutput = captureMode; 1453 QString newCmd; 1454 1455 const bool isMI = command.isMachineInterface(); 1456 1457 if (isMI) { 1458 newCmd = cmd; 1459 } else { 1460 newCmd = QLatin1String("-interpreter-exec console \"%1\"").arg(gdbmi::quotedString(cmd)); 1461 } 1462 1463 if (command.check(QLatin1String("-break-list"))) { 1464 command.type = GdbCommand::BreakpointList; 1465 } else if (command.check(QLatin1String("-exec-continue")) || command.check(QLatin1String("continue")) || command.check(QLatin1String("c")) 1466 || command.check(QLatin1String("fg"))) { 1467 // continue family 1468 command.type = GdbCommand::Continue; 1469 } else if (command.check(QLatin1String("-exec-step")) || command.check(QLatin1String("step")) || command.check(QLatin1String("s")) 1470 || command.check(QLatin1String("-exec-finish")) || command.check(QLatin1String("finish")) || command.check(QLatin1String("fin")) 1471 || command.check(QLatin1String("-exec-next")) || command.check(QLatin1String("next")) || command.check(QLatin1String("n"))) { 1472 command.type = GdbCommand::Step; 1473 } else if (command.check(QLatin1String("-thread-info")) || command.check(QLatin1String("-thread-list-ids"))) { 1474 command.type = GdbCommand::ThreadInfo; 1475 } else if (command.check(QLatin1String("-stack-list-frames"))) { 1476 command.type = GdbCommand::StackListFrames; 1477 } else if (command.check(QLatin1String("-stack-list-variables"))) { 1478 command.type = GdbCommand::StackListVariables; 1479 } else if (command.check(QLatin1String("-break-insert"))) { 1480 command.type = GdbCommand::BreakInsert; 1481 } else if (command.check(QLatin1String("-break-delete"))) { 1482 command.type = GdbCommand::BreakDelete; 1483 } else if (command.check(QLatin1String("-list-features"))) { 1484 command.type = GdbCommand::ListFeatures; 1485 } else if (command.check(QLatin1String("-data-evaluate-expression"))) { 1486 command.type = GdbCommand::DataEvaluateExpression; 1487 } else if (command.check(QLatin1String("-data-list-register-names"))) { 1488 command.type = GdbCommand::RegisterNames; 1489 } else if (command.check(QLatin1String("-data-list-register-values"))) { 1490 command.type = GdbCommand::RegisterValues; 1491 } else if (command.check(QLatin1String("-data-list-changed-registers"))) { 1492 command.type = GdbCommand::ChangedRegisters; 1493 } else if (command.check(QLatin1String("-gdb-exit"))) { 1494 command.type = GdbCommand::Exit; 1495 } else if (command.check(QLatin1String("-info-gdb-mi-command"))) { 1496 command.type = GdbCommand::InfoGdbMiCommand; 1497 } else if (command.check(QLatin1String("kill"))) { 1498 command.type = GdbCommand::Kill; 1499 } else if (command.check(QLatin1String("version")) && data) { 1500 command.type = GdbCommand::LldbVersion; 1501 } 1502 1503 // register the response parsing type 1504 newCmd = QStringLiteral("%1%2").arg(m_seq).arg(newCmd); 1505 m_requests[m_seq++] = command; 1506 1507 m_lastCommand = cmd; 1508 1509 if (!isMI && !(m_captureOutput & MuteLog)) { 1510 Q_EMIT outputText(QStringLiteral("(gdb) %1\n").arg(cmd)); 1511 } 1512 #ifdef DEBUG_GDBMI 1513 Q_EMIT outputText(QStringLiteral("\n***(gdbmi>)\n%1\n***\n").arg(newCmd)); 1514 #endif 1515 m_debugProcess.write(qPrintable(newCmd)); 1516 m_debugProcess.write("\n"); 1517 } 1518 } 1519 1520 void GdbBackend::updateInputReady(bool newState, bool force) 1521 { 1522 // refresh only when the state changed 1523 if (force || (m_lastInputReady != newState)) { 1524 m_lastInputReady = newState; 1525 Q_EMIT readyForInput(newState); 1526 } 1527 } 1528 1529 void GdbBackend::setState(State newState, std::optional<GdbState> newGdbState) 1530 { 1531 m_state = newState; 1532 if (newGdbState) { 1533 m_gdbState = *newGdbState; 1534 } 1535 1536 updateInputReady(!debuggerBusy() && canMove(), true); 1537 } 1538 1539 void GdbBackend::setGdbState(GdbState newState) 1540 { 1541 m_gdbState = newState; 1542 1543 updateInputReady(!debuggerBusy() && canMove(), true); 1544 } 1545 1546 void GdbBackend::issueNextCommand() 1547 { 1548 if (m_state == ready) { 1549 if (!m_nextCommands.empty()) { 1550 const auto cmd = m_nextCommands.takeFirst(); 1551 issueCommand(cmd.command, cmd.data, cmd.captureMode); 1552 } else { 1553 if (m_debugLocationChanged) { 1554 m_debugLocationChanged = false; 1555 if (m_queryLocals) { 1556 slotQueryLocals(true); 1557 issueNextCommand(); 1558 return; 1559 } 1560 } 1561 updateInputReady(!debuggerBusy() && canMove()); 1562 } 1563 } 1564 } 1565 1566 QUrl GdbBackend::resolveFileName(const QString &fileName, bool silent) 1567 { 1568 QFileInfo fInfo = QFileInfo(fileName); 1569 // did we end up with an absolute path or a relative one? 1570 if (fInfo.exists()) { 1571 return QUrl::fromUserInput(fInfo.absoluteFilePath()); 1572 } 1573 1574 if (fInfo.isAbsolute()) { 1575 // we can not do anything just return the fileName 1576 return QUrl::fromUserInput(fileName); 1577 } 1578 1579 // Now try to add the working path 1580 fInfo = QFileInfo(m_targetConf.workDir + fileName); 1581 if (fInfo.exists()) { 1582 return QUrl::fromUserInput(fInfo.absoluteFilePath()); 1583 } 1584 1585 // now try the executable path 1586 fInfo = QFileInfo(QFileInfo(m_targetConf.executable).absolutePath() + fileName); 1587 if (fInfo.exists()) { 1588 return QUrl::fromUserInput(fInfo.absoluteFilePath()); 1589 } 1590 1591 for (const QString &srcPath : qAsConst(m_targetConf.srcPaths)) { 1592 fInfo = QFileInfo(srcPath + QDir::separator() + fileName); 1593 if (fInfo.exists()) { 1594 return QUrl::fromUserInput(fInfo.absoluteFilePath()); 1595 } 1596 } 1597 1598 // we can not do anything just return the fileName 1599 if (!silent) { 1600 Q_EMIT sourceFileNotFound(fileName); 1601 } 1602 return QUrl::fromUserInput(fileName); 1603 } 1604 1605 void GdbBackend::processMIStreamOutput(const gdbmi::StreamOutput &output) 1606 { 1607 switch (output.channel) { 1608 case gdbmi::StreamOutput::Console: 1609 if (m_captureOutput & CaptureConsole) { 1610 m_capturedOutput << output.message; 1611 } 1612 Q_EMIT outputText(output.message); 1613 break; 1614 case gdbmi::StreamOutput::Log: 1615 if (!(m_captureOutput & ~MuteLog)) { 1616 Q_EMIT outputError(output.message); 1617 } 1618 break; 1619 case gdbmi::StreamOutput::Output: 1620 Q_EMIT debuggeeOutput(dap::Output(output.message, dap::Output::Category::Stdout)); 1621 break; 1622 } 1623 } 1624 1625 void GdbBackend::informStackFrame() 1626 { 1627 if (!m_queryLocals) { 1628 return; 1629 } 1630 1631 int level = 0; 1632 1633 for (const auto &frame : m_stackFrames) { 1634 // emit stackFrameInfo 1635 // name at source:line 1636 QString info = frame.name; 1637 if (frame.source) { 1638 info = QStringLiteral("%1 at %2:%3").arg(info).arg(frame.source->path).arg(frame.line); 1639 } 1640 1641 Q_EMIT stackFrameInfo(level, info); 1642 1643 ++level; 1644 } 1645 Q_EMIT stackFrameInfo(-1, QString()); 1646 } 1647 1648 void GdbBackend::slotQueryLocals(bool query) 1649 { 1650 if (!debuggerRunning()) { 1651 return; 1652 } 1653 m_queryLocals = query; 1654 1655 #ifdef DEBUG_GDBMI 1656 Q_EMIT outputText(QStringLiteral("\n***(gdbmi^)\nSLOT QUERY LOCALS INIT %1\n***\n").arg(query)); 1657 #endif 1658 1659 if (!query) { 1660 return; 1661 } 1662 1663 // !current thread -> no threads; current thread -> threads 1664 if (!m_currentThread) { 1665 // get threads 1666 #ifdef DEBUG_GDBMI 1667 Q_EMIT outputText(QStringLiteral("\n***(gdbmi^)\nSLOT QUERY LOCALS THREAD INFO %1\n***\n").arg(query)); 1668 #endif 1669 enqueueThreadInfo(); 1670 issueNextCommandLater(std::nullopt); 1671 } else if (!m_currentFrame) { 1672 // get frames 1673 #ifdef DEBUG_GDBMI 1674 Q_EMIT outputText(QStringLiteral("\n***(gdbmi^)\nSLOT QUERY LOCALS CHANGE THREAD %1\n***\n").arg(query)); 1675 #endif 1676 changeThread(*m_currentThread); 1677 } else if (!m_watchedScope) { 1678 // get scopes 1679 #ifdef DEBUG_GDBMI 1680 Q_EMIT outputText(QStringLiteral("\n***(gdbmi^)\nSLOT QUERY LOCALS CHANGE STACK FRAME %1\n***\n").arg(query)); 1681 #endif 1682 changeStackFrame(*m_currentFrame); 1683 } else { 1684 // get variables 1685 #ifdef DEBUG_GDBMI 1686 Q_EMIT outputText(QStringLiteral("\n***(gdbmi^)\nSLOT QUERY LOCALS CHANGE SCOPE %1\n***\n").arg(query)); 1687 #endif 1688 changeScope(*m_watchedScope); 1689 } 1690 } 1691 1692 QString GdbBackend::targetName() const 1693 { 1694 return m_targetConf.targetName; 1695 } 1696 1697 void GdbBackend::setFileSearchPaths(const QStringList &paths) 1698 { 1699 m_targetConf.srcPaths = paths; 1700 } 1701 1702 void GdbBackend::updateInspectable(bool inspectable) 1703 { 1704 m_inspectable = inspectable; 1705 m_currentThread.reset(); 1706 m_currentFrame.reset(); 1707 m_currentScope.reset(); 1708 clearFrames(); 1709 Q_EMIT scopesInfo(QList<dap::Scope>{}, std::nullopt); 1710 } 1711 1712 void GdbBackend::changeStackFrame(int index) 1713 { 1714 #ifdef DEBUG_GDBMI 1715 Q_EMIT outputText(QStringLiteral("\n***(gdbmi^)\nCHANGE FRAME %1\n***\n").arg(index)); 1716 #endif 1717 if (!debuggerRunning() || !m_inspectable) { 1718 return; 1719 } 1720 if (!m_currentThread) { 1721 // unexpected state 1722 updateInspectable(false); 1723 return; 1724 } 1725 if ((m_stackFrames.size() < index) || (index < 0)) { 1726 // frame not found in stack 1727 return; 1728 } 1729 1730 if (m_currentFrame && (*m_currentFrame == index)) { 1731 // already loaded 1732 return; 1733 } 1734 1735 m_currentFrame = index; 1736 1737 const auto &frame = m_stackFrames[index]; 1738 if (frame.source) { 1739 Q_EMIT debugLocationChanged(resolveFileName(frame.source->path), frame.line - 1); 1740 } 1741 1742 Q_EMIT stackFrameChanged(index); 1743 1744 m_currentScope.reset(); 1745 enqueueScopes(); 1746 issueNextCommandLater(std::nullopt); 1747 } 1748 1749 void GdbBackend::changeThread(int index) 1750 { 1751 #ifdef DEBUG_GDBMI 1752 Q_EMIT outputText(QStringLiteral("\n***(gdbmi^)\nCHANGE THREAD %1\n***\n").arg(index)); 1753 #endif 1754 if (!debuggerRunning() || !m_inspectable) { 1755 return; 1756 } 1757 if (!m_queryLocals) { 1758 return; 1759 } 1760 if (*m_currentThread && (*m_currentThread == index)) { 1761 // already loaded 1762 return; 1763 } 1764 m_currentThread = index; 1765 1766 enqueue(QStringLiteral("-stack-list-frames --thread %1").arg(index)); 1767 issueNextCommandLater(std::nullopt); 1768 } 1769 1770 void GdbBackend::changeScope(int scopeId) 1771 { 1772 #ifdef DEBUG_GDBMI 1773 Q_EMIT outputText(QStringLiteral("\n***(gdbmi^)\nCHANGE SCOPE %1\n***\n").arg(scopeId)); 1774 #endif 1775 if (!debuggerRunning() || !m_inspectable) { 1776 return; 1777 } 1778 1779 m_watchedScope = scopeId; 1780 1781 if (m_currentScope && (*m_currentScope == scopeId)) { 1782 // already loaded 1783 return; 1784 } 1785 1786 m_currentScope = m_watchedScope; 1787 1788 if (m_queryLocals) { 1789 enqueueScopeVariables(); 1790 issueNextCommandLater(std::nullopt); 1791 } 1792 } 1793 1794 #include "moc_gdbbackend.cpp"