File indexing completed on 2024-05-19 05:44:22
0001 /* 0002 SPDX-FileCopyrightText: 2016-2019 Milian Wolff <mail@milianw.de> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #ifndef CALLERCALLEEMODEL_H 0008 #define CALLERCALLEEMODEL_H 0009 0010 #include <QAbstractItemModel> 0011 #include <QVector> 0012 0013 #include <KLocalizedString> 0014 0015 #include "../allocationdata.h" 0016 #include "hashmodel.h" 0017 #include "locationdata.h" 0018 #include "resultdata.h" 0019 #include "util.h" 0020 0021 using SymbolCostMap = QHash<Symbol, AllocationData>; 0022 Q_DECLARE_METATYPE(SymbolCostMap) 0023 0024 using CalleeMap = SymbolCostMap; 0025 using CallerMap = SymbolCostMap; 0026 0027 struct EntryCost 0028 { 0029 AllocationData inclusiveCost; 0030 AllocationData selfCost; 0031 }; 0032 Q_DECLARE_TYPEINFO(EntryCost, Q_MOVABLE_TYPE); 0033 0034 using LocationCostMap = QHash<FileLine, EntryCost>; 0035 Q_DECLARE_METATYPE(LocationCostMap) 0036 0037 struct CallerCalleeEntry : EntryCost 0038 { 0039 // callers, i.e. other symbols and locations that called this symbol 0040 CallerMap callers; 0041 // callees, i.e. symbols being called from this symbol 0042 CalleeMap callees; 0043 // source map for this symbol, i.e. locations mapped to associated costs 0044 LocationCostMap sourceMap; 0045 }; 0046 0047 using CallerCalleeEntryMap = QHash<Symbol, CallerCalleeEntry>; 0048 struct CallerCalleeResults 0049 { 0050 CallerCalleeEntryMap entries; 0051 std::shared_ptr<const ResultData> resultData; 0052 }; 0053 Q_DECLARE_TYPEINFO(CallerCalleeResults, Q_MOVABLE_TYPE); 0054 Q_DECLARE_METATYPE(CallerCalleeResults) 0055 0056 class CallerCalleeModel : public HashModel<CallerCalleeEntryMap, CallerCalleeModel> 0057 { 0058 Q_OBJECT 0059 public: 0060 explicit CallerCalleeModel(QObject* parent = nullptr); 0061 ~CallerCalleeModel(); 0062 0063 void setResults(const CallerCalleeResults& results); 0064 const CallerCalleeResults& results() const 0065 { 0066 return m_results; 0067 } 0068 void clearData(); 0069 0070 enum Columns 0071 { 0072 LocationColumn = 0, 0073 InclusivePeakColumn, 0074 InclusiveLeakedColumn, 0075 InclusiveAllocationsColumn, 0076 InclusiveTemporaryColumn, 0077 SelfPeakColumn, 0078 SelfLeakedColumn, 0079 SelfAllocationsColumn, 0080 SelfTemporaryColumn, 0081 NUM_COLUMNS 0082 }; 0083 enum 0084 { 0085 InitialSortColumn = InclusivePeakColumn 0086 }; 0087 0088 enum Roles 0089 { 0090 SortRole = Qt::UserRole, 0091 TotalCostRole, 0092 CalleesRole, 0093 CallersRole, 0094 SourceMapRole, 0095 SymbolRole, 0096 ResultDataRole, 0097 }; 0098 0099 QVariant headerCell(int column, int role) const final override; 0100 QVariant cell(int column, int role, const Symbol& symbol, const CallerCalleeEntry& entry) const final override; 0101 int numColumns() const final override; 0102 QModelIndex indexForSymbol(const Symbol& symbol) const; 0103 0104 private: 0105 CallerCalleeResults m_results; 0106 }; 0107 0108 template <typename ModelImpl> 0109 class SymbolCostModelImpl : public HashModel<SymbolCostMap, ModelImpl> 0110 { 0111 public: 0112 explicit SymbolCostModelImpl(QObject* parent = nullptr) 0113 : HashModel<SymbolCostMap, ModelImpl>(parent) 0114 { 0115 } 0116 0117 virtual ~SymbolCostModelImpl() = default; 0118 0119 void setResults(const SymbolCostMap& map, std::shared_ptr<const ResultData> resultData) 0120 { 0121 Q_ASSERT(resultData); 0122 m_resultData = std::move(resultData); 0123 HashModel<SymbolCostMap, ModelImpl>::setRows(map); 0124 } 0125 0126 enum Columns 0127 { 0128 LocationColumn = 0, 0129 PeakColumn, 0130 LeakedColumn, 0131 AllocationsColumn, 0132 TemporaryColumn, 0133 NUM_COLUMNS 0134 }; 0135 enum 0136 { 0137 InitialSortColumn = PeakColumn 0138 }; 0139 0140 enum Roles 0141 { 0142 SortRole = Qt::UserRole, 0143 TotalCostRole, 0144 SymbolRole 0145 }; 0146 0147 QVariant headerCell(int column, int role) const final override 0148 { 0149 if (role == Qt::InitialSortOrderRole && column != LocationColumn) { 0150 return Qt::DescendingOrder; 0151 } else if (role == Qt::DisplayRole) { 0152 switch (static_cast<Columns>(column)) { 0153 case LocationColumn: 0154 return symbolHeader(); 0155 case PeakColumn: 0156 return i18n("Peak"); 0157 case LeakedColumn: 0158 return i18n("Leaked"); 0159 case AllocationsColumn: 0160 return i18n("Allocations"); 0161 case TemporaryColumn: 0162 return i18n("Temporary"); 0163 case NUM_COLUMNS: 0164 break; 0165 } 0166 } else if (role == Qt::ToolTipRole) { 0167 switch (static_cast<Columns>(column)) { 0168 case LocationColumn: 0169 return i18n( 0170 "The location of the %1. The function name may be unresolved when debug information is missing.", 0171 symbolHeader()); 0172 case PeakColumn: 0173 return i18n("<qt>The inclusive maximum heap memory in bytes consumed " 0174 "from allocations originating at this " 0175 "location or from functions called from here. " 0176 "This takes deallocations into account.</qt>"); 0177 case LeakedColumn: 0178 return i18n("<qt>The bytes allocated at this location that have not been " 0179 "deallocated.</qt>"); 0180 case AllocationsColumn: 0181 return i18n("<qt>The inclusive number of times an allocation function " 0182 "was called from this location or any " 0183 "functions called from here.</qt>"); 0184 case TemporaryColumn: 0185 return i18n("<qt>The number of inclusive temporary allocations. These " 0186 "allocations are directly followed by " 0187 "a free without any other allocations in-between.</qt>"); 0188 case NUM_COLUMNS: 0189 break; 0190 } 0191 } 0192 0193 return {}; 0194 } 0195 0196 QVariant cell(int column, int role, const Symbol& symbol, const AllocationData& costs) const final override 0197 { 0198 if (role == SortRole) { 0199 switch (static_cast<Columns>(column)) { 0200 case LocationColumn: 0201 return QVariant::fromValue(symbol); 0202 case PeakColumn: 0203 // NOTE: we sort by unsigned absolute value 0204 return QVariant::fromValue<quint64>(std::abs(costs.peak)); 0205 case LeakedColumn: 0206 return QVariant::fromValue<quint64>(std::abs(costs.leaked)); 0207 case AllocationsColumn: 0208 return QVariant::fromValue<quint64>(std::abs(costs.allocations)); 0209 case TemporaryColumn: 0210 return QVariant::fromValue<quint64>(std::abs(costs.temporary)); 0211 case NUM_COLUMNS: 0212 break; 0213 } 0214 } else if (role == TotalCostRole && column >= PeakColumn) { 0215 switch (static_cast<Columns>(column)) { 0216 case PeakColumn: 0217 return QVariant::fromValue<qint64>(costs.peak); 0218 case LeakedColumn: 0219 return QVariant::fromValue<qint64>(costs.leaked); 0220 case AllocationsColumn: 0221 return QVariant::fromValue<qint64>(costs.allocations); 0222 case TemporaryColumn: 0223 return QVariant::fromValue<qint64>(costs.temporary); 0224 case LocationColumn: 0225 case NUM_COLUMNS: 0226 break; 0227 } 0228 } else if (role == Qt::DisplayRole) { 0229 switch (static_cast<Columns>(column)) { 0230 case LocationColumn: 0231 return Util::toString(symbol, *m_resultData, Util::Short); 0232 case PeakColumn: 0233 return Util::formatBytes(costs.peak); 0234 case LeakedColumn: 0235 return Util::formatBytes(costs.leaked); 0236 case AllocationsColumn: 0237 return QVariant::fromValue<qint64>(costs.allocations); 0238 case TemporaryColumn: 0239 return QVariant::fromValue<qint64>(costs.temporary); 0240 case NUM_COLUMNS: 0241 break; 0242 } 0243 } else if (role == SymbolRole) { 0244 return QVariant::fromValue(symbol); 0245 } else if (role == Qt::ToolTipRole) { 0246 return Util::formatTooltip(symbol, costs, *m_resultData); 0247 } 0248 0249 return {}; 0250 } 0251 0252 int numColumns() const final override 0253 { 0254 return NUM_COLUMNS; 0255 } 0256 0257 private: 0258 virtual QString symbolHeader() const = 0; 0259 0260 std::shared_ptr<const ResultData> m_resultData; 0261 }; 0262 0263 class CallerModel : public SymbolCostModelImpl<CallerModel> 0264 { 0265 Q_OBJECT 0266 public: 0267 explicit CallerModel(QObject* parent = nullptr); 0268 ~CallerModel(); 0269 0270 QString symbolHeader() const final override; 0271 }; 0272 0273 class CalleeModel : public SymbolCostModelImpl<CalleeModel> 0274 { 0275 Q_OBJECT 0276 public: 0277 explicit CalleeModel(QObject* parent = nullptr); 0278 ~CalleeModel(); 0279 0280 QString symbolHeader() const final override; 0281 }; 0282 0283 template <typename ModelImpl> 0284 class LocationCostModelImpl : public HashModel<LocationCostMap, ModelImpl> 0285 { 0286 public: 0287 explicit LocationCostModelImpl(QObject* parent = nullptr) 0288 : HashModel<LocationCostMap, ModelImpl>(parent) 0289 { 0290 } 0291 0292 virtual ~LocationCostModelImpl() = default; 0293 0294 void setResults(const LocationCostMap& map, std::shared_ptr<const ResultData> resultData) 0295 { 0296 m_resultData = std::move(resultData); 0297 HashModel<LocationCostMap, ModelImpl>::setRows(map); 0298 } 0299 0300 enum Columns 0301 { 0302 LocationColumn = 0, 0303 InclusivePeakColumn, 0304 InclusiveLeakedColumn, 0305 InclusiveAllocationsColumn, 0306 InclusiveTemporaryColumn, 0307 SelfPeakColumn, 0308 SelfLeakedColumn, 0309 SelfAllocationsColumn, 0310 SelfTemporaryColumn, 0311 NUM_COLUMNS 0312 }; 0313 enum 0314 { 0315 InitialSortColumn = InclusivePeakColumn 0316 }; 0317 0318 enum Roles 0319 { 0320 SortRole = Qt::UserRole, 0321 TotalCostRole, 0322 ResultDataRole, 0323 LocationRole 0324 }; 0325 0326 QVariant headerCell(int column, int role) const final override 0327 { 0328 if (role == Qt::InitialSortOrderRole && column > LocationColumn) { 0329 return Qt::DescendingOrder; 0330 } else if (role == Qt::DisplayRole) { 0331 switch (static_cast<Columns>(column)) { 0332 case LocationColumn: 0333 return i18n("Location"); 0334 case SelfAllocationsColumn: 0335 return i18n("Allocations (Self)"); 0336 case SelfTemporaryColumn: 0337 return i18n("Temporary (Self)"); 0338 case SelfPeakColumn: 0339 return i18n("Peak (Self)"); 0340 case SelfLeakedColumn: 0341 return i18n("Leaked (Self)"); 0342 case InclusiveAllocationsColumn: 0343 return i18n("Allocations (Incl.)"); 0344 case InclusiveTemporaryColumn: 0345 return i18n("Temporary (Incl.)"); 0346 case InclusivePeakColumn: 0347 return i18n("Peak (Incl.)"); 0348 case InclusiveLeakedColumn: 0349 return i18n("Leaked (Incl.)"); 0350 case NUM_COLUMNS: 0351 break; 0352 } 0353 } else if (role == Qt::ToolTipRole) { 0354 switch (static_cast<Columns>(column)) { 0355 case LocationColumn: 0356 return i18n("<qt>The source code location that called an allocation function. " 0357 "May be unknown when debug information is missing.</qt>"); 0358 case SelfAllocationsColumn: 0359 return i18n("<qt>The number of times an allocation function was directly " 0360 "called from this location.</qt>"); 0361 case SelfTemporaryColumn: 0362 return i18n("<qt>The number of direct temporary allocations. These " 0363 "allocations are directly followed by a " 0364 "free without any other allocations in-between.</qt>"); 0365 case SelfPeakColumn: 0366 return i18n("<qt>The maximum heap memory in bytes consumed from " 0367 "allocations originating directly at " 0368 "this location. " 0369 "This takes deallocations into account.</qt>"); 0370 case SelfLeakedColumn: 0371 return i18n("<qt>The bytes allocated directly at this location that have " 0372 "not been deallocated.</qt>"); 0373 case InclusiveAllocationsColumn: 0374 return i18n("<qt>The inclusive number of times an allocation function " 0375 "was called from this location or any " 0376 "functions called from here.</qt>"); 0377 case InclusiveTemporaryColumn: 0378 return i18n("<qt>The number of inclusive temporary allocations. These " 0379 "allocations are directly followed by " 0380 "a free without any other allocations in-between.</qt>"); 0381 case InclusivePeakColumn: 0382 return i18n("<qt>The inclusive maximum heap memory in bytes consumed " 0383 "from allocations originating at this " 0384 "location or from functions called from here. " 0385 "This takes deallocations into account.</qt>"); 0386 case InclusiveLeakedColumn: 0387 return i18n("<qt>The bytes allocated at this location that have not been " 0388 "deallocated.</qt>"); 0389 case NUM_COLUMNS: 0390 break; 0391 } 0392 } 0393 0394 return {}; 0395 } 0396 0397 QVariant cell(int column, int role, const FileLine& location, const EntryCost& costs) const final override 0398 { 0399 if (role == SortRole) { 0400 switch (static_cast<Columns>(column)) { 0401 case LocationColumn: 0402 return Util::toString(location, *m_resultData, Util::Long); 0403 case SelfAllocationsColumn: 0404 // NOTE: we sort by unsigned absolute value 0405 return QVariant::fromValue<quint64>(std::abs(costs.selfCost.allocations)); 0406 case SelfTemporaryColumn: 0407 return QVariant::fromValue<quint64>(std::abs(costs.selfCost.temporary)); 0408 case SelfPeakColumn: 0409 return QVariant::fromValue<quint64>(std::abs(costs.selfCost.peak)); 0410 case SelfLeakedColumn: 0411 return QVariant::fromValue<quint64>(std::abs(costs.selfCost.leaked)); 0412 case InclusiveAllocationsColumn: 0413 return QVariant::fromValue<quint64>(std::abs(costs.inclusiveCost.allocations)); 0414 case InclusiveTemporaryColumn: 0415 return QVariant::fromValue<quint64>(std::abs(costs.inclusiveCost.temporary)); 0416 case InclusivePeakColumn: 0417 return QVariant::fromValue<quint64>(std::abs(costs.inclusiveCost.peak)); 0418 case InclusiveLeakedColumn: 0419 return QVariant::fromValue<quint64>(std::abs(costs.inclusiveCost.leaked)); 0420 case NUM_COLUMNS: 0421 break; 0422 } 0423 } else if (role == TotalCostRole) { 0424 switch (static_cast<Columns>(column)) { 0425 case SelfAllocationsColumn: 0426 case InclusiveAllocationsColumn: 0427 return QVariant::fromValue<qint64>(m_resultData->totalCosts().allocations); 0428 case SelfTemporaryColumn: 0429 case InclusiveTemporaryColumn: 0430 return QVariant::fromValue<qint64>(m_resultData->totalCosts().temporary); 0431 case SelfPeakColumn: 0432 case InclusivePeakColumn: 0433 return QVariant::fromValue<qint64>(m_resultData->totalCosts().peak); 0434 case SelfLeakedColumn: 0435 case InclusiveLeakedColumn: 0436 return QVariant::fromValue<qint64>(m_resultData->totalCosts().leaked); 0437 case LocationColumn: 0438 case NUM_COLUMNS: 0439 break; 0440 } 0441 } else if (role == Qt::DisplayRole) { 0442 switch (static_cast<Columns>(column)) { 0443 case LocationColumn: 0444 return Util::toString(location, *m_resultData, Util::Short); 0445 case SelfAllocationsColumn: 0446 return QVariant::fromValue<qint64>(costs.selfCost.allocations); 0447 case SelfTemporaryColumn: 0448 return QVariant::fromValue<qint64>(costs.selfCost.temporary); 0449 case SelfPeakColumn: 0450 return Util::formatBytes(costs.selfCost.peak); 0451 case SelfLeakedColumn: 0452 return Util::formatBytes(costs.selfCost.leaked); 0453 case InclusiveAllocationsColumn: 0454 return QVariant::fromValue<qint64>(costs.inclusiveCost.allocations); 0455 case InclusiveTemporaryColumn: 0456 return QVariant::fromValue<qint64>(costs.inclusiveCost.temporary); 0457 case InclusivePeakColumn: 0458 return Util::formatBytes(costs.inclusiveCost.peak); 0459 case InclusiveLeakedColumn: 0460 return Util::formatBytes(costs.inclusiveCost.leaked); 0461 case NUM_COLUMNS: 0462 break; 0463 } 0464 } else if (role == LocationRole) { 0465 return QVariant::fromValue(location); 0466 } else if (role == ResultDataRole) { 0467 return QVariant::fromValue(m_resultData.get()); 0468 } else if (role == Qt::ToolTipRole) { 0469 return Util::formatTooltip(location, costs.selfCost, costs.inclusiveCost, *m_resultData); 0470 } 0471 0472 return {}; 0473 } 0474 0475 int numColumns() const final override 0476 { 0477 return NUM_COLUMNS; 0478 } 0479 0480 private: 0481 std::shared_ptr<const ResultData> m_resultData; 0482 }; 0483 0484 class SourceMapModel : public LocationCostModelImpl<SourceMapModel> 0485 { 0486 Q_OBJECT 0487 public: 0488 explicit SourceMapModel(QObject* parent = nullptr); 0489 ~SourceMapModel(); 0490 }; 0491 0492 #endif // CALLERCALLEEMODEL_H