coregdbadapter.cpp 11 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11
** 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
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
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
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
30 31

#include "coregdbadapter.h"
hjk's avatar
hjk committed
32

33 34
#include <coreplugin/messagebox.h>

35 36 37 38
#include <debugger/debuggercore.h>
#include <debugger/debuggerprotocol.h>
#include <debugger/debuggerstartparameters.h>
#include <debugger/debuggerstringutils.h>
39

40
#include <utils/fileutils.h>
hjk's avatar
hjk committed
41
#include <utils/qtcassert.h>
42

43
#include <QDir>
hjk's avatar
hjk committed
44
#include <QTemporaryFile>
45

hjk's avatar
hjk committed
46 47
using namespace Utils;

48 49 50
namespace Debugger {
namespace Internal {

51
#define CB(callback) [this](const DebuggerResponse &r) { callback(r); }
52
#define CHECK_STATE(s) do { checkState(s, __FILE__, __LINE__); } while (0)
53 54 55 56 57 58 59

///////////////////////////////////////////////////////////////////////
//
// CoreGdbAdapter
//
///////////////////////////////////////////////////////////////////////

60
GdbCoreEngine::GdbCoreEngine(const DebuggerRunParameters &startParameters)
61 62
    : GdbEngine(startParameters),
      m_coreUnpackProcess(0)
63 64
{}

65
GdbCoreEngine::~GdbCoreEngine()
hjk's avatar
hjk committed
66
{
67 68 69 70 71
    if (m_coreUnpackProcess) {
        m_coreUnpackProcess->blockSignals(true);
        m_coreUnpackProcess->terminate();
        m_coreUnpackProcess->deleteLater();
        m_coreUnpackProcess = 0;
72 73
        if (m_tempCoreFile.isOpen())
            m_tempCoreFile.close();
74 75
    }
    if (!m_tempCoreName.isEmpty()) {
hjk's avatar
hjk committed
76 77 78 79 80
        QFile tmpFile(m_tempCoreName);
        tmpFile.remove();
    }
}

81
void GdbCoreEngine::setupEngine()
82
{
hjk's avatar
hjk committed
83
    QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
84
    showMessage(_("TRYING TO START ADAPTER"));
85

86 87 88
    const DebuggerRunParameters &rp = runParameters();
    m_executable = rp.executable;
    QFileInfo fi(rp.coreFile);
hjk's avatar
hjk committed
89 90 91 92 93
    m_coreName = fi.absoluteFilePath();

    unpackCoreIfNeeded();
}

94
static QString findExecutableFromName(const QString &fileNameFromCore, const QString &coreFile)
95
{
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
    if (QFileInfo(fileNameFromCore).isFile())
        return fileNameFromCore;
    if (fileNameFromCore.isEmpty())
        return QString();

    // turn the filename into an absolute path, using the location of the core as a hint
    QString absPath;
    QFileInfo fi(fileNameFromCore);
    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 QString &debuggerCommand, const QString &coreFile)
{
    CoreInfo cinfo;
132
#if 0
133 134 135
    ElfReader reader(coreFile);
    cinfo.rawStringFromCore = QString::fromLocal8Bit(reader.readCoreName(&cinfo.isCore));
    cinfo.foundExecutableName = findExecutableFromName(cinfo.rawStringFromCore, coreFile);
136 137 138 139 140
#else
    QStringList args;
    args.append(QLatin1String("-nx"));
    args.append(QLatin1String("-batch"));
    args.append(QLatin1String("-c"));
141
    args.append(coreFile);
142

143
    QProcess proc;
144 145 146
    QStringList envLang = QProcess::systemEnvironment();
    envLang.replaceInStrings(QRegExp(QLatin1String("^LC_ALL=.*")), QLatin1String("LC_ALL=C"));
    proc.setEnvironment(envLang);
147
    proc.start(debuggerCommand, args);
148

149 150 151 152 153 154 155 156 157
    if (proc.waitForFinished()) {
        QByteArray ba = proc.readAllStandardOutput();
        // Core was generated by `/data/dev/creator-2.6/bin/qtcreator'.
        // Program terminated with signal 11, Segmentation fault.
        int pos1 = ba.indexOf("Core was generated by");
        if (pos1 != -1) {
            pos1 += 23;
            int pos2 = ba.indexOf('\'', pos1);
            if (pos2 != -1) {
158 159 160
                cinfo.isCore = true;
                cinfo.rawStringFromCore = QString::fromLocal8Bit(ba.mid(pos1, pos2 - pos1));
                cinfo.foundExecutableName = findExecutableFromName(cinfo.rawStringFromCore, coreFile);
161 162 163 164
            }
        }
    }
#endif
165
    return cinfo;
166 167
}

168
void GdbCoreEngine::continueSetupEngine()
hjk's avatar
hjk committed
169
{
170 171 172 173 174
    bool isCore = true;
    if (m_coreUnpackProcess) {
        isCore = m_coreUnpackProcess->exitCode() == 0;
        m_coreUnpackProcess->deleteLater();
        m_coreUnpackProcess = 0;
175 176
        if (m_tempCoreFile.isOpen())
            m_tempCoreFile.close();
177 178
    }
    if (isCore && m_executable.isEmpty()) {
179
        GdbCoreEngine::CoreInfo cinfo = readExecutableNameFromCore(
180
                                            runParameters().debuggerCommand,
181 182 183 184
                                            coreFileName());

        if (cinfo.isCore) {
            m_executable = cinfo.foundExecutableName;
185
            if (m_executable.isEmpty()) {
186
                Core::AsynchronousMessageBox::warning(
187 188 189 190 191
                    tr("Error Loading Symbols"),
                    tr("No executable to load symbols from specified core."));
                notifyEngineSetupFailed();
                return;
            }
hjk's avatar
hjk committed
192 193
        }
    }
194
    if (isCore) {
195
        startGdb();
196
    } else {
197
        Core::AsynchronousMessageBox::warning(
198 199 200 201
            tr("Error Loading Core File"),
            tr("The specified file does not appear to be a core file."));
        notifyEngineSetupFailed();
    }
202
}
203

204 205 206 207 208
void GdbCoreEngine::writeCoreChunk()
{
    m_tempCoreFile.write(m_coreUnpackProcess->readAll());
}

209
void GdbCoreEngine::setupInferior()
hjk's avatar
hjk committed
210
{
211
    CHECK_STATE(InferiorSetupRequested);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
212 213
    // Do that first, otherwise no symbols are loaded.
    QFileInfo fi(m_executable);
214
    QByteArray path = fi.absoluteFilePath().toLocal8Bit();
215
    postCommand("-file-exec-and-symbols \"" + path + '"', NoFlags,
216
         CB(handleFileExecAndSymbols));
217 218
}

219
void GdbCoreEngine::handleFileExecAndSymbols(const DebuggerResponse &response)
220
{
221
    CHECK_STATE(InferiorSetupRequested);
hjk's avatar
hjk committed
222
    QString core = coreFileName();
223
    if (response.resultClass == ResultDone) {
224
        showMessage(tr("Symbols found."), StatusBar);
225 226 227 228 229 230 231 232
        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);
233
    }
234 235 236 237
}

void GdbCoreEngine::runEngine()
{
238
    CHECK_STATE(EngineRunRequested);
239
    postCommand("target core " + coreFileName().toLocal8Bit(), NoFlags, CB(handleTargetCore));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
240 241
}

242
void GdbCoreEngine::handleTargetCore(const DebuggerResponse &response)
hjk's avatar
hjk committed
243
{
244
    CHECK_STATE(EngineRunRequested);
hjk's avatar
hjk committed
245
    notifyEngineRunOkAndInferiorUnrunnable();
246
    if (response.resultClass == ResultDone) {
247
        showMessage(tr("Attached to core."), StatusBar);
248 249
        // Due to the auto-solib-add off setting, we don't have any
        // symbols yet. Load them in order of importance.
250
        reloadStack();
251
        reloadModulesInternal();
252
        postCommand("p 5", NoFlags, CB(handleRoundTrip));
253
        return;
hjk's avatar
hjk committed
254
    }
255
    showStatusMessage(tr("Attach to core \"%1\" failed:").arg(runParameters().coreFile)
256 257
        + QLatin1Char('\n') + QString::fromLocal8Bit(response.data["msg"].data()));
    notifyEngineIll();
hjk's avatar
hjk committed
258
}
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
259

260
void GdbCoreEngine::handleRoundTrip(const DebuggerResponse &response)
261
{
262
    CHECK_STATE(InferiorUnrunnable);
263
    Q_UNUSED(response);
264
    loadSymbolsForStack();
265
    handleStop2();
266 267 268
    QTimer::singleShot(1000, this, SLOT(loadAllSymbols()));
}

269
void GdbCoreEngine::interruptInferior()
270
{
hjk's avatar
hjk committed
271
    // A core never runs, so this cannot be called.
272
    QTC_CHECK(false);
273 274
}

275
void GdbCoreEngine::shutdownEngine()
hjk's avatar
hjk committed
276
{
277
    notifyAdapterShutdownOk();
hjk's avatar
hjk committed
278 279
}

280 281 282 283 284 285 286 287
static QString tempCoreFilename()
{
    QString pattern = QDir::tempPath() + QLatin1String("/tmpcore-XXXXXX");
    QTemporaryFile tmp(pattern);
    tmp.open();
    return tmp.fileName();
}

288
void GdbCoreEngine::unpackCoreIfNeeded()
hjk's avatar
hjk committed
289
{
290 291 292 293 294 295 296 297 298
    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(QDir::tempPath());
        m_coreUnpackProcess->start(QLatin1String("lzop"), arguments);
Montel Laurent's avatar
Montel Laurent committed
299 300
        connect(m_coreUnpackProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
                this, &GdbCoreEngine::continueSetupEngine);
301 302 303 304 305 306 307 308 309
    } 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(QDir::tempPath());
        m_coreUnpackProcess->start(QLatin1String("gzip"), arguments);
Montel Laurent's avatar
Montel Laurent committed
310 311 312
        connect(m_coreUnpackProcess, &QProcess::readyRead, this, &GdbCoreEngine::writeCoreChunk);
        connect(m_coreUnpackProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
                this, &GdbCoreEngine::continueSetupEngine);
313
    } else {
314
        continueSetupEngine();
hjk's avatar
hjk committed
315 316 317
    }
}

318
QString GdbCoreEngine::coreFileName() const
hjk's avatar
hjk committed
319 320
{
    return m_tempCoreName.isEmpty() ? m_coreName : m_tempCoreName;
hjk's avatar
hjk committed
321 322
}

323 324
} // namespace Internal
} // namespace Debugger