File indexing completed on 2024-04-21 16:12:15

0001 /*******************************************************************
0002  * parsebugbacktraces.cpp
0003  * SPDX-FileCopyrightText: 2011 Matthias Fuchs <mat69@gmx.net>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-or-later
0006  *
0007  ******************************************************************/
0008 
0009 #include "parsebugbacktraces.h"
0010 
0011 #include "parser/backtraceparser.h"
0012 
0013 typedef QList<BacktraceLine>::const_iterator BacktraceConstIterator;
0014 
0015 BacktraceConstIterator findCrashStackFrame(BacktraceConstIterator it, BacktraceConstIterator itEnd)
0016 {
0017     BacktraceConstIterator result = itEnd;
0018 
0019     // find the beginning of the crash
0020     for (; it != itEnd; ++it) {
0021         if (it->type() == BacktraceLine::KCrash) {
0022             result = it;
0023             break;
0024         }
0025     }
0026 
0027     // find the beginning of the stack frame
0028     for (it = result; it != itEnd; ++it) {
0029         if (it->type() == BacktraceLine::StackFrame) {
0030             result = it;
0031             break;
0032         }
0033     }
0034 
0035     return result;
0036 }
0037 
0038 // TODO improve this stuff, it is just a HACK
0039 ParseBugBacktraces::DuplicateRating rating(BacktraceConstIterator it, BacktraceConstIterator itEnd, BacktraceConstIterator it2, BacktraceConstIterator itEnd2)
0040 {
0041     int matches = 0;
0042     int lines = 0;
0043 
0044     it = findCrashStackFrame(it, itEnd);
0045     it2 = findCrashStackFrame(it2, itEnd2);
0046 
0047     while (it != itEnd && it2 != itEnd2) {
0048         if (it->type() == BacktraceLine::StackFrame && it2->type() == BacktraceLine::StackFrame) {
0049             ++lines;
0050             if (it->frameNumber() == it2->frameNumber() && it->functionName() == it2->functionName()) {
0051                 ++matches;
0052             }
0053             ++it;
0054             ++it2;
0055             continue;
0056         }
0057 
0058         // if iters do not point to emptylines or a stackframe increase them
0059         if (it->type() != BacktraceLine::StackFrame && it->type() != BacktraceLine::EmptyLine) {
0060             ++it;
0061             continue;
0062         }
0063         if (it2->type() != BacktraceLine::StackFrame && it2->type() != BacktraceLine::EmptyLine) {
0064             ++it2;
0065             continue;
0066         }
0067 
0068         // one bt is shorter than the other
0069         if (it->type() == BacktraceLine::StackFrame && it2->type() == BacktraceLine::EmptyLine) {
0070             ++lines;
0071             ++it;
0072             continue;
0073         }
0074         if (it2->type() == BacktraceLine::StackFrame && it->type() == BacktraceLine::EmptyLine) {
0075             ++lines;
0076             ++it2;
0077             continue;
0078         }
0079 
0080         if (it->type() == BacktraceLine::EmptyLine && it2->type() == BacktraceLine::EmptyLine) {
0081             // done
0082             break;
0083         }
0084     }
0085 
0086     if (!lines) {
0087         return ParseBugBacktraces::NoDuplicate;
0088     }
0089 
0090     const int rating = matches * 100 / lines;
0091     if (rating == 100) {
0092         return ParseBugBacktraces::PerfectDuplicate;
0093     } else if (rating >= 90) {
0094         return ParseBugBacktraces::MostLikelyDuplicate;
0095     } else if (rating >= 60) {
0096         return ParseBugBacktraces::MaybeDuplicate;
0097     } else {
0098         return ParseBugBacktraces::NoDuplicate;
0099     }
0100 }
0101 
0102 ParseBugBacktraces::ParseBugBacktraces(const QList<Bugzilla::Comment::Ptr> &comments, QObject *parent)
0103     : QObject(parent)
0104     , m_comments(comments)
0105 {
0106     m_parser = BacktraceParser::newParser(QStringLiteral("gdb"), this);
0107     m_parser->connectToGenerator(this);
0108 }
0109 
0110 void ParseBugBacktraces::parse()
0111 {
0112     for (const auto &comment : m_comments) {
0113         parse(comment->text());
0114     }
0115 }
0116 
0117 void ParseBugBacktraces::parse(const QString &comment)
0118 {
0119     Q_EMIT starting();
0120 
0121     int start = 0;
0122     int end = -1;
0123     do {
0124         start = end + 1;
0125         end = comment.indexOf(QLatin1Char('\n'), start);
0126         Q_EMIT newLine(comment.mid(start, (end != -1 ? end - start + 1 : end)));
0127     } while (end != -1);
0128 
0129     // accepts anything as backtrace, the start of the backtrace is searched later anyway
0130     m_backtraces << m_parser->parsedBacktraceLines();
0131 }
0132 
0133 ParseBugBacktraces::DuplicateRating ParseBugBacktraces::findDuplicate(const QList<BacktraceLine> &backtrace)
0134 {
0135     if (m_backtraces.isEmpty() || backtrace.isEmpty()) {
0136         return NoDuplicate;
0137     }
0138 
0139     DuplicateRating bestRating = NoDuplicate;
0140     DuplicateRating currentRating = NoDuplicate;
0141 
0142     QList<QList<BacktraceLine>>::const_iterator itBts;
0143     QList<QList<BacktraceLine>>::const_iterator itEndBts = m_backtraces.constEnd();
0144     for (itBts = m_backtraces.constBegin(); itBts != itEndBts; ++itBts) {
0145         currentRating = rating(backtrace.constBegin(), backtrace.constEnd(), itBts->constBegin(), itBts->constEnd());
0146         if (currentRating < bestRating) {
0147             bestRating = currentRating;
0148         }
0149 
0150         if (bestRating == PerfectDuplicate) {
0151             return bestRating;
0152         }
0153     }
0154 
0155     return bestRating;
0156 }