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 }