forked from qt-creator/qt-creator
Debugger: Consolidate "Attach to running process"
Change-Id: I78e89a662140f37f5f9719dbbbff070f1e2fbe84 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
HEADERS += \
|
||||
$$PWD/gdbengine.h \
|
||||
$$PWD/startgdbserverdialog.h
|
||||
$$PWD/gdbengine.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/gdbengine.cpp \
|
||||
$$PWD/gdboptionspage.cpp \
|
||||
$$PWD/startgdbserverdialog.cpp
|
||||
$$PWD/gdboptionspage.cpp
|
||||
|
||||
@@ -1330,11 +1330,12 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
|
||||
notifyInferiorStopOk();
|
||||
} else if (state() == EngineRunRequested) {
|
||||
// This is gdb 7+'s initial *stopped in response to attach that
|
||||
// appears before the ^done is seen.
|
||||
// appears before the ^done is seen for local setups.
|
||||
notifyEngineRunAndInferiorStopOk();
|
||||
if (terminal())
|
||||
if (terminal()) {
|
||||
continueInferiorInternal();
|
||||
return;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
QTC_CHECK(false);
|
||||
}
|
||||
@@ -4267,7 +4268,11 @@ void GdbEngine::setupInferior()
|
||||
|
||||
const DebuggerRunParameters &rp = runParameters();
|
||||
|
||||
if (isAttachEngine()) {
|
||||
if (rp.startMode == AttachToRemoteProcess) {
|
||||
|
||||
notifyInferiorSetupOk();
|
||||
|
||||
} else if (isAttachEngine()) {
|
||||
// Task 254674 does not want to remove them
|
||||
//qq->breakHandler()->removeAllBreakpoints();
|
||||
handleInferiorPrepared();
|
||||
@@ -4377,7 +4382,6 @@ void GdbEngine::setupInferior()
|
||||
} else if (isPlainEngine()) {
|
||||
|
||||
setEnvironmentVariables();
|
||||
const DebuggerRunParameters &rp = runParameters();
|
||||
if (!rp.inferior.workingDirectory.isEmpty())
|
||||
runCommand({"cd " + rp.inferior.workingDirectory});
|
||||
if (!rp.inferior.commandLineArguments.isEmpty()) {
|
||||
@@ -4395,9 +4399,18 @@ void GdbEngine::runEngine()
|
||||
{
|
||||
CHECK_STATE(EngineRunRequested);
|
||||
|
||||
if (isAttachEngine()) {
|
||||
const DebuggerRunParameters &rp = runParameters();
|
||||
|
||||
const qint64 pid = runParameters().attachPID.pid();
|
||||
if (rp.startMode == AttachToRemoteProcess) {
|
||||
|
||||
notifyEngineRunAndInferiorStopOk();
|
||||
|
||||
QString channel = rp.remoteChannel;
|
||||
runCommand({"target remote " + channel});
|
||||
|
||||
} else if (isAttachEngine()) {
|
||||
|
||||
const qint64 pid = rp.attachPID.pid();
|
||||
showStatusMessage(tr("Attaching to process %1.").arg(pid));
|
||||
runCommand({"attach " + QString::number(pid),
|
||||
[this](const DebuggerResponse &r) { handleAttach(r); }});
|
||||
@@ -4460,6 +4473,7 @@ void GdbEngine::handleAttach(const DebuggerResponse &response)
|
||||
// InferiorStopOk, e.g. for "Attach to running application".
|
||||
// The *stopped came in between sending the 'attach' and
|
||||
// receiving its '^done'.
|
||||
notifyEngineRunAndInferiorStopOk();
|
||||
if (runParameters().continueAfterAttach)
|
||||
continueInferiorInternal();
|
||||
}
|
||||
@@ -4663,6 +4677,7 @@ void GdbEngine::handleSetTargetAsync(const DebuggerResponse &response)
|
||||
|
||||
void GdbEngine::callTargetRemote()
|
||||
{
|
||||
CHECK_STATE(InferiorSetupRequested);
|
||||
QString channel = runParameters().remoteChannel;
|
||||
|
||||
// Don't touch channels with explicitly set protocols.
|
||||
|
||||
@@ -1,234 +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 "startgdbserverdialog.h"
|
||||
|
||||
#include <debugger/debuggerengine.h>
|
||||
#include <debugger/debuggermainwindow.h>
|
||||
#include <debugger/debuggerplugin.h>
|
||||
#include <debugger/debuggerkitinformation.h>
|
||||
#include <debugger/debuggerruncontrol.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/messagebox.h>
|
||||
#include <projectexplorer/kitchooser.h>
|
||||
#include <projectexplorer/devicesupport/deviceprocesslist.h>
|
||||
#include <projectexplorer/devicesupport/deviceprocessesdialog.h>
|
||||
#include <projectexplorer/devicesupport/deviceusedportsgatherer.h>
|
||||
#include <ssh/sshremoteprocessrunner.h>
|
||||
#include <utils/portlist.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
using namespace Core;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace QSsh;
|
||||
using namespace Utils;
|
||||
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class StartGdbServerDialogPrivate
|
||||
{
|
||||
public:
|
||||
StartGdbServerDialogPrivate() : dialog(0), kit(0) {}
|
||||
|
||||
DeviceProcessesDialog *dialog;
|
||||
bool attachToServer;
|
||||
DeviceProcessItem process;
|
||||
Kit *kit;
|
||||
IDevice::ConstPtr device;
|
||||
|
||||
DeviceUsedPortsGatherer gatherer;
|
||||
SshRemoteProcessRunner runner;
|
||||
};
|
||||
|
||||
GdbServerStarter::GdbServerStarter(DeviceProcessesDialog *dlg, bool attachAfterServerStart)
|
||||
: QObject(dlg)
|
||||
{
|
||||
d = new StartGdbServerDialogPrivate;
|
||||
d->dialog = dlg;
|
||||
d->kit = dlg->kitChooser()->currentKit();
|
||||
d->process = dlg->currentProcess();
|
||||
d->device = DeviceKitInformation::device(d->kit);
|
||||
d->attachToServer = attachAfterServerStart;
|
||||
}
|
||||
|
||||
GdbServerStarter::~GdbServerStarter()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void GdbServerStarter::handleRemoteError(const QString &errorMsg)
|
||||
{
|
||||
AsynchronousMessageBox::critical(tr("Remote Error"), errorMsg);
|
||||
}
|
||||
|
||||
void GdbServerStarter::portGathererError(const QString &text)
|
||||
{
|
||||
logMessage(tr("Could not retrieve list of free ports:"));
|
||||
logMessage(text);
|
||||
logMessage(tr("Process aborted"));
|
||||
}
|
||||
|
||||
void GdbServerStarter::run()
|
||||
{
|
||||
QTC_ASSERT(d->device, return);
|
||||
connect(&d->gatherer, &DeviceUsedPortsGatherer::error,
|
||||
this, &GdbServerStarter::portGathererError);
|
||||
connect(&d->gatherer, &DeviceUsedPortsGatherer::portListReady,
|
||||
this, &GdbServerStarter::portListReady);
|
||||
d->gatherer.start(d->device);
|
||||
}
|
||||
|
||||
void GdbServerStarter::portListReady()
|
||||
{
|
||||
PortList ports = d->device->freePorts();
|
||||
const Port port = d->gatherer.getNextFreePort(&ports);
|
||||
if (!port.isValid()) {
|
||||
QTC_ASSERT(false, /**/);
|
||||
emit logMessage(tr("Process aborted"));
|
||||
return;
|
||||
}
|
||||
|
||||
connect(&d->runner, &SshRemoteProcessRunner::connectionError,
|
||||
this, &GdbServerStarter::handleConnectionError);
|
||||
connect(&d->runner, &SshRemoteProcessRunner::processStarted,
|
||||
this, &GdbServerStarter::handleProcessStarted);
|
||||
connect(&d->runner, &SshRemoteProcessRunner::readyReadStandardOutput,
|
||||
this, &GdbServerStarter::handleProcessOutputAvailable);
|
||||
connect(&d->runner, &SshRemoteProcessRunner::readyReadStandardError,
|
||||
this, &GdbServerStarter::handleProcessErrorOutput);
|
||||
connect(&d->runner, &SshRemoteProcessRunner::processClosed,
|
||||
this, &GdbServerStarter::handleProcessClosed);
|
||||
|
||||
QByteArray gdbServerPath = d->device->debugServerPath().toUtf8();
|
||||
if (gdbServerPath.isEmpty())
|
||||
gdbServerPath = "gdbserver";
|
||||
QByteArray cmd = gdbServerPath + " --attach :"
|
||||
+ QByteArray::number(port.number()) + ' ' + QByteArray::number(d->process.pid);
|
||||
logMessage(tr("Running command: %1").arg(QString::fromLatin1(cmd)));
|
||||
d->runner.run(cmd, d->device->sshParameters());
|
||||
}
|
||||
|
||||
void GdbServerStarter::handleConnectionError()
|
||||
{
|
||||
logMessage(tr("Connection error: %1").arg(d->runner.lastConnectionErrorString()));
|
||||
}
|
||||
|
||||
void GdbServerStarter::handleProcessStarted()
|
||||
{
|
||||
logMessage(tr("Starting gdbserver..."));
|
||||
}
|
||||
|
||||
void GdbServerStarter::handleProcessOutputAvailable()
|
||||
{
|
||||
logMessage(QString::fromUtf8(d->runner.readAllStandardOutput().trimmed()));
|
||||
}
|
||||
|
||||
void GdbServerStarter::handleProcessErrorOutput()
|
||||
{
|
||||
const QByteArray ba = d->runner.readAllStandardError();
|
||||
logMessage(QString::fromUtf8(ba.trimmed()));
|
||||
// "Attached; pid = 16740"
|
||||
// "Listening on port 10000"
|
||||
foreach (const QByteArray &line, ba.split('\n')) {
|
||||
if (line.startsWith("Listening on port")) {
|
||||
const int port = line.mid(18).trimmed().toInt();
|
||||
logMessage(tr("Port %1 is now accessible.").arg(port));
|
||||
logMessage(tr("Server started on %1:%2")
|
||||
.arg(d->device->sshParameters().host).arg(port));
|
||||
if (d->attachToServer)
|
||||
attach(port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GdbServerStarter::attach(int port)
|
||||
{
|
||||
QString sysroot = SysRootKitInformation::sysRoot(d->kit).toString();
|
||||
QString binary;
|
||||
QString localExecutable;
|
||||
QString candidate = sysroot + d->process.exe;
|
||||
if (QFileInfo::exists(candidate))
|
||||
localExecutable = candidate;
|
||||
if (localExecutable.isEmpty()) {
|
||||
binary = d->process.cmdLine.section(QLatin1Char(' '), 0, 0);
|
||||
candidate = sysroot + QLatin1Char('/') + binary;
|
||||
if (QFileInfo::exists(candidate))
|
||||
localExecutable = candidate;
|
||||
}
|
||||
if (localExecutable.isEmpty()) {
|
||||
candidate = sysroot + QLatin1String("/usr/bin/") + binary;
|
||||
if (QFileInfo::exists(candidate))
|
||||
localExecutable = candidate;
|
||||
}
|
||||
if (localExecutable.isEmpty()) {
|
||||
candidate = sysroot + QLatin1String("/bin/") + binary;
|
||||
if (QFileInfo::exists(candidate))
|
||||
localExecutable = candidate;
|
||||
}
|
||||
if (localExecutable.isEmpty()) {
|
||||
AsynchronousMessageBox::warning(tr("Warning"),
|
||||
tr("Cannot find local executable for remote process \"%1\".")
|
||||
.arg(d->process.exe));
|
||||
return;
|
||||
}
|
||||
|
||||
QList<Abi> abis = Abi::abisOfBinary(FileName::fromString(localExecutable));
|
||||
if (abis.isEmpty()) {
|
||||
AsynchronousMessageBox::warning(tr("Warning"),
|
||||
tr("Cannot find ABI for remote process \"%1\".")
|
||||
.arg(d->process.exe));
|
||||
return;
|
||||
}
|
||||
|
||||
QString remoteChannel = QString("%1:%2").arg(d->device->sshParameters().host).arg(port);
|
||||
|
||||
auto runControl = new RunControl(nullptr, ProjectExplorer::Constants::DEBUG_RUN_MODE);
|
||||
auto debugger = new DebuggerRunTool(runControl, d->kit);
|
||||
debugger->setRemoteChannel(remoteChannel);
|
||||
debugger->setRunControlName(tr("Remote: \"%1\"").arg(remoteChannel));
|
||||
debugger->setInferiorExecutable(localExecutable);
|
||||
debugger->setStartMode(AttachToRemoteServer);
|
||||
debugger->setCloseMode(KillAtClose);
|
||||
|
||||
debugger->startRunControl();
|
||||
}
|
||||
|
||||
void GdbServerStarter::handleProcessClosed(int status)
|
||||
{
|
||||
logMessage(tr("Process gdbserver finished. Status: %1").arg(status));
|
||||
}
|
||||
|
||||
void GdbServerStarter::logMessage(const QString &line)
|
||||
{
|
||||
d->dialog->logMessage(line);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -1,65 +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 <QObject>
|
||||
|
||||
namespace ProjectExplorer { class DeviceProcessesDialog; }
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class StartGdbServerDialogPrivate;
|
||||
|
||||
class GdbServerStarter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GdbServerStarter(ProjectExplorer::DeviceProcessesDialog *dlg,
|
||||
bool attachAfterServerStart);
|
||||
~GdbServerStarter();
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
void handleRemoteError(const QString &errorMessage);
|
||||
void portGathererError(const QString &errorMessage);
|
||||
void portListReady();
|
||||
|
||||
void handleProcessClosed(int);
|
||||
void handleProcessErrorOutput();
|
||||
void handleProcessOutputAvailable();
|
||||
void handleProcessStarted();
|
||||
void handleConnectionError();
|
||||
|
||||
void attach(int port);
|
||||
void logMessage(const QString &line);
|
||||
StartGdbServerDialogPrivate *d;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
Reference in New Issue
Block a user