File indexing completed on 2025-04-20 10:55:36
0001 /* 0002 SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "coredumpbackend.h" 0008 0009 #include <KCrash> 0010 #include <QDebug> 0011 #include <QProcess> 0012 #include <QScopeGuard> 0013 #include <QSettings> 0014 0015 #include <unistd.h> 0016 0017 #include "crashedapplication.h" 0018 #include "debugger.h" 0019 #include "debuggermanager.h" 0020 #include "drkonqi.h" 0021 #include "drkonqi_debug.h" 0022 #include "linuxprocmapsparser.h" 0023 0024 // Only use signal safe API here. 0025 // man 7 signal-safety 0026 static void emergencySaveFunction(int signal) 0027 { 0028 // Should we crash while dealing with this crash, then make sure to remove the metadata file. 0029 // Otherwise the helper daemon will call us again, and again cause a crash, until the user manually removes the file. 0030 Q_UNUSED(signal); 0031 unlink(qPrintable(CoredumpBackend::metadataPath())); 0032 } 0033 0034 bool CoredumpBackend::init() 0035 { 0036 KCrash::setEmergencySaveFunction(emergencySaveFunction); 0037 0038 Q_ASSERT(!metadataPath().isEmpty()); 0039 Q_ASSERT_X(QFile::exists(metadataPath()), static_cast<const char *>(Q_FUNC_INFO), qUtf8Printable(metadataPath())); 0040 qCDebug(DRKONQI_LOG) << "loading metadata" << metadataPath(); 0041 0042 QSettings metadata(metadataPath(), QSettings::IniFormat); 0043 metadata.beginGroup(QStringLiteral("Journal")); 0044 const QStringList keys = metadata.allKeys(); 0045 for (const auto &key : keys) { 0046 m_journalEntry.insert(key.toUtf8(), metadata.value(key).toByteArray()); 0047 } 0048 // conceivably the file contains no Journal group for unknown reasons 0049 Q_ASSERT_X(!m_journalEntry.isEmpty(), static_cast<const char *>(Q_FUNC_INFO), qUtf8Printable(metadataPath())); 0050 0051 AbstractDrKonqiBackend::init(); // calls constructCrashedApplication -> we need to have our members set before calling it 0052 0053 if (crashedApplication()->pid() <= 0) { 0054 qCWarning(DRKONQI_LOG) << "Invalid pid specified or it wasn't found in journald."; 0055 return false; 0056 } 0057 0058 return true; 0059 } 0060 0061 CrashedApplication *CoredumpBackend::constructCrashedApplication() 0062 { 0063 Q_ASSERT(!m_journalEntry.isEmpty()); 0064 0065 bool ok = false; 0066 // Journald timestamps are always micro seconds, coredumpd takes care to make sure this also the case for its timestamp. 0067 const std::chrono::microseconds timestamp(m_journalEntry["COREDUMP_TIMESTAMP"].toLong(&ok)); 0068 Q_ASSERT(ok); 0069 const auto datetime = QDateTime::fromMSecsSinceEpoch(std::chrono::duration_cast<std::chrono::milliseconds>(timestamp).count()); 0070 Q_ASSERT(ok); 0071 const QFileInfo executable(QString::fromUtf8(m_journalEntry["COREDUMP_EXE"])); 0072 const int signal = m_journalEntry["COREDUMP_SIGNAL"].toInt(&ok); 0073 Q_ASSERT(ok); 0074 const bool hasDeletedFiles = LinuxProc::hasMapsDeletedFiles(executable.path(), m_journalEntry["COREDUMP_PROC_MAPS"], LinuxProc::Check::Stat); 0075 0076 Q_ASSERT_X(m_journalEntry["COREDUMP_PID"].toInt() == DrKonqi::pid(), 0077 static_cast<const char *>(Q_FUNC_INFO), 0078 qPrintable(QStringLiteral("journal: %1, drkonqi: %2").arg(QString::fromUtf8(m_journalEntry["COREDUMP_PID"]), QString::number(DrKonqi::pid())))); 0079 0080 m_crashedApplication = std::make_unique<CrashedApplication>(DrKonqi::pid(), 0081 DrKonqi::thread(), 0082 signal, 0083 executable, 0084 DrKonqi::appVersion(), 0085 BugReportAddress(DrKonqi::bugAddress()), 0086 DrKonqi::programName(), 0087 DrKonqi::productName(), 0088 datetime, 0089 DrKonqi::isRestarted(), 0090 hasDeletedFiles); 0091 0092 qCDebug(DRKONQI_LOG) << "Executable is:" << executable.absoluteFilePath(); 0093 qCDebug(DRKONQI_LOG) << "Executable exists:" << executable.exists(); 0094 0095 return m_crashedApplication.get(); 0096 } 0097 0098 DebuggerManager *CoredumpBackend::constructDebuggerManager() 0099 { 0100 const QList<Debugger> internalDebuggers = Debugger::availableInternalDebuggers(m_backendType); 0101 const QList<Debugger> externalDebuggers = Debugger::availableExternalDebuggers(m_backendType); 0102 0103 const Debugger preferredDebugger(Debugger::findDebugger(internalDebuggers, QStringLiteral("gdb"))); 0104 qCDebug(DRKONQI_LOG) << "Using debugger:" << preferredDebugger.codeName(); 0105 0106 m_debuggerManager = new DebuggerManager(preferredDebugger, externalDebuggers, this); 0107 return m_debuggerManager; 0108 } 0109 0110 void CoredumpBackend::prepareForDebugger() 0111 { 0112 if (m_preparationProc) { 0113 return; // Preparation in progress. 0114 } 0115 0116 // Legacy coredumpd doesn't support debugger arguments. We'll have to actually extract the core and manually trace on it, 0117 // somewhat meh. When Ubuntu 20.04 either goes EOL or is deemed irrelevant enough we can remove the entire preparation 0118 // rigging (even in AbstractDrKonqiBackend). 0119 const bool needPreparation = (m_backendType == QLatin1String("coredumpd")); 0120 const bool alreadyPrepared = (m_coreDir != nullptr); 0121 0122 if (!needPreparation || alreadyPrepared) { 0123 // Synthesize a signal. 0124 QMetaObject::invokeMethod(this, &CoredumpBackend::preparedForDebugger, Qt::QueuedConnection); 0125 return; 0126 } 0127 0128 m_coreDir = std::make_unique<QTemporaryDir>(QDir::tempPath() + QStringLiteral("/kcrash-core")); 0129 Q_ASSERT(m_coreDir->isValid()); 0130 0131 const QString coreFile = m_coreDir->filePath(QStringLiteral("core")); 0132 m_preparationProc = std::make_unique<QProcess>(); 0133 m_preparationProc->setProcessChannelMode(QProcess::ForwardedChannels); 0134 m_preparationProc->setProgram(QStringLiteral("coredumpctl")); 0135 m_preparationProc->setArguments({QStringLiteral("--output"), coreFile, QStringLiteral("dump"), QString::number(m_crashedApplication->pid())}); 0136 QObject::connect( 0137 m_preparationProc.get(), 0138 &QProcess::finished, 0139 this, 0140 [this, coreFile](int exitCode, QProcess::ExitStatus exitStatus) { 0141 qDebug() << "Coredumpd core dumping completed" << exitCode << exitStatus << coreFile; 0142 // We dont really care if the dumping failed. The debugger will fail if the core file isn't there and on the UI 0143 // side there's not much point differentiating as the user won't be able to file a bug in any case. 0144 m_preparationProc = nullptr; 0145 m_crashedApplication->m_coreFile = coreFile; 0146 Q_EMIT preparedForDebugger(); 0147 }, 0148 Qt::QueuedConnection /* queue so we don't delete the object out from under it */); 0149 m_preparationProc->start(); 0150 }