1 /*
2     SPDX-FileCopyrightText: 1999-2001 John Birch <jbb@kdevelop.org>
3     SPDX-FileCopyrightText: 2001 Bernd Gehrmann <bernd@kdevelop.org>
4     SPDX-FileCopyrightText: 2006 Vladimir Prus <ghost@cs.msu.su>
5     SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org>
6     SPDX-FileCopyrightText: 2009 Niko Sams <niko.sams@gmail.com>
7     SPDX-FileCopyrightText: 2016 Aetf <aetf@unlimitedcodeworks.xyz>
8 
9     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
10 */
11 
12 #include "midebugsession.h"
13 
14 #include "debuglog.h"
15 #include "midebugger.h"
16 #include "midebuggerplugin.h"
17 #include "mivariable.h"
18 #include "mi/mi.h"
19 #include "mi/micommand.h"
20 #include "mi/micommandqueue.h"
21 #include "stty.h"
22 
23 #include <debugger/interfaces/iframestackmodel.h>
24 #include <execute/iexecuteplugin.h>
25 #include <interfaces/icore.h>
26 #include <interfaces/idocument.h>
27 #include <interfaces/idocumentcontroller.h>
28 #include <interfaces/ilaunchconfiguration.h>
29 #include <interfaces/iuicontroller.h>
30 #include <sublime/message.h>
31 #include <util/processlinemaker.h>
32 
33 #include <KConfigGroup>
34 #include <KLocalizedString>
35 #include <KSharedConfig>
36 #include <KShell>
37 
38 #include <QApplication>
39 #include <QFileInfo>
40 #include <QMetaEnum>
41 #include <QRegularExpression>
42 #include <QUrl>
43 #include <QTimer>
44 
45 using namespace KDevelop;
46 using namespace KDevMI;
47 using namespace KDevMI::MI;
48 
49 namespace {
50 constexpr DBGStateFlags notStartedDebuggerFlags{s_dbgNotStarted | s_appNotStarted};
51 }
52 
MIDebugSession(MIDebuggerPlugin * plugin)53 MIDebugSession::MIDebugSession(MIDebuggerPlugin *plugin)
54     : m_procLineMaker(new ProcessLineMaker(this))
55     , m_commandQueue(new CommandQueue)
56     , m_debuggerState{notStartedDebuggerFlags}
57     , m_tty(nullptr)
58     , m_plugin(plugin)
59 {
60     // setup signals
61     connect(m_procLineMaker, &ProcessLineMaker::receivedStdoutLines,
62             this, &MIDebugSession::inferiorStdoutLines);
63     connect(m_procLineMaker, &ProcessLineMaker::receivedStderrLines,
64             this, &MIDebugSession::inferiorStderrLines);
65 
66     // forward tty output to process line maker
67     connect(this, &MIDebugSession::inferiorTtyStdout,
68             m_procLineMaker, &ProcessLineMaker::slotReceivedStdout);
69     connect(this, &MIDebugSession::inferiorTtyStderr,
70             m_procLineMaker, &ProcessLineMaker::slotReceivedStderr);
71 
72     // FIXME: see if this still works
73     //connect(statusBarIndicator, SIGNAL(doubleClicked()),
74     //        controller, SLOT(explainDebuggerStatus()));
75 
76     // FIXME: reimplement / re-enable
77     //connect(this, SIGNAL(addWatchVariable(QString)), controller->variables(), SLOT(slotAddWatchVariable(QString)));
78     //connect(this, SIGNAL(evaluateExpression(QString)), controller->variables(), SLOT(slotEvaluateExpression(QString)));
79 }
80 
~MIDebugSession()81 MIDebugSession::~MIDebugSession()
82 {
83     qCDebug(DEBUGGERCOMMON) << "Destroying MIDebugSession";
84     // Deleting the session involves shutting down gdb nicely.
85     // When were attached to a process, we must first detach so that the process
86     // can continue running as it was before being attached. gdb is quite slow to
87     // detach from a process, so we must process events within here to get a "clean"
88     // shutdown.
89     if (!debuggerStateIsOn(s_dbgNotStarted)) {
90         stopDebugger();
91     }
92 }
93 
state() const94 IDebugSession::DebuggerState MIDebugSession::state() const
95 {
96     return m_sessionState;
97 }
98 
variableMapping()99 QMap<QString, MIVariable*> & MIDebugSession::variableMapping()
100 {
101     return m_allVariables;
102 }
103 
findVariableByVarobjName(const QString & varobjName) const104 MIVariable* MIDebugSession::findVariableByVarobjName(const QString &varobjName) const
105 {
106     if (m_allVariables.count(varobjName) == 0)
107         return nullptr;
108     return m_allVariables.value(varobjName);
109 }
110 
markAllVariableDead()111 void MIDebugSession::markAllVariableDead()
112 {
113     for (auto* variable : qAsConst(m_allVariables)) {
114         variable->markAsDead();
115     }
116     m_allVariables.clear();
117 }
118 
restartAvaliable() const119 bool MIDebugSession::restartAvaliable() const
120 {
121     if (debuggerStateIsOn(s_attached) || debuggerStateIsOn(s_core)) {
122         return false;
123     } else {
124         return true;
125     }
126 }
127 
startDebugger(ILaunchConfiguration * cfg)128 bool MIDebugSession::startDebugger(ILaunchConfiguration *cfg)
129 {
130     qCDebug(DEBUGGERCOMMON) << "Starting new debugger instance";
131     if (m_debugger) {
132         qCWarning(DEBUGGERCOMMON) << "m_debugger object still exists";
133         delete m_debugger;
134         m_debugger = nullptr;
135     }
136     m_debugger = createDebugger();
137     m_debugger->setParent(this);
138 
139     // output signals
140     connect(m_debugger, &MIDebugger::applicationOutput,
141             this, [this](const QString &output) {
142 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
143                 auto lines = output.split(QRegularExpression(QStringLiteral("[\r\n]")), Qt::SkipEmptyParts);
144 #else
145                 auto lines = output.split(QRegularExpression(QStringLiteral("[\r\n]")), QString::SkipEmptyParts);
146 #endif
147                 for (auto &line : lines) {
148                     int p = line.length();
149                     while (p >= 1 && (line[p-1] == QLatin1Char('\r') || line[p-1] == QLatin1Char('\n'))) {
150                         p--;
151                     }
152                     if (p != line.length())
153                         line.truncate(p);
154                 }
155                 emit inferiorStdoutLines(lines);
156             });
157     connect(m_debugger, &MIDebugger::userCommandOutput, this, &MIDebugSession::debuggerUserCommandOutput);
158     connect(m_debugger, &MIDebugger::internalCommandOutput, this, &MIDebugSession::debuggerInternalCommandOutput);
159     connect(m_debugger, &MIDebugger::debuggerInternalOutput, this, &MIDebugSession::debuggerInternalOutput);
160 
161     // state signals
162     connect(m_debugger, &MIDebugger::programStopped, this, &MIDebugSession::inferiorStopped);
163     connect(m_debugger, &MIDebugger::programRunning, this, &MIDebugSession::inferiorRunning);
164 
165     // internal handlers
166     connect(m_debugger, &MIDebugger::ready, this, &MIDebugSession::slotDebuggerReady);
167     connect(m_debugger, &MIDebugger::exited, this, &MIDebugSession::slotDebuggerExited);
168     connect(m_debugger, &MIDebugger::programStopped, this, &MIDebugSession::slotInferiorStopped);
169     connect(m_debugger, &MIDebugger::programRunning, this, &MIDebugSession::slotInferiorRunning);
170     connect(m_debugger, &MIDebugger::notification, this, &MIDebugSession::processNotification);
171 
172 
173     // start the debugger. Do this after connecting all signals so that initial
174     // debugger output, and important events like the debugger died are reported.
175     QStringList extraArguments;
176     if (!m_sourceInitFile)
177         extraArguments << QStringLiteral("--nx");
178 
179     auto config = cfg ? cfg->config()
180                 // FIXME: this is only used when attachToProcess or examineCoreFile.
181                 // Change to use a global launch configuration when calling
182                 : KConfigGroup(KSharedConfig::openConfig(), "GDB Config");
183 
184     if (!m_debugger->start(config, extraArguments)) {
185         // debugger failed to start, ensure debugger and session state are correctly updated.
186         setDebuggerStateOn(s_dbgFailedStart);
187         return false;
188     }
189 
190     // FIXME: here, we should wait until the debugger is up and waiting for input.
191     // Then, clear s_dbgNotStarted
192     // It's better to do this right away so that the state bit is always correct.
193     setDebuggerStateOff(s_dbgNotStarted);
194 
195     // Initialise debugger. At this stage debugger is sitting wondering what to do,
196     // and to whom.
197     initializeDebugger();
198 
199     qCDebug(DEBUGGERCOMMON) << "Debugger instance started";
200     return true;
201 }
202 
startDebugging(ILaunchConfiguration * cfg,IExecutePlugin * iexec)203 bool MIDebugSession::startDebugging(ILaunchConfiguration* cfg, IExecutePlugin* iexec)
204 {
205     qCDebug(DEBUGGERCOMMON) << "Starting new debug session";
206     Q_ASSERT(cfg);
207     Q_ASSERT(iexec);
208 
209     // Ensure debugger is started first
210     if (debuggerStateIsOn(s_appNotStarted)) {
211         emit showMessage(i18n("Running program"), 1000);
212     }
213 
214     if (debuggerStateIsOn(s_dbgNotStarted)) {
215         if (!startDebugger(cfg))
216             return false;
217     }
218 
219     if (debuggerStateIsOn(s_shuttingDown)) {
220         qCDebug(DEBUGGERCOMMON) << "Tried to run when debugger shutting down";
221         return false;
222     }
223 
224     // Only dummy err here, actual erros have been checked already in the job and we don't get here if there were any
225     QString err;
226     QString executable = iexec->executable(cfg, err).toLocalFile();
227     configInferior(cfg, iexec, executable);
228 
229     // Set up the tty for the inferior
230     bool config_useExternalTerminal = iexec->useTerminal(cfg);
231     QString config_ternimalName = iexec->terminal(cfg);
232     if (!config_ternimalName.isEmpty()) {
233         // the external terminal cmd contains additional arguments, just get the terminal name
234         config_ternimalName = KShell::splitArgs(config_ternimalName).first();
235     }
236 
237     m_tty.reset(new STTY(config_useExternalTerminal, config_ternimalName));
238     if (!config_useExternalTerminal) {
239         connect(m_tty.get(), &STTY::OutOutput, this, &MIDebugSession::inferiorTtyStdout);
240         connect(m_tty.get(), &STTY::ErrOutput, this, &MIDebugSession::inferiorTtyStderr);
241     }
242     QString tty(m_tty->getSlave());
243 #ifndef Q_OS_WIN
244     if (tty.isEmpty()) {
245         auto* message = new Sublime::Message(m_tty->lastError(), Sublime::Message::Information);
246         ICore::self()->uiController()->postMessage(message);
247 
248         m_tty.reset(nullptr);
249         return false;
250     }
251 #endif
252     addCommand(InferiorTtySet, tty);
253 
254     // Change the working directory to the correct one
255     QString dir = iexec->workingDirectory(cfg).toLocalFile();
256     if (dir.isEmpty()) {
257         dir = QFileInfo(executable).absolutePath();
258     }
259     addCommand(EnvironmentCd, QLatin1Char('"') + dir + QLatin1Char('"'));
260 
261     // Set the run arguments
262     QStringList arguments = iexec->arguments(cfg, err);
263     if (!arguments.isEmpty())
264         addCommand(ExecArguments, KShell::joinArgs(arguments));
265 
266     // Do other debugger specific config options and actually start the inferior program
267     if (!execInferior(cfg, iexec, executable)) {
268         return false;
269     }
270 
271     QString config_startWith = cfg->config().readEntry(Config::StartWithEntry, QStringLiteral("ApplicationOutput"));
272     if (config_startWith == QLatin1String("GdbConsole")) {
273         emit raiseDebuggerConsoleViews();
274     } else if (config_startWith == QLatin1String("FrameStack")) {
275         emit raiseFramestackViews();
276     } else {
277         // ApplicationOutput is raised in DebugJob (by setting job to Verbose/Silent)
278     }
279 
280     return true;
281 }
282 
283 // FIXME: use same configuration process as startDebugging
attachToProcess(int pid)284 bool MIDebugSession::attachToProcess(int pid)
285 {
286     qCDebug(DEBUGGERCOMMON) << "Attach to process" << pid;
287 
288     emit showMessage(i18n("Attaching to process %1", pid), 1000);
289 
290     if (debuggerStateIsOn(s_dbgNotStarted)) {
291         // FIXME: use global launch configuration rather than nullptr
292         if (!startDebugger(nullptr)) {
293             return false;
294         }
295     }
296 
297     setDebuggerStateOn(s_attached);
298 
299     //set current state to running, after attaching we will get *stopped response
300     setDebuggerStateOn(s_appRunning);
301 
302     addCommand(TargetAttach, QString::number(pid),
303                this, &MIDebugSession::handleTargetAttach,
304                CmdHandlesError);
305 
306     addCommand(new SentinelCommand(breakpointController(),
307                                    &MIBreakpointController::initSendBreakpoints));
308 
309     raiseEvent(connected_to_program);
310 
311     emit raiseFramestackViews();
312 
313     return true;
314 }
315 
handleTargetAttach(const MI::ResultRecord & r)316 void MIDebugSession::handleTargetAttach(const MI::ResultRecord& r)
317 {
318     if (r.reason == QLatin1String("error")) {
319         const QString messageText =
320             i18n("<b>Could not attach debugger:</b><br />")+
321                  r[QStringLiteral("msg")].literal();
322         auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
323         ICore::self()->uiController()->postMessage(message);
324         stopDebugger();
325     }
326 }
327 
examineCoreFile(const QUrl & debugee,const QUrl & coreFile)328 bool MIDebugSession::examineCoreFile(const QUrl &debugee, const QUrl &coreFile)
329 {
330     emit showMessage(i18n("Examining core file %1", coreFile.toLocalFile()), 1000);
331 
332     if (debuggerStateIsOn(s_dbgNotStarted)) {
333         // FIXME: use global launch configuration rather than nullptr
334         if (!startDebugger(nullptr)) {
335             return false;
336         }
337     }
338 
339     // FIXME: support non-local URLs
340     if (!loadCoreFile(nullptr, debugee.toLocalFile(), coreFile.toLocalFile())) {
341         return false;
342     }
343 
344     raiseEvent(program_state_changed);
345 
346     return true;
347 }
348 
349 #define ENUM_NAME(o,e,v) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).valueToKey((v)))
setSessionState(DebuggerState state)350 void MIDebugSession::setSessionState(DebuggerState state)
351 {
352     qCDebug(DEBUGGERCOMMON) << "Session state changed to"
353                             << ENUM_NAME(IDebugSession, DebuggerState, state)
354                             << "(" << state << ")";
355     if (state != m_sessionState) {
356         m_sessionState = state;
357         emit stateChanged(state);
358     }
359 }
360 
debuggerStateIsOn(DBGStateFlags state) const361 bool MIDebugSession::debuggerStateIsOn(DBGStateFlags state) const
362 {
363     return m_debuggerState & state;
364 }
365 
debuggerState() const366 DBGStateFlags MIDebugSession::debuggerState() const
367 {
368     return m_debuggerState;
369 }
370 
setDebuggerStateOn(DBGStateFlags stateOn)371 void MIDebugSession::setDebuggerStateOn(DBGStateFlags stateOn)
372 {
373     DBGStateFlags oldState = m_debuggerState;
374     m_debuggerState |= stateOn;
375     handleDebuggerStateChange(oldState, m_debuggerState);
376 }
377 
setDebuggerStateOff(DBGStateFlags stateOff)378 void MIDebugSession::setDebuggerStateOff(DBGStateFlags stateOff)
379 {
380     DBGStateFlags oldState = m_debuggerState;
381     m_debuggerState &= ~stateOff;
382     handleDebuggerStateChange(oldState, m_debuggerState);
383 }
384 
setDebuggerState(DBGStateFlags newState)385 void MIDebugSession::setDebuggerState(DBGStateFlags newState)
386 {
387     DBGStateFlags oldState = m_debuggerState;
388     m_debuggerState = newState;
389     handleDebuggerStateChange(oldState, m_debuggerState);
390 }
391 
handleDebuggerStateChange(DBGStateFlags oldState,DBGStateFlags newState)392 void MIDebugSession::handleDebuggerStateChange(DBGStateFlags oldState, DBGStateFlags newState)
393 {
394     QString message;
395 
396     DebuggerState oldSessionState = state();
397     DebuggerState newSessionState = oldSessionState;
398     DBGStateFlags changedState = oldState ^ newState;
399 
400     if (newState & s_dbgNotStarted) {
401         if (changedState & s_dbgNotStarted) {
402             message = i18n("Debugger stopped");
403             emit finished();
404         }
405         if (oldSessionState != NotStartedState || newState & s_dbgFailedStart) {
406             newSessionState = EndedState;
407         }
408     } else {
409         if (newState & s_appNotStarted) {
410             if (oldSessionState == NotStartedState || oldSessionState == StartingState) {
411                 newSessionState = StartingState;
412             } else {
413                 newSessionState = StoppedState;
414             }
415         } else if (newState & s_programExited) {
416             if (changedState & s_programExited) {
417                 message = i18n("Process exited");
418             }
419             newSessionState = StoppedState;
420         } else if (newState & s_appRunning) {
421             if (changedState & s_appRunning) {
422                 message = i18n("Application is running");
423             }
424             newSessionState = ActiveState;
425         } else {
426             if (changedState & s_appRunning) {
427                 message = i18n("Application is paused");
428             }
429             newSessionState = PausedState;
430         }
431     }
432 
433     // And now? :-)
434     qCDebug(DEBUGGERCOMMON) << "Debugger state changed to:" << newState << message << "- changes:" << changedState;
435 
436     if (!message.isEmpty())
437         emit showMessage(message, 3000);
438 
439     emit debuggerStateChanged(oldState, newState);
440 
441     // must be last, since it can lead to deletion of the DebugSession
442     if (newSessionState != oldSessionState) {
443         setSessionState(newSessionState);
444     }
445 }
446 
restartDebugger()447 void MIDebugSession::restartDebugger()
448 {
449     // We implement restart as kill + slotRun, as opposed as plain "run"
450     // command because kill + slotRun allows any special logic in slotRun
451     // to apply for restart.
452     //
453     // That includes:
454     // - checking for out-of-date project
455     // - special setup for remote debugging.
456     //
457     // Had we used plain 'run' command, restart for remote debugging simply
458     // would not work.
459     if (!debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown)) {
460         // FIXME: s_dbgBusy or m_debugger->isReady()?
461         if (debuggerStateIsOn(s_dbgBusy)) {
462             interruptDebugger();
463         }
464         // The -exec-abort is not implemented in gdb
465         // addCommand(ExecAbort);
466         addCommand(NonMI, QStringLiteral("kill"));
467     }
468     run();
469 }
470 
stopDebugger()471 void MIDebugSession::stopDebugger()
472 {
473     if (debuggerStateIsOn(s_dbgNotStarted)) {
474         qCDebug(DEBUGGERCOMMON) << "Stopping debugger when it's not started";
475         if (debuggerState() != notStartedDebuggerFlags) {
476             setDebuggerState(notStartedDebuggerFlags);
477         }
478         // Transition into EndedState to let DebugController destroy this session.
479         if (state() != EndedState) {
480             setSessionState(EndedState);
481         }
482         return;
483     }
484 
485     m_commandQueue->clear();
486 
487     qCDebug(DEBUGGERCOMMON) << "try stopping debugger";
488     if (debuggerStateIsOn(s_shuttingDown) || !m_debugger)
489         return;
490 
491     setDebuggerStateOn(s_shuttingDown);
492     qCDebug(DEBUGGERCOMMON) << "stopping debugger";
493 
494     // Get debugger's attention if it's busy. We need debugger to be at the
495     // command line so we can stop it.
496     if (!m_debugger->isReady()) {
497         qCDebug(DEBUGGERCOMMON) << "debugger busy on shutdown - interrupting";
498         interruptDebugger();
499     }
500 
501     // If the app is attached then we release it here. This doesn't stop
502     // the app running.
503     if (debuggerStateIsOn(s_attached)) {
504         addCommand(TargetDetach);
505         emit debuggerUserCommandOutput(QStringLiteral("(gdb) detach\n"));
506     }
507 
508     // Now try to stop debugger running.
509     addCommand(GdbExit);
510     emit debuggerUserCommandOutput(QStringLiteral("(gdb) quit"));
511 
512     // We cannot wait forever, kill gdb after 5 seconds if it's not yet quit
513     QTimer::singleShot(5000, this, [this]() {
514         if (!debuggerStateIsOn(s_programExited) && debuggerStateIsOn(s_shuttingDown)) {
515             qCDebug(DEBUGGERCOMMON) << "debugger not shutdown - killing";
516             killDebuggerImpl();
517         }
518     });
519 
520     emit reset();
521 }
522 
killDebuggerNow()523 void MIDebugSession::killDebuggerNow()
524 {
525     if (!debuggerStateIsOn(s_dbgNotStarted)) {
526         qCDebug(DEBUGGERCOMMON) << "killing debugger now";
527         killDebuggerImpl();
528     }
529 }
530 
killDebuggerImpl()531 void MIDebugSession::killDebuggerImpl()
532 {
533     Q_ASSERT(m_debugger);
534     m_debugger->kill();
535     setDebuggerState(notStartedDebuggerFlags);
536     raiseEvent(debugger_exited);
537 }
538 
interruptDebugger()539 void MIDebugSession::interruptDebugger()
540 {
541     Q_ASSERT(m_debugger);
542 
543     // Explicitly send the interrupt in case something went wrong with the usual
544     // ensureGdbListening logic.
545     m_debugger->interrupt();
546     addCommand(ExecInterrupt, QString(), CmdInterrupt);
547 }
548 
run()549 void MIDebugSession::run()
550 {
551     if (debuggerStateIsOn(s_appNotStarted|s_dbgNotStarted|s_shuttingDown))
552         return;
553 
554     addCommand(MI::ExecContinue, QString(), CmdMaybeStartsRunning);
555 }
556 
runToCursor()557 void MIDebugSession::runToCursor()
558 {
559     if (IDocument* doc = ICore::self()->documentController()->activeDocument()) {
560         KTextEditor::Cursor cursor = doc->cursorPosition();
561         if (cursor.isValid())
562             runUntil(doc->url(), cursor.line() + 1);
563     }
564 }
565 
jumpToCursor()566 void MIDebugSession::jumpToCursor()
567 {
568     if (IDocument* doc = ICore::self()->documentController()->activeDocument()) {
569         KTextEditor::Cursor cursor = doc->cursorPosition();
570         if (cursor.isValid())
571             jumpTo(doc->url(), cursor.line() + 1);
572     }
573 }
574 
stepOver()575 void MIDebugSession::stepOver()
576 {
577     if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown))
578         return;
579 
580     addCommand(ExecNext, QString(), CmdMaybeStartsRunning | CmdTemporaryRun);
581 }
582 
stepIntoInstruction()583 void MIDebugSession::stepIntoInstruction()
584 {
585     if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown))
586         return;
587 
588     addCommand(ExecStepInstruction, QString(),
589                CmdMaybeStartsRunning | CmdTemporaryRun);
590 }
591 
stepInto()592 void MIDebugSession::stepInto()
593 {
594     if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown))
595         return;
596 
597     addCommand(ExecStep, QString(), CmdMaybeStartsRunning | CmdTemporaryRun);
598 }
599 
stepOverInstruction()600 void MIDebugSession::stepOverInstruction()
601 {
602     if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown))
603         return;
604 
605     addCommand(ExecNextInstruction, QString(),
606                CmdMaybeStartsRunning | CmdTemporaryRun);
607 }
608 
stepOut()609 void MIDebugSession::stepOut()
610 {
611     if (debuggerStateIsOn(s_appNotStarted|s_shuttingDown))
612         return;
613 
614     addCommand(ExecFinish, QString(), CmdMaybeStartsRunning | CmdTemporaryRun);
615 }
616 
runUntil(const QUrl & url,int line)617 void MIDebugSession::runUntil(const QUrl& url, int line)
618 {
619     if (debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown))
620         return;
621 
622     if (!url.isValid()) {
623         addCommand(ExecUntil, QString::number(line),
624                    CmdMaybeStartsRunning | CmdTemporaryRun);
625     } else {
626         addCommand(ExecUntil,
627                    QStringLiteral("%1:%2").arg(url.toLocalFile()).arg(line),
628                    CmdMaybeStartsRunning | CmdTemporaryRun);
629     }
630 }
631 
runUntil(const QString & address)632 void MIDebugSession::runUntil(const QString& address)
633 {
634     if (debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown))
635         return;
636 
637     if (!address.isEmpty()) {
638         addCommand(ExecUntil, QStringLiteral("*%1").arg(address),
639                    CmdMaybeStartsRunning | CmdTemporaryRun);
640     }
641 }
642 
jumpTo(const QUrl & url,int line)643 void MIDebugSession::jumpTo(const QUrl& url, int line)
644 {
645     if (debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown))
646         return;
647 
648     if (url.isValid()) {
649         addCommand(NonMI, QStringLiteral("tbreak %1:%2").arg(url.toLocalFile()).arg(line));
650         addCommand(NonMI, QStringLiteral("jump %1:%2").arg(url.toLocalFile()).arg(line));
651     }
652 }
653 
jumpToMemoryAddress(const QString & address)654 void MIDebugSession::jumpToMemoryAddress(const QString& address)
655 {
656     if (debuggerStateIsOn(s_dbgNotStarted|s_shuttingDown))
657         return;
658 
659     if (!address.isEmpty()) {
660         addCommand(NonMI, QStringLiteral("tbreak *%1").arg(address));
661         addCommand(NonMI, QStringLiteral("jump *%1").arg(address));
662     }
663 }
664 
addUserCommand(const QString & cmd)665 void MIDebugSession::addUserCommand(const QString& cmd)
666 {
667     auto usercmd = createUserCommand(cmd);
668     if (!usercmd)
669         return;
670 
671     queueCmd(usercmd);
672     // User command can theoretically modify absolutely everything,
673     // so need to force a reload.
674 
675     // We can do it right now, and don't wait for user command to finish
676     // since commands used to reload all view will be executed after
677     // user command anyway.
678     if (!debuggerStateIsOn(s_appNotStarted) && !debuggerStateIsOn(s_programExited))
679         raiseEvent(program_state_changed);
680 }
681 
createUserCommand(const QString & cmd) const682 MICommand *MIDebugSession::createUserCommand(const QString &cmd) const
683 {
684     MICommand *res = nullptr;
685     if (!cmd.isEmpty() && cmd[0].isDigit()) {
686         // Add a space to the beginning, so debugger won't get confused if the
687         // command starts with a number (won't mix it up with command token added)
688         res = new UserCommand(MI::NonMI, QLatin1Char(' ') + cmd);
689     } else {
690         res = new UserCommand(MI::NonMI, cmd);
691     }
692     return res;
693 }
694 
createCommand(CommandType type,const QString & arguments,CommandFlags flags) const695 MICommand *MIDebugSession::createCommand(CommandType type, const QString& arguments,
696                                          CommandFlags flags) const
697 {
698     return new MICommand(type, arguments, flags);
699 }
700 
addCommand(MICommand * cmd)701 void MIDebugSession::addCommand(MICommand* cmd)
702 {
703     queueCmd(cmd);
704 }
705 
addCommand(MI::CommandType type,const QString & arguments,MI::CommandFlags flags)706 void MIDebugSession::addCommand(MI::CommandType type, const QString& arguments, MI::CommandFlags flags)
707 {
708     queueCmd(createCommand(type, arguments, flags));
709 }
710 
addCommand(MI::CommandType type,const QString & arguments,MI::MICommandHandler * handler,MI::CommandFlags flags)711 void MIDebugSession::addCommand(MI::CommandType type, const QString& arguments,
712                 MI::MICommandHandler *handler,
713                 MI::CommandFlags flags)
714 {
715     auto cmd = createCommand(type, arguments, flags);
716     cmd->setHandler(handler);
717     queueCmd(cmd);
718 }
719 
addCommand(MI::CommandType type,const QString & arguments,const MI::FunctionCommandHandler::Function & callback,MI::CommandFlags flags)720 void MIDebugSession::addCommand(MI::CommandType type, const QString& arguments,
721                 const MI::FunctionCommandHandler::Function& callback,
722                 MI::CommandFlags flags)
723 {
724     auto cmd = createCommand(type, arguments, flags);
725     cmd->setHandler(callback);
726     queueCmd(cmd);
727 }
728 
729 // Fairly obvious that we'll add whatever command you give me to a queue
730 // Not quite so obvious though is that if we are going to run again. then any
731 // information requests become redundent and must be removed.
732 // We also try and run whatever command happens to be at the head of
733 // the queue.
queueCmd(MICommand * cmd)734 void MIDebugSession::queueCmd(MICommand *cmd)
735 {
736     if (debuggerStateIsOn(s_dbgNotStarted)) {
737         const QString messageText =
738             i18n("<b>Gdb command sent when debugger is not running</b><br>"
739                  "The command was:<br> %1", cmd->initialString());
740         auto* message = new Sublime::Message(messageText, Sublime::Message::Information);
741         ICore::self()->uiController()->postMessage(message);
742         return;
743     }
744 
745     if (m_stateReloadInProgress)
746         cmd->setStateReloading(true);
747 
748     m_commandQueue->enqueue(cmd);
749 
750     qCDebug(DEBUGGERCOMMON) << "QUEUE: " << cmd->initialString()
751                             << (m_stateReloadInProgress ? "(state reloading)" : "")
752                             << m_commandQueue->count() << "pending";
753 
754     bool varCommandWithContext= (cmd->type() >= MI::VarAssign
755                                  && cmd->type() <= MI::VarUpdate
756                                  && cmd->type() != MI::VarDelete);
757 
758     bool stackCommandWithContext = (cmd->type() >= MI::StackInfoDepth
759                                     && cmd->type() <= MI::StackListLocals);
760 
761     if (varCommandWithContext || stackCommandWithContext) {
762         if (cmd->thread() == -1)
763             qCDebug(DEBUGGERCOMMON) << "\t--thread will be added on execution";
764 
765         if (cmd->frame() == -1)
766             qCDebug(DEBUGGERCOMMON) << "\t--frame will be added on execution";
767     }
768 
769     setDebuggerStateOn(s_dbgBusy);
770     raiseEvent(debugger_busy);
771 
772     executeCmd();
773 }
774 
executeCmd()775 void MIDebugSession::executeCmd()
776 {
777     Q_ASSERT(m_debugger);
778 
779     if (debuggerStateIsOn(s_dbgNotListening) && m_commandQueue->haveImmediateCommand()) {
780         // We may have to call this even while a command is currently executing, because
781         // debugger can get into a state where a command such as ExecRun does not send a response
782         // while the inferior is running.
783         ensureDebuggerListening();
784     }
785 
786     if (!m_debugger->isReady())
787         return;
788 
789     MICommand* currentCmd = m_commandQueue->nextCommand();
790     if (!currentCmd)
791         return;
792 
793     if (currentCmd->flags() & (CmdMaybeStartsRunning | CmdInterrupt)) {
794         setDebuggerStateOff(s_automaticContinue);
795     }
796 
797     if (currentCmd->flags() & CmdMaybeStartsRunning) {
798         // GDB can be in a state where it is listening for commands while the program is running.
799         // However, when we send a command such as ExecContinue in this state, GDB may return to
800         // the non-listening state without acknowledging that the ExecContinue command has even
801         // finished, let alone sending a new notification about the program's running state.
802         // So let's be extra cautious about ensuring that we will wake GDB up again if required.
803         setDebuggerStateOn(s_dbgNotListening);
804     }
805 
806     bool varCommandWithContext= (currentCmd->type() >= MI::VarAssign
807                                  && currentCmd->type() <= MI::VarUpdate
808                                  && currentCmd->type() != MI::VarDelete);
809 
810     bool stackCommandWithContext = (currentCmd->type() >= MI::StackInfoDepth
811                                     && currentCmd->type() <= MI::StackListLocals);
812 
813     if (varCommandWithContext || stackCommandWithContext) {
814         // Most var commands should be executed in the context
815         // of the selected thread and frame.
816         if (currentCmd->thread() == -1)
817             currentCmd->setThread(frameStackModel()->currentThread());
818 
819         if (currentCmd->frame() == -1)
820             currentCmd->setFrame(frameStackModel()->currentFrame());
821     }
822 
823     QString commandText = currentCmd->cmdToSend();
824     bool bad_command = false;
825     QString message;
826 
827     int length = commandText.length();
828     // No i18n for message since it's mainly for debugging.
829     if (length == 0) {
830         // The command might decide it's no longer necessary to send
831         // it.
832         if (auto* sc = dynamic_cast<SentinelCommand*>(currentCmd))
833         {
834             qCDebug(DEBUGGERCOMMON) << "SEND: sentinel command, not sending";
835             sc->invokeHandler();
836         }
837         else
838         {
839             qCDebug(DEBUGGERCOMMON) << "SEND: command " << currentCmd->initialString()
840                           << "changed its mind, not sending";
841         }
842 
843         delete currentCmd;
844         executeCmd();
845         return;
846     } else {
847         if (commandText[length-1] != QLatin1Char('\n')) {
848             bad_command = true;
849             message = QStringLiteral("Debugger command does not end with newline");
850         }
851     }
852 
853     if (bad_command) {
854         const QString messageText = i18n("<b>Invalid debugger command</b><br>%1", message);
855         auto* message = new Sublime::Message(messageText, Sublime::Message::Information);
856         ICore::self()->uiController()->postMessage(message);
857         executeCmd();
858         return;
859     }
860 
861     m_debugger->execute(currentCmd);
862 }
863 
ensureDebuggerListening()864 void MIDebugSession::ensureDebuggerListening()
865 {
866     Q_ASSERT(m_debugger);
867 
868     // Note: we don't use interruptDebugger() here since
869     // we don't want to queue more commands before queuing a command
870     m_debugger->interrupt();
871 
872     setDebuggerStateOn(s_interruptSent);
873     if (debuggerStateIsOn(s_appRunning))
874         setDebuggerStateOn(s_automaticContinue);
875     setDebuggerStateOff(s_dbgNotListening);
876 }
877 
destroyCmds()878 void MIDebugSession::destroyCmds()
879 {
880     m_commandQueue->clear();
881 }
882 
883 // FIXME: I don't fully remember what is the business with
884 // m_stateReloadInProgress and whether we can lift it to the
885 // generic level.
raiseEvent(event_t e)886 void MIDebugSession::raiseEvent(event_t e)
887 {
888     if (e == program_exited || e == debugger_exited) {
889         m_stateReloadInProgress = false;
890     }
891 
892     if (e == program_state_changed) {
893         m_stateReloadInProgress = true;
894         qCDebug(DEBUGGERCOMMON) << "State reload in progress\n";
895     }
896 
897     IDebugSession::raiseEvent(e);
898 
899     if (e == program_state_changed) {
900         m_stateReloadInProgress = false;
901     }
902 }
903 
hasCrashed() const904 bool KDevMI::MIDebugSession::hasCrashed() const
905 {
906     return m_hasCrashed;
907 }
908 
slotDebuggerReady()909 void MIDebugSession::slotDebuggerReady()
910 {
911     Q_ASSERT(m_debugger);
912 
913     m_stateReloadInProgress = false;
914 
915     executeCmd();
916     if (m_debugger->isReady()) {
917         /* There is nothing in the command queue and no command is currently executing. */
918         if (debuggerStateIsOn(s_automaticContinue)) {
919             if (!debuggerStateIsOn(s_appRunning)) {
920                 qCDebug(DEBUGGERCOMMON) << "Posting automatic continue";
921                 addCommand(ExecContinue, QString(), CmdMaybeStartsRunning);
922             }
923             setDebuggerStateOff(s_automaticContinue);
924             return;
925         }
926 
927         if (m_stateReloadNeeded && !debuggerStateIsOn(s_appRunning)) {
928             qCDebug(DEBUGGERCOMMON) << "Finishing program stop";
929             // Set to false right now, so that if 'actOnProgramPauseMI_part2'
930             // sends some commands, we won't call it again when handling replies
931             // from that commands.
932             m_stateReloadNeeded = false;
933             reloadProgramState();
934         }
935 
936         qCDebug(DEBUGGERCOMMON) << "No more commands";
937         setDebuggerStateOff(s_dbgBusy);
938         raiseEvent(debugger_ready);
939     }
940 }
941 
slotDebuggerExited(bool abnormal,const QString & msg)942 void MIDebugSession::slotDebuggerExited(bool abnormal, const QString &msg)
943 {
944     /* Technically speaking, GDB is likely not to kill the application, and
945        we should have some backup mechanism to make sure the application is
946        killed by KDevelop.  But even if application stays around, we no longer
947        can control it in any way, so mark it as exited.  */
948     setDebuggerStateOn(s_appNotStarted);
949     setDebuggerStateOn(s_dbgNotStarted);
950     setDebuggerStateOn(s_programExited);
951     setDebuggerStateOff(s_shuttingDown);
952 
953     if (!msg.isEmpty())
954         emit showMessage(msg, 3000);
955 
956     if (abnormal) {
957         /* The error is reported to user in MIDebugger now.
958         KMessageBox::information(
959             KDevelop::ICore::self()->uiController()->activeMainWindow(),
960             i18n("<b>Debugger exited abnormally</b>"
961                 "<p>This is likely a bug in GDB. "
962                 "Examine the gdb output window and then stop the debugger"),
963             i18n("Debugger exited abnormally"));
964         */
965         // FIXME: not sure if the following still applies.
966         // Note: we don't stop the debugger here, becuse that will hide gdb
967         // window and prevent the user from finding the exact reason of the
968         // problem.
969     }
970 
971     /* FIXME: raiseEvent is handled across multiple places where we explicitly
972      * stop/kill the debugger, a better way is to let the debugger itself report
973      * its exited event.
974      */
975     // raiseEvent(debugger_exited);
976 }
977 
slotInferiorStopped(const MI::AsyncRecord & r)978 void MIDebugSession::slotInferiorStopped(const MI::AsyncRecord& r)
979 {
980     /* By default, reload all state on program stop.  */
981     m_stateReloadNeeded = true;
982     setDebuggerStateOff(s_appRunning);
983     setDebuggerStateOff(s_dbgNotListening);
984 
985     QString reason;
986     if (r.hasField(QStringLiteral("reason"))) reason = r[QStringLiteral("reason")].literal();
987 
988     if (reason == QLatin1String("exited-normally") || reason == QLatin1String("exited")) {
989         if (r.hasField(QStringLiteral("exit-code"))) {
990             programNoApp(i18n("Exited with return code: %1", r[QStringLiteral("exit-code")].literal()));
991         } else {
992             programNoApp(i18n("Exited normally"));
993         }
994         m_stateReloadNeeded = false;
995         return;
996     }
997 
998     if (reason == QLatin1String("exited-signalled")) {
999         programNoApp(i18n("Exited on signal %1", r[QStringLiteral("signal-name")].literal()));
1000         m_stateReloadNeeded = false;
1001         return;
1002     }
1003 
1004     if (reason == QLatin1String("watchpoint-scope")) {
1005         // FIXME: should remove this watchpoint
1006         // But first, we should consider if removing all
1007         // watchpoints on program exit is the right thing to
1008         // do.
1009 
1010         addCommand(ExecContinue, QString(), CmdMaybeStartsRunning);
1011 
1012         m_stateReloadNeeded = false;
1013         return;
1014     }
1015 
1016     bool wasInterrupt = false;
1017 
1018     if (reason == QLatin1String("signal-received")) {
1019         QString name = r[QStringLiteral("signal-name")].literal();
1020         QString user_name = r[QStringLiteral("signal-meaning")].literal();
1021 
1022         // SIGINT is a "break into running program".
1023         // We do this when the user set/mod/clears a breakpoint but the
1024         // application is running.
1025         // And the user does this to stop the program also.
1026         if (name == QLatin1String("SIGINT") && debuggerStateIsOn(s_interruptSent)) {
1027             wasInterrupt = true;
1028         } else {
1029             // Whenever we have a signal raised then tell the user, but don't
1030             // end the program as we want to allow the user to look at why the
1031             // program has a signal that's caused the prog to stop.
1032             // Continuing from SIG FPE/SEGV will cause a "Cannot ..." and
1033             // that'll end the program.
1034             programFinished(i18n("Program received signal %1 (%2)", name, user_name));
1035 
1036             m_hasCrashed = true;
1037         }
1038     }
1039 
1040     if (!reason.contains(QLatin1String("exited"))) {
1041         // FIXME: we should immediately update the current thread and
1042         // frame in the framestackmodel, so that any user actions
1043         // are in that thread. However, the way current framestack model
1044         // is implemented, we can't change thread id until we refresh
1045         // the entire list of threads -- otherwise we might set a thread
1046         // id that is not already in the list, and it will be upset.
1047 
1048         //Indicates if program state should be reloaded immediately.
1049         bool updateState = false;
1050 
1051         if (r.hasField(QStringLiteral("frame"))) {
1052             const MI::Value& frame = r[QStringLiteral("frame")];
1053             QString file, line, addr;
1054 
1055             if (frame.hasField(QStringLiteral("fullname"))) file = frame[QStringLiteral("fullname")].literal();
1056             if (frame.hasField(QStringLiteral("line")))     line = frame[QStringLiteral("line")].literal();
1057             if (frame.hasField(QStringLiteral("addr")))     addr = frame[QStringLiteral("addr")].literal();
1058 
1059             // gdb counts lines from 1 and we don't
1060             setCurrentPosition(QUrl::fromLocalFile(file), line.toInt() - 1, addr);
1061 
1062             updateState = true;
1063         }
1064 
1065         if (updateState) {
1066             reloadProgramState();
1067         }
1068     }
1069 
1070     setDebuggerStateOff(s_interruptSent);
1071     if (!wasInterrupt)
1072         setDebuggerStateOff(s_automaticContinue);
1073 }
1074 
slotInferiorRunning()1075 void MIDebugSession::slotInferiorRunning()
1076 {
1077     setDebuggerStateOn(s_appRunning);
1078     raiseEvent(program_running);
1079 
1080     if (m_commandQueue->haveImmediateCommand() ||
1081         (m_debugger->currentCommand() && (m_debugger->currentCommand()->flags() & (CmdImmediately | CmdInterrupt)))) {
1082         ensureDebuggerListening();
1083     } else {
1084         setDebuggerStateOn(s_dbgNotListening);
1085     }
1086 }
1087 
processNotification(const MI::AsyncRecord & async)1088 void MIDebugSession::processNotification(const MI::AsyncRecord & async)
1089 {
1090     if (async.reason == QLatin1String("thread-group-started")) {
1091         setDebuggerStateOff(s_appNotStarted | s_programExited);
1092     } else if (async.reason == QLatin1String("thread-group-exited")) {
1093         setDebuggerStateOn(s_programExited);
1094     } else if (async.reason == QLatin1String("library-loaded")) {
1095         // do nothing
1096     } else if (async.reason == QLatin1String("breakpoint-created")) {
1097         breakpointController()->notifyBreakpointCreated(async);
1098     } else if (async.reason == QLatin1String("breakpoint-modified")) {
1099         breakpointController()->notifyBreakpointModified(async);
1100     } else if (async.reason == QLatin1String("breakpoint-deleted")) {
1101         breakpointController()->notifyBreakpointDeleted(async);
1102     } else {
1103         qCDebug(DEBUGGERCOMMON) << "Unhandled notification: " << async.reason;
1104     }
1105 }
1106 
reloadProgramState()1107 void MIDebugSession::reloadProgramState()
1108 {
1109     raiseEvent(program_state_changed);
1110     m_stateReloadNeeded = false;
1111 }
1112 
1113 // There is no app anymore. This can be caused by program exiting
1114 // an invalid program specified or ...
1115 // gdb is still running though, but only the run command (may) make sense
1116 // all other commands are disabled.
programNoApp(const QString & msg)1117 void MIDebugSession::programNoApp(const QString& msg)
1118 {
1119     qCDebug(DEBUGGERCOMMON) << msg;
1120 
1121     setDebuggerState(s_appNotStarted | s_programExited | (m_debuggerState & s_shuttingDown));
1122 
1123     destroyCmds();
1124 
1125     // The application has existed, but it's possible that
1126     // some of application output is still in the pipe. We use
1127     // different pipes to communicate with gdb and to get application
1128     // output, so "exited" message from gdb might have arrived before
1129     // last application output. Get this last bit.
1130 
1131     // Note: this method can be called when we open an invalid
1132     // core file. In that case, tty_ won't be set.
1133     if (m_tty){
1134         m_tty->readRemaining();
1135         // Tty is no longer usable, delete it. Without this, QSocketNotifier
1136         // will continuously bomd STTY with signals, so we need to either disable
1137         // QSocketNotifier, or delete STTY. The latter is simpler, since we can't
1138         // reuse it for future debug sessions anyway.
1139         m_tty.reset(nullptr);
1140     }
1141 
1142     stopDebugger();
1143 
1144     raiseEvent(program_exited);
1145     raiseEvent(debugger_exited);
1146 
1147     emit showMessage(msg, 0);
1148 
1149     programFinished(msg);
1150 }
1151 
programFinished(const QString & msg)1152 void MIDebugSession::programFinished(const QString& msg)
1153 {
1154     QString m = QStringLiteral("*** %0 ***").arg(msg.trimmed());
1155     emit inferiorStderrLines(QStringList(m));
1156 
1157     /* Also show message in gdb window, so that users who
1158        prefer to look at gdb window know what's up.  */
1159     emit debuggerUserCommandOutput(m);
1160 }
1161 
explainDebuggerStatus()1162 void MIDebugSession::explainDebuggerStatus()
1163 {
1164     MICommand* currentCmd_ = m_debugger->currentCommand();
1165     QString information =
1166         i18np("1 command in queue\n", "%1 commands in queue\n", m_commandQueue->count()) +
1167         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)) +
1168         i18n("Debugger state: %1\n", m_debuggerState);
1169 
1170     if (currentCmd_) {
1171         QString extra = i18n("Current command class: '%1'\n"
1172                              "Current command text: '%2'\n"
1173                              "Current command original text: '%3'\n",
1174                              QString::fromUtf8(typeid(*currentCmd_).name()),
1175                              currentCmd_->cmdToSend(),
1176                              currentCmd_->initialString());
1177 
1178         information += extra;
1179     }
1180 
1181     auto* message = new Sublime::Message(information, Sublime::Message::Information);
1182     ICore::self()->uiController()->postMessage(message);
1183 }
1184 
1185 // There is no app anymore. This can be caused by program exiting
1186 // an invalid program specified or ...
1187 // gdb is still running though, but only the run command (may) make sense
1188 // all other commands are disabled.
handleNoInferior(const QString & msg)1189 void MIDebugSession::handleNoInferior(const QString& msg)
1190 {
1191     qCDebug(DEBUGGERCOMMON) << msg;
1192 
1193     setDebuggerState(s_appNotStarted | s_programExited | (debuggerState() & s_shuttingDown));
1194 
1195     destroyCmds();
1196 
1197     // The application has existed, but it's possible that
1198     // some of application output is still in the pipe. We use
1199     // different pipes to communicate with gdb and to get application
1200     // output, so "exited" message from gdb might have arrived before
1201     // last application output. Get this last bit.
1202 
1203     // Note: this method can be called when we open an invalid
1204     // core file. In that case, tty_ won't be set.
1205     if (m_tty){
1206         m_tty->readRemaining();
1207         // Tty is no longer usable, delete it. Without this, QSocketNotifier
1208         // will continuously bomd STTY with signals, so we need to either disable
1209         // QSocketNotifier, or delete STTY. The latter is simpler, since we can't
1210         // reuse it for future debug sessions anyway.
1211         m_tty.reset(nullptr);
1212     }
1213 
1214     stopDebugger();
1215 
1216     raiseEvent(program_exited);
1217     raiseEvent(debugger_exited);
1218 
1219     emit showMessage(msg, 0);
1220 
1221     handleInferiorFinished(msg);
1222 }
1223 
handleInferiorFinished(const QString & msg)1224 void MIDebugSession::handleInferiorFinished(const QString& msg)
1225 {
1226     QString m = QStringLiteral("*** %0 ***").arg(msg.trimmed());
1227     emit inferiorStderrLines(QStringList(m));
1228 
1229     /* Also show message in gdb window, so that users who
1230        prefer to look at gdb window know what's up.  */
1231     emit debuggerUserCommandOutput(m);
1232 }
1233 
1234 // FIXME: connect to debugger's slot.
defaultErrorHandler(const MI::ResultRecord & result)1235 void MIDebugSession::defaultErrorHandler(const MI::ResultRecord& result)
1236 {
1237     QString msg = result[QStringLiteral("msg")].literal();
1238 
1239     if (msg.contains(QLatin1String("No such process")))
1240     {
1241         setDebuggerState(s_appNotStarted|s_programExited);
1242         raiseEvent(program_exited);
1243         return;
1244     }
1245 
1246     const QString messageText =
1247         i18n("<b>Debugger error</b>"
1248              "<p>Debugger reported the following error:"
1249              "<p><tt>%1", result[QStringLiteral("msg")].literal());
1250     auto* message = new Sublime::Message(messageText, Sublime::Message::Error);
1251     ICore::self()->uiController()->postMessage(message);
1252 
1253     // Error most likely means that some change made in GUI
1254     // was not communicated to the gdb, so GUI is now not
1255     // in sync with gdb. Resync it.
1256     //
1257     // Another approach is to make each widget reload it content
1258     // on errors from commands that it sent, but that's too complex.
1259     // Errors are supposed to happen rarely, so full reload on error
1260     // is not a big deal. Well, maybe except for memory view, but
1261     // it's no auto-reloaded anyway.
1262     //
1263     // Also, don't reload state on errors appeared during state
1264     // reloading!
1265     if (!m_debugger->currentCommand()->stateReloading())
1266         raiseEvent(program_state_changed);
1267 }
1268 
setSourceInitFile(bool enable)1269 void MIDebugSession::setSourceInitFile(bool enable)
1270 {
1271     m_sourceInitFile = enable;
1272 }
1273