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