From 588443b259d366dfc0e98db133914cf969c11c55 Mon Sep 17 00:00:00 2001
From: hjk <hjk@theqtcompany.com>
Date: Fri, 29 Jul 2016 15:55:15 +0200
Subject: [PATCH] Debugger: Use a StandardRunnable for the debugger process

One step further to separate the debugger environment from the
inferior environment and to make it possible to configure a
working directory. Guessing one from the inferior's working
directory is not always a good idea.

Change-Id: I33d139c0f228ec0870556b82bc6aecca0a8e62d6
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
---
 src/plugins/android/androidconfigurations.cpp |  2 +-
 src/plugins/debugger/cdb/cdbengine.cpp        |  4 +-
 src/plugins/debugger/debuggerengine.cpp       |  2 +-
 src/plugins/debugger/debuggerengine.h         |  4 +-
 src/plugins/debugger/debuggeritem.cpp         | 19 ++++++--
 src/plugins/debugger/debuggeritem.h           |  6 ++-
 .../debugger/debuggerkitinformation.cpp       | 21 +++++----
 src/plugins/debugger/debuggerkitinformation.h |  3 +-
 src/plugins/debugger/debuggeroptionspage.cpp  | 43 +++++++++++------
 src/plugins/debugger/debuggerplugin.cpp       |  4 +-
 src/plugins/debugger/debuggerruncontrol.cpp   | 12 ++++-
 .../debugger/debuggerstartparameters.h        |  1 -
 src/plugins/debugger/gdb/attachgdbadapter.cpp |  4 --
 src/plugins/debugger/gdb/coregdbadapter.cpp   | 10 ++--
 src/plugins/debugger/gdb/coregdbadapter.h     |  3 +-
 src/plugins/debugger/gdb/gdbengine.cpp        | 46 +++++++++----------
 src/plugins/debugger/gdb/gdbengine.h          |  4 +-
 src/plugins/debugger/gdb/gdbplainengine.cpp   |  4 --
 .../debugger/gdb/remotegdbserveradapter.cpp   |  2 -
 src/plugins/debugger/lldb/lldbengine.cpp      | 16 +++----
 src/plugins/debugger/lldb/lldbengine.h        |  1 -
 src/plugins/debugger/loadcoredialog.cpp       |  5 +-
 src/plugins/debugger/pdb/pdbengine.cpp        |  2 +-
 23 files changed, 125 insertions(+), 93 deletions(-)

diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp
index 812928811b6..eb39966d37a 100644
--- a/src/plugins/android/androidconfigurations.cpp
+++ b/src/plugins/android/androidconfigurations.cpp
@@ -1268,7 +1268,7 @@ void AndroidConfigurations::updateAutomaticKitList()
 
         // Update code for 3.0 beta, which shipped with a bug for the debugger settings
         ToolChain *tc = ToolChainKitInformation::toolChain(k, ToolChain::Language::Cxx);
