File indexing completed on 2024-04-21 04:36:00
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 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_model.h" 0025 0026 #include "debug.h" 0027 #include "utils.h" 0028 0029 #include <qtcompat_p.h> 0030 0031 #include <KLocalizedString> 0032 0033 #include <QItemSelectionModel> 0034 0035 namespace Valgrind 0036 { 0037 0038 void addValues(const QStringList& stringValues, QVector<int>& intValues) 0039 { 0040 for (int i = 0; i < stringValues.size(); ++i) { 0041 intValues[i] += stringValues[i].toInt(); 0042 } 0043 } 0044 0045 CallgrindCallInformation::CallgrindCallInformation(const QStringList& stringValues) 0046 { 0047 Q_ASSERT(!stringValues.isEmpty()); 0048 0049 m_eventValues.resize(stringValues.size()); 0050 addValues(stringValues, m_eventValues); 0051 } 0052 0053 int CallgrindCallInformation::eventValue(int type) 0054 { 0055 Q_ASSERT(type < m_eventValues.size()); 0056 return m_eventValues[type]; 0057 } 0058 0059 CallgrindFunction::CallgrindFunction(int eventsCount) 0060 { 0061 m_eventValues.resize(eventsCount); 0062 } 0063 0064 int CallgrindFunction::callCount() 0065 { 0066 int count = 0; 0067 for (auto info : qAsConst(callersInformation)) { 0068 count += info->callCount; 0069 } 0070 return count; 0071 } 0072 0073 int CallgrindFunction::eventValue(int type, bool inclusive) 0074 { 0075 Q_ASSERT(type < m_eventValues.size()); 0076 0077 int value = m_eventValues[type]; 0078 if (!inclusive) { 0079 return value; 0080 } 0081 0082 if (callersInformation.isEmpty()) { 0083 // The function is NOT CALLED by others, therefore we calc 0084 // the event inclusive value as sum of self value and all callees. 0085 for (auto info : qAsConst(calleesInformation)) { 0086 value += info->eventValue(type); 0087 } 0088 return value; 0089 } 0090 0091 // The function is CALLED by others, therefore we calc 0092 // the event inclusive value as sum of all callers. 0093 value = 0; 0094 for (auto info : qAsConst(callersInformation)) { 0095 value += info->eventValue(type); 0096 } 0097 0098 return value; 0099 } 0100 0101 void CallgrindFunction::addEventValues(const QStringList& stringValues) 0102 { 0103 Q_ASSERT(stringValues.size() == m_eventValues.size()); 0104 addValues(stringValues, m_eventValues); 0105 } 0106 0107 CallgrindFunctionsModel::CallgrindFunctionsModel() 0108 : m_currentEventType(0) 0109 , m_percentageValues(false) 0110 { 0111 } 0112 0113 CallgrindFunctionsModel::~CallgrindFunctionsModel() 0114 { 0115 qDeleteAll(m_functions); 0116 qDeleteAll(m_information); 0117 } 0118 0119 const QStringList & CallgrindFunctionsModel::eventTypes() 0120 { 0121 return m_eventTypes; 0122 } 0123 0124 void CallgrindFunctionsModel::setEventTypes(const QStringList& eventTypes) 0125 { 0126 Q_ASSERT(!eventTypes.isEmpty()); 0127 m_eventTypes = eventTypes; 0128 m_eventTotals.resize(m_eventTypes.size()); 0129 } 0130 0131 void CallgrindFunctionsModel::setEventTotals(const QStringList& stringValues) 0132 { 0133 Q_ASSERT(stringValues.size() == m_eventTotals.size()); 0134 addValues(stringValues, m_eventTotals); 0135 } 0136 0137 int CallgrindFunctionsModel::currentEventType() 0138 { 0139 return m_currentEventType; 0140 } 0141 0142 void CallgrindFunctionsModel::setCurrentEventType(int type) 0143 { 0144 Q_ASSERT(type < m_eventTotals.size()); 0145 0146 m_currentEventType = type; 0147 emitDataChanged(this); 0148 } 0149 0150 void CallgrindFunctionsModel::setPercentageValues(bool value) 0151 { 0152 m_percentageValues = value; 0153 emitDataChanged(this); 0154 } 0155 0156 CallgrindFunction* CallgrindFunctionsModel::addFunction( 0157 const QString& name, 0158 const QString& sourceFile, 0159 const QString& binaryFile) 0160 { 0161 Q_ASSERT(!name.isEmpty()); 0162 Q_ASSERT(!m_eventTypes.isEmpty()); 0163 0164 CallgrindFunction* function = nullptr; 0165 0166 for (auto currentFunction : qAsConst(m_functions)) { 0167 if (currentFunction->name == name && (currentFunction->binaryFile.isEmpty() || 0168 binaryFile.isEmpty() || 0169 currentFunction->binaryFile == binaryFile)) { 0170 function = currentFunction; 0171 break; 0172 } 0173 } 0174 0175 if (function) { 0176 if (function->binaryFile.isEmpty()) { 0177 function->binaryFile = binaryFile; 0178 } 0179 0180 function->sourceFiles += sourceFile; 0181 function->sourceFiles.removeDuplicates(); 0182 function->sourceFiles.sort(); 0183 } 0184 0185 else { 0186 function = new CallgrindFunction(m_eventTypes.size()); 0187 0188 function->name = name; 0189 function->binaryFile = binaryFile; 0190 function->sourceFiles += sourceFile; 0191 0192 m_functions.append(function); 0193 } 0194 0195 return function; 0196 } 0197 0198 void CallgrindFunctionsModel::addCall( 0199 CallgrindFunction* caller, 0200 CallgrindFunction* callee, 0201 int callCount, 0202 const QStringList& eventValues) 0203 { 0204 Q_ASSERT(caller); 0205 Q_ASSERT(callee); 0206 0207 auto info = new CallgrindCallInformation(eventValues); 0208 m_information.append(info); 0209 0210 info->caller = caller; 0211 info->callee = callee; 0212 info->callCount = callCount; 0213 0214 caller->calleesInformation.append(info); 0215 callee->callersInformation.append(info); 0216 } 0217 0218 QModelIndex CallgrindFunctionsModel::index(int row, int column, const QModelIndex&) const 0219 { 0220 if (hasIndex(row, column)) { 0221 return createIndex(row, column, m_functions.at(row)); 0222 } 0223 0224 return QModelIndex(); 0225 } 0226 0227 int CallgrindFunctionsModel::rowCount(const QModelIndex&) const 0228 { 0229 return m_functions.size(); 0230 } 0231 0232 int CallgrindFunctionsModel::columnCount(const QModelIndex&) const 0233 { 0234 return 4; 0235 } 0236 0237 QString CallgrindFunctionsModel::displayValue(int eventIntValue, int eventType) const 0238 { 0239 if (m_percentageValues) { 0240 return Valgrind::displayValue(eventIntValue * 100.0 / m_eventTotals[eventType]); 0241 } 0242 0243 return Valgrind::displayValue(eventIntValue); 0244 } 0245 0246 0247 QVariant CallgrindFunctionsModel::data(const QModelIndex& index, int role) const 0248 { 0249 if (!index.isValid()) { 0250 return QVariant(); 0251 } 0252 0253 auto function = static_cast<CallgrindFunction*>(index.internalPointer()); 0254 0255 if (role == Qt::TextAlignmentRole && index.column() < 3) { 0256 return rightAlign; 0257 } 0258 0259 if (index.column() < 2) { 0260 int intValue = function->eventValue(m_currentEventType, (index.column() == 0)); 0261 0262 if (role == SortRole) { 0263 return intValue; 0264 } 0265 0266 if (role == Qt::DisplayRole) { 0267 return displayValue(intValue, m_currentEventType); 0268 } 0269 } 0270 0271 if (index.column() == 2 && (role == Qt::DisplayRole || role == SortRole)) { 0272 return function->callCount(); 0273 } 0274 0275 if (index.column() == 3 && (role == Qt::DisplayRole || role == SortRole)) { 0276 return function->name; 0277 } 0278 0279 return QVariant(); 0280 } 0281 0282 QVariant CallgrindFunctionsModel::headerData(int section, Qt::Orientation, int role) const 0283 { 0284 if (role == Qt::DisplayRole) { 0285 switch (section) { 0286 case 0: return i18n("Incl."); 0287 case 1: return i18n("Self"); 0288 case 2: return i18n("Called"); 0289 case 3: return i18n("Function"); 0290 } 0291 } 0292 0293 return QVariant(); 0294 } 0295 0296 CallgrindFunctionEventsModel::CallgrindFunctionEventsModel(CallgrindFunctionsModel* baseModel) 0297 : QAbstractTableModel(baseModel) 0298 , m_baseModel(baseModel) 0299 , m_function(nullptr) 0300 { 0301 Q_ASSERT(m_baseModel); 0302 0303 connect(m_baseModel, &CallgrindFunctionsModel::dataChanged, 0304 this, [this](const QModelIndex&, const QModelIndex&, const QVector<int>&) { 0305 emitDataChanged(this); 0306 }); 0307 } 0308 0309 void CallgrindFunctionEventsModel::setFunction(CallgrindFunction* function) 0310 { 0311 m_function = function; 0312 emitDataChanged(this); 0313 } 0314 0315 QModelIndex CallgrindFunctionEventsModel::index(int row, int column, const QModelIndex&) const 0316 { 0317 if (hasIndex(row, column)) { 0318 return createIndex(row, column); 0319 } 0320 0321 return QModelIndex(); 0322 } 0323 0324 int CallgrindFunctionEventsModel::rowCount(const QModelIndex&) const 0325 { 0326 return m_baseModel->eventTypes().size(); 0327 } 0328 0329 int CallgrindFunctionEventsModel::columnCount(const QModelIndex&) const 0330 { 0331 return 4; 0332 } 0333 0334 QVariant CallgrindFunctionEventsModel::data(const QModelIndex& index, int role) const 0335 { 0336 if (!index.isValid()) { 0337 return QVariant(); 0338 } 0339 0340 int row = index.row(); 0341 int column = index.column(); 0342 0343 if (column == 0 && role == Qt::DisplayRole) { 0344 return eventFullName(m_baseModel->eventTypes().at(row)); 0345 } 0346 0347 if (column == 3 && role == Qt::DisplayRole) { 0348 return m_baseModel->eventTypes().at(row); 0349 } 0350 0351 if (m_function && (column == 1 || column == 2)) { 0352 if (role == Qt::TextAlignmentRole) { 0353 return rightAlign; 0354 } 0355 0356 int intValue = m_function->eventValue(row, (column == 1)); 0357 0358 if (role == SortRole) { 0359 return intValue; 0360 } 0361 0362 if (role == Qt::DisplayRole) { 0363 return m_baseModel->displayValue(intValue, row); 0364 } 0365 } 0366 0367 return QVariant(); 0368 } 0369 0370 QVariant CallgrindFunctionEventsModel::headerData(int section, Qt::Orientation, int role) const 0371 { 0372 if (role == Qt::DisplayRole) { 0373 switch (section) { 0374 case 0: return i18n("Event"); 0375 case 1: return i18n("Incl."); 0376 case 2: return i18n("Self"); 0377 case 3: return i18n("Short"); 0378 } 0379 } 0380 0381 return QVariant(); 0382 } 0383 0384 CallgrindFunctionCallersCalleesModel::CallgrindFunctionCallersCalleesModel(CallgrindFunctionsModel* baseModel, bool isCallerModel) 0385 : QAbstractTableModel(baseModel) 0386 , m_baseModel(baseModel) 0387 , m_isCallerModel(isCallerModel) 0388 , m_function(nullptr) 0389 { 0390 Q_ASSERT(m_baseModel); 0391 0392 connect(m_baseModel, &CallgrindFunctionsModel::dataChanged, 0393 this, [this](const QModelIndex&, const QModelIndex&, const QVector<int>&) { 0394 0395 emitDataChanged(this); 0396 emit headerDataChanged(Qt::Horizontal, 0, 1); 0397 }); 0398 } 0399 0400 void CallgrindFunctionCallersCalleesModel::setFunction(CallgrindFunction* function) 0401 { 0402 beginResetModel(); 0403 m_function = function; 0404 endResetModel(); 0405 } 0406 0407 QModelIndex CallgrindFunctionCallersCalleesModel::index(int row, int column, const QModelIndex&) const 0408 { 0409 if (hasIndex(row, column)) { 0410 return createIndex(row, column); 0411 } 0412 0413 return QModelIndex(); 0414 } 0415 0416 int CallgrindFunctionCallersCalleesModel::rowCount(const QModelIndex&) const 0417 { 0418 if (!m_function) { 0419 return 0; 0420 } 0421 0422 if (m_isCallerModel) { 0423 return m_function->callersInformation.size(); 0424 } 0425 0426 return m_function->calleesInformation.size(); 0427 } 0428 0429 int CallgrindFunctionCallersCalleesModel::columnCount(const QModelIndex&) const 0430 { 0431 return 4; 0432 } 0433 0434 QVariant CallgrindFunctionCallersCalleesModel::data(const QModelIndex& index, int role) const 0435 { 0436 if (!index.isValid()) { 0437 return QVariant(); 0438 } 0439 0440 int row = index.row(); 0441 int column = index.column(); 0442 0443 if (role == Qt::TextAlignmentRole && column < 3) { 0444 return rightAlign; 0445 } 0446 0447 auto info = m_isCallerModel ? m_function->callersInformation.at(row) : m_function->calleesInformation.at(row); 0448 int eventType = m_baseModel->currentEventType(); 0449 int intValue = info->eventValue(eventType); 0450 int callCount = info->callCount; 0451 0452 if (column == 0) { 0453 if (role == SortRole) { 0454 return intValue; 0455 } 0456 0457 if (role == Qt::DisplayRole) { 0458 return m_baseModel->displayValue(intValue, eventType); 0459 } 0460 } 0461 0462 if (column == 1) { 0463 int perCallValue = intValue / callCount; 0464 0465 if (role == SortRole) { 0466 return perCallValue; 0467 } 0468 0469 if (role == Qt::DisplayRole) { 0470 return displayValue(perCallValue); 0471 } 0472 } 0473 0474 if (column == 2) { 0475 if (role == SortRole || role == Qt::DisplayRole) { 0476 return callCount; 0477 } 0478 } 0479 0480 if (column == 3) { 0481 if (role == SortRole || role == Qt::DisplayRole) { 0482 if (m_isCallerModel) { 0483 return info->caller->name; 0484 } 0485 return info->callee->name; 0486 } 0487 } 0488 0489 return QVariant(); 0490 } 0491 0492 QVariant CallgrindFunctionCallersCalleesModel::headerData(int section, Qt::Orientation, int role) const 0493 { 0494 if (role == Qt::DisplayRole) { 0495 const QString& eventType = m_baseModel->eventTypes().at(m_baseModel->currentEventType()); 0496 0497 switch (section) { 0498 case 0: return eventType; 0499 case 1: return i18n("%1 per call", eventType); 0500 case 2: return i18n("Count"); 0501 case 3: 0502 if (m_isCallerModel) { 0503 return i18n("Caller"); 0504 } 0505 return i18n("Callee"); 0506 } 0507 } 0508 0509 return QVariant(); 0510 } 0511 0512 }