diff --git a/src/data/debuggers/external/cdbrc b/src/data/debuggers/external/cdbrc new file mode 100644 index 00000000..af38c6bc --- /dev/null +++ b/src/data/debuggers/external/cdbrc @@ -0,0 +1,7 @@ +[General] +Name=cdb +TryExec=cdb +Backends=KCrash + +[KCrash] +Exec=konsole --nofork -e cdb.exe -p %pid -lines -c "~*kv; q" \ No newline at end of file diff --git a/src/data/debuggers/internal/cdbrc b/src/data/debuggers/internal/cdbrc new file mode 100644 index 00000000..193d1b96 --- /dev/null +++ b/src/data/debuggers/internal/cdbrc @@ -0,0 +1,7 @@ +[General] +Name=cdb +TryExec=cdb +Backends=KCrash + +[KCrash] +Exec=cdb.exe -p %pid -lines -c "~*kv; q" \ No newline at end of file diff --git a/src/drkonqibackends.cpp b/src/drkonqibackends.cpp index 7a30ca21..fc9a22fe 100644 --- a/src/drkonqibackends.cpp +++ b/src/drkonqibackends.cpp @@ -1,239 +1,239 @@ /* Copyright (C) 2009 George Kiagiadakis This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "drkonqibackends.h" #include #include #include #include #include #include #include #include #include #include #include "drkonqi_debug.h" #include "crashedapplication.h" #include "debugger.h" #include "debuggermanager.h" #include "backtracegenerator.h" #include "drkonqi.h" #ifdef Q_OS_MACOS #include #endif AbstractDrKonqiBackend::~AbstractDrKonqiBackend() { } bool AbstractDrKonqiBackend::init() { m_crashedApplication = constructCrashedApplication(); m_debuggerManager = constructDebuggerManager(); return true; } KCrashBackend::KCrashBackend() : QObject(), AbstractDrKonqiBackend(), m_state(ProcessRunning) { } KCrashBackend::~KCrashBackend() { continueAttachedProcess(); } bool KCrashBackend::init() { AbstractDrKonqiBackend::init(); //check whether the attached process exists and whether we have permissions to inspect it if (crashedApplication()->pid() <= 0) { qCWarning(DRKONQI_LOG) << "Invalid pid specified"; return false; } #if !defined(Q_OS_WIN32) if (::kill(crashedApplication()->pid(), 0) < 0) { switch (errno) { case EPERM: qCWarning(DRKONQI_LOG) << "DrKonqi doesn't have permissions to inspect the specified process"; break; case ESRCH: qCWarning(DRKONQI_LOG) << "The specified process does not exist."; break; default: break; } return false; } //--keeprunning means: generate backtrace instantly and let the process continue execution if(DrKonqi::isKeepRunning()) { stopAttachedProcess(); debuggerManager()->backtraceGenerator()->start(); connect(debuggerManager(), &DebuggerManager::debuggerFinished, this, &KCrashBackend::continueAttachedProcess); } else { connect(debuggerManager(), &DebuggerManager::debuggerStarting, this, &KCrashBackend::onDebuggerStarting); connect(debuggerManager(), &DebuggerManager::debuggerFinished, this, &KCrashBackend::onDebuggerFinished); //stop the process to avoid high cpu usage by other threads (bug 175362). //if the process was started by kdeinit, we need to wait a bit for KCrash //to reach the alarm(0); call in kdeui/util/kcrash.cpp line 406 or else //if we stop it before this call, pending alarm signals will kill the //process when we try to continue it. QTimer::singleShot(2000, this, &KCrashBackend::stopAttachedProcess); } #endif //Handle drkonqi crashes s_pid = crashedApplication()->pid(); //copy pid for use by the crash handler, so that it is safer KCrash::setEmergencySaveFunction(emergencySaveFunction); return true; } CrashedApplication *KCrashBackend::constructCrashedApplication() { CrashedApplication *a = new CrashedApplication(this); a->m_datetime = QDateTime::currentDateTime(); a->m_name = DrKonqi::programName(); a->m_version = DrKonqi::appVersion(); a->m_reportAddress = BugReportAddress(DrKonqi::bugAddress()); a->m_pid = DrKonqi::pid(); a->m_signalNumber = DrKonqi::signal(); a->m_restarted = DrKonqi::isRestarted(); a->m_thread = DrKonqi::thread(); //try to determine the executable that crashed if ( QFileInfo(QStringLiteral("/proc/%1/exe").arg(a->m_pid)).exists() ) { //on linux, the fastest and most reliable way is to get the path from /proc qCDebug(DRKONQI_LOG) << "Using /proc to determine executable path"; a->m_executable.setFile(QFile::symLinkTarget(QStringLiteral("/proc/%1/exe").arg(a->m_pid))); if (DrKonqi::isKdeinit() || a->m_executable.fileName().startsWith(QLatin1String("python")) ) { a->m_fakeBaseName = DrKonqi::appName(); } } else { if ( DrKonqi::isKdeinit() ) { a->m_executable = QFileInfo(QStandardPaths::findExecutable(QStringLiteral("kdeinit5"))); a->m_fakeBaseName = DrKonqi::appName(); } else { QFileInfo execPath(DrKonqi::appName()); if ( execPath.isAbsolute() ) { a->m_executable = execPath; } else if ( !DrKonqi::appPath().isEmpty() ) { QDir execDir(DrKonqi::appPath()); a->m_executable = execDir.absoluteFilePath(execPath.fileName()); } else { a->m_executable = QFileInfo(QStandardPaths::findExecutable(execPath.fileName())); } } } qCDebug(DRKONQI_LOG) << "Executable is:" << a->m_executable.absoluteFilePath(); qCDebug(DRKONQI_LOG) << "Executable exists:" << a->m_executable.exists(); return a; } DebuggerManager *KCrashBackend::constructDebuggerManager() { QList internalDebuggers = Debugger::availableInternalDebuggers(QStringLiteral("KCrash")); KConfigGroup config(KSharedConfig::openConfig(), "DrKonqi"); #if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED > 1070 QString defaultDebuggerName = config.readEntry("Debugger", QStringLiteral("lldb")); #elif !defined(Q_OS_WIN) QString defaultDebuggerName = config.readEntry("Debugger", QStringLiteral("gdb")); #else - QString defaultDebuggerName = config.readEntry("Debugger", QStringLiteral("kdbgwin")); + QString defaultDebuggerName = config.readEntry("Debugger", QStringLiteral("cdb")); #endif Debugger firstKnownGoodDebugger, preferredDebugger; foreach (const Debugger & debugger, internalDebuggers) { if (!firstKnownGoodDebugger.isValid() && debugger.isInstalled()) { firstKnownGoodDebugger = debugger; } if (debugger.codeName() == defaultDebuggerName) { preferredDebugger = debugger; } if (firstKnownGoodDebugger.isValid() && preferredDebugger.isValid()) { break; } } if (!preferredDebugger.isInstalled()) { if (firstKnownGoodDebugger.isValid()) { preferredDebugger = firstKnownGoodDebugger; } else { qCWarning(DRKONQI_LOG) << "Unable to find an internal debugger that can work with the KCrash backend"; } } return new DebuggerManager(preferredDebugger, Debugger::availableExternalDebuggers(QStringLiteral("KCrash")), this); } void KCrashBackend::stopAttachedProcess() { if (m_state == ProcessRunning) { qCDebug(DRKONQI_LOG) << "Sending SIGSTOP to process"; ::kill(crashedApplication()->pid(), SIGSTOP); m_state = ProcessStopped; } } void KCrashBackend::continueAttachedProcess() { if (m_state == ProcessStopped) { qCDebug(DRKONQI_LOG) << "Sending SIGCONT to process"; ::kill(crashedApplication()->pid(), SIGCONT); m_state = ProcessRunning; } } void KCrashBackend::onDebuggerStarting() { continueAttachedProcess(); m_state = DebuggerRunning; } void KCrashBackend::onDebuggerFinished() { m_state = ProcessRunning; stopAttachedProcess(); } //static qint64 KCrashBackend::s_pid = 0; //static void KCrashBackend::emergencySaveFunction(int signal) { // In case drkonqi itself crashes, we need to get rid of the process being debugged, // so we kill it, no matter what its state was. Q_UNUSED(signal); ::kill(s_pid, SIGKILL); } diff --git a/src/parser/CMakeLists.txt b/src/parser/CMakeLists.txt index 9910585f..e2cd783f 100644 --- a/src/parser/CMakeLists.txt +++ b/src/parser/CMakeLists.txt @@ -1,15 +1,16 @@ set(BACKTRACEPARSER_SRCS backtraceparser.cpp backtraceparsergdb.cpp backtraceparserkdbgwin.cpp backtraceparsernull.cpp backtraceparserlldb.cpp + backtraceparsercdb.cpp ) ecm_qt_declare_logging_category(BACKTRACEPARSER_SRCS HEADER drkonqi_parser_debug.h IDENTIFIER DRKONQI_PARSER_LOG CATEGORY_NAME org.kde.drkonqi.parser) add_library(drkonqi_backtrace_parser STATIC ${BACKTRACEPARSER_SRCS}) target_link_libraries(drkonqi_backtrace_parser PUBLIC Qt5::Core ) diff --git a/src/parser/backtraceparser.cpp b/src/parser/backtraceparser.cpp index 9abd03c6..96fbccf1 100644 --- a/src/parser/backtraceparser.cpp +++ b/src/parser/backtraceparser.cpp @@ -1,401 +1,404 @@ /* Copyright (C) 2009-2010 George Kiagiadakis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "backtraceparser_p.h" #include "backtraceparsergdb.h" #include "backtraceparserkdbgwin.h" #include "backtraceparserlldb.h" +#include "backtraceparsercdb.h" #include "backtraceparsernull.h" #include "drkonqi_parser_debug.h" #include #include #include //factory BacktraceParser *BacktraceParser::newParser(const QString & debuggerName, QObject *parent) { if (debuggerName == QLatin1String("gdb")) { return new BacktraceParserGdb(parent); } else if (debuggerName == QLatin1String("kdbgwin")) { return new BacktraceParserKdbgwin(parent); } else if (debuggerName == QLatin1String("lldb")) { return new BacktraceParserLldb(parent); + } else if (debuggerName == QLatin1String("cdb")) { + return new BacktraceParserCdb(parent); } else { return new BacktraceParserNull(parent); } } BacktraceParser::BacktraceParser(QObject *parent) : QObject(parent), d_ptr(nullptr) {} BacktraceParser::~BacktraceParser() { delete d_ptr; } void BacktraceParser::connectToGenerator(QObject *generator) { connect(generator, SIGNAL(starting()), this, SLOT(resetState())); connect(generator, SIGNAL(newLine(QString)), this, SLOT(newLine(QString))); } QString BacktraceParser::parsedBacktrace() const { Q_D(const BacktraceParser); QString result; if (d) { for (QList::const_iterator i = d->m_linesList.constBegin(), total = d->m_linesList.constEnd(); i != total; ++i) { result += i->toString(); } } return result; } QList BacktraceParser::parsedBacktraceLines() const { Q_D(const BacktraceParser); return d ? d->m_linesList : QList(); } QString BacktraceParser::simplifiedBacktrace() const { Q_D(const BacktraceParser); //if there is no cached usefulness, the data calculation function has not run yet. if (d && d->m_usefulness == InvalidUsefulness) { const_cast(this)->calculateRatingData(); } //if there is no d, the debugger has not run yet, so we have no backtrace. return d ? d->m_simplifiedBacktrace : QString(); } BacktraceParser::Usefulness BacktraceParser::backtraceUsefulness() const { Q_D(const BacktraceParser); //if there is no cached usefulness, the data calculation function has not run yet. if (d && d->m_usefulness == InvalidUsefulness) { const_cast(this)->calculateRatingData(); } //if there is no d, the debugger has not run yet, //so we can say that the (inexistent) backtrace is Useless. return d ? d->m_usefulness : Useless; } QStringList BacktraceParser::firstValidFunctions() const { Q_D(const BacktraceParser); //if there is no cached usefulness, the data calculation function has not run yet. if (d && d->m_usefulness == InvalidUsefulness) { const_cast(this)->calculateRatingData(); } //if there is no d, the debugger has not run yet, so we have no functions to return. return d ? d->m_firstUsefulFunctions : QStringList(); } QSet BacktraceParser::librariesWithMissingDebugSymbols() const { Q_D(const BacktraceParser); //if there is no cached usefulness, the data calculation function has not run yet. if (d && d->m_usefulness == InvalidUsefulness) { const_cast(this)->calculateRatingData(); } //if there is no d, the debugger has not run yet, so we have no libraries. return d ? d->m_librariesWithMissingDebugSymbols : QSet(); } void BacktraceParser::resetState() { //reset the state of the parser by getting a new instance of Private delete d_ptr; d_ptr = constructPrivate(); } BacktraceParserPrivate *BacktraceParser::constructPrivate() const { return new BacktraceParserPrivate; } /* This function returns true if the given stack frame line is the base of the backtrace and thus the parser should not rate any frames below that one. */ static bool lineIsStackBase(const BacktraceLine & line) { //optimization. if there is no function name, do not bother to check it if ( line.rating() == BacktraceLine::MissingEverything || line.rating() == BacktraceLine::MissingFunction ) return false; //this is the base frame for all threads except the main thread //FIXME that probably works only on linux if ( line.functionName() == QLatin1String("start_thread") ) return true; QRegExp regExp; regExp.setPattern(QStringLiteral("(kde)?main")); //main() or kdemain() is the base for the main thread if ( regExp.exactMatch(line.functionName()) ) return true; //HACK for better rating. we ignore all stack frames below any function that matches //the following regular expression. The functions that match this expression are usually //"QApplicationPrivate::notify_helper", "QApplication::notify" and similar, which //are used to send any kind of event to the Qt application. All stack frames below this, //with or without debug symbols, are useless to KDE developers, so we ignore them. regExp.setPattern(QStringLiteral("(Q|K)(Core)?Application(Private)?::notify.*")); if ( regExp.exactMatch(line.functionName()) ) return true; //attempt to recognize crashes that happen after main has returned (bug 200993) if ( line.functionName() == QLatin1String("~KCleanUpGlobalStatic") || line.functionName() == QLatin1String("~QGlobalStatic") || line.functionName() == QLatin1String("exit") || line.functionName() == QLatin1String("*__GI_exit") ) return true; return false; } /* This function returns true if the given stack frame line is the top of the bactrace and thus the parser should not rate any frames above that one. This is used to avoid rating the stack frames of abort(), assert(), Q_ASSERT() and qCCritical(DRKONQI_PARSER_LOG) */ static bool lineIsStackTop(const BacktraceLine & line) { //optimization. if there is no function name, do not bother to check it if ( line.rating() == BacktraceLine::MissingEverything || line.rating() == BacktraceLine::MissingFunction ) return false; if ( line.functionName().startsWith(QLatin1String("qt_assert")) //qt_assert and qt_assert_x || line.functionName() == QLatin1String("qFatal") || line.functionName() == QLatin1String("abort") || line.functionName() == QLatin1String("*__GI_abort") || line.functionName() == QLatin1String("*__GI___assert_fail") ) return true; return false; } /* This function returns true if the given stack frame line should be ignored from rating for some reason. Currently it ignores all libc/libstdc++/libpthread functions. */ static bool lineShouldBeIgnored(const BacktraceLine & line) { if ( line.libraryName().contains(QStringLiteral("libc.so")) || line.libraryName().contains(QStringLiteral("libstdc++.so")) || line.functionName().startsWith(QLatin1String("*__GI_")) //glibc2.9 uses *__GI_ as prefix || line.libraryName().contains(QStringLiteral("libpthread.so")) || line.libraryName().contains(QStringLiteral("libglib-2.0.so")) #ifdef Q_OS_MACOS || (line.libraryName().startsWith(QStringLiteral("libsystem_")) && line.libraryName().endsWith(QStringLiteral(".dylib"))) || line.libraryName().contains(QStringLiteral("Foundation`")) #endif || line.libraryName().contains(QStringLiteral("ntdll.dll")) || line.libraryName().contains(QStringLiteral("kernel32.dll")) || line.functionName().contains(QStringLiteral("_tmain")) || line.functionName() == QLatin1String("WinMain") ) return true; return false; } static bool isFunctionUseful(const BacktraceLine & line) { //We need the function name if ( line.rating() == BacktraceLine::MissingEverything || line.rating() == BacktraceLine::MissingFunction ) { return false; } //Misc ignores if ( line.functionName() == QLatin1String("__kernel_vsyscall") || line.functionName() == QLatin1String("raise") || line.functionName() == QLatin1String("abort") || line.functionName() == QLatin1String("__libc_message") || line.functionName() == QLatin1String("thr_kill") /* *BSD */) { return false; } //Ignore core Qt functions //(QObject can be useful in some cases) if ( line.functionName().startsWith(QLatin1String("QBasicAtomicInt::")) || line.functionName().startsWith(QLatin1String("QBasicAtomicPointer::")) || line.functionName().startsWith(QLatin1String("QAtomicInt::")) || line.functionName().startsWith(QLatin1String("QAtomicPointer::")) || line.functionName().startsWith(QLatin1String("QMetaObject::")) || line.functionName().startsWith(QLatin1String("QPointer::")) || line.functionName().startsWith(QLatin1String("QWeakPointer::")) || line.functionName().startsWith(QLatin1String("QSharedPointer::")) || line.functionName().startsWith(QLatin1String("QScopedPointer::")) || line.functionName().startsWith(QLatin1String("QMetaCallEvent::")) ) { return false; } //Ignore core Qt containers misc functions if ( line.functionName().endsWith(QLatin1String("detach")) || line.functionName().endsWith(QLatin1String("detach_helper")) || line.functionName().endsWith(QLatin1String("node_create")) || line.functionName().endsWith(QLatin1String("deref")) || line.functionName().endsWith(QLatin1String("ref")) || line.functionName().endsWith(QLatin1String("node_copy")) || line.functionName().endsWith(QLatin1String("d_func")) ) { return false; } //Misc Qt stuff if ( line.functionName() == QLatin1String("qt_message_output") || line.functionName() == QLatin1String("qt_message") || line.functionName() == QLatin1String("qFatal") || line.functionName().startsWith(QLatin1String("qGetPtrHelper")) || line.functionName().startsWith(QLatin1String("qt_meta_")) ) { return false; } return true; } static bool isFunctionUsefulForSearch(const BacktraceLine & line) { //Ignore Qt containers (and iterators Q*Iterator) if ( line.functionName().startsWith(QLatin1String("QList")) || line.functionName().startsWith(QLatin1String("QLinkedList")) || line.functionName().startsWith(QLatin1String("QVector")) || line.functionName().startsWith(QLatin1String("QStack")) || line.functionName().startsWith(QLatin1String("QQueue")) || line.functionName().startsWith(QLatin1String("QSet")) || line.functionName().startsWith(QLatin1String("QMap")) || line.functionName().startsWith(QLatin1String("QMultiMap")) || line.functionName().startsWith(QLatin1String("QMapData")) || line.functionName().startsWith(QLatin1String("QHash")) || line.functionName().startsWith(QLatin1String("QMultiHash")) || line.functionName().startsWith(QLatin1String("QHashData")) ) { return false; } return true; } void BacktraceParser::calculateRatingData() { Q_D(BacktraceParser); uint rating = 0, bestPossibleRating = 0, counter = 0; bool haveSeenStackBase = false; QListIterator i(d->m_linesToRate); i.toBack(); //start from the end of the list while( i.hasPrevious() ) { const BacktraceLine & line = i.previous(); if ( !i.hasPrevious() && line.rating() == BacktraceLine::MissingEverything ) { //Under some circumstances, the very first stack frame is invalid (ex, calling a function //at an invalid address could result in a stack frame like "0x00000000 in ?? ()"), //which however does not necessarily mean that the backtrace has a missing symbol on //the first line. Here we make sure to ignore this line from rating. (bug 190882) break; //there are no more items anyway, just break the loop } if ( lineIsStackBase(line) ) { rating = bestPossibleRating = counter = 0; //restart rating ignoring any previous frames haveSeenStackBase = true; } else if ( lineIsStackTop(line) ) { break; //we have reached the top, no need to inspect any more frames } if ( lineShouldBeIgnored(line) ) { continue; } if ( line.rating() == BacktraceLine::MissingFunction || line.rating() == BacktraceLine::MissingSourceFile) { d->m_librariesWithMissingDebugSymbols.insert(line.libraryName().trimmed()); } uint multiplier = ++counter; //give weight to the first lines rating += static_cast(line.rating()) * multiplier; bestPossibleRating += static_cast(BacktraceLine::BestRating) * multiplier; qCDebug(DRKONQI_PARSER_LOG) << line.rating() << line.toString(); } //Generate a simplified backtrace //- Starts from the first useful function //- Max of 5 lines //- Replaces garbage with [...] //At the same time, grab the first three useful functions for search queries i.toFront(); //Reuse the list iterator int functionIndex = 0; int usefulFunctionsCount = 0; bool firstUsefulFound = false; while( i.hasNext() && functionIndex < 5 ) { const BacktraceLine & line = i.next(); if ( !lineShouldBeIgnored(line) && isFunctionUseful(line) ) { //Line is not garbage to use if (!firstUsefulFound) { firstUsefulFound = true; } //Save simplified backtrace line d->m_simplifiedBacktrace += line.toString(); //Fetch three useful functions (only functionName) for search queries if (usefulFunctionsCount < 3 && isFunctionUsefulForSearch(line) && !d->m_firstUsefulFunctions.contains(line.functionName())) { d->m_firstUsefulFunctions.append(line.functionName()); usefulFunctionsCount++; } functionIndex++; } else if (firstUsefulFound) { //Add "[...]" if there are invalid functions in the middle if (!d->m_simplifiedBacktrace.endsWith(QLatin1String("[...]\n"))) { d->m_simplifiedBacktrace += QLatin1String("[...]\n"); } } } //calculate rating d->m_usefulness = Useless; if (rating >= (bestPossibleRating*0.90)) { d->m_usefulness = ReallyUseful; } else if (rating >= (bestPossibleRating*0.70)) { d->m_usefulness = MayBeUseful; } else if (rating >= (bestPossibleRating*0.40)) { d->m_usefulness = ProbablyUseless; } //if there is no stack base, the executable is probably stripped, //so we need to be more strict with rating if ( !haveSeenStackBase ) { //less than 4 stack frames is useless if ( counter < 4 ) { d->m_usefulness = Useless; //more than 4 stack frames might have some value, so let's not be so strict, just lower the rating } else if ( d->m_usefulness > Useless ) { d->m_usefulness = (Usefulness) (d->m_usefulness - 1); } } qCDebug(DRKONQI_PARSER_LOG) << "Rating:" << rating << "out of" << bestPossibleRating << "Usefulness:" << staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("Usefulness")).valueToKey(d->m_usefulness); qCDebug(DRKONQI_PARSER_LOG) << "90%:" << (bestPossibleRating*0.90) << "70%:" << (bestPossibleRating*0.70) << "40%:" << (bestPossibleRating*0.40); qCDebug(DRKONQI_PARSER_LOG) << "Have seen stack base:" << haveSeenStackBase << "Lines counted:" << counter; } diff --git a/src/parser/backtraceparsercdb.cpp b/src/parser/backtraceparsercdb.cpp new file mode 100644 index 00000000..52793626 --- /dev/null +++ b/src/parser/backtraceparsercdb.cpp @@ -0,0 +1,44 @@ +/* + Copyright (C) 2019 Patrick José Pereira + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include "backtraceparsercdb.h" +#include "backtraceparser_p.h" + +BacktraceParserCdb::BacktraceParserCdb(QObject *parent) + : BacktraceParser(parent) +{ +} + +BacktraceParserPrivate *BacktraceParserCdb::constructPrivate() const +{ + BacktraceParserPrivate *d = BacktraceParser::constructPrivate(); + d->m_usefulness = MayBeUseful; + return d; +} + +void BacktraceParserCdb::newLine(const QString &lineStr) +{ + d_ptr->m_linesList.append(BacktraceLineCdb(lineStr)); +} + +BacktraceLineCdb::BacktraceLineCdb(const QString &line) + : BacktraceLine() +{ + d->m_line = line; + // We should do the faith jump to believe that cdb will provides useful information + d->m_rating = Good; +} \ No newline at end of file diff --git a/src/parser/backtraceparsercdb.h b/src/parser/backtraceparsercdb.h new file mode 100644 index 00000000..38705072 --- /dev/null +++ b/src/parser/backtraceparsercdb.h @@ -0,0 +1,41 @@ +/* + Copyright (C) 2019 Patrick José Pereira + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#pragma once + +#include "backtraceparser.h" + +class BacktraceParserCdb : public BacktraceParser +{ + Q_OBJECT + Q_DECLARE_PRIVATE(BacktraceParser) + +public: + explicit BacktraceParserCdb(QObject *parent = nullptr); + +protected Q_SLOTS: + void newLine(const QString &lineStr) override; + +protected: + BacktraceParserPrivate *constructPrivate() const override; +}; + +class BacktraceLineCdb : public BacktraceLine +{ +public: + BacktraceLineCdb(const QString & line); +};