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 "debuggermainwindow.h"
27 #include "debuggerruncontrol.h"
28 #include "terminal.h"
29
30 #include "analyzer/analyzermanager.h"
31 #include "console/console.h"
32 #include "debuggeractions.h"
33 #include "debuggercore.h"
34 #include "debuggerengine.h"
35 #include "debuggerinternalconstants.h"
36 #include "debuggerkitinformation.h"
37 #include "debuggerplugin.h"
38 #include "debuggerrunconfigurationaspect.h"
39 #include "breakhandler.h"
40 #include "enginemanager.h"
41 #include "shared/peutils.h"
42
43 #include <projectexplorer/buildconfiguration.h>
44 #include <projectexplorer/devicesupport/deviceprocessesdialog.h>
45 #include <projectexplorer/devicesupport/deviceprocesslist.h>
46 #include <projectexplorer/environmentaspect.h> // For the environment
47 #include <projectexplorer/project.h>
48 #include <projectexplorer/projectexplorer.h>
49 #include <projectexplorer/projectexplorericons.h>
50 #include <projectexplorer/runconfigurationaspects.h>
51 #include <projectexplorer/session.h>
52 #include <projectexplorer/target.h>
53 #include <projectexplorer/taskhub.h>
54 #include <projectexplorer/toolchain.h>
55
56 #include <utils/algorithm.h>
57 #include <utils/checkablemessagebox.h>
58 #include <utils/fileutils.h>
59 #include <utils/portlist.h>
60 #include <utils/qtcassert.h>
61 #include <utils/qtcprocess.h>
62 #include <utils/temporarydirectory.h>
63 #include <utils/temporaryfile.h>
64 #include <utils/url.h>
65 #include <utils/winutils.h>
66
67 #include <coreplugin/icontext.h>
68 #include <coreplugin/icore.h>
69 #include <coreplugin/coreconstants.h>
70 #include <coreplugin/messagebox.h>
71
72 #include <qmldebug/qmldebugcommandlinearguments.h>
73
74 #include <qtsupport/qtkitinformation.h>
75
76 #include <ssh/sshconnection.h>
77
78 #include <QTcpServer>
79 #include <QTimer>
80
81 using namespace Core;
82 using namespace Debugger::Internal;
83 using namespace ProjectExplorer;
84 using namespace Utils;
85
86 enum { debug = 0 };
87
88 namespace Debugger {
89 namespace Internal {
90
91 DebuggerEngine *createCdbEngine();
92 DebuggerEngine *createGdbEngine();
93 DebuggerEngine *createPdbEngine();
94 DebuggerEngine *createQmlEngine();
95 DebuggerEngine *createLldbEngine();
96 DebuggerEngine *createUvscEngine();
97
98 class CoreUnpacker final : public RunWorker
99 {
100 public:
CoreUnpacker(RunControl * runControl,const QString & coreFileName)101 CoreUnpacker(RunControl *runControl, const QString &coreFileName)
102 : RunWorker(runControl), m_coreFileName(coreFileName)
103 {}
104
coreFileName() const105 QString coreFileName() const { return m_tempCoreFileName; }
106
107 private:
~CoreUnpacker()108 ~CoreUnpacker() final
109 {
110 m_coreUnpackProcess.blockSignals(true);
111 m_coreUnpackProcess.terminate();
112 m_coreUnpackProcess.deleteLater();
113 if (m_tempCoreFile.isOpen())
114 m_tempCoreFile.close();
115
116 QFile::remove(m_tempCoreFileName);
117 }
118
start()119 void start() final
120 {
121 {
122 Utils::TemporaryFile tmp("tmpcore-XXXXXX");
123 tmp.open();
124 m_tempCoreFileName = tmp.fileName();
125 }
126
127 m_coreUnpackProcess.setWorkingDirectory(TemporaryDirectory::masterDirectoryPath());
128 connect(&m_coreUnpackProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
129 this, &CoreUnpacker::reportStarted);
130
131 const QString msg = DebuggerRunTool::tr("Unpacking core file to %1");
132 appendMessage(msg.arg(m_tempCoreFileName), LogMessageFormat);
133
134 if (m_coreFileName.endsWith(".lzo")) {
135 m_coreUnpackProcess.start("lzop", {"-o", m_tempCoreFileName, "-x", m_coreFileName});
136 return;
137 }
138
139 if (m_coreFileName.endsWith(".gz")) {
140 appendMessage(msg.arg(m_tempCoreFileName), LogMessageFormat);
141 m_tempCoreFile.setFileName(m_tempCoreFileName);
142 m_tempCoreFile.open(QFile::WriteOnly);
143 connect(&m_coreUnpackProcess, &QProcess::readyRead, this, [this] {
144 m_tempCoreFile.write(m_coreUnpackProcess.readAll());
145 });
146 m_coreUnpackProcess.start("gzip", {"-c", "-d", m_coreFileName});
147 return;
148 }
149
150 QTC_CHECK(false);
151 reportFailure("Unknown file extension in " + m_coreFileName);
152 }
153
154 QFile m_tempCoreFile;
155 QString m_coreFileName;
156 QString m_tempCoreFileName;
157 QProcess m_coreUnpackProcess;
158 };
159
160 class DebuggerRunToolPrivate
161 {
162 public:
163 bool useTerminal = false;
164 QPointer<CoreUnpacker> coreUnpacker;
165 QPointer<DebugServerPortsGatherer> portsGatherer;
166 bool addQmlServerInferiorCommandLineArgumentIfNeeded = false;
167 TerminalRunner *terminalRunner = nullptr;
168 int snapshotCounter = 0;
169 int engineStartsNeeded = 0;
170 int engineStopsNeeded = 0;
171 QString runId;
172 };
173
174 } // namespace Internal
175
176 static bool breakOnMainNextTime = false;
177
setBreakOnMainNextTime()178 void DebuggerRunTool::setBreakOnMainNextTime()
179 {
180 breakOnMainNextTime = true;
181 }
182
setStartMode(DebuggerStartMode startMode)183 void DebuggerRunTool::setStartMode(DebuggerStartMode startMode)
184 {
185 if (startMode == AttachToQmlServer) {
186 m_runParameters.startMode = AttachToRemoteProcess;
187 m_runParameters.cppEngineType = NoEngineType;
188 m_runParameters.isQmlDebugging = true;
189 m_runParameters.closeMode = KillAtClose;
190
191 // FIXME: This is horribly wrong.
192 // get files from all the projects in the session
193 QList<Project *> projects = SessionManager::projects();
194 if (Project *startupProject = SessionManager::startupProject()) {
195 // startup project first
196 projects.removeOne(startupProject);
197 projects.insert(0, startupProject);
198 }
199 foreach (Project *project, projects)
200 m_runParameters.projectSourceFiles.append(project->files(Project::SourceFiles));
201 if (!projects.isEmpty())
202 m_runParameters.projectSourceDirectory = projects.first()->projectDirectory();
203
204 } else {
205 m_runParameters.startMode = startMode;
206 }
207 }
208
setCloseMode(DebuggerCloseMode closeMode)209 void DebuggerRunTool::setCloseMode(DebuggerCloseMode closeMode)
210 {
211 m_runParameters.closeMode = closeMode;
212 }
213
setAttachPid(ProcessHandle pid)214 void DebuggerRunTool::setAttachPid(ProcessHandle pid)
215 {
216 m_runParameters.attachPID = pid;
217 }
218
setAttachPid(qint64 pid)219 void DebuggerRunTool::setAttachPid(qint64 pid)
220 {
221 m_runParameters.attachPID = ProcessHandle(pid);
222 }
223
setSysRoot(const Utils::FilePath & sysRoot)224 void DebuggerRunTool::setSysRoot(const Utils::FilePath &sysRoot)
225 {
226 m_runParameters.sysRoot = sysRoot;
227 }
228
setSymbolFile(const FilePath & symbolFile)229 void DebuggerRunTool::setSymbolFile(const FilePath &symbolFile)
230 {
231 if (symbolFile.isEmpty())
232 reportFailure(tr("Cannot debug: Local executable is not set."));
233 m_runParameters.symbolFile = symbolFile;
234 }
235
setLldbPlatform(const QString & platform)236 void DebuggerRunTool::setLldbPlatform(const QString &platform)
237 {
238 m_runParameters.platform = platform;
239 }
240
setRemoteChannel(const QString & channel)241 void DebuggerRunTool::setRemoteChannel(const QString &channel)
242 {
243 m_runParameters.remoteChannel = channel;
244 }
245
setRemoteChannel(const QUrl & url)246 void DebuggerRunTool::setRemoteChannel(const QUrl &url)
247 {
248 m_runParameters.remoteChannel = QString("%1:%2").arg(url.host()).arg(url.port());
249 }
250
remoteChannel() const251 QString DebuggerRunTool::remoteChannel() const
252 {
253 return m_runParameters.remoteChannel;
254 }
255
setRemoteChannel(const QString & host,int port)256 void DebuggerRunTool::setRemoteChannel(const QString &host, int port)
257 {
258 m_runParameters.remoteChannel = QString("%1:%2").arg(host).arg(port);
259 }
260
setUseExtendedRemote(bool on)261 void DebuggerRunTool::setUseExtendedRemote(bool on)
262 {
263 m_runParameters.useExtendedRemote = on;
264 }
265
setUseContinueInsteadOfRun(bool on)266 void DebuggerRunTool::setUseContinueInsteadOfRun(bool on)
267 {
268 m_runParameters.useContinueInsteadOfRun = on;
269 }
270
setUseTargetAsync(bool on)271 void DebuggerRunTool::setUseTargetAsync(bool on)
272 {
273 m_runParameters.useTargetAsync = on;
274 }
275
setContinueAfterAttach(bool on)276 void DebuggerRunTool::setContinueAfterAttach(bool on)
277 {
278 m_runParameters.continueAfterAttach = on;
279 }
280
setSkipExecutableValidation(bool on)281 void DebuggerRunTool::setSkipExecutableValidation(bool on)
282 {
283 m_runParameters.skipExecutableValidation = on;
284 }
285
setUseCtrlCStub(bool on)286 void DebuggerRunTool::setUseCtrlCStub(bool on)
287 {
288 m_runParameters.useCtrlCStub = on;
289 }
290
setBreakOnMain(bool on)291 void DebuggerRunTool::setBreakOnMain(bool on)
292 {
293 m_runParameters.breakOnMain = on;
294 }
295
setUseTerminal(bool on)296 void DebuggerRunTool::setUseTerminal(bool on)
297 {
298 // CDB has a built-in console that might be preferred by some.
299 bool useCdbConsole = m_runParameters.cppEngineType == CdbEngineType
300 && (m_runParameters.startMode == StartInternal
301 || m_runParameters.startMode == StartExternal)
302 && debuggerSettings()->useCdbConsole.value();
303
304 if (on && !d->terminalRunner && !useCdbConsole) {
305 d->terminalRunner =
306 new TerminalRunner(runControl(), [this] { return m_runParameters.inferior; });
307 d->terminalRunner->setRunAsRoot(m_runParameters.runAsRoot);
308 addStartDependency(d->terminalRunner);
309 }
310 if (!on && d->terminalRunner) {
311 QTC_CHECK(false); // User code can only switch from no terminal to one terminal.
312 }
313 }
314
setRunAsRoot(bool on)315 void DebuggerRunTool::setRunAsRoot(bool on)
316 {
317 m_runParameters.runAsRoot = on;
318 if (d->terminalRunner)
319 d->terminalRunner->setRunAsRoot(on);
320 }
321
setCommandsAfterConnect(const QString & commands)322 void DebuggerRunTool::setCommandsAfterConnect(const QString &commands)
323 {
324 m_runParameters.commandsAfterConnect = commands;
325 }
326
setCommandsForReset(const QString & commands)327 void DebuggerRunTool::setCommandsForReset(const QString &commands)
328 {
329 m_runParameters.commandsForReset = commands;
330 }
331
setDebugInfoLocation(const QString & debugInfoLocation)332 void DebuggerRunTool::setDebugInfoLocation(const QString &debugInfoLocation)
333 {
334 m_runParameters.debugInfoLocation = debugInfoLocation;
335 }
336
qmlServer() const337 QUrl DebuggerRunTool::qmlServer() const
338 {
339 return m_runParameters.qmlServer;
340 }
341
setQmlServer(const QUrl & qmlServer)342 void DebuggerRunTool::setQmlServer(const QUrl &qmlServer)
343 {
344 m_runParameters.qmlServer = qmlServer;
345 }
346
setIosPlatform(const QString & platform)347 void DebuggerRunTool::setIosPlatform(const QString &platform)
348 {
349 m_runParameters.platform = platform;
350 }
351
setDeviceSymbolsRoot(const QString & deviceSymbolsRoot)352 void DebuggerRunTool::setDeviceSymbolsRoot(const QString &deviceSymbolsRoot)
353 {
354 m_runParameters.deviceSymbolsRoot = deviceSymbolsRoot;
355 }
356
setTestCase(int testCase)357 void DebuggerRunTool::setTestCase(int testCase)
358 {
359 m_runParameters.testCase = testCase;
360 }
361
setOverrideStartScript(const QString & script)362 void DebuggerRunTool::setOverrideStartScript(const QString &script)
363 {
364 m_runParameters.overrideStartScript = script;
365 }
366
setAbi(const Abi & abi)367 void DebuggerRunTool::setAbi(const Abi &abi)
368 {
369 m_runParameters.toolChainAbi = abi;
370 }
371
setInferior(const Runnable & runnable)372 void DebuggerRunTool::setInferior(const Runnable &runnable)
373 {
374 m_runParameters.inferior = runnable;
375 }
376
setInferiorExecutable(const FilePath & executable)377 void DebuggerRunTool::setInferiorExecutable(const FilePath &executable)
378 {
379 m_runParameters.inferior.executable = executable;
380 }
381
setInferiorEnvironment(const Utils::Environment & env)382 void DebuggerRunTool::setInferiorEnvironment(const Utils::Environment &env)
383 {
384 m_runParameters.inferior.environment = env;
385 }
386
setInferiorDevice(IDevice::ConstPtr device)387 void DebuggerRunTool::setInferiorDevice(IDevice::ConstPtr device)
388 {
389 m_runParameters.inferior.device = device;
390 }
391
setRunControlName(const QString & name)392 void DebuggerRunTool::setRunControlName(const QString &name)
393 {
394 m_runParameters.displayName = name;
395 }
396
setStartMessage(const QString & msg)397 void DebuggerRunTool::setStartMessage(const QString &msg)
398 {
399 m_runParameters.startMessage = msg;
400 }
401
setCoreFileName(const QString & coreFile,bool isSnapshot)402 void DebuggerRunTool::setCoreFileName(const QString &coreFile, bool isSnapshot)
403 {
404 if (coreFile.endsWith(".gz") || coreFile.endsWith(".lzo")) {
405 d->coreUnpacker = new CoreUnpacker(runControl(), coreFile);
406 addStartDependency(d->coreUnpacker);
407 }
408
409 m_runParameters.coreFile = coreFile;
410 m_runParameters.isSnapshot = isSnapshot;
411 }
412
addQmlServerInferiorCommandLineArgumentIfNeeded()413 void DebuggerRunTool::addQmlServerInferiorCommandLineArgumentIfNeeded()
414 {
415 d->addQmlServerInferiorCommandLineArgumentIfNeeded = true;
416 }
417
modifyDebuggerEnvironment(const EnvironmentItems & items)418 void DebuggerRunTool::modifyDebuggerEnvironment(const EnvironmentItems &items)
419 {
420 m_runParameters.debugger.environment.modify(items);
421 }
422
setCrashParameter(const QString & event)423 void DebuggerRunTool::setCrashParameter(const QString &event)
424 {
425 m_runParameters.crashParameter = event;
426 }
427
addExpectedSignal(const QString & signal)428 void DebuggerRunTool::addExpectedSignal(const QString &signal)
429 {
430 m_runParameters.expectedSignals.append(signal);
431 }
432
addSearchDirectory(const Utils::FilePath & dir)433 void DebuggerRunTool::addSearchDirectory(const Utils::FilePath &dir)
434 {
435 m_runParameters.additionalSearchDirectories.append(dir);
436 }
437
start()438 void DebuggerRunTool::start()
439 {
440 TaskHub::clearTasks(Constants::TASK_CATEGORY_DEBUGGER_DEBUGINFO);
441 TaskHub::clearTasks(Constants::TASK_CATEGORY_DEBUGGER_RUNTIME);
442
443 if (d->portsGatherer) {
444 setRemoteChannel(d->portsGatherer->gdbServer());
445 setQmlServer(d->portsGatherer->qmlServer());
446 if (d->addQmlServerInferiorCommandLineArgumentIfNeeded
447 && m_runParameters.isQmlDebugging
448 && m_runParameters.isCppDebugging()) {
449
450 int qmlServerPort = m_runParameters.qmlServer.port();
451 QTC_ASSERT(qmlServerPort > 0, reportFailure(); return);
452 QString mode = QString("port:%1").arg(qmlServerPort);
453
454 CommandLine cmd{m_runParameters.inferior.executable};
455 cmd.addArg(qmlDebugCommandLineArguments(QmlDebug::QmlDebuggerServices, mode, true));
456 cmd.addArgs(m_runParameters.inferior.commandLineArguments, CommandLine::Raw);
457
458 m_runParameters.inferior.setCommandLine(cmd);
459 }
460 }
461
462 // User canceled input dialog asking for executable when working on library project.
463 if (m_runParameters.startMode == StartInternal
464 && m_runParameters.inferior.executable.isEmpty()
465 && m_runParameters.interpreter.isEmpty()) {
466 reportFailure(tr("No executable specified."));
467 return;
468 }
469
470 // QML and/or mixed are not prepared for it.
471 setSupportsReRunning(!m_runParameters.isQmlDebugging);
472
473 // FIXME: Disabled due to Android. Make Android device report available ports instead.
474 // int portsUsed = portsUsedByDebugger();
475 // if (portsUsed > device()->freePorts().count()) {
476 // reportFailure(tr("Cannot debug: Not enough free ports available."));
477 // return;
478 // }
479
480 if (d->coreUnpacker)
481 m_runParameters.coreFile = d->coreUnpacker->coreFileName();
482
483 if (!fixupParameters())
484 return;
485
486 if (m_runParameters.cppEngineType == CdbEngineType
487 && Utils::is64BitWindowsBinary(m_runParameters.inferior.executable.toString())
488 && !Utils::is64BitWindowsBinary(m_runParameters.debugger.executable.toString())) {
489 reportFailure(
490 DebuggerPlugin::tr(
491 "%1 is a 64 bit executable which can not be debugged by a 32 bit Debugger.\n"
492 "Please select a 64 bit Debugger in the kit settings for this kit.")
493 .arg(m_runParameters.inferior.executable.toUserOutput()));
494 return;
495 }
496
497 Utils::globalMacroExpander()->registerFileVariables(
498 "DebuggedExecutable", tr("Debugged executable"),
499 [this] { return m_runParameters.inferior.executable; }
500 );
501
502 runControl()->setDisplayName(m_runParameters.displayName);
503
504 if (!m_engine) {
505 if (m_runParameters.isCppDebugging()) {
506 switch (m_runParameters.cppEngineType) {
507 case GdbEngineType:
508 m_engine = createGdbEngine();
509 break;
510 case CdbEngineType:
511 if (!HostOsInfo::isWindowsHost()) {
512 reportFailure(tr("Unsupported CDB host system."));
513 return;
514 }
515 m_engine = createCdbEngine();
516 break;
517 case LldbEngineType:
518 m_engine = createLldbEngine();
519 break;
520 case PdbEngineType: // FIXME: Yes, Python counts as C++...
521 QTC_CHECK(false); // Called from DebuggerRunTool constructor already.
522 // m_engine = createPdbEngine();
523 break;
524 case UvscEngineType:
525 m_engine = createUvscEngine();
526 break;
527 default:
528 if (!m_runParameters.isQmlDebugging) {
529 reportFailure(DebuggerPlugin::tr("Unable to create a debugging engine. "
530 "Please select a Debugger Setting from the Run page of the project mode."));
531 return;
532 }
533 // Can happen for pure Qml.
534 break;
535 }
536 }
537
538 if (m_runParameters.isQmlDebugging) {
539 if (m_engine) {
540 m_engine2 = createQmlEngine();
541 } else {
542 m_engine = createQmlEngine();
543 }
544 }
545 }
546
547 if (!m_engine) {
548 reportFailure(DebuggerPlugin::tr("Unable to create a debugging engine."));
549 return;
550 }
551
552 m_engine->setRunParameters(m_runParameters);
553 m_engine->setRunId(d->runId);
554 m_engine->setRunTool(this);
555 m_engine->setCompanionEngine(m_engine2);
556 connect(m_engine, &DebuggerEngine::requestRunControlFinish,
557 runControl(), &RunControl::initiateFinish);
558 connect(m_engine, &DebuggerEngine::requestRunControlStop,
559 runControl(), &RunControl::initiateStop);
560 connect(m_engine, &DebuggerEngine::engineStarted,
561 this, [this] { handleEngineStarted(m_engine); });
562 connect(m_engine, &DebuggerEngine::engineFinished,
563 this, [this] { handleEngineFinished(m_engine); });
564 connect(m_engine, &DebuggerEngine::appendMessageRequested,
565 this, &DebuggerRunTool::appendMessage);
566 ++d->engineStartsNeeded;
567 ++d->engineStopsNeeded;
568
569 connect(m_engine, &DebuggerEngine::attachToCoreRequested, this, [this](const QString &coreFile) {
570 auto runConfig = runControl()->runConfiguration();
571 QTC_ASSERT(runConfig, return);
572 auto rc = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
573 rc->setRunConfiguration(runConfig);
574 auto name = QString(tr("%1 - Snapshot %2").arg(runControl()->displayName()).arg(++d->snapshotCounter));
575 auto debugger = new DebuggerRunTool(rc);
576 debugger->setStartMode(AttachToCore);
577 debugger->setRunControlName(name);
578 debugger->setCoreFileName(coreFile, true);
579 debugger->startRunControl();
580 });
581
582 if (m_engine2) {
583 m_engine2->setRunParameters(m_runParameters);
584 m_engine2->setRunId(d->runId);
585 m_engine2->setRunTool(this);
586 m_engine2->setCompanionEngine(m_engine);
587 m_engine2->setSecondaryEngine();
588 connect(m_engine2, &DebuggerEngine::requestRunControlFinish,
589 runControl(), &RunControl::initiateFinish);
590 connect(m_engine2, &DebuggerEngine::requestRunControlStop,
591 runControl(), &RunControl::initiateStop);
592 connect(m_engine2, &DebuggerEngine::engineStarted,
593 this, [this] { handleEngineStarted(m_engine2); });
594 connect(m_engine2, &DebuggerEngine::engineFinished,
595 this, [this] { handleEngineFinished(m_engine2); });
596 connect(m_engine2, &DebuggerEngine::appendMessageRequested,
597 this, &DebuggerRunTool::appendMessage);
598 ++d->engineStartsNeeded;
599 ++d->engineStopsNeeded;
600 }
601
602 if (m_runParameters.startMode == StartInternal) {
603 QStringList unhandledIds;
604 bool hasQmlBreakpoints = false;
605 for (const GlobalBreakpoint &gbp : BreakpointManager::globalBreakpoints()) {
606 if (gbp->isEnabled()) {
607 const BreakpointParameters &bp = gbp->requestedParameters();
608 hasQmlBreakpoints = hasQmlBreakpoints || bp.isQmlFileAndLineBreakpoint();
609 if (!m_engine->acceptsBreakpoint(bp)) {
610 if (!m_engine2 || !m_engine2->acceptsBreakpoint(bp))
611 unhandledIds.append(gbp->displayName());
612 }
613 }
614 }
615 if (!unhandledIds.isEmpty()) {
616 QString warningMessage =
617 DebuggerPlugin::tr("Some breakpoints cannot be handled by the debugger "
618 "languages currently active, and will be ignored.<p>"
619 "Affected are breakpoints %1")
620 .arg(unhandledIds.join(", "));
621
622 if (hasQmlBreakpoints) {
623 warningMessage += "<p>" +
624 DebuggerPlugin::tr("QML debugging needs to be enabled both in the Build "
625 "and the Run settings.");
626 }
627
628 showMessage(warningMessage, LogWarning);
629
630 static bool checked = true;
631 if (checked)
632 CheckableMessageBox::information(Core::ICore::dialogParent(),
633 tr("Debugger"),
634 warningMessage,
635 tr("&Show this message again."),
636 &checked, QDialogButtonBox::Ok);
637 }
638 }
639
640 appendMessage(tr("Debugging %1 ...").arg(m_runParameters.inferior.commandLine().toUserOutput()),
641 NormalMessageFormat);
642 QString debuggerName = m_engine->objectName();
643 if (m_engine2)
644 debuggerName += ' ' + m_engine2->objectName();
645
646 const QString message = tr("Starting debugger \"%1\" for ABI \"%2\"...")
647 .arg(debuggerName).arg(m_runParameters.toolChainAbi.toString());
648 DebuggerMainWindow::showStatusMessage(message, 10000);
649
650 showMessage(m_engine->formatStartParameters(), LogDebug);
651 showMessage(DebuggerSettings::dump(), LogDebug);
652
653 if (m_engine2)
654 m_engine2->start();
655 m_engine->start();
656 }
657
stop()658 void DebuggerRunTool::stop()
659 {
660 QTC_ASSERT(m_engine, reportStopped(); return);
661 if (m_engine2)
662 m_engine2->quitDebugger();
663 m_engine->quitDebugger();
664 }
665
handleEngineStarted(DebuggerEngine * engine)666 void DebuggerRunTool::handleEngineStarted(DebuggerEngine *engine)
667 {
668 // Correct:
669 // if (--d->engineStartsNeeded == 0) {
670 // EngineManager::activateDebugMode();
671 // reportStarted();
672 // }
673
674 // Feels better, as the QML Engine might attach late or not at all.
675 if (engine == m_engine) {
676 EngineManager::activateDebugMode();
677 reportStarted();
678 }
679 }
680
handleEngineFinished(DebuggerEngine * engine)681 void DebuggerRunTool::handleEngineFinished(DebuggerEngine *engine)
682 {
683 engine->prepareForRestart();
684 if (--d->engineStopsNeeded == 0) {
685 QString cmd = m_runParameters.inferior.commandLine().toUserOutput();
686 QString msg = engine->runParameters().exitCode // Main engine.
687 ? tr("Debugging of %1 has finished with exit code %2.")
688 .arg(cmd).arg(engine->runParameters().exitCode.value())
689 : tr("Debugging of %1 has finished.").arg(cmd);
690 appendMessage(msg, NormalMessageFormat);
691 reportStopped();
692 }
693 }
694
isCppDebugging() const695 bool DebuggerRunTool::isCppDebugging() const
696 {
697 return m_runParameters.isCppDebugging();
698 }
699
isQmlDebugging() const700 bool DebuggerRunTool::isQmlDebugging() const
701 {
702 return m_runParameters.isQmlDebugging;
703 }
704
portsUsedByDebugger() const705 int DebuggerRunTool::portsUsedByDebugger() const
706 {
707 return isCppDebugging() + isQmlDebugging();
708 }
709
setUsePortsGatherer(bool useCpp,bool useQml)710 void DebuggerRunTool::setUsePortsGatherer(bool useCpp, bool useQml)
711 {
712 QTC_ASSERT(!d->portsGatherer, reportFailure(); return);
713 d->portsGatherer = new DebugServerPortsGatherer(runControl());
714 d->portsGatherer->setUseGdbServer(useCpp);
715 d->portsGatherer->setUseQmlServer(useQml);
716 addStartDependency(d->portsGatherer);
717 }
718
portsGatherer() const719 DebugServerPortsGatherer *DebuggerRunTool::portsGatherer() const
720 {
721 return d->portsGatherer;
722 }
723
setSolibSearchPath(const QStringList & list)724 void DebuggerRunTool::setSolibSearchPath(const QStringList &list)
725 {
726 m_runParameters.solibSearchPath = list;
727 }
728
fixupParameters()729 bool DebuggerRunTool::fixupParameters()
730 {
731 DebuggerRunParameters &rp = m_runParameters;
732 if (rp.symbolFile.isEmpty())
733 rp.symbolFile = rp.inferior.executable;
734
735 // Copy over DYLD_IMAGE_SUFFIX etc
736 for (const auto &var :
737 QStringList({"DYLD_IMAGE_SUFFIX", "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH"}))
738 if (rp.inferior.environment.hasKey(var))
739 rp.debugger.environment.set(var, rp.inferior.environment.expandedValueForKey(var));
740
741 // validate debugger if C++ debugging is enabled
742 if (rp.isCppDebugging() && !rp.validationErrors.isEmpty()) {
743 reportFailure(rp.validationErrors.join('\n'));
744 return false;
745 }
746
747 if (rp.isQmlDebugging) {
748 if (device() && device()->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
749 if (rp.qmlServer.port() <= 0) {
750 rp.qmlServer = Utils::urlFromLocalHostAndFreePort();
751 if (rp.qmlServer.port() <= 0) {
752 reportFailure(DebuggerPlugin::tr("Not enough free ports for QML debugging."));
753 return false;
754 }
755 }
756 // Makes sure that all bindings go through the JavaScript engine, so that
757 // breakpoints are actually hit!
758 const QString optimizerKey = "QML_DISABLE_OPTIMIZER";
759 if (!rp.inferior.environment.hasKey(optimizerKey))
760 rp.inferior.environment.set(optimizerKey, "1");
761 }
762 }
763
764 if (!debuggerSettings()->autoEnrichParameters.value()) {
765 const QString sysroot = rp.sysRoot.toString();
766 if (rp.debugInfoLocation.isEmpty())
767 rp.debugInfoLocation = sysroot + "/usr/lib/debug";
768 if (rp.debugSourceLocation.isEmpty()) {
769 QString base = sysroot + "/usr/src/debug/";
770 rp.debugSourceLocation.append(base + "qt5base/src/corelib");
771 rp.debugSourceLocation.append(base + "qt5base/src/gui");
772 rp.debugSourceLocation.append(base + "qt5base/src/network");
773 }
774 }
775
776 if (rp.isQmlDebugging) {
777 QmlDebug::QmlDebugServicesPreset service;
778 if (rp.isCppDebugging()) {
779 if (rp.nativeMixedEnabled) {
780 service = QmlDebug::QmlNativeDebuggerServices;
781 } else {
782 service = QmlDebug::QmlDebuggerServices;
783 }
784 } else {
785 service = QmlDebug::QmlDebuggerServices;
786 }
787 if (rp.startMode != AttachToLocalProcess && rp.startMode != AttachToCrashedProcess) {
788 QString qmlarg = rp.isCppDebugging() && rp.nativeMixedEnabled
789 ? QmlDebug::qmlDebugNativeArguments(service, false)
790 : QmlDebug::qmlDebugTcpArguments(service, rp.qmlServer);
791 ProcessArgs::addArg(&rp.inferior.commandLineArguments, qmlarg);
792 }
793 }
794
795 if (rp.startMode == NoStartMode)
796 rp.startMode = StartInternal;
797
798 if (breakOnMainNextTime) {
799 rp.breakOnMain = true;
800 breakOnMainNextTime = false;
801 }
802
803 if (HostOsInfo::isWindowsHost()) {
804 ProcessArgs::SplitError perr;
805 rp.inferior.commandLineArguments =
806 ProcessArgs::prepareArgs(rp.inferior.commandLineArguments, &perr,
807 HostOsInfo::hostOs(), nullptr,
808 &rp.inferior.workingDirectory).toWindowsArgs();
809 if (perr != ProcessArgs::SplitOk) {
810 // perr == BadQuoting is never returned on Windows
811 // FIXME? QTCREATORBUG-2809
812 reportFailure(DebuggerPlugin::tr("Debugging complex command lines "
813 "is currently not supported on Windows."));
814 return false;
815 }
816 }
817
818 if (rp.isNativeMixedDebugging())
819 rp.inferior.environment.set("QV4_FORCE_INTERPRETER", "1");
820
821 if (debuggerSettings()->forceLoggingToConsole.value())
822 rp.inferior.environment.set("QT_LOGGING_TO_CONSOLE", "1");
823
824 return true;
825 }
826
terminalRunner() const827 Internal::TerminalRunner *DebuggerRunTool::terminalRunner() const
828 {
829 return d->terminalRunner;
830 }
831
cppEngineType() const832 DebuggerEngineType DebuggerRunTool::cppEngineType() const
833 {
834 return m_runParameters.cppEngineType;
835 }
836
DebuggerRunTool(RunControl * runControl,AllowTerminal allowTerminal)837 DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerminal)
838 : RunWorker(runControl), d(new DebuggerRunToolPrivate)
839 {
840 setId("DebuggerRunTool");
841
842 static int toolRunCount = 0;
843
844 // Reset once all are gone.
845 if (EngineManager::engines().isEmpty())
846 toolRunCount = 0;
847
848 d->runId = QString::number(++toolRunCount);
849
850 runControl->setIcon(ProjectExplorer::Icons::DEBUG_START_SMALL_TOOLBAR);
851 runControl->setPromptToStop([](bool *optionalPrompt) {
852 return RunControl::showPromptToStopDialog(
853 DebuggerRunTool::tr("Close Debugging Session"),
854 DebuggerRunTool::tr("A debugging session is still in progress. "
855 "Terminating the session in the current"
856 " state can leave the target in an inconsistent state."
857 " Would you still like to terminate it?"),
858 QString(), QString(), optionalPrompt);
859 });
860
861 m_runParameters.displayName = runControl->displayName();
862
863 if (auto symbolsAspect = runControl->aspect<SymbolFileAspect>())
864 m_runParameters.symbolFile = symbolsAspect->filePath();
865 if (auto terminalAspect = runControl->aspect<TerminalAspect>())
866 m_runParameters.useTerminal = terminalAspect->useTerminal();
867 if (auto runAsRootAspect = runControl->aspect<RunAsRootAspect>())
868 m_runParameters.runAsRoot = runAsRootAspect->value();
869
870 Kit *kit = runControl->kit();
871 QTC_ASSERT(kit, return);
872
873 m_runParameters.sysRoot = SysRootKitAspect::sysRoot(kit);
874 m_runParameters.macroExpander = kit->macroExpander();
875 m_runParameters.debugger = DebuggerKitAspect::runnable(kit);
876 m_runParameters.cppEngineType = DebuggerKitAspect::engineType(kit);
877
878 if (QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(kit))
879 m_runParameters.qtPackageSourceLocation = qtVersion->qtPackageSourcePath().toString();
880
881 if (auto aspect = runControl->aspect<DebuggerRunConfigurationAspect>()) {
882 if (!aspect->useCppDebugger())
883 m_runParameters.cppEngineType = NoEngineType;
884 m_runParameters.isQmlDebugging = aspect->useQmlDebugger();
885 m_runParameters.multiProcess = aspect->useMultiProcess();
886 m_runParameters.additionalStartupCommands = aspect->overrideStartup();
887 }
888
889 m_runParameters.inferior = runnable();
890 // Normalize to work around QTBUG-17529 (QtDeclarative fails with 'File name case mismatch'...)
891 m_runParameters.inferior.workingDirectory =
892 FileUtils::normalizePathName(m_runParameters.inferior.workingDirectory);
893 setUseTerminal(allowTerminal == DoAllowTerminal && m_runParameters.useTerminal);
894
895 const QByteArray envBinary = qgetenv("QTC_DEBUGGER_PATH");
896 if (!envBinary.isEmpty())
897 m_runParameters.debugger.executable = FilePath::fromString(QString::fromLocal8Bit(envBinary));
898
899 if (Project *project = runControl->project()) {
900 m_runParameters.projectSourceDirectory = project->projectDirectory();
901 m_runParameters.projectSourceFiles = project->files(Project::SourceFiles);
902 }
903
904 m_runParameters.toolChainAbi = ToolChainKitAspect::targetAbi(kit);
905
906 bool ok = false;
907 const int nativeMixedOverride = qEnvironmentVariableIntValue("QTC_DEBUGGER_NATIVE_MIXED", &ok);
908 if (ok)
909 m_runParameters.nativeMixedEnabled = bool(nativeMixedOverride);
910
911 // This will only be shown in some cases, but we don't want to access
912 // the kit at that time anymore.
913 const Tasks tasks = DebuggerKitAspect::validateDebugger(kit);
914 for (const Task &t : tasks) {
915 if (t.type != Task::Warning)
916 m_runParameters.validationErrors.append(t.description());
917 }
918
919 RunConfiguration *runConfig = runControl->runConfiguration();
920 if (runConfig && runConfig->property("supportsDebugger").toBool()) {
921 const QString mainScript = runConfig->property("mainScript").toString();
922 const QString interpreter = runConfig->property("interpreter").toString();
923 if (!interpreter.isEmpty() && mainScript.endsWith(".py")) {
924 m_runParameters.mainScript = mainScript;
925 m_runParameters.interpreter = interpreter;
926 const QString args = runConfig->property("arguments").toString();
927 if (!args.isEmpty()) {
928 if (!m_runParameters.inferior.commandLineArguments.isEmpty())
929 m_runParameters.inferior.commandLineArguments.append(' ');
930 m_runParameters.inferior.commandLineArguments.append(args);
931 }
932 m_engine = createPdbEngine();
933 }
934 }
935 }
936
startRunControl()937 void DebuggerRunTool::startRunControl()
938 {
939 ProjectExplorerPlugin::startRunControl(runControl());
940 }
941
addSolibSearchDir(const QString & str)942 void DebuggerRunTool::addSolibSearchDir(const QString &str)
943 {
944 QString path = str;
945 path.replace("%{sysroot}", m_runParameters.sysRoot.toString());
946 m_runParameters.solibSearchPath.append(path);
947 }
948
~DebuggerRunTool()949 DebuggerRunTool::~DebuggerRunTool()
950 {
951 if (m_runParameters.isSnapshot && !m_runParameters.coreFile.isEmpty())
952 QFile::remove(m_runParameters.coreFile);
953
954 delete m_engine2;
955 m_engine2 = nullptr;
956 delete m_engine;
957 m_engine = nullptr;
958
959 delete d;
960 }
961
showMessage(const QString & msg,int channel,int timeout)962 void DebuggerRunTool::showMessage(const QString &msg, int channel, int timeout)
963 {
964 if (channel == ConsoleOutput)
965 debuggerConsole()->printItem(ConsoleItem::DefaultType, msg);
966
967 QTC_ASSERT(m_engine, qDebug() << msg; return);
968
969 m_engine->showMessage(msg, channel, timeout);
970 if (m_engine2)
971 m_engine->showMessage(msg, channel, timeout);
972 switch (channel) {
973 case AppOutput:
974 appendMessage(msg, StdOutFormat);
975 break;
976 case AppError:
977 appendMessage(msg, StdErrFormat);
978 break;
979 case AppStuff:
980 appendMessage(msg, DebugFormat);
981 break;
982 default:
983 break;
984 }
985 }
986
987 ////////////////////////////////////////////////////////////////////////
988 //
989 // Externally visible helper.
990 //
991 ////////////////////////////////////////////////////////////////////////
992
993 // GdbServerPortGatherer
994
DebugServerPortsGatherer(RunControl * runControl)995 DebugServerPortsGatherer::DebugServerPortsGatherer(RunControl *runControl)
996 : ChannelProvider(runControl, 2)
997 {
998 setId("DebugServerPortsGatherer");
999 }
1000
1001 DebugServerPortsGatherer::~DebugServerPortsGatherer() = default;
1002
gdbServer() const1003 QUrl DebugServerPortsGatherer::gdbServer() const
1004 {
1005 return channel(0);
1006 }
1007
qmlServer() const1008 QUrl DebugServerPortsGatherer::qmlServer() const
1009 {
1010 return channel(1);
1011 }
1012
1013 // DebugServerRunner
1014
DebugServerRunner(RunControl * runControl,DebugServerPortsGatherer * portsGatherer)1015 DebugServerRunner::DebugServerRunner(RunControl *runControl, DebugServerPortsGatherer *portsGatherer)
1016 : SimpleTargetRunner(runControl)
1017 {
1018 setId("DebugServerRunner");
1019 const Runnable mainRunnable = runControl->runnable();
1020 addStartDependency(portsGatherer);
1021
1022 QTC_ASSERT(portsGatherer, reportFailure(); return);
1023
1024 setStarter([this, runControl, mainRunnable, portsGatherer] {
1025 QTC_ASSERT(portsGatherer, reportFailure(); return);
1026
1027 Runnable debugServer;
1028 debugServer.environment = mainRunnable.environment;
1029 debugServer.workingDirectory = mainRunnable.workingDirectory;
1030
1031 QStringList args = ProcessArgs::splitArgs(mainRunnable.commandLineArguments, OsTypeLinux);
1032
1033 const bool isQmlDebugging = portsGatherer->useQmlServer();
1034 const bool isCppDebugging = portsGatherer->useGdbServer();
1035
1036 if (isQmlDebugging) {
1037 args.prepend(QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlDebuggerServices,
1038 portsGatherer->qmlServer()));
1039 }
1040 if (isQmlDebugging && !isCppDebugging) {
1041 debugServer.executable = mainRunnable.executable; // FIXME: Case should not happen?
1042 } else {
1043 debugServer.executable = FilePath::fromString(runControl->device()->debugServerPath());
1044 if (debugServer.executable.isEmpty())
1045 debugServer.executable = FilePath::fromString("gdbserver");
1046 args.clear();
1047 if (debugServer.executable.toString().contains("lldb-server")) {
1048 args.append("platform");
1049 args.append("--listen");
1050 args.append(QString("*:%1").arg(portsGatherer->gdbServer().port()));
1051 args.append("--server");
1052 } else {
1053 // Something resembling gdbserver
1054 if (m_useMulti)
1055 args.append("--multi");
1056 if (m_pid.isValid())
1057 args.append("--attach");
1058 args.append(QString(":%1").arg(portsGatherer->gdbServer().port()));
1059 if (m_pid.isValid())
1060 args.append(QString::number(m_pid.pid()));
1061 }
1062 }
1063 debugServer.commandLineArguments = ProcessArgs::joinArgs(args, OsTypeLinux);
1064
1065 doStart(debugServer, runControl->device());
1066 });
1067 }
1068
1069 DebugServerRunner::~DebugServerRunner() = default;
1070
setUseMulti(bool on)1071 void DebugServerRunner::setUseMulti(bool on)
1072 {
1073 m_useMulti = on;
1074 }
1075
setAttachPid(ProcessHandle pid)1076 void DebugServerRunner::setAttachPid(ProcessHandle pid)
1077 {
1078 m_pid = pid;
1079 }
1080
1081 } // namespace Debugger
1082