File indexing completed on 2024-04-28 05:41:35
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 * For part file selection, to be put into a QDockWindow 0011 */ 0012 0013 #include "partselection.h" 0014 0015 #include <QLabel> 0016 #include <QPushButton> 0017 #include <QVBoxLayout> 0018 #include <QAction> 0019 #include <QMenu> 0020 0021 #include "toplevelbase.h" 0022 #include "partgraph.h" 0023 #include "globalconfig.h" 0024 #include "config.h" 0025 0026 // 0027 // PartSelection 0028 // 0029 0030 // defaults 0031 #define DEFAULT_PARTITIONMODE "Inclusive" 0032 #define DEFAULT_DIAGRAMMODE false 0033 #define DEFAULT_DRAWFRAMES true 0034 #define DEFAULT_SHOWINFO false 0035 #define DEFAULT_FUNCTIONZOOM false 0036 #define DEFAULT_CALLEELEVELS 1 0037 #define DEFAULT_DRAWNAME true 0038 #define DEFAULT_DRAWCOST true 0039 #define DEFAULT_FORCESTRINGS false 0040 #define DEFAULT_ALLOWROTATION true 0041 0042 PartSelection::PartSelection( TopLevelBase* top, 0043 QWidget* parent) 0044 : QWidget(parent), TraceItemView(nullptr, top) 0045 { 0046 _inSelectionUpdate = false; 0047 0048 setWindowTitle(tr("Parts Overview")); 0049 0050 QVBoxLayout* vboxLayout = new QVBoxLayout(this); 0051 vboxLayout->setSpacing(6); 0052 vboxLayout->setContentsMargins(6, 6, 6, 6); 0053 0054 _partAreaWidget = new PartAreaWidget(this); 0055 _partAreaWidget->setMinimumHeight(50); 0056 _partAreaWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0057 _partAreaWidget->setMaxSelectDepth(2); 0058 _partAreaWidget->setSelectionMode(TreeMapWidget::Extended); 0059 _partAreaWidget->setSplitMode(TreeMapItem::HAlternate); 0060 _partAreaWidget->setVisibleWidth(2, true); 0061 _partAreaWidget->setFieldType(0, tr("Name", "A thing's name")); 0062 _partAreaWidget->setFieldType(1, tr("Cost" )); 0063 vboxLayout->addWidget(_partAreaWidget); 0064 0065 _rangeLabel = new QLabel(this); 0066 _rangeLabel->setWordWrap(false); 0067 _rangeLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); 0068 vboxLayout->addWidget(_rangeLabel); 0069 _rangeLabel->setText(tr("(no trace parts)")); 0070 _showInfo = true; // label is currently shown 0071 0072 _diagramMode = DEFAULT_DIAGRAMMODE; 0073 _drawFrames = DEFAULT_DRAWFRAMES; 0074 0075 // sets _showInfo 0076 showInfo(DEFAULT_SHOWINFO); 0077 0078 _partAreaWidget->setAllowRotation(DEFAULT_ALLOWROTATION); 0079 0080 connect(_partAreaWidget, SIGNAL(selectionChanged()), 0081 this, SLOT(selectionChanged())); 0082 connect(_partAreaWidget, &TreeMapWidget::currentChanged, 0083 this, &PartSelection::currentChangedSlot); 0084 connect(_partAreaWidget, &TreeMapWidget::doubleClicked, 0085 this, &PartSelection::doubleClicked); 0086 connect(_partAreaWidget, 0087 &TreeMapWidget::contextMenuRequested, 0088 this, 0089 &PartSelection::contextMenuRequested); 0090 0091 setWhatsThis(whatsThis()); 0092 } 0093 0094 QString PartSelection::whatsThis() const 0095 { 0096 return tr( "<b>The Parts Overview</b>" 0097 "<p>A trace consists of multiple trace parts when " 0098 "there are several profile data files from one profile run. " 0099 "The Trace Part Overview dockable shows these, " 0100 "horizontally ordered in execution time; " 0101 "the rectangle sizes are proportional to the total " 0102 "cost spent in the parts. You can select one or several " 0103 "parts to constrain all costs shown to these parts only." 0104 "</p>" 0105 "<p>The parts are further subdivided: there is a " 0106 "partitioning and an callee split mode: " 0107 "<ul><li>Partitioning: You see the " 0108 "partitioning into groups for a trace part, according to " 0109 "the group type selected. E.g. if ELF object groups are " 0110 "selected, you see colored rectangles for each " 0111 "used ELF object (shared library or executable), sized " 0112 "according to the cost spent therein.</li>" 0113 "<li>Callee: A rectangle showing the inclusive " 0114 "cost of the current selected function in the trace part " 0115 "is shown. " 0116 "This is split up into smaller rectangles to show the costs of its " 0117 "callees.</li></ul></p>"); 0118 } 0119 0120 void PartSelection::setData(TraceData* data) 0121 { 0122 TraceItemView::setData(data); 0123 _partAreaWidget->setData(data); 0124 } 0125 0126 0127 CostItem* PartSelection::canShow(CostItem* i) 0128 { 0129 ProfileContext::Type t = i ? i->type() : ProfileContext::InvalidType; 0130 0131 switch(t) { 0132 case ProfileContext::Function: 0133 case ProfileContext::FunctionCycle: 0134 return i; 0135 default: 0136 break; 0137 } 0138 return nullptr; 0139 } 0140 0141 /* Helper for doUpdate(), called on partsChanged event. 0142 * This makes the graph selection the same to the parts in the list 0143 */ 0144 void PartSelection::selectParts(const TracePartList& list) 0145 { 0146 _inSelectionUpdate = true; 0147 0148 qDebug("Entering PartSelection::activePartsChangedSlot"); 0149 0150 TreeMapItemList l = *_partAreaWidget->base()->children(); 0151 // first deselect inactive, then select active (makes current active) 0152 foreach(TreeMapItem* i, l) { 0153 TracePart* part = ((PartItem*)i)->part(); 0154 bool active = list.contains(part); 0155 if (!active && _partAreaWidget->isSelected(i)) { 0156 #if 0 0157 qDebug("PartSelection::selectParts: Part %s changed to unselected.", 0158 ((PartItem*)i)->part()->shortName()); 0159 #endif 0160 0161 _partAreaWidget->setSelected(i, false); 0162 } 0163 } 0164 foreach(TreeMapItem* i, l) { 0165 TracePart* part = ((PartItem*)i)->part(); 0166 bool active = list.contains(part); 0167 if (active && !_partAreaWidget->isSelected(i)) { 0168 #if 0 0169 qDebug("PartSelection::selectParts: Part %s changed to selected.", 0170 ((PartItem*)i)->part()->shortName()); 0171 #endif 0172 _partAreaWidget->setSelected(i, true); 0173 } 0174 } 0175 0176 _inSelectionUpdate = false; 0177 0178 qDebug("Leaving PartSelection::activePartsChangedSlot"); 0179 } 0180 0181 0182 void PartSelection::doUpdate(int changeType, bool) 0183 { 0184 if (changeType == eventType2Changed) return; 0185 if (changeType == selectedItemChanged) return; 0186 0187 if (changeType & eventTypeChanged) 0188 _partAreaWidget->setEventType(_eventType); 0189 0190 if (changeType & groupTypeChanged) 0191 _partAreaWidget->setGroupType(_groupType); 0192 0193 if (changeType & activeItemChanged) { 0194 TraceFunction* f = nullptr; 0195 0196 if (_activeItem) { 0197 switch(_activeItem->type()) { 0198 case ProfileContext::Function: 0199 case ProfileContext::FunctionCycle: 0200 f = (TraceFunction*)_activeItem; 0201 break; 0202 default: 0203 break; 0204 } 0205 } 0206 0207 _inSelectionUpdate = true; 0208 _partAreaWidget->setFunction(f); 0209 _inSelectionUpdate = false; 0210 } 0211 0212 if (changeType & partsChanged) 0213 selectParts(_partList); 0214 0215 _partAreaWidget->redraw(); 0216 fillInfo(); 0217 } 0218 0219 0220 0221 void PartSelection::currentChangedSlot(TreeMapItem* i, bool kbd) 0222 { 0223 if (!i) return; 0224 if (!kbd) return; 0225 if (i->text(0).isEmpty()) return; 0226 0227 if (_topLevel) { 0228 QString str = i->text(0); 0229 if (!i->text(1).isEmpty()) 0230 str += " (" + i->text(1) + ')'; 0231 QString msg = tr("Profile Part Overview: Current is '%1'").arg(str); 0232 0233 _topLevel->showMessage(msg, 5000); 0234 } 0235 0236 if (_showInfo) fillInfo(); 0237 } 0238 0239 0240 void PartSelection::doubleClicked(TreeMapItem* i) 0241 { 0242 if (!i || i->rtti() != 3) return; 0243 0244 ProfileCostArray* c = ((SubPartItem*) i)->partCostItem(); 0245 TraceCostItem* ci = nullptr; 0246 0247 switch(c->type()) { 0248 case ProfileContext::PartFunction: 0249 { 0250 TraceFunction* f = ((TracePartFunction*)c)->function(); 0251 if (f) 0252 activated(f); 0253 } 0254 return; 0255 0256 case ProfileContext::PartObject: 0257 ci = ((TracePartObject*)c)->object(); 0258 break; 0259 case ProfileContext::PartClass: 0260 ci = ((TracePartClass*)c)->cls(); 0261 break; 0262 case ProfileContext::PartFile: 0263 ci = ((TracePartFile*)c)->file(); 0264 break; 0265 default: 0266 break; 0267 } 0268 0269 if (ci) 0270 activated(ci); 0271 } 0272 0273 0274 void PartSelection::selectionChanged() 0275 { 0276 if (_inSelectionUpdate) return; 0277 0278 qDebug("PartSelection::selectionChanged"); 0279 0280 bool something_changed = false; 0281 bool nothingSelected = true; 0282 0283 TracePartList pList; 0284 TracePart* part; 0285 0286 // if nothing is selected, activate all parts 0287 TreeMapItemList* list = _partAreaWidget->base()->children(); 0288 if (!list) return; 0289 0290 foreach(TreeMapItem* i, *list) 0291 if (_partAreaWidget->isSelected(i)) { 0292 nothingSelected = false; 0293 break; 0294 } 0295 0296 foreach(TreeMapItem* i, *list) { 0297 part = ((PartItem*)i)->part(); 0298 bool active = nothingSelected || _partAreaWidget->isSelected(i); 0299 if (active) { 0300 pList.append(part); 0301 something_changed = true; 0302 } 0303 } 0304 0305 if (something_changed) { 0306 //qDebug("PartSelection: Something changed."); 0307 partsSelected(pList); 0308 } 0309 } 0310 0311 void PartSelection::itemSelected() 0312 { 0313 QAction* a = qobject_cast<QAction*>(sender()); 0314 if (a) 0315 doubleClicked( (TreeMapItem*) a->data().value<void*>() ); 0316 } 0317 0318 void PartSelection::contextMenuRequested(TreeMapItem* i, 0319 const QPoint & p) 0320 { 0321 if (!i) return; 0322 0323 QMenu popup; 0324 QAction* a; 0325 0326 QString str; 0327 TreeMapItem* s = nullptr; 0328 0329 QAction* selectPartAction = nullptr; 0330 QAction* selectAllPartsAction = nullptr; 0331 QAction* hidePartsAction = nullptr; 0332 QAction* showPartsAction = nullptr; 0333 if (_data && (_data->parts().count()>1)) { 0334 s = _partAreaWidget->possibleSelection(i); 0335 if (!s->text(0).isEmpty()) { 0336 if (_partAreaWidget->isSelected(s)) 0337 str = tr("Deselect '%1'").arg(s->text(0)); 0338 else 0339 str = tr("Select '%1'").arg(s->text(0)); 0340 0341 selectPartAction = popup.addAction(str); 0342 } 0343 0344 selectAllPartsAction = popup.addAction(tr("Select All Parts")); 0345 QMenu* ppopup = popup.addMenu(tr("Visible Parts")); 0346 hidePartsAction = ppopup->addAction(tr("Hide Selected Parts")); 0347 showPartsAction = ppopup->addAction(tr("Show Hidden Parts")); 0348 0349 popup.addSeparator(); 0350 } 0351 0352 addGoMenu(&popup); 0353 0354 if (i->rtti() == 3) { 0355 TreeMapItem* ni = i; 0356 while (ni && ni->rtti() == 3) { 0357 ProfileCostArray* c = ((SubPartItem*)ni)->partCostItem(); 0358 if (c->type() == ProfileContext::PartFunction) 0359 if ( ((TracePartFunction*)c)->function() == _selectedItem) break; 0360 0361 str = tr("Go to '%1'").arg(GlobalConfig::shortenSymbol(ni->text(0))); 0362 a = popup.addAction(str); 0363 a->setData(QVariant::fromValue( (void*)ni )); 0364 connect(a, &QAction::triggered, this, &PartSelection::itemSelected); 0365 ni = ni->parent(); 0366 } 0367 } 0368 popup.addSeparator(); 0369 0370 QMenu* vpopup = popup.addMenu(tr("Visualization")); 0371 QAction* showPartitioningAction = vpopup->addAction(tr("Partitioning Mode")); 0372 showPartitioningAction->setCheckable(true); 0373 QAction* zoomFunctionAction = vpopup->addAction(tr("Zoom Function")); 0374 zoomFunctionAction->setCheckable(true); 0375 QAction* directCallsAction = vpopup->addAction(tr("Show Direct Calls")); 0376 QAction* incCallsAction = vpopup->addAction(tr("Increment Shown Call Levels")); 0377 QAction* showDiagramAction = vpopup->addAction(tr("Diagram Mode")); 0378 showDiagramAction->setCheckable(true); 0379 if (_partAreaWidget->visualization() == PartAreaWidget::Partitioning) { 0380 showPartitioningAction->setChecked(true); 0381 zoomFunctionAction->setEnabled(false); 0382 directCallsAction->setEnabled(false); 0383 incCallsAction->setEnabled(false); 0384 } 0385 else { 0386 zoomFunctionAction->setChecked(_partAreaWidget->zoomFunction()); 0387 } 0388 showDiagramAction->setChecked(_diagramMode); 0389 0390 vpopup->addSeparator(); 0391 0392 QAction* drawNamesAction = vpopup->addAction(tr("Draw Names")); 0393 drawNamesAction->setCheckable(true); 0394 QAction* drawCostsAction = vpopup->addAction(tr("Draw Costs")); 0395 drawCostsAction->setCheckable(true); 0396 QAction* ignorePropsAction = vpopup->addAction(tr("Ignore Proportions")); 0397 ignorePropsAction->setCheckable(true); 0398 QAction* allowRotationAction = vpopup->addAction(tr("Allow Rotation")); 0399 allowRotationAction->setCheckable(true); 0400 QAction* drawFramesAction = vpopup->addAction(tr("Draw Frames")); 0401 drawFramesAction->setCheckable(true); 0402 if (!_partAreaWidget->fieldVisible(0) && 0403 !_partAreaWidget->fieldVisible(1)) { 0404 ignorePropsAction->setEnabled(false); 0405 allowRotationAction->setEnabled(false); 0406 } 0407 else { 0408 drawNamesAction->setChecked(_partAreaWidget->fieldVisible(0)); 0409 drawCostsAction->setChecked(_partAreaWidget->fieldVisible(1)); 0410 ignorePropsAction->setChecked(_partAreaWidget->fieldForced(0)); 0411 allowRotationAction->setChecked(_partAreaWidget->allowRotation()); 0412 drawFramesAction->setChecked(_drawFrames); 0413 } 0414 QAction* showInfoAction = popup.addAction(_showInfo ? tr("Hide Info"):tr("Show Info")); 0415 0416 a = popup.exec(_partAreaWidget->mapToGlobal(p)); 0417 0418 if (a == selectPartAction) { 0419 // select/deselect part under mouse 0420 _partAreaWidget->setSelected(s, !_partAreaWidget->isSelected(s)); 0421 } 0422 else if (a == selectAllPartsAction) { 0423 // select all parts 0424 TreeMapItemList list = *_partAreaWidget->base()->children(); 0425 _partAreaWidget->setRangeSelection(list.first(), list.last(), true); 0426 } 0427 else if (a == hidePartsAction) 0428 emit partsHideSelected(); 0429 else if (a == showPartsAction) 0430 emit partsUnhideAll(); 0431 else if (a == drawNamesAction) 0432 _partAreaWidget->setFieldVisible(0, !_partAreaWidget->fieldVisible(0)); 0433 else if (a == drawCostsAction) 0434 _partAreaWidget->setFieldVisible(1, !_partAreaWidget->fieldVisible(1)); 0435 else if (a == ignorePropsAction) { 0436 _partAreaWidget->setFieldForced(0, !_partAreaWidget->fieldForced(0)); 0437 _partAreaWidget->setFieldForced(1, !_partAreaWidget->fieldForced(0)); 0438 } 0439 else if (a == allowRotationAction) 0440 _partAreaWidget->setAllowRotation(!_partAreaWidget->allowRotation()); 0441 else if (a == drawFramesAction) { 0442 _drawFrames = !_drawFrames; 0443 _partAreaWidget->drawFrame(2,_drawFrames); 0444 _partAreaWidget->drawFrame(3,_drawFrames); 0445 } 0446 else if (a == showInfoAction) 0447 showInfo(!_showInfo); 0448 else if (a == showPartitioningAction) 0449 _partAreaWidget->setVisualization( 0450 (_partAreaWidget->visualization() != PartAreaWidget::Partitioning) ? 0451 PartAreaWidget::Partitioning : PartAreaWidget::Inclusive ); 0452 else if (a == zoomFunctionAction) { 0453 // zoom/unzoom function 0454 _partAreaWidget->setZoomFunction(!_partAreaWidget->zoomFunction()); 0455 } 0456 else if (a == directCallsAction) 0457 _partAreaWidget->setCallLevels(1); 0458 else if (a == incCallsAction) { 0459 int l = _partAreaWidget->callLevels()+1; 0460 _partAreaWidget->setCallLevels(l); 0461 } 0462 else if (a == showDiagramAction) { 0463 _diagramMode = !_diagramMode; 0464 _partAreaWidget->setTransparent(2,_diagramMode); 0465 } 0466 } 0467 0468 void PartSelection::hiddenPartsChangedSlot(const TracePartList& list) 0469 { 0470 _partAreaWidget->changeHidden(list); 0471 } 0472 0473 void PartSelection::restoreOptions(const QString& prefix, const QString& postfix) 0474 { 0475 ConfigGroup* g = ConfigStorage::group(prefix, postfix); 0476 0477 QString pmode = g->value(QStringLiteral("PartitionMode"), 0478 QStringLiteral(DEFAULT_PARTITIONMODE)).toString(); 0479 if (pmode == QLatin1String("Inclusive")) 0480 _partAreaWidget->setVisualization(PartAreaWidget::Inclusive); 0481 else 0482 _partAreaWidget->setVisualization(PartAreaWidget::Partitioning); 0483 0484 _diagramMode = g->value(QStringLiteral("DiagramMode"), DEFAULT_DIAGRAMMODE).toBool(); 0485 _partAreaWidget->setTransparent(2,_diagramMode); 0486 0487 _drawFrames = g->value(QStringLiteral("DrawFrames"), DEFAULT_DRAWFRAMES).toBool(); 0488 _partAreaWidget->drawFrame(2,_drawFrames); 0489 _partAreaWidget->drawFrame(3,_drawFrames); 0490 0491 showInfo(g->value(QStringLiteral("ShowInfo"), DEFAULT_SHOWINFO).toBool()); 0492 0493 bool enable = g->value(QStringLiteral("FunctionZoom"), DEFAULT_FUNCTIONZOOM).toBool(); 0494 _partAreaWidget->setZoomFunction(enable); 0495 0496 int levels = g->value(QStringLiteral("CalleeLevels"), DEFAULT_CALLEELEVELS).toInt(); 0497 _partAreaWidget->setCallLevels(levels); 0498 0499 enable = g->value(QStringLiteral("DrawName"), DEFAULT_DRAWNAME).toBool(); 0500 _partAreaWidget->setFieldVisible(0, enable); 0501 0502 enable = g->value(QStringLiteral("DrawCost"), DEFAULT_DRAWCOST).toBool(); 0503 _partAreaWidget->setFieldVisible(1, enable); 0504 0505 enable = g->value(QStringLiteral("ForceStrings"), DEFAULT_FORCESTRINGS).toBool(); 0506 _partAreaWidget->setFieldForced(0, enable); 0507 _partAreaWidget->setFieldForced(1, enable); 0508 0509 enable = g->value(QStringLiteral("AllowRotation"), DEFAULT_ALLOWROTATION).toBool(); 0510 _partAreaWidget->setAllowRotation(enable); 0511 0512 delete g; 0513 } 0514 0515 void PartSelection::saveOptions(const QString& prefix, const QString& postfix) 0516 { 0517 ConfigGroup* g = ConfigStorage::group(prefix + postfix); 0518 0519 QString mode; 0520 if (_partAreaWidget->visualization() == PartAreaWidget::Inclusive) 0521 mode = QStringLiteral("Inclusive"); 0522 else 0523 mode = QStringLiteral("Partitioning"); 0524 0525 g->setValue(QStringLiteral("PartitionMode"), mode, QStringLiteral(DEFAULT_PARTITIONMODE)); 0526 g->setValue(QStringLiteral("DiagramMode"), _diagramMode, DEFAULT_DIAGRAMMODE); 0527 g->setValue(QStringLiteral("DrawFrames"), _drawFrames, DEFAULT_DRAWFRAMES); 0528 g->setValue(QStringLiteral("ShowInfo"), _showInfo, DEFAULT_SHOWINFO); 0529 0530 g->setValue(QStringLiteral("FunctionZoom"), 0531 _partAreaWidget->zoomFunction(), DEFAULT_FUNCTIONZOOM); 0532 g->setValue(QStringLiteral("CalleeLevels"), 0533 _partAreaWidget->callLevels(), DEFAULT_CALLEELEVELS); 0534 g->setValue(QStringLiteral("DrawName"), 0535 _partAreaWidget->fieldVisible(0), DEFAULT_DRAWNAME); 0536 g->setValue(QStringLiteral("DrawCost"), 0537 _partAreaWidget->fieldVisible(1), DEFAULT_DRAWCOST); 0538 g->setValue(QStringLiteral("ForceStrings"), 0539 _partAreaWidget->fieldForced(0), DEFAULT_FORCESTRINGS); 0540 g->setValue(QStringLiteral("AllowRotation"), 0541 _partAreaWidget->allowRotation(), DEFAULT_ALLOWROTATION); 0542 0543 delete g; 0544 } 0545 0546 void PartSelection::showInfo(bool enable) 0547 { 0548 if (_showInfo == enable) return; 0549 0550 _showInfo = enable; 0551 if (enable) { 0552 _rangeLabel->show(); 0553 fillInfo(); 0554 } 0555 else 0556 _rangeLabel->hide(); 0557 } 0558 0559 void PartSelection::fillInfo() 0560 { 0561 if (!_data) { 0562 _rangeLabel->setText(tr("(no trace loaded)")); 0563 return; 0564 } 0565 0566 QString info = _data->activePartRange(); 0567 0568 TreeMapItem* i = _partAreaWidget->current(); 0569 while (i && i->rtti()!=2) i = i->parent(); 0570 if (i) { 0571 TracePart* part = ((PartItem*)i)->part(); 0572 0573 //if (!part->trigger().isEmpty()) info += ", " + part->trigger(); 0574 if (!part->timeframe().isEmpty()) 0575 info += ", Time " + part->timeframe() + " BBs"; 0576 } 0577 else { 0578 TracePart* part = _data->parts().first(); 0579 0580 if (part && !part->version().isEmpty()) 0581 info += ", Cachegrind " + part->version(); 0582 } 0583 0584 0585 _rangeLabel->setText(info); 0586 } 0587 0588 #include "moc_partselection.cpp"