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 }