File indexing completed on 2024-12-08 04:54:50

0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0002 // SPDX-FileCopyrightText: 2020-2022 Harald Sitter <sitter@kde.org>
0003 
0004 #include "Patient.h"
0005 
0006 #include <QApplication>
0007 #include <QDebug>
0008 #include <QFileInfo>
0009 #include <QMessageBox>
0010 #include <QProcess>
0011 
0012 #include <KApplicationTrader>
0013 #include <KLocalizedString>
0014 #include <KShell>
0015 #include <KTerminalLauncherJob>
0016 
0017 #include "../coredump/coredump.h"
0018 
0019 using namespace Qt::StringLiterals;
0020 
0021 Patient::Patient(const Coredump &dump)
0022     : m_origCoreFilename(QString::fromUtf8(dump.m_rawData.value("COREDUMP_FILENAME")))
0023     , m_coreFileInfo(m_origCoreFilename)
0024     , m_signal(dump.m_rawData["COREDUMP_SIGNAL"].toInt())
0025     , m_appName(QFileInfo(dump.exe).fileName())
0026     , m_pid(dump.pid)
0027     , m_timestamp(dump.m_rawData["COREDUMP_TIMESTAMP"].toLong())
0028     , m_coredumpExe(dump.m_rawData["COREDUMP_EXE"])
0029     , m_coredumpCom(dump.m_rawData["COREDUMP_COMM"])
0030 {
0031 }
0032 
0033 QStringList Patient::coredumpctlArguments(const QString &command) const
0034 {
0035     return {command, u"COREDUMP_FILENAME=%1"_s.arg(m_origCoreFilename)};
0036 }
0037 
0038 void Patient::launchDebugger()
0039 {
0040     const QString arguments = KShell::joinArgs({u"gdb"_s, u"--core=%1"_s.arg(m_coreFileInfo.filePath()), QString::fromUtf8(m_coredumpExe)});
0041     auto job = new KTerminalLauncherJob(arguments);
0042     connect(job, &KJob::result, this, [job] {
0043         job->deleteLater();
0044         if (job->error() != KJob::NoError) {
0045             qWarning() << job->errorText();
0046         }
0047     });
0048     job->start();
0049 }
0050 
0051 void Patient::debug()
0052 {
0053     if (!m_excavator) {
0054         m_excavator = std::make_unique<AutomaticCoredumpExcavator>();
0055         connect(m_excavator.get(), &AutomaticCoredumpExcavator::failed, this, [this] {
0056             QMessageBox::critical(qApp ? qApp->activeWindow() : nullptr,
0057                                   i18nc("@title", "Failure"),
0058                                   i18nc("@info", "Failed to access crash data for unknown reasons."));
0059             m_excavator.release()->deleteLater();
0060         });
0061         connect(m_excavator.get(), &AutomaticCoredumpExcavator::excavated, this, [this](const QString &corePath) {
0062             m_coreFileInfo = QFileInfo(corePath);
0063             launchDebugger();
0064         });
0065         m_excavator->excavateFrom(m_coreFileInfo.filePath());
0066         return;
0067     }
0068     if (m_coreFileInfo.isReadable()) {
0069         launchDebugger();
0070     }
0071     // else supposedly still waiting for excavator
0072 }
0073 
0074 QString Patient::dateTime() const
0075 {
0076     QDateTime time;
0077     time.setMSecsSinceEpoch(m_timestamp / 1000);
0078     return QLocale().toString(time, QLocale::LongFormat);
0079 }
0080 
0081 QString Patient::iconName() const
0082 {
0083     // Caching it because it's an N² look-up and there generally are tons of duplicates
0084     static QHash<QString, QString> s_cache;
0085     const QString executable = m_appName;
0086     auto it = s_cache.find(executable);
0087     if (it == s_cache.end()) {
0088         const auto servicesFound = KApplicationTrader::query([&executable](const KService::Ptr &service) {
0089             return QFileInfo(service->exec()).fileName() == executable;
0090         });
0091 
0092         QString iconName;
0093         if (servicesFound.isEmpty()) {
0094             iconName = QStringLiteral("applications-science");
0095         } else {
0096             iconName = servicesFound.constFirst()->icon();
0097         }
0098         it = s_cache.insert(executable, iconName);
0099     }
0100     return *it;
0101 }
0102 
0103 bool Patient::canDebug()
0104 {
0105     return m_coreFileInfo.exists();
0106 }
0107 
0108 #include "moc_Patient.cpp"