Warning, file /plasma/drkonqi/src/debugger.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2009 George Kiagiadakis <gkiagia@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2021-2022 Harald Sitter <sitter@kde.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 #include "debugger.h" 0008 0009 #include <KConfig> 0010 #include <KConfigGroup> 0011 #include <KFileUtils> 0012 #include <KMacroExpander> 0013 #include <QCoreApplication> 0014 #include <QDir> 0015 0016 #include "crashedapplication.h" 0017 #include "drkonqi.h" 0018 #include "drkonqi_debug.h" 0019 0020 // static 0021 QList<Debugger> Debugger::availableInternalDebuggers(const QString &backend) 0022 { 0023 return availableDebuggers(QStringLiteral("debuggers/internal"), backend); 0024 } 0025 0026 // static 0027 QList<Debugger> Debugger::availableExternalDebuggers(const QString &backend) 0028 { 0029 return availableDebuggers(QStringLiteral("debuggers/external"), backend); 0030 } 0031 0032 bool Debugger::isValid() const 0033 { 0034 return m_config; 0035 } 0036 0037 bool Debugger::isInstalled() const 0038 { 0039 QString tryexec = tryExec(); 0040 if (tryexec.isEmpty()) { 0041 qCDebug(DRKONQI_LOG) << "tryExec of" << codeName() << "is empty!"; 0042 return false; 0043 } 0044 0045 // Find for executable in PATH and in our application path 0046 return !QStandardPaths::findExecutable(tryexec).isEmpty() || !QStandardPaths::findExecutable(tryexec, {QCoreApplication::applicationDirPath()}).isEmpty(); 0047 } 0048 0049 QString Debugger::displayName() const 0050 { 0051 return isValid() ? m_config->group("General").readEntry("Name") : QString(); 0052 } 0053 0054 QString Debugger::codeName() const 0055 { 0056 // fall back to the "TryExec" string if "CodeName" is not specified. 0057 // for most debuggers those strings should be the same 0058 return isValid() ? m_config->group("General").readEntry("CodeName", tryExec()) : QString(); 0059 } 0060 0061 QString Debugger::tryExec() const 0062 { 0063 return isValid() ? m_config->group("General").readEntry("TryExec") : QString(); 0064 } 0065 0066 QStringList Debugger::supportedBackends() const 0067 { 0068 return isValid() ? m_config->group("General").readEntry("Backends").split(QLatin1Char('|'), Qt::SkipEmptyParts) : QStringList(); 0069 } 0070 0071 void Debugger::setUsedBackend(const QString &backendName) 0072 { 0073 if (supportedBackends().contains(backendName)) { 0074 m_backend = backendName; 0075 } 0076 } 0077 0078 QString Debugger::command() const 0079 { 0080 if (!isValid() || !m_config->hasGroup(m_backend)) { 0081 return {}; 0082 } 0083 return expandCommand(m_config->group(m_backend).readPathEntry("Exec", QString())); 0084 } 0085 0086 bool Debugger::supportsCommandWithSymbolResolution() const 0087 { 0088 if (!isValid() || !m_config->hasGroup(m_backend)) { 0089 return false; 0090 } 0091 return m_config->group(m_backend).hasKey("ExecWithSymbolResolution"); 0092 } 0093 0094 QString Debugger::commandWithSymbolResolution() const 0095 { 0096 if (!isValid() || !m_config->hasGroup(m_backend)) { 0097 return {}; 0098 } 0099 return expandCommand(m_config->group(m_backend).readPathEntry("ExecWithSymbolResolution", command())); 0100 } 0101 0102 QString Debugger::backtraceBatchCommands() const 0103 { 0104 if (!isValid() || !m_config->hasGroup(m_backend)) { 0105 return {}; 0106 } 0107 return expandCommand(m_config->group(m_backend).readPathEntry("BatchCommands", QString())); 0108 } 0109 0110 QString Debugger::preambleCommands() const 0111 { 0112 if (!isValid() || !m_config->hasGroup(m_backend)) { 0113 return {}; 0114 } 0115 return expandCommand(m_config->group(m_backend).readPathEntry("PreambleCommands", QString())); 0116 } 0117 0118 QString Debugger::expandCommand(const QString &command) const 0119 { 0120 static QHash<QString, QString> map = { 0121 {QStringLiteral("drkonqi_datadir"), QStandardPaths::locate(QStandardPaths::AppDataLocation, codeName(), QStandardPaths::LocateDirectory)}, 0122 }; 0123 return KMacroExpander::expandMacros(command, map); 0124 } 0125 0126 bool Debugger::runInTerminal() const 0127 { 0128 return (isValid() && m_config->hasGroup(m_backend)) ? m_config->group(m_backend).readEntry("Terminal", false) : false; 0129 } 0130 0131 QString Debugger::backendValueOfParameter(const QString &key) const 0132 { 0133 return (isValid() && m_config->hasGroup(m_backend)) ? m_config->group(m_backend).readEntry(key, QString()) : QString(); 0134 } 0135 0136 // static 0137 void Debugger::expandString(QString &str, ExpandStringUsage usage, const QString &tempFile, const QString &preambleFile) 0138 { 0139 const CrashedApplication *appInfo = DrKonqi::crashedApplication(); 0140 const QHash<QString, QString> map = { 0141 {QLatin1String("progname"), appInfo->name()}, 0142 {QLatin1String("execname"), appInfo->fakeExecutableBaseName()}, 0143 {QLatin1String("execpath"), appInfo->executable().absoluteFilePath()}, 0144 {QLatin1String("signum"), QString::number(appInfo->signalNumber())}, 0145 {QLatin1String("signame"), appInfo->signalName()}, 0146 {QLatin1String("pid"), QString::number(appInfo->pid())}, 0147 {QLatin1String("tempfile"), tempFile}, 0148 {QLatin1String("preamblefile"), preambleFile}, 0149 {QLatin1String("thread"), QString::number(appInfo->thread())}, 0150 {QStringLiteral("corefile"), appInfo->m_coreFile}, 0151 }; 0152 0153 if (usage == ExpansionUsageShell) { 0154 str = KMacroExpander::expandMacrosShellQuote(str, map); 0155 } else { 0156 str = KMacroExpander::expandMacros(str, map); 0157 } 0158 } 0159 0160 // static 0161 QList<Debugger> Debugger::availableDebuggers(const QString &path, const QString &backend) 0162 { 0163 const QStringList debuggerDirs{// Search from application path, this helps when deploying an application 0164 // as binary blob (e.g. Windows exe). 0165 QCoreApplication::applicationDirPath() + QLatin1Char('/') + path, 0166 // Search in default path 0167 QStandardPaths::locate(QStandardPaths::AppDataLocation, path, QStandardPaths::LocateDirectory)}; 0168 const QStringList debuggerFiles = KFileUtils::findAllUniqueFiles(debuggerDirs); 0169 0170 QList<Debugger> result; 0171 for (const auto &debuggerFile : debuggerFiles) { 0172 Debugger debugger; 0173 debugger.m_config = KSharedConfig::openConfig(debuggerFile); 0174 if (debugger.supportedBackends().contains(backend)) { 0175 debugger.setUsedBackend(backend); 0176 result << debugger; 0177 } 0178 } 0179 return result; 0180 } 0181 0182 Debugger Debugger::findDebugger(const QList<Debugger> &debuggers, const QString &defaultDebuggerCodeName) 0183 { 0184 Debugger firstKnownGoodDebugger; 0185 Debugger preferredDebugger; 0186 for (const Debugger &debugger : debuggers) { 0187 qCDebug(DRKONQI_LOG) << "Check debugger if" << debugger.displayName() << "[" << debugger.codeName() << "]" 0188 << "is installed:" << debugger.isInstalled(); 0189 if (!firstKnownGoodDebugger.isValid() && debugger.isInstalled()) { 0190 firstKnownGoodDebugger = debugger; 0191 } 0192 if (debugger.codeName() == defaultDebuggerCodeName) { 0193 preferredDebugger = debugger; 0194 } 0195 if (firstKnownGoodDebugger.isValid() && preferredDebugger.isValid()) { 0196 break; 0197 } 0198 } 0199 0200 if (!preferredDebugger.isInstalled()) { 0201 if (firstKnownGoodDebugger.isValid()) { 0202 preferredDebugger = firstKnownGoodDebugger; 0203 } else { 0204 qCWarning(DRKONQI_LOG) << "Unable to find an internal debugger that can work with the crash backend"; 0205 } 0206 } 0207 0208 return preferredDebugger; 0209 }