backtracegenerator.cpp 6.46 KB
Newer Older
1
2
3
/*****************************************************************
 * drkonqi - The KDE Crash Handler
 *
4
5
 * SPDX-FileCopyrightText: 2000-2003 Hans Petter Bieker <bieker@kde.org>
 * SPDX-FileCopyrightText: 2009 George Kiagiadakis <gkiagia@users.sourceforge.net>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
9
10
 *****************************************************************/
#include "backtracegenerator.h"

Laurent Montel's avatar
Laurent Montel committed
11
#include "drkonqi_debug.h"
12
#include <KProcess>
Alexander Lohnau's avatar
Alexander Lohnau committed
13
#include <KShell>
14
15
16

#include "parser/backtraceparser.h"

Alexander Lohnau's avatar
Alexander Lohnau committed
17
18
19
20
21
22
BacktraceGenerator::BacktraceGenerator(const Debugger &debugger, QObject *parent)
    : QObject(parent)
    , m_debugger(debugger)
    , m_proc(nullptr)
    , m_temp(nullptr)
    , m_state(NotLoaded)
23
24
25
26
27
{
    m_parser = BacktraceParser::newParser(m_debugger.codeName(), this);
    m_parser->connectToGenerator(this);

#ifdef BACKTRACE_PARSER_DEBUG
Alexander Lohnau's avatar
Alexander Lohnau committed
28
    m_debugParser = BacktraceParser::newParser(QString(), this); // uses the null parser
29
30
31
32
33
34
35
    m_debugParser->connectToGenerator(this);
#endif
}

BacktraceGenerator::~BacktraceGenerator()
{
    if (m_proc && m_proc->state() == QProcess::Running) {
Laurent Montel's avatar
Laurent Montel committed
36
        qCWarning(DRKONQI_LOG) << "Killing running debugger instance";
37
38
39
40
        m_proc->disconnect(this);
        m_proc->terminate();
        if (!m_proc->waitForFinished(10000)) {
            m_proc->kill();
René J.V. Bertin's avatar
René J.V. Bertin committed
41
42
43
44
45
            // lldb can become "stuck" on OS X; just mark m_proc as to be deleted later rather
            // than waiting a potentially very long time for it to heed the kill() request.
            m_proc->deleteLater();
        } else {
            delete m_proc;
46
47
48
49
50
51
52
        }
        delete m_temp;
    }
}

bool BacktraceGenerator::start()
{
Alexander Lohnau's avatar
Alexander Lohnau committed
53
    // they should always be null before entering this function.
54
55
    Q_ASSERT(!m_proc);
    Q_ASSERT(!m_temp);
56
57
58
59
60
61
62

    m_parsedBacktrace.clear();
    m_state = Loading;

    emit starting();

    if (!m_debugger.isValid() || !m_debugger.isInstalled()) {
Harald Sitter's avatar
Harald Sitter committed
63
        qCWarning(DRKONQI_LOG) << "Debugger valid" << m_debugger.isValid() << "installed" << m_debugger.isInstalled();
64
65
66
67
68
69
        m_state = FailedToStart;
        emit failedToStart();
        return false;
    }

    m_proc = new KProcess;
Alexander Lohnau's avatar
Alexander Lohnau committed
70
    m_proc->setEnv(QStringLiteral("LC_ALL"), QStringLiteral("C")); // force C locale
71
72
73
74
75
76
77

    m_temp = new QTemporaryFile;
    m_temp->open();
    m_temp->write(m_debugger.backtraceBatchCommands().toLatin1());
    m_temp->write("\n", 1);
    m_temp->flush();

78
79
80
81
82
83
    auto preamble = new QTemporaryFile(m_proc);
    preamble->open();
    preamble->write(m_debugger.preambleCommands().toUtf8());
    preamble->write("\n", 1);
    preamble->flush();

84
85
    // start the debugger
    QString str = m_debugger.command();
86
    Debugger::expandString(str, Debugger::ExpansionUsageShell, m_temp->fileName(), preamble->fileName());
87
88
89
90

    *m_proc << KShell::splitArgs(str);
    m_proc->setOutputChannelMode(KProcess::OnlyStdoutChannel);
    m_proc->setNextOpenMode(QIODevice::ReadWrite | QIODevice::Text);
René J.V. Bertin's avatar
René J.V. Bertin committed
91
92
93
    // check if the debugger should take its input from a file we'll generate,
    // and take the appropriate steps if so
    QString stdinFile = m_debugger.backendValueOfParameter(QStringLiteral("ExecInputFile"));
94
    Debugger::expandString(stdinFile, Debugger::ExpansionUsageShell, m_temp->fileName(), preamble->fileName());
René J.V. Bertin's avatar
René J.V. Bertin committed
95
96
97
    if (!stdinFile.isEmpty() && QFile::exists(stdinFile)) {
        m_proc->setStandardInputFile(stdinFile);
    }
98
99
    connect(m_proc, &KProcess::readyReadStandardOutput, this, &BacktraceGenerator::slotReadInput);
    connect(m_proc, static_cast<void (KProcess::*)(int, QProcess::ExitStatus)>(&KProcess::finished), this, &BacktraceGenerator::slotProcessExited);
100
101

    m_proc->start();
102
103
    // FIXME: don't call wait functions on the GUI thread, instead connect to errorOcurred signal.
    //   Requires refactoring because this function returns a bool :|
104
    if (!m_proc->waitForStarted()) {
105
106
        qCWarning(DRKONQI_LOG) << "Debugger process failed to start" << m_proc->program() << m_proc->arguments() << m_proc->environment();

Alexander Lohnau's avatar
Alexander Lohnau committed
107
        // we mustn't keep these around...
108
109
        m_proc->deleteLater();
        m_temp->deleteLater();
110
111
        m_proc = nullptr;
        m_temp = nullptr;
112
113
114
115
116
117
118
119
120
121
122

        m_state = FailedToStart;
        emit failedToStart();
        return false;
    }

    return true;
}