-        if (tc && Debugger::DebuggerKitInformation::debuggerCommand(k) != tc->suggestedDebugger()) {
+        if (tc && Debugger::DebuggerKitInformation::runnable(k).executable != tc->suggestedDebugger().toString()) {
             Debugger::DebuggerItem debugger;
             debugger.setCommand(tc->suggestedDebugger());
             debugger.setEngineType(Debugger::GdbEngineType);
diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp
index a1ec4a0824e..e3c7e7a5a24 100644
--- a/src/plugins/debugger/cdb/cdbengine.cpp
+++ b/src/plugins/debugger/cdb/cdbengine.cpp
@@ -506,7 +506,7 @@ bool CdbEngine::launchCDB(const DebuggerRunParameters &sp, QString *errorMessage
     // Determine binary (force MSVC), extension lib name and path to use
     // The extension is passed as relative name with the path variable set
     //(does not work with absolute path names)
-    const QString executable = sp.debuggerCommand;
+    const QString executable = sp.debugger.executable;
     if (executable.isEmpty()) {
         *errorMessage = tr("There is no CDB executable specified.");
         return false;
@@ -975,7 +975,7 @@ void CdbEngine::doInterruptInferior(SpecialStopMode sm)
     connect(m_signalOperation.data(), &DeviceProcessSignalOperation::finished,
             this, &CdbEngine::handleDoInterruptInferior);
 
-    m_signalOperation->setDebuggerCommand(runParameters().debuggerCommand);
+    m_signalOperation->setDebuggerCommand(runParameters().debugger.executable);
     m_signalOperation->interruptProcess(inferiorPid());
 }
 
diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp
index 607d8613eac..e7117bfc3c9 100644
--- a/src/plugins/debugger/debuggerengine.cpp
+++ b/src/plugins/debugger/debuggerengine.cpp
@@ -104,7 +104,7 @@ QDebug operator<<(QDebug str, const DebuggerRunParameters &sp)
             << " coreFile=" << sp.coreFile
             << " processArgs=" << sp.inferior.commandLineArguments
             << " inferior environment=<" << sp.inferior.environment.size() << " variables>"
-            << " debugger environment=<" << sp.debuggerEnvironment.size() << " variables>"
+            << " debugger environment=<" << sp.debugger.environment.size() << " variables>"
             << " workingDir=" << sp.inferior.workingDirectory
             << " attachPID=" << sp.attachPID
             << " useTerminal=" << sp.useTerminal
diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h
index cf5acf4db1c..a7664bfd507 100644
--- a/src/plugins/debugger/debuggerengine.h
+++ b/src/plugins/debugger/debuggerengine.h
@@ -27,10 +27,12 @@
 
 #include "debugger_global.h"
 #include "debuggerconstants.h"
+#include "debuggeritem.h"
 #include "debuggerprotocol.h"
 #include "debuggerstartparameters.h"
 
 #include <projectexplorer/devicesupport/idevice.h>
+#include <projectexplorer/runnables.h>
 #include <texteditor/textmark.h>
 
 #include <QObject>
@@ -89,7 +91,7 @@ public:
     bool breakOnMain = false;
     bool multiProcess = false; // Whether to set detach-on-fork off.
 
-    QString debuggerCommand;
+    ProjectExplorer::StandardRunnable debugger;
     QString coreFile;
     QString overrideStartScript; // Used in attach to core and remote debugging
     QString startMessage; // First status message shown.
diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp
index 977904e4bf2..9af62e949e4 100644
--- a/src/plugins/debugger/debuggeritem.cpp
+++ b/src/plugins/debugger/debuggeritem.cpp
@@ -29,6 +29,7 @@
 #include "debuggeroptionspage.h"
 #include "debuggerprotocol.h"
 
+#include <coreplugin/coreicons.h>
 #include <projectexplorer/abi.h>
 
 #include <utils/fileutils.h>
@@ -59,6 +60,7 @@ const char DEBUGGER_INFORMATION_AUTODETECTION_SOURCE[] = "AutoDetectionSource";
 const char DEBUGGER_INFORMATION_VERSION[] = "Version";
 const char DEBUGGER_INFORMATION_ABIS[] = "Abis";
 const char DEBUGGER_INFORMATION_LASTMODIFIED[] = "LastModified";
+const char DEBUGGER_INFORMATION_WORKINGDIRECTORY[] = "WorkingDirectory";
 
 namespace Debugger {
 
@@ -81,8 +83,9 @@ DebuggerItem::DebuggerItem(const QVariant &id)
 
 DebuggerItem::DebuggerItem(const QVariantMap &data)
 {
-    m_command = FileName::fromUserInput(data.value(QLatin1String(DEBUGGER_INFORMATION_COMMAND)).toString());
     m_id = data.value(QLatin1String(DEBUGGER_INFORMATION_ID)).toString();
+    m_command = FileName::fromUserInput(data.value(QLatin1String(DEBUGGER_INFORMATION_COMMAND)).toString());
+    m_workingDirectory = FileName::fromUserInput(data.value(DEBUGGER_INFORMATION_WORKINGDIRECTORY).toString());
     m_unexpandedDisplayName = data.value(QLatin1String(DEBUGGER_INFORMATION_DISPLAYNAME)).toString();
     m_isAutoDetected = data.value(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTED), false).toBool();
     m_autoDetectionSource = data.value(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTION_SOURCE)).toString();
@@ -213,9 +216,15 @@ QDateTime DebuggerItem::lastModified() const
     return m_lastModified;
 }
 
-bool DebuggerItem::isGood() const
+QIcon DebuggerItem::decoration() const
 {
-    return m_engineType != NoEngineType;
+    if (m_engineType == NoEngineType)
+        return Core::Icons::ERROR.icon();
+    if (!m_command.toFileInfo().isExecutable())
+        return Core::Icons::WARNING.icon();
+    if (!m_workingDirectory.isEmpty() && !m_workingDirectory.toFileInfo().isDir())
+        return Core::Icons::WARNING.icon();
+    return QIcon();
 }
 
 QString DebuggerItem::validityMessage() const
@@ -230,7 +239,8 @@ bool DebuggerItem::operator==(const DebuggerItem &other) const
     return m_id == other.m_id
             && m_unexpandedDisplayName == other.m_unexpandedDisplayName
             && m_isAutoDetected == other.m_isAutoDetected
-            && m_command == other.m_command;
+            && m_command == other.m_command
+            && m_workingDirectory == other.m_workingDirectory;
 }
 
 QVariantMap DebuggerItem::toMap() const
@@ -239,6 +249,7 @@ QVariantMap DebuggerItem::toMap() const
     data.insert(QLatin1String(DEBUGGER_INFORMATION_DISPLAYNAME), m_unexpandedDisplayName);
     data.insert(QLatin1String(DEBUGGER_INFORMATION_ID), m_id);
     data.insert(QLatin1String(DEBUGGER_INFORMATION_COMMAND), m_command.toString());
+    data.insert(QLatin1String(DEBUGGER_INFORMATION_WORKINGDIRECTORY), m_workingDirectory.toString());
     data.insert(QLatin1String(DEBUGGER_INFORMATION_ENGINETYPE), int(m_engineType));
     data.insert(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTED), m_isAutoDetected);
     data.insert(QLatin1String(DEBUGGER_INFORMATION_AUTODETECTION_SOURCE), m_autoDetectionSource);
