startgdbserverdialog.cpp 7.9 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Eike Ziller's avatar
Eike Ziller committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
Eike Ziller's avatar
Eike Ziller committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
Eike Ziller's avatar
Eike Ziller committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12
** 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 Digia.  For licensing terms and
Eike Ziller's avatar
Eike Ziller committed
13 14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
Eike Ziller's avatar
Eike Ziller committed
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24 25 26
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
Eike Ziller's avatar
Eike Ziller committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
Eike Ziller's avatar
Eike Ziller committed
30

31 32
#include "startgdbserverdialog.h"

33 34 35
#include <debugger/debuggermainwindow.h>
#include <debugger/debuggerplugin.h>
#include <debugger/debuggerkitinformation.h>
36
#include <debugger/debuggerruncontrol.h>
37
#include <debugger/debuggerstartparameters.h>
38

39
#include <coreplugin/icore.h>
40
#include <coreplugin/messagebox.h>
Tobias Hunger's avatar
Tobias Hunger committed
41
#include <projectexplorer/kitchooser.h>
42
#include <projectexplorer/devicesupport/deviceprocesslist.h>
43
#include <projectexplorer/devicesupport/deviceprocessesdialog.h>
44
#include <projectexplorer/devicesupport/deviceusedportsgatherer.h>
45
#include <ssh/sshremoteprocessrunner.h>
Kai Koehne's avatar
Kai Koehne committed
46
#include <utils/portlist.h>
47
#include <utils/qtcassert.h>
48

Friedemann Kleint's avatar
Friedemann Kleint committed
49
#include <QFileInfo>
50 51

using namespace Core;
52
using namespace ProjectExplorer;
53
using namespace QSsh;
54 55
using namespace Utils;

56

