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"