diff --git a/src/plugins/debugger/debuggeritem.h b/src/plugins/debugger/debuggeritem.h
index 9ba7220191b..5526c52130a 100644
--- a/src/plugins/debugger/debuggeritem.h
+++ b/src/plugins/debugger/debuggeritem.h
@@ -94,7 +94,7 @@ public:
     QStringList abiNames() const;
     QDateTime lastModified() const;
 
-    bool isGood() const;
+    QIcon decoration() const;
     QString validityMessage() const;
 
     bool operator==(const DebuggerItem &other) const;
@@ -102,6 +102,9 @@ public:
 
     void reinitializeFromFile();
 
+    Utils::FileName workingDirectory() const { return m_workingDirectory; }
+    void setWorkingDirectory(const Utils::FileName &workingPath) { m_workingDirectory = workingPath; }
+
 private:
     DebuggerItem(const QVariant &id);
     void initMacroExpander();
@@ -110,6 +113,7 @@ private:
     QString m_unexpandedDisplayName;
     DebuggerEngineType m_engineType;
     Utils::FileName m_command;
+    Utils::FileName m_workingDirectory;
     bool m_isAutoDetected;
     QString m_autoDetectionSource;
     QString m_version;
diff --git a/src/plugins/debugger/debuggerkitinformation.cpp b/src/plugins/debugger/debuggerkitinformation.cpp
index bf80b93f89c..da20b9170ca 100644
--- a/src/plugins/debugger/debuggerkitinformation.cpp
+++ b/src/plugins/debugger/debuggerkitinformation.cpp
@@ -32,6 +32,7 @@
 #include <projectexplorer/toolchain.h>
 #include <projectexplorer/projectexplorerconstants.h>
 
+#include <utils/environment.h>
 #include <utils/fileutils.h>
 #include <utils/macroexpander.h>
 #include <utils/qtcassert.h>
@@ -244,6 +245,18 @@ const DebuggerItem *DebuggerKitInformation::debugger(const Kit *kit)
     return DebuggerItemManager::findById(id);
 }
 
+StandardRunnable DebuggerKitInformation::runnable(const Kit *kit)
+{
+    StandardRunnable runnable;
+    if (const DebuggerItem *item = debugger(kit)) {
+        runnable.executable = item->command().toString();
+        runnable.workingDirectory = item->workingDirectory().toString();
+        runnable.environment = Utils::Environment::systemEnvironment();
+        runnable.environment.set("LC_NUMERIC", "C");
+    }
+    return runnable;
+}
+
 bool DebuggerKitInformation::isValidDebugger(const Kit *k)
 {
     return debuggerConfigurationErrors(k) == 0;
@@ -326,14 +339,6 @@ KitInformation::ItemList DebuggerKitInformation::toUserOutput(const Kit *k) cons
     return ItemList() << qMakePair(tr("Debugger"), displayString(k));
 }
 
