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"