1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "debuggerplugin.h"
27
28 #include "debuggeractions.h"
29 #include "debuggerinternalconstants.h"
30 #include "debuggercore.h"
31 #include "debuggerdialogs.h"
32 #include "debuggerengine.h"
33 #include "debuggericons.h"
34 #include "debuggeritem.h"
35 #include "debuggeritemmanager.h"
36 #include "debuggermainwindow.h"
37 #include "debuggerrunconfigurationaspect.h"
38 #include "debuggerruncontrol.h"
39 #include "debuggerkitinformation.h"
40 #include "memoryagent.h"
41 #include "breakhandler.h"
42 #include "disassemblerlines.h"
43 #include "enginemanager.h"
44 #include "logwindow.h"
45 #include "moduleshandler.h"
46 #include "stackhandler.h"
47 #include "stackwindow.h"
48 #include "watchhandler.h"
49 #include "watchwindow.h"
50 #include "watchutils.h"
51 #include "unstartedappwatcherdialog.h"
52 #include "localsandexpressionswindow.h"
53 #include "loadcoredialog.h"
54 #include "sourceutils.h"
55 #include "shared/hostutils.h"
56 #include "console/console.h"
57
58 #include "threadshandler.h"
59 #include "commonoptionspage.h"
60
61 #include "analyzer/analyzerconstants.h"
62 #include "analyzer/analyzermanager.h"
63
64 #include <app/app_version.h>
65
66 #include <coreplugin/actionmanager/actioncontainer.h>
67 #include <coreplugin/actionmanager/actionmanager.h>
68 #include <coreplugin/actionmanager/command.h>
69 #include <coreplugin/coreconstants.h>
70 #include <coreplugin/editormanager/documentmodel.h>
71 #include <coreplugin/editormanager/editormanager.h>
72 #include <coreplugin/find/itemviewfind.h>
73 #include <coreplugin/findplaceholder.h>
74 #include <coreplugin/icore.h>
75 #include <coreplugin/imode.h>
76 #include <coreplugin/messagebox.h>
77 #include <coreplugin/messagemanager.h>
78 #include <coreplugin/modemanager.h>
79 #include <coreplugin/modemanager.h>
80 #include <coreplugin/navigationwidget.h>
81 #include <coreplugin/outputpane.h>
82 #include <coreplugin/rightpane.h>
83
84 #include <extensionsystem/pluginmanager.h>
85
86 #include <cppeditor/cppeditorconstants.h>
87 #include <qmljseditor/qmljseditorconstants.h>
88
89 #include <projectexplorer/buildconfiguration.h>
90 #include <projectexplorer/buildmanager.h>
91 #include <projectexplorer/devicesupport/deviceprocessesdialog.h>
92 #include <projectexplorer/devicesupport/deviceprocesslist.h>
93 #include <projectexplorer/itaskhandler.h>
94 #include <projectexplorer/project.h>
95 #include <projectexplorer/projectexplorer.h>
96 #include <projectexplorer/projectexplorericons.h>
97 #include <projectexplorer/projectexplorersettings.h>
98 #include <projectexplorer/projecttree.h>
99 #include <projectexplorer/runconfiguration.h>
100 #include <projectexplorer/session.h>
101 #include <projectexplorer/target.h>
102 #include <projectexplorer/taskhub.h>
103 #include <projectexplorer/toolchain.h>
104 #include <ssh/sshconnection.h>
105
106 #include <texteditor/texteditor.h>
107 #include <texteditor/textdocument.h>
108 #include <texteditor/fontsettings.h>
109 #include <texteditor/texteditorsettings.h>
110
111 #include <utils/algorithm.h>
112 #include <utils/appmainwindow.h>
113 #include <utils/basetreeview.h>
114 #include <utils/checkablemessagebox.h>
115 #include <utils/fancymainwindow.h>
116 #include <utils/hostosinfo.h>
117 #include <utils/proxyaction.h>
118 #include <utils/qtcassert.h>
119 #include <utils/statuslabel.h>
120 #include <utils/styledbar.h>
121 #include <utils/temporarydirectory.h>
122 #include <utils/utilsicons.h>
123 #include <utils/winutils.h>
124
125 #include <QAction>
126 #include <QApplication>
127 #include <QCheckBox>
128 #include <QComboBox>
129 #include <QDebug>
130 #include <QDialog>
131 #include <QDialogButtonBox>
132 #include <QDockWidget>
133 #include <QFileDialog>
134 #include <QHBoxLayout>
135 #include <QHeaderView>
136 #include <QInputDialog>
137 #include <QMenu>
138 #include <QMessageBox>
139 #include <QPointer>
140 #include <QPushButton>
141 #include <QSettings>
142 #include <QStackedWidget>
143 #include <QTextBlock>
144 #include <QToolButton>
145 #include <QTreeWidget>
146 #include <QVBoxLayout>
147 #include <QVariant>
148 #include <QJsonDocument>
149 #include <QJsonObject>
150
151 #ifdef WITH_TESTS
152
153 #include <cpptools/cpptoolstestcase.h>
154 #include <cpptools/projectinfo.h>
155
156 #include <utils/executeondestruction.h>
157
158 #include <QTest>
159 #include <QSignalSpy>
160 #include <QTestEventLoop>
161
162 //#define WITH_BENCHMARK
163 #ifdef WITH_BENCHMARK
164 #include <valgrind/callgrind.h>
165 #endif
166
167 #endif // WITH_TESTS
168
169 #include <climits>
170
171 #define DEBUG_STATE 1
172 #ifdef DEBUG_STATE
173 //# define STATE_DEBUG(s)
174 // do { QString msg; QTextStream ts(&msg); ts << s;
175 // showMessage(msg, LogDebug); } while (0)
176 # define STATE_DEBUG(s) do { qDebug() << s; } while (0)
177 #else
178 # define STATE_DEBUG(s)
179 #endif
180
181 /*!
182 \namespace Debugger
183 Debugger plugin namespace
184 */
185
186 /*!
187 \namespace Debugger::Internal
188 Internal namespace of the Debugger plugin
189 \internal
190 */
191
192 /*!
193 \class Debugger::DebuggerEngine
194
195 \brief The DebuggerEngine class is the base class of a debugger engine.
196
197 \note The Debugger process itself and any helper processes like
198 gdbserver are referred to as 'Engine', whereas the debugged process
199 is referred to as 'Inferior'.
200
201 Transitions marked by '---' are done in the individual engines.
202 Transitions marked by '+-+' are done in the base DebuggerEngine.
203 Transitions marked by '*' are done asynchronously.
204
205 The GdbEngine->setupEngine() function is described in more detail below.
206
207 The engines are responsible for local roll-back to the last
208 acknowledged state before calling notify*Failed. I.e. before calling
209 notifyEngineSetupFailed() any process started during setupEngine()
210 so far must be terminated.
211 \code
212
213 DebuggerNotReady
214 progressmanager/progressmanager.cpp +
215 EngineSetupRequested
216 +
217 (calls *Engine->setupEngine())
218 | |
219 | |
220 {notify- {notify-
221 Engine- Engine-
222 SetupOk} SetupFailed}
223 + | +
224 EngineRunRequested <+-+' | `+-+-+> EngineSetupFailed
225 | +
226 | [calls RunControl->startFailed]
227 | +
228 | DebuggerFinished
229 |
230 ------------------------
231 / | | \
232 / | | \
233 / | | \
234 | (core) | (attach) | |
235 | | | |
236 {notify- {notifyER&- {notifyER&- {notify-
237 Inferior- Inferior- Inferior- EngineRun-
238 Unrunnable} StopOk} RunOk} Failed}
239 + + + +
240 InferiorUnrunnable + InferiorRunOk +
241 + +
242 InferiorStopOk EngineRunFailed
243 +
244 `-+-+-+-+-+-+-+-+-+-+-+>-+
245 +
246 +
247 #Interrupt@InferiorRunOk# +
248 + +
249 InferiorStopRequested +
250 #SpontaneousStop + +
251 @InferiorRunOk# (calls *Engine-> +
252 + interruptInferior()) +
253 {notify- | | +
254 Spontaneous- {notify- {notify- +
255 Inferior- Inferior- Inferior- +
256 StopOk} StopOk} StopFailed} +
257 + + + +
258 + + + +
259 InferiorStopOk + +
260 + + +
261 + + +
262 + + +
263 #Stop@InferiorUnrunnable# + +
264 #Creator Close Event# + +
265 + + +
266 InferiorShutdownRequested +
267 + +
268 (calls *Engine->shutdownInferior()) +
269 | +
270 {notifyInferiorShutdownFinished} +
271 + +
272 + +
273 #Inferior exited# + +
274 | + +
275 {notifyInferior- + +
276 Exited} + +
277 + + +
278 + + +
279 + + +
280 InferiorShutdownFinished +
281 * +
282 EngineShutdownRequested +
283 + +
284 (calls *Engine->shutdownEngine()) <+-+-+-+-+-+-+-+-+-+-+-+-+-+'
285 |
286 |
287 {notifyEngineShutdownFinished}
288 +
289 EngineShutdownFinished
290 *
291 DebuggerFinished
292
293 \endcode */
294
295 /* Here is a matching graph as a GraphViz graph. View it using
296 * \code
297 grep "^sg1:" debuggerplugin.cpp | cut -c5- | dot -osg1.ps -Tps && gv sg1.ps
298
299 sg1: digraph DebuggerStates {
300 sg1: DebuggerNotReady -> EngineSetupRequested
301 sg1: EngineSetupRequested -> EngineSetupOk [ label="notifyEngineSetupOk", style="dashed" ];
302 sg1: EngineSetupRequested -> EngineSetupFailed [ label= "notifyEngineSetupFailed", style="dashed"];
303 sg1: EngineSetupFailed -> DebuggerFinished [ label= "RunControl::StartFailed" ];
304 sg1: EngineSetupOk -> EngineRunRequested [ label= "RunControl::StartSuccessful" ];
305 sg1: EngineRunRequested -> InferiorUnrunnable [ label="notifyInferiorUnrunnable", style="dashed" ];
306 sg1: EngineRunRequested -> InferiorStopOk [ label="notifyEngineRunAndInferiorStopOk", style="dashed" ];
307 sg1: EngineRunRequested -> InferiorRunOk [ label="notifyEngineRunAndInferiorRunOk", style="dashed" ];
308 sg1: EngineRunRequested -> EngineRunFailed [ label="notifyEngineRunFailed", style="dashed" ];
309 sg1: EngineRunFailed -> EngineShutdownRequested
310 sg1: InferiorRunOk -> InferiorStopOk [ label="SpontaneousStop\nnotifyInferiorSpontaneousStop", style="dashed" ];
311 sg1: InferiorRunOk -> InferiorStopRequested [ label="User stop\nEngine::interruptInferior", style="dashed"];
312 sg1: InferiorStopRequested -> InferiorStopOk [ label="notifyInferiorStopOk", style="dashed" ];
313 sg1: InferiorStopRequested -> InferiorShutdownRequested [ label="notifyInferiorStopFailed", style="dashed" ];
314 sg1: InferiorStopOk -> InferiorRunRequested [ label="User\nEngine::continueInferior" ];
315 sg1: InferiorRunRequested -> InferiorRunOk [ label="notifyInferiorRunOk", style="dashed"];
316 sg1: InferiorRunRequested -> InferiorRunFailed [ label="notifyInferiorRunFailed", style="dashed"];
317 sg1: InferiorRunFailed -> InferiorStopOk
318 sg1: InferiorStopOk -> InferiorShutdownRequested [ label="Close event" ];
319 sg1: InferiorUnrunnable -> InferiorShutdownRequested [ label="Close event" ];
320 sg1: InferiorShutdownRequested -> InferiorShutdownFinished [ label= "Engine::shutdownInferior\nnotifyInferiorShutdownFinished", style="dashed" ];
321 sg1: InferiorExited -> InferiorExitOk [ label="notifyInferiorExited", style="dashed"];
322 sg1: InferiorExitOk -> InferiorShutdownOk
323 sg1: InferiorShutdownFinished -> EngineShutdownRequested
324 sg1: EngineShutdownRequested -> EngineShutdownFinished [ label="Engine::shutdownEngine\nnotifyEngineShutdownFinished", style="dashed" ];
325 sg1: EngineShutdownFinished -> DebuggerFinished [ style = "dotted" ];
326 sg1: }
327 * \endcode */
328 // Additional signalling: {notifyInferiorIll} {notifyEngineIll}
329
330
331 /*!
332 \class Debugger::Internal::GdbEngine
333 \brief The GdbEngine class implements Debugger::Engine driving a GDB
334 executable.
335
336 GdbEngine specific startup. All happens in EngineSetupRequested state:
337
338 \list
339 \li Transitions marked by '---' are done in the individual adapters.
340
341 \li Transitions marked by '+-+' are done in the GdbEngine.
342 \endlist
343
344 \code
345 GdbEngine::setupEngine()
346 +
347 (calls *Adapter->startAdapter())
348 | |
349 | `---> handleAdapterStartFailed()
350 | +
351 | {notifyEngineSetupFailed}
352 |
353 handleAdapterStarted()
354 +
355 {notifyEngineSetupOk}
356
357
358
359 GdbEngine::setupInferior()
360 +
361 (calls *Adapter->prepareInferior())
362 | |
363 | `---> handlePrepareInferiorFailed()
364 | +
365 | {notifyInferiorSetupFailed}
366 |
367 handleInferiorPrepared()
368 +
369 {notifyInferiorSetupOk}
370
371 \endcode */
372
373 using namespace Core;
374 using namespace Core::Constants;
375 using namespace Debugger::Constants;
376 using namespace Debugger::Internal;
377 using namespace ExtensionSystem;
378 using namespace ProjectExplorer;
379 using namespace TextEditor;
380 using namespace Utils;
381
382 namespace CC = Core::Constants;
383 namespace PE = ProjectExplorer::Constants;
384
385 namespace Debugger {
386 namespace Internal {
387
388 const char DEBUGGER_START[] = "Debugger.Start";
389
390 // Menu Groups
391 const char MENU_GROUP_GENERAL[] = "Debugger.Group.General";
392 const char MENU_GROUP_SPECIAL[] = "Debugger.Group.Special";
393 const char MENU_GROUP_START_QML[] = "Debugger.Group.Start.Qml";
394
395 void addCdbOptionPages(QList<IOptionsPage*> *opts);
396 void addGdbOptionPages(QList<IOptionsPage*> *opts);
397
startIcon(bool toolBarStyle)398 static QIcon startIcon(bool toolBarStyle)
399 {
400 const static QIcon sidebarIcon =
401 Icon::sideBarIcon(ProjectExplorer::Icons::DEBUG_START, ProjectExplorer::Icons::DEBUG_START_FLAT);
402 const static QIcon icon =
403 Icon::combinedIcon({ProjectExplorer::Icons::DEBUG_START_SMALL.icon(), sidebarIcon});
404 const static QIcon iconToolBar =
405 Icon::combinedIcon({ProjectExplorer::Icons::DEBUG_START_SMALL_TOOLBAR.icon(), sidebarIcon});
406 return toolBarStyle ? iconToolBar : icon;
407 }
408
continueIcon(bool toolBarStyle)409 static QIcon continueIcon(bool toolBarStyle)
410 {
411 const static QIcon sidebarIcon =
412 Icon::sideBarIcon(Icons::CONTINUE, Icons::CONTINUE_FLAT);
413 const static QIcon icon =
414 Icon::combinedIcon({Icons::DEBUG_CONTINUE_SMALL.icon(), sidebarIcon});
415 const static QIcon iconToolBar =
416 Icon::combinedIcon({Icons::DEBUG_CONTINUE_SMALL_TOOLBAR.icon(), sidebarIcon});
417 return toolBarStyle ? iconToolBar : icon;
418 }
419
interruptIcon(bool toolBarStyle)420 static QIcon interruptIcon(bool toolBarStyle)
421 {
422 const static QIcon sidebarIcon =
423 Icon::sideBarIcon(Icons::INTERRUPT, Icons::INTERRUPT_FLAT);
424 const static QIcon icon =
425 Icon::combinedIcon({Icons::DEBUG_INTERRUPT_SMALL.icon(), sidebarIcon});
426 const static QIcon iconToolBar =
427 Icon::combinedIcon({Icons::DEBUG_INTERRUPT_SMALL_TOOLBAR.icon(), sidebarIcon});
428 return toolBarStyle ? iconToolBar : icon;
429 }
430
addAction(QMenu * menu,const QString & display,bool on,const std::function<void ()> & onTriggered)431 QAction *addAction(QMenu *menu, const QString &display, bool on,
432 const std::function<void()> &onTriggered)
433 {
434 QAction *act = menu->addAction(display);
435 act->setEnabled(on);
436 QObject::connect(act, &QAction::triggered, onTriggered);
437 return act;
438 };
439
addAction(QMenu * menu,const QString & d1,const QString & d2,bool on,const std::function<void ()> & onTriggered)440 QAction *addAction(QMenu *menu, const QString &d1, const QString &d2, bool on,
441 const std::function<void()> &onTriggered)
442 {
443 return on ? addAction(menu, d1, true, onTriggered) : addAction(menu, d2, false);
444 };
445
addCheckableAction(QMenu * menu,const QString & display,bool on,bool checked,const std::function<void ()> & onTriggered)446 QAction *addCheckableAction(QMenu *menu, const QString &display, bool on, bool checked,
447 const std::function<void()> &onTriggered)
448 {
449 QAction *act = addAction(menu, display, on, onTriggered);
450 act->setCheckable(true);
451 act->setChecked(checked);
452 return act;
453 }
454
455 ///////////////////////////////////////////////////////////////////////
456 //
457 // DebugMode
458 //
459 ///////////////////////////////////////////////////////////////////////
460
461 class DebugMode : public IMode
462 {
463 public:
DebugMode()464 DebugMode()
465 {
466 setObjectName("DebugMode");
467 setContext(Context(C_DEBUGMODE, CC::C_NAVIGATION_PANE));
468 setDisplayName(DebuggerPlugin::tr("Debug"));
469 setIcon(Utils::Icon::modeIcon(Icons::MODE_DEBUGGER_CLASSIC,
470 Icons::MODE_DEBUGGER_FLAT, Icons::MODE_DEBUGGER_FLAT_ACTIVE));
471 setPriority(85);
472 setId(MODE_DEBUG);
473
474 DebuggerMainWindow *mainWindow = DebuggerMainWindow::instance();
475
476 auto editorHolderLayout = new QVBoxLayout;
477 editorHolderLayout->setContentsMargins(0, 0, 0, 0);
478 editorHolderLayout->setSpacing(0);
479
480 auto editorAndFindWidget = new QWidget;
481 editorAndFindWidget->setLayout(editorHolderLayout);
482 editorHolderLayout->addWidget(DebuggerMainWindow::centralWidgetStack());
483 editorHolderLayout->addWidget(new FindToolBarPlaceHolder(editorAndFindWidget));
484
485 auto documentAndRightPane = new MiniSplitter;
486 documentAndRightPane->addWidget(editorAndFindWidget);
487 documentAndRightPane->addWidget(new RightPanePlaceHolder(MODE_DEBUG));
488 documentAndRightPane->setStretchFactor(0, 1);
489 documentAndRightPane->setStretchFactor(1, 0);
490
491 auto centralEditorWidget = mainWindow->centralWidget();
492 auto centralLayout = new QVBoxLayout(centralEditorWidget);
493 centralEditorWidget->setLayout(centralLayout);
494 centralLayout->setContentsMargins(0, 0, 0, 0);
495 centralLayout->setSpacing(0);
496 centralLayout->addWidget(documentAndRightPane);
497 centralLayout->setStretch(0, 1);
498 centralLayout->setStretch(1, 0);
499
500 // Right-side window with editor, output etc.
501 auto mainWindowSplitter = new MiniSplitter;
502 mainWindowSplitter->addWidget(mainWindow);
503 mainWindowSplitter->addWidget(new OutputPanePlaceHolder(MODE_DEBUG, mainWindowSplitter));
504 auto outputPane = new OutputPanePlaceHolder(MODE_DEBUG, mainWindowSplitter);
505 outputPane->setObjectName("DebuggerOutputPanePlaceHolder");
506 mainWindowSplitter->addWidget(outputPane);
507 mainWindowSplitter->setStretchFactor(0, 10);
508 mainWindowSplitter->setStretchFactor(1, 0);
509 mainWindowSplitter->setOrientation(Qt::Vertical);
510
511 // Navigation and right-side window.
512 auto splitter = new MiniSplitter;
513 splitter->setFocusProxy(DebuggerMainWindow::centralWidgetStack());
514 splitter->addWidget(new NavigationWidgetPlaceHolder(MODE_DEBUG, Side::Left));
515 splitter->addWidget(mainWindowSplitter);
516 splitter->setStretchFactor(0, 0);
517 splitter->setStretchFactor(1, 1);
518 splitter->setObjectName("DebugModeWidget");
519
520 mainWindow->addSubPerspectiveSwitcher(EngineManager::engineChooser());
521
522 setWidget(splitter);
523
524 setMenu(DebuggerMainWindow::perspectiveMenu());
525 }
526
~DebugMode()527 ~DebugMode() { delete widget(); }
528 };
529
530 ///////////////////////////////////////////////////////////////////////
531 //
532 // Misc
533 //
534 ///////////////////////////////////////////////////////////////////////
535
cdbPredicate(char wordWidth=0)536 static Kit::Predicate cdbPredicate(char wordWidth = 0)
537 {
538 return [wordWidth](const Kit *k) -> bool {
539 if (DebuggerKitAspect::engineType(k) != CdbEngineType
540 || DebuggerKitAspect::configurationErrors(k)) {
541 return false;
542 }
543 if (wordWidth)
544 return ToolChainKitAspect::targetAbi(k).wordWidth() == wordWidth;
545 return true;
546 };
547 }
548
549 // Find a CDB kit for debugging unknown processes.
550 // On a 64bit OS, prefer a 64bit debugger.
findUniversalCdbKit()551 static Kit *findUniversalCdbKit()
552 {
553 if (Utils::is64BitWindowsSystem()) {
554 if (Kit *cdb64Kit = KitManager::kit(cdbPredicate(64)))
555 return cdb64Kit;
556 }
557 return KitManager::kit(cdbPredicate());
558 }
559
560 ///////////////////////////////////////////////////////////////////////
561 //
562 // DebuggerPluginPrivate
563 //
564 ///////////////////////////////////////////////////////////////////////
565
566 static DebuggerPlugin *m_instance = nullptr;
567 static DebuggerPluginPrivate *dd = nullptr;
568
569 /*!
570 \class Debugger::Internal::DebuggerCore
571
572 This is the "internal" interface of the debugger plugin that's
573 used by debugger views and debugger engines. The interface is
574 implemented in DebuggerPluginPrivate.
575 */
576
577 /*!
578 \class Debugger::Internal::DebuggerPluginPrivate
579
580 Implementation of DebuggerCore.
581 */
582
583 class DebuggerPluginPrivate : public QObject
584 {
585 Q_OBJECT
586
587 public:
588 explicit DebuggerPluginPrivate(const QStringList &arguments);
589 ~DebuggerPluginPrivate() override;
590
591 void extensionsInitialized();
592 void aboutToShutdown();
593
594 RunControl *attachToRunningProcess(Kit *kit, DeviceProcessItem process, bool contAfterAttach);
595
writeSettings()596 void writeSettings()
597 {
598 m_debuggerSettings.writeSettings();
599 // writeWindowSettings();
600 }
601
breakpointSetMarginActionTriggered(bool isMessageOnly,const ContextData & data)602 void breakpointSetMarginActionTriggered(bool isMessageOnly, const ContextData &data)
603 {
604 QString message;
605 if (isMessageOnly) {
606 if (data.type == LocationByAddress) {
607 //: Message tracepoint: Address hit.
608 message = tr("0x%1 hit").arg(data.address, 0, 16);
609 } else {
610 //: Message tracepoint: %1 file, %2 line %3 function hit.
611 message = tr("%1:%2 %3() hit").arg(data.fileName.fileName()).
612 arg(data.lineNumber).
613 arg(cppFunctionAt(data.fileName.toString(), data.lineNumber));
614 }
615 QInputDialog dialog; // Create wide input dialog.
616 dialog.setWindowFlags(dialog.windowFlags()
617 & ~(Qt::MSWindowsFixedSizeDialogHint));
618 dialog.resize(600, dialog.height());
619 dialog.setWindowTitle(tr("Add Message Tracepoint"));
620 dialog.setLabelText (tr("Message:"));
621 dialog.setTextValue(message);
622 if (dialog.exec() != QDialog::Accepted || dialog.textValue().isEmpty())
623 return;
624 message = dialog.textValue();
625 }
626 BreakpointManager::toggleBreakpoint(data, message);
627 }
628
629 void editorOpened(IEditor *editor);
630 void updateBreakMenuItem(IEditor *editor);
631 void requestMark(TextEditorWidget *widget, int lineNumber,
632 TextMarkRequestKind kind);
633 void requestContextMenu(TextEditorWidget *widget,
634 int lineNumber, QMenu *menu);
635
636 void toggleBreakpointHelper();
637 void updateDebugWithoutDeployMenu();
638
639 void startRemoteCdbSession();
640 void attachToRunningApplication();
641 void attachToUnstartedApplicationDialog();
642 void attachToQmlPort();
643 void runScheduled();
644 void attachCore();
645 void reloadDebuggingHelpers();
646
647 void remoteCommand(const QStringList &options);
648
649 void dumpLog();
650 void setInitialState();
651
652 void onStartupProjectChanged(Project *project);
653
654 bool parseArgument(QStringList::const_iterator &it,
655 const QStringList::const_iterator &cend, QString *errorMessage);
656 bool parseArguments(const QStringList &args, QString *errorMessage);
657 void parseCommandLineArguments();
658
659 void updatePresetState();
660 QWidget *addSearch(BaseTreeView *treeView);
661
662 public:
663 QPointer<DebugMode> m_mode;
664
665 ActionContainer *m_menu = nullptr;
666
667 QVector<DebuggerRunTool *> m_scheduledStarts;
668
669 ProxyAction m_visibleStartAction; // The fat debug button
670 ProxyAction m_hiddenStopAction;
671 QAction m_undisturbableAction;
672 OptionalAction m_startAction;
673 QAction m_debugWithoutDeployAction{tr("Start Debugging Without Deployment")};
674 QAction m_startAndDebugApplicationAction{tr("Start and Debug External Application...")};
675 QAction m_attachToRunningApplication{tr("Attach to Running Application...")};
676 QAction m_attachToUnstartedApplication{tr("Attach to Unstarted Application...")};
677 QAction m_attachToQmlPortAction{tr("Attach to QML Port...")};
678 QAction m_attachToRemoteServerAction{tr("Attach to Running Debug Server...")};
679 QAction m_startRemoteCdbAction{tr("Attach to Remote CDB Session...")};
680 QAction m_attachToCoreAction{tr("Load Core File...")};
681
682 // In the Debug menu.
683 QAction m_startAndBreakOnMain{tr("Start and Break on Main")};
684 QAction m_watchAction{tr("Add Expression Evaluator")};
685 Command *m_watchCommand = nullptr;
686 QAction m_breakAction{tr("Toggle Breakpoint")};
687 QAction m_reloadDebuggingHelpersAction{tr("Reload Debugging Helpers")};
688
689 BreakpointManager m_breakpointManager;
690 QString m_lastPermanentStatusMessage;
691
692 EngineManager m_engineManager;
693 QTimer m_shutdownTimer;
694 bool m_shuttingDown = false;
695
696 Console m_console; // ensure Debugger Console is created before settings are taken into account
697 DebuggerSettings m_debuggerSettings;
698 QStringList m_arguments;
699
700 DebuggerItemManager m_debuggerItemManager;
701
702 QList<IOptionsPage *> m_optionPages;
703 IContext m_debugModeContext;
704
705 Perspective m_perspective{Constants::PRESET_PERSPECTIVE_ID, tr("Debugger")};
706
707 DebuggerKitAspect debuggerKitAspect;
708 CommonOptionsPage commonOptionsPage;
709
710 RunWorkerFactory debuggerWorkerFactory{
711 RunWorkerFactory::make<DebuggerRunTool>(),
712 {ProjectExplorer::Constants::DEBUG_RUN_MODE},
713 {}, // All local run configs?
714 {PE::DESKTOP_DEVICE_TYPE, "DockerDeviceType"}
715 };
716
717 // FIXME: Needed?
718 // QString mainScript = runConfig->property("mainScript").toString();
719 // const bool isDebuggableScript = mainScript.endsWith(".py"); // Only Python for now.
720 // return isDebuggableScript;
721 };
722
DebuggerPluginPrivate(const QStringList & arguments)723 DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments)
724 {
725 qRegisterMetaType<ContextData>("ContextData");
726 qRegisterMetaType<DebuggerRunParameters>("DebuggerRunParameters");
727 qRegisterMetaType<QString *>();
728
729 // Menu groups
730 ActionContainer *mstart = ActionManager::actionContainer(PE::M_DEBUG_STARTDEBUGGING);
731 mstart->appendGroup(MENU_GROUP_GENERAL);
732 mstart->appendGroup(MENU_GROUP_SPECIAL);
733 mstart->appendGroup(MENU_GROUP_START_QML);
734
735 // Separators
736 mstart->addSeparator(MENU_GROUP_GENERAL);
737 mstart->addSeparator(MENU_GROUP_SPECIAL);
738
739 // Task integration.
740 //: Category under which Analyzer tasks are listed in Issues view
741 TaskHub::addCategory(ANALYZERTASK_ID, tr("Debugger"));
742
743 const Context debuggerNotRunning(C_DEBUGGER_NOTRUNNING);
744 ICore::addAdditionalContext(debuggerNotRunning);
745
746 m_arguments = arguments;
747 if (!m_arguments.isEmpty())
748 connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::finishedInitialization,
749 this, &DebuggerPluginPrivate::parseCommandLineArguments);
750
751 // Menus
752 m_menu = ActionManager::createMenu(M_DEBUG_ANALYZER);
753 m_menu->menu()->setTitle(tr("&Analyze"));
754 m_menu->menu()->setEnabled(true);
755
756 m_menu->appendGroup(G_ANALYZER_CONTROL);
757 m_menu->appendGroup(G_ANALYZER_TOOLS);
758 m_menu->appendGroup(G_ANALYZER_REMOTE_TOOLS);
759 m_menu->appendGroup(G_ANALYZER_OPTIONS);
760
761 ActionContainer *touchBar = ActionManager::createTouchBar("Debugger.TouchBar",
762 Icons::MACOS_TOUCHBAR_DEBUG.icon());
763 ActionManager::actionContainer(Core::Constants::TOUCH_BAR)
764 ->addMenu(touchBar, Core::Constants::G_TOUCHBAR_OTHER);
765
766 ActionContainer *menubar = ActionManager::actionContainer(MENU_BAR);
767 ActionContainer *mtools = ActionManager::actionContainer(M_TOOLS);
768 menubar->addMenu(mtools, m_menu);
769
770 m_menu->addSeparator(G_ANALYZER_TOOLS);
771 m_menu->addSeparator(G_ANALYZER_REMOTE_TOOLS);
772 m_menu->addSeparator(G_ANALYZER_OPTIONS);
773
774 QAction *act;
775
776 // Populate Windows->Views menu with standard actions.
777 act = new QAction(tr("Memory..."), this);
778 act->setVisible(false);
779 act->setEnabled(false);
780 Command *cmd = ActionManager::registerAction(act, Constants::OPEN_MEMORY_EDITOR);
781
782 TaskHub::addCategory(TASK_CATEGORY_DEBUGGER_DEBUGINFO, tr("Debug Information"));
783 TaskHub::addCategory(TASK_CATEGORY_DEBUGGER_RUNTIME, tr("Debugger Runtime"));
784
785 m_debuggerSettings.readSettings();
786
787 const auto addLabel = [](QWidget *widget, const QString &text) {
788 auto vbox = qobject_cast<QVBoxLayout *>(widget->layout());
789 QTC_ASSERT(vbox, return);
790 auto label = new QLabel(widget);
791 label->setText(text);
792 label->setContentsMargins(6, 6, 6, 6);
793 vbox->insertWidget(0, label);
794 };
795
796 const auto addFontSizeAdaptation = [this](QWidget *widget) {
797 QObject::connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged,
798 [widget](const FontSettings &settings) {
799 if (!debuggerSettings()->fontSizeFollowsEditor.value())
800 return;
801 qreal size = settings.fontZoom() * settings.fontSize() / 100.;
802 QFont font = widget->font();
803 font.setPointSizeF(size);
804 widget->setFont(font);
805 });
806 };
807
808 auto breakpointManagerView = new BaseTreeView;
809 breakpointManagerView->setActivationMode(Utils::DoubleClickActivation);
810 breakpointManagerView->setIconSize(QSize(10, 10));
811 breakpointManagerView->setWindowIcon(Icons::BREAKPOINTS.icon());
812 breakpointManagerView->setSelectionMode(QAbstractItemView::ExtendedSelection);
813 breakpointManagerView->setSettings(ICore::settings(), "Debugger.BreakWindow");
814 breakpointManagerView->setRootIsDecorated(true);
815 breakpointManagerView->setModel(BreakpointManager::model());
816 breakpointManagerView->setSpanColumn(BreakpointFunctionColumn);
817 breakpointManagerView->enableColumnHiding();
818
819 auto breakpointManagerWindow = addSearch(breakpointManagerView);
820 breakpointManagerWindow->setWindowTitle(tr("Breakpoint Preset"));
821 breakpointManagerWindow->setObjectName("Debugger.Docks.BreakpointManager");
822 addLabel(breakpointManagerWindow, breakpointManagerWindow->windowTitle());
823 addFontSizeAdaptation(breakpointManagerWindow);
824
825 // Snapshot
826 auto engineManagerView = new BaseTreeView;
827 engineManagerView->setWindowTitle(tr("Running Debuggers"));
828 engineManagerView->setSettings(ICore::settings(), "Debugger.SnapshotView");
829 engineManagerView->setIconSize(QSize(10, 10));
830 engineManagerView->setModel(EngineManager::model());
831 engineManagerView->setSelectionMode(QAbstractItemView::SingleSelection);
832 engineManagerView->enableColumnHiding();
833
834 auto engineManagerWindow = addSearch(engineManagerView);
835 engineManagerWindow->setWindowTitle(tr("Debugger Perspectives"));
836 engineManagerWindow->setObjectName("Debugger.Docks.Snapshots");
837 addLabel(engineManagerWindow, engineManagerWindow->windowTitle());
838 addFontSizeAdaptation(engineManagerWindow);
839
840 // Logging
841 auto globalLogWindow = new GlobalLogWindow;
842 addFontSizeAdaptation(globalLogWindow);
843
844 ActionContainer *debugMenu = ActionManager::actionContainer(PE::M_DEBUG);
845
846 RunConfiguration::registerAspect<DebuggerRunConfigurationAspect>();
847
848 // The main "Start Debugging" action. Acts as "Continue" at times.
849 connect(&m_startAction, &QAction::triggered, this, [] {
850 ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE, false);
851 });
852
853 connect(&m_debugWithoutDeployAction, &QAction::triggered, this, [] {
854 ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE, true);
855 });
856
857 connect(&m_startAndDebugApplicationAction, &QAction::triggered,
858 this, &StartApplicationDialog::startAndDebugApplication);
859
860 connect(&m_attachToCoreAction, &QAction::triggered,
861 this, &DebuggerPluginPrivate::attachCore);
862
863 connect(&m_attachToRemoteServerAction, &QAction::triggered,
864 this, &StartApplicationDialog::attachToRemoteServer);
865
866 connect(&m_attachToRunningApplication, &QAction::triggered,
867 this, &DebuggerPluginPrivate::attachToRunningApplication);
868
869 connect(&m_attachToUnstartedApplication, &QAction::triggered,
870 this, &DebuggerPluginPrivate::attachToUnstartedApplicationDialog);
871
872 connect(&m_attachToQmlPortAction, &QAction::triggered,
873 this, &DebuggerPluginPrivate::attachToQmlPort);
874
875 connect(&m_startRemoteCdbAction, &QAction::triggered,
876 this, &DebuggerPluginPrivate::startRemoteCdbSession);
877
878 // "Start Debugging" sub-menu
879 // groups:
880 // G_DEFAULT_ONE
881 // MENU_GROUP_START_LOCAL
882 // MENU_GROUP_START_REMOTE
883 // MENU_GROUP_START_QML
884
885 const QKeySequence startShortcut(useMacShortcuts ? tr("Ctrl+Y") : tr("F5"));
886
887 cmd = ActionManager::registerAction(&m_visibleStartAction, "Debugger.Debug");
888
889 cmd->setDescription(tr("Start Debugging or Continue"));
890 cmd->setAttribute(Command::CA_UpdateText);
891 cmd->setAttribute(Command::CA_UpdateIcon);
892 //mstart->addAction(cmd, CC::G_DEFAULT_ONE);
893
894 cmd = ActionManager::registerAction(&m_startAction, DEBUGGER_START);
895 cmd->setDescription(tr("Start Debugging"));
896 cmd->setAttribute(Command::CA_UpdateText);
897 cmd->setDefaultKeySequence(startShortcut);
898 mstart->addAction(cmd, CC::G_DEFAULT_ONE);
899
900 m_visibleStartAction.initialize(&m_startAction);
901 m_visibleStartAction.setAttribute(ProxyAction::UpdateText);
902 m_visibleStartAction.setAttribute(ProxyAction::UpdateIcon);
903 m_visibleStartAction.setAction(&m_startAction);
904
905 m_visibleStartAction.setObjectName("Debug"); // used for UI introduction
906 ModeManager::addAction(&m_visibleStartAction, /*priority*/ 90);
907
908 m_undisturbableAction.setIcon(interruptIcon(false));
909 m_undisturbableAction.setEnabled(false);
910
911 cmd = ActionManager::registerAction(&m_debugWithoutDeployAction,
912 "Debugger.DebugWithoutDeploy");
913 cmd->setAttribute(Command::CA_Hide);
914 mstart->addAction(cmd, CC::G_DEFAULT_ONE);
915
916 cmd = ActionManager::registerAction(&m_attachToRunningApplication,
917 "Debugger.AttachToRemoteProcess");
918 cmd->setDescription(tr("Attach to Running Application"));
919 mstart->addAction(cmd, MENU_GROUP_GENERAL);
920
921 cmd = ActionManager::registerAction(&m_attachToUnstartedApplication,
922 "Debugger.AttachToUnstartedProcess");
923 cmd->setDescription(tr("Attach to Unstarted Application"));
924 mstart->addAction(cmd, MENU_GROUP_GENERAL);
925
926 cmd = ActionManager::registerAction(&m_startAndDebugApplicationAction,
927 "Debugger.StartAndDebugApplication");
928 cmd->setAttribute(Command::CA_Hide);
929 mstart->addAction(cmd, MENU_GROUP_GENERAL);
930
931 cmd = ActionManager::registerAction(&m_attachToCoreAction,
932 "Debugger.AttachCore");
933 cmd->setAttribute(Command::CA_Hide);
934 mstart->addAction(cmd, MENU_GROUP_GENERAL);
935
936 cmd = ActionManager::registerAction(&m_attachToRemoteServerAction,
937 "Debugger.AttachToRemoteServer");
938 cmd->setAttribute(Command::CA_Hide);
939 mstart->addAction(cmd, MENU_GROUP_SPECIAL);
940
941 if (HostOsInfo::isWindowsHost()) {
942 cmd = ActionManager::registerAction(&m_startRemoteCdbAction,
943 "Debugger.AttachRemoteCdb");
944 cmd->setAttribute(Command::CA_Hide);
945 mstart->addAction(cmd, MENU_GROUP_SPECIAL);
946 }
947
948 mstart->addSeparator(Context(CC::C_GLOBAL), MENU_GROUP_START_QML);
949
950 cmd = ActionManager::registerAction(&m_attachToQmlPortAction, "Debugger.AttachToQmlPort");
951 cmd->setAttribute(Command::CA_Hide);
952 mstart->addAction(cmd, MENU_GROUP_START_QML);
953
954 act = new QAction(tr("Detach Debugger"), this);
955 act->setEnabled(false);
956 cmd = ActionManager::registerAction(act, Constants::DETACH);
957 debugMenu->addAction(cmd, CC::G_DEFAULT_ONE);
958
959 act = new QAction(interruptIcon(false), tr("Interrupt"), this);
960 act->setEnabled(false);
961 cmd = ActionManager::registerAction(act, Constants::INTERRUPT);
962 cmd->setDescription(tr("Interrupt Debugger"));
963 cmd->setAttribute(Command::CA_UpdateText);
964 cmd->setDefaultKeySequence(startShortcut);
965 cmd->setTouchBarIcon(Icons::MACOS_TOUCHBAR_DEBUG_INTERRUPT.icon());
966 touchBar->addAction(cmd);
967 debugMenu->addAction(cmd, CC::G_DEFAULT_ONE);
968
969 act = new QAction(continueIcon(false), tr("Continue"), this);
970 act->setEnabled(false);
971 cmd = ActionManager::registerAction(act, Constants::CONTINUE);
972 cmd->setAttribute(Command::CA_UpdateText);
973 cmd->setDefaultKeySequence(startShortcut);
974 cmd->setTouchBarIcon(Icons::MACOS_TOUCHBAR_DEBUG_CONTINUE.icon());
975 touchBar->addAction(cmd);
976 debugMenu->addAction(cmd, CC::G_DEFAULT_ONE);
977
978 const QIcon sidebarStopIcon = Icon::sideBarIcon(Icons::STOP, Icons::STOP_FLAT);
979 const QIcon stopIcon = Icon::combinedIcon({Icons::DEBUG_EXIT_SMALL.icon(), sidebarStopIcon});
980 act = new QAction(stopIcon, tr("Stop Debugger"), this);
981 act->setEnabled(false);
982 cmd = ActionManager::registerAction(act, Constants::STOP);
983 cmd->setTouchBarIcon(Icons::MACOS_TOUCHBAR_DEBUG_EXIT.icon());
984 touchBar->addAction(cmd);
985 debugMenu->addAction(cmd, CC::G_DEFAULT_ONE);
986 m_hiddenStopAction.initialize(cmd->action());
987 m_hiddenStopAction.setAttribute(ProxyAction::UpdateText);
988 m_hiddenStopAction.setAttribute(ProxyAction::UpdateIcon);
989
990 cmd = ActionManager::registerAction(&m_hiddenStopAction, "Debugger.HiddenStop");
991 cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Shift+Ctrl+Y") : tr("Shift+F5")));
992
993 act = new QAction(tr("Abort Debugging"), this);
994 act->setEnabled(false);
995 cmd = ActionManager::registerAction(act, Constants::ABORT);
996 cmd->setDescription(tr("Reset Debugger"));
997 debugMenu->addAction(cmd, CC::G_DEFAULT_ONE);
998
999 act = new QAction(Icons::RESTART_TOOLBAR.icon(), tr("Restart Debugging"), this);
1000 act->setEnabled(false);
1001 act->setToolTip(tr("Restart the debugging session."));
1002 cmd = ActionManager::registerAction(act, Constants::RESET);
1003 cmd->setDescription(tr("Restart Debugging"));
1004 debugMenu->addAction(cmd, CC::G_DEFAULT_ONE);
1005
1006 debugMenu->addSeparator();
1007
1008 cmd = ActionManager::registerAction(&m_startAndBreakOnMain,
1009 "Debugger.StartAndBreakOnMain",
1010 debuggerNotRunning);
1011 cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Ctrl+Shift+O") : tr("F10")));
1012 cmd->setAttribute(Command::CA_Hide);
1013 debugMenu->addAction(cmd);
1014 connect(&m_startAndBreakOnMain, &QAction::triggered, this, [] {
1015 DebuggerRunTool::setBreakOnMainNextTime();
1016 ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE, false);
1017 });
1018
1019 act = new QAction(Icons::STEP_OVER.icon(), tr("Step Over"), this);
1020 act->setEnabled(false);
1021 cmd = ActionManager::registerAction(act, Constants::NEXT);
1022 cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Ctrl+Shift+O") : tr("F10")));
1023 cmd->setTouchBarIcon(Icons::MACOS_TOUCHBAR_DEBUG_STEP_OVER.icon());
1024 touchBar->addAction(cmd);
1025 debugMenu->addAction(cmd);
1026
1027 act = new QAction(Icons::STEP_INTO.icon(), tr("Step Into"), this);
1028 act->setEnabled(false);
1029 cmd = ActionManager::registerAction(act, Constants::STEP);
1030 cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Ctrl+Shift+I") : tr("F11")));
1031 cmd->setTouchBarIcon(Icons::MACOS_TOUCHBAR_DEBUG_STEP_INTO.icon());
1032 touchBar->addAction(cmd);
1033 debugMenu->addAction(cmd);
1034
1035 act = new QAction(Icons::STEP_OUT.icon(), tr("Step Out"), this);
1036 act->setEnabled(false);
1037 cmd = ActionManager::registerAction(act, Constants::STEPOUT);
1038 cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Ctrl+Shift+T") : tr("Shift+F11")));
1039 cmd->setTouchBarIcon(Icons::MACOS_TOUCHBAR_DEBUG_STEP_OUT.icon());
1040 touchBar->addAction(cmd);
1041 debugMenu->addAction(cmd);
1042
1043 act = new QAction(tr("Run to Line"), this);
1044 act->setEnabled(false);
1045 act->setVisible(false);
1046 cmd = ActionManager::registerAction(act, Constants::RUNTOLINE);
1047 cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Shift+F8") : tr("Ctrl+F10")));
1048 debugMenu->addAction(cmd);
1049
1050 act = new QAction(tr("Run to Selected Function"), this);
1051 act->setEnabled(false);
1052 act->setEnabled(false);
1053 cmd = ActionManager::registerAction(act, Constants::RUNTOSELECTEDFUNCTION);
1054 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+F6")));
1055 // Don't add to menu by default as keeping its enabled state
1056 // and text up-to-date is a lot of hassle.
1057 // debugMenu->addAction(cmd);
1058
1059 act = new QAction(tr("Jump to Line"), this);
1060 act->setEnabled(false);
1061 act->setVisible(false);
1062 cmd = ActionManager::registerAction(act, Constants::JUMPTOLINE);
1063 debugMenu->addAction(cmd);
1064
1065 act = new QAction(tr("Immediately Return From Inner Function"), this);
1066 act->setEnabled(false);
1067 act->setVisible(false);
1068 cmd = ActionManager::registerAction(act, Constants::RETURNFROMFUNCTION);
1069 debugMenu->addAction(cmd);
1070
1071 debugMenu->addSeparator();
1072
1073 act = new QAction(this);
1074 act->setText(QCoreApplication::translate("Debugger::Internal::DebuggerPluginPrivate",
1075 "Move to Calling Frame"));
1076 act->setEnabled(false);
1077 act->setVisible(false);
1078 ActionManager::registerAction(act, Constants::FRAME_UP);
1079
1080 act = new QAction(this);
1081 act->setText(QCoreApplication::translate("Debugger::Internal::DebuggerPluginPrivate",
1082 "Move to Called Frame"));
1083 act->setEnabled(false);
1084 act->setVisible(false);
1085 ActionManager::registerAction(act, Constants::FRAME_DOWN);
1086
1087 act = new QAction(this);
1088 act->setText(QCoreApplication::translate("Debugger::Internal::DebuggerEnginePrivate",
1089 "Operate by Instruction"));
1090 act->setEnabled(false);
1091 act->setVisible(false);
1092 act->setCheckable(true);
1093 act->setChecked(false);
1094 cmd = ActionManager::registerAction(act, Constants::OPERATE_BY_INSTRUCTION);
1095 debugMenu->addAction(cmd);
1096
1097 cmd = ActionManager::registerAction(&m_breakAction, "Debugger.ToggleBreak");
1098 cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("F8") : tr("F9")));
1099 debugMenu->addAction(cmd);
1100 connect(&m_breakAction, &QAction::triggered,
1101 this, &DebuggerPluginPrivate::toggleBreakpointHelper);
1102
1103 debugMenu->addSeparator();
1104
1105 auto qmlShowAppOnTopDummyAction = new QAction(tr("Show Application on Top"), this);
1106 qmlShowAppOnTopDummyAction->setCheckable(true);
1107 qmlShowAppOnTopDummyAction->setIcon(Icons::APP_ON_TOP.icon());
1108 qmlShowAppOnTopDummyAction->setEnabled(false);
1109 cmd = ActionManager::registerAction(qmlShowAppOnTopDummyAction, Constants::QML_SHOW_APP_ON_TOP);
1110 debugMenu->addAction(cmd);
1111
1112 auto qmlSelectDummyAction = new QAction(tr("Select"), this);
1113 qmlSelectDummyAction->setCheckable(true);
1114 qmlSelectDummyAction->setIcon(Icons::SELECT.icon());
1115 qmlSelectDummyAction->setEnabled(false);
1116 cmd = ActionManager::registerAction(qmlSelectDummyAction, Constants::QML_SELECTTOOL);
1117 debugMenu->addAction(cmd);
1118
1119 debugMenu->addSeparator();
1120
1121 ActionManager::registerAction(&m_reloadDebuggingHelpersAction, Constants::RELOAD_DEBUGGING_HELPERS);
1122 connect(&m_reloadDebuggingHelpersAction, &QAction::triggered,
1123 this, &DebuggerPluginPrivate::reloadDebuggingHelpers);
1124
1125 cmd = m_watchCommand = ActionManager::registerAction(&m_watchAction, Constants::WATCH);
1126 debugMenu->addAction(cmd);
1127
1128 // FIXME: Re-vive watcher creation before engine runs.
1129 // connect(&m_watchAction, &QAction::triggered, this, [&] {
1130 // QTC_CHECK(false);
1131 // });
1132
1133 addGdbOptionPages(&m_optionPages);
1134 addCdbOptionPages(&m_optionPages);
1135 m_optionPages.append(new LocalsAndExpressionsOptionsPage);
1136
1137 connect(ModeManager::instance(), &ModeManager::currentModeAboutToChange, this, [] {
1138 if (ModeManager::currentModeId() == MODE_DEBUG)
1139 DebuggerMainWindow::leaveDebugMode();
1140 });
1141
1142 connect(ModeManager::instance(), &ModeManager::currentModeChanged, [](Id mode, Id oldMode) {
1143 QTC_ASSERT(mode != oldMode, return);
1144 if (mode == MODE_DEBUG) {
1145 DebuggerMainWindow::enterDebugMode();
1146 if (IEditor *editor = EditorManager::currentEditor())
1147 editor->widget()->setFocus();
1148 }
1149 });
1150
1151 connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged,
1152 this, &DebuggerPluginPrivate::updateDebugWithoutDeployMenu);
1153
1154 // Debug mode setup
1155 m_mode = new DebugMode;
1156
1157 m_debugModeContext.setContext(Context(CC::C_EDITORMANAGER));
1158 m_debugModeContext.setWidget(m_mode->widget());
1159 ICore::addContextObject(&m_debugModeContext);
1160
1161 //
1162 // Connections
1163 //
1164
1165 // Core
1166 connect(ICore::instance(), &ICore::saveSettingsRequested,
1167 this, &DebuggerPluginPrivate::writeSettings);
1168
1169 // ProjectExplorer
1170 connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::runActionsUpdated,
1171 this, &DebuggerPluginPrivate::updatePresetState);
1172
1173 // EditorManager
1174 connect(EditorManager::instance(), &EditorManager::editorOpened,
1175 this, &DebuggerPluginPrivate::editorOpened);
1176 connect(EditorManager::instance(), &EditorManager::currentEditorChanged,
1177 this, &DebuggerPluginPrivate::updateBreakMenuItem);
1178
1179 // Application interaction
1180 connect(debuggerSettings()->settingsDialog.action(), &QAction::triggered,
1181 [] { ICore::showOptionsDialog(DEBUGGER_COMMON_SETTINGS_ID); });
1182
1183 m_perspective.useSubPerspectiveSwitcher(EngineManager::engineChooser());
1184 m_perspective.addToolBarAction(&m_startAction);
1185
1186 m_perspective.addWindow(engineManagerWindow, Perspective::SplitVertical, nullptr);
1187 m_perspective.addWindow(breakpointManagerWindow, Perspective::SplitHorizontal, engineManagerWindow);
1188 m_perspective.addWindow(globalLogWindow, Perspective::AddToTab, nullptr, false, Qt::TopDockWidgetArea);
1189
1190 setInitialState();
1191
1192 connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
1193 this, &DebuggerPluginPrivate::onStartupProjectChanged);
1194 connect(EngineManager::instance(), &EngineManager::engineStateChanged,
1195 this, &DebuggerPluginPrivate::updatePresetState);
1196 connect(EngineManager::instance(), &EngineManager::currentEngineChanged,
1197 this, &DebuggerPluginPrivate::updatePresetState);
1198 }
1199
1200
~DebuggerPluginPrivate()1201 DebuggerPluginPrivate::~DebuggerPluginPrivate()
1202 {
1203 qDeleteAll(m_optionPages);
1204 m_optionPages.clear();
1205 }
1206
msgParameterMissing(const QString & a)1207 static QString msgParameterMissing(const QString &a)
1208 {
1209 return DebuggerPlugin::tr("Option \"%1\" is missing the parameter.").arg(a);
1210 }
1211
guessKitFromAbis(const Abis & abis)1212 static Kit *guessKitFromAbis(const Abis &abis)
1213 {
1214 Kit *kit = nullptr;
1215
1216 // Try to find a kit via ABI.
1217 if (!abis.isEmpty()) {
1218 // Try exact abis.
1219 kit = KitManager::kit([abis](const Kit *k) {
1220 const Abi tcAbi = ToolChainKitAspect::targetAbi(k);
1221 return abis.contains(tcAbi) && !DebuggerKitAspect::configurationErrors(k);
1222 });
1223 if (!kit) {
1224 // Or something compatible.
1225 kit = KitManager::kit([abis](const Kit *k) {
1226 const Abi tcAbi = ToolChainKitAspect::targetAbi(k);
1227 return !DebuggerKitAspect::configurationErrors(k)
1228 && Utils::contains(abis, [tcAbi](const Abi &a) { return a.isCompatibleWith(tcAbi); });
1229 });
1230 }
1231 }
1232
1233 if (!kit)
1234 kit = KitManager::defaultKit();
1235
1236 return kit;
1237 }
1238
parseArgument(QStringList::const_iterator & it,const QStringList::const_iterator & cend,QString * errorMessage)1239 bool DebuggerPluginPrivate::parseArgument(QStringList::const_iterator &it,
1240 const QStringList::const_iterator &cend, QString *errorMessage)
1241 {
1242 const QString &option = *it;
1243 // '-debug <pid>'
1244 // '-debug <exe>[,server=<server:port>][,core=<core>][,kit=<kit>][,terminal={0,1}]'
1245 if (*it == "-debug") {
1246 ++it;
1247 if (it == cend) {
1248 *errorMessage = msgParameterMissing(*it);
1249 return false;
1250 }
1251 const qint64 pid = it->toLongLong();
1252 const QStringList args = it->split(',');
1253
1254 Kit *kit = nullptr;
1255 DebuggerStartMode startMode = StartExternal;
1256 FilePath executable;
1257 QString remoteChannel;
1258 QString coreFile;
1259 QString sysRoot;
1260 bool useTerminal = false;
1261
1262 if (!pid) {
1263 for (const QString &arg : args) {
1264 const QString key = arg.section('=', 0, 0);
1265 const QString val = arg.section('=', 1, 1);
1266 if (val.isEmpty()) {
1267 if (key.isEmpty()) {
1268 continue;
1269 } else if (executable.isEmpty()) {
1270 executable = FilePath::fromString(key);
1271 } else {
1272 *errorMessage = DebuggerPlugin::tr("Only one executable allowed.");
1273 return false;
1274 }
1275 } else if (key == "kit") {
1276 kit = KitManager::kit(Id::fromString(val));
1277 if (!kit)
1278 kit = KitManager::kit(Utils::equal(&Kit::displayName, val));
1279 } else if (key == "server") {
1280 startMode = AttachToRemoteServer;
1281 remoteChannel = val;
1282 } else if (key == "core") {
1283 startMode = AttachToCore;
1284 coreFile = val;
1285 } else if (key == "terminal") {
1286 useTerminal = true;
1287 } else if (key == "sysroot") {
1288 sysRoot = val;
1289 }
1290 }
1291 }
1292 if (!kit)
1293 kit = guessKitFromAbis(Abi::abisOfBinary(executable));
1294
1295 auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
1296 runControl->setKit(kit);
1297 auto debugger = new DebuggerRunTool(runControl);
1298 debugger->setInferiorExecutable(executable);
1299 if (!sysRoot.isEmpty())
1300 debugger->setSysRoot(FilePath::fromUserInput(sysRoot));
1301 if (pid) {
1302 debugger->setStartMode(AttachToLocalProcess);
1303 debugger->setCloseMode(DetachAtClose);
1304 debugger->setAttachPid(pid);
1305 debugger->setRunControlName(tr("Process %1").arg(pid));
1306 debugger->setStartMessage(tr("Attaching to local process %1.").arg(pid));
1307 } else if (startMode == AttachToRemoteServer) {
1308 debugger->setStartMode(AttachToRemoteServer);
1309 debugger->setRemoteChannel(remoteChannel);
1310 debugger->setRunControlName(tr("Remote: \"%1\"").arg(remoteChannel));
1311 debugger->setStartMessage(tr("Attaching to remote server %1.").arg(remoteChannel));
1312 } else if (startMode == AttachToCore) {
1313 debugger->setStartMode(AttachToCore);
1314 debugger->setCloseMode(DetachAtClose);
1315 debugger->setCoreFileName(coreFile);
1316 debugger->setRunControlName(tr("Core file \"%1\"").arg(coreFile));
1317 debugger->setStartMessage(tr("Attaching to core file %1.").arg(coreFile));
1318 } else {
1319 debugger->setStartMode(StartExternal);
1320 debugger->setRunControlName(tr("Executable file \"%1\"").arg(executable.toUserOutput()));
1321 debugger->setStartMessage(tr("Debugging file %1.").arg(executable.toUserOutput()));
1322 }
1323 debugger->setUseTerminal(useTerminal);
1324
1325 m_scheduledStarts.append(debugger);
1326 return true;
1327 }
1328 // -wincrashevent <event-handle>:<pid>. A handle used for
1329 // a handshake when attaching to a crashed Windows process.
1330 // This is created by $QTC/src/tools/qtcdebugger/main.cpp:
1331 // args << "-wincrashevent"
1332 // << QString::fromLatin1("%1:%2").arg(argWinCrashEvent).arg(argProcessId);
1333 if (*it == "-wincrashevent") {
1334 ++it;
1335 if (it == cend) {
1336 *errorMessage = msgParameterMissing(*it);
1337 return false;
1338 }
1339 qint64 pid = it->section(':', 1, 1).toLongLong();
1340 auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
1341 runControl->setKit(findUniversalCdbKit());
1342 auto debugger = new DebuggerRunTool(runControl);
1343 debugger->setStartMode(AttachToCrashedProcess);
1344 debugger->setCrashParameter(it->section(':', 0, 0));
1345 debugger->setAttachPid(pid);
1346 debugger->setRunControlName(tr("Crashed process %1").arg(pid));
1347 debugger->setStartMessage(tr("Attaching to crashed process %1").arg(pid));
1348 if (pid < 1) {
1349 *errorMessage = DebuggerPlugin::tr("The parameter \"%1\" of option \"%2\" "
1350 "does not match the pattern <handle>:<pid>.").arg(*it, option);
1351 return false;
1352 }
1353 m_scheduledStarts.append(debugger);
1354 return true;
1355 }
1356
1357 *errorMessage = DebuggerPlugin::tr("Invalid debugger option: %1").arg(option);
1358 return false;
1359 }
1360
parseArguments(const QStringList & args,QString * errorMessage)1361 bool DebuggerPluginPrivate::parseArguments(const QStringList &args,
1362 QString *errorMessage)
1363 {
1364 const QStringList::const_iterator cend = args.constEnd();
1365 for (QStringList::const_iterator it = args.constBegin(); it != cend; ++it)
1366 if (!parseArgument(it, cend, errorMessage))
1367 return false;
1368 return true;
1369 }
1370
parseCommandLineArguments()1371 void DebuggerPluginPrivate::parseCommandLineArguments()
1372 {
1373 QString errorMessage;
1374 if (!parseArguments(m_arguments, &errorMessage)) {
1375 errorMessage = tr("Error evaluating command line arguments: %1")
1376 .arg(errorMessage);
1377 qWarning("%s\n", qPrintable(errorMessage));
1378 MessageManager::writeDisrupting(errorMessage);
1379 }
1380 if (!m_scheduledStarts.isEmpty())
1381 QTimer::singleShot(0, this, &DebuggerPluginPrivate::runScheduled);
1382 }
1383
setConfigValue(const QString & name,const QVariant & value)1384 static void setConfigValue(const QString &name, const QVariant &value)
1385 {
1386 ICore::settings()->setValue("DebugMode/" + name, value);
1387 }
1388
configValue(const QString & name)1389 static QVariant configValue(const QString &name)
1390 {
1391 return ICore::settings()->value("DebugMode/" + name);
1392 }
1393
updatePresetState()1394 void DebuggerPluginPrivate::updatePresetState()
1395 {
1396 if (m_shuttingDown)
1397 return;
1398
1399 Project *startupProject = SessionManager::startupProject();
1400 RunConfiguration *startupRunConfig = SessionManager::startupRunConfiguration();
1401 DebuggerEngine *currentEngine = EngineManager::currentEngine();
1402
1403 QString whyNot;
1404 const bool canRun =
1405 ProjectExplorerPlugin::canRunStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE, &whyNot);
1406
1407 QString startupRunConfigName;
1408 if (startupRunConfig)
1409 startupRunConfigName = startupRunConfig->displayName();
1410 if (startupRunConfigName.isEmpty() && startupProject)
1411 startupRunConfigName = startupProject->displayName();
1412
1413 // Restrict width, otherwise Creator gets too wide, see QTCREATORBUG-21885
1414 const QString startToolTip =
1415 canRun ? tr("Start debugging of startup project") : whyNot;
1416
1417 m_startAction.setToolTip(startToolTip);
1418 m_startAction.setText(canRun ? startToolTip : tr("Start Debugging"));
1419
1420 if (!currentEngine) {
1421 // No engine running -- or -- we have a running engine but it does not
1422 // correspond to the current start up project.
1423 m_startAction.setEnabled(canRun);
1424 m_startAction.setIcon(startIcon(true));
1425 m_startAction.setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
1426 m_startAction.setVisible(true);
1427 m_debugWithoutDeployAction.setEnabled(canRun);
1428 m_visibleStartAction.setAction(&m_startAction);
1429 m_hiddenStopAction.setAction(&m_undisturbableAction);
1430 return;
1431 }
1432
1433 QTC_ASSERT(currentEngine, return);
1434
1435 // We have a current engine, and it belongs to the startup runconfig.
1436 // The 'state' bits only affect the fat debug button, not the preset start button.
1437 m_startAction.setIcon(startIcon(false));
1438 m_startAction.setEnabled(false);
1439 m_startAction.setVisible(false);
1440
1441 m_debugWithoutDeployAction.setEnabled(canRun);
1442
1443 const DebuggerState state = currentEngine->state();
1444
1445 if (state == InferiorStopOk) {
1446 // F5 continues, Shift-F5 kills. It is "continuable".
1447 m_startAction.setEnabled(false);
1448 m_debugWithoutDeployAction.setEnabled(false);
1449 m_visibleStartAction.setAction(ActionManager::command(Constants::CONTINUE)->action());
1450 m_hiddenStopAction.setAction(ActionManager::command(Constants::STOP)->action());
1451 } else if (state == InferiorRunOk) {
1452 // Shift-F5 interrupts. It is also "interruptible".
1453 m_startAction.setEnabled(false);
1454 m_debugWithoutDeployAction.setEnabled(false);
1455 m_visibleStartAction.setAction(ActionManager::command(Constants::INTERRUPT)->action());
1456 m_hiddenStopAction.setAction(ActionManager::command(Constants::INTERRUPT)->action());
1457 } else if (state == DebuggerFinished) {
1458 // We don't want to do anything anymore.
1459 m_startAction.setEnabled(canRun);
1460 m_debugWithoutDeployAction.setEnabled(canRun);
1461 m_visibleStartAction.setAction(ActionManager::command(DEBUGGER_START)->action());
1462 m_hiddenStopAction.setAction(&m_undisturbableAction);
1463 } else if (state == InferiorUnrunnable) {
1464 // We don't want to do anything anymore.
1465 m_startAction.setEnabled(false);
1466 m_debugWithoutDeployAction.setEnabled(false);
1467 m_visibleStartAction.setAction(ActionManager::command(Constants::STOP)->action());
1468 m_hiddenStopAction.setAction(ActionManager::command(Constants::STOP)->action());
1469 } else {
1470 // The startup phase should be over once we are here.
1471 // But treat it as 'undisturbable if we are here by accident.
1472 //QTC_CHECK(state != DebuggerNotReady);
1473 // Everything else is "undisturbable".
1474 m_startAction.setEnabled(false);
1475 m_debugWithoutDeployAction.setEnabled(false);
1476 m_visibleStartAction.setAction(&m_undisturbableAction);
1477 m_hiddenStopAction.setAction(&m_undisturbableAction);
1478 }
1479
1480 // FIXME: Decentralize the actions below
1481 const bool actionsEnabled = currentEngine->debuggerActionsEnabled();
1482 const bool canDeref = actionsEnabled && currentEngine->hasCapability(AutoDerefPointersCapability);
1483 DebuggerSettings *s = debuggerSettings();
1484 s->autoDerefPointers.setEnabled(canDeref);
1485 s->autoDerefPointers.setEnabled(true);
1486 s->expandStack.setEnabled(actionsEnabled);
1487
1488 m_startAndDebugApplicationAction.setEnabled(true);
1489 m_attachToQmlPortAction.setEnabled(true);
1490 m_attachToCoreAction.setEnabled(true);
1491 m_attachToRemoteServerAction.setEnabled(true);
1492 m_attachToRunningApplication.setEnabled(true);
1493 m_attachToUnstartedApplication.setEnabled(true);
1494
1495 m_watchAction.setEnabled(state != DebuggerFinished && state != DebuggerNotReady);
1496 m_breakAction.setEnabled(true);
1497 }
1498
onStartupProjectChanged(Project * project)1499 void DebuggerPluginPrivate::onStartupProjectChanged(Project *project)
1500 {
1501 RunConfiguration *activeRc = nullptr;
1502 if (project) {
1503 Target *target = project->activeTarget();
1504 if (target)
1505 activeRc = target->activeRunConfiguration();
1506 if (!activeRc)
1507 return;
1508 }
1509 for (DebuggerEngine *engine : EngineManager::engines()) {
1510 // Run controls might be deleted during exit.
1511 engine->updateState();
1512 }
1513
1514 updatePresetState();
1515 }
1516
attachCore()1517 void DebuggerPluginPrivate::attachCore()
1518 {
1519 AttachCoreDialog dlg(ICore::dialogParent());
1520
1521 const QString lastExternalKit = configValue("LastExternalKit").toString();
1522 if (!lastExternalKit.isEmpty())
1523 dlg.setKitId(Id::fromString(lastExternalKit));
1524 dlg.setSymbolFile(configValue("LastExternalExecutableFile").toString());
1525 dlg.setLocalCoreFile(configValue("LastLocalCoreFile").toString());
1526 dlg.setRemoteCoreFile(configValue("LastRemoteCoreFile").toString());
1527 dlg.setOverrideStartScript(configValue("LastExternalStartScript").toString());
1528 dlg.setSysRoot(configValue("LastSysRoot").toString());
1529 dlg.setForceLocalCoreFile(configValue("LastForceLocalCoreFile").toBool());
1530
1531 if (dlg.exec() != QDialog::Accepted)
1532 return;
1533
1534 setConfigValue("LastExternalExecutableFile", dlg.symbolFile().toVariant());
1535 setConfigValue("LastLocalCoreFile", dlg.localCoreFile());
1536 setConfigValue("LastRemoteCoreFile", dlg.remoteCoreFile());
1537 setConfigValue("LastExternalKit", dlg.kit()->id().toSetting());
1538 setConfigValue("LastExternalStartScript", dlg.overrideStartScript());
1539 setConfigValue("LastSysRoot", dlg.sysRoot().toString());
1540 setConfigValue("LastForceLocalCoreFile", dlg.forcesLocalCoreFile());
1541
1542 auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
1543 runControl->setKit(dlg.kit());
1544 runControl->setDisplayName(tr("Core file \"%1\"")
1545 .arg(dlg.useLocalCoreFile() ? dlg.localCoreFile() : dlg.remoteCoreFile()));
1546 auto debugger = new DebuggerRunTool(runControl);
1547 debugger->setInferiorExecutable(dlg.symbolFile());
1548 debugger->setCoreFileName(dlg.localCoreFile());
1549 debugger->setStartMode(AttachToCore);
1550 debugger->setCloseMode(DetachAtClose);
1551 debugger->setOverrideStartScript(dlg.overrideStartScript());
1552 const FilePath sysRoot = dlg.sysRoot();
1553 if (!sysRoot.isEmpty())
1554 debugger->setSysRoot(sysRoot);
1555 debugger->startRunControl();
1556 }
1557
reloadDebuggingHelpers()1558 void DebuggerPluginPrivate::reloadDebuggingHelpers()
1559 {
1560 if (DebuggerEngine *engine = EngineManager::currentEngine())
1561 engine->reloadDebuggingHelpers();
1562 else
1563 DebuggerMainWindow::showStatusMessage(
1564 tr("Reload debugging helpers skipped as no engine is running."), 5000);
1565 }
1566
startRemoteCdbSession()1567 void DebuggerPluginPrivate::startRemoteCdbSession()
1568 {
1569 const QString connectionKey = "CdbRemoteConnection";
1570 Kit *kit = findUniversalCdbKit();
1571 QTC_ASSERT(kit, return);
1572
1573 StartRemoteCdbDialog dlg(ICore::dialogParent());
1574 QString previousConnection = configValue(connectionKey).toString();
1575 if (previousConnection.isEmpty())
1576 previousConnection = "localhost:1234";
1577 dlg.setConnection(previousConnection);
1578 if (dlg.exec() != QDialog::Accepted)
1579 return;
1580 setConfigValue(connectionKey, dlg.connection());
1581
1582 auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
1583 runControl->setKit(kit);
1584 auto debugger = new DebuggerRunTool(runControl);
1585 debugger->setStartMode(AttachToRemoteServer);
1586 debugger->setCloseMode(KillAtClose);
1587 debugger->setRemoteChannel(dlg.connection());
1588 debugger->startRunControl();
1589 }
1590
1591 class RemoteAttachRunner : public DebuggerRunTool
1592 {
1593 public:
RemoteAttachRunner(RunControl * runControl,ProcessHandle pid)1594 RemoteAttachRunner(RunControl *runControl, ProcessHandle pid)
1595 : DebuggerRunTool(runControl)
1596 {
1597 setId("AttachToRunningProcess");
1598 setUsePortsGatherer(true, false);
1599
1600 auto gdbServer = new DebugServerRunner(runControl, portsGatherer());
1601 gdbServer->setUseMulti(false);
1602 gdbServer->setAttachPid(pid);
1603
1604 addStartDependency(gdbServer);
1605
1606 setStartMode(AttachToRemoteProcess);
1607 setCloseMode(DetachAtClose);
1608
1609 // setInferiorExecutable(localExecutable);
1610 setUseContinueInsteadOfRun(true);
1611 setContinueAfterAttach(false);
1612 }
1613 };
1614
attachToRunningApplication()1615 void DebuggerPluginPrivate::attachToRunningApplication()
1616 {
1617 auto kitChooser = new KitChooser;
1618 kitChooser->setShowIcons(true);
1619
1620 auto dlg = new DeviceProcessesDialog(kitChooser, ICore::dialogParent());
1621 dlg->addAcceptButton(DeviceProcessesDialog::tr("&Attach to Process"));
1622 dlg->showAllDevices();
1623 if (dlg->exec() == QDialog::Rejected) {
1624 delete dlg;
1625 return;
1626 }
1627
1628 dlg->setAttribute(Qt::WA_DeleteOnClose);
1629 Kit *kit = kitChooser->currentKit();
1630 QTC_ASSERT(kit, return);
1631 IDevice::ConstPtr device = DeviceKitAspect::device(kit);
1632 QTC_ASSERT(device, return);
1633
1634 DeviceProcessItem process = dlg->currentProcess();
1635
1636 if (device->type() == PE::DESKTOP_DEVICE_TYPE) {
1637 attachToRunningProcess(kit, process, false);
1638 } else {
1639 auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
1640 runControl->setKit(kit);
1641 //: %1: PID
1642 runControl->setDisplayName(tr("Process %1").arg(process.pid));
1643 auto debugger = new RemoteAttachRunner(runControl, ProcessHandle(process.pid));
1644 debugger->startRunControl();
1645 }
1646 }
1647
attachToUnstartedApplicationDialog()1648 void DebuggerPluginPrivate::attachToUnstartedApplicationDialog()
1649 {
1650 auto dlg = new UnstartedAppWatcherDialog(ICore::dialogParent());
1651
1652 connect(dlg, &QDialog::finished, dlg, &QObject::deleteLater);
1653 connect(dlg, &UnstartedAppWatcherDialog::processFound, this, [this, dlg] {
1654 RunControl *rc = attachToRunningProcess(dlg->currentKit(),
1655 dlg->currentProcess(),
1656 dlg->continueOnAttach());
1657 if (!rc)
1658 return;
1659
1660 if (dlg->hideOnAttach())
1661 connect(rc, &RunControl::stopped, dlg, &UnstartedAppWatcherDialog::startWatching);
1662 });
1663
1664 dlg->show();
1665 }
1666
attachToRunningProcess(Kit * kit,DeviceProcessItem process,bool contAfterAttach)1667 RunControl *DebuggerPluginPrivate::attachToRunningProcess(Kit *kit,
1668 DeviceProcessItem process, bool contAfterAttach)
1669 {
1670 QTC_ASSERT(kit, return nullptr);
1671 IDevice::ConstPtr device = DeviceKitAspect::device(kit);
1672 QTC_ASSERT(device, return nullptr);
1673 if (process.pid == 0) {
1674 AsynchronousMessageBox::warning(tr("Warning"), tr("Cannot attach to process with PID 0"));
1675 return nullptr;
1676 }
1677
1678 const Abi tcAbi = ToolChainKitAspect::targetAbi(kit);
1679 const bool isWindows = (tcAbi.os() == Abi::WindowsOS);
1680 if (isWindows && isWinProcessBeingDebugged(process.pid)) {
1681 AsynchronousMessageBox::warning(
1682 tr("Process Already Under Debugger Control"),
1683 tr("The process %1 is already under the control of a debugger.\n"
1684 "%2 cannot attach to it.").arg(process.pid)
1685 .arg(Core::Constants::IDE_DISPLAY_NAME));
1686 return nullptr;
1687 }
1688
1689 if (device->type() != PE::DESKTOP_DEVICE_TYPE) {
1690 AsynchronousMessageBox::warning(tr("Not a Desktop Device Type"),
1691 tr("It is only possible to attach to a locally running process."));
1692 return nullptr;
1693 }
1694
1695 auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
1696 runControl->setKit(kit);
1697 //: %1: PID
1698 runControl->setDisplayName(tr("Process %1").arg(process.pid));
1699 auto debugger = new DebuggerRunTool(runControl);
1700 debugger->setAttachPid(ProcessHandle(process.pid));
1701 debugger->setInferiorExecutable(FilePath::fromString(process.exe));
1702 debugger->setInferiorDevice(device);
1703 debugger->setStartMode(AttachToLocalProcess);
1704 debugger->setCloseMode(DetachAtClose);
1705 debugger->setContinueAfterAttach(contAfterAttach);
1706
1707 debugger->startRunControl();
1708
1709 return debugger->runControl();
1710 }
1711
attachExternalApplication(RunControl * rc)1712 void DebuggerPlugin::attachExternalApplication(RunControl *rc)
1713 {
1714 ProcessHandle pid = rc->applicationProcessHandle();
1715 auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
1716 runControl->setTarget(rc->target());
1717 runControl->setDisplayName(tr("Process %1").arg(pid.pid()));
1718 auto debugger = new DebuggerRunTool(runControl);
1719 debugger->setAttachPid(pid);
1720 debugger->setStartMode(AttachToLocalProcess);
1721 debugger->setCloseMode(DetachAtClose);
1722 debugger->startRunControl();
1723 }
1724
getEnginesState(QByteArray * json) const1725 void DebuggerPlugin::getEnginesState(QByteArray *json) const
1726 {
1727 QTC_ASSERT(json, return);
1728 QVariantMap result {
1729 {"version", 1}
1730 };
1731 QVariantMap states;
1732
1733 int i = 0;
1734 DebuggerEngine *currentEngine = EngineManager::currentEngine();
1735 for (DebuggerEngine *engine : EngineManager::engines()) {
1736 states[QString::number(i)] = QVariantMap({
1737 {"current", engine == currentEngine},
1738 {"pid", engine->inferiorPid()},
1739 {"state", engine->state()}
1740 });
1741 ++i;
1742 }
1743
1744 if (!states.isEmpty())
1745 result["states"] = states;
1746
1747 *json = QJsonDocument(QJsonObject::fromVariantMap(result)).toJson();
1748 }
1749
autoDetectDebuggersForDevice(const FilePath & deviceRoot,const QString & detectionSource,QString * logMessage)1750 void DebuggerPlugin::autoDetectDebuggersForDevice(const FilePath &deviceRoot,
1751 const QString &detectionSource,
1752 QString *logMessage)
1753 {
1754 dd->m_debuggerItemManager.autoDetectDebuggersForDevice(deviceRoot, detectionSource, logMessage);
1755 }
1756
removeDetectedDebuggers(const QString & detectionSource,QString * logMessage)1757 void DebuggerPlugin::removeDetectedDebuggers(const QString &detectionSource, QString *logMessage)
1758 {
1759 dd->m_debuggerItemManager.removeDetectedDebuggers(detectionSource, logMessage);
1760 }
1761
listDetectedDebuggers(const QString & detectionSource,QString * logMessage)1762 void DebuggerPlugin::listDetectedDebuggers(const QString &detectionSource, QString *logMessage)
1763 {
1764 dd->m_debuggerItemManager.listDetectedDebuggers(detectionSource, logMessage);
1765 }
1766
attachToQmlPort()1767 void DebuggerPluginPrivate::attachToQmlPort()
1768 {
1769 AttachToQmlPortDialog dlg(ICore::dialogParent());
1770
1771 const QVariant qmlServerPort = configValue("LastQmlServerPort");
1772 if (qmlServerPort.isValid())
1773 dlg.setPort(qmlServerPort.toInt());
1774 else
1775 dlg.setPort(-1);
1776
1777 const Id kitId = Id::fromSetting(configValue("LastProfile"));
1778 if (kitId.isValid())
1779 dlg.setKitId(kitId);
1780
1781 if (dlg.exec() != QDialog::Accepted)
1782 return;
1783
1784 Kit *kit = dlg.kit();
1785 QTC_ASSERT(kit, return);
1786 setConfigValue("LastQmlServerPort", dlg.port());
1787 setConfigValue("LastProfile", kit->id().toSetting());
1788
1789 IDevice::ConstPtr device = DeviceKitAspect::device(kit);
1790 QTC_ASSERT(device, return);
1791
1792 auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
1793 runControl->setKit(kit);
1794 auto debugger = new DebuggerRunTool(runControl);
1795
1796 QUrl qmlServer = device->toolControlChannel(IDevice::QmlControlChannel);
1797 qmlServer.setPort(dlg.port());
1798 debugger->setQmlServer(qmlServer);
1799
1800 QSsh::SshConnectionParameters sshParameters = device->sshParameters();
1801 debugger->setRemoteChannel(sshParameters.host(), sshParameters.port());
1802 debugger->setStartMode(AttachToQmlServer);
1803
1804 debugger->startRunControl();
1805 }
1806
runScheduled()1807 void DebuggerPluginPrivate::runScheduled()
1808 {
1809 for (DebuggerRunTool *debugger : qAsConst(m_scheduledStarts))
1810 debugger->startRunControl();
1811 }
1812
editorOpened(IEditor * editor)1813 void DebuggerPluginPrivate::editorOpened(IEditor *editor)
1814 {
1815 if (auto widget = TextEditorWidget::fromEditor(editor)) {
1816 connect(widget, &TextEditorWidget::markRequested,
1817 this, &DebuggerPluginPrivate::requestMark);
1818
1819 connect(widget, &TextEditorWidget::markContextMenuRequested,
1820 this, &DebuggerPluginPrivate::requestContextMenu);
1821 }
1822 }
1823
updateBreakMenuItem(IEditor * editor)1824 void DebuggerPluginPrivate::updateBreakMenuItem(IEditor *editor)
1825 {
1826 BaseTextEditor *textEditor = qobject_cast<BaseTextEditor *>(editor);
1827 m_breakAction.setEnabled(textEditor != nullptr);
1828 }
1829
requestContextMenu(TextEditorWidget * widget,int lineNumber,QMenu * menu)1830 void DebuggerPluginPrivate::requestContextMenu(TextEditorWidget *widget,
1831 int lineNumber, QMenu *menu)
1832 {
1833 TextDocument *document = widget->textDocument();
1834
1835 const ContextData args = getLocationContext(document, lineNumber);
1836 const GlobalBreakpoint gbp = BreakpointManager::findBreakpointFromContext(args);
1837
1838 if (gbp) {
1839
1840 // Remove existing breakpoint.
1841 auto act = menu->addAction(tr("Remove Breakpoint"));
1842 connect(act, &QAction::triggered, [gbp] { gbp->deleteBreakpoint(); });
1843
1844 // Enable/disable existing breakpoint.
1845 if (gbp->isEnabled()) {
1846 act = menu->addAction(tr("Disable Breakpoint"));
1847 connect(act, &QAction::triggered, [gbp] { gbp->setEnabled(false); });
1848 } else {
1849 act = menu->addAction(tr("Enable Breakpoint"));
1850 connect(act, &QAction::triggered, [gbp] { gbp->setEnabled(true); });
1851 }
1852
1853 // Edit existing breakpoint.
1854 act = menu->addAction(tr("Edit Breakpoint..."));
1855 connect(act, &QAction::triggered, [gbp] {
1856 BreakpointManager::editBreakpoint(gbp, ICore::dialogParent());
1857 });
1858
1859 } else {
1860 // Handle non-existing breakpoint.
1861 const QString text = args.address
1862 ? tr("Set Breakpoint at 0x%1").arg(args.address, 0, 16)
1863 : tr("Set Breakpoint at Line %1").arg(lineNumber);
1864 auto act = menu->addAction(text);
1865 act->setEnabled(args.isValid());
1866 connect(act, &QAction::triggered, [this, args] {
1867 breakpointSetMarginActionTriggered(false, args);
1868 });
1869
1870 // Message trace point
1871 const QString tracePointText = args.address
1872 ? tr("Set Message Tracepoint at 0x%1...").arg(args.address, 0, 16)
1873 : tr("Set Message Tracepoint at Line %1...").arg(lineNumber);
1874 act = menu->addAction(tracePointText);
1875 act->setEnabled(args.isValid());
1876 connect(act, &QAction::triggered, [this, args] {
1877 breakpointSetMarginActionTriggered(true, args);
1878 });
1879 }
1880
1881 // Run to, jump to line below in stopped state.
1882 for (const QPointer<DebuggerEngine> &engine : EngineManager::engines()) {
1883 if (engine->state() == InferiorStopOk && args.isValid()) {
1884 menu->addSeparator();
1885 if (engine->hasCapability(RunToLineCapability)) {
1886 auto act = menu->addAction(args.address
1887 ? DebuggerEngine::tr("Run to Address 0x%1").arg(args.address, 0, 16)
1888 : DebuggerEngine::tr("Run to Line %1").arg(args.lineNumber));
1889 connect(act, &QAction::triggered, this, [args, engine] {
1890 QTC_ASSERT(engine, return);
1891 engine->executeRunToLine(args);
1892 });
1893 }
1894 if (engine->hasCapability(JumpToLineCapability)) {
1895 auto act = menu->addAction(args.address
1896 ? DebuggerEngine::tr("Jump to Address 0x%1").arg(args.address, 0, 16)
1897 : DebuggerEngine::tr("Jump to Line %1").arg(args.lineNumber));
1898 connect(act, &QAction::triggered, this, [args, engine] {
1899 QTC_ASSERT(engine, return);
1900 engine->executeJumpToLine(args);
1901 });
1902 }
1903 // Disassemble current function in stopped state.
1904 if (engine->hasCapability(DisassemblerCapability)) {
1905 StackFrame frame;
1906 frame.function = cppFunctionAt(args.fileName.toString(), lineNumber, 1);
1907 frame.line = 42; // trick gdb into mixed mode.
1908 if (!frame.function.isEmpty()) {
1909 const QString text = tr("Disassemble Function \"%1\"")
1910 .arg(frame.function);
1911 auto act = new QAction(text, menu);
1912 connect(act, &QAction::triggered, this, [frame, engine] {
1913 QTC_ASSERT(engine, return);
1914 engine->openDisassemblerView(Location(frame));
1915 });
1916 menu->addAction(act);
1917 }
1918 }
1919 }
1920 }
1921 }
1922
toggleBreakpointHelper()1923 void DebuggerPluginPrivate::toggleBreakpointHelper()
1924 {
1925 BaseTextEditor *textEditor = BaseTextEditor::currentTextEditor();
1926 QTC_ASSERT(textEditor, return);
1927 const int lineNumber = textEditor->currentLine();
1928 ContextData location = getLocationContext(textEditor->textDocument(), lineNumber);
1929 if (location.isValid())
1930 BreakpointManager::toggleBreakpoint(location);
1931 }
1932
requestMark(TextEditorWidget * widget,int lineNumber,TextMarkRequestKind kind)1933 void DebuggerPluginPrivate::requestMark(TextEditorWidget *widget, int lineNumber,
1934 TextMarkRequestKind kind)
1935 {
1936 if (kind == BreakpointRequest) {
1937 ContextData location = getLocationContext(widget->textDocument(), lineNumber);
1938 if (location.isValid())
1939 BreakpointManager::toggleBreakpoint(location);
1940 }
1941 }
1942
setInitialState()1943 void DebuggerPluginPrivate::setInitialState()
1944 {
1945 m_startAndDebugApplicationAction.setEnabled(true);
1946 m_attachToQmlPortAction.setEnabled(true);
1947 m_attachToCoreAction.setEnabled(true);
1948 m_attachToRemoteServerAction.setEnabled(true);
1949 m_attachToRunningApplication.setEnabled(true);
1950 m_attachToUnstartedApplication.setEnabled(true);
1951
1952 m_watchAction.setEnabled(false);
1953 m_breakAction.setEnabled(false);
1954 //m_snapshotAction.setEnabled(false);
1955
1956 debuggerSettings()->autoDerefPointers.setEnabled(true);
1957 debuggerSettings()->expandStack.setEnabled(false);
1958 }
1959
updateDebugWithoutDeployMenu()1960 void DebuggerPluginPrivate::updateDebugWithoutDeployMenu()
1961 {
1962 const bool state = ProjectExplorerPlugin::projectExplorerSettings().deployBeforeRun;
1963 m_debugWithoutDeployAction.setVisible(state);
1964 }
1965
dumpLog()1966 void DebuggerPluginPrivate::dumpLog()
1967 {
1968 DebuggerEngine *engine = EngineManager::currentEngine();
1969 if (!engine)
1970 return;
1971 LogWindow *logWindow = engine->logWindow();
1972 QTC_ASSERT(logWindow, return);
1973
1974 QString fileName = QFileDialog::getSaveFileName(ICore::dialogParent(),
1975 tr("Save Debugger Log"), Utils::TemporaryDirectory::masterDirectoryPath());
1976 if (fileName.isEmpty())
1977 return;
1978 FileSaver saver(Utils::FilePath::fromUserInput(fileName));
1979 if (!saver.hasError()) {
1980 QTextStream ts(saver.file());
1981 ts << logWindow->inputContents();
1982 ts << "\n\n=======================================\n\n";
1983 ts << logWindow->combinedContents();
1984 saver.setResult(&ts);
1985 }
1986 saver.finalize(ICore::dialogParent());
1987 }
1988
aboutToShutdown()1989 void DebuggerPluginPrivate::aboutToShutdown()
1990 {
1991 m_shuttingDown = true;
1992
1993 disconnect(SessionManager::instance(), &SessionManager::startupProjectChanged, this, nullptr);
1994
1995 m_shutdownTimer.setInterval(0);
1996 m_shutdownTimer.setSingleShot(true);
1997
1998 connect(&m_shutdownTimer, &QTimer::timeout, this, [this] {
1999 DebuggerMainWindow::doShutdown();
2000
2001 m_shutdownTimer.stop();
2002
2003 delete m_mode;
2004 m_mode = nullptr;
2005 emit m_instance->asynchronousShutdownFinished();
2006 });
2007
2008 if (EngineManager::shutDown()) {
2009 // If any engine is aborting we give them extra three seconds.
2010 m_shutdownTimer.setInterval(3000);
2011 }
2012 m_shutdownTimer.start();
2013 }
2014
remoteCommand(const QStringList & options)2015 void DebuggerPluginPrivate::remoteCommand(const QStringList &options)
2016 {
2017 if (options.isEmpty())
2018 return;
2019
2020 QString errorMessage;
2021
2022 if (!parseArguments(options, &errorMessage)) {
2023 qWarning("%s", qPrintable(errorMessage));
2024 return;
2025 }
2026 runScheduled();
2027 }
2028
extensionsInitialized()2029 void DebuggerPluginPrivate::extensionsInitialized()
2030 {
2031 // If the CppEditor or QmlJS editor plugin is there, we want to add something to
2032 // the editor context menu.
2033 for (Id menuId : { CppEditor::Constants::M_CONTEXT, QmlJSEditor::Constants::M_CONTEXT }) {
2034 if (ActionContainer *editorContextMenu = ActionManager::actionContainer(menuId)) {
2035 auto cmd = editorContextMenu->addSeparator(m_watchCommand->context());
2036 cmd->setAttribute(Command::CA_Hide);
2037 cmd = m_watchCommand;
2038 cmd->action()->setEnabled(true);
2039 editorContextMenu->addAction(cmd);
2040 cmd->setAttribute(Command::CA_Hide);
2041 cmd->setAttribute(Command::CA_NonConfigurable);
2042 }
2043 }
2044
2045 DebuggerMainWindow::ensureMainWindowExists();
2046 }
2047
addSearch(BaseTreeView * treeView)2048 QWidget *DebuggerPluginPrivate::addSearch(BaseTreeView *treeView)
2049 {
2050 BoolAspect &act = debuggerSettings()->useAlternatingRowColors;
2051 treeView->setAlternatingRowColors(act.value());
2052 treeView->setProperty(PerspectiveState::savesHeaderKey(), true);
2053 connect(&act, &BaseAspect::changed, treeView, [treeView] {
2054 treeView->setAlternatingRowColors(debuggerSettings()->useAlternatingRowColors.value());
2055 });
2056
2057 return ItemViewFind::createSearchableWrapper(treeView);
2058 }
2059
debuggerConsole()2060 Console *debuggerConsole()
2061 {
2062 return &dd->m_console;
2063 }
2064
addSearch(BaseTreeView * treeView)2065 QWidget *addSearch(BaseTreeView *treeView)
2066 {
2067 return dd->addSearch(treeView);
2068 }
2069
openTextEditor(const QString & titlePattern0,const QString & contents)2070 void openTextEditor(const QString &titlePattern0, const QString &contents)
2071 {
2072 if (dd->m_shuttingDown)
2073 return;
2074 QString titlePattern = titlePattern0;
2075 IEditor *editor = EditorManager::openEditorWithContents(
2076 CC::K_DEFAULT_TEXT_EDITOR_ID, &titlePattern, contents.toUtf8(), QString(),
2077 EditorManager::IgnoreNavigationHistory);
2078 if (auto textEditor = qobject_cast<BaseTextEditor *>(editor)) {
2079 QString suggestion = titlePattern;
2080 if (!suggestion.contains('.'))
2081 suggestion.append(".txt");
2082 textEditor->textDocument()->setFallbackSaveAsFileName(suggestion);
2083 }
2084 QTC_ASSERT(editor, return);
2085 }
2086
2087 ///////////////////////////////////////////////////////////////////////
2088 //
2089 // DebuggerPlugin
2090 //
2091 ///////////////////////////////////////////////////////////////////////
2092
2093 /*!
2094 \class Debugger::DebuggerPlugin
2095
2096 This is the "external" interface of the debugger plugin that's visible
2097 from Qt Creator core. The internal interface to global debugger
2098 functionality that is used by debugger views and debugger engines
2099 is DebuggerCore, implemented in DebuggerPluginPrivate.
2100 */
2101
DebuggerPlugin()2102 DebuggerPlugin::DebuggerPlugin()
2103 {
2104 setObjectName("DebuggerPlugin");
2105 m_instance = this;
2106
2107 qRegisterMetaType<PerspectiveState>("Utils::PerspectiveState");
2108 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2109 qRegisterMetaTypeStreamOperators<PerspectiveState>("Utils::PerspectiveState");
2110 #endif
2111 }
2112
~DebuggerPlugin()2113 DebuggerPlugin::~DebuggerPlugin()
2114 {
2115 delete dd;
2116 dd = nullptr;
2117 m_instance = nullptr;
2118 }
2119
initialize(const QStringList & arguments,QString * errorMessage)2120 bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMessage)
2121 {
2122 Q_UNUSED(errorMessage)
2123
2124 // Needed for call from AppOutputPane::attachToRunControl() and GammarayIntegration.
2125 ExtensionSystem::PluginManager::addObject(this);
2126
2127 dd = new DebuggerPluginPrivate(arguments);
2128 return true;
2129 }
2130
aboutToShutdown()2131 IPlugin::ShutdownFlag DebuggerPlugin::aboutToShutdown()
2132 {
2133 ExtensionSystem::PluginManager::removeObject(this);
2134 dd->aboutToShutdown();
2135 return AsynchronousShutdown;
2136 }
2137
remoteCommand(const QStringList & options,const QString & workingDirectory,const QStringList & list)2138 QObject *DebuggerPlugin::remoteCommand(const QStringList &options,
2139 const QString &workingDirectory,
2140 const QStringList &list)
2141 {
2142 Q_UNUSED(workingDirectory)
2143 Q_UNUSED(list)
2144 dd->remoteCommand(options);
2145 return nullptr;
2146 }
2147
extensionsInitialized()2148 void DebuggerPlugin::extensionsInitialized()
2149 {
2150 dd->extensionsInitialized();
2151 }
2152
2153 } // namespace Internal
2154
buildTypeAccepted(QFlags<ToolMode> toolMode,BuildConfiguration::BuildType buildType)2155 static bool buildTypeAccepted(QFlags<ToolMode> toolMode, BuildConfiguration::BuildType buildType)
2156 {
2157 if (buildType == BuildConfiguration::Unknown)
2158 return true;
2159 if (buildType == BuildConfiguration::Debug && (toolMode & DebugMode))
2160 return true;
2161 if (buildType == BuildConfiguration::Release && (toolMode & ReleaseMode))
2162 return true;
2163 if (buildType == BuildConfiguration::Profile && (toolMode & ProfileMode))
2164 return true;
2165 return false;
2166 }
2167
startupBuildType()2168 static BuildConfiguration::BuildType startupBuildType()
2169 {
2170 BuildConfiguration::BuildType buildType = BuildConfiguration::Unknown;
2171 if (RunConfiguration *runConfig = SessionManager::startupRunConfiguration()) {
2172 if (const BuildConfiguration *buildConfig = runConfig->target()->activeBuildConfiguration())
2173 buildType = buildConfig->buildType();
2174 }
2175 return buildType;
2176 }
2177
showCannotStartDialog(const QString & text)2178 void showCannotStartDialog(const QString &text)
2179 {
2180 auto errorDialog = new QMessageBox(ICore::dialogParent());
2181 errorDialog->setAttribute(Qt::WA_DeleteOnClose);
2182 errorDialog->setIcon(QMessageBox::Warning);
2183 errorDialog->setWindowTitle(text);
2184 errorDialog->setText(DebuggerPlugin::tr("Cannot start %1 without a project. Please open the project "
2185 "and try again.").arg(text));
2186 errorDialog->setStandardButtons(QMessageBox::Ok);
2187 errorDialog->setDefaultButton(QMessageBox::Ok);
2188 errorDialog->show();
2189 }
2190
wantRunTool(ToolMode toolMode,const QString & toolName)2191 bool wantRunTool(ToolMode toolMode, const QString &toolName)
2192 {
2193 // Check the project for whether the build config is in the correct mode
2194 // if not, notify the user and urge him to use the correct mode.
2195 BuildConfiguration::BuildType buildType = startupBuildType();
2196 if (!buildTypeAccepted(toolMode, buildType)) {
2197 QString currentMode;
2198 switch (buildType) {
2199 case BuildConfiguration::Debug:
2200 currentMode = DebuggerPlugin::tr("Debug");
2201 break;
2202 case BuildConfiguration::Profile:
2203 currentMode = DebuggerPlugin::tr("Profile");
2204 break;
2205 case BuildConfiguration::Release:
2206 currentMode = DebuggerPlugin::tr("Release");
2207 break;
2208 default:
2209 QTC_CHECK(false);
2210 }
2211
2212 QString toolModeString;
2213 switch (toolMode) {
2214 case DebugMode:
2215 toolModeString = DebuggerPlugin::tr("in Debug mode");
2216 break;
2217 case ProfileMode:
2218 toolModeString = DebuggerPlugin::tr("in Profile mode");
2219 break;
2220 case ReleaseMode:
2221 toolModeString = DebuggerPlugin::tr("in Release mode");
2222 break;
2223 case SymbolsMode:
2224 toolModeString = DebuggerPlugin::tr("with debug symbols (Debug or Profile mode)");
2225 break;
2226 case OptimizedMode:
2227 toolModeString = DebuggerPlugin::tr("on optimized code (Profile or Release mode)");
2228 break;
2229 default:
2230 QTC_CHECK(false);
2231 }
2232 const QString title = DebuggerPlugin::tr("Run %1 in %2 Mode?").arg(toolName).arg(currentMode);
2233 const QString message = DebuggerPlugin::tr("<html><head/><body><p>You are trying "
2234 "to run the tool \"%1\" on an application in %2 mode. "
2235 "The tool is designed to be used %3.</p><p>"
2236 "Run-time characteristics differ significantly between "
2237 "optimized and non-optimized binaries. Analytical "
2238 "findings for one mode may or may not be relevant for "
2239 "the other.</p><p>"
2240 "Running tools that need debug symbols on binaries that "
2241 "don't provide any may lead to missing function names "
2242 "or otherwise insufficient output.</p><p>"
2243 "Do you want to continue and run the tool in %2 mode?</p></body></html>")
2244 .arg(toolName).arg(currentMode).arg(toolModeString);
2245 if (Utils::CheckableMessageBox::doNotAskAgainQuestion(ICore::dialogParent(),
2246 title, message, ICore::settings(), "AnalyzerCorrectModeWarning")
2247 != QDialogButtonBox::Yes)
2248 return false;
2249 }
2250
2251 return true;
2252 }
2253
createStartAction()2254 QAction *createStartAction()
2255 {
2256 auto action = new QAction(DebuggerMainWindow::tr("Start"), m_instance);
2257 action->setIcon(ProjectExplorer::Icons::ANALYZER_START_SMALL_TOOLBAR.icon());
2258 action->setEnabled(true);
2259 return action;
2260 }
2261
createStopAction()2262 QAction *createStopAction()
2263 {
2264 auto action = new QAction(DebuggerMainWindow::tr("Stop"), m_instance);
2265 action->setIcon(Utils::Icons::STOP_SMALL_TOOLBAR.icon());
2266 action->setEnabled(true);
2267 return action;
2268 }
2269
enableMainWindow(bool on)2270 void enableMainWindow(bool on)
2271 {
2272 DebuggerMainWindow::instance()->setEnabled(on);
2273 }
2274
showPermanentStatusMessage(const QString & message)2275 void showPermanentStatusMessage(const QString &message)
2276 {
2277 DebuggerMainWindow::showStatusMessage(message, -1);
2278 }
2279
2280 namespace Internal {
2281
2282 static bool s_testRun = false;
isTestRun()2283 bool isTestRun() { return s_testRun; }
2284
2285 #ifdef WITH_TESTS
2286
2287 class DebuggerUnitTests : public QObject
2288 {
2289 Q_OBJECT
2290
2291 public:
2292 DebuggerUnitTests() = default;
2293
2294 private slots:
2295 void initTestCase();
2296 void cleanupTestCase();
2297
2298 void testDebuggerMatching_data();
2299 void testDebuggerMatching();
2300
2301 void testBenchmark();
2302 void testStateMachine();
2303
2304 private:
2305 CppTools::Tests::TemporaryCopiedDir *m_tmpDir = nullptr;
2306 };
2307
initTestCase()2308 void DebuggerUnitTests::initTestCase()
2309 {
2310 // const QList<Kit *> allKits = KitManager::kits();
2311 // if (allKits.count() != 1)
2312 // QSKIP("This test requires exactly one kit to be present");
2313 // const ToolChain * const toolchain = ToolChainKitAspect::toolChain(allKits.first());
2314 // if (!toolchain)
2315 // QSKIP("This test requires that there is a kit with a toolchain.");
2316 // bool hasClangExecutable;
2317 // clangExecutableFromSettings(toolchain->typeId(), &hasClangExecutable);
2318 // if (!hasClangExecutable)
2319 // QSKIP("No clang suitable for analyzing found");
2320
2321 s_testRun = true;
2322 m_tmpDir = new CppTools::Tests::TemporaryCopiedDir(":/debugger/unit-tests");
2323 QVERIFY(m_tmpDir->isValid());
2324 }
2325
cleanupTestCase()2326 void DebuggerUnitTests::cleanupTestCase()
2327 {
2328 delete m_tmpDir;
2329 }
2330
testStateMachine()2331 void DebuggerUnitTests::testStateMachine()
2332 {
2333 QString proFile = m_tmpDir->absolutePath("simple/simple.pro");
2334
2335 CppTools::Tests::ProjectOpenerAndCloser projectManager;
2336 const CppTools::ProjectInfo projectInfo = projectManager.open(proFile, true);
2337 QVERIFY(projectInfo.isValid());
2338
2339 QEventLoop loop;
2340 connect(BuildManager::instance(), &BuildManager::buildQueueFinished,
2341 &loop, &QEventLoop::quit);
2342 BuildManager::buildProjectWithDependencies(SessionManager::startupProject());
2343 loop.exec();
2344
2345 ExecuteOnDestruction guard([] { EditorManager::closeAllEditors(false); });
2346
2347 RunConfiguration *rc = SessionManager::startupRunConfiguration();
2348 QVERIFY(rc);
2349
2350 auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
2351 runControl->setRunConfiguration(rc);
2352 auto debugger = new DebuggerRunTool(runControl);
2353
2354 debugger->setInferior(rc->runnable());
2355 debugger->setTestCase(TestNoBoundsOfCurrentFunction);
2356
2357 connect(debugger, &DebuggerRunTool::stopped,
2358 &QTestEventLoop::instance(), &QTestEventLoop::exitLoop);
2359
2360 debugger->startRunControl();
2361
2362 QTestEventLoop::instance().enterLoop(5);
2363 }
2364
2365
2366 enum FakeEnum { FakeDebuggerCommonSettingsId };
2367
testBenchmark()2368 void DebuggerUnitTests::testBenchmark()
2369 {
2370 #ifdef WITH_BENCHMARK
2371 CALLGRIND_START_INSTRUMENTATION;
2372 volatile Id id1 = Id(DEBUGGER_COMMON_SETTINGS_ID);
2373 CALLGRIND_STOP_INSTRUMENTATION;
2374 CALLGRIND_DUMP_STATS;
2375
2376 CALLGRIND_START_INSTRUMENTATION;
2377 volatile FakeEnum id2 = FakeDebuggerCommonSettingsId;
2378 CALLGRIND_STOP_INSTRUMENTATION;
2379 CALLGRIND_DUMP_STATS;
2380 #endif
2381 }
2382
testDebuggerMatching_data()2383 void DebuggerUnitTests::testDebuggerMatching_data()
2384 {
2385 QTest::addColumn<QStringList>("debugger");
2386 QTest::addColumn<QString>("target");
2387 QTest::addColumn<int>("result");
2388
2389 QTest::newRow("Invalid data")
2390 << QStringList()
2391 << QString()
2392 << int(DebuggerItem::DoesNotMatch);
2393 QTest::newRow("Invalid debugger")
2394 << QStringList()
2395 << QString::fromLatin1("x86-linux-generic-elf-32bit")
2396 << int(DebuggerItem::DoesNotMatch);
2397 QTest::newRow("Invalid target")
2398 << QStringList("x86-linux-generic-elf-32bit")
2399 << QString()
2400 << int(DebuggerItem::DoesNotMatch);
2401
2402 QTest::newRow("Fuzzy match 1")
2403 << QStringList("unknown-unknown-unknown-unknown-0bit")
2404 << QString::fromLatin1("x86-linux-generic-elf-32bit")
2405 << int(DebuggerItem::MatchesWell); // Is this the expected behavior?
2406 QTest::newRow("Fuzzy match 2")
2407 << QStringList("unknown-unknown-unknown-unknown-0bit")
2408 << QString::fromLatin1("arm-windows-msys-pe-64bit")
2409 << int(DebuggerItem::MatchesWell); // Is this the expected behavior?
2410
2411 QTest::newRow("Architecture mismatch")
2412 << QStringList("x86-linux-generic-elf-32bit")
2413 << QString::fromLatin1("arm-linux-generic-elf-32bit")
2414 << int(DebuggerItem::DoesNotMatch);
2415 QTest::newRow("OS mismatch")
2416 << QStringList("x86-linux-generic-elf-32bit")
2417 << QString::fromLatin1("x86-macosx-generic-elf-32bit")
2418 << int(DebuggerItem::DoesNotMatch);
2419 QTest::newRow("Format mismatch")
2420 << QStringList("x86-linux-generic-elf-32bit")
2421 << QString::fromLatin1("x86-linux-generic-pe-32bit")
2422 << int(DebuggerItem::DoesNotMatch);
2423
2424 QTest::newRow("Linux perfect match")
2425 << QStringList("x86-linux-generic-elf-32bit")
2426 << QString::fromLatin1("x86-linux-generic-elf-32bit")
2427 << int(DebuggerItem::MatchesWell);
2428 QTest::newRow("Linux match")
2429 << QStringList("x86-linux-generic-elf-64bit")
2430 << QString::fromLatin1("x86-linux-generic-elf-32bit")
2431 << int(DebuggerItem::MatchesSomewhat);
2432
2433 QTest::newRow("Windows perfect match 1")
2434 << QStringList("x86-windows-msvc2013-pe-64bit")
2435 << QString::fromLatin1("x86-windows-msvc2013-pe-64bit")
2436 << int(DebuggerItem::MatchesWell);
2437 QTest::newRow("Windows perfect match 2")
2438 << QStringList("x86-windows-msvc2013-pe-64bit")
2439 << QString::fromLatin1("x86-windows-msvc2012-pe-64bit")
2440 << int(DebuggerItem::MatchesWell);
2441 QTest::newRow("Windows match 1")
2442 << QStringList("x86-windows-msvc2013-pe-64bit")
2443 << QString::fromLatin1("x86-windows-msvc2013-pe-32bit")
2444 << int(DebuggerItem::MatchesSomewhat);
2445 QTest::newRow("Windows match 2")
2446 << QStringList("x86-windows-msvc2013-pe-64bit")
2447 << QString::fromLatin1("x86-windows-msvc2012-pe-32bit")
2448 << int(DebuggerItem::MatchesSomewhat);
2449 QTest::newRow("Windows mismatch on word size")
2450 << QStringList("x86-windows-msvc2013-pe-32bit")
2451 << QString::fromLatin1("x86-windows-msvc2013-pe-64bit")
2452 << int(DebuggerItem::DoesNotMatch);
2453 QTest::newRow("Windows mismatch on osflavor 1")
2454 << QStringList("x86-windows-msvc2013-pe-32bit")
2455 << QString::fromLatin1("x86-windows-msys-pe-64bit")
2456 << int(DebuggerItem::DoesNotMatch);
2457 QTest::newRow("Windows mismatch on osflavor 2")
2458 << QStringList("x86-windows-msys-pe-32bit")
2459 << QString::fromLatin1("x86-windows-msvc2010-pe-64bit")
2460 << int(DebuggerItem::DoesNotMatch);
2461 }
2462
testDebuggerMatching()2463 void DebuggerUnitTests::testDebuggerMatching()
2464 {
2465 QFETCH(QStringList, debugger);
2466 QFETCH(QString, target);
2467 QFETCH(int, result);
2468
2469 auto expectedLevel = static_cast<DebuggerItem::MatchLevel>(result);
2470
2471 Abis debuggerAbis;
2472 for (const QString &abi : qAsConst(debugger))
2473 debuggerAbis << Abi::fromString(abi);
2474
2475 DebuggerItem item;
2476 item.setAbis(debuggerAbis);
2477
2478 DebuggerItem::MatchLevel level = item.matchTarget(Abi::fromString(target));
2479 if (level == DebuggerItem::MatchesPerfectly)
2480 level = DebuggerItem::MatchesWell;
2481
2482 QCOMPARE(expectedLevel, level);
2483 }
2484
createTestObjects() const2485 QVector<QObject *> DebuggerPlugin::createTestObjects() const
2486 {
2487 return {new DebuggerUnitTests};
2488 }
2489
2490 #else // ^-- if WITH_TESTS else --v
2491
createTestObjects() const2492 QVector<QObject *> DebuggerPlugin::createTestObjects() const
2493 {
2494 return {};
2495 }
2496
2497 #endif // if WITH_TESTS
2498
2499 } // namespace Internal
2500 } // namespace Debugger
2501
2502 #include "debuggerplugin.moc"
2503