File indexing completed on 2024-04-28 04:38:35
0001 /* 0002 SPDX-FileCopyrightText: 1999-2001 John Birch <jbb@kdevelop.org> 0003 SPDX-FileCopyrightText: 2001 Bernd Gehrmann <bernd@kdevelop.org> 0004 SPDX-FileCopyrightText: 2006 Vladimir Prus <ghost@cs.msu.su> 0005 SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org> 0006 SPDX-FileCopyrightText: 2009 Niko Sams <niko.sams@gmail.com> 0007 SPDX-FileCopyrightText: 2016 Aetf <aetf@unlimitedcodeworks.xyz> 0008 0009 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0010 */ 0011 0012 #include "midebugsession.h" 0013 0014 #include "debuglog.h" 0015 #include "midebugger.h" 0016 #include "midebuggerplugin.h" 0017 #include "mivariable.h" 0018 #include "mi/mi.h" 0019 #include "mi/micommand.h" 0020 #include "mi/micommandqueue.h" 0021 #include "stty.h" 0022 0023 #include <debugger/interfaces/iframestackmodel.h> 0024 #include <execute/iexecuteplugin.h> 0025 #include <interfaces/icore.h> 0026 #include <interfaces/idocument.h> 0027 #include <interfaces/idocumentcontroller.h> 0028 #include <interfaces/ilaunchconfiguration.h> 0029 #include <interfaces/iuicontroller.h> 0030 #include <sublime/message.h> 0031 #include <util/processlinemaker.h> 0032 0033 #include <KConfigGroup> 0034 #include <KLocalizedString> 0035 #include <KSharedConfig> 0036 #include <KShell> 0037 0038 #include <QApplication> 0039 #include <QFileInfo> 0040 #include <QMetaEnum> 0041 #include <QRegularExpression> 0042 #include <QUrl> 0043 #include <QTimer> 0044 0045 using namespace KDevelop; 0046 using namespace KDevMI; 0047 using namespace KDevMI::MI; 0048 0049 namespace { 0050 constexpr DBGStateFlags notStartedDebuggerFlags{s_dbgNotStarted | s_appNotStarted}; 0051 } 0052 0053 MIDebugSession::MIDebugSession(MIDebuggerPlugin *plugin) 0054 : m_procLineMaker(new ProcessLineMaker(this)) 0055 , m_commandQueue(new CommandQueue) 0056 , m_debuggerState{notStartedDebuggerFlags} 0057 , m_tty(nullptr) 0058 , m_plugin(plugin) 0059 { 0060 qCDebug(DEBUGGERCOMMON) << "Creating" << this; 0061 0062 // setup signals 0063 connect(m_procLineMaker, &ProcessLineMaker::receivedStdoutLines, 0064 this, &MIDebugSession::inferiorStdoutLines); 0065 connect(m_procLineMaker, &ProcessLineMaker::receivedStderrLines, 0066 this, &MIDebugSession::inferiorStderrLines); 0067 0068 // forward tty output to process line maker 0069 connect(this, &MIDebugSession::inferiorTtyStdout, 0070 m_procLineMaker, &ProcessLineMaker::slotReceivedStdout); 0071 connect(this, &MIDebugSession::inferiorTtyStderr, 0072 m_procLineMaker, &ProcessLineMaker::slotReceivedStderr); 0073 0074 // FIXME: see if this still works 0075 //connect(statusBarIndicator, SIGNAL(doubleClicked()), 0076 // controller, SLOT(explainDebuggerStatus())); 0077 0078 // FIXME: reimplement / re-enable 0079 //connect(this, SIGNAL(addWatchVariable(QString)), controller->variables(), SLOT(slotAddWatchVariable(QString))); 0080 //connect(this, SIGNAL(evaluateExpression(QString)), controller->variables(), SLOT(slotEvaluateExpression(QString))); 0081 } 0082 0083 MIDebugSession::~MIDebugSession() 0084 { 0085 qCDebug(DEBUGGERCOMMON) << "Destroying" << this; 0086 // Deleting the session involves shutting down gdb nicely. 0087 // When were attached to a process, we must first detach so that the process 0088 // can continue running as it was before being attached. gdb is quite slow to 0089 // detach from a process, so we must process events within here to get a "clean" 0090 // shutdown. 0091 if (!debuggerStateIsOn(s_dbgNotStarted)) { 0092 stopDebugger(); 0093 } 0094 } 0095 0096 IDebugSession::DebuggerState MIDebugSession::state() const 0097 { 0098 return m_sessionState; 0099 } 0100 0101 QMap<QString, MIVariable*> & MIDebugSession::variableMapping() 0102 { 0103 return m_allVariables; 0104 } 0105 0106 MIVariable* MIDebugSession::findVariableByVarobjName(const QString &varobjName) const 0107 { 0108 if (m_allVariables.count(varobjName) == 0) 0109 return nullptr; 0110 return m_allVariables.value(varobjName); 0111 } 0112 0113 void MIDebugSession::markAllVariableDead() 0114 { 0115 for (auto* variable : qAsConst(m_allVariables)) { 0116 variable->markAsDead(); 0117 } 0118 m_allVariables.clear(); 0119 } 0120 0121 bool MIDebugSession::restartAvaliable() const 0122 { 0123 if (debuggerStateIsOn(s_attached) || debuggerStateIsOn(s_core)) { 0124 return false; 0125 } else { 0126 return true; 0127 } 0128 } 0129 0130 bool MIDebugSession::startDebugger(ILaunchConfiguration *cfg) 0131 { 0132 qCDebug(DEBUGGERCOMMON) << "Starting new debugger instance"; 0133 if (m_debugger) { 0134 qCWarning(DEBUGGERCOMMON) << "m_debugger object still exists"; 0135 delete m_debugger; 0136 m_debugger = nullptr; 0137 } 0138 m_debugger = createDebugger(); 0139 m_debugger->setParent(this); 0140 0141 // output signals 0142 connect(m_debugger, &MIDebugger::applicationOutput, this, [this](const QString& output) { 0143 auto lines = output.split(QRegularExpression(QStringLiteral("[\r\n]")), Qt::SkipEmptyParts); 0144 for (auto& line : lines) { 0145 int p = line.length(); 0146 while (p >= 1 && (line[p - 1] == QLatin1Char('\r') || line[p - 1] == QLatin1Char('\n'))) { 0147 p--; 0148 } 0149 if (p != line.length()) 0150 line.truncate(p); 0151 } 0152 emit inferiorStdoutLines(lines); 0153 }); 0154 connect(m_debugger, &MIDebugger::userCommandOutput, this, &MIDebugSession::debuggerUserCommandOutput); 0155 connect(m_debugger, &MIDebugger::internalCommandOutput, this, &MIDebugSession::debuggerInternalCommandOutput); 0156 connect(m_debugger, &MIDebugger::debuggerInternalOutput, this, &MIDebugSession::debuggerInternalOutput); 0157 0158 // state signals 0159 connect(m_debugger, &MIDebugger::programStopped, this, &MIDebugSession::inferiorStopped); 0160 connect(m_debugger, &MIDebugger::programRunning, this, &MIDebugSession::inferiorRunning); 0161 0162 // internal handlers 0163 connect(m_debugger, &MIDebugger::ready, this, &MIDebugSession::slotDebuggerReady); 0164 connect(m_debugger, &MIDebugger::exited, this, &MIDebugSession::slotDebuggerExited); 0165 connect(m_debugger, &MIDebugger::programStopped, this, &MIDebugSession::slotInferiorStopped); 0166 connect(m_debugger, &MIDebugger::programRunning, this, &MIDebugSession::slotInferiorRunning); 0167 connect(m_debugger, &MIDebugger::notification, this, &MIDebugSession::processNotification); 0168 0169 0170 // start the debugger. Do this after connecting all signals so that initial 0171 // debugger output, and important events like the debugger died are reported. 0172 QStringList extraArguments; 0173 if (!m_sourceInitFile) 0174 extraArguments << QStringLiteral("--nx"); 0175 0176 auto config = cfg ? cfg->config() 0177 // FIXME: this is only used when attachToProcess or examineCoreFile. 0178 // Change to use a global launch configuration when calling 0179 : KConfigGroup(KSharedConfig::openConfig(), "GDB Config"); 0180 0181 if (!m_debugger->start(config, extraArguments)) { 0182 // debugger failed to start, ensure debugger and session state are correctly updated. 0183 setDebuggerStateOn(s_dbgFailedStart); 0184 return false; 0185 } 0186 0187 // FIXME: here, we should wait until the debugger is up and waiting for input. 0188 // Then, clear s_dbgNotStarted 0189 // It's better to do this right away so that the state bit is always correct. 0190 setDebuggerStateOff(s_dbgNotStarted); 0191 0192 // Initialise debugger. At this stage debugger is sitting wondering what to do, 0193 // and to whom. 0194 initializeDebugger(); 0195 0196 qCDebug(DEBUGGERCOMMON) << "Debugger instance started"; 0197 return true; 0198 } 0199 0200 bool MIDebugSession::startDebugging(ILaunchConfiguration* cfg, IExecutePlugin* iexec) 0201 { 0202 qCDebug(DEBUGGERCOMMON) << "Starting new debug session"; 0203 Q_ASSERT(cfg); 0204 Q_ASSERT(iexec); 0205 0206 // Ensure debugger is started first 0207 if (debuggerStateIsOn(s_appNotStarted)) { 0208 emit showMessage(i18n("Running program"), 1000); 0209 } 0210 0211 if (debuggerStateIsOn(s_dbgNotStarted)) { 0212 if (!startDebugger(cfg)) 0213 return false; 0214 } 0215 0216 if (debuggerStateIsOn(s_shuttingDown)) { 0217 qCDebug(DEBUGGERCOMMON) << "Tried to run when debugger shutting down"; 0218 return false; 0219 } 0220 0221 // Only dummy err here, actual errors have been checked already in the job and we don't get here if there were any 0222 QString err; 0223 QString executable = iexec->executable(cfg, err).toLocalFile(); 0224 configInferior(cfg, iexec, executable); 0225 0226 // Set up the tty for the inferior 0227 bool config_useExternalTerminal = iexec->useTerminal(cfg); 0228 QString config_ternimalName = iexec->terminal(cfg); 0229 if (!config_ternimalName.isEmpty()) { 0230 // the external terminal cmd contains additional arguments, just get the terminal name 0231 config_ternimalName = KShell::splitArgs(config_ternimalName).first(); 0232 } 0233 0234 m_tty.reset(new STTY(config_useExternalTerminal, config_ternimalName)); 0235 if (!config_useExternalTerminal) { 0236 connect(m_tty.get(), &STTY::OutOutput, this, &MIDebugSession::inferiorTtyStdout); 0237 connect(m_tty.get(), &STTY::ErrOutput, this, &MIDebugSession::inferiorTtyStderr); 0238 } 0239 QString tty(m_tty->getSlave()); 0240 #ifndef Q_OS_WIN 0241 if (tty.isEmpty()) { 0242 auto* const message = new Sublime::Message(m_tty->lastError(), Sublime::Message::Error); 0243 ICore::self()->uiController()->postMessage(message); 0244 0245 m_tty.reset(nullptr); 0246 0247 qCDebug(DEBUGGERCOMMON) << "no TTY slave, stopping debugger"; 0248 // Cannot simply call stopDebugger() here, because for some reason the interruptDebugger() call in 0249 // stopDebugger() makes the "Debugger Crashed" error dialog appear. KDevelop itself crashes then 0250 // unless the error dialog is dismissed promptly - before the GDB process is killed at the 5-second 0251 // timeout set up in stopDebugger(). Duplicate the relevant parts of stopDebugger() definition here. 0252 m_commandQueue->clear(); 0253 setDebuggerStateOn(s_shuttingDown); 0254 addGdbExitCommand(); 0255 emit reset(); 0256 return false; 0257 } 0258 #endif 0259 addCommand(InferiorTtySet, tty); 0260 0261 // Change the working directory to the correct one 0262 QString dir = iexec->workingDirectory(cfg).toLocalFile(); 0263 if (dir.isEmpty()) { 0264 dir = QFileInfo(executable).absolutePath(); 0265 } 0266 addCommand(EnvironmentCd, QLatin1Char('"') + dir + QLatin1Char('"')); 0267 0268 // Set the run arguments 0269 QStringList arguments = iexec->arguments(cfg, err); 0270 if (!arguments.isEmpty()) 0271 addCommand(ExecArguments, KShell::joinArgs(arguments)); 0272 0273 // Do other debugger specific config options and actually start the inferior program 0274 if (!execInferior(cfg, iexec, executable)) { 0275 return false; 0276 } 0277 0278 QString config_startWith = cfg->config().readEntry(Config::StartWithEntry, QStringLiteral("ApplicationOutput")); 0279 if (config_startWith == QLatin1String("GdbConsole")) { 0280 emit raiseDebuggerConsoleViews(); 0281 } else if (config_startWith == QLatin1String("FrameStack")) { 0282 emit raiseFramestackViews(); 0283 } else { 0284 // ApplicationOutput is raised in DebugJob (by setting job to Verbose/Silent) 0285 } 0286 0287 return true; 0288 } 0289 0290 // FIXME: use same configuration process as startDebugging 0291 bool MIDebugSession::attachToProcess(int pid) 0292 { 0293 qCDebug(DEBUGGERCOMMON) << "Attach to process" << pid; 0294 0295 emit showMessage(i18n("Attaching to process %1", pid), 1000); 0296 0297 if (debuggerStateIsOn(s_dbgNotStarted)) { 0298 // FIXME: use global launch configuration rather than nullptr 0299 if (!startDebugger(nullptr)) { 0300 return false; 0301 } 0302 } 0303 0304 setDebuggerStateOn(s_attached); 0305 0306 //set current state to running, after attaching we will get *stopped response 0307 setDebuggerStateOn(s_appRunning); 0308 0309 addCommand(TargetAttach, QString::number(pid), 0310 this, &MIDebugSession::handleTargetAttach, 0311 CmdHandlesError); 0312 0313 addCommand(std::make_unique<SentinelCommand>(breakpointController(), &MIBreakpointController::initSendBreakpoints)); 0314 0315 raiseEvent(connected_to_program); 0316 0317 emit raiseFramestackViews(); 0318 0319 return true; 0320 } 0321 0322 void MIDebugSession::handleTargetAttach(const MI::ResultRecord& r) 0323 { 0324 if (r.reason == QLatin1String("error")) { 0325 const QString messageText = 0326 i18n("<b>Could not attach debugger:</b><br />")+ 0327 r[QStringLiteral("msg")].literal(); 0328 auto* message = new Sublime::Message(messageText, Sublime::Message::Error); 0329 ICore::self()->uiController()->postMessage(message); 0330 stopDebugger(); 0331 } 0332 } 0333 0334 bool MIDebugSession::examineCoreFile(const QUrl &debugee, const QUrl &coreFile) 0335 { 0336 emit showMessage(i18n("Examining core file %1", coreFile.toLocalFile()), 1000); 0337 0338 if (debuggerStateIsOn(s_dbgNotStarted)) { 0339 // FIXME: use global launch configuration rather than nullptr 0340 if (!startDebugger(nullptr)) { 0341 return false; 0342 } 0343 } 0344 0345 // FIXME: support non-local URLs 0346 if (!loadCoreFile(nullptr, debugee.toLocalFile(), coreFile.toLocalFile())) { 0347 return false; 0348 } 0349 0350 raiseEvent(program_state_changed); 0351 0352 return true; 0353 } 0354 0355 #define ENUM_NAME(o,e,v) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).valueToKey((v))) 0356 void MIDebugSession::setSessionState(DebuggerState state) 0357 { 0358 qCDebug(DEBUGGERCOMMON) << "Session state changed to" 0359 << ENUM_NAME(IDebugSession, DebuggerState, state) 0360 << "(" << state << ")"; 0361 if (state != m_sessionState) { 0362 m_sessionState = state; 0363 emit stateChanged(state); 0364 } 0365 } 0366 0367 bool MIDebugSession::debuggerStateIsOn(DBGStateFlags state) const 0368 { 0369 return m_debuggerState & state; 0370 } 0371 0372 DBGStateFlags MIDebugSession::debuggerState() const 0373 { 0374 return m_debuggerState; 0375 } 0376 0377 void MIDebugSession::setDebuggerStateOn(DBGStateFlags stateOn) 0378 { 0379 DBGStateFlags oldState = m_debuggerState; 0380 m_debuggerState |= stateOn; 0381 handleDebuggerStateChange(oldState, m_debuggerState); 0382 } 0383 0384 void MIDebugSession::setDebuggerStateOff(DBGStateFlags stateOff) 0385 { 0386 DBGStateFlags oldState = m_debuggerState; 0387 m_debuggerState &= ~stateOff; 0388 handleDebuggerStateChange(oldState, m_debuggerState); 0389 } 0390 0391 void MIDebugSession::setDebuggerState(DBGStateFlags newState) 0392 { 0393 DBGStateFlags oldState = m_debuggerState; 0394 m_debuggerState = newState; 0395 handleDebuggerStateChange(oldState, m_debuggerState); 0396 } 0397 0398 void MIDebugSession::handleDebuggerStateChange(DBGStateFlags oldState, DBGStateFlags newState) 0399 { 0400 QString message; 0401 0402 DebuggerState oldSessionState = state(); 0403 DebuggerState newSessionState = oldSessionState; 0404 DBGStateFlags changedState = oldState ^ newState; 0405 0406 if (newState & s_dbgNotStarted) { 0407 if (changedState & s_dbgNotStarted) { 0408 message = i18n("Debugger stopped"); 0409 emit finished(); 0410 } 0411 if (oldSessionState != NotStartedState || newState & s_dbgFailedStart) { 0412 newSessionState = EndedState; 0413 } 0414 } else { 0415 if (newState & s_appNotStarted) { 0416 if (oldSessionState == NotStartedState || oldSessionState == StartingState) { 0417 newSessionState = StartingState; 0418 } else { 0419 newSessionState = StoppedState; 0420 } 0421 } else if (newState & s_programExited) { 0422 if (changedState & s_programExited) { 0423 message = i18n("Process exited"); 0424 } 0425 newSessionState = StoppedState; 0426 } else if (newState & s_appRunning) { 0427 if (changedState & s_appRunning) { 0428 message = i18n("Application is running"); 0429 } 0430 newSessionState = ActiveState; 0431 } else { 0432 if (changedState & s_appRunning) { 0433 message = i18n("Application is paused"); 0434 } 0435 newSessionState = PausedState; 0436 } 0437 } 0438 0439 // And now? :-) 0440 qCDebug(DEBUGGERCOMMON) << "Debugger state changed to:" << newState << message << "- changes:" << changedState; 0441 0442 if (!message.isEmpty()) 0443 emit showMessage(message, 3000); 0444 0445 emit debuggerStateChanged(oldState, newState); 0446 0447 // must be last, since it can lead to deletion of the DebugSession 0448 if (newSessionState != oldSessionState) { 0449 setSessionState(newSessionState); 0450 } 0451 } 0452 0453 void MIDebugSession::restartDebugger() 0454 { 0455 // We implement restart as kill + slotRun, as opposed as plain "run" 0456 // command because kill + slotRun allows any special logic in slotRun 0457 // to apply for restart. 0458 // 0459 // That includes: 0460 // - checking for out-of-date project 0461 // - special setup for remote debugging. 0462 // 0463 // Had we used plain 'run' command, restart for remote debugging simply 0464 // would not work. 0465 if (!debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown)) { 0466 // FIXME: s_dbgBusy or m_debugger->isReady()? 0467 if (debuggerStateIsOn(s_dbgBusy)) { 0468 interruptDebugger(); 0469 } 0470 // The -exec-abort is not implemented in gdb 0471 // addCommand(ExecAbort); 0472 addCommand(NonMI, QStringLiteral("kill")); 0473 } 0474 run(); 0475 } 0476 0477 void MIDebugSession::stopDebugger() 0478 { 0479 if (debuggerStateIsOn(s_dbgNotStarted)) { 0480 qCDebug(DEBUGGERCOMMON) << "Stopping debugger when it's not started"; 0481 if (debuggerState() != notStartedDebuggerFlags) { 0482 setDebuggerState(notStartedDebuggerFlags); 0483 } 0484 // Transition into EndedState to let DebugController destroy this session. 0485 if (state() != EndedState) { 0486 setSessionState(EndedState); 0487 } 0488 return; 0489 } 0490 0491 m_commandQueue->clear(); 0492 0493 qCDebug(DEBUGGERCOMMON) << "try stopping debugger"; 0494 if (debuggerStateIsOn(s_shuttingDown) || !m_debugger) 0495 return; 0496 0497 setDebuggerStateOn(s_shuttingDown); 0498 qCDebug(DEBUGGERCOMMON) << "stopping debugger"; 0499 0500 // Get debugger's attention if it's busy. We need debugger to be at the 0501 // command line so we can stop it. 0502 if (!m_debugger->isReady()) { 0503 qCDebug(DEBUGGERCOMMON) << "debugger busy on shutdown - interrupting"; 0504 interruptDebugger(); 0505 } 0506 0507 // If the app is attached then we release it here. This doesn't stop 0508 // the app running. 0509 if (debuggerStateIsOn(s_attached)) { 0510 addCommand(TargetDetach); 0511 emit debuggerUserCommandOutput(QStringLiteral("(gdb) detach\n")); 0512 } 0513 0514 // Now try to stop debugger running. 0515 addGdbExitCommand(); 0516 0517 // We cannot wait forever, kill gdb after 5 seconds if it's not yet quit 0518 QTimer::singleShot(5000, this, [this]() { 0519 if (!debuggerStateIsOn(s_programExited) && debuggerStateIsOn(s_shuttingDown)) { 0520 qCDebug(DEBUGGERCOMMON) << "debugger not shutdown - killing"; 0521 killDebuggerImpl(); 0522 } 0523 }); 0524 0525 emit reset(); 0526 } 0527 0528 void MIDebugSession::addGdbExitCommand() 0529 { 0530 addCommand(GdbExit); 0531 emit debuggerUserCommandOutput(QStringLiteral("(gdb) quit")); 0532 } 0533 0534 void MIDebugSession::killDebuggerNow() 0535 { 0536 if (!debuggerStateIsOn(s_dbgNotStarted)) { 0537 qCDebug(DEBUGGERCOMMON) << "killing debugger now"; 0538 killDebuggerImpl(); 0539 } 0540 } 0541 0542 void MIDebugSession::killDebuggerImpl() 0543 { 0544 Q_ASSERT(m_debugger); 0545 m_debugger->kill(); 0546 setDebuggerState(notStartedDebuggerFlags); 0547 raiseEvent(debugger_exited); 0548 } 0549 0550 void MIDebugSession::interruptDebugger() 0551 { 0552 Q_ASSERT(m_debugger); 0553 0554 // Explicitly send the interrupt in case something went wrong with the usual 0555 // ensureGdbListening logic. 0556 m_debugger->interrupt(); 0557 addCommand(ExecInterrupt, QString(), CmdInterrupt); 0558 } 0559 0560 void MIDebugSession::run() 0561 { 0562 if (debuggerStateIsOn(s_appNotStarted|s_dbgNotStarted|s_shuttingDown)) 0563 return; 0564 0565 addCommand(MI::ExecContinue, QString(), CmdMaybeStartsRunning); 0566 } 0567 0568 void MIDebugSession::runToCursor() 0569 { 0570 if (IDocument* doc = ICore::self()->documentController()->activeDocument()) { 0571 KTextEditor::Cursor cursor = doc->cursorPosition(); 0572 if (cursor.isValid()) 0573 runUntil(doc->url(), cursor.line() + 1); 0574 } 0575 } 0576 0577 void MIDebugSession::jumpToCursor() 0578 { 0579 if (IDocument* doc = ICore::self()->documentController()->activeDocument()) { 0580 KTextEditor::Cursor cursor = doc->cursorPosition(); 0581 if (cursor.isValid()) 0582 jumpTo(doc->url(), cursor.line() + 1); 0583 } 0584 } 0585 0586 void MIDebugSession::stepOver() 0587 { 0588 if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown)) 0589 return; 0590 0591 addCommand(ExecNext, QString(), CmdMaybeStartsRunning | CmdTemporaryRun); 0592 } 0593 0594 void MIDebugSession::stepIntoInstruction() 0595 { 0596 if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown)) 0597 return; 0598 0599 addCommand(ExecStepInstruction, QString(), 0600 CmdMaybeStartsRunning | CmdTemporaryRun); 0601 } 0602 0603 void MIDebugSession::stepInto() 0604 { 0605 if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown)) 0606 return; 0607 0608 addCommand(ExecStep, QString(), CmdMaybeStartsRunning | CmdTemporaryRun); 0609 } 0610 0611 void MIDebugSession::stepOverInstruction() 0612 { 0613 if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown)) 0614 return; 0615 0616 addCommand(ExecNextInstruction, QString(), 0617 CmdMaybeStartsRunning | CmdTemporaryRun); 0618 } 0619 0620 void MIDebugSession::stepOut() 0621 { 0622 if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown)) 0623 return; 0624 0625 addCommand(ExecFinish, QString(), CmdMaybeStartsRunning | CmdTemporaryRun); 0626 } 0627 0628 void MIDebugSession::runUntil(const QUrl& url, int line) 0629 { 0630 if (debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown)) 0631 return; 0632 0633 if (!url.isValid()) { 0634 addCommand(ExecUntil, QString::number(line), 0635 CmdMaybeStartsRunning | CmdTemporaryRun); 0636 } else { 0637 addCommand(ExecUntil, 0638 QStringLiteral("%1:%2").arg(url.toLocalFile()).arg(line), 0639 CmdMaybeStartsRunning | CmdTemporaryRun); 0640 } 0641 } 0642 0643 void MIDebugSession::runUntil(const QString& address) 0644 { 0645 if (debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown)) 0646 return; 0647 0648 if (!address.isEmpty()) { 0649 addCommand(ExecUntil, QStringLiteral("*%1").arg(address), 0650 CmdMaybeStartsRunning | CmdTemporaryRun); 0651 } 0652 } 0653 0654 void MIDebugSession::jumpTo(const QUrl& url, int line) 0655 { 0656 if (debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown)) 0657 return; 0658 0659 if (url.isValid()) { 0660 addCommand(NonMI, QStringLiteral("tbreak %1:%2").arg(url.toLocalFile()).arg(line)); 0661 addCommand(NonMI, QStringLiteral("jump %1:%2").arg(url.toLocalFile()).arg(line)); 0662 } 0663 } 0664 0665 void MIDebugSession::jumpToMemoryAddress(const QString& address) 0666 { 0667 if (debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown)) 0668 return; 0669 0670 if (!address.isEmpty()) { 0671 addCommand(NonMI, QStringLiteral("tbreak *%1").arg(address)); 0672 addCommand(NonMI, QStringLiteral("jump *%1").arg(address)); 0673 } 0674 } 0675 0676 void MIDebugSession::addUserCommand(const QString& cmd) 0677 { 0678 auto usercmd = createUserCommand(cmd); 0679 if (!usercmd) 0680 return; 0681 0682 queueCmd(std::move(usercmd)); 0683 // User command can theoretically modify absolutely everything, 0684 // so need to force a reload. 0685 0686 // We can do it right now, and don't wait for user command to finish 0687 // since commands used to reload all view will be executed after 0688 // user command anyway. 0689 if (!debuggerStateIsOn(s_appNotStarted) && !debuggerStateIsOn(s_programExited)) 0690 raiseEvent(program_state_changed); 0691 } 0692 0693 std::unique_ptr<MICommand> MIDebugSession::createUserCommand(const QString& cmd) const 0694 { 0695 if (!cmd.isEmpty() && cmd[0].isDigit()) { 0696 // Add a space to the beginning, so debugger won't get confused if the 0697 // command starts with a number (won't mix it up with command token added) 0698 return std::make_unique<UserCommand>(MI::NonMI, QLatin1Char(' ') + cmd); 0699 } else { 0700 return std::make_unique<UserCommand>(MI::NonMI, cmd); 0701 } 0702 } 0703 0704 std::unique_ptr<MICommand> MIDebugSession::createCommand(CommandType type, const QString& arguments, 0705 CommandFlags flags) const 0706 { 0707 // uses protected ctor, only accessible to MIDebugSession via friendship - cannot use make_unique :( 0708 return std::unique_ptr<MICommand>(new MICommand(type, arguments, flags)); 0709 } 0710 0711 void MIDebugSession::addCommand(std::unique_ptr<MICommand> cmd) 0712 { 0713 queueCmd(std::move(cmd)); 0714 } 0715 0716 void MIDebugSession::addCommand(MI::CommandType type, const QString& arguments, MI::CommandFlags flags) 0717 { 0718 queueCmd(createCommand(type, arguments, flags)); 0719 } 0720 0721 void MIDebugSession::addCommand(MI::CommandType type, const QString& arguments, 0722 MI::MICommandHandler *handler, 0723 MI::CommandFlags flags) 0724 { 0725 auto cmd = createCommand(type, arguments, flags); 0726 cmd->setHandler(handler); 0727 queueCmd(std::move(cmd)); 0728 } 0729 0730 void MIDebugSession::addCommand(MI::CommandType type, const QString& arguments, 0731 const MI::FunctionCommandHandler::Function& callback, 0732 MI::CommandFlags flags) 0733 { 0734 auto cmd = createCommand(type, arguments, flags); 0735 cmd->setHandler(callback); 0736 queueCmd(std::move(cmd)); 0737 } 0738 0739 // Fairly obvious that we'll add whatever command you give me to a queue 0740 // Not quite so obvious though is that if we are going to run again. then any 0741 // information requests become redundant and must be removed. 0742 // We also try and run whatever command happens to be at the head of 0743 // the queue. 0744 void MIDebugSession::queueCmd(std::unique_ptr<MICommand> cmd) 0745 { 0746 if (debuggerStateIsOn(s_dbgNotStarted)) { 0747 const QString messageText = 0748 i18n("<b>Gdb command sent when debugger is not running</b><br>" 0749 "The command was:<br> %1", cmd->initialString()); 0750 auto* message = new Sublime::Message(messageText, Sublime::Message::Information); 0751 ICore::self()->uiController()->postMessage(message); 0752 return; 0753 } 0754 0755 if (m_stateReloadInProgress) 0756 cmd->setStateReloading(true); 0757 0758 qCDebug(DEBUGGERCOMMON) << "QUEUE: " << cmd->initialString() 0759 << (m_stateReloadInProgress ? "(state reloading)" : "") 0760 << m_commandQueue->count() << "pending"; 0761 0762 bool varCommandWithContext= (cmd->type() >= MI::VarAssign 0763 && cmd->type() <= MI::VarUpdate 0764 && cmd->type() != MI::VarDelete); 0765 0766 bool stackCommandWithContext = (cmd->type() >= MI::StackInfoDepth 0767 && cmd->type() <= MI::StackListLocals); 0768 0769 if (varCommandWithContext || stackCommandWithContext) { 0770 if (cmd->thread() == -1) 0771 qCDebug(DEBUGGERCOMMON) << "\t--thread will be added on execution"; 0772 0773 if (cmd->frame() == -1) 0774 qCDebug(DEBUGGERCOMMON) << "\t--frame will be added on execution"; 0775 } 0776 0777 m_commandQueue->enqueue(std::move(cmd)); 0778 0779 setDebuggerStateOn(s_dbgBusy); 0780 raiseEvent(debugger_busy); 0781 0782 executeCmd(); 0783 } 0784 0785 void MIDebugSession::executeCmd() 0786 { 0787 Q_ASSERT(m_debugger); 0788 0789 if (debuggerStateIsOn(s_dbgNotListening) && m_commandQueue->haveImmediateCommand()) { 0790 // We may have to call this even while a command is currently executing, because 0791 // debugger can get into a state where a command such as ExecRun does not send a response 0792 // while the inferior is running. 0793 ensureDebuggerListening(); 0794 } 0795 0796 if (!m_debugger->isReady()) 0797 return; 0798 0799 auto currentCmd = m_commandQueue->nextCommand(); 0800 if (!currentCmd) 0801 return; 0802 0803 if (currentCmd->flags() & (CmdMaybeStartsRunning | CmdInterrupt)) { 0804 setDebuggerStateOff(s_automaticContinue); 0805 } 0806 0807 if (currentCmd->flags() & CmdMaybeStartsRunning) { 0808 // GDB can be in a state where it is listening for commands while the program is running. 0809 // However, when we send a command such as ExecContinue in this state, GDB may return to 0810 // the non-listening state without acknowledging that the ExecContinue command has even 0811 // finished, let alone sending a new notification about the program's running state. 0812 // So let's be extra cautious about ensuring that we will wake GDB up again if required. 0813 setDebuggerStateOn(s_dbgNotListening); 0814 } 0815 0816 bool varCommandWithContext= (currentCmd->type() >= MI::VarAssign 0817 && currentCmd->type() <= MI::VarUpdate 0818 && currentCmd->type() != MI::VarDelete); 0819 0820 bool stackCommandWithContext = (currentCmd->type() >= MI::StackInfoDepth 0821 && currentCmd->type() <= MI::StackListLocals); 0822 0823 if (varCommandWithContext || stackCommandWithContext) { 0824 // Most var commands should be executed in the context 0825 // of the selected thread and frame. 0826 if (currentCmd->thread() == -1) 0827 currentCmd->setThread(frameStackModel()->currentThread()); 0828 0829 if (currentCmd->frame() == -1) 0830 currentCmd->setFrame(frameStackModel()->currentFrame()); 0831 } 0832 0833 QString commandText = currentCmd->cmdToSend(); 0834 bool bad_command = false; 0835 QString message; 0836 0837 int length = commandText.length(); 0838 // No i18n for message since it's mainly for debugging. 0839 if (length == 0) { 0840 // The command might decide it's no longer necessary to send 0841 // it. 0842 if (auto* sc = dynamic_cast<SentinelCommand*>(currentCmd.get())) { 0843 qCDebug(DEBUGGERCOMMON) << "SEND: sentinel command, not sending"; 0844 sc->invokeHandler(); 0845 } else { 0846 qCDebug(DEBUGGERCOMMON) << "SEND: command " << currentCmd->initialString() 0847 << "changed its mind, not sending"; 0848 } 0849 0850 executeCmd(); 0851 return; 0852 } else { 0853 if (commandText[length-1] != QLatin1Char('\n')) { 0854 bad_command = true; 0855 message = QStringLiteral("Debugger command does not end with newline"); 0856 } 0857 } 0858 0859 if (bad_command) { 0860 const QString messageText = i18n("<b>Invalid debugger command</b><br>%1", message); 0861 auto* message = new Sublime::Message(messageText, Sublime::Message::Information); 0862 ICore::self()->uiController()->postMessage(message); 0863 executeCmd(); 0864 return; 0865 } 0866 0867 m_debugger->execute(std::move(currentCmd)); 0868 } 0869 0870 void MIDebugSession::ensureDebuggerListening() 0871 { 0872 Q_ASSERT(m_debugger); 0873 0874 // Note: we don't use interruptDebugger() here since 0875 // we don't want to queue more commands before queuing a command 0876 m_debugger->interrupt(); 0877 0878 setDebuggerStateOn(s_interruptSent); 0879 if (debuggerStateIsOn(s_appRunning)) 0880 setDebuggerStateOn(s_automaticContinue); 0881 setDebuggerStateOff(s_dbgNotListening); 0882 } 0883 0884 void MIDebugSession::destroyCmds() 0885 { 0886 m_commandQueue->clear(); 0887 } 0888 0889 // FIXME: I don't fully remember what is the business with 0890 // m_stateReloadInProgress and whether we can lift it to the 0891 // generic level. 0892 void MIDebugSession::raiseEvent(event_t e) 0893 { 0894 if (e == program_exited || e == debugger_exited) { 0895 m_stateReloadInProgress = false; 0896 } 0897 0898 if (e == program_state_changed) { 0899 m_stateReloadInProgress = true; 0900 qCDebug(DEBUGGERCOMMON) << "State reload in progress\n"; 0901 } 0902 0903 IDebugSession::raiseEvent(e); 0904 0905 if (e == program_state_changed) { 0906 m_stateReloadInProgress = false; 0907 } 0908 } 0909 0910 bool KDevMI::MIDebugSession::hasCrashed() const 0911 { 0912 return m_hasCrashed; 0913 } 0914 0915 void MIDebugSession::slotDebuggerReady() 0916 { 0917 Q_ASSERT(m_debugger); 0918 0919 m_stateReloadInProgress = false; 0920 0921 executeCmd(); 0922 if (m_debugger->isReady()) { 0923 /* There is nothing in the command queue and no command is currently executing. */ 0924 if (debuggerStateIsOn(s_automaticContinue)) { 0925 if (!debuggerStateIsOn(s_appRunning)) { 0926 qCDebug(DEBUGGERCOMMON) << "Posting automatic continue"; 0927 addCommand(ExecContinue, QString(), CmdMaybeStartsRunning); 0928 } 0929 setDebuggerStateOff(s_automaticContinue); 0930 return; 0931 } 0932 0933 if (m_stateReloadNeeded && !debuggerStateIsOn(s_appRunning)) { 0934 qCDebug(DEBUGGERCOMMON) << "Finishing program stop"; 0935 // Set to false right now, so that if 'actOnProgramPauseMI_part2' 0936 // sends some commands, we won't call it again when handling replies 0937 // from that commands. 0938 m_stateReloadNeeded = false; 0939 reloadProgramState(); 0940 } 0941 0942 qCDebug(DEBUGGERCOMMON) << "No more commands"; 0943 setDebuggerStateOff(s_dbgBusy); 0944 raiseEvent(debugger_ready); 0945 } 0946 } 0947 0948 void MIDebugSession::slotDebuggerExited(bool abnormal, const QString &msg) 0949 { 0950 /* Technically speaking, GDB is likely not to kill the application, and 0951 we should have some backup mechanism to make sure the application is 0952 killed by KDevelop. But even if application stays around, we no longer 0953 can control it in any way, so mark it as exited. */ 0954 setDebuggerStateOn(s_appNotStarted); 0955 setDebuggerStateOn(s_dbgNotStarted); 0956 setDebuggerStateOn(s_programExited); 0957 setDebuggerStateOff(s_shuttingDown); 0958 0959 if (!msg.isEmpty()) 0960 emit showMessage(msg, 3000); 0961 0962 if (abnormal) { 0963 /* The error is reported to user in MIDebugger now. 0964 KMessageBox::information( 0965 KDevelop::ICore::self()->uiController()->activeMainWindow(), 0966 i18n("<b>Debugger exited abnormally</b>" 0967 "<p>This is likely a bug in GDB. " 0968 "Examine the gdb output window and then stop the debugger"), 0969 i18n("Debugger exited abnormally")); 0970 */ 0971 // FIXME: not sure if the following still applies. 0972 // Note: we don't stop the debugger here, because that will hide gdb 0973 // window and prevent the user from finding the exact reason of the 0974 // problem. 0975 } 0976 0977 /* FIXME: raiseEvent is handled across multiple places where we explicitly 0978 * stop/kill the debugger, a better way is to let the debugger itself report 0979 * its exited event. 0980 */ 0981 // raiseEvent(debugger_exited); 0982 } 0983 0984 void MIDebugSession::slotInferiorStopped(const MI::AsyncRecord& r) 0985 { 0986 /* By default, reload all state on program stop. */ 0987 m_stateReloadNeeded = true; 0988 setDebuggerStateOff(s_appRunning); 0989 setDebuggerStateOff(s_dbgNotListening); 0990 0991 QString reason; 0992 if (r.hasField(QStringLiteral("reason"))) reason = r[QStringLiteral("reason")].literal(); 0993 0994 if (reason == QLatin1String("exited-normally") || reason == QLatin1String("exited")) { 0995 if (r.hasField(QStringLiteral("exit-code"))) { 0996 programNoApp(i18n("Exited with return code: %1", r[QStringLiteral("exit-code")].literal())); 0997 } else { 0998 programNoApp(i18n("Exited normally")); 0999 } 1000 m_stateReloadNeeded = false; 1001 return; 1002 } 1003 1004 if (reason == QLatin1String("exited-signalled")) { 1005 programNoApp(i18n("Exited on signal %1", r[QStringLiteral("signal-name")].literal())); 1006 m_stateReloadNeeded = false; 1007 return; 1008 } 1009 1010 if (reason == QLatin1String("watchpoint-scope")) { 1011 // FIXME: should remove this watchpoint 1012 // But first, we should consider if removing all 1013 // watchpoints on program exit is the right thing to 1014 // do. 1015 1016 addCommand(ExecContinue, QString(), CmdMaybeStartsRunning); 1017 1018 m_stateReloadNeeded = false; 1019 return; 1020 } 1021 1022 bool wasInterrupt = false; 1023 1024 if (reason == QLatin1String("signal-received")) { 1025 QString name = r[QStringLiteral("signal-name")].literal(); 1026 QString user_name = r[QStringLiteral("signal-meaning")].literal(); 1027 1028 // SIGINT is a "break into running program". 1029 // We do this when the user set/mod/clears a breakpoint but the 1030 // application is running. 1031 // And the user does this to stop the program also. 1032 if (name == QLatin1String("SIGINT") && debuggerStateIsOn(s_interruptSent)) { 1033 wasInterrupt = true; 1034 } else { 1035 // Whenever we have a signal raised then tell the user, but don't 1036 // end the program as we want to allow the user to look at why the 1037 // program has a signal that's caused the prog to stop. 1038 // Continuing from SIG FPE/SEGV will cause a "Cannot ..." and 1039 // that'll end the program. 1040 programFinished(i18n("Program received signal %1 (%2)", name, user_name)); 1041 1042 m_hasCrashed = true; 1043 } 1044 } 1045 1046 if (!reason.contains(QLatin1String("exited"))) { 1047 // FIXME: we should immediately update the current thread and 1048 // frame in the framestackmodel, so that any user actions 1049 // are in that thread. However, the way current framestack model 1050 // is implemented, we can't change thread id until we refresh 1051 // the entire list of threads -- otherwise we might set a thread 1052 // id that is not already in the list, and it will be upset. 1053 1054 //Indicates if program state should be reloaded immediately. 1055 bool updateState = false; 1056 1057 if (r.hasField(QStringLiteral("frame"))) { 1058 const MI::Value& frame = r[QStringLiteral("frame")]; 1059 QString file, line, addr; 1060 1061 if (frame.hasField(QStringLiteral("fullname"))) file = frame[QStringLiteral("fullname")].literal(); 1062 if (frame.hasField(QStringLiteral("line"))) line = frame[QStringLiteral("line")].literal(); 1063 if (frame.hasField(QStringLiteral("addr"))) addr = frame[QStringLiteral("addr")].literal(); 1064 1065 // gdb counts lines from 1 and we don't 1066 setCurrentPosition(QUrl::fromLocalFile(file), line.toInt() - 1, addr); 1067 1068 updateState = true; 1069 } 1070 1071 if (updateState) { 1072 reloadProgramState(); 1073 } 1074 } 1075 1076 setDebuggerStateOff(s_interruptSent); 1077 if (!wasInterrupt) 1078 setDebuggerStateOff(s_automaticContinue); 1079 } 1080 1081 void MIDebugSession::slotInferiorRunning() 1082 { 1083 setDebuggerStateOn(s_appRunning); 1084 raiseEvent(program_running); 1085 1086 if (m_commandQueue->haveImmediateCommand() || 1087 (m_debugger->currentCommand() && (m_debugger->currentCommand()->flags() & (CmdImmediately | CmdInterrupt)))) { 1088 ensureDebuggerListening(); 1089 } else { 1090 setDebuggerStateOn(s_dbgNotListening); 1091 } 1092 } 1093 1094 void MIDebugSession::processNotification(const MI::AsyncRecord & async) 1095 { 1096 if (async.reason == QLatin1String("thread-group-started")) { 1097 setDebuggerStateOff(s_appNotStarted | s_programExited); 1098 } else if (async.reason == QLatin1String("thread-group-exited")) { 1099 setDebuggerStateOn(s_programExited); 1100 } else if (async.reason == QLatin1String("library-loaded")) { 1101 // do nothing 1102 } else if (async.reason == QLatin1String("breakpoint-created")) { 1103 breakpointController()->notifyBreakpointCreated(async); 1104 } else if (async.reason == QLatin1String("breakpoint-modified")) { 1105 breakpointController()->notifyBreakpointModified(async); 1106 } else if (async.reason == QLatin1String("breakpoint-deleted")) { 1107 breakpointController()->notifyBreakpointDeleted(async); 1108 } else { 1109 qCDebug(DEBUGGERCOMMON) << "Unhandled notification: " << async.reason; 1110 } 1111 } 1112 1113 void MIDebugSession::reloadProgramState() 1114 { 1115 raiseEvent(program_state_changed); 1116 m_stateReloadNeeded = false; 1117 } 1118 1119 // There is no app anymore. This can be caused by program exiting 1120 // an invalid program specified or ... 1121 // gdb is still running though, but only the run command (may) make sense 1122 // all other commands are disabled. 1123 void MIDebugSession::programNoApp(const QString& msg) 1124 { 1125 qCDebug(DEBUGGERCOMMON) << msg; 1126 1127 setDebuggerState(s_appNotStarted | s_programExited | (m_debuggerState & s_shuttingDown)); 1128 1129 destroyCmds(); 1130 1131 // The application has existed, but it's possible that 1132 // some of application output is still in the pipe. We use 1133 // different pipes to communicate with gdb and to get application 1134 // output, so "exited" message from gdb might have arrived before 1135 // last application output. Get this last bit. 1136 1137 // Note: this method can be called when we open an invalid 1138 // core file. In that case, tty_ won't be set. 1139 if (m_tty){ 1140 m_tty->readRemaining(); 1141 // Tty is no longer usable, delete it. Without this, QSocketNotifier 1142 // will continuously bomd STTY with signals, so we need to either disable 1143 // QSocketNotifier, or delete STTY. The latter is simpler, since we can't 1144 // reuse it for future debug sessions anyway. 1145 m_tty.reset(nullptr); 1146 } 1147 1148 stopDebugger(); 1149 1150 raiseEvent(program_exited); 1151 raiseEvent(debugger_exited); 1152 1153 emit showMessage(msg, 0); 1154 1155 programFinished(msg); 1156 } 1157 1158 void MIDebugSession::programFinished(const QString& msg) 1159 { 1160 QString m = QStringLiteral("*** %0 ***").arg(msg.trimmed()); 1161 emit inferiorStderrLines(QStringList(m)); 1162 1163 /* Also show message in gdb window, so that users who 1164 prefer to look at gdb window know what's up. */ 1165 emit debuggerUserCommandOutput(m); 1166 } 1167 1168 void MIDebugSession::explainDebuggerStatus() 1169 { 1170 MICommand* currentCmd_ = m_debugger->currentCommand(); 1171 QString information = 1172 i18np("1 command in queue\n", "%1 commands in queue\n", m_commandQueue->count()) + 1173 i18ncp("Only the 0 and 1 cases need to be translated", "1 command being processed by gdb\n", "%1 commands being processed by gdb\n", (currentCmd_ ? 1 : 0)) + 1174 i18n("Debugger state: %1\n", m_debuggerState); 1175 1176 if (currentCmd_) { 1177 QString extra = i18n("Current command class: '%1'\n" 1178 "Current command text: '%2'\n" 1179 "Current command original text: '%3'\n", 1180 QString::fromUtf8(typeid(*currentCmd_).name()), 1181 currentCmd_->cmdToSend(), 1182 currentCmd_->initialString()); 1183 1184 information += extra; 1185 } 1186 1187 auto* message = new Sublime::Message(information, Sublime::Message::Information); 1188 ICore::self()->uiController()->postMessage(message); 1189 } 1190 1191 // There is no app anymore. This can be caused by program exiting 1192 // an invalid program specified or ... 1193 // gdb is still running though, but only the run command (may) make sense 1194 // all other commands are disabled. 1195 void MIDebugSession::handleNoInferior(const QString& msg) 1196 { 1197 qCDebug(DEBUGGERCOMMON) << msg; 1198 1199 setDebuggerState(s_appNotStarted | s_programExited | (debuggerState() & s_shuttingDown)); 1200 1201 destroyCmds(); 1202 1203 // The application has existed, but it's possible that 1204 // some of application output is still in the pipe. We use 1205 // different pipes to communicate with gdb and to get application 1206 // output, so "exited" message from gdb might have arrived before 1207 // last application output. Get this last bit. 1208 1209 // Note: this method can be called when we open an invalid 1210 // core file. In that case, tty_ won't be set. 1211 if (m_tty){ 1212 m_tty->readRemaining(); 1213 // Tty is no longer usable, delete it. Without this, QSocketNotifier 1214 // will continuously bomd STTY with signals, so we need to either disable 1215 // QSocketNotifier, or delete STTY. The latter is simpler, since we can't 1216 // reuse it for future debug sessions anyway. 1217 m_tty.reset(nullptr); 1218 } 1219 1220 stopDebugger(); 1221 1222 raiseEvent(program_exited); 1223 raiseEvent(debugger_exited); 1224 1225 emit showMessage(msg, 0); 1226 1227 handleInferiorFinished(msg); 1228 } 1229 1230 void MIDebugSession::handleInferiorFinished(const QString& msg) 1231 { 1232 QString m = QStringLiteral("*** %0 ***").arg(msg.trimmed()); 1233 emit inferiorStderrLines(QStringList(m)); 1234 1235 /* Also show message in gdb window, so that users who 1236 prefer to look at gdb window know what's up. */ 1237 emit debuggerUserCommandOutput(m); 1238 } 1239 1240 // FIXME: connect to debugger's slot. 1241 void MIDebugSession::defaultErrorHandler(const MI::ResultRecord& result) 1242 { 1243 QString msg = result[QStringLiteral("msg")].literal(); 1244 1245 if (msg.contains(QLatin1String("No such process"))) 1246 { 1247 setDebuggerState(s_appNotStarted|s_programExited); 1248 raiseEvent(program_exited); 1249 return; 1250 } 1251 1252 const QString messageText = 1253 i18n("<b>Debugger error</b>" 1254 "<p>Debugger reported the following error:" 1255 "<p><tt>%1", result[QStringLiteral("msg")].literal()); 1256 auto* message = new Sublime::Message(messageText, Sublime::Message::Error); 1257 ICore::self()->uiController()->postMessage(message); 1258 1259 // Error most likely means that some change made in GUI 1260 // was not communicated to the gdb, so GUI is now not 1261 // in sync with gdb. Resync it. 1262 // 1263 // Another approach is to make each widget reload it content 1264 // on errors from commands that it sent, but that's too complex. 1265 // Errors are supposed to happen rarely, so full reload on error 1266 // is not a big deal. Well, maybe except for memory view, but 1267 // it's no auto-reloaded anyway. 1268 // 1269 // Also, don't reload state on errors appeared during state 1270 // reloading! 1271 if (!m_debugger->currentCommand()->stateReloading()) 1272 raiseEvent(program_state_changed); 1273 } 1274 1275 void MIDebugSession::setSourceInitFile(bool enable) 1276 { 1277 m_sourceInitFile = enable; 1278 } 1279 1280 #include "moc_midebugsession.cpp"