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