Changeset View
Changeset View
Standalone View
Standalone View
src/backtracegenerator.cpp
Show All 31 Lines | |||||
32 | #include <KShell> | 32 | #include <KShell> | ||
33 | #include <KProcess> | 33 | #include <KProcess> | ||
34 | 34 | | |||
35 | #include "parser/backtraceparser.h" | 35 | #include "parser/backtraceparser.h" | ||
36 | 36 | | |||
37 | BacktraceGenerator::BacktraceGenerator(const Debugger & debugger, QObject *parent) | 37 | BacktraceGenerator::BacktraceGenerator(const Debugger & debugger, QObject *parent) | ||
38 | : QObject(parent), | 38 | : QObject(parent), | ||
39 | m_debugger(debugger), m_proc(nullptr), | 39 | m_debugger(debugger), m_proc(nullptr), | ||
40 | m_temp(nullptr), m_state(NotLoaded) | 40 | m_temp(nullptr), m_state(NotLoaded), | ||
41 | m_debuggerKilled(false) | ||||
41 | { | 42 | { | ||
42 | m_parser = BacktraceParser::newParser(m_debugger.codeName(), this); | 43 | m_parser = BacktraceParser::newParser(m_debugger.codeName(), this); | ||
43 | m_parser->connectToGenerator(this); | 44 | m_parser->connectToGenerator(this); | ||
44 | 45 | | |||
45 | #ifdef BACKTRACE_PARSER_DEBUG | 46 | #ifdef BACKTRACE_PARSER_DEBUG | ||
46 | m_debugParser = BacktraceParser::newParser(QString(), this); //uses the null parser | 47 | m_debugParser = BacktraceParser::newParser(QString(), this); //uses the null parser | ||
47 | m_debugParser->connectToGenerator(this); | 48 | m_debugParser->connectToGenerator(this); | ||
48 | #endif | 49 | #endif | ||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Line(s) | 71 | { | |||
98 | 99 | | |||
99 | *m_proc << KShell::splitArgs(str); | 100 | *m_proc << KShell::splitArgs(str); | ||
100 | m_proc->setOutputChannelMode(KProcess::OnlyStdoutChannel); | 101 | m_proc->setOutputChannelMode(KProcess::OnlyStdoutChannel); | ||
101 | m_proc->setNextOpenMode(QIODevice::ReadWrite | QIODevice::Text); | 102 | m_proc->setNextOpenMode(QIODevice::ReadWrite | QIODevice::Text); | ||
102 | // check if the debugger should take its input from a file we'll generate, | 103 | // check if the debugger should take its input from a file we'll generate, | ||
103 | // and take the appropriate steps if so | 104 | // and take the appropriate steps if so | ||
104 | QString stdinFile = m_debugger.backendValueOfParameter(QStringLiteral("ExecInputFile")); | 105 | QString stdinFile = m_debugger.backendValueOfParameter(QStringLiteral("ExecInputFile")); | ||
105 | Debugger::expandString(stdinFile, Debugger::ExpansionUsageShell, m_temp->fileName()); | 106 | Debugger::expandString(stdinFile, Debugger::ExpansionUsageShell, m_temp->fileName()); | ||
107 | m_temp->close(); | ||||
106 | if (!stdinFile.isEmpty() && QFile::exists(stdinFile)) { | 108 | if (!stdinFile.isEmpty() && QFile::exists(stdinFile)) { | ||
107 | m_proc->setStandardInputFile(stdinFile); | 109 | m_proc->setStandardInputFile(stdinFile); | ||
108 | } | 110 | } | ||
109 | connect(m_proc, &KProcess::readyReadStandardOutput, this, &BacktraceGenerator::slotReadInput); | 111 | connect(m_proc, &KProcess::readyReadStandardOutput, this, &BacktraceGenerator::slotReadInput); | ||
110 | connect(m_proc, static_cast<void (KProcess::*)(int, QProcess::ExitStatus)>(&KProcess::finished), this, &BacktraceGenerator::slotProcessExited); | 112 | connect(m_proc, static_cast<void (KProcess::*)(int, QProcess::ExitStatus)>(&KProcess::finished), this, &BacktraceGenerator::slotProcessExited); | ||
111 | 113 | | |||
112 | m_proc->start(); | 114 | m_proc->start(); | ||
113 | if (!m_proc->waitForStarted()) { | 115 | if (!m_proc->waitForStarted()) { | ||
Show All 21 Lines | 131 | { | |||
135 | // we do not know if the output array ends in the middle of an utf-8 sequence | 137 | // we do not know if the output array ends in the middle of an utf-8 sequence | ||
136 | m_output += m_proc->readAllStandardOutput(); | 138 | m_output += m_proc->readAllStandardOutput(); | ||
137 | 139 | | |||
138 | int pos; | 140 | int pos; | ||
139 | while ((pos = m_output.indexOf('\n')) != -1) { | 141 | while ((pos = m_output.indexOf('\n')) != -1) { | ||
140 | QString line = QString::fromLocal8Bit(m_output.constData(), pos + 1); | 142 | QString line = QString::fromLocal8Bit(m_output.constData(), pos + 1); | ||
141 | m_output.remove(0, pos + 1); | 143 | m_output.remove(0, pos + 1); | ||
142 | 144 | | |||
143 | emit newLine(line); | 145 | if (line.simplified() == QStringLiteral("(lldb) exit")) { | ||
144 | line = line.simplified(); | 146 | // lldb is acting on the exit command (from lldbrc) | ||
145 | if (line.startsWith(QLatin1String("Process ")) && line.endsWith(QLatin1String(" detached"))) { | | |||
146 | // lldb is acting on a detach command (in lldbrc) | | |||
147 | // Anything following this line doesn't interest us, and lldb has been known | 147 | // Anything following this line doesn't interest us, and lldb has been known | ||
148 | // to turn into a zombie instead of exitting, thereby blocking us. | 148 | // to turn into a zombie instead of exitting, thereby blocking us. | ||
149 | // Tell the process to quit if it's still running, and pretend it did. | 149 | // Tell the process to quit if it's still running, and pretend it did. | ||
150 | if (m_proc && m_proc->state() == QProcess::Running) { | 150 | if (m_proc && m_proc->state() == QProcess::Running) { | ||
151 | m_proc->terminate(); | 151 | // give it a bit of time before termination | ||
152 | if (!m_proc->waitForFinished(500)) { | 152 | if (!m_proc->waitForFinished(500)) { | ||
153 | m_debuggerKilled = true; | ||||
154 | m_proc->terminate(); | ||||
155 | } | ||||
156 | if (m_proc && !m_proc->waitForFinished(500)) { | ||||
157 | // insist harder. | ||||
153 | m_proc->kill(); | 158 | m_proc->kill(); | ||
154 | } | 159 | } | ||
155 | if (m_proc) { | | |||
156 | slotProcessExited(0, QProcess::NormalExit); | 160 | slotProcessExited(0, QProcess::NormalExit); | ||
157 | } | 161 | } | ||
158 | } | | |||
159 | return; | 162 | return; | ||
160 | } | 163 | } | ||
164 | emit newLine(line); | ||||
161 | } | 165 | } | ||
162 | } | 166 | } | ||
163 | 167 | | |||
164 | void BacktraceGenerator::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus) | 168 | void BacktraceGenerator::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus) | ||
165 | { | 169 | { | ||
170 | QString errorString; | ||||
171 | if (m_proc) { | ||||
172 | errorString = m_proc->errorString(); | ||||
166 | //these are useless now | 173 | //these are useless now | ||
167 | m_proc->deleteLater(); | 174 | m_proc->deleteLater(); | ||
168 | m_temp->deleteLater(); | | |||
169 | m_proc = nullptr; | 175 | m_proc = nullptr; | ||
176 | m_temp->deleteLater(); | ||||
170 | m_temp = nullptr; | 177 | m_temp = nullptr; | ||
178 | } | ||||
171 | 179 | | |||
172 | //mark the end of the backtrace for the parser | 180 | //mark the end of the backtrace for the parser | ||
173 | emit newLine(QString()); | 181 | emit newLine(QString()); | ||
174 | 182 | | |||
175 | if (exitStatus != QProcess::NormalExit || exitCode != 0) { | 183 | // killing the debugger processes will raise an error; ignore it. | ||
184 | if (!m_debuggerKilled && (exitStatus != QProcess::NormalExit || exitCode != 0)) { | ||||
185 | if (!errorString.isEmpty()) { | ||||
186 | qCCritical(DRKONQI_LOG) << m_debugger.command() << "encountered an error:" << errorString; | ||||
187 | } | ||||
176 | m_state = Failed; | 188 | m_state = Failed; | ||
177 | emit someError(); | 189 | emit someError(); | ||
178 | return; | 190 | return; | ||
179 | } | 191 | } | ||
180 | 192 | | |||
181 | //no translation, string appears in the report | 193 | //no translation, string appears in the report | ||
182 | QString tmp(QStringLiteral("Application: %progname (%execname), signal: %signame\n")); | 194 | QString tmp(QStringLiteral("Application: %progname (%execname), signal: %signame\n")); | ||
183 | Debugger::expandString(tmp); | 195 | Debugger::expandString(tmp); | ||
184 | 196 | | |||
185 | m_parsedBacktrace = tmp + m_parser->parsedBacktrace(); | 197 | m_parsedBacktrace = tmp + m_parser->parsedBacktrace(); | ||
186 | m_state = Loaded; | 198 | m_state = Loaded; | ||
187 | 199 | | |||
188 | #ifdef BACKTRACE_PARSER_DEBUG | 200 | #ifdef BACKTRACE_PARSER_DEBUG | ||
189 | //append the raw unparsed backtrace | 201 | //append the raw unparsed backtrace | ||
190 | m_parsedBacktrace += "\n------------ Unparsed Backtrace ------------\n"; | 202 | m_parsedBacktrace += "\n------------ Unparsed Backtrace ------------\n"; | ||
191 | m_parsedBacktrace += m_debugParser->parsedBacktrace(); //it's not really parsed, it's from the null parser. | 203 | m_parsedBacktrace += m_debugParser->parsedBacktrace(); //it's not really parsed, it's from the null parser. | ||
192 | #endif | 204 | #endif | ||
193 | 205 | | |||
194 | emit done(); | 206 | emit done(); | ||
195 | } | 207 | } | ||
196 | | ||||
197 | |