File indexing completed on 2024-04-28 05:41:32
0001 /* 0002 This file is part of KCachegrind. 0003 0004 SPDX-FileCopyrightText: 2003-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de> 0005 0006 SPDX-License-Identifier: GPL-2.0-only 0007 */ 0008 0009 /* 0010 * Coverage Views 0011 */ 0012 0013 0014 #include "coverageview.h" 0015 0016 #include <QAction> 0017 #include <QMenu> 0018 #include <QHeaderView> 0019 #include <QKeyEvent> 0020 0021 #include "globalconfig.h" 0022 #include "coverageitem.h" 0023 #include "coverage.h" 0024 0025 0026 // 0027 // CoverageView 0028 // 0029 0030 0031 CoverageView::CoverageView(bool showCallers, TraceItemView* parentView, QWidget* parent) 0032 : QTreeWidget(parent), TraceItemView(parentView) 0033 { 0034 _showCallers = showCallers; 0035 0036 QStringList labels; 0037 labels << tr( "Incl." ); 0038 if (_showCallers) { 0039 setColumnCount(4); 0040 labels << tr( "Distance" ), 0041 labels << tr( "Called" ), 0042 labels << tr( "Caller" ); 0043 } 0044 else { 0045 setColumnCount(5); 0046 labels << tr( "Self" ), 0047 labels << tr( "Distance" ), 0048 labels << tr( "Calling" ), 0049 labels << tr( "Callee" ); 0050 } 0051 setHeaderLabels(labels); 0052 0053 // forbid scaling icon pixmaps to smaller size 0054 setIconSize(QSize(99,99)); 0055 setAllColumnsShowFocus(true); 0056 setRootIsDecorated(false); 0057 setUniformRowHeights(true); 0058 // sorting will be enabled after refresh() 0059 sortByColumn(0, Qt::DescendingOrder); 0060 setMinimumHeight(50); 0061 0062 this->setWhatsThis( whatsThis() ); 0063 0064 connect( this, 0065 &QTreeWidget::currentItemChanged, 0066 this, &CoverageView::selectedSlot ); 0067 0068 setContextMenuPolicy(Qt::CustomContextMenu); 0069 connect( this, 0070 &QWidget::customContextMenuRequested, 0071 this, &CoverageView::context); 0072 0073 connect(this, 0074 &QTreeWidget::itemDoubleClicked, 0075 this, &CoverageView::activatedSlot); 0076 0077 connect(header(), &QHeaderView::sectionClicked, 0078 this, &CoverageView::headerClicked); 0079 } 0080 0081 QString CoverageView::whatsThis() const 0082 { 0083 return _showCallers ? 0084 tr( "<b>List of all Callers</b>" 0085 "<p>This list shows all functions calling the " 0086 "current selected one, either directly or with " 0087 "several functions in-between on the stack; the " 0088 "number of functions in-between plus one " 0089 "is called the <em>Distance</em> (e.g. " 0090 "for function A,B,C there exists a call from " 0091 "A to C when A calls B and B calls C, i.e. " 0092 "A => B => C. The distance here is 2).</p>" 0093 0094 "<p>Absolute cost shown is the cost spent in the " 0095 "selected function while a listed function is active; " 0096 "relative cost is the percentage of all cost spent in " 0097 "the selected function while the listed one is " 0098 "active. The cost graphic shows logarithmic " 0099 "percentage with a different color for each " 0100 "distance.</p>" 0101 0102 "<p>As there can be many calls from the same function, " 0103 "the distance column sometimes shows " 0104 "the range of distances for all " 0105 "calls happening; then, in parentheses, there is the " 0106 "medium distance, i.e. the distance where most of the " 0107 "call costs happened.</p>" 0108 0109 "<p>Selecting a function makes it the current selected " 0110 "one of this information panel. " 0111 "If there are two panels (Split mode), the " 0112 "function of the other panel is changed instead.</p>") : 0113 0114 tr( "<b>List of all Callees</b>" 0115 "<p>This list shows all functions called by the " 0116 "current selected one, either directly or with " 0117 "several function in-between on the stack; the " 0118 "number of function in-between plus one " 0119 "is called the <em>Distance</em> (e.g. " 0120 "for function A,B,C there exists a call from " 0121 "A to C when A calls B and B calls C, i.e. " 0122 "A => B => C. The distance here is 2).</p>" 0123 0124 "<p>Absolute cost shown is the cost spent in the " 0125 "listed function while the selected is active; " 0126 "relative cost is the percentage of all cost spent in " 0127 "the listed function while the selected one is active. " 0128 "The cost graphic always shows logarithmic " 0129 "percentage with a different color for each " 0130 "distance.</p>" 0131 0132 "<p>As there can be many calls to the same function, " 0133 "the distance column sometimes shows " 0134 "the range of distances for all " 0135 "calls happening; then, in parentheses, there is the " 0136 "medium distance, i.e. the distance where most of the " 0137 "call costs happened.</p>" 0138 0139 "<p>Selecting a function makes it the current selected " 0140 "one of this information panel. " 0141 "If there are two panels (Split mode), the " 0142 "function of the other panel is changed instead.</p>"); 0143 } 0144 0145 void CoverageView::context(const QPoint & p) 0146 { 0147 int c = columnAt(p.x()); 0148 QTreeWidgetItem* i = itemAt(p); 0149 QMenu popup; 0150 0151 TraceFunction* f = nullptr; 0152 if (i) { 0153 f = _showCallers ? 0154 ((CallerCoverageItem*)i)->function() : 0155 ((CalleeCoverageItem*)i)->function(); 0156 } 0157 0158 QAction* activateFunctionAction = nullptr; 0159 if (f) { 0160 QString menuText = tr("Go to '%1'").arg(GlobalConfig::shortenSymbol(f->prettyName())); 0161 activateFunctionAction = popup.addAction(menuText); 0162 popup.addSeparator(); 0163 } 0164 0165 if ((c == 0) || (!_showCallers && c == 1)) { 0166 addEventTypeMenu(&popup, false); 0167 popup.addSeparator(); 0168 } 0169 addGoMenu(&popup); 0170 0171 QAction* a = popup.exec(mapToGlobal(p + QPoint(0,header()->height()))); 0172 if (a == activateFunctionAction) 0173 TraceItemView::activated(f); 0174 } 0175 0176 void CoverageView::selectedSlot(QTreeWidgetItem* i, QTreeWidgetItem*) 0177 { 0178 TraceFunction* f = nullptr; 0179 if (i) { 0180 f = _showCallers ? 0181 ((CallerCoverageItem*)i)->function() : 0182 ((CalleeCoverageItem*)i)->function(); 0183 } 0184 0185 if (f) { 0186 _selectedItem = f; 0187 selected(f); 0188 } 0189 } 0190 0191 void CoverageView::activatedSlot(QTreeWidgetItem* i, int) 0192 { 0193 TraceFunction* f = nullptr; 0194 if (i) { 0195 f = _showCallers ? 0196 ((CallerCoverageItem*)i)->function() : 0197 ((CalleeCoverageItem*)i)->function(); 0198 } 0199 0200 if (f) TraceItemView::activated(f); 0201 } 0202 0203 void CoverageView::headerClicked(int col) 0204 { 0205 // distance and name columns should be sortable in both ways 0206 if (_showCallers) { 0207 if ((col == 1) || (col==3)) return; 0208 } 0209 else { 0210 if ((col == 2) || (col==4)) return; 0211 } 0212 0213 // all others only descending 0214 sortByColumn(col, Qt::DescendingOrder); 0215 } 0216 0217 void CoverageView::keyPressEvent(QKeyEvent* event) 0218 { 0219 QTreeWidgetItem *item = currentItem(); 0220 if (item && ((event->key() == Qt::Key_Return) || 0221 (event->key() == Qt::Key_Space))) 0222 { 0223 TraceFunction* f; 0224 f = _showCallers ? 0225 ((CallerCoverageItem*)item)->function() : 0226 ((CalleeCoverageItem*)item)->function(); 0227 TraceItemView::activated(f); 0228 } 0229 QTreeView::keyPressEvent(event); 0230 } 0231 0232 CostItem* CoverageView::canShow(CostItem* i) 0233 { 0234 ProfileContext::Type t = i ? i->type() : ProfileContext::InvalidType; 0235 0236 switch(t) { 0237 case ProfileContext::Function: 0238 case ProfileContext::FunctionCycle: 0239 return i; 0240 default: 0241 break; 0242 } 0243 return nullptr; 0244 } 0245 0246 void CoverageView::doUpdate(int changeType, bool) 0247 { 0248 // Special case ? 0249 if (changeType == selectedItemChanged) { 0250 0251 if (!_selectedItem) { 0252 clearSelection(); 0253 return; 0254 } 0255 0256 TraceFunction* f = nullptr; 0257 QTreeWidgetItem* i = currentItem(); 0258 if (i) { 0259 f = _showCallers ? 0260 ((CallerCoverageItem*)i)->function() : 0261 ((CalleeCoverageItem*)i)->function(); 0262 } 0263 if (f == _selectedItem) return; 0264 0265 QTreeWidgetItem *item; 0266 for (int i=0; i<topLevelItemCount(); i++) { 0267 item = this->topLevelItem(i); 0268 f = _showCallers ? 0269 ((CallerCoverageItem*)item)->function() : 0270 ((CalleeCoverageItem*)item)->function(); 0271 if (f == _selectedItem) { 0272 scrollToItem(item); 0273 setCurrentItem(item); 0274 break; 0275 } 0276 } 0277 return; 0278 } 0279 0280 if (changeType == groupTypeChanged) { 0281 QTreeWidgetItem *item; 0282 for (int i=0; i<topLevelItemCount();i++) { 0283 item = topLevelItem(i); 0284 if (_showCallers) 0285 ((CallerCoverageItem*)item)->setGroupType(_groupType); 0286 else 0287 ((CalleeCoverageItem*)item)->setGroupType(_groupType); 0288 } 0289 return; 0290 } 0291 0292 refresh(); 0293 } 0294 0295 void CoverageView::refresh() 0296 { 0297 clear(); 0298 0299 if (!_data || !_activeItem) return; 0300 0301 ProfileContext::Type t = _activeItem->type(); 0302 TraceFunction* f = nullptr; 0303 if (t == ProfileContext::Function) f = (TraceFunction*) _activeItem; 0304 if (t == ProfileContext::FunctionCycle) f = (TraceFunction*) _activeItem; 0305 if (!f) return; 0306 0307 0308 0309 _hc.clear(GlobalConfig::maxListCount()); 0310 SubCost realSum = f->inclusive()->subCost(_eventType); 0311 0312 TraceFunctionList l; 0313 if (_showCallers) 0314 l = Coverage::coverage(f, Coverage::Caller, _eventType); 0315 else 0316 l = Coverage::coverage(f, Coverage::Called, _eventType); 0317 0318 foreach(TraceFunction* f2, l) { 0319 Coverage* c = (Coverage*) f2->association(Coverage::Rtti); 0320 if (c && (c->inclusive()>0.0)) 0321 _hc.addCost(f2, SubCost(realSum * c->inclusive())); 0322 } 0323 0324 QList<QTreeWidgetItem*> items; 0325 QTreeWidgetItem* item; 0326 TraceFunction* ff; 0327 for(int i=0;i<_hc.realCount();i++) { 0328 ff = (TraceFunction*) _hc[i]; 0329 Coverage* c = (Coverage*) ff->association(Coverage::Rtti); 0330 if (_showCallers) 0331 item = new CallerCoverageItem(nullptr, c, f, _eventType, _groupType); 0332 else 0333 item = new CalleeCoverageItem(nullptr, c, f, _eventType, _groupType); 0334 items.append(item); 0335 } 0336 if (_hc.hasMore()) { 0337 // a placeholder for all the functions skipped ... 0338 ff = (TraceFunction*) _hc[_hc.maxSize()-1]; 0339 Coverage* c = (Coverage*) ff->association(Coverage::Rtti); 0340 if (_showCallers) 0341 item = new CallerCoverageItem(nullptr, _hc.count() - _hc.maxSize(), 0342 c, f, _eventType, _groupType); 0343 else 0344 item = new CalleeCoverageItem(nullptr, _hc.count() - _hc.maxSize(), 0345 c, f, _eventType, _groupType); 0346 items.append(item); 0347 } 0348 0349 // when inserting, switch off sorting for performance reason 0350 setSortingEnabled(false); 0351 addTopLevelItems(items); 0352 setSortingEnabled(true); 0353 // enabling sorting switches on the indicator, but we want it off 0354 header()->setSortIndicatorShown(false); 0355 // resize to content now (section size still can be interactively changed) 0356 header()->resizeSections(QHeaderView::ResizeToContents); 0357 0358 if (_data->maxCallCount() == 0) { 0359 // hide call count 0360 setColumnWidth(_showCallers ? 2 : 3, 0); 0361 } 0362 } 0363 0364 #include "moc_coverageview.cpp"