diff options
-rw-r--r-- | src/plugins/debugger/debugger.qbs | 5 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/attachgdbadapter.cpp | 125 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/attachgdbadapter.h | 57 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/coregdbadapter.cpp | 321 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/coregdbadapter.h | 78 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdb.pri | 10 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbengine.cpp | 953 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbengine.h | 246 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbplainengine.cpp | 145 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/gdbplainengine.h | 57 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/remotegdbserveradapter.cpp | 375 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/remotegdbserveradapter.h | 62 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/termgdbadapter.cpp | 205 | ||||
-rw-r--r-- | src/plugins/debugger/gdb/termgdbadapter.h | 67 | ||||
-rw-r--r-- | src/plugins/debugger/loadcoredialog.cpp | 4 |
15 files changed, 1033 insertions, 1677 deletions
diff --git a/src/plugins/debugger/debugger.qbs b/src/plugins/debugger/debugger.qbs index 758e3a0b93..6bc9384e26 100644 --- a/src/plugins/debugger/debugger.qbs +++ b/src/plugins/debugger/debugger.qbs @@ -109,14 +109,9 @@ Project { name: "gdb" prefix: "gdb/" files: [ - "attachgdbadapter.cpp", "attachgdbadapter.h", - "coregdbadapter.cpp", "coregdbadapter.h", "gdbengine.cpp", "gdbengine.h", "gdboptionspage.cpp", - "gdbplainengine.cpp", "gdbplainengine.h", - "remotegdbserveradapter.cpp", "remotegdbserveradapter.h", "startgdbserverdialog.cpp", "startgdbserverdialog.h", - "termgdbadapter.cpp", "termgdbadapter.h" ] } diff --git a/src/plugins/debugger/gdb/attachgdbadapter.cpp b/src/plugins/debugger/gdb/attachgdbadapter.cpp deleted file mode 100644 index 519647d703..0000000000 --- a/src/plugins/debugger/gdb/attachgdbadapter.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "attachgdbadapter.h" - -#include <coreplugin/messagebox.h> - -#include <debugger/debuggerprotocol.h> -#include <debugger/debuggerstartparameters.h> - -#include <utils/qtcassert.h> -#include <utils/qtcfallthrough.h> - -namespace Debugger { -namespace Internal { - -GdbAttachEngine::GdbAttachEngine(bool useTerminal) - : GdbEngine(useTerminal) -{ -} - -void GdbAttachEngine::setupEngine() -{ - QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); - showMessage("TRYING TO START ADAPTER"); - - startGdb(); -} - -void GdbAttachEngine::setupInferior() -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - // Task 254674 does not want to remove them - //qq->breakHandler()->removeAllBreakpoints(); - handleInferiorPrepared(); -} - -void GdbAttachEngine::runEngine() -{ - QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); - const qint64 pid = runParameters().attachPID.pid(); - showStatusMessage(tr("Attaching to process %1.").arg(pid)); - runCommand({"attach " + QString::number(pid), - [this](const DebuggerResponse &r) { handleAttach(r); }}); - // In some cases we get only output like - // "Could not attach to process. If your uid matches the uid of the target\n" - // "process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try\n" - // " again as the root user. For more details, see /etc/sysctl.d/10-ptrace.conf\n" - // " ptrace: Operation not permitted.\n" - // but no(!) ^ response. Use a second command to force *some* output - runCommand({"print 24"}); -} - -void GdbAttachEngine::handleAttach(const DebuggerResponse &response) -{ - QTC_ASSERT(state() == EngineRunRequested || state() == InferiorStopOk, - qDebug() << state()); - switch (response.resultClass) { - case ResultDone: - case ResultRunning: - showMessage("INFERIOR ATTACHED"); - if (state() == EngineRunRequested) { - // Happens e.g. for "Attach to unstarted application" - // We will get a '*stopped' later that we'll interpret as 'spontaneous' - // So acknowledge the current state and put a delayed 'continue' in the pipe. - showMessage(tr("Attached to running application"), StatusBar); - notifyEngineRunAndInferiorRunOk(); - } else { - // InferiorStopOk, e.g. for "Attach to running application". - // The *stopped came in between sending the 'attach' and - // receiving its '^done'. - if (runParameters().continueAfterAttach) - continueInferiorInternal(); - } - break; - case ResultError: - if (response.data["msg"].data() == "ptrace: Operation not permitted.") { - QString msg = msgPtraceError(runParameters().startMode); - showStatusMessage(tr("Failed to attach to application: %1").arg(msg)); - Core::AsynchronousMessageBox::warning(tr("Debugger Error"), msg); - notifyEngineIll(); - break; - } - Q_FALLTHROUGH(); // if msg != "ptrace: ..." - default: - showStatusMessage(tr("Failed to attach to application: %1") - .arg(QString(response.data["msg"].data()))); - notifyEngineIll(); - } -} - -void GdbAttachEngine::interruptInferior2() -{ - interruptLocalInferior(runParameters().attachPID.pid()); -} - -void GdbAttachEngine::shutdownEngine() -{ - notifyAdapterShutdownOk(); -} - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/gdb/attachgdbadapter.h b/src/plugins/debugger/gdb/attachgdbadapter.h deleted file mode 100644 index cabf468806..0000000000 --- a/src/plugins/debugger/gdb/attachgdbadapter.h +++ /dev/null @@ -1,57 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "gdbengine.h" - -namespace Debugger { -namespace Internal { - -/////////////////////////////////////////////////////////////////////// -// -// AttachGdbAdapter -// -/////////////////////////////////////////////////////////////////////// - -class GdbAttachEngine : public GdbEngine -{ - Q_OBJECT - -public: - explicit GdbAttachEngine(bool useTerminal); - -private: - void setupEngine() override; - void setupInferior() override; - void runEngine() override; - void interruptInferior2() override; - void shutdownEngine() override; - - void handleAttach(const DebuggerResponse &response); -}; - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/gdb/coregdbadapter.cpp b/src/plugins/debugger/gdb/coregdbadapter.cpp deleted file mode 100644 index df6d040ccd..0000000000 --- a/src/plugins/debugger/gdb/coregdbadapter.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "coregdbadapter.h" - -#include <coreplugin/messagebox.h> - -#include <debugger/debuggercore.h> -#include <debugger/debuggerprotocol.h> -#include <debugger/debuggerstartparameters.h> - -#include <utils/fileutils.h> -#include <utils/qtcassert.h> -#include <utils/synchronousprocess.h> -#include <utils/temporarydirectory.h> -#include <utils/temporaryfile.h> - -#include <QDir> - -using namespace Utils; -using namespace ProjectExplorer; - -namespace Debugger { -namespace Internal { - -#define CB(callback) [this](const DebuggerResponse &r) { callback(r); } -#define CHECK_STATE(s) do { checkState(s, __FILE__, __LINE__); } while (0) - -/////////////////////////////////////////////////////////////////////// -// -// CoreGdbAdapter -// -/////////////////////////////////////////////////////////////////////// - -GdbCoreEngine::GdbCoreEngine(bool useTerminal) - : GdbEngine(useTerminal) -{} - -GdbCoreEngine::~GdbCoreEngine() -{ - if (m_coreUnpackProcess) { - m_coreUnpackProcess->blockSignals(true); - m_coreUnpackProcess->terminate(); - m_coreUnpackProcess->deleteLater(); - m_coreUnpackProcess = 0; - if (m_tempCoreFile.isOpen()) - m_tempCoreFile.close(); - } - if (!m_tempCoreName.isEmpty()) { - QFile tmpFile(m_tempCoreName); - tmpFile.remove(); - } -} - -void GdbCoreEngine::setupEngine() -{ - QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); - showMessage("TRYING TO START ADAPTER"); - - const DebuggerRunParameters &rp = runParameters(); - m_executable = rp.inferior.executable; - QFileInfo fi(rp.coreFile); - m_coreName = fi.absoluteFilePath(); - - unpackCoreIfNeeded(); -} - -static QString findExecutableFromName(const QString &fileNameFromCore, const QString &coreFile) -{ - if (fileNameFromCore.isEmpty()) - return fileNameFromCore; - QFileInfo fi(fileNameFromCore); - if (fi.isFile()) - return fileNameFromCore; - - // turn the filename into an absolute path, using the location of the core as a hint - QString absPath; - if (fi.isAbsolute()) { - absPath = fileNameFromCore; - } else { - QFileInfo coreInfo(coreFile); - QDir coreDir = coreInfo.dir(); - absPath = FileUtils::resolvePath(coreDir.absolutePath(), fileNameFromCore); - } - if (QFileInfo(absPath).isFile() || absPath.isEmpty()) - return absPath; - - // remove possible trailing arguments - QLatin1Char sep(' '); - QStringList pathFragments = absPath.split(sep); - while (pathFragments.size() > 0) { - QString joined_path = pathFragments.join(sep); - if (QFileInfo(joined_path).isFile()) { - return joined_path; - } - pathFragments.pop_back(); - } - - return QString(); -} - -GdbCoreEngine::CoreInfo -GdbCoreEngine::readExecutableNameFromCore(const StandardRunnable &debugger, const QString &coreFile) -{ - CoreInfo cinfo; -#if 0 - ElfReader reader(coreFile); - cinfo.rawStringFromCore = QString::fromLocal8Bit(reader.readCoreName(&cinfo.isCore)); - cinfo.foundExecutableName = findExecutableFromName(cinfo.rawStringFromCore, coreFile); -#else - QStringList args; - args.append(QLatin1String("-nx")); - args.append(QLatin1String("-batch")); - args.append(QLatin1String("-c")); - args.append(coreFile); - - SynchronousProcess proc; - QStringList envLang = QProcess::systemEnvironment(); - Utils::Environment::setupEnglishOutput(&envLang); - proc.setEnvironment(envLang); - SynchronousProcessResponse response = proc.runBlocking(debugger.executable, args); - - if (response.result == SynchronousProcessResponse::Finished) { - QString output = response.stdOut(); - // Core was generated by `/data/dev/creator-2.6/bin/qtcreator'. - // Program terminated with signal 11, Segmentation fault. - int pos1 = output.indexOf("Core was generated by"); - if (pos1 != -1) { - pos1 += 23; - int pos2 = output.indexOf('\'', pos1); - if (pos2 != -1) { - cinfo.isCore = true; - cinfo.rawStringFromCore = output.mid(pos1, pos2 - pos1); - cinfo.foundExecutableName = findExecutableFromName(cinfo.rawStringFromCore, coreFile); - } - } - } -#endif - return cinfo; -} - -void GdbCoreEngine::continueSetupEngine() -{ - bool isCore = true; - if (m_coreUnpackProcess) { - isCore = m_coreUnpackProcess->exitCode() == 0; - m_coreUnpackProcess->deleteLater(); - m_coreUnpackProcess = 0; - if (m_tempCoreFile.isOpen()) - m_tempCoreFile.close(); - } - if (isCore && m_executable.isEmpty()) { - GdbCoreEngine::CoreInfo cinfo = - readExecutableNameFromCore(runParameters().debugger, coreFileName()); - - if (cinfo.isCore) { - m_executable = cinfo.foundExecutableName; - if (m_executable.isEmpty()) { - Core::AsynchronousMessageBox::warning( - tr("Error Loading Symbols"), - tr("No executable to load symbols from specified core.")); - notifyEngineSetupFailed(); - return; - } - } - } - if (isCore) { - startGdb(); - } else { - Core::AsynchronousMessageBox::warning( - tr("Error Loading Core File"), - tr("The specified file does not appear to be a core file.")); - notifyEngineSetupFailed(); - } -} - -void GdbCoreEngine::writeCoreChunk() -{ - m_tempCoreFile.write(m_coreUnpackProcess->readAll()); -} - -void GdbCoreEngine::setupInferior() -{ - CHECK_STATE(InferiorSetupRequested); - setLinuxOsAbi(); - // Do that first, otherwise no symbols are loaded. - QFileInfo fi(m_executable); - QString path = fi.absoluteFilePath(); - runCommand({"-file-exec-and-symbols \"" + path + '"', - CB(handleFileExecAndSymbols)}); -} - -void GdbCoreEngine::handleFileExecAndSymbols(const DebuggerResponse &response) -{ - CHECK_STATE(InferiorSetupRequested); - QString core = coreFileName(); - if (response.resultClass == ResultDone) { - showMessage(tr("Symbols found."), StatusBar); - handleInferiorPrepared(); - } else { - QString msg = tr("No symbols found in core file <i>%1</i>.").arg(core) - + ' ' + tr("This can be caused by a path length limitation " - "in the core file.") - + ' ' + tr("Try to specify the binary using the " - "<i>Debug->Start Debugging->Attach to Core</i> dialog."); - notifyInferiorSetupFailed(msg); - } -} - -void GdbCoreEngine::runEngine() -{ - CHECK_STATE(EngineRunRequested); - runCommand({"target core " + coreFileName(), CB(handleTargetCore)}); -} - -void GdbCoreEngine::handleTargetCore(const DebuggerResponse &response) -{ - CHECK_STATE(EngineRunRequested); - notifyEngineRunOkAndInferiorUnrunnable(); - showMessage(tr("Attached to core."), StatusBar); - if (response.resultClass == ResultError) { - // We'll accept any kind of error e.g. &"Cannot access memory at address 0x2abc2a24\n" - // Even without the stack, the user can find interesting stuff by exploring - // the memory, globals etc. - showStatusMessage(tr("Attach to core \"%1\" failed:").arg(runParameters().coreFile) - + '\n' + response.data["msg"].data() - + '\n' + tr("Continuing nevertheless.")); - } - // Due to the auto-solib-add off setting, we don't have any - // symbols yet. Load them in order of importance. - reloadStack(); - reloadModulesInternal(); - runCommand({"p 5", CB(handleRoundTrip)}); -} - -void GdbCoreEngine::handleRoundTrip(const DebuggerResponse &response) -{ - CHECK_STATE(InferiorUnrunnable); - Q_UNUSED(response); - loadSymbolsForStack(); - handleStop3(); - QTimer::singleShot(1000, this, &GdbEngine::loadAllSymbols); -} - -void GdbCoreEngine::interruptInferior() -{ - // A core never runs, so this cannot be called. - QTC_CHECK(false); -} - -void GdbCoreEngine::shutdownEngine() -{ - notifyAdapterShutdownOk(); -} - -static QString tempCoreFilename() -{ - Utils::TemporaryFile tmp("tmpcore-XXXXXX"); - tmp.open(); - return tmp.fileName(); -} - -void GdbCoreEngine::unpackCoreIfNeeded() -{ - QStringList arguments; - const QString msg = "Unpacking core file to %1"; - if (m_coreName.endsWith(QLatin1String(".lzo"))) { - m_tempCoreName = tempCoreFilename(); - showMessage(msg.arg(m_tempCoreName)); - arguments << QLatin1String("-o") << m_tempCoreName << QLatin1String("-x") << m_coreName; - m_coreUnpackProcess = new QProcess(this); - m_coreUnpackProcess->setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryPath()); - m_coreUnpackProcess->start(QLatin1String("lzop"), arguments); - connect(m_coreUnpackProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished), - this, &GdbCoreEngine::continueSetupEngine); - } else if (m_coreName.endsWith(QLatin1String(".gz"))) { - m_tempCoreName = tempCoreFilename(); - showMessage(msg.arg(m_tempCoreName)); - m_tempCoreFile.setFileName(m_tempCoreName); - m_tempCoreFile.open(QFile::WriteOnly); - arguments << QLatin1String("-c") << QLatin1String("-d") << m_coreName; - m_coreUnpackProcess = new QProcess(this); - m_coreUnpackProcess->setWorkingDirectory(Utils::TemporaryDirectory::masterDirectoryPath()); - m_coreUnpackProcess->start(QLatin1String("gzip"), arguments); - connect(m_coreUnpackProcess, &QProcess::readyRead, this, &GdbCoreEngine::writeCoreChunk); - connect(m_coreUnpackProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished), - this, &GdbCoreEngine::continueSetupEngine); - } else { - continueSetupEngine(); - } -} - -QString GdbCoreEngine::coreFileName() const -{ - return m_tempCoreName.isEmpty() ? m_coreName : m_tempCoreName; -} - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/gdb/coregdbadapter.h b/src/plugins/debugger/gdb/coregdbadapter.h deleted file mode 100644 index b4b7081455..0000000000 --- a/src/plugins/debugger/gdb/coregdbadapter.h +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "gdbengine.h" - -#include <QFile> - -namespace Debugger { -namespace Internal { - -class GdbCoreEngine : public GdbEngine -{ - Q_OBJECT - -public: - explicit GdbCoreEngine(bool useTerminal); - ~GdbCoreEngine() override; - - struct CoreInfo - { - QString rawStringFromCore; - QString foundExecutableName; // empty if no corresponding exec could be found - bool isCore = false; - }; - static CoreInfo readExecutableNameFromCore(const ProjectExplorer::StandardRunnable &debugger, - const QString &coreFile); - -private: - void setupEngine() override; - void setupInferior() override; - void runEngine() override; - void interruptInferior() override; - void shutdownEngine() override; - - void handleFileExecAndSymbols(const DebuggerResponse &response); - void handleTargetCore(const DebuggerResponse &response); - void handleRoundTrip(const DebuggerResponse &response); - void unpackCoreIfNeeded(); - QString coreFileName() const; - QString coreName() const; - - void continueSetupEngine(); - void writeCoreChunk(); - -private: - QString m_executable; - QString m_coreName; - QString m_tempCoreName; - QProcess *m_coreUnpackProcess = nullptr; - QFile m_tempCoreFile; -}; - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/gdb/gdb.pri b/src/plugins/debugger/gdb/gdb.pri index 3bed54bcc4..4e0883fa03 100644 --- a/src/plugins/debugger/gdb/gdb.pri +++ b/src/plugins/debugger/gdb/gdb.pri @@ -1,18 +1,8 @@ HEADERS += \ $$PWD/gdbengine.h \ - $$PWD/attachgdbadapter.h \ - $$PWD/coregdbadapter.h \ - $$PWD/termgdbadapter.h \ - $$PWD/remotegdbserveradapter.h \ - $$PWD/gdbplainengine.h \ $$PWD/startgdbserverdialog.h SOURCES += \ $$PWD/gdbengine.cpp \ $$PWD/gdboptionspage.cpp \ - $$PWD/attachgdbadapter.cpp \ - $$PWD/coregdbadapter.cpp \ - $$PWD/termgdbadapter.cpp \ - $$PWD/remotegdbserveradapter.cpp \ - $$PWD/gdbplainengine.cpp \ $$PWD/startgdbserverdialog.cpp diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 8a10f42ed8..e3c2cc3cca 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -25,12 +25,6 @@ #include "gdbengine.h" -#include "attachgdbadapter.h" -#include "coregdbadapter.h" -#include "gdbplainengine.h" -#include "termgdbadapter.h" -#include "remotegdbserveradapter.h" - #include <debugger/debuggerstartparameters.h> #include <debugger/debuggerinternalconstants.h> #include <debugger/debuggerruncontrol.h> @@ -73,9 +67,10 @@ #include <utils/qtcassert.h> #include <utils/qtcprocess.h> #include <utils/savedaction.h> +#include <utils/synchronousprocess.h> +#include <utils/temporarydirectory.h> #include <utils/temporaryfile.h> -#include <QBuffer> #include <QDirIterator> #include <QMessageBox> #include <QProcess> @@ -97,11 +92,6 @@ enum { debugPending = 0 }; #define CHECK_STATE(s) do { checkState(s, __FILE__, __LINE__); } while (0) -QString GdbEngine::tooltipIName(const QString &exp) -{ - return "tooltip." + toHex(exp); -} - static bool stateAcceptsGdbCommands(DebuggerState state) { switch (state) { @@ -205,27 +195,13 @@ private: // /////////////////////////////////////////////////////////////////////// -GdbEngine::GdbEngine(bool useTerminal) +GdbEngine::GdbEngine(bool useTerminal, DebuggerStartMode startMode) + : m_startMode(startMode), m_useTerminal(useTerminal), m_terminalTrap(useTerminal) { setObjectName("GdbEngine"); - m_busy = false; - m_gdbVersion = 100; - m_pythonVersion = 0; - m_isQnxGdb = false; - m_registerNamesListed = false; - m_sourcesListUpdating = false; - m_oldestAcceptableToken = -1; - m_nonDiscardableCount = 0; m_gdbOutputCodec = QTextCodec::codecForLocale(); m_inferiorOutputCodec = QTextCodec::codecForLocale(); - m_pendingBreakpointRequests = 0; - m_commandsDoneCallback = 0; - m_stackNeeded = false; - m_terminalTrap = useTerminal; - m_systemDumpersLoaded = false; - m_rerunPending = false; - m_inUpdateLocals = false; m_debugInfoTaskHandler = new DebugInfoTaskHandler(this); //ExtensionSystem::PluginManager::addObject(m_debugInfoTaskHandler); @@ -242,10 +218,45 @@ GdbEngine::GdbEngine(bool useTerminal) this, &GdbEngine::reloadLocals); connect(action(UseDynamicType), &SavedAction::valueChanged, this, &GdbEngine::reloadLocals); + + // Output + connect(&m_outputCollector, &OutputCollector::byteDelivery, + this, &GdbEngine::readDebuggeeOutput); + + if (isTermEngine()) { + if (HostOsInfo::isWindowsHost()) { + // Windows up to xp needs a workaround for attaching to freshly started processes. see proc_stub_win + if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA) + m_stubProc.setMode(ConsoleProcess::Suspend); + else + m_stubProc.setMode(ConsoleProcess::Debug); + } else { + m_stubProc.setMode(ConsoleProcess::Debug); + m_stubProc.setSettings(ICore::settings()); + } + } } GdbEngine::~GdbEngine() { + if (isTermEngine()) + m_stubProc.disconnect(); // Avoid spurious state transitions from late exiting stub + + if (isCoreEngine()) { + if (m_coreUnpackProcess) { + m_coreUnpackProcess->blockSignals(true); + m_coreUnpackProcess->terminate(); + m_coreUnpackProcess->deleteLater(); + m_coreUnpackProcess = nullptr; + if (m_tempCoreFile.isOpen()) + m_tempCoreFile.close(); + } + if (!m_tempCoreName.isEmpty()) { + QFile tmpFile(m_tempCoreName); + tmpFile.remove(); + } + } + //ExtensionSystem::PluginManager::removeObject(m_debugInfoTaskHandler); delete m_debugInfoTaskHandler; m_debugInfoTaskHandler = 0; @@ -254,35 +265,14 @@ GdbEngine::~GdbEngine() disconnect(); } -DebuggerStartMode GdbEngine::startMode() const -{ - return runParameters().startMode; -} - QString GdbEngine::failedToStartMessage() { return tr("The gdb process failed to start."); } -#if 0 -static void dump(const char *first, const char *middle, const QString & to) -{ - QString ba(first, middle - first); - Q_UNUSED(to) - // note that qDebug cuts off output after a certain size... (bug?) - qDebug("\n>>>>> %s\n%s\n====\n%s\n<<<<<\n", - qPrintable(currentTime()), - qPrintable(QString(ba).trimmed()), - qPrintable(to.trimmed())); - //qDebug() << ""; - //qDebug() << qPrintable(currentTime()) - // << " Reading response: " << QString(ba).trimmed() << "\n"; -} -#endif - // Parse "~:gdb: unknown target exception 0xc0000139 at 0x77bef04e\n" // and return an exception message -static inline QString msgWinException(const QString &data, unsigned *exCodeIn = 0) +static QString msgWinException(const QString &data, unsigned *exCodeIn = 0) { if (exCodeIn) *exCodeIn = 0; @@ -787,6 +777,9 @@ void GdbEngine::readGdbStandardOutput() void GdbEngine::interruptInferior() { + // A core never runs, so this cannot be called. + QTC_ASSERT(!isCoreEngine(), return); + CHECK_STATE(InferiorStopRequested); if (terminal()->sendInterrupt()) @@ -1017,7 +1010,7 @@ void GdbEngine::handleResultRecord(DebuggerResponse *response) //notifyInferiorIll(); //showStatusMessage(tr("Executable failed: %1").arg(msg)); //shutdown(); - //Core::AsynchronousMessageBox::critical(tr("Executable failed"), msg); + //AsynchronousMessageBox::critical(tr("Executable failed"), msg); } else if (msg.contains("Cannot insert breakpoint")) { // For breakpoints set by address to non-existent addresses we // might get something like "6^error,msg="Warning:\nCannot insert @@ -1862,7 +1855,7 @@ void GdbEngine::setLinuxOsAbi() void GdbEngine::detachDebugger() { CHECK_STATE(InferiorStopOk); - QTC_ASSERT(startMode() != AttachCore, qDebug() << startMode()); + QTC_ASSERT(m_startMode != AttachCore, qDebug() << m_startMode); DebuggerCommand cmd("detach", ExitRequest); cmd.callback = [this](const DebuggerResponse &) { CHECK_STATE(InferiorStopOk); @@ -3984,6 +3977,10 @@ void GdbEngine::startGdb(const QStringList &args) void GdbEngine::handleGdbStartFailed() { + if (isTermEngine()) + m_stubProc.stop(); + else if (isPlainEngine()) + m_outputCollector.shutdown(); } void GdbEngine::loadInitScript() @@ -4314,7 +4311,7 @@ QString GdbEngine::msgConnectRemoteServerFailed(const QString &why) void GdbEngine::interruptLocalInferior(qint64 pid) { - QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state(); return); + CHECK_STATE(InferiorStopRequested); if (pid <= 0) { showMessage("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED", LogError); return; @@ -4333,25 +4330,836 @@ void GdbEngine::debugLastCommand() runCommand(m_lastDebuggableCommand); } -// -// Factory -// +bool GdbEngine::isPlainEngine() const +{ + return !isCoreEngine() && !isAttachEngine() && !isRemoteEngine() && !m_terminalTrap; +} + +bool GdbEngine::isCoreEngine() const +{ + return m_startMode == AttachCore; +} -DebuggerEngine *createGdbEngine(bool useTerminal, DebuggerStartMode sm) +bool GdbEngine::isRemoteEngine() const { - switch (sm) { - case AttachCore: - return new GdbCoreEngine(useTerminal); - case StartRemoteProcess: - case AttachToRemoteServer: - return new GdbRemoteServerEngine(useTerminal); - case AttachExternal: - return new GdbAttachEngine(useTerminal); + return m_startMode == StartRemoteProcess || m_startMode == AttachToRemoteServer; +} + +bool GdbEngine::isAttachEngine() const +{ + return m_startMode == AttachExternal; +} + +bool GdbEngine::isTermEngine() const +{ + return !isCoreEngine() && !isAttachEngine() && !isRemoteEngine() && m_terminalTrap; +} + +void GdbEngine::setupEngine() +{ + CHECK_STATE(EngineSetupRequested); + showMessage("TRYING TO START ADAPTER"); + + if (isAttachEngine()) { + + startGdb(); + + } else if (isRemoteEngine()) { + + if (HostOsInfo::isWindowsHost()) + m_gdbProc.setUseCtrlCStub(runParameters().useCtrlCStub); // This is only set for QNX + + startGdb(); + + } else if (isTermEngine()) { + + showMessage("TRYING TO START ADAPTER"); + + // Currently, GdbEngines are not re-used + // // We leave the console open, so recycle it now. + // m_stubProc.blockSignals(true); + // m_stubProc.stop(); + // m_stubProc.blockSignals(false); + + m_stubProc.setWorkingDirectory(runParameters().inferior.workingDirectory); + // Set environment + dumper preload. + m_stubProc.setEnvironment(runParameters().stubEnvironment); + + connect(&m_stubProc, &ConsoleProcess::processError, + this, &GdbEngine::stubError); + connect(&m_stubProc, &ConsoleProcess::processStarted, + this, [this] { startGdb(); }); + connect(&m_stubProc, &ConsoleProcess::stubStopped, + this, &GdbEngine::stubExited); + // FIXME: Starting the stub implies starting the inferior. This is + // fairly unclean as far as the state machine and error reporting go. + + if (!m_stubProc.start(runParameters().inferior.executable, + runParameters().inferior.commandLineArguments)) { + // Error message for user is delivered via a signal. + handleAdapterStartFailed(QString()); + } + + } else if (isCoreEngine()) { + + CHECK_STATE(EngineSetupRequested); + showMessage("TRYING TO START ADAPTER"); + + const DebuggerRunParameters &rp = runParameters(); + m_executable = rp.inferior.executable; + QFileInfo fi(rp.coreFile); + m_coreName = fi.absoluteFilePath(); + + unpackCoreIfNeeded(); + + } else if (isPlainEngine()) { + + QStringList gdbArgs; + + if (!m_outputCollector.listen()) { + handleAdapterStartFailed(tr("Cannot set up communication with child process: %1") + .arg(m_outputCollector.errorString())); + return; + } + gdbArgs.append("--tty=" + m_outputCollector.serverName()); + + startGdb(gdbArgs); + } +} + +void GdbEngine::setupInferior() +{ + CHECK_STATE(InferiorSetupRequested); + + if (isAttachEngine()) { + // Task 254674 does not want to remove them + //qq->breakHandler()->removeAllBreakpoints(); + handleInferiorPrepared(); + + } else if (isRemoteEngine()) { + + setLinuxOsAbi(); + const DebuggerRunParameters &rp = runParameters(); + QString symbolFile; + if (!rp.symbolFile.isEmpty()) { + QFileInfo fi(rp.symbolFile); + symbolFile = fi.absoluteFilePath(); + } + + //const QByteArray sysroot = sp.sysroot.toLocal8Bit(); + //const QByteArray remoteArch = sp.remoteArchitecture.toLatin1(); + const QString args = runParameters().inferior.commandLineArguments; + + // if (!remoteArch.isEmpty()) + // postCommand("set architecture " + remoteArch); + const QString solibSearchPath = rp.solibSearchPath.join(HostOsInfo::pathListSeparator()); + if (!solibSearchPath.isEmpty()) + runCommand({"set solib-search-path " + solibSearchPath}); + + if (!args.isEmpty()) + runCommand({"-exec-arguments " + args}); + + setEnvironmentVariables(); + + // This has to be issued before 'target remote'. On pre-7.0 the + // command is not present and will result in ' No symbol table is + // loaded. Use the "file" command.' as gdb tries to set the + // value of a variable with name 'target-async'. + // + // Testing with -list-target-features which was introduced at + // the same time would not work either, as this need an existing + // target. + // + // Using it even without a target and having it fail might still + // be better as: + // Some external comment: '[but] "set target-async on" with a native + // windows gdb will work, but then fail when you actually do + // "run"/"attach", I think.. + + + // gdb/mi/mi-main.c:1958: internal-error: + // mi_execute_async_cli_command: Assertion `is_running (inferior_ptid)' + // failed.\nA problem internal to GDB has been detected,[...] + if (usesTargetAsync()) + runCommand({"set target-async on", CB(handleSetTargetAsync)}); + + if (symbolFile.isEmpty()) { + showMessage(tr("No symbol file given."), StatusBar); + callTargetRemote(); + return; + } + + if (!symbolFile.isEmpty()) { + runCommand({"-file-exec-and-symbols \"" + symbolFile + '"', + CB(handleFileExecAndSymbols)}); + } + + } else if (isCoreEngine()) { + + setLinuxOsAbi(); + // Do that first, otherwise no symbols are loaded. + QFileInfo fi(m_executable); + QString path = fi.absoluteFilePath(); + runCommand({"-file-exec-and-symbols \"" + path + '"', + CB(handleFileExecAndSymbols)}); + + } else if (isTermEngine()) { + + const qint64 attachedPID = m_stubProc.applicationPID(); + const qint64 attachedMainThreadID = m_stubProc.applicationMainThreadID(); + notifyInferiorPid(ProcessHandle(attachedPID)); + const QString msg = (attachedMainThreadID != -1) + ? QString("Going to attach to %1 (%2)").arg(attachedPID).arg(attachedMainThreadID) + : QString("Going to attach to %1").arg(attachedPID); + showMessage(msg, LogMisc); + handleInferiorPrepared(); + + } else if (isPlainEngine()) { + + setEnvironmentVariables(); + const DebuggerRunParameters &rp = runParameters(); + if (!rp.inferior.workingDirectory.isEmpty()) + runCommand({"cd " + rp.inferior.workingDirectory}); + if (!rp.inferior.commandLineArguments.isEmpty()) { + QString args = rp.inferior.commandLineArguments; + runCommand({"-exec-arguments " + args}); + } + + QString executable = QFileInfo(runParameters().inferior.executable).absoluteFilePath(); + runCommand({"-file-exec-and-symbols \"" + executable + '"', + CB(handleFileExecAndSymbols)}); + } +} + +void GdbEngine::runEngine() +{ + CHECK_STATE(EngineRunRequested); + + if (isAttachEngine()) { + + const qint64 pid = runParameters().attachPID.pid(); + showStatusMessage(tr("Attaching to process %1.").arg(pid)); + runCommand({"attach " + QString::number(pid), + [this](const DebuggerResponse &r) { handleAttach(r); }}); + // In some cases we get only output like + // "Could not attach to process. If your uid matches the uid of the target\n" + // "process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try\n" + // " again as the root user. For more details, see /etc/sysctl.d/10-ptrace.conf\n" + // " ptrace: Operation not permitted.\n" + // but no(!) ^ response. Use a second command to force *some* output + runCommand({"print 24"}); + + } else if (isRemoteEngine()) { + + if (runParameters().useContinueInsteadOfRun) { + notifyEngineRunAndInferiorStopOk(); + continueInferiorInternal(); + } else { + runCommand({"-exec-run", DebuggerCommand::RunRequest, CB(handleExecRun)}); + } + + } else if (isCoreEngine()) { + + runCommand({"target core " + coreFileName(), CB(handleTargetCore)}); + + } else if (isTermEngine()) { + + const qint64 attachedPID = m_stubProc.applicationPID(); + runCommand({"attach " + QString::number(attachedPID), + [this](const DebuggerResponse &r) { handleStubAttached(r); }}); + + } else if (isPlainEngine()) { + + if (runParameters().useContinueInsteadOfRun) + runCommand({"-exec-continue", DebuggerCommand::RunRequest, CB(handleExecuteContinue)}); + else + runCommand({"-exec-run", DebuggerCommand::RunRequest, CB(handleExecRun)}); + + } +} + +void GdbEngine::handleAttach(const DebuggerResponse &response) +{ + if (isAttachEngine()) { + + QTC_ASSERT(state() == EngineRunRequested || state() == InferiorStopOk, qDebug() << state()); + switch (response.resultClass) { + case ResultDone: + case ResultRunning: + showMessage("INFERIOR ATTACHED"); + if (state() == EngineRunRequested) { + // Happens e.g. for "Attach to unstarted application" + // We will get a '*stopped' later that we'll interpret as 'spontaneous' + // So acknowledge the current state and put a delayed 'continue' in the pipe. + showMessage(tr("Attached to running application"), StatusBar); + notifyEngineRunAndInferiorRunOk(); + } else { + // InferiorStopOk, e.g. for "Attach to running application". + // The *stopped came in between sending the 'attach' and + // receiving its '^done'. + if (runParameters().continueAfterAttach) + continueInferiorInternal(); + } + break; + case ResultError: + if (response.data["msg"].data() == "ptrace: Operation not permitted.") { + QString msg = msgPtraceError(runParameters().startMode); + showStatusMessage(tr("Failed to attach to application: %1").arg(msg)); + AsynchronousMessageBox::warning(tr("Debugger Error"), msg); + notifyEngineIll(); + break; + } + showStatusMessage(tr("Failed to attach to application: %1") + .arg(QString(response.data["msg"].data()))); + notifyEngineIll(); + break; + default: + showStatusMessage(tr("Failed to attach to application: %1") + .arg(QString(response.data["msg"].data()))); + notifyEngineIll(); + break; + } + + } else if (isRemoteEngine()) { + + CHECK_STATE(InferiorSetupRequested); + switch (response.resultClass) { + case ResultDone: + case ResultRunning: { + showMessage("INFERIOR ATTACHED"); + showMessage(msgAttachedToStoppedInferior(), StatusBar); + handleInferiorPrepared(); + break; + } + case ResultError: + if (response.data["msg"].data() == "ptrace: Operation not permitted.") { + notifyInferiorSetupFailed(msgPtraceError(runParameters().startMode)); + break; + } + notifyInferiorSetupFailed(response.data["msg"].data()); + break; + default: + notifyInferiorSetupFailed(response.data["msg"].data()); + break; + } + + } +} + +void GdbEngine::interruptInferior2() +{ + if (isAttachEngine()) { + + interruptLocalInferior(runParameters().attachPID.pid()); + + } else if (isRemoteEngine()) { + + CHECK_STATE(InferiorStopRequested); + if (usesTargetAsync()) { + runCommand({"-exec-interrupt", CB(handleInterruptInferior)}); + } else if (m_isQnxGdb && HostOsInfo::isWindowsHost()) { + m_gdbProc.interrupt(); + } else { + qint64 pid = m_gdbProc.processId(); + bool ok = interruptProcess(pid, GdbEngineType, &m_errorString); + if (!ok) { + // FIXME: Extra state needed? + showMessage("NOTE: INFERIOR STOP NOT POSSIBLE"); + showStatusMessage(tr("Interrupting not possible")); + notifyInferiorRunOk(); + } + } + + } else if (isTermEngine() || isPlainEngine()) { + + interruptLocalInferior(inferiorPid()); + + } +} + +void GdbEngine::shutdownEngine() +{ + if (isPlainEngine()) { + showMessage(QString("PLAIN ADAPTER SHUTDOWN %1").arg(state())); + m_outputCollector.shutdown(); + } + + notifyAdapterShutdownOk(); +} + +void GdbEngine::handleFileExecAndSymbols(const DebuggerResponse &response) +{ + CHECK_STATE(InferiorSetupRequested); + + if (isRemoteEngine()) { + if (response.resultClass == ResultDone) { + callTargetRemote(); + } else { + QString reason = response.data["msg"].data(); + QString msg = tr("Reading debug information failed:") + '\n' + reason; + if (reason.endsWith("No such file or directory.")) { + showMessage("INFERIOR STARTUP: BINARY NOT FOUND"); + showMessage(msg, StatusBar); + callTargetRemote(); // Proceed nevertheless. + } else { + notifyInferiorSetupFailed(msg); + } + } + + } else if (isCoreEngine()) { + + QString core = coreFileName(); + if (response.resultClass == ResultDone) { + showMessage(tr("Symbols found."), StatusBar); + handleInferiorPrepared(); + } else { + QString msg = tr("No symbols found in core file <i>%1</i>.").arg(core) + + ' ' + tr("This can be caused by a path length limitation " + "in the core file.") + + ' ' + tr("Try to specify the binary using the " + "<i>Debug->Start Debugging->Attach to Core</i> dialog."); + notifyInferiorSetupFailed(msg); + } + + } else if (isPlainEngine()) { + + if (response.resultClass == ResultDone) { + handleInferiorPrepared(); + } else { + QString msg = response.data["msg"].data(); + // Extend the message a bit in unknown cases. + if (!msg.endsWith("File format not recognized")) + msg = tr("Starting executable failed:") + '\n' + msg; + notifyInferiorSetupFailed(msg); + } + + } +} + +void GdbEngine::handleExecRun(const DebuggerResponse &response) +{ + CHECK_STATE(EngineRunRequested); + + if (isRemoteEngine()) { + + if (response.resultClass == ResultRunning) { + notifyEngineRunAndInferiorRunOk(); + showMessage("INFERIOR STARTED"); + showMessage(msgInferiorSetupOk(), StatusBar); + } else { + showMessage(response.data["msg"].data()); + notifyEngineRunFailed(); + } + + } else if (isPlainEngine()) { + + if (response.resultClass == ResultRunning) { + notifyEngineRunAndInferiorRunOk(); // For gdb < 7.0 + //showStatusMessage(tr("Running...")); + showMessage("INFERIOR STARTED"); + showMessage(msgInferiorSetupOk(), StatusBar); + // FIXME: That's the wrong place for it. + if (boolSetting(EnableReverseDebugging)) + runCommand({"target record"}); + } else { + QString msg = response.data["msg"].data(); + //QTC_CHECK(status() == InferiorRunOk); + //interruptInferior(); + showMessage(msg); + notifyEngineRunFailed(); + } + + } +} + +void GdbEngine::handleSetTargetAsync(const DebuggerResponse &response) +{ + CHECK_STATE(InferiorSetupRequested); + if (response.resultClass == ResultError) + qDebug() << "Adapter too old: does not support asynchronous mode."; +} + +void GdbEngine::callTargetRemote() +{ + QString channel = runParameters().remoteChannel; + + // Don't touch channels with explicitly set protocols. + if (!channel.startsWith("tcp:") && !channel.startsWith("udp:") + && !channel.startsWith("file:") && channel.contains(':') + && !channel.startsWith('|')) + { + // "Fix" the IPv6 case with host names without '['...']' + if (!channel.startsWith('[') && channel.count(':') >= 2) { + channel.insert(0, '['); + channel.insert(channel.lastIndexOf(':'), ']'); + } + channel = "tcp:" + channel; + } + + if (m_isQnxGdb) + runCommand({"target qnx " + channel, CB(handleTargetQnx)}); + else if (runParameters().useExtendedRemote) + runCommand({"target extended-remote " + channel, CB(handleTargetExtendedRemote)}); + else + runCommand({"target remote " + channel, CB(handleTargetRemote)}); +} + +void GdbEngine::handleTargetRemote(const DebuggerResponse &response) +{ + CHECK_STATE(InferiorSetupRequested); + if (response.resultClass == ResultDone) { + // gdb server will stop the remote application itself. + showMessage("INFERIOR STARTED"); + showMessage(msgAttachedToStoppedInferior(), StatusBar); + QString commands = expand(stringSetting(GdbPostAttachCommands)); + if (!commands.isEmpty()) + runCommand({commands, NativeCommand}); + handleInferiorPrepared(); + } else { + // 16^error,msg="hd:5555: Connection timed out." + notifyInferiorSetupFailed(msgConnectRemoteServerFailed(response.data["msg"].data())); + } +} + +void GdbEngine::handleTargetExtendedRemote(const DebuggerResponse &response) +{ + CHECK_STATE(InferiorSetupRequested); + if (response.resultClass == ResultDone) { + showMessage("ATTACHED TO GDB SERVER STARTED"); + showMessage(msgAttachedToStoppedInferior(), StatusBar); + QString commands = expand(stringSetting(GdbPostAttachCommands)); + if (!commands.isEmpty()) + runCommand({commands, NativeCommand}); + if (runParameters().attachPID.isValid()) { // attach to pid if valid + // gdb server will stop the remote application itself. + runCommand({"attach " + QString::number(runParameters().attachPID.pid()), + CB(handleTargetExtendedAttach)}); + } else if (!runParameters().inferior.executable.isEmpty()) { + runCommand({"-gdb-set remote exec-file " + runParameters().inferior.executable, + CB(handleTargetExtendedAttach)}); + } else { + const QString title = tr("No Remote Executable or Process ID Specified"); + const QString msg = tr( + "No remote executable could be determined from your build system files.<p>" + "In case you use qmake, consider adding<p>" + " target.path = /tmp/your_executable # path on device<br>" + " INSTALLS += target</p>" + "to your .pro file."); + QMessageBox *mb = showMessageBox(QMessageBox::Critical, title, msg, + QMessageBox::Ok | QMessageBox::Cancel); + mb->button(QMessageBox::Cancel)->setText(tr("Continue Debugging")); + mb->button(QMessageBox::Ok)->setText(tr("Stop Debugging")); + if (mb->exec() == QMessageBox::Ok) { + showMessage("KILLING DEBUGGER AS REQUESTED BY USER"); + notifyInferiorSetupFailed(title); + } else { + showMessage("CONTINUE DEBUGGER AS REQUESTED BY USER"); + handleInferiorPrepared(); // This will likely fail. + } + } + } else { + notifyInferiorSetupFailed(msgConnectRemoteServerFailed(response.data["msg"].data())); + } +} + +void GdbEngine::handleTargetExtendedAttach(const DebuggerResponse &response) +{ + CHECK_STATE(InferiorSetupRequested); + if (response.resultClass == ResultDone) { + // gdb server will stop the remote application itself. + handleInferiorPrepared(); + } else { + notifyInferiorSetupFailed(msgConnectRemoteServerFailed(response.data["msg"].data())); + } +} + +void GdbEngine::handleTargetQnx(const DebuggerResponse &response) +{ + CHECK_STATE(InferiorSetupRequested); + if (response.resultClass == ResultDone) { + // gdb server will stop the remote application itself. + showMessage("INFERIOR STARTED"); + showMessage(msgAttachedToStoppedInferior(), StatusBar); + + const DebuggerRunParameters &rp = runParameters(); + const QString remoteExecutable = rp.inferior.executable; + if (rp.attachPID.isValid()) + runCommand({"attach " + QString::number(rp.attachPID.pid()), CB(handleAttach)}); + else if (!remoteExecutable.isEmpty()) + runCommand({"set nto-executable " + remoteExecutable, CB(handleSetNtoExecutable)}); + else + handleInferiorPrepared(); + } else { + // 16^error,msg="hd:5555: Connection timed out." + notifyInferiorSetupFailed(response.data["msg"].data()); + } +} + +void GdbEngine::handleSetNtoExecutable(const DebuggerResponse &response) +{ + CHECK_STATE(InferiorSetupRequested); + switch (response.resultClass) { + case ResultDone: + case ResultRunning: { + showMessage("EXECUTABLE SET"); + showMessage(msgAttachedToStoppedInferior(), StatusBar); + handleInferiorPrepared(); + break; + } + case ResultError: default: - if (useTerminal) - return new GdbTermEngine(useTerminal); - return new GdbPlainEngine(useTerminal); + notifyInferiorSetupFailed(response.data["msg"].data()); + } +} + +void GdbEngine::handleInterruptInferior(const DebuggerResponse &response) +{ + if (response.resultClass == ResultDone) { + // The gdb server will trigger extra output that we will pick up + // to do a proper state transition. + } else { + // FIXME: On some gdb versions like git 170ffa5d7dd this produces + // >810^error,msg="mi_cmd_exec_interrupt: Inferior not executing." + notifyInferiorStopOk(); + } +} + +void GdbEngine::handleStubAttached(const DebuggerResponse &response) +{ + // InferiorStopOk can happen if the "*stopped" in response to the + // 'attach' comes in before its '^done' + QTC_ASSERT(state() == EngineRunRequested || state() == InferiorStopOk, qDebug() << state()); + + switch (response.resultClass) { + case ResultDone: + case ResultRunning: + if (runParameters().toolChainAbi.os() == ProjectExplorer::Abi::WindowsOS) { + QString errorMessage; + // Resume thread that was suspended by console stub process (see stub code). + const qint64 mainThreadId = m_stubProc.applicationMainThreadID(); + if (winResumeThread(mainThreadId, &errorMessage)) { + showMessage(QString("Inferior attached, thread %1 resumed"). + arg(mainThreadId), LogMisc); + } else { + showMessage(QString("Inferior attached, unable to resume thread %1: %2"). + arg(mainThreadId).arg(errorMessage), + LogWarning); + } + notifyEngineRunAndInferiorStopOk(); + continueInferiorInternal(); + } else { + showMessage("INFERIOR ATTACHED AND RUNNING"); + //notifyEngineRunAndInferiorRunOk(); + // Wait for the upcoming *stopped and handle it there. + } + break; + case ResultError: + if (response.data["msg"].data() == "ptrace: Operation not permitted.") { + showMessage(msgPtraceError(runParameters().startMode)); + notifyEngineRunFailed(); + break; + } + showMessage(response.data["msg"].data()); + notifyEngineIll(); + break; + default: + showMessage(QString("Invalid response %1").arg(response.resultClass)); + notifyEngineIll(); + break; + } +} + +void GdbEngine::stubError(const QString &msg) +{ + AsynchronousMessageBox::critical(tr("Debugger Error"), msg); + notifyEngineIll(); +} + +void GdbEngine::stubExited() +{ + if (state() == EngineShutdownRequested || state() == DebuggerFinished) { + showMessage("STUB EXITED EXPECTEDLY"); + } else { + showMessage("STUB EXITED"); + notifyEngineIll(); + } +} + +static QString findExecutableFromName(const QString &fileNameFromCore, const QString &coreFile) +{ + if (fileNameFromCore.isEmpty()) + return fileNameFromCore; + QFileInfo fi(fileNameFromCore); + if (fi.isFile()) + return fileNameFromCore; + + // turn the filename into an absolute path, using the location of the core as a hint + QString absPath; + if (fi.isAbsolute()) { + absPath = fileNameFromCore; + } else { + QFileInfo coreInfo(coreFile); + QDir coreDir = coreInfo.dir(); + absPath = FileUtils::resolvePath(coreDir.absolutePath(), fileNameFromCore); + } + if (QFileInfo(absPath).isFile() || absPath.isEmpty()) + return absPath; + + // remove possible trailing arguments + QLatin1Char sep(' '); + QStringList pathFragments = absPath.split(sep); + while (pathFragments.size() > 0) { + QString joined_path = pathFragments.join(sep); + if (QFileInfo(joined_path).isFile()) { + return joined_path; + } + pathFragments.pop_back(); } + + return QString(); +} + +CoreInfo CoreInfo::readExecutableNameFromCore(const StandardRunnable &debugger, const QString &coreFile) +{ + CoreInfo cinfo; +#if 0 + ElfReader reader(coreFile); + cinfo.rawStringFromCore = QString::fromLocal8Bit(reader.readCoreName(&cinfo.isCore)); + cinfo.foundExecutableName = findExecutableFromName(cinfo.rawStringFromCore, coreFile); +#else + QStringList args = {"-nx", "-batch", "-c", coreFile}; + + SynchronousProcess proc; + QStringList envLang = QProcess::systemEnvironment(); + Utils::Environment::setupEnglishOutput(&envLang); + proc.setEnvironment(envLang); + SynchronousProcessResponse response = proc.runBlocking(debugger.executable, args); + + if (response.result == SynchronousProcessResponse::Finished) { + QString output = response.stdOut(); + // Core was generated by `/data/dev/creator-2.6/bin/qtcreator'. + // Program terminated with signal 11, Segmentation fault. + int pos1 = output.indexOf("Core was generated by"); + if (pos1 != -1) { + pos1 += 23; + int pos2 = output.indexOf('\'', pos1); + if (pos2 != -1) { + cinfo.isCore = true; + cinfo.rawStringFromCore = output.mid(pos1, pos2 - pos1); + cinfo.foundExecutableName = findExecutableFromName(cinfo.rawStringFromCore, coreFile); + } + } + } +#endif + return cinfo; +} + +void GdbEngine::continueSetupEngine() +{ + if (isCoreEngine()) { + bool isCore = true; + if (m_coreUnpackProcess) { + isCore = m_coreUnpackProcess->exitCode() == 0; + m_coreUnpackProcess->deleteLater(); + m_coreUnpackProcess = 0; + if (m_tempCoreFile.isOpen()) + m_tempCoreFile.close(); + } + if (isCore && m_executable.isEmpty()) { + CoreInfo cinfo = + CoreInfo::readExecutableNameFromCore(runParameters().debugger, coreFileName()); + + if (cinfo.isCore) { + m_executable = cinfo.foundExecutableName; + if (m_executable.isEmpty()) { + AsynchronousMessageBox::warning(tr("Error Loading Symbols"), + tr("No executable to load symbols from specified core.")); + notifyEngineSetupFailed(); + return; + } + } + } + if (isCore) { + startGdb(); + } else { + AsynchronousMessageBox::warning(tr("Error Loading Core File"), + tr("The specified file does not appear to be a core file.")); + notifyEngineSetupFailed(); + } + } +} + +void GdbEngine::handleTargetCore(const DebuggerResponse &response) +{ + CHECK_STATE(EngineRunRequested); + notifyEngineRunOkAndInferiorUnrunnable(); + showMessage(tr("Attached to core."), StatusBar); + if (response.resultClass == ResultError) { + // We'll accept any kind of error e.g. &"Cannot access memory at address 0x2abc2a24\n" + // Even without the stack, the user can find interesting stuff by exploring + // the memory, globals etc. + showStatusMessage(tr("Attach to core \"%1\" failed:").arg(runParameters().coreFile) + + '\n' + response.data["msg"].data() + + '\n' + tr("Continuing nevertheless.")); + } + // Due to the auto-solib-add off setting, we don't have any + // symbols yet. Load them in order of importance. + reloadStack(); + reloadModulesInternal(); + runCommand({"p 5", CB(handleCoreRoundTrip)}); +} + +void GdbEngine::handleCoreRoundTrip(const DebuggerResponse &response) +{ + CHECK_STATE(InferiorUnrunnable); + Q_UNUSED(response); + loadSymbolsForStack(); + handleStop3(); + QTimer::singleShot(1000, this, &GdbEngine::loadAllSymbols); +} + +static QString tempCoreFilename() +{ + Utils::TemporaryFile tmp("tmpcore-XXXXXX"); + tmp.open(); + return tmp.fileName(); +} + +void GdbEngine::unpackCoreIfNeeded() +{ + QStringList arguments; + const QString msg = "Unpacking core file to %1"; + if (m_coreName.endsWith(".lzo")) { + m_tempCoreName = tempCoreFilename(); + showMessage(msg.arg(m_tempCoreName)); + arguments << "-o" << m_tempCoreName << "-x" << m_coreName; + m_coreUnpackProcess = new QProcess(this); + m_coreUnpackProcess->setWorkingDirectory(TemporaryDirectory::masterDirectoryPath()); + m_coreUnpackProcess->start("lzop", arguments); + connect(m_coreUnpackProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished), + this, &GdbEngine::continueSetupEngine); + } else if (m_coreName.endsWith(".gz")) { + m_tempCoreName = tempCoreFilename(); + showMessage(msg.arg(m_tempCoreName)); + m_tempCoreFile.setFileName(m_tempCoreName); + m_tempCoreFile.open(QFile::WriteOnly); + arguments << "-c" << "-d" << m_coreName; + m_coreUnpackProcess = new QProcess(this); + m_coreUnpackProcess->setWorkingDirectory(TemporaryDirectory::masterDirectoryPath()); + m_coreUnpackProcess->start("gzip", arguments); + connect(m_coreUnpackProcess, &QProcess::readyRead, this, [this] { + m_tempCoreFile.write(m_coreUnpackProcess->readAll()); + }); + connect(m_coreUnpackProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished), + this, &GdbEngine::continueSetupEngine); + } else { + continueSetupEngine(); + } +} + +QString GdbEngine::coreFileName() const +{ + return m_tempCoreName.isEmpty() ? m_coreName : m_tempCoreName; } void GdbEngine::doUpdateLocals(const UpdateParameters ¶ms) @@ -4417,6 +5225,15 @@ QString GdbEngine::msgPtraceError(DebuggerStartMode sm) "For more details, see /etc/sysctl.d/10-ptrace.conf\n"); } +// +// Factory +// + +DebuggerEngine *createGdbEngine(bool useTerminal, DebuggerStartMode startMode) +{ + return new GdbEngine(useTerminal, startMode); +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index 3a5a9d6645..01b1d86a49 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -33,64 +33,69 @@ #include <debugger/watchutils.h> #include <debugger/debuggeritem.h> #include <debugger/debuggertooltipmanager.h> +#include <debugger/outputcollector.h> #include <coreplugin/id.h> #include <utils/qtcprocess.h> +#include <utils/consoleprocess.h> #include <QProcess> #include <QTextCodec> #include <QTimer> -#include <functional> - namespace Debugger { namespace Internal { -class GdbProcess; +class BreakpointParameters; +class BreakpointResponse; class DebugInfoTask; class DebugInfoTaskHandler; class DebuggerResponse; +class DisassemblerAgentCookie; class GdbMi; class MemoryAgentCookie; -class BreakpointParameters; -class BreakpointResponse; -class DisassemblerAgentCookie; -class DisassemblerLines; +struct CoreInfo +{ + QString rawStringFromCore; + QString foundExecutableName; // empty if no corresponding exec could be found + bool isCore = false; + + static CoreInfo readExecutableNameFromCore(const ProjectExplorer::StandardRunnable &debugger, + const QString &coreFile); +}; class GdbEngine : public DebuggerEngine { Q_OBJECT public: - explicit GdbEngine(bool useTerminal); - ~GdbEngine() override; + explicit GdbEngine(bool useTerminal, DebuggerStartMode startMode); + ~GdbEngine() final; private: ////////// General Interface ////////// - DebuggerEngine *cppEngine() override { return this; } + DebuggerEngine *cppEngine() final { return this; } - virtual void handleGdbStartFailed(); - void notifyInferiorSetupFailed() override; - void prepareForRestart() override; + void handleGdbStartFailed(); + void notifyInferiorSetupFailed() final; + void prepareForRestart() final; - bool hasCapability(unsigned) const override; - void detachDebugger() override; - void shutdownInferior() override; - void abortDebugger() override; - void resetInferior() override; + bool hasCapability(unsigned) const final; + void detachDebugger() final; + void shutdownInferior() final; + void abortDebugger() final; + void resetInferior() final; - bool acceptsDebuggerCommands() const override; - void executeDebuggerCommand(const QString &command, DebuggerLanguages languages) override; + bool acceptsDebuggerCommands() const final; + void executeDebuggerCommand(const QString &command, DebuggerLanguages languages) final; -private: ////////// General State ////////// + ////////// General State ////////// - DebuggerStartMode startMode() const; - void reloadLocals(); - - bool m_registerNamesListed; + const DebuggerStartMode m_startMode; + bool m_registerNamesListed = false; -protected: ////////// Gdb Process Management ////////// + ////////// Gdb Process Management ////////// void startGdb(const QStringList &args = QStringList()); void handleInferiorShutdown(const DebuggerResponse &response); @@ -123,9 +128,6 @@ protected: ////////// Gdb Process Management ////////// // Make sure to clean up everything before emitting this signal. void handleAdapterCrashed(const QString &msg); -private: - friend class GdbPlainEngine; - friend class GdbCoreEngine; void handleGdbFinished(int exitCode, QProcess::ExitStatus exitStatus); void handleGdbError(QProcess::ProcessError error); void readGdbStandardOutput(); @@ -138,17 +140,16 @@ private: QTextCodec::ConverterState m_inferiorOutputCodecState; QByteArray m_inbuffer; - bool m_busy; + bool m_busy = false; // Name of the convenience variable containing the last // known function return value. QString m_resultVarName; -protected: ////////// Gdb Command Management ////////// + ////////// Gdb Command Management ////////// - void runCommand(const DebuggerCommand &command) override; + void runCommand(const DebuggerCommand &command) final; -private: void commandTimeout(); void setTokenBarrier(); @@ -166,19 +167,19 @@ private: // This contains the first token number for the current round // of evaluation. Responses with older tokens are considers // out of date and discarded. - int m_oldestAcceptableToken; - int m_nonDiscardableCount; + int m_oldestAcceptableToken = -1; + int m_nonDiscardableCount = 0; - int m_pendingBreakpointRequests; // Watch updating commands in flight + int m_pendingBreakpointRequests = 0; // Watch updating commands in flight typedef void (GdbEngine::*CommandsDoneCallback)(); // This function is called after all previous responses have been received. - CommandsDoneCallback m_commandsDoneCallback; + CommandsDoneCallback m_commandsDoneCallback = nullptr; + + bool m_rerunPending = false; - bool m_rerunPending; + ////////// Gdb Output, State & Capability Handling ////////// -private: ////////// Gdb Output, State & Capability Handling ////////// -protected: Q_INVOKABLE void handleResponse(const QString &buff); void handleAsyncOutput(const QString &asyncClass, const GdbMi &result); void handleStopResponse(const GdbMi &data); @@ -188,42 +189,40 @@ protected: void handleStop3(); void resetCommandQueue(); - bool isSynchronous() const override { return true; } + bool isSynchronous() const final { return true; } // Gdb initialization sequence void handleShowVersion(const DebuggerResponse &response); void handleListFeatures(const DebuggerResponse &response); void handlePythonSetup(const DebuggerResponse &response); - int m_gdbVersion; // 7.6.1 is 70601 - int m_pythonVersion; // 2.7.2 is 20702 - bool m_isQnxGdb; + int m_gdbVersion = 100; // 7.6.1 is 70601 + int m_pythonVersion = 0; // 2.7.2 is 20702 + bool m_isQnxGdb = false; -private: ////////// Inferior Management ////////// + ////////// Inferior Management ////////// // This should be always the last call in a function. - bool stateAcceptsBreakpointChanges() const override; - bool acceptsBreakpoint(Breakpoint bp) const override; - void insertBreakpoint(Breakpoint bp) override; - void removeBreakpoint(Breakpoint bp) override; - void changeBreakpoint(Breakpoint bp) override; - - void executeStep() override; - void executeStepOut() override; - void executeNext() override; - void executeStepI() override; - void executeNextI() override; - - protected: + bool stateAcceptsBreakpointChanges() const final; + bool acceptsBreakpoint(Breakpoint bp) const final; + void insertBreakpoint(Breakpoint bp) final; + void removeBreakpoint(Breakpoint bp) final; + void changeBreakpoint(Breakpoint bp) final; + + void executeStep() final; + void executeStepOut() final; + void executeNext() final; + void executeStepI() final; + void executeNextI() final; + void continueInferiorInternal(); - void continueInferior() override; - void interruptInferior() override; - virtual void interruptInferior2() {} + void continueInferior() final; + void interruptInferior() final; - void executeRunToLine(const ContextData &data) override; - void executeRunToFunction(const QString &functionName) override; - void executeJumpToLine(const ContextData &data) override; - void executeReturn() override; + void executeRunToLine(const ContextData &data) final; + void executeRunToFunction(const QString &functionName) final; + void executeJumpToLine(const ContextData &data) final; + void executeReturn() final; void handleExecuteContinue(const DebuggerResponse &response); void handleExecuteStep(const DebuggerResponse &response); @@ -234,10 +233,10 @@ private: ////////// Inferior Management ////////// QString msgPtraceError(DebuggerStartMode sm); -private: ////////// View & Data Stuff ////////// + ////////// View & Data Stuff ////////// - void selectThread(ThreadId threadId) override; - void activateFrame(int index) override; + void selectThread(ThreadId threadId) final; + void activateFrame(int index) final; void handleAutoContinueInferior(); // @@ -265,14 +264,13 @@ private: ////////// View & Data Stuff ////////// // // Modules specific stuff // - protected: - void loadSymbols(const QString &moduleName) override; - void loadAllSymbols() override; - void loadSymbolsForStack() override; - void requestModuleSymbols(const QString &moduleName) override; - void requestModuleSections(const QString &moduleName) override; - void reloadModules() override; - void examineModules() override; + void loadSymbols(const QString &moduleName) final; + void loadAllSymbols() final; + void loadSymbolsForStack() final; + void requestModuleSymbols(const QString &moduleName) final; + void requestModuleSections(const QString &moduleName) final; + void reloadModules() final; + void examineModules() final; void reloadModulesInternal(); void handleModulesList(const DebuggerResponse &response); @@ -281,14 +279,14 @@ private: ////////// View & Data Stuff ////////// // // Snapshot specific stuff // - virtual void createSnapshot() override; + void createSnapshot() final; void handleMakeSnapshot(const DebuggerResponse &response, const QString &coreFile); // // Register specific stuff // - void reloadRegisters() override; - void setRegisterValue(const QString &name, const QString &value) override; + void reloadRegisters() final; + void setRegisterValue(const QString &name, const QString &value) final; void handleRegisterListNames(const DebuggerResponse &response); void handleRegisterListing(const DebuggerResponse &response); void handleRegisterListValues(const DebuggerResponse &response); @@ -299,7 +297,7 @@ private: ////////// View & Data Stuff ////////// // Disassembler specific stuff // // Chain of fallbacks: PointMixed -> PointPlain -> RangeMixed -> RangePlain. - void fetchDisassembler(DisassemblerAgent *agent) override; + void fetchDisassembler(DisassemblerAgent *agent) final; void fetchDisassemblerByCliPointMixed(const DisassemblerAgentCookie &ac); void fetchDisassemblerByCliRangeMixed(const DisassemblerAgentCookie &ac); void fetchDisassemblerByCliRangePlain(const DisassemblerAgentCookie &ac); @@ -308,7 +306,7 @@ private: ////////// View & Data Stuff ////////// // // Source file specific stuff // - void reloadSourceFiles() override; + void reloadSourceFiles() final; void reloadSourceFilesInternal(); void handleQuerySources(const DebuggerResponse &response); @@ -320,13 +318,12 @@ private: ////////// View & Data Stuff ////////// QMap<QString, QString> m_fullToShortName; QMultiMap<QString, QString> m_baseNameToFullName; - bool m_sourcesListUpdating; + bool m_sourcesListUpdating = false; // // Stack specific stuff // -protected: - void updateAll() override; + void updateAll() final; void handleStackListFrames(const DebuggerResponse &response, bool isFull); void handleStackSelectThread(const DebuggerResponse &response); void handleThreadListIds(const DebuggerResponse &response); @@ -334,20 +331,21 @@ protected: void handleThreadNames(const DebuggerResponse &response); DebuggerCommand stackCommand(int depth); void reloadStack(); - void reloadFullStack() override; - void loadAdditionalQmlStack() override; + void reloadFullStack() final; + void loadAdditionalQmlStack() final; int currentFrame() const; // // Watch specific stuff // - virtual void assignValueInDebugger(WatchItem *item, - const QString &expr, const QVariant &value) override; + void reloadLocals(); + void assignValueInDebugger(WatchItem *item, + const QString &expr, const QVariant &value) final; - void fetchMemory(MemoryAgent *agent, quint64 addr, quint64 length) override; + void fetchMemory(MemoryAgent *agent, quint64 addr, quint64 length) final; void fetchMemoryHelper(const MemoryAgentCookie &cookie); void handleChangeMemory(const DebuggerResponse &response); - void changeMemory(MemoryAgent *agent, quint64 addr, const QByteArray &data) override; + void changeMemory(MemoryAgent *agent, quint64 addr, const QByteArray &data) final; void handleFetchMemory(const DebuggerResponse &response, MemoryAgentCookie ac); void showToolTip(); @@ -358,7 +356,7 @@ protected: void createFullBacktrace(); - void doUpdateLocals(const UpdateParameters ¶meters) override; + void doUpdateLocals(const UpdateParameters ¶meters) final; void handleFetchVariables(const DebuggerResponse &response); void setLocals(const QList<GdbMi> &locals); @@ -366,7 +364,7 @@ protected: // // Dumper Management // - void reloadDebuggingHelpers() override; + void reloadDebuggingHelpers() final; // // Convenience Functions @@ -374,19 +372,18 @@ protected: void showExecutionError(const QString &message); QString failedToStartMessage(); - static QString tooltipIName(const QString &exp); - // For short-circuiting stack and thread list evaluation. - bool m_stackNeeded; + bool m_stackNeeded = false; // For suppressing processing *stopped and *running responses // while updating locals. - bool m_inUpdateLocals; + bool m_inUpdateLocals = false; // HACK: QString m_currentThread; QString m_lastWinException; QString m_lastMissingDebugInfo; + const bool m_useTerminal; bool m_terminalTrap; bool usesExecInterrupt() const; bool usesTargetAsync() const; @@ -401,7 +398,7 @@ protected: void requestDebugInformation(const DebugInfoTask &task); DebugInfoTaskHandler *m_debugInfoTaskHandler; - bool m_systemDumpersLoaded; + bool m_systemDumpersLoaded = false; static QString msgGdbStopFailed(const QString &why); static QString msgInferiorStopFailed(const QString &why); @@ -410,14 +407,63 @@ protected: static QString msgInferiorRunOk(); static QString msgConnectRemoteServerFailed(const QString &why); - void debugLastCommand() override; + void debugLastCommand() final; DebuggerCommand m_lastDebuggableCommand; -protected: + bool isPlainEngine() const; + bool isCoreEngine() const; + bool isRemoteEngine() const; + bool isAttachEngine() const; + bool isTermEngine() const; + + void setupEngine() final; + void setupInferior() final; + void runEngine() final; + void shutdownEngine() final; + + void interruptInferior2(); + + // Plain + void handleExecRun(const DebuggerResponse &response); + void handleFileExecAndSymbols(const DebuggerResponse &response); + + // Attach + void handleAttach(const DebuggerResponse &response); + + // Remote + void callTargetRemote(); + void handleSetTargetAsync(const DebuggerResponse &response); + void handleTargetRemote(const DebuggerResponse &response); + void handleTargetExtendedRemote(const DebuggerResponse &response); + void handleTargetExtendedAttach(const DebuggerResponse &response); + void handleTargetQnx(const DebuggerResponse &response); + void handleSetNtoExecutable(const DebuggerResponse &response); + void handleInterruptInferior(const DebuggerResponse &response); void interruptLocalInferior(qint64 pid); -protected: + // Terminal + void handleStubAttached(const DebuggerResponse &response); + void stubExited(); + void stubError(const QString &msg); + Utils::ConsoleProcess m_stubProc; + + // Core + void handleTargetCore(const DebuggerResponse &response); + void handleCoreRoundTrip(const DebuggerResponse &response); + void unpackCoreIfNeeded(); + QString coreFileName() const; + QString coreName() const; + + void continueSetupEngine(); + + QString m_executable; + QString m_coreName; + QString m_tempCoreName; + QProcess *m_coreUnpackProcess = nullptr; + QFile m_tempCoreFile; + Utils::QtcProcess m_gdbProc; + OutputCollector m_outputCollector; QString m_errorString; }; diff --git a/src/plugins/debugger/gdb/gdbplainengine.cpp b/src/plugins/debugger/gdb/gdbplainengine.cpp deleted file mode 100644 index a1b5bf7ed4..0000000000 --- a/src/plugins/debugger/gdb/gdbplainengine.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "gdbplainengine.h" - -#include <debugger/debuggeractions.h> -#include <debugger/debuggercore.h> -#include <debugger/debuggerprotocol.h> -#include <debugger/debuggerstartparameters.h> - -#include <utils/hostosinfo.h> -#include <utils/qtcassert.h> - -#include <QFileInfo> - -namespace Debugger { -namespace Internal { - -#define CB(callback) [this](const DebuggerResponse &r) { callback(r); } - -GdbPlainEngine::GdbPlainEngine(bool useTerminal) - : GdbEngine(useTerminal) -{ - // Output - connect(&m_outputCollector, &OutputCollector::byteDelivery, - this, &GdbEngine::readDebuggeeOutput); -} - -void GdbPlainEngine::setupInferior() -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - setEnvironmentVariables(); - const DebuggerRunParameters &rp = runParameters(); - if (!rp.inferior.workingDirectory.isEmpty()) - runCommand({"cd " + rp.inferior.workingDirectory}); - if (!rp.inferior.commandLineArguments.isEmpty()) { - QString args = rp.inferior.commandLineArguments; - runCommand({"-exec-arguments " + args}); - } - - QString executable = QFileInfo(runParameters().inferior.executable).absoluteFilePath(); - runCommand({"-file-exec-and-symbols \"" + executable + '"', - CB(handleFileExecAndSymbols)}); -} - -void GdbPlainEngine::handleFileExecAndSymbols(const DebuggerResponse &response) -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - if (response.resultClass == ResultDone) { - handleInferiorPrepared(); - } else { - QString msg = response.data["msg"].data(); - // Extend the message a bit in unknown cases. - if (!msg.endsWith("File format not recognized")) - msg = tr("Starting executable failed:") + '\n' + msg; - notifyInferiorSetupFailed(msg); - } -} - -void GdbPlainEngine::runEngine() -{ - if (runParameters().useContinueInsteadOfRun) - runCommand({"-exec-continue", DebuggerCommand::RunRequest, CB(handleExecuteContinue)}); - else - runCommand({"-exec-run", DebuggerCommand::RunRequest, CB(handleExecRun)}); -} - -void GdbPlainEngine::handleExecRun(const DebuggerResponse &response) -{ - QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); - if (response.resultClass == ResultRunning) { - notifyEngineRunAndInferiorRunOk(); // For gdb < 7.0 - //showStatusMessage(tr("Running...")); - showMessage("INFERIOR STARTED"); - showMessage(msgInferiorSetupOk(), StatusBar); - // FIXME: That's the wrong place for it. - if (boolSetting(EnableReverseDebugging)) - runCommand({"target record"}); - } else { - QString msg = response.data["msg"].data(); - //QTC_CHECK(status() == InferiorRunOk); - //interruptInferior(); - showMessage(msg); - notifyEngineRunFailed(); - } -} - -void GdbPlainEngine::setupEngine() -{ - QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); - showMessage("TRYING TO START ADAPTER"); - - QStringList gdbArgs; - - if (!m_outputCollector.listen()) { - handleAdapterStartFailed(tr("Cannot set up communication with child process: %1") - .arg(m_outputCollector.errorString())); - return; - } - gdbArgs.append("--tty=" + m_outputCollector.serverName()); - - startGdb(gdbArgs); -} - -void GdbPlainEngine::handleGdbStartFailed() -{ - m_outputCollector.shutdown(); -} - -void GdbPlainEngine::interruptInferior2() -{ - interruptLocalInferior(inferiorPid()); -} - -void GdbPlainEngine::shutdownEngine() -{ - showMessage(QString("PLAIN ADAPTER SHUTDOWN %1").arg(state())); - m_outputCollector.shutdown(); - notifyAdapterShutdownOk(); -} - -} // namespace Debugger -} // namespace Internal diff --git a/src/plugins/debugger/gdb/gdbplainengine.h b/src/plugins/debugger/gdb/gdbplainengine.h deleted file mode 100644 index a2f6e04a6b..0000000000 --- a/src/plugins/debugger/gdb/gdbplainengine.h +++ /dev/null @@ -1,57 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "gdbengine.h" -#include <debugger/outputcollector.h> - -namespace Debugger { -namespace Internal { - -class GdbPlainEngine : public GdbEngine -{ - // Needs tr - Context - Q_OBJECT - -public: - explicit GdbPlainEngine(bool useTerminal); - -private: - void handleExecRun(const DebuggerResponse &response); - void handleFileExecAndSymbols(const DebuggerResponse &response); - - void setupInferior() override; - void runEngine() override; - void setupEngine() override; - void handleGdbStartFailed() override; - void interruptInferior2() override; - void shutdownEngine() override; - - OutputCollector m_outputCollector; -}; - -} // namespace Debugger -} // namespace Internal diff --git a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp deleted file mode 100644 index 5184e4b7c4..0000000000 --- a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp +++ /dev/null @@ -1,375 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "remotegdbserveradapter.h" - -#include <debugger/debuggeractions.h> -#include <debugger/debuggercore.h> -#include <debugger/debuggerprotocol.h> -#include <debugger/debuggerruncontrol.h> -#include <debugger/procinterrupt.h> - -#include <utils/hostosinfo.h> -#include <utils/qtcfallthrough.h> -#include <utils/qtcassert.h> -#include <utils/qtcprocess.h> - -#include <QAbstractButton> -#include <QFileInfo> -#include <QMessageBox> - -using namespace Utils; - -namespace Debugger { -namespace Internal { - -#define CB(callback) [this](const DebuggerResponse &r) { callback(r); } - -/////////////////////////////////////////////////////////////////////// -// -// RemoteGdbAdapter -// -/////////////////////////////////////////////////////////////////////// - -GdbRemoteServerEngine::GdbRemoteServerEngine(bool useTerminal) - : GdbEngine(useTerminal) -{ -} - -void GdbRemoteServerEngine::setupEngine() -{ - if (HostOsInfo::isWindowsHost()) - m_gdbProc.setUseCtrlCStub(runParameters().useCtrlCStub); // This is only set for QNX - - QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); - showMessage("TRYING TO START ADAPTER"); - - startGdb(); -} - -void GdbRemoteServerEngine::setupInferior() -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - setLinuxOsAbi(); - const DebuggerRunParameters &rp = runParameters(); - QString symbolFile; - if (!rp.symbolFile.isEmpty()) { - QFileInfo fi(rp.symbolFile); - symbolFile = fi.absoluteFilePath(); - } - - //const QByteArray sysroot = sp.sysroot.toLocal8Bit(); - //const QByteArray remoteArch = sp.remoteArchitecture.toLatin1(); - const QString args = runParameters().inferior.commandLineArguments; - -// if (!remoteArch.isEmpty()) -// postCommand("set architecture " + remoteArch); - const QString solibSearchPath = rp.solibSearchPath.join(HostOsInfo::pathListSeparator()); - if (!solibSearchPath.isEmpty()) - runCommand({"set solib-search-path " + solibSearchPath}); - - if (!args.isEmpty()) - runCommand({"-exec-arguments " + args}); - - setEnvironmentVariables(); - - // This has to be issued before 'target remote'. On pre-7.0 the - // command is not present and will result in ' No symbol table is - // loaded. Use the "file" command.' as gdb tries to set the - // value of a variable with name 'target-async'. - // - // Testing with -list-target-features which was introduced at - // the same time would not work either, as this need an existing - // target. - // - // Using it even without a target and having it fail might still - // be better as: - // Some external comment: '[but] "set target-async on" with a native - // windows gdb will work, but then fail when you actually do - // "run"/"attach", I think.. - - - // gdb/mi/mi-main.c:1958: internal-error: - // mi_execute_async_cli_command: Assertion `is_running (inferior_ptid)' - // failed.\nA problem internal to GDB has been detected,[...] - if (usesTargetAsync()) - runCommand({"set target-async on", CB(handleSetTargetAsync)}); - - if (symbolFile.isEmpty()) { - showMessage(tr("No symbol file given."), StatusBar); - callTargetRemote(); - return; - } - - if (!symbolFile.isEmpty()) { - runCommand({"-file-exec-and-symbols \"" + symbolFile + '"', - CB(handleFileExecAndSymbols)}); - } -} - -void GdbRemoteServerEngine::handleSetTargetAsync(const DebuggerResponse &response) -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - if (response.resultClass == ResultError) - qDebug() << "Adapter too old: does not support asynchronous mode."; -} - -void GdbRemoteServerEngine::handleFileExecAndSymbols(const DebuggerResponse &response) -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - if (response.resultClass == ResultDone) { - callTargetRemote(); - } else { - QString reason = response.data["msg"].data(); - QString msg = tr("Reading debug information failed:") + '\n' + reason; - if (reason.endsWith("No such file or directory.")) { - showMessage("INFERIOR STARTUP: BINARY NOT FOUND"); - showMessage(msg, StatusBar); - callTargetRemote(); // Proceed nevertheless. - } else { - notifyInferiorSetupFailed(msg); - } - } -} - -void GdbRemoteServerEngine::callTargetRemote() -{ - QString channel = runParameters().remoteChannel; - - // Don't touch channels with explicitly set protocols. - if (!channel.startsWith("tcp:") && !channel.startsWith("udp:") - && !channel.startsWith("file:") && channel.contains(':') - && !channel.startsWith('|')) - { - // "Fix" the IPv6 case with host names without '['...']' - if (!channel.startsWith('[') && channel.count(':') >= 2) { - channel.insert(0, '['); - channel.insert(channel.lastIndexOf(':'), ']'); - } - channel = "tcp:" + channel; - } - - if (m_isQnxGdb) - runCommand({"target qnx " + channel, CB(handleTargetQnx)}); - else if (runParameters().useExtendedRemote) - runCommand({"target extended-remote " + channel, CB(handleTargetExtendedRemote)}); - else - runCommand({"target remote " + channel, CB(handleTargetRemote)}); -} - -void GdbRemoteServerEngine::handleTargetRemote(const DebuggerResponse &response) -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - if (response.resultClass == ResultDone) { - // gdb server will stop the remote application itself. - showMessage("INFERIOR STARTED"); - showMessage(msgAttachedToStoppedInferior(), StatusBar); - QString commands = expand(stringSetting(GdbPostAttachCommands)); - if (!commands.isEmpty()) - runCommand({commands, NativeCommand}); - handleInferiorPrepared(); - } else { - // 16^error,msg="hd:5555: Connection timed out." - notifyInferiorSetupFailed(msgConnectRemoteServerFailed(response.data["msg"].data())); - } -} - -void GdbRemoteServerEngine::handleTargetExtendedRemote(const DebuggerResponse &response) -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - if (response.resultClass == ResultDone) { - showMessage("ATTACHED TO GDB SERVER STARTED"); - showMessage(msgAttachedToStoppedInferior(), StatusBar); - QString commands = expand(stringSetting(GdbPostAttachCommands)); - if (!commands.isEmpty()) - runCommand({commands, NativeCommand}); - if (runParameters().attachPID.isValid()) { // attach to pid if valid - // gdb server will stop the remote application itself. - runCommand({"attach " + QString::number(runParameters().attachPID.pid()), - CB(handleTargetExtendedAttach)}); - } else if (!runParameters().inferior.executable.isEmpty()) { - runCommand({"-gdb-set remote exec-file " + runParameters().inferior.executable, - CB(handleTargetExtendedAttach)}); - } else { - const QString title = tr("No Remote Executable or Process ID Specified"); - const QString msg = tr( - "No remote executable could be determined from your build system files.<p>" - "In case you use qmake, consider adding<p>" - " target.path = /tmp/your_executable # path on device<br>" - " INSTALLS += target</p>" - "to your .pro file."); - QMessageBox *mb = showMessageBox(QMessageBox::Critical, title, msg, - QMessageBox::Ok | QMessageBox::Cancel); - mb->button(QMessageBox::Cancel)->setText(tr("Continue Debugging")); - mb->button(QMessageBox::Ok)->setText(tr("Stop Debugging")); - if (mb->exec() == QMessageBox::Ok) { - showMessage("KILLING DEBUGGER AS REQUESTED BY USER"); - notifyInferiorSetupFailed(title); - } else { - showMessage("CONTINUE DEBUGGER AS REQUESTED BY USER"); - handleInferiorPrepared(); // This will likely fail. - } - } - } else { - notifyInferiorSetupFailed(msgConnectRemoteServerFailed(response.data["msg"].data())); - } -} - -void GdbRemoteServerEngine::handleTargetExtendedAttach(const DebuggerResponse &response) -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - if (response.resultClass == ResultDone) { - // gdb server will stop the remote application itself. - handleInferiorPrepared(); - } else { - notifyInferiorSetupFailed(msgConnectRemoteServerFailed(response.data["msg"].data())); - } -} - -void GdbRemoteServerEngine::handleTargetQnx(const DebuggerResponse &response) -{ - QTC_ASSERT(m_isQnxGdb, qDebug() << m_isQnxGdb); - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - if (response.resultClass == ResultDone) { - // gdb server will stop the remote application itself. - showMessage("INFERIOR STARTED"); - showMessage(msgAttachedToStoppedInferior(), StatusBar); - - const DebuggerRunParameters &rp = isMasterEngine() ? runParameters() : masterEngine()->runParameters(); - const QString remoteExecutable = rp.inferior.executable; - if (rp.attachPID.isValid()) - runCommand({"attach " + QString::number(rp.attachPID.pid()), CB(handleAttach)}); - else if (!remoteExecutable.isEmpty()) - runCommand({"set nto-executable " + remoteExecutable, CB(handleSetNtoExecutable)}); - else - handleInferiorPrepared(); - } else { - // 16^error,msg="hd:5555: Connection timed out." - notifyInferiorSetupFailed(response.data["msg"].data()); - } -} - -void GdbRemoteServerEngine::handleAttach(const DebuggerResponse &response) -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - switch (response.resultClass) { - case ResultDone: - case ResultRunning: { - showMessage("INFERIOR ATTACHED"); - showMessage(msgAttachedToStoppedInferior(), StatusBar); - handleInferiorPrepared(); - break; - } - case ResultError: - if (response.data["msg"].data() == "ptrace: Operation not permitted.") { - notifyInferiorSetupFailed(msgPtraceError(runParameters().startMode)); - break; - } - Q_FALLTHROUGH(); // if msg != "ptrace: ..." - default: - notifyInferiorSetupFailed(response.data["msg"].data()); - } -} - -void GdbRemoteServerEngine::handleSetNtoExecutable(const DebuggerResponse &response) -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - switch (response.resultClass) { - case ResultDone: - case ResultRunning: { - showMessage("EXECUTABLE SET"); - showMessage(msgAttachedToStoppedInferior(), StatusBar); - handleInferiorPrepared(); - break; - } - case ResultError: - default: - notifyInferiorSetupFailed(response.data["msg"].data()); - } -} - -void GdbRemoteServerEngine::runEngine() -{ - QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); - - if (runParameters().useContinueInsteadOfRun) { - notifyEngineRunAndInferiorStopOk(); - continueInferiorInternal(); - } else { - runCommand({"-exec-run", DebuggerCommand::RunRequest, CB(handleExecRun)}); - } -} - -void GdbRemoteServerEngine::handleExecRun(const DebuggerResponse &response) -{ - QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); - if (response.resultClass == ResultRunning) { - notifyEngineRunAndInferiorRunOk(); - showMessage("INFERIOR STARTED"); - showMessage(msgInferiorSetupOk(), StatusBar); - } else { - showMessage(response.data["msg"].data()); - notifyEngineRunFailed(); - } -} - -void GdbRemoteServerEngine::interruptInferior2() -{ - QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state()); - if (usesTargetAsync()) { - runCommand({"-exec-interrupt", CB(handleInterruptInferior)}); - } else if (m_isQnxGdb && HostOsInfo::isWindowsHost()) { - m_gdbProc.interrupt(); - } else { - qint64 pid = m_gdbProc.processId(); - bool ok = interruptProcess(pid, GdbEngineType, &m_errorString); - if (!ok) { - // FIXME: Extra state needed? - showMessage("NOTE: INFERIOR STOP NOT POSSIBLE"); - showStatusMessage(tr("Interrupting not possible")); - notifyInferiorRunOk(); - } - } -} - -void GdbRemoteServerEngine::handleInterruptInferior(const DebuggerResponse &response) -{ - if (response.resultClass == ResultDone) { - // The gdb server will trigger extra output that we will pick up - // to do a proper state transition. - } else { - // FIXME: On some gdb versions like git 170ffa5d7dd this produces - // >810^error,msg="mi_cmd_exec_interrupt: Inferior not executing." - notifyInferiorStopOk(); - } -} - -void GdbRemoteServerEngine::shutdownEngine() -{ - notifyAdapterShutdownOk(); -} - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/gdb/remotegdbserveradapter.h b/src/plugins/debugger/gdb/remotegdbserveradapter.h deleted file mode 100644 index a72c3814f7..0000000000 --- a/src/plugins/debugger/gdb/remotegdbserveradapter.h +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "gdbengine.h" - -namespace Debugger { -namespace Internal { - -class GdbRemoteServerEngine : public GdbEngine -{ - Q_OBJECT - -public: - explicit GdbRemoteServerEngine(bool useTerminal); - -private: - void setupEngine() override; - void setupInferior() override; - void runEngine() override; - void interruptInferior2() override; - void shutdownEngine() override; - - void callTargetRemote(); - - void handleSetTargetAsync(const DebuggerResponse &response); - void handleFileExecAndSymbols(const DebuggerResponse &response); - void handleTargetRemote(const DebuggerResponse &response); - void handleTargetExtendedRemote(const DebuggerResponse &response); - void handleTargetExtendedAttach(const DebuggerResponse &response); - void handleTargetQnx(const DebuggerResponse &response); - void handleAttach(const DebuggerResponse &response); - void handleSetNtoExecutable(const DebuggerResponse &response); - void handleInterruptInferior(const DebuggerResponse &response); - void handleExecRun(const DebuggerResponse &response); -}; - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/gdb/termgdbadapter.cpp b/src/plugins/debugger/gdb/termgdbadapter.cpp deleted file mode 100644 index 33a1390b02..0000000000 --- a/src/plugins/debugger/gdb/termgdbadapter.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "termgdbadapter.h" - -#include <debugger/debuggercore.h> -#include <debugger/debuggerprotocol.h> -#include <debugger/debuggerstartparameters.h> -#include <debugger/shared/hostutils.h> - -#include <utils/hostosinfo.h> -#include <utils/qtcassert.h> -#include <coreplugin/icore.h> -#include <coreplugin/messagebox.h> - -using namespace Utils; - -namespace Debugger { -namespace Internal { - -/////////////////////////////////////////////////////////////////////// -// -// TermGdbAdapter -// -/////////////////////////////////////////////////////////////////////// - -GdbTermEngine::GdbTermEngine(bool useTerminal) - : GdbEngine(useTerminal) -{ - if (HostOsInfo::isWindowsHost()) { - // Windows up to xp needs a workaround for attaching to freshly started processes. see proc_stub_win - if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA) - m_stubProc.setMode(ConsoleProcess::Suspend); - else - m_stubProc.setMode(ConsoleProcess::Debug); - } else { - m_stubProc.setMode(ConsoleProcess::Debug); - m_stubProc.setSettings(Core::ICore::settings()); - } -} - -GdbTermEngine::~GdbTermEngine() -{ - m_stubProc.disconnect(); // Avoid spurious state transitions from late exiting stub -} - -void GdbTermEngine::setupEngine() -{ - QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); - showMessage("TRYING TO START ADAPTER"); - -// Currently, adapters are not re-used -// // We leave the console open, so recycle it now. -// m_stubProc.blockSignals(true); -// m_stubProc.stop(); -// m_stubProc.blockSignals(false); - - m_stubProc.setWorkingDirectory(runParameters().inferior.workingDirectory); - // Set environment + dumper preload. - m_stubProc.setEnvironment(runParameters().stubEnvironment); - - connect(&m_stubProc, &ConsoleProcess::processError, - this, &GdbTermEngine::stubError); - connect(&m_stubProc, &ConsoleProcess::processStarted, - this, &GdbTermEngine::stubStarted); - connect(&m_stubProc, &ConsoleProcess::stubStopped, - this, &GdbTermEngine::stubExited); - // FIXME: Starting the stub implies starting the inferior. This is - // fairly unclean as far as the state machine and error reporting go. - - if (!m_stubProc.start(runParameters().inferior.executable, - runParameters().inferior.commandLineArguments)) { - // Error message for user is delivered via a signal. - handleAdapterStartFailed(QString()); - return; - } -} - -void GdbTermEngine::stubStarted() -{ - startGdb(); -} - -void GdbTermEngine::handleGdbStartFailed() -{ - m_stubProc.stop(); -} - -void GdbTermEngine::setupInferior() -{ - QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); - const qint64 attachedPID = m_stubProc.applicationPID(); - const qint64 attachedMainThreadID = m_stubProc.applicationMainThreadID(); - notifyInferiorPid(ProcessHandle(attachedPID)); - const QString msg = (attachedMainThreadID != -1) - ? QString("Going to attach to %1 (%2)").arg(attachedPID).arg(attachedMainThreadID) - : QString("Going to attach to %1").arg(attachedPID); - showMessage(msg, LogMisc); - handleInferiorPrepared(); -} - -void GdbTermEngine::runEngine() -{ - QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); - const qint64 attachedPID = m_stubProc.applicationPID(); - runCommand({"attach " + QString::number(attachedPID), - [this](const DebuggerResponse &r) { handleStubAttached(r); }}); -} - -void GdbTermEngine::handleStubAttached(const DebuggerResponse &response) -{ - // InferiorStopOk can happen if the "*stopped" in response to the - // 'attach' comes in before its '^done' - QTC_ASSERT(state() == EngineRunRequested || state() == InferiorStopOk, - qDebug() << state()); - - switch (response.resultClass) { - case ResultDone: - case ResultRunning: - if (runParameters().toolChainAbi.os() == ProjectExplorer::Abi::WindowsOS) { - QString errorMessage; - // Resume thread that was suspended by console stub process (see stub code). - const qint64 mainThreadId = m_stubProc.applicationMainThreadID(); - if (winResumeThread(mainThreadId, &errorMessage)) { - showMessage(QString("Inferior attached, thread %1 resumed"). - arg(mainThreadId), LogMisc); - } else { - showMessage(QString("Inferior attached, unable to resume thread %1: %2"). - arg(mainThreadId).arg(errorMessage), - LogWarning); - } - notifyEngineRunAndInferiorStopOk(); - continueInferiorInternal(); - } else { - showMessage("INFERIOR ATTACHED AND RUNNING"); - //notifyEngineRunAndInferiorRunOk(); - // Wait for the upcoming *stopped and handle it there. - } - break; - case ResultError: - if (response.data["msg"].data() == "ptrace: Operation not permitted.") { - showMessage(msgPtraceError(runParameters().startMode)); - notifyEngineRunFailed(); - break; - } - showMessage(response.data["msg"].data()); - notifyEngineIll(); - break; - default: - showMessage(QString("Invalid response %1").arg(response.resultClass)); - notifyEngineIll(); - break; - } -} - -void GdbTermEngine::interruptInferior2() -{ - interruptLocalInferior(inferiorPid()); -} - -void GdbTermEngine::stubError(const QString &msg) -{ - Core::AsynchronousMessageBox::critical(tr("Debugger Error"), msg); - notifyEngineIll(); -} - -void GdbTermEngine::stubExited() -{ - if (state() == EngineShutdownRequested || state() == DebuggerFinished) { - showMessage("STUB EXITED EXPECTEDLY"); - return; - } - showMessage("STUB EXITED"); - notifyEngineIll(); -} - -void GdbTermEngine::shutdownEngine() -{ - notifyAdapterShutdownOk(); -} - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/gdb/termgdbadapter.h b/src/plugins/debugger/gdb/termgdbadapter.h deleted file mode 100644 index 73a2413637..0000000000 --- a/src/plugins/debugger/gdb/termgdbadapter.h +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "gdbengine.h" - -#include <utils/consoleprocess.h> - -namespace Debugger { -namespace Internal { - -/////////////////////////////////////////////////////////////////////// -// -// TermGdbAdapter -// -/////////////////////////////////////////////////////////////////////// - -class GdbTermEngine : public GdbEngine -{ - Q_OBJECT - -public: - explicit GdbTermEngine(bool useTerminal); - ~GdbTermEngine() override; - -private: - void setupEngine() override; - void handleGdbStartFailed() override; - void setupInferior() override; - void runEngine() override; - void interruptInferior2() override; - void shutdownEngine() override; - - void handleStubAttached(const DebuggerResponse &response); - - void stubStarted(); - void stubExited(); - void stubError(const QString &msg); - - Utils::ConsoleProcess m_stubProc; -}; - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/loadcoredialog.cpp b/src/plugins/debugger/loadcoredialog.cpp index d0e8d87636..cb0fb49cee 100644 --- a/src/plugins/debugger/loadcoredialog.cpp +++ b/src/plugins/debugger/loadcoredialog.cpp @@ -28,7 +28,7 @@ #include "debuggerstartparameters.h" #include "debuggerdialogs.h" #include "debuggerkitinformation.h" -#include "gdb/coregdbadapter.h" +#include "gdb/gdbengine.h" #include <projectexplorer/kitinformation.h> #include <projectexplorer/projectexplorerconstants.h> @@ -364,7 +364,7 @@ void AttachCoreDialog::coreFileChanged(const QString &core) Kit *k = d->kitChooser->currentKit(); QTC_ASSERT(k, return); StandardRunnable debugger = DebuggerKitInformation::runnable(k); - GdbCoreEngine::CoreInfo cinfo = GdbCoreEngine::readExecutableNameFromCore(debugger, core); + CoreInfo cinfo = CoreInfo::readExecutableNameFromCore(debugger, core); if (!cinfo.foundExecutableName.isEmpty()) d->localExecFileName->setFileName(FileName::fromString(cinfo.foundExecutableName)); else if (!d->localExecFileName->isValid() && !cinfo.rawStringFromCore.isEmpty()) |