File indexing completed on 2024-04-14 04:31:20

0001 /* This file is part of KDevelop
0002    Copyright 2011 Mathieu Lornac <mathieu.lornac@gmail.com>
0003    Copyright 2011 Damien Coppel <damien.coppel@gmail.com>
0004    Copyright 2011 Lionel Duc <lionel.data@gmail.com>
0005    Copyright 2011 Lucas Sarie <lucas.sarie@gmail.com>
0006    Copyright 2016-2017 Anton Anikin <anton@anikin.xyz>
0007 
0008    This program is free software; you can redistribute it and/or
0009    modify it under the terms of the GNU General Public
0010    License as published by the Free Software Foundation; either
0011    version 2 of the License, or (at your option) any later version.
0012 
0013    This program is distributed in the hope that it will be useful,
0014    but WITHOUT ANY WARRANTY; without even the implied warranty of
0015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0016    General Public License for more details.
0017 
0018    You should have received a copy of the GNU General Public License
0019    along with this program; see the file COPYING.  If not, write to
0020    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021    Boston, MA 02110-1301, USA.
0022 */
0023 
0024 #include "callgrind_parser.h"
0025 
0026 #include "debug.h"
0027 #include "callgrind_model.h"
0028 
0029 #include <QBuffer>
0030 #include <QRegularExpression>
0031 #include <QUrl>
0032 
0033 namespace Valgrind
0034 {
0035 
0036 void callgrindParseCallInformation(
0037     const QString& line,
0038     bool programTotal,
0039     const QStringList& eventTypes,
0040     CallgrindFunction*& caller,
0041     CallgrindFunctionsModel* model)
0042 {
0043     static const QRegularExpression binaryExpression("^(.*)\\[(.*)\\]$");
0044     static const QRegularExpression callCountExpression("^(.*)\\((\\d+)x\\)$");
0045 
0046     const int eventsCount = eventTypes.size();
0047 
0048     QStringList lineItems = line.split(QChar(' '), QString::SkipEmptyParts);
0049     for (int i = 0; i < eventsCount; ++i) {
0050         lineItems[i].remove(QLatin1Char(','));
0051     }
0052 
0053     if (programTotal) {
0054         while (lineItems.size() > eventsCount) {
0055             lineItems.removeLast();
0056         }
0057         model->setEventTotals(lineItems);
0058 
0059         return;
0060     }
0061 
0062     const char lineType = lineItems.takeAt(eventsCount).at(0).toLatin1();
0063 
0064     // skip caller lines
0065     if (lineType == '<') {
0066         return;
0067     }
0068 
0069     QString idString;
0070     while (lineItems.size() > eventsCount) {
0071         idString += QStringLiteral("%1 ").arg(lineItems.at(eventsCount));
0072         lineItems.removeAt(eventsCount);
0073     }
0074     idString = idString.trimmed();
0075 
0076     QString binaryFile;
0077 
0078     auto match = binaryExpression.match(idString);
0079     if (match.hasMatch()) {
0080         binaryFile = match.captured(2).trimmed();
0081         idString = match.captured(1).trimmed();
0082     }
0083 
0084     int callCount = 0;
0085     match = callCountExpression.match(idString);
0086     if (match.hasMatch()) {
0087         callCount = match.captured(2).toInt();
0088         idString = match.captured(1).trimmed();
0089     }
0090 
0091     int colonPos = idString.indexOf(':');
0092     Q_ASSERT(colonPos >= 0);
0093 
0094     auto sourceUrl = QUrl::fromLocalFile(idString.mid(0, colonPos)).adjusted(QUrl::NormalizePathSegments);
0095     QString sourceFile = sourceUrl.toLocalFile();
0096     QString functionName = idString.mid(colonPos + 1);
0097 
0098     auto function = model->addFunction(functionName, sourceFile, binaryFile);
0099 
0100     // the function itself
0101     if (lineType == '*') {
0102         caller = function;
0103         caller->addEventValues(lineItems);
0104     }
0105 
0106     // the callee
0107     else if (lineType == '>') {
0108         model->addCall(caller, function, callCount, lineItems);
0109     }
0110 
0111     else {
0112         qCWarning(KDEV_VALGRIND) << "unknown line type:" << lineType;
0113     }
0114 }
0115 
0116 enum CallgrindParserState
0117 {
0118     ParseRoot,
0119     ParseProgramTotalHeader,
0120     ParseProgramTotal,
0121     ParseProgramHeader,
0122     ParseProgram
0123 };
0124 
0125 void callgrindParse(QByteArray& baData, CallgrindFunctionsModel* model)
0126 {
0127     Q_ASSERT(model);
0128 
0129     CallgrindParserState parserState = ParseRoot;
0130     QStringList eventTypes;
0131     QString eventsString;
0132     QString line;
0133 
0134     CallgrindFunction* caller = nullptr;
0135 
0136     QBuffer data(&baData);
0137     data.open(QIODevice::ReadOnly);
0138 
0139     while (!data.atEnd()) {
0140         line = data.readLine().simplified();
0141 
0142         if (line.startsWith(QLatin1String("--")) && line.contains(QLatin1String("annotated source:"))) {
0143                 break;
0144         }
0145 
0146         if (parserState == ParseRoot) {
0147             if (line.startsWith(QLatin1String("Events shown:"))) {
0148                 // 13 is 'Events shown:' size;
0149                 eventsString = line.mid(13).simplified();
0150 
0151                 eventTypes = eventsString.split(QChar(' '), QString::SkipEmptyParts);
0152                 model->setEventTypes(eventTypes);
0153 
0154                 parserState = ParseProgramTotalHeader;
0155             }
0156         }
0157 
0158         else if (parserState == ParseProgramTotalHeader) {
0159             if (line == eventsString) {
0160                 parserState = ParseProgramTotal;
0161             }
0162         }
0163 
0164         else if (parserState == ParseProgramHeader) {
0165             if (line.startsWith(eventsString)) {
0166                 parserState = ParseProgram;
0167             }
0168         }
0169 
0170         else if (!line.isEmpty() && line.at(0).isDigit()) {
0171             if (parserState == ParseProgramTotal) {
0172                 callgrindParseCallInformation(line, true, eventTypes, caller, model);
0173                 parserState = ParseProgramHeader;
0174             }
0175             else {
0176                 callgrindParseCallInformation(line, false, eventTypes, caller, model);
0177             }
0178         }
0179     }
0180 }
0181 
0182 }