57
namespace Debugger {
58 59 60 61 62
namespace Internal {

class StartGdbServerDialogPrivate
{
public:
63
    StartGdbServerDialogPrivate() : dialog(0), kit(0) {}
64

65
    DeviceProcessesDialog *dialog;
66
    bool startServerOnly;
67
    DeviceProcessItem process;
Tobias Hunger's avatar
Tobias Hunger committed
68
    Kit *kit;
69
    IDevice::ConstPtr device;
70

71
    DeviceUsedPortsGatherer gatherer;
72
    SshRemoteProcessRunner runner;
73 74
};

75 76
GdbServerStarter::GdbServerStarter(DeviceProcessesDialog *dlg, bool startServerOnly)
  : QObject(dlg)
77
{
78 79
    d = new StartGdbServerDialogPrivate;
    d->dialog = dlg;
Tobias Hunger's avatar
Tobias Hunger committed
80
    d->kit = dlg->kitChooser()->currentKit();
81
    d->process = dlg->currentProcess();
Tobias Hunger's avatar
Tobias Hunger committed
82
    d->device = DeviceKitInformation::device(d->kit);
83
    d->startServerOnly = startServerOnly;
84 85
}

86
GdbServerStarter::~GdbServerStarter()
87 88 89 90
{
    delete d;
}

91
void GdbServerStarter::handleRemoteError(const QString &errorMsg)
92
{
93
    Core::AsynchronousMessageBox::critical(tr("Remote Error"), errorMsg);
94 95
}

96
void GdbServerStarter::portGathererError(const QString &text)
97
{
98 99 100
    logMessage(tr("Could not retrieve list of free ports:"));
    logMessage(text);
    logMessage(tr("Process aborted"));
101 102
}

103
void GdbServerStarter::run()
104
{
105 106 107 108
    QTC_ASSERT(d->device, return);
    connect(&d->gatherer, SIGNAL(error(QString)), SLOT(portGathererError(QString)));
    connect(&d->gatherer, SIGNAL(portListReady()), SLOT(portListReady()));
    d->gatherer.start(d->device);
109 110
}

111
void GdbServerStarter::portListReady()
112
{
113
    PortList ports = d->device->freePorts();
114
    const int port = d->gatherer.getNextFreePort(&ports);
115
    if (port == -1) {
116 117
        QTC_ASSERT(false, /**/);
        emit logMessage(tr("Process aborted"));
118
        return;
119
    }
120

121 122 123 124 125
    connect(&d->runner, SIGNAL(connectionError()), SLOT(handleConnectionError()));
    connect(&d->runner, SIGNAL(processStarted()), SLOT(handleProcessStarted()));
    connect(&d->runner, SIGNAL(readyReadStandardOutput()), SLOT(handleProcessOutputAvailable()));
    connect(&d->runner, SIGNAL(readyReadStandardError()), SLOT(handleProcessErrorOutput()));
    connect(&d->runner, SIGNAL(processClosed(int)), SLOT(handleProcessClosed(int)));
126

127 128 129 130
    QByteArray gdbServerPath = d->device->debugServerPath().toUtf8();
    if (gdbServerPath.isEmpty())
        gdbServerPath = "gdbserver";
    QByteArray cmd = gdbServerPath + " --attach :"
131
            + QByteArray::number(port) + ' ' + QByteArray::number(d->process.pid);
132 133
    logMessage(tr("Running command: %1").arg(QString::fromLatin1(cmd)));
    d->runner.run(cmd, d->device->sshParameters());
134 135
}

136
void GdbServerStarter::handleConnectionError()
137
{
138
    logMessage(tr("Connection error: %1").arg(d->runner.lastConnectionErrorString()));
139 140
}

141
void GdbServerStarter::handleProcessStarted()
142
{
143
    logMessage(tr("Starting gdbserver..."));
144 145
}

146
void GdbServerStarter::handleProcessOutputAvailable()
147
{
148
    logMessage(QString::fromUtf8(d->runner.readAllStandardOutput().trimmed()));
149 150
}

151
void GdbServerStarter::handleProcessErrorOutput()
152
{
153
    const QByteArray ba = d->runner.readAllStandardError();
154
    logMessage(QString::fromUtf8(ba.trimmed()));
155 156
    // "Attached; pid = 16740"
    // "Listening on port 10000"
157 158 159
    foreach (const QByteArray &line, ba.split('\n')) {
        if (line.startsWith("Listening on port")) {
            const int port = line.mid(18).trimmed().toInt();
160 161 162 163 164
            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->startServerOnly)
                attach(port);
165 166
        }
    }
167 168
}

169
void GdbServerStarter::attach(int port)
170
{
Tobias Hunger's avatar
Tobias Hunger committed
171
    QString sysroot = SysRootKitInformation::sysRoot(d->kit).toString();
172 173 174
    QString binary;
    QString localExecutable;
    QString candidate = sysroot + d->process.exe;
175
    if (QFileInfo::exists(candidate))
176 177 178 179
        localExecutable = candidate;
    if (localExecutable.isEmpty()) {
        binary = d->process.cmdLine.section(QLatin1Char(' '), 0, 0);
        candidate = sysroot + QLatin1Char('/') + binary;
180
        if (QFileInfo::exists(candidate))
181
            localExecutable = candidate;
182 183 184
    }
    if (localExecutable.isEmpty()) {
        candidate = sysroot + QLatin1String("/usr/bin/") + binary;
185
        if (QFileInfo::exists(candidate))
186 187 188 189
            localExecutable = candidate;
    }
    if (localExecutable.isEmpty()) {
        candidate = sysroot + QLatin1String("/bin/") + binary;
190
        if (QFileInfo::exists(candidate))
191 192 193
            localExecutable = candidate;
    }
    if (localExecutable.isEmpty()) {
194
        Core::AsynchronousMessageBox::warning(tr("Warning"),
195 196 197 198
            tr("Cannot find local executable for remote process \"%1\".")
                .arg(d->process.exe));
        return;
    }
199

200 201
    QList<Abi> abis = Abi::abisOfBinary(Utils::FileName::fromString(localExecutable));
    if (abis.isEmpty()) {
202
        Core::AsynchronousMessageBox::warning(tr("Warning"),
203 204 205 206
            tr("Cannot find ABI for remote process \"%1\".")
                .arg(d->process.exe));
        return;
    }
207

208
    DebuggerStartParameters sp;
209 210
    bool res = DebuggerRunControlFactory::fillParametersFromKit(&sp, d->kit);
    QTC_ASSERT(res, return);
hjk's avatar
hjk committed
211 212
    sp.masterEngineType = GdbEngineType;
    sp.connParams.port = port;
213
    sp.remoteChannel = sp.connParams.host + QLatin1Char(':') + QString::number(sp.connParams.port);
hjk's avatar
hjk committed
214
    sp.displayName = tr("Remote: \"%1:%2\"").arg(sp.connParams.host).arg(port);
215 216 217
    sp.executable = localExecutable;
    sp.startMode = AttachToRemoteServer;
    sp.closeMode = KillAtClose;
hjk's avatar
hjk committed
218
    DebuggerRunControlFactory::createAndScheduleRun(sp);
219 220
}

221
void GdbServerStarter::handleProcessClosed(int status)
222
{
223
    logMessage(tr("Process gdbserver finished. Status: %1").arg(status));
224 225
}

226
void GdbServerStarter::logMessage(const QString &line)
227
{
228
    d->dialog->logMessage(line);
229 230
}

231
} // namespace Internal
232
} // namespace Debugger