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