remotegdbserveradapter.cpp 11.5 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
8
9
10
11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
30
31
32
**
**************************************************************************/

ck's avatar
ck committed
33
#include "remotegdbserveradapter.h"
hjk's avatar
hjk committed
34

Friedemann Kleint's avatar
Friedemann Kleint committed
35
#include "debuggerstartparameters.h"
hjk's avatar
hjk committed
36
#include "debuggercore.h"
37
#include "debuggerstringutils.h"
38
#include "gdbengine.h"
hjk's avatar
hjk committed
39
#include "gdbmi.h"
40
41

#include <utils/qtcassert.h>
42
#include <utils/fancymainwindow.h>
43
#include <projectexplorer/abi.h>
44
45
46
47
48
49
50
51

#include <QtCore/QFileInfo>
#include <QtGui/QMessageBox>

namespace Debugger {
namespace Internal {

#define CB(callback) \
ck's avatar
ck committed
52
    static_cast<GdbEngine::AdapterCallback>(&RemoteGdbServerAdapter::callback), \
53
54
55
56
57
58
59
60
    STRINGIFY(callback)

///////////////////////////////////////////////////////////////////////
//
// RemoteGdbAdapter
//
///////////////////////////////////////////////////////////////////////

hjk's avatar
hjk committed
61
62
RemoteGdbServerAdapter::RemoteGdbServerAdapter(GdbEngine *engine)
    : AbstractGdbAdapter(engine)
63
{
64
    connect(&m_uploadProc, SIGNAL(error(QProcess::ProcessError)),
hjk's avatar
hjk committed
65
        SLOT(uploadProcError(QProcess::ProcessError)));
66
    connect(&m_uploadProc, SIGNAL(readyReadStandardOutput()),
hjk's avatar
hjk committed
67
        SLOT(readUploadStandardOutput()));
68
    connect(&m_uploadProc, SIGNAL(readyReadStandardError()),
hjk's avatar
hjk committed
69
70
        SLOT(readUploadStandardError()));
    connect(&m_uploadProc, SIGNAL(finished(int)),
ck's avatar
ck committed
71
        SLOT(uploadProcFinished()));
72
73
}

ck's avatar
ck committed
74
AbstractGdbAdapter::DumperHandling RemoteGdbServerAdapter::dumperHandling() const
75
{
hjk's avatar
hjk committed
76
77
78
79
80
    using namespace ProjectExplorer;
    const Abi abi = startParameters().toolChainAbi;
    if (abi.os() == Abi::SymbianOS
            || abi.os() == Abi::WindowsOS
            || abi.binaryFormat() == Abi::ElfFormat)
81
82
83
84
        return DumperLoadedByGdb;
    return DumperLoadedByGdbPreload;
}

ck's avatar
ck committed
85
void RemoteGdbServerAdapter::startAdapter()
86
{
hjk's avatar
hjk committed
87
    QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
88
    showMessage(_("TRYING TO START ADAPTER"));
hjk's avatar
hjk committed
89
90
91
92
    if (!startParameters().useServerStartScript) {
        handleSetupDone();
        return;
    }
93
    if (startParameters().serverStartScript.isEmpty()) {
ck's avatar
ck committed
94
        showMessage(_("No server start script given. "), StatusBar);
95
        m_engine->requestRemoteSetup();
hjk's avatar
hjk committed
96
    } else {
97
        m_uploadProc.start(_("/bin/sh ") + startParameters().serverStartScript);
hjk's avatar
hjk committed
98
99
        m_uploadProc.waitForStarted();
    }
100
101
}

ck's avatar
ck committed
102
void RemoteGdbServerAdapter::uploadProcError(QProcess::ProcessError error)
103
104
105
106
{
    QString msg;
    switch (error) {
        case QProcess::FailedToStart:
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
107
            msg = tr("The upload process failed to start. Shell missing?");
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
            break;
        case QProcess::Crashed:
            msg = tr("The upload process crashed some time after starting "
                "successfully.");
            break;
        case QProcess::Timedout:
            msg = tr("The last waitFor...() function timed out. "
                "The state of QProcess is unchanged, and you can try calling "
                "waitFor...() again.");
            break;
        case QProcess::WriteError:
            msg = tr("An error occurred when attempting to write "
                "to the upload process. For example, the process may not be running, "
                "or it may have closed its input channel.");
            break;
        case QProcess::ReadError:
            msg = tr("An error occurred when attempting to read from "
                "the upload process. For example, the process may not be running.");
            break;
        default:
            msg = tr("An unknown error in the upload process occurred. "
                "This is the default return value of error().");
    }

132
    showMessage(msg, StatusBar);
hjk's avatar
hjk committed
133
    showMessageBox(QMessageBox::Critical, tr("Error"), msg);
134
135
}

ck's avatar
ck committed
136
void RemoteGdbServerAdapter::readUploadStandardOutput()
137
{
138
139
140
141
    const QByteArray ba = m_uploadProc.readAllStandardOutput();
    const QString msg = QString::fromLocal8Bit(ba, ba.length());
    showMessage(msg, LogOutput);
    showMessage(msg, AppOutput);
142
143
}

ck's avatar
ck committed
144
void RemoteGdbServerAdapter::readUploadStandardError()
145
{
146
147
148
149
    const QByteArray ba = m_uploadProc.readAllStandardError();
    const QString msg = QString::fromLocal8Bit(ba, ba.length());
    showMessage(msg, LogOutput);
    showMessage(msg, AppError);
150
151
}

ck's avatar
ck committed
152
153
154
155
156
157
void RemoteGdbServerAdapter::uploadProcFinished()
{
    if (m_uploadProc.exitStatus() == QProcess::NormalExit
        && m_uploadProc.exitCode() == 0)
        handleSetupDone();
    else
158
        handleRemoteSetupFailed(m_uploadProc.errorString());
ck's avatar
ck committed
159
160
}

hjk's avatar
hjk committed
161
void RemoteGdbServerAdapter::setupInferior()
162
{
hjk's avatar
hjk committed
163
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
hjk's avatar
hjk committed
164
    const DebuggerStartParameters &sp = startParameters();
165

166
    QString fileName;
hjk's avatar
hjk committed
167
168
    if (!sp.executable.isEmpty()) {
        QFileInfo fi(sp.executable);
169
170
        fileName = fi.absoluteFilePath();
    }
hjk's avatar
hjk committed
171
172
173
174
    const QByteArray sysroot = sp.sysroot.toLocal8Bit();
    const QByteArray remoteArch = sp.remoteArchitecture.toLatin1();
    const QByteArray gnuTarget = sp.gnuTarget.toLatin1();
    const QString args = sp.processArgs;
175
176
177

    if (!remoteArch.isEmpty())
        m_engine->postCommand("set architecture " + remoteArch);
178
179
    if (!gnuTarget.isEmpty())
        m_engine->postCommand("set gnutarget " + gnuTarget);
180
181
    if (!sysroot.isEmpty())
        m_engine->postCommand("set sysroot " + sysroot);
182
    if (!args.isEmpty())
183
        m_engine->postCommand("-exec-arguments " + args.toLocal8Bit());
184

185
186
    // 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
187
    // loaded.  Use the "file" command.' as gdb tries to set the
188
189
190
191
192
193
194
195
196
197
198
    // 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..
hjk's avatar
hjk committed
199
200
201
202
203
204


    // ~"/build/buildd/gdb-7.2/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,[...]
    //m_engine->postCommand("set target-async on", CB(handleSetTargetAsync));
205
206
207
208
209
210
211

    if (fileName.isEmpty()) {
        showMessage(tr("No symbol file given."), StatusBar);
        callTargetRemote();
        return;
    }

212
213
    m_engine->postCommand("-file-exec-and-symbols \""
        + fileName.toLocal8Bit() + '"',
hjk's avatar
hjk committed
214
        CB(handleFileExecAndSymbols));
215
216
}

ck's avatar
ck committed
217
void RemoteGdbServerAdapter::handleSetTargetAsync(const GdbResponse &response)
218
{
hjk's avatar
hjk committed
219
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
220
221
    if (response.resultClass == GdbResultError)
        qDebug() << "Adapter too old: does not support asynchronous mode.";
222
223
}

ck's avatar
ck committed
224
void RemoteGdbServerAdapter::handleFileExecAndSymbols(const GdbResponse &response)
225
{
hjk's avatar
hjk committed
226
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
227
    if (response.resultClass == GdbResultDone) {
228
        callTargetRemote();
229
    } else {
230
        QString msg = tr("Reading debug information failed:\n");
231
        msg += QString::fromLocal8Bit(response.data.findChild("msg").data());
hjk's avatar
hjk committed
232
        m_engine->notifyInferiorSetupFailed(msg);
233
234
235
    }
}

236
237
238
239
240
241
242
243
244
245
246
247
248
void RemoteGdbServerAdapter::callTargetRemote()
{
    //m_breakHandler->clearBreakMarkers();

    // "target remote" does three things:
    //     (1) connects to the gdb server
    //     (2) starts the remote application
    //     (3) stops the remote application (early, e.g. in the dynamic linker)
    QString channel = startParameters().remoteChannel;
    m_engine->postCommand("target remote " + channel.toLatin1(),
        CB(handleTargetRemote));
}

ck's avatar
ck committed
249
void RemoteGdbServerAdapter::handleTargetRemote(const GdbResponse &record)
250
{
hjk's avatar
hjk committed
251
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
252
    if (record.resultClass == GdbResultDone) {
253
        // gdb server will stop the remote application itself.
254
255
        showMessage(_("INFERIOR STARTED"));
        showMessage(msgAttachedToStoppedInferior(), StatusBar);
256
        m_engine->handleInferiorPrepared();
257
    } else {
258
        // 16^error,msg="hd:5555: Connection timed out."
259
260
        QString msg = msgConnectRemoteServerFailed(
            QString::fromLocal8Bit(record.data.findChild("msg").data()));
hjk's avatar
hjk committed
261
        m_engine->notifyInferiorSetupFailed(msg);
262
263
264
    }
}

hjk's avatar
hjk committed
265
void RemoteGdbServerAdapter::runEngine()
266
{
267
    QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
268
    m_engine->notifyEngineRunAndInferiorStopOk();
269
    m_engine->continueInferiorInternal();
270
271
}

ck's avatar
ck committed
272
void RemoteGdbServerAdapter::interruptInferior()
273
{
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
    QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state());
    m_engine->postCommand("-exec-interrupt", GdbEngine::Immediate,
        CB(handleInterruptInferior));
}

void RemoteGdbServerAdapter::handleInterruptInferior(const GdbResponse &response)
{
    if (response.resultClass == GdbResultDone) {
        // 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."
        m_engine->notifyInferiorStopOk();
    }
289
290
}

hjk's avatar
hjk committed
291
void RemoteGdbServerAdapter::shutdownInferior()
292
{
293
294
295
296
    if (m_engine->startParameters().startMode == AttachToRemoteServer)
        m_engine->defaultInferiorShutdown("detach");
    else
        m_engine->defaultInferiorShutdown("kill");
hjk's avatar
hjk committed
297
298
299
300
301
}

void RemoteGdbServerAdapter::shutdownAdapter()
{
    m_engine->notifyAdapterShutdownOk();
302
303
}

304
void RemoteGdbServerAdapter::handleRemoteSetupDone(int gdbServerPort, int qmlPort)
ck's avatar
ck committed
305
306
307
{
    QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());

308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
    if (qmlPort != -1)
        startParameters().qmlServerPort = qmlPort;
    if (gdbServerPort != -1) {
        QString &rc = startParameters().remoteChannel;
        const int sepIndex = rc.lastIndexOf(QLatin1Char(':'));
        if (sepIndex != -1) {
            rc.replace(sepIndex + 1, rc.count() - sepIndex - 1,
                       QString::number(gdbServerPort));
        }
    }
    handleSetupDone();
}

void RemoteGdbServerAdapter::handleSetupDone()
{
323
    if (m_engine->startGdb())
ck's avatar
ck committed
324
325
326
        m_engine->handleAdapterStarted();
}

327
void RemoteGdbServerAdapter::handleRemoteSetupFailed(const QString &reason)
ck's avatar
ck committed
328
329
330
331
332
333
{
    QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());

    m_engine->handleAdapterStartFailed(reason);
}

334
335
} // namespace Internal
} // namespace Debugger