Debugger: Consolidate "Attach to running process"

Change-Id: I78e89a662140f37f5f9719dbbbff070f1e2fbe84
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
hjk
2017-10-19 08:29:20 +02:00
parent 6a470f0a70
commit 3d11a27ad0
10 changed files with 139 additions and 372 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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