File indexing completed on 2024-04-28 05:41:33

0001 /*
0002     This file is part of KCachegrind.
0003 
0004     SPDX-FileCopyrightText: 2010-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only
0007 */
0008 
0009 #include "functionlistmodel.h"
0010 
0011 #include "globalguiconfig.h"
0012 #include "listutils.h"
0013 
0014 /* helper for setting function filter: we want it to work similar to globbing:
0015  * - escape most special characters in regexps: ( ) [ ] | . \
0016  * - change * to .*
0017  */
0018 QString glob2Regex(QString pattern)
0019 {
0020     pattern.replace(QChar('\\'),QLatin1String("\\\\"));
0021     pattern.replace(QChar('('),QLatin1String("\\("));
0022     pattern.replace(QChar(')'),QLatin1String("\\)"));
0023     pattern.replace(QChar('['),QLatin1String("\\["));
0024     pattern.replace(QChar(']'),QLatin1String("\\]"));
0025     pattern.replace(QChar('|'),QLatin1String("\\|"));
0026     pattern.replace(QChar('.'),QLatin1String("\\."));
0027     pattern.replace(QChar('*'),QLatin1String(".*"));
0028 
0029     return pattern;
0030 }
0031 
0032 
0033 //
0034 // FunctionListModel
0035 //
0036 
0037 FunctionListModel::FunctionListModel()
0038     : QAbstractItemModel(nullptr)
0039 {
0040     _maxCount = 300;
0041     _sortColumn = 0;
0042     _sortOrder = Qt::DescendingOrder;
0043 
0044     _headerData
0045             << tr("Incl.")
0046             << tr("Self")
0047             << tr("Called")
0048             << tr("Function")
0049             << tr("Location");
0050 
0051     _max0 = _max1 = _max2 = nullptr;
0052 }
0053 
0054 FunctionListModel::~FunctionListModel()
0055 {}
0056 
0057 int FunctionListModel::columnCount(const QModelIndex& parent) const
0058 {
0059     return (parent.isValid()) ? 0 : 5;
0060 }
0061 
0062 int FunctionListModel::rowCount(const QModelIndex& parent ) const
0063 {
0064     if (parent.isValid()) return 0;
0065 
0066     int rowCount = _topList.count();
0067     // add one more row if functions are skipped
0068     if (_topList.count() < _filteredList.count()) rowCount++;
0069     return rowCount;
0070 }
0071 
0072 TraceFunction* FunctionListModel::function(const QModelIndex& index)
0073 {
0074     if (!index.isValid()) return nullptr;
0075 
0076     return (TraceFunction*) index.internalPointer();
0077 }
0078 
0079 QVariant FunctionListModel::data(const QModelIndex &index, int role) const
0080 {
0081     if (!index.isValid()) return QVariant();
0082 
0083     // the skipped items entry
0084     if ( (_topList.count() < _filteredList.count()) &&
0085          (index.row() == _topList.count()) ) {
0086         if( (role != Qt::DisplayRole) || (index.column() != 3))
0087             return QVariant();
0088 
0089         return tr("(%1 function(s) skipped)").arg(_filteredList.count() - _topList.count());
0090     }
0091 
0092     TraceFunction *f = (TraceFunction*) index.internalPointer();
0093     Q_ASSERT(f != nullptr);
0094     switch(role) {
0095     case Qt::TextAlignmentRole:
0096         return (index.column()<3) ? Qt::AlignRight : Qt::AlignLeft;
0097 
0098     case Qt::DecorationRole:
0099         switch (index.column()) {
0100         case 0:
0101             return getInclPixmap(f);
0102         case 1:
0103             return getSelfPixmap(f);
0104         case 3:
0105             return getNamePixmap(f);
0106         default:
0107             break;
0108         }
0109         break;
0110 
0111     case Qt::DisplayRole:
0112         switch (index.column()) {
0113         case 0:
0114             return getInclCost(f);
0115         case 1:
0116             return getSelfCost(f);
0117         case 2:
0118             return getCallCount(f);
0119         case 3:
0120             return getName(f);
0121         case 4:
0122             return getLocation(f);
0123         default:
0124             break;
0125         }
0126 
0127     default:
0128         break;
0129     }
0130     return QVariant();
0131 }
0132 
0133 Qt::ItemFlags FunctionListModel::flags(const QModelIndex &index) const
0134 {
0135     if (!index.isValid())
0136         return Qt::NoItemFlags;
0137 
0138     return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
0139 }
0140 
0141 QVariant FunctionListModel::headerData(int section, Qt::Orientation orientation,
0142                                        int role) const
0143 {
0144     if ((orientation != Qt::Horizontal) || (role != Qt::DisplayRole))
0145         return QVariant();
0146 
0147     return _headerData.value(section);
0148 }
0149 
0150 QModelIndex FunctionListModel::index(int row, int column,
0151                                      const QModelIndex &parent) const
0152 {
0153     if (!hasIndex(row, column, parent)) return QModelIndex();
0154 
0155     //the skipped items entry
0156     if ( (_topList.count() < _list.count()) && (row == _topList.count()) )
0157         return createIndex(row, column);
0158 
0159     return createIndex(row, column, (void*)_topList[row]);
0160 }
0161 
0162 QModelIndex FunctionListModel::indexForFunction(TraceFunction *f, bool add)
0163 {
0164     if (!f) return QModelIndex();
0165 
0166     int row = _topList.indexOf(f);
0167     if (row<0) {
0168         // we only add a function from _list matching the filter
0169         if ( !add ||
0170              !_filteredList.contains(f) ) return QModelIndex();
0171 
0172         // find insertion point with current list order
0173         FunctionLessThan lessThan(_sortColumn, _sortOrder, _eventType);
0174         QList<TraceFunction*>::iterator insertPos;
0175         insertPos = std::lower_bound(_topList.begin(), _topList.end(),
0176                                      f, lessThan);
0177         row = insertPos - _topList.begin();
0178         beginInsertRows(QModelIndex(), row, row);
0179         _topList.insert(row, f);
0180         endInsertRows();
0181     }
0182 
0183     return createIndex(row, 0, (void*)f);
0184 }
0185 
0186 QModelIndex FunctionListModel::parent(const QModelIndex& /*index*/ ) const
0187 {
0188     /* only toplevel items */
0189     return QModelIndex();
0190 }
0191 
0192 void FunctionListModel::sort(int column, Qt::SortOrder order)
0193 {
0194     _sortColumn = column;
0195     _sortOrder = order;
0196     computeTopList();
0197 }
0198 
0199 void FunctionListModel::setFilter(QString filterString)
0200 {
0201     if (_filterString == filterString) return;
0202     _filterString = filterString;
0203 
0204     _filter = QRegularExpression(glob2Regex(_filterString),
0205                                  QRegularExpression::CaseInsensitiveOption);
0206     computeFilteredList();
0207     computeTopList();
0208 }
0209 
0210 void FunctionListModel::setEventType(EventType* et)
0211 {
0212     _eventType = et;
0213     // needed to recalculate max value entries
0214     computeFilteredList();
0215     computeTopList();
0216 }
0217 
0218 void FunctionListModel::setMaxCount(int c)
0219 {
0220     if (_maxCount == c) return;
0221     _maxCount = c;
0222     computeTopList();
0223 }
0224 
0225 void FunctionListModel::resetModelData(TraceData *data,
0226                                        TraceCostItem *group, QString filterString,
0227                                        EventType * eventType)
0228 {
0229     _eventType = eventType;
0230 
0231     if (!group) {
0232         _list.clear();
0233         _groupType = ProfileContext::Function;
0234         if (data) {
0235             TraceFunctionMap::iterator i = data->functionMap().begin();
0236             while (i != data->functionMap().end()) {
0237                 _list.append(&(i.value()));
0238                 ++i;
0239             }
0240             foreach(TraceFunction* f, data->functionCycles())
0241                 _list.append(f);
0242         }
0243     }
0244     else {
0245         _groupType = group->type();
0246         switch(_groupType) {
0247         case ProfileContext::Object:
0248         {
0249             TraceObject* o = dynamic_cast<TraceObject*>(group);
0250             Q_ASSERT(o != nullptr);
0251             _list = o->functions();
0252         }
0253             break;
0254 
0255         case ProfileContext::Class:
0256         {
0257             TraceClass* c = dynamic_cast<TraceClass*>(group);
0258             Q_ASSERT(c != nullptr);
0259             _list = c->functions();
0260         }
0261             break;
0262 
0263         case ProfileContext::File:
0264         {
0265             TraceFile* f = dynamic_cast<TraceFile*>(group);
0266             Q_ASSERT(f != nullptr);
0267             _list = f->functions();
0268         }
0269             break;
0270 
0271         case ProfileContext::FunctionCycle:
0272         {
0273             TraceFunctionCycle* c = dynamic_cast<TraceFunctionCycle*>(group);
0274             Q_ASSERT(c != nullptr);
0275             _list = c->members();
0276         }
0277             break;
0278 
0279         default:
0280             _list.clear();
0281             break;
0282         }
0283     }
0284 
0285     _filterString = filterString;
0286     _filter = QRegularExpression(glob2Regex(_filterString),
0287                                  QRegularExpression::CaseInsensitiveOption);
0288 
0289     computeFilteredList();
0290     computeTopList();
0291 }
0292 
0293 void FunctionListModel::computeFilteredList()
0294 {
0295     FunctionLessThan lessThan0(0, Qt::AscendingOrder, _eventType);
0296     FunctionLessThan lessThan1(1, Qt::AscendingOrder, _eventType);
0297     FunctionLessThan lessThan2(2, Qt::AscendingOrder, _eventType);
0298 
0299     // reset max functions
0300     _max0 = nullptr;
0301     _max1 = nullptr;
0302     _max2 = nullptr;
0303 
0304     _filteredList.clear();
0305     int index = 0;
0306     foreach(TraceFunction* f, _list) {
0307         if (!_filterString.isEmpty() && _filter.isValid())
0308             if (!f->name().contains(_filter)) continue;
0309 
0310         _filteredList.append(f);
0311         if (!_max0 || lessThan0(_max0, f)) { _max0 = f; }
0312         if (!_max1 || lessThan1(_max1, f)) { _max1 = f; }
0313         if (!_max2 || lessThan2(_max2, f)) { _max2 = f; }
0314         index++;
0315     }
0316 }
0317 
0318 void FunctionListModel::computeTopList()
0319 {
0320     beginResetModel();
0321     _topList.clear();
0322     if (_filteredList.isEmpty()) {
0323         endResetModel();
0324         return;
0325     }
0326 
0327     FunctionLessThan lessThan(_sortColumn, _sortOrder, _eventType);
0328     std::stable_sort(_filteredList.begin(), _filteredList.end(), lessThan);
0329 
0330     foreach(TraceFunction* f, _filteredList) {
0331         _topList.append(f);
0332         if (_topList.count() >= _maxCount) break;
0333     }
0334 
0335     // append max entries
0336     QList<TraceFunction*> maxList;
0337     if (_max0 && !_topList.contains(_max0)) maxList.append(_max0);
0338     if (_max1 && !_topList.contains(_max1)) maxList.append(_max1);
0339     if (_max2 && !_topList.contains(_max2)) maxList.append(_max2);
0340     std::stable_sort(maxList.begin(), maxList.end(), lessThan);
0341     _topList.append(maxList);
0342 
0343     endResetModel();
0344 }
0345 
0346 QString FunctionListModel::getName(TraceFunction *f) const
0347 {
0348     return f->prettyName();
0349 }
0350 
0351 QPixmap FunctionListModel::getNamePixmap(TraceFunction *f) const
0352 {
0353     QColor c = GlobalGUIConfig::functionColor(_groupType, f);
0354     return colorPixmap(10, 10, c);
0355 }
0356 
0357 QString FunctionListModel::getLocation(TraceFunction *f) const
0358 {
0359     return f->prettyLocation();
0360 }
0361 
0362 QString FunctionListModel::getSelfCost(TraceFunction *f) const
0363 {
0364     ProfileCostArray* selfCost = f->data();
0365     if (GlobalConfig::showExpanded()) {
0366         switch(_groupType) {
0367         case ProfileContext::Object: selfCost = f->object(); break;
0368         case ProfileContext::Class:  selfCost = f->cls(); break;
0369         case ProfileContext::File:   selfCost = f->file(); break;
0370         default: break;
0371         }
0372     }
0373     double selfTotal = selfCost->subCost(_eventType);
0374     if (selfTotal == 0.0)
0375         return QStringLiteral("-");
0376 
0377     // self
0378     SubCost pure = f->subCost(_eventType);
0379     double self  = 100.0 * pure / selfTotal;
0380     if (GlobalConfig::showPercentage())
0381         return QStringLiteral("%1")
0382                 .arg(self, 0, 'f', GlobalConfig::percentPrecision());
0383     else
0384         return f->prettySubCost(_eventType);
0385 }
0386 
0387 QPixmap FunctionListModel::getSelfPixmap(TraceFunction *f) const
0388 {
0389     ProfileCostArray* selfCost = f->data();
0390     if (GlobalConfig::showExpanded()) {
0391         switch(_groupType) {
0392         case ProfileContext::Object: selfCost = f->object(); break;
0393         case ProfileContext::Class:  selfCost = f->cls(); break;
0394         case ProfileContext::File:   selfCost = f->file(); break;
0395         default: break;
0396         }
0397     }
0398     double selfTotal = selfCost->subCost(_eventType);
0399     if (selfTotal == 0.0)
0400         return QPixmap();
0401 
0402     return costPixmap(_eventType, f, selfTotal, false);
0403 }
0404 
0405 QString FunctionListModel::getInclCost(TraceFunction *f) const
0406 {
0407     double inclTotal = f->data()->subCost(_eventType);
0408     if (inclTotal == 0.0)
0409         return QStringLiteral("-");
0410 
0411     SubCost sum  = f->inclusive()->subCost(_eventType);
0412     double incl  = 100.0 * sum / inclTotal;
0413     if (GlobalConfig::showPercentage())
0414         return QStringLiteral("%1")
0415                 .arg(incl, 0, 'f', GlobalConfig::percentPrecision());
0416     else
0417         return f->inclusive()->prettySubCost(_eventType);
0418 }
0419 
0420 QPixmap FunctionListModel::getInclPixmap(TraceFunction *f) const
0421 {
0422     double inclTotal = f->data()->subCost(_eventType);
0423     if (inclTotal == 0.0)
0424         return QPixmap();
0425 
0426     return costPixmap(_eventType, f->inclusive(), inclTotal, false);
0427 }
0428 
0429 
0430 QString FunctionListModel::getCallCount(TraceFunction *f) const
0431 {
0432     QString str;
0433     if (f->calledCount() > 0)
0434         str = f->prettyCalledCount();
0435     else {
0436         if (f == f->cycle())
0437             str = QStringLiteral("-");
0438         else
0439             str = QStringLiteral("(0)");
0440     }
0441     return str;
0442 }
0443 
0444 //
0445 // FunctionListModel::FunctionLessThan
0446 //
0447 bool FunctionListModel::FunctionLessThan::operator()(TraceFunction *left,
0448                                                      TraceFunction *right)
0449 {
0450     TraceFunction* f1 = left;
0451     TraceFunction* f2 = right;
0452 
0453     // descending: swap arguments
0454     if (_order == Qt::DescendingOrder) {
0455         TraceFunction* temp = f1;
0456         f1 = f2;
0457         f2 = temp;
0458     }
0459 
0460     switch(_column) {
0461     case 0:
0462     {
0463         SubCost sum1 = f1->inclusive()->subCost(_eventType);
0464         SubCost sum2 = f2->inclusive()->subCost(_eventType);
0465         return sum1 < sum2;
0466     }
0467 
0468     case 1:
0469     {
0470         SubCost pure1 = f1->subCost(_eventType);
0471         SubCost pure2 = f2->subCost(_eventType);
0472         return pure1 < pure2;
0473     }
0474 
0475     case 2:
0476         return f1->calledCount() < f2->calledCount();
0477 
0478     case 3:
0479         return f1->name() < f2->name();
0480 
0481     case 4:
0482         return f1->object()->name() < f2->object()->name();
0483     }
0484 
0485     return false;
0486 }
0487 
0488 #include "moc_functionlistmodel.cpp"