-FileName DebuggerKitInformation::debuggerCommand(const Kit *k)
-{
-    const DebuggerItem *item = debugger(k);
-    if (item)
-        return item->command();
-    return FileName();
-}
-
 DebuggerEngineType DebuggerKitInformation::engineType(const Kit *k)
 {
     const DebuggerItem *item = debugger(k);
diff --git a/src/plugins/debugger/debuggerkitinformation.h b/src/plugins/debugger/debuggerkitinformation.h
index 44af814954a..cae68aaf5ae 100644
--- a/src/plugins/debugger/debuggerkitinformation.h
+++ b/src/plugins/debugger/debuggerkitinformation.h
@@ -29,6 +29,7 @@
 #include "debuggerconstants.h"
 
 #include <projectexplorer/kitinformation.h>
+#include <projectexplorer/runnables.h>
 
 namespace Debugger {
 class DebuggerItem;
@@ -49,6 +50,7 @@ public:
     void fix(ProjectExplorer::Kit *k) override;
 
     static const DebuggerItem *debugger(const ProjectExplorer::Kit *kit);
+    static ProjectExplorer::StandardRunnable runnable(const ProjectExplorer::Kit *kit);
 
     static QList<ProjectExplorer::Task> validateDebugger(const ProjectExplorer::Kit *k);
     static bool isValidDebugger(const ProjectExplorer::Kit *k);
@@ -61,7 +63,6 @@ public:
     static void setDebugger(ProjectExplorer::Kit *k, const QVariant &id);
 
     static Core::Id id();
-    static Utils::FileName debuggerCommand(const ProjectExplorer::Kit *k);
     static DebuggerEngineType engineType(const ProjectExplorer::Kit *k);
     static QString displayString(const ProjectExplorer::Kit *k);
 };
diff --git a/src/plugins/debugger/debuggeroptionspage.cpp b/src/plugins/debugger/debuggeroptionspage.cpp
index 701a84c6711..757a6a44d29 100644
--- a/src/plugins/debugger/debuggeroptionspage.cpp
+++ b/src/plugins/debugger/debuggeroptionspage.cpp
@@ -65,8 +65,6 @@ public:
 
     QVariant data(int column, int role) const
     {
-        static const QIcon errorIcon = Core::Icons::ERROR.icon();
-
         switch (role) {
             case Qt::DisplayRole:
                 switch (column) {
@@ -74,19 +72,23 @@ public:
                 case 1: return m_item.command().toUserOutput();
                 case 2: return m_item.engineTypeName();
                 }
+                break;
+
+            case Qt::FontRole:
+                if (m_changed) {
+                    QFont font;
+                    font.setBold(true);
+                    return font;
+                }
+                break;
+
+            case Qt::DecorationRole:
+                if (column == 0)
+                    return m_item.decoration();
+                break;
 
-            case Qt::FontRole: {
-                QFont font;
-                font.setBold(m_changed);
-                return font;
-            }
-            case Qt::DecorationRole: {
-                if (column == 0 && !m_item.isGood())
-                    return errorIcon;
-            }
-            case Qt::ToolTipRole: {
+            case Qt::ToolTipRole:
                 return m_item.validityMessage();
-            }
         }
         return QVariant();
     }
@@ -214,6 +216,7 @@ private:
     QLabel *m_cdbLabel;
     QLineEdit *m_versionLabel;
     PathChooser *m_binaryChooser;
+    PathChooser *m_workingDirectoryChooser;
     QLineEdit *m_abis;
     bool m_autodetected;
     DebuggerEngineType m_engineType;
@@ -231,7 +234,12 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget(DebuggerItemModel *model)
     m_binaryChooser = new PathChooser(this);
     m_binaryChooser->setExpectedKind(PathChooser::ExistingCommand);
     m_binaryChooser->setMinimumWidth(400);
-    m_binaryChooser->setHistoryCompleter(QLatin1String("DebuggerPaths"));
+    m_binaryChooser->setHistoryCompleter("DebuggerPaths");
+
+    m_workingDirectoryChooser = new PathChooser(this);
+    m_workingDirectoryChooser->setExpectedKind(PathChooser::Directory);
+    m_workingDirectoryChooser->setMinimumWidth(400);
+    m_workingDirectoryChooser->setHistoryCompleter("DebuggerPaths");
 
     m_cdbLabel = new QLabel(this);
     m_cdbLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
@@ -252,9 +260,12 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget(DebuggerItemModel *model)
     formLayout->addRow(new QLabel(tr("Type:")), m_typeLineEdit);
     formLayout->addRow(new QLabel(tr("ABIs:")), m_abis);
     formLayout->addRow(new QLabel(tr("Version:")), m_versionLabel);
+    formLayout->addRow(new QLabel(tr("Working directory:")), m_workingDirectoryChooser);
 
     connect(m_binaryChooser, &PathChooser::pathChanged,
             this, &DebuggerItemConfigWidget::binaryPathHasChanged);
+    connect(m_workingDirectoryChooser, &PathChooser::pathChanged,
+            this, &DebuggerItemConfigWidget::store);
     connect(m_displayNameLineEdit, &QLineEdit::textChanged,
             this, &DebuggerItemConfigWidget::store);
 }
@@ -264,6 +275,7 @@ DebuggerItem DebuggerItemConfigWidget::item() const
     DebuggerItem item(m_id);
     item.setUnexpandedDisplayName(m_displayNameLineEdit->text());
     item.setCommand(m_binaryChooser->fileName());