void BacktraceGenerator::slotReadInput()
{
René J.V. Bertin's avatar
René J.V. Bertin committed
123
124
125
126
127
    if (!m_proc) {
        // this can happen with lldb after we detected that it detached from the debuggee.
        return;
    }

128
129
130
131
132
    // we do not know if the output array ends in the middle of an utf-8 sequence
    m_output += m_proc->readAllStandardOutput();

    int pos;
    while ((pos = m_output.indexOf('\n')) != -1) {
133
        QString line = QString::fromLocal8Bit(m_output.constData(), pos + 1);
134
135
136
        m_output.remove(0, pos + 1);

        emit newLine(line);
René J.V. Bertin's avatar
René J.V. Bertin committed
137
138
139
140
        line = line.simplified();
        if (line.startsWith(QLatin1String("Process ")) && line.endsWith(QLatin1String(" detached"))) {
            // lldb is acting on a detach command (in lldbrc)
            // Anything following this line doesn't interest us, and lldb has been known
Yuri Chornoivan's avatar
Yuri Chornoivan committed
141
            // to turn into a zombie instead of exiting, thereby blocking us.
René J.V. Bertin's avatar
René J.V. Bertin committed
142
143
144
145
146
147
148
149
150
151
152
153
            // Tell the process to quit if it's still running, and pretend it did.
            if (m_proc && m_proc->state() == QProcess::Running) {
                m_proc->terminate();
                if (!m_proc->waitForFinished(500)) {
                    m_proc->kill();
                }
                if (m_proc) {
                    slotProcessExited(0, QProcess::NormalExit);
                }
            }
            return;
        }
154
155
156
157
158
    }
}

void BacktraceGenerator::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus)
{
Alexander Lohnau's avatar
Alexander Lohnau committed
159
    // these are useless now
160
161
    m_proc->deleteLater();
    m_temp->deleteLater();
162
163
    m_proc = nullptr;
    m_temp = nullptr;
164

Alexander Lohnau's avatar
Alexander Lohnau committed
165
    // mark the end of the backtrace for the parser
166
167
168
169
170
171
172
173
    emit newLine(QString());

    if (exitStatus != QProcess::NormalExit || exitCode != 0) {
        m_state = Failed;
        emit someError();
        return;
    }

Alexander Lohnau's avatar
Alexander Lohnau committed
174
    // no translation, string appears in the report
175
    QString tmp(QStringLiteral("Application: %progname (%execname), signal: %signame\n"));
176
177
    Debugger::expandString(tmp);

178
    m_parsedBacktrace = tmp + m_parser->informationLines() + m_parser->parsedBacktrace();
179
180
181
    m_state = Loaded;

#ifdef BACKTRACE_PARSER_DEBUG
Alexander Lohnau's avatar
Alexander Lohnau committed
182
    // append the raw unparsed backtrace
183
    m_parsedBacktrace += "\n------------ Unparsed Backtrace ------------\n";
Alexander Lohnau's avatar
Alexander Lohnau committed
184
    m_parsedBacktrace += m_debugParser->parsedBacktrace(); // it's not really parsed, it's from the null parser.
185
186
187
188
#endif

    emit done();
}