+    item.setWorkingDirectory(m_workingDirectoryChooser->fileName());
     item.setAutoDetected(m_autodetected);
     QList<ProjectExplorer::Abi> abiList;
     foreach (const QString &a, m_abis->text().split(QRegExp(QLatin1String("[^A-Za-z0-9-_]+")))) {
@@ -305,6 +317,9 @@ void DebuggerItemConfigWidget::load(const DebuggerItem *item)
     m_binaryChooser->setReadOnly(item->isAutoDetected());
     m_binaryChooser->setFileName(item->command());
 
+    m_workingDirectoryChooser->setReadOnly(item->isAutoDetected());
+    m_workingDirectoryChooser->setFileName(item->workingDirectory());
+
     QString text;
     QString versionCommand;
     if (item->engineType() == CdbEngineType) {
diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp
index 73100c637b0..5f118ed6cde 100644
--- a/src/plugins/debugger/debuggerplugin.cpp
+++ b/src/plugins/debugger/debuggerplugin.cpp
@@ -1204,7 +1204,7 @@ bool DebuggerPluginPrivate::parseArgument(QStringList::const_iterator &it,
         }
         rp.inferior.environment = Utils::Environment::systemEnvironment();
         rp.stubEnvironment = Utils::Environment::systemEnvironment();
-        rp.debuggerEnvironment = Utils::Environment::systemEnvironment();
+        rp.debugger.environment = Utils::Environment::systemEnvironment();
 
         if (!kit)
             kit = guessKitFromParameters(rp);
@@ -2912,7 +2912,7 @@ static QString formatStartParameters(DebuggerRunParameters &sp)
             str << "Directory: " << QDir::toNativeSeparators(sp.inferior.workingDirectory)
                 << '\n';
     }
-    QString cmd = sp.debuggerCommand;
+    QString cmd = sp.debugger.executable;
     if (!cmd.isEmpty())
         str << "Debugger: " << QDir::toNativeSeparators(cmd) << '\n';
     if (!sp.coreFile.isEmpty())
diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp
index fe468b9f5b8..a0ead85150e 100644
--- a/src/plugins/debugger/debuggerruncontrol.cpp
+++ b/src/plugins/debugger/debuggerruncontrol.cpp
@@ -354,11 +354,20 @@ static DebuggerRunControl *doCreate(DebuggerRunParameters rp, RunConfiguration *
     if (rp.symbolFile.isEmpty())
         rp.symbolFile = rp.inferior.executable;
 
+    rp.debugger = DebuggerKitInformation::runnable(kit);
+    const QByteArray envBinary = qgetenv("QTC_DEBUGGER_PATH");
+    if (!envBinary.isEmpty())
+        rp.debugger.executable = QString::fromLocal8Bit(envBinary);
+
     if (runConfig) {
         if (auto envAspect = runConfig->extraAspect<EnvironmentAspect>()) {
             rp.inferior.environment = envAspect->environment(); // Correct.
             rp.stubEnvironment = rp.inferior.environment; // FIXME: Wrong, but contains DYLD_IMAGE_SUFFIX
-            rp.debuggerEnvironment = rp.inferior.environment; // FIXME: Wrong, but contains DYLD_IMAGE_SUFFIX
+
+            // Copy over DYLD_IMAGE_SUFFIX etc
+            for (auto var : QStringList({"DYLD_IMAGE_SUFFIX", "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH"}))
+                if (rp.inferior.environment.hasKey(var))
+                    rp.debugger.environment.set(var, rp.inferior.environment.value(var));
         }
         if (Project *project = runConfig->target()->project()) {
             rp.projectSourceDirectory = project->projectDirectory().toString();
@@ -380,7 +389,6 @@ static DebuggerRunControl *doCreate(DebuggerRunParameters rp, RunConfiguration *
 
     rp.cppEngineType = DebuggerKitInformation::engineType(kit);
     rp.sysRoot = SysRootKitInformation::sysRoot(kit).toString();
-    rp.debuggerCommand = DebuggerKitInformation::debuggerCommand(kit).toString();
     rp.device = DeviceKitInformation::device(kit);
 
     if (rp.displayName.isEmpty() && runConfig)
diff --git a/src/plugins/debugger/debuggerstartparameters.h b/src/plugins/debugger/debuggerstartparameters.h
index 7416cf59672..6e02ef43717 100644
--- a/src/plugins/debugger/debuggerstartparameters.h
+++ b/src/plugins/debugger/debuggerstartparameters.h
@@ -72,7 +72,6 @@ public:
 
     ProjectExplorer::StandardRunnable inferior;
     QString displayName; // Used in the Snapshots view.
-    Utils::Environment debuggerEnvironment;
     Utils::Environment stubEnvironment;
     qint64 attachPID = InvalidPid;
     QStringList solibSearchPath;
diff --git a/src/plugins/debugger/gdb/attachgdbadapter.cpp b/src/plugins/debugger/gdb/attachgdbadapter.cpp
index 0d372a10ac7..88ef01a5b6b 100644
--- a/src/plugins/debugger/gdb/attachgdbadapter.cpp
+++ b/src/plugins/debugger/gdb/attachgdbadapter.cpp
@@ -45,10 +45,6 @@ void GdbAttachEngine::setupEngine()
     QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
     showMessage("TRYING TO START ADAPTER");
 
-    if (!runParameters().inferior.workingDirectory.isEmpty())
-        m_gdbProc.setWorkingDirectory(runParameters().inferior.workingDirectory);
-    m_gdbProc.setEnvironment(runParameters().debuggerEnvironment);
-
     startGdb();
 }
 
diff --git a/src/plugins/debugger/gdb/coregdbadapter.cpp b/src/plugins/debugger/gdb/coregdbadapter.cpp
index 4534c295a55..689e62ee3d8 100644
--- a/src/plugins/debugger/gdb/coregdbadapter.cpp
+++ b/src/plugins/debugger/gdb/coregdbadapter.cpp
@@ -39,6 +39,7 @@
 #include <QTemporaryFile>
 
 using namespace Utils;
+using namespace ProjectExplorer;
 
 namespace Debugger {
 namespace Internal {
@@ -121,7 +122,7 @@ static QString findExecutableFromName(const QString &fileNameFromCore, const QSt
 }
 
 GdbCoreEngine::CoreInfo
-GdbCoreEngine::readExecutableNameFromCore(const QString &debuggerCommand, const QString &coreFile)
+GdbCoreEngine::readExecutableNameFromCore(const StandardRunnable &debugger, const QString &coreFile)
 {
     CoreInfo cinfo;
 #if 0
@@ -139,7 +140,7 @@ GdbCoreEngine::readExecutableNameFromCore(const QString &debuggerCommand, const
     QStringList envLang = QProcess::systemEnvironment();
     Utils::Environment::setupEnglishOutput(&envLang);
     proc.setEnvironment(envLang);
-    SynchronousProcessResponse response = proc.runBlocking(debuggerCommand, args);
+    SynchronousProcessResponse response = proc.runBlocking(debugger.executable, args);
 
     if (response.result == SynchronousProcessResponse::Finished) {
         QString output = response.stdOut();
@@ -171,9 +172,8 @@ void GdbCoreEngine::continueSetupEngine()
             m_tempCoreFile.close();
     }
     if (isCore && m_executable.isEmpty()) {
-        GdbCoreEngine::CoreInfo cinfo = readExecutableNameFromCore(
-                                            runParameters().debuggerCommand,
-                                            coreFileName());
+        GdbCoreEngine::CoreInfo cinfo =
+                readExecutableNameFromCore(runParameters().debugger, coreFileName());
 
         if (cinfo.isCore) {
             m_executable = cinfo.foundExecutableName;
diff --git a/src/plugins/debugger/gdb/coregdbadapter.h b/src/plugins/debugger/gdb/coregdbadapter.h
index cdc0987b63b..0067200e5b0 100644
--- a/src/plugins/debugger/gdb/coregdbadapter.h
+++ b/src/plugins/debugger/gdb/coregdbadapter.h
@@ -46,7 +46,8 @@ public:
         QString foundExecutableName; // empty if no corresponding exec could be found
         bool isCore = false;
     };
-    static CoreInfo readExecutableNameFromCore(const QString &debuggerCmd, const QString &coreFile);
+    static CoreInfo readExecutableNameFromCore(const ProjectExplorer::StandardRunnable &debugger,
+                                               const QString &coreFile);
 
 private:
     void setupEngine() override;
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 31c4eb82c60..1cb7158a41e 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -267,14 +267,19 @@ DebuggerStartMode GdbEngine::startMode() const
     return runParameters().startMode;
 }
 
+QString GdbEngine::failedToStartMessage()
+{
+    return tr("The gdb process failed to start.");
+}
+
 QString GdbEngine::errorMessage(QProcess::ProcessError error)
 {
     switch (error) {
         case QProcess::FailedToStart:
-            return tr("The gdb process failed to start. Either the "
+            return failedToStartMessage() + ' ' + tr("Either the "
                 "invoked program \"%1\" is missing, or you may have insufficient "
                 "permissions to invoke the program.\n%2")
-                .arg(m_gdb, m_gdbProc.errorString());
+                .arg(runParameters().debugger.executable, m_gdbProc.errorString());
         case QProcess::Crashed:
             if (targetState() == DebuggerFinished)
                 return tr("The gdb process crashed some time after starting "
@@ -860,7 +865,7 @@ void GdbEngine::interruptInferior()
             connect(m_signalOperation.data(), &DeviceProcessSignalOperation::finished,
                     this, &GdbEngine::handleInterruptDeviceInferior);
 
-            m_signalOperation->setDebuggerCommand(runParameters().debuggerCommand);
+            m_signalOperation->setDebuggerCommand(runParameters().debugger.executable);
             m_signalOperation->interruptProcess(inferiorPid());
         } else {
             interruptInferior2();
@@ -3945,18 +3950,6 @@ bool GdbEngine::handleCliDisassemblerResult(const QString &output, DisassemblerA
     return false;
 }
 
-// Binary/configuration check logic.
-
-static QString gdbBinary(const DebuggerRunParameters &sp)
-{
-    // 1) Environment.
-    const QByteArray envBinary = qgetenv("QTC_DEBUGGER_PATH");
-    if (!envBinary.isEmpty())
-        return QString::fromLocal8Bit(envBinary);
-    // 2) Command from profile.
-    return sp.debuggerCommand;
-}
-
 static SourcePathMap mergeStartParametersSourcePathMap(const DebuggerRunParameters &sp,
                                                        const SourcePathMap &in)
 {
@@ -3982,14 +3975,14 @@ void GdbEngine::startGdb(const QStringList &args)
     m_gdbProc.disconnect(); // From any previous runs
 
     const DebuggerRunParameters &rp = runParameters();
-    m_gdb = gdbBinary(rp);
-    if (m_gdb.isEmpty()) {
+    if (rp.debugger.executable.isEmpty()) {
         handleGdbStartFailed();
         handleAdapterStartFailed(
             msgNoGdbBinaryForToolChain(rp.toolChainAbi),
             Constants::DEBUGGER_COMMON_SETTINGS_ID);
         return;
     }
+
     QStringList gdbArgs;
     gdbArgs << "-i";
     gdbArgs << "mi";
@@ -4004,16 +3997,21 @@ void GdbEngine::startGdb(const QStringList &args)
     connect(&m_gdbProc, &QtcProcess::readyReadStandardOutput, this, &GdbEngine::readGdbStandardOutput);
     connect(&m_gdbProc, &QtcProcess::readyReadStandardError, this, &GdbEngine::readGdbStandardError);
 
-    showMessage("STARTING " + m_gdb + " " + gdbArgs.join(' '));
-    m_gdbProc.setCommand(m_gdb, QtcProcess::joinArgs(gdbArgs));
-    Environment env = Environment(m_gdbProc.systemEnvironment());
-    env.set("LC_NUMERIC", "C");
-    m_gdbProc.setEnvironment(env);
+    showMessage("STARTING " + rp.debugger.executable + " " + gdbArgs.join(' '));
+    m_gdbProc.setCommand(rp.debugger.executable, QtcProcess::joinArgs(gdbArgs));
+    if (QFileInfo(rp.debugger.workingDirectory).isDir())
+        m_gdbProc.setWorkingDirectory(rp.debugger.workingDirectory);
+    m_gdbProc.setEnvironment(rp.debugger.environment);
     m_gdbProc.start();
 
     if (!m_gdbProc.waitForStarted()) {
         handleGdbStartFailed();
-        const QString msg = errorMessage(QProcess::FailedToStart);
+        QString msg;
+        QString wd = m_gdbProc.workingDirectory();
+        if (!QFileInfo(wd).isDir())
+            msg = failedToStartMessage() + ' ' + tr("The working directory \"%s\" is not usable.").arg(wd);
+        else
+            msg = errorMessage(QProcess::FailedToStart);
         handleAdapterStartFailed(msg);
         return;
     }
@@ -4136,7 +4134,7 @@ void GdbEngine::startGdb(const QStringList &args)
     if (terminal()->isUsable())
         runCommand({"set inferior-tty " + QString::fromUtf8(terminal()->slaveDevice()), NoFlags});
 
-    const QFileInfo gdbBinaryFile(m_gdb);
+    const QFileInfo gdbBinaryFile(rp.debugger.executable);
     const QString uninstalledData = gdbBinaryFile.absolutePath() + "/data-directory/python";
 
     runCommand({"python sys.path.insert(1, '" + dumperSourcePath + "')", NoFlags});
diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h
index 6fdfd10b1af..5f8bcce0631 100644
--- a/src/plugins/debugger/gdb/gdbengine.h
+++ b/src/plugins/debugger/gdb/gdbengine.h
@@ -31,6 +31,7 @@
 #include <debugger/registerhandler.h>
 #include <debugger/watchhandler.h>
 #include <debugger/watchutils.h>
+#include <debugger/debuggeritem.h>
 #include <debugger/debuggertooltipmanager.h>
 
 #include <coreplugin/id.h>
@@ -397,13 +398,12 @@ protected:
     //
     void reloadDebuggingHelpers() override;
 
-    QString m_gdb;
-
     //
     // Convenience Functions
     //
     QString errorMessage(QProcess::ProcessError error);
     void showExecutionError(const QString &message);
+    QString failedToStartMessage();
 
     static QString tooltipIName(const QString &exp);
 
diff --git a/src/plugins/debugger/gdb/gdbplainengine.cpp b/src/plugins/debugger/gdb/gdbplainengine.cpp
index 1c827bdb57d..7df122d8208 100644
--- a/src/plugins/debugger/gdb/gdbplainengine.cpp
+++ b/src/plugins/debugger/gdb/gdbplainengine.cpp
@@ -121,10 +121,6 @@ void GdbPlainEngine::setupEngine()
     }
     gdbArgs.append("--tty=" + m_outputCollector.serverName());
 
-    QString workingDirectory = runParameters().inferior.workingDirectory;
-    if (!workingDirectory.isEmpty() && QFileInfo::exists(workingDirectory))
-        m_gdbProc.setWorkingDirectory(workingDirectory);
-
     startGdb(gdbArgs);
 }
 
diff --git a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp
index c279a0a9f43..0d40d703a14 100644
--- a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp
+++ b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp
@@ -86,8 +86,6 @@ void GdbRemoteServerEngine::setupEngine()
         m_uploadProc.start(arglist);
         m_uploadProc.waitForStarted();
     }
-    if (!runParameters().inferior.workingDirectory.isEmpty())
-        m_gdbProc.setWorkingDirectory(runParameters().inferior.workingDirectory);
 
     if (runParameters().remoteSetupNeeded) {
         notifyEngineRequestRemoteSetup();
diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp
index 19761ba6dee..b5b37351e00 100644
--- a/src/plugins/debugger/lldb/lldbengine.cpp
+++ b/src/plugins/debugger/lldb/lldbengine.cpp
@@ -247,7 +247,7 @@ void LldbEngine::setupEngine()
 
 void LldbEngine::startLldb()
 {
-    m_lldbCmd = runParameters().debuggerCommand;
+    QString lldbCmd = runParameters().debugger.executable;
     connect(&m_lldbProc, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error),
             this, &LldbEngine::handleLldbError);
     connect(&m_lldbProc, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
@@ -260,17 +260,17 @@ void LldbEngine::startLldb()
     connect(this, &LldbEngine::outputReady,
             this, &LldbEngine::handleResponse, Qt::QueuedConnection);
 
-    showMessage("STARTING LLDB: " + m_lldbCmd);
-    m_lldbProc.setEnvironment(runParameters().debuggerEnvironment);
-    if (!runParameters().inferior.workingDirectory.isEmpty())
-        m_lldbProc.setWorkingDirectory(runParameters().inferior.workingDirectory);
+    showMessage("STARTING LLDB: " + lldbCmd);
+    m_lldbProc.setEnvironment(runParameters().debugger.environment);
+    if (QFileInfo(runParameters().debugger.workingDirectory).isDir())
+        m_lldbProc.setWorkingDirectory(runParameters().debugger.workingDirectory);
 
-    m_lldbProc.setCommand(m_lldbCmd, QString());
+    m_lldbProc.setCommand(lldbCmd, QString());
     m_lldbProc.start();
 
     if (!m_lldbProc.waitForStarted()) {
         const QString msg = tr("Unable to start LLDB \"%1\": %2")
-            .arg(m_lldbCmd, m_lldbProc.errorString());
+            .arg(lldbCmd, m_lldbProc.errorString());
         notifyEngineSetupFailed();
         showMessage("ADAPTER START FAILED");
         if (!msg.isEmpty())
@@ -852,7 +852,7 @@ QString LldbEngine::errorMessage(QProcess::ProcessError error) const
             return tr("The LLDB process failed to start. Either the "
                 "invoked program \"%1\" is missing, or you may have insufficient "
                 "permissions to invoke the program.")
-                .arg(m_lldbCmd);
+                .arg(runParameters().debugger.executable);
         case QProcess::Crashed:
             return tr("The LLDB process crashed some time after starting "
                 "successfully.");
diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h
index 8129cc4042f..c3aca9df412 100644
--- a/src/plugins/debugger/lldb/lldbengine.h
+++ b/src/plugins/debugger/lldb/lldbengine.h
@@ -150,7 +150,6 @@ private:
     QString m_inbuffer;
     QString m_scriptFileName;
     Utils::QtcProcess m_lldbProc;
-    QString m_lldbCmd;
 
     // FIXME: Make generic.
     int m_lastAgentId;
diff --git a/src/plugins/debugger/loadcoredialog.cpp b/src/plugins/debugger/loadcoredialog.cpp
index 31d4a6064a5..4241a508899 100644
--- a/src/plugins/debugger/loadcoredialog.cpp
+++ b/src/plugins/debugger/loadcoredialog.cpp
@@ -363,9 +363,8 @@ void AttachCoreDialog::coreFileChanged(const QString &core)
     if (!HostOsInfo::isWindowsHost() && QFile::exists(core)) {
         Kit *k = d->kitChooser->currentKit();
         QTC_ASSERT(k, return);
-        FileName cmd = DebuggerKitInformation::debuggerCommand(k);
-        GdbCoreEngine::CoreInfo cinfo =
-            GdbCoreEngine::readExecutableNameFromCore(cmd.toString(), core);
+        StandardRunnable debugger = DebuggerKitInformation::runnable(k);
+        GdbCoreEngine::CoreInfo cinfo = GdbCoreEngine::readExecutableNameFromCore(debugger, core);
         if (!cinfo.foundExecutableName.isEmpty())
             d->localExecFileName->setFileName(FileName::fromString(cinfo.foundExecutableName));
         else if (!d->localExecFileName->isValid() && !cinfo.rawStringFromCore.isEmpty())
diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp
index 9aa864e5886..e0969d0221b 100644
--- a/src/plugins/debugger/pdb/pdbengine.cpp
+++ b/src/plugins/debugger/pdb/pdbengine.cpp
@@ -136,7 +136,7 @@ void PdbEngine::setupEngine()
     QStringList args = { bridge, scriptFile.fileName() };
     args.append(Utils::QtcProcess::splitArgs(runParameters().inferior.workingDirectory));
     showMessage("STARTING " + m_interpreter + QLatin1Char(' ') + args.join(QLatin1Char(' ')));
-    m_proc.setEnvironment(runParameters().debuggerEnvironment.toStringList());
+    m_proc.setEnvironment(runParameters().debugger.environment.toStringList());
     m_proc.start(m_interpreter, args);
 
     if (!m_proc.waitForStarted()) {
-- 
GitLab