File indexing completed on 2024-04-28 05:41:23
0001 /* 0002 This file is part of KCachegrind. 0003 0004 SPDX-FileCopyrightText: 2002-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de> 0005 0006 SPDX-License-Identifier: GPL-2.0-only 0007 */ 0008 0009 /* 0010 * KCachegrind top level window 0011 */ 0012 0013 #define TRACE_UPDATES 0 0014 #define ENABLE_DUMPDOCK 0 0015 0016 #include "toplevel.h" 0017 #include <karchive_version.h> 0018 0019 #include <stdlib.h> // for system() 0020 0021 #include <QDebug> 0022 #include <QDockWidget> 0023 #include <QEventLoop> 0024 #include <QFile> 0025 #include <QFileDialog> 0026 #include <QLabel> 0027 #include <QLineEdit> 0028 #include <QMenu> 0029 #include <QMimeDatabase> 0030 #include <QProcess> 0031 #include <QProgressBar> 0032 #include <QStatusBar> 0033 #include <QTemporaryFile> 0034 #include <QTimer> 0035 #include <QUrl> 0036 #include <QDBusConnection> 0037 0038 #include <KToggleAction> 0039 #include <KToolBarPopupAction> 0040 #include <KActionCollection> 0041 #include <KSelectAction> 0042 #include <KRecentFilesAction> 0043 #include <KToolBar> 0044 #include <KStandardGuiItem> 0045 #include <KStandardShortcut> 0046 #include <KStandardAction> 0047 #include <KIO/Job> 0048 #include <KIO/FileCopyJob> 0049 #include <KJobWidgets> 0050 #include <KEditToolBar> 0051 #include <KShortcutsDialog> 0052 #include <KMessageBox> 0053 #include <KSharedConfig> 0054 #include <KConfigGroup> 0055 #include <KCompressionDevice> 0056 0057 #if ENABLE_DUMPDOCK 0058 #include "dumpselection.h" 0059 #endif 0060 0061 #include "partselection.h" 0062 #include "functionselection.h" 0063 #include "stackselection.h" 0064 #include "stackbrowser.h" 0065 #include "tracedata.h" 0066 #include "globalguiconfig.h" 0067 #include "config.h" 0068 #include "configdlg.h" 0069 #include "multiview.h" 0070 #include "callgraphview.h" 0071 0072 TopLevel::TopLevel() 0073 : KXmlGuiWindow(nullptr) 0074 { 0075 QDBusConnection::sessionBus().registerObject(QStringLiteral("/KCachegrind"), this, QDBusConnection::ExportScriptableSlots); 0076 0077 _progressBar = nullptr; 0078 _statusbar = statusBar(); 0079 _statusLabel = new QLabel(_statusbar); 0080 _statusbar->addWidget(_statusLabel, 1); 0081 _ccProcess = nullptr; 0082 0083 _layoutCount = 1; 0084 _layoutCurrent = 0; 0085 0086 resetState(); 0087 0088 KConfig *kconfig = KSharedConfig::openConfig().data(); 0089 GlobalGUIConfig::config()->readOptions(); 0090 0091 createDocks(); 0092 0093 _multiView = new MultiView(this, this ); 0094 _multiView->setObjectName(QStringLiteral("MultiView")); 0095 setCentralWidget(_multiView); 0096 0097 createActions(); 0098 0099 _partDockShown->setChecked(!_partDock->isHidden()); 0100 _stackDockShown->setChecked(!_stackDock->isHidden()); 0101 _functionDockShown->setChecked(!_functionDock->isHidden()); 0102 0103 connect(_partDock, &QDockWidget::visibilityChanged, 0104 this, &TopLevel::partVisibilityChanged); 0105 connect(_stackDock, &QDockWidget::visibilityChanged, 0106 this, &TopLevel::stackVisibilityChanged); 0107 connect(_functionDock, &QDockWidget::visibilityChanged, 0108 this, &TopLevel::functionVisibilityChanged); 0109 0110 #if ENABLE_DUMPDOCK 0111 _dumpDockShown->setChecked(!_dumpDock->isHidden()); 0112 connect(_dumpDock, SIGNAL(visibilityChanged(bool)), 0113 this, SLOT(dumpVisibilityChanged(bool))); 0114 #endif 0115 0116 0117 // set toggle after reading configuration 0118 _showPercentage = GlobalConfig::showPercentage(); 0119 _showExpanded = GlobalConfig::showExpanded(); 0120 _showCycles = GlobalConfig::showCycles(); 0121 _hideTemplates = GlobalConfig::hideTemplates(); 0122 _taPercentage->setChecked(_showPercentage); 0123 _taExpanded->setChecked(_showExpanded); 0124 _taCycles->setChecked(_showCycles); 0125 _taHideTemplates->setChecked(_hideTemplates); 0126 0127 setupPartSelection(_partSelection); 0128 0129 // KCachegrind for KDE 3.0.x does not allow to hide toolbars... 0130 setStandardToolBarMenuEnabled(true); 0131 _openRecent->loadEntries( KConfigGroup( kconfig, "" ) ); 0132 0133 // QT dock windows are created before (using QT position restoring) 0134 createGUI(); 0135 0136 setAutoSaveSettings(); 0137 0138 // restore current state settings (not configuration options) 0139 restoreCurrentState(QString()); 0140 } 0141 0142 void TopLevel::resetState() 0143 { 0144 _activeParts.clear(); 0145 _hiddenParts.clear(); 0146 0147 _data = nullptr; 0148 _function = nullptr; 0149 _eventType = nullptr; 0150 _eventType2 = nullptr; 0151 _groupType = ProfileContext::InvalidType; 0152 _group = nullptr; 0153 0154 // for delayed slots 0155 _traceItemDelayed = nullptr; 0156 _eventTypeDelayed = nullptr; 0157 _eventType2Delayed = nullptr; 0158 _groupTypeDelayed = ProfileContext::InvalidType; 0159 _groupDelayed = nullptr; 0160 _directionDelayed = TraceItemView::None; 0161 _lastSender = nullptr; 0162 } 0163 0164 0165 /** 0166 * Setup the part selection widget. 0167 * Statusbar has to be created before. 0168 */ 0169 void TopLevel::setupPartSelection(PartSelection* ps) 0170 { 0171 // setup connections from the part selection widget 0172 0173 connect(ps, &PartSelection::partsHideSelected, 0174 this, &TopLevel::partsHideSelectedSlotDelayed); 0175 connect(ps, &PartSelection::partsUnhideAll, 0176 this, &TopLevel::partsUnhideAllSlotDelayed); 0177 } 0178 0179 /** 0180 * This saves the current state of the main window and 0181 * sub widgets. 0182 * 0183 * No positions are saved. These is done automatically for 0184 * KToolbar, and manually in queryClose() for Qt docks. 0185 */ 0186 void TopLevel::saveCurrentState(const QString& postfix) 0187 { 0188 QString eventType = _eventType ? _eventType->name() : QStringLiteral("?"); 0189 QString eventType2 = _eventType2 ? _eventType2->name() : QStringLiteral("?"); 0190 0191 ConfigGroup* stateConfig = ConfigStorage::group(QLatin1String("CurrentState") + postfix); 0192 stateConfig->setValue(QStringLiteral("EventType"), eventType); 0193 stateConfig->setValue(QStringLiteral("EventType2"), eventType2); 0194 stateConfig->setValue(QStringLiteral("GroupType"), ProfileContext::typeName(_groupType)); 0195 delete stateConfig; 0196 0197 _partSelection->saveOptions(QStringLiteral("PartOverview"), postfix); 0198 _multiView->saveLayout(QStringLiteral("MainView"), postfix); 0199 _multiView->saveOptions(QStringLiteral("MainView"), postfix); 0200 } 0201 0202 /** 0203 * This function is called when a trace is closed. 0204 * Save browsing position for later restoring 0205 */ 0206 void TopLevel::saveTraceSettings() 0207 { 0208 QString key = traceKey(); 0209 0210 ConfigGroup* lConfig = ConfigStorage::group(QStringLiteral("Layouts")); 0211 lConfig->setValue(QStringLiteral("Count%1").arg(key), _layoutCount); 0212 lConfig->setValue(QStringLiteral("Current%1").arg(key), _layoutCurrent); 0213 delete lConfig; 0214 0215 ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions")); 0216 if (_eventType) 0217 pConfig->setValue(QStringLiteral("EventType%1").arg(key), _eventType->name()); 0218 if (_eventType2) 0219 pConfig->setValue(QStringLiteral("EventType2%1").arg(key), _eventType2->name()); 0220 if (_groupType != ProfileContext::InvalidType) 0221 pConfig->setValue(QStringLiteral("GroupType%1").arg(key), 0222 ProfileContext::typeName(_groupType)); 0223 0224 if (_data) { 0225 if (_group) 0226 pConfig->setValue(QStringLiteral("Group%1").arg(key), _group->name()); 0227 saveCurrentState(key); 0228 } 0229 delete pConfig; 0230 } 0231 0232 /** 0233 * This restores the current state of the main window and 0234 * sub widgets. 0235 * 0236 * This does NOT restore any positions. This is done automatically for 0237 * KToolbar, and manually in the createDocks() for QT docks.. 0238 */ 0239 void TopLevel::restoreCurrentState(const QString& postfix) 0240 { 0241 _partSelection->restoreOptions(QStringLiteral("PartOverview"), postfix); 0242 _multiView->restoreLayout(QStringLiteral("MainView"), postfix); 0243 _multiView->restoreOptions(QStringLiteral("MainView"), postfix); 0244 0245 _taSplit->setChecked(_multiView->childCount()>1); 0246 _taSplitDir->setEnabled(_multiView->childCount()>1); 0247 _taSplitDir->setChecked(_multiView->orientation() == Qt::Horizontal); 0248 } 0249 0250 0251 void TopLevel::createDocks() 0252 { 0253 _partDock = new QDockWidget(this); 0254 _partDock->setObjectName(QStringLiteral("part dock")); 0255 _partDock->setWindowTitle(i18n("Parts Overview")); 0256 _partSelection = new PartSelection(this, _partDock); 0257 _partDock->setWidget(_partSelection); 0258 0259 _stackDock = new QDockWidget(this); 0260 _stackDock->setObjectName(QStringLiteral("stack dock")); 0261 // Why is the caption only correct with a close button? 0262 _stackSelection = new StackSelection(_stackDock); 0263 _stackDock->setWidget(_stackSelection); 0264 _stackDock->setWindowTitle(i18n("Top Cost Call Stack")); 0265 _stackSelection->setWhatsThis( i18n( 0266 "<b>The Top Cost Call Stack</b>" 0267 "<p>This is a purely fictional 'most probable' call stack. " 0268 "It is built up by starting with the current selected " 0269 "function and adds the callers/callees with highest cost " 0270 "at the top and to bottom.</p>" 0271 "<p>The <b>Cost</b> and <b>Calls</b> columns show the " 0272 "cost used for all calls from the function in the line " 0273 "above.</p>")); 0274 0275 connect(_stackSelection, SIGNAL(functionSelected(CostItem*)), 0276 this, SLOT(setTraceItemDelayed(CostItem*))); 0277 0278 _functionDock = new QDockWidget(this); 0279 _functionDock->setObjectName(QStringLiteral("function dock")); 0280 _functionDock->setWindowTitle(i18n("Flat Profile")); 0281 _functionSelection = new FunctionSelection(this, _functionDock); 0282 _functionDock->setWidget(_functionSelection); 0283 0284 #if ENABLE_DUMPDOCK 0285 _dumpDock = new QDockWidget(this); 0286 _dumpDock->setWindowTitle(i18n("Profile Dumps")); 0287 _dumpSelection = new DumpSelection(this, _dumpDock, 0288 "dumpSelection"); 0289 _dumpSelection->setTopLevel(this); 0290 0291 _dumpDock->setWidget(_dumpSelection); 0292 _dumpSelection->setWhatsThis( i18n( 0293 "<b>Profile Dumps</b>" 0294 "<p>This dockable shows in the top part the list of " 0295 "loadable profile dumps in all subdirectories of: " 0296 "<ul><li>current working directory of KCachegrind, " 0297 "i.e. where it was started from, and </li>" 0298 "<li>the default profile dump directory given in the " 0299 "configuration.</li></ul> " 0300 "The list is sorted according to the target command " 0301 "profiled in the corresponding dump.</p>" 0302 "<p>On selecting a profile dump, information for it " 0303 "is shown in the bottom area of the dockable: " 0304 "<ul><li><b>Options</b> allows you to view the profiled " 0305 "command and profile options of this dump. By changing " 0306 "any item, a new (yet unexisting) profile template " 0307 "is created. Press <b>Run Profile</b> to start a " 0308 "profile run with these options in the background. </li>" 0309 "<li><b>Info</b> gives detailed info on the selected " 0310 "dump like event cost summary and properties of the " 0311 "simulated cache. </li>" 0312 "<li><b>State</b> is only available for current happening " 0313 "profiles runs. Press <b>Update</b> to see different " 0314 "counters of the run, and a stack trace of the current " 0315 "position in the program profiled. Check the <b>Every</b> " 0316 "option to let KCachegrind regularly poll these data. " 0317 "Check the <b>Sync</b> option to let the dockable activate " 0318 "the top function in the current loaded dump.</li></ul></p>")); 0319 #endif 0320 0321 // default positions, will be adjusted automatically by stored state in config 0322 addDockWidget(Qt::LeftDockWidgetArea, _partDock ); 0323 addDockWidget(Qt::LeftDockWidgetArea, _stackDock ); 0324 addDockWidget(Qt::LeftDockWidgetArea, _functionDock ); 0325 _stackDock->hide(); 0326 _partDock->hide(); 0327 0328 #if ENABLE_DUMPDOCK 0329 addDockWidget( Qt::LeftDockWidgetArea, _dumpDock ); 0330 _dumpDock->hide(); 0331 #endif 0332 0333 KConfigGroup dockConfig(KSharedConfig::openConfig(), "Docks"); 0334 _forcePartDock = dockConfig.readEntry("ForcePartDockVisible", false); 0335 } 0336 0337 0338 TopLevel::~TopLevel() 0339 { 0340 delete _data; 0341 } 0342 0343 0344 void TopLevel::saveProperties(KConfigGroup & c) 0345 { 0346 if ( _data ) 0347 c.writeEntry("TraceName", _data->traceName()); 0348 } 0349 0350 void TopLevel::readProperties(const KConfigGroup &c) 0351 { 0352 QString traceName = c.readEntry("TraceName"); 0353 if (!traceName.isEmpty()) { 0354 openDataFile(traceName); 0355 } 0356 } 0357 0358 void TopLevel::createLayoutActions() 0359 { 0360 QString hint; 0361 QAction* action; 0362 0363 action = actionCollection()->addAction( QStringLiteral("layout_duplicate") ); 0364 action->setText( i18n( "&Duplicate" ) ); 0365 connect(action, &QAction::triggered, this, &TopLevel::layoutDuplicate); 0366 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Plus)); 0367 hint = i18n("<b>Duplicate Current Layout</b>" 0368 "<p>Make a copy of the current layout.</p>"); 0369 action->setWhatsThis( hint ); 0370 0371 action = actionCollection()->addAction( QStringLiteral("layout_remove") ); 0372 action->setText( i18n( "&Remove" ) ); 0373 connect(action, &QAction::triggered, this, &TopLevel::layoutRemove); 0374 hint = i18n("<b>Remove Current Layout</b>" 0375 "<p>Delete current layout and make the previous active.</p>"); 0376 action->setWhatsThis( hint ); 0377 0378 action = actionCollection()->addAction( QStringLiteral("layout_next") ); 0379 action->setText( i18n( "&Go to Next" ) ); 0380 connect(action, &QAction::triggered, this, &TopLevel::layoutNext); 0381 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Right)); 0382 hint = i18n("Go to Next Layout"); 0383 action->setWhatsThis( hint ); 0384 0385 action = actionCollection()->addAction( QStringLiteral("layout_previous") ); 0386 action->setText( i18n( "&Go to Previous" ) ); 0387 connect(action, &QAction::triggered, this, &TopLevel::layoutPrevious); 0388 actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Left)); 0389 hint = i18n("Go to Previous Layout"); 0390 action->setWhatsThis( hint ); 0391 0392 action = actionCollection()->addAction( QStringLiteral("layout_restore") ); 0393 action->setText( i18n( "&Restore to Default" ) ); 0394 connect(action, &QAction::triggered, this, &TopLevel::layoutRestore); 0395 hint = i18n("Restore Layouts to Default"); 0396 action->setWhatsThis( hint ); 0397 0398 action = actionCollection()->addAction( QStringLiteral("layout_save") ); 0399 action->setText( i18n( "&Save as Default" ) ); 0400 connect(action, &QAction::triggered, this, &TopLevel::layoutSave); 0401 hint = i18n("Save Layouts as Default"); 0402 action->setWhatsThis( hint ); 0403 } 0404 0405 // TODO: split this up... 0406 void TopLevel::createMiscActions() 0407 { 0408 QString hint; 0409 QAction* action; 0410 0411 action = KStandardAction::openNew(this, SLOT(newWindow()), actionCollection()); 0412 hint = i18n("<b>New</b><p>Open new empty KCachegrind window.</p>"); 0413 action->setWhatsThis( hint ); 0414 0415 action = actionCollection()->addAction( QStringLiteral("file_add") ); 0416 action->setText( i18n( "&Add..." ) ); 0417 connect(action, SIGNAL(triggered(bool)), SLOT(add())); 0418 hint = i18n("<b>Add Profile Data</b>" 0419 "<p>This opens an additional profile data file in the current window.</p>"); 0420 action->setWhatsThis( hint ); 0421 0422 action = actionCollection()->addAction( QStringLiteral("reload") ); 0423 action->setIcon( QIcon::fromTheme(QStringLiteral("view-refresh")) ); 0424 action->setText( i18nc("Reload a document", "&Reload" ) ); 0425 connect(action, &QAction::triggered, this, &TopLevel::reload); 0426 actionCollection()->setDefaultShortcut(action, KStandardShortcut::Reload); 0427 hint = i18n("<b>Reload Profile Data</b>" 0428 "<p>This loads any new created parts, too.</p>"); 0429 action->setWhatsThis( hint ); 0430 0431 action = actionCollection()->addAction( QStringLiteral("export") ); 0432 action->setText( i18n( "&Export Graph" ) ); 0433 connect(action, &QAction::triggered, this, &TopLevel::exportGraph); 0434 0435 hint = i18n("<b>Export Call Graph</b>" 0436 "<p>Generates a file with extension .dot for the tools " 0437 "of the GraphViz package.</p>"); 0438 action->setWhatsThis( hint ); 0439 0440 0441 _taDump = actionCollection()->add<KToggleAction>( QStringLiteral("dump") ); 0442 _taDump->setIcon( QIcon::fromTheme(QStringLiteral("edit-redo")) ); 0443 _taDump->setText( i18n( "&Force Dump" ) ); 0444 connect(_taDump, &QAction::triggered, this, &TopLevel::forceTrace); 0445 actionCollection()->setDefaultShortcut(_taDump, QKeySequence::Undo); 0446 hint = i18n("<b>Force Dump</b>" 0447 "<p>This forces a dump for a Callgrind profile run " 0448 "in the current directory. This action is checked while " 0449 "KCachegrind looks for the dump. If the dump is " 0450 "finished, it automatically reloads the current trace. " 0451 "If this is the one from the running Callgrind, the new " 0452 "created trace part will be loaded, too.</p>" 0453 "<p>Force dump creates a file 'callgrind.cmd', and " 0454 "checks every second for its existence. A running " 0455 "Callgrind will detect this file, dump a trace part, " 0456 "and delete 'callgrind.cmd'. " 0457 "The deletion is detected by KCachegrind, " 0458 "and it does a Reload. If there is <em>no</em> Callgrind " 0459 "running, press 'Force Dump' again to cancel the dump " 0460 "request. This deletes 'callgrind.cmd' itself and " 0461 "stops polling for a new dump.</p>" 0462 "<p>Note: A Callgrind run <em>only</em> detects " 0463 "existence of 'callgrind.cmd' when actively running " 0464 "a few milliseconds, i.e. " 0465 "<em>not</em> sleeping. Tip: For a profiled GUI program, " 0466 "you can awake Callgrind e.g. by resizing a window " 0467 "of the program.</p>"); 0468 _taDump->setWhatsThis( hint ); 0469 0470 action = KStandardAction::open(this, SLOT(load()), actionCollection()); 0471 hint = i18n("<b>Open Profile Data</b>" 0472 "<p>This opens a profile data file, with possible multiple parts</p>"); 0473 action->setToolTip( hint ); 0474 action->setWhatsThis( hint ); 0475 0476 _openRecent = KStandardAction::openRecent(this, SLOT(load(QUrl)), 0477 actionCollection()); 0478 0479 KStandardAction::showStatusbar(this, 0480 SLOT(toggleStatusBar()), actionCollection()); 0481 0482 _partDockShown = actionCollection()->add<KToggleAction>(QStringLiteral("settings_show_partdock")); 0483 _partDockShown->setText(i18n("Parts Overview")); 0484 connect(_partDockShown, &QAction::triggered, this, &TopLevel::togglePartDock); 0485 0486 hint = i18n("Show/Hide the Parts Overview Dockable"); 0487 _partDockShown->setToolTip( hint ); 0488 _partDockShown->setWhatsThis( hint ); 0489 0490 _stackDockShown = actionCollection()->add<KToggleAction>(QStringLiteral("settings_show_stackdock")); 0491 _stackDockShown->setText(i18n("Call Stack")); 0492 connect(_stackDockShown, &QAction::triggered, this, &TopLevel::toggleStackDock); 0493 0494 hint = i18n("Show/Hide the Call Stack Dockable"); 0495 _stackDockShown->setToolTip( hint ); 0496 _stackDockShown->setWhatsThis( hint ); 0497 0498 _functionDockShown = actionCollection()->add<KToggleAction>(QStringLiteral("settings_show_profiledock")); 0499 _functionDockShown->setText(i18n("Function Profile")); 0500 connect(_functionDockShown, &QAction::triggered, this, &TopLevel::toggleFunctionDock); 0501 0502 hint = i18n("Show/Hide the Function Profile Dockable"); 0503 _functionDockShown->setToolTip( hint ); 0504 _functionDockShown->setWhatsThis( hint ); 0505 0506 #if ENABLE_DUMPDOCK 0507 _dumpDockShown = actionCollection()->add<KToggleAction>("settings_show_dumpdock", 0508 this, SLOT(toggleDumpDock())); 0509 _dumpDockShown->setText(i18n("Profile Dumps")); 0510 hint = i18n("Show/Hide the Profile Dumps Dockable"); 0511 _dumpDockShown->setToolTip( hint ); 0512 _dumpDockShown->setWhatsThis( hint ); 0513 #endif 0514 0515 _taPercentage = actionCollection()->add<KToggleAction>(QStringLiteral("view_percentage")); 0516 _taPercentage->setIcon(QIcon::fromTheme(QStringLiteral("percent"))); 0517 _taPercentage->setText(i18n("Relative")); 0518 connect(_taPercentage, &QAction::triggered, this, &TopLevel::togglePercentage); 0519 hint = i18n("Show relative instead of absolute costs"); 0520 _taPercentage->setToolTip( hint ); 0521 _taPercentage->setWhatsThis( hint ); 0522 0523 _taExpanded = actionCollection()->add<KToggleAction>(QStringLiteral("view_expanded")); 0524 _taExpanded->setIcon(QIcon::fromTheme(QStringLiteral("move"))); 0525 _taExpanded->setText(i18n("Relative to Parent")); 0526 connect(_taExpanded, &QAction::triggered, this, &TopLevel::toggleExpanded); 0527 0528 hint = i18n("Show percentage costs relative to parent"); 0529 _taExpanded->setToolTip( hint ); 0530 _taExpanded->setWhatsThis( hint ); 0531 0532 hint = i18n("<b>Show percentage costs relative to parent</b>" 0533 "<p>If this is switched off, percentage costs are always shown " 0534 "relative to the total cost of the profile part(s) that are " 0535 "currently browsed. By turning on this option, percentage cost " 0536 "of shown cost items will be relative to the parent cost item.</p>" 0537 "<ul><table>" 0538 "<tr><td><b>Cost Type</b></td><td><b>Parent Cost</b></td></tr>" 0539 "<tr><td>Function Cumulative</td><td>Total</td></tr>" 0540 "<tr><td>Function Self</td><td>Function Group (*) / Total</td></tr>" 0541 "<tr><td>Call</td><td>Function Inclusive</td></tr>" 0542 "<tr><td>Source Line</td><td>Function Inclusive</td></tr>" 0543 "</table></ul>" 0544 "<p>(*) Only if function grouping is switched on (e.g. ELF object grouping).</p>"); 0545 _taExpanded->setWhatsThis( hint ); 0546 0547 _taCycles = actionCollection()->add<KToggleAction>(QStringLiteral("view_cycles")); 0548 _taCycles->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo"))); 0549 _taCycles->setText(i18n( "Cycle Detection" )); 0550 connect(_taCycles, &QAction::triggered, this, &TopLevel::toggleCycles); 0551 0552 hint = i18n("<b>Detect recursive cycles</b>" 0553 "<p>If this is switched off, the treemap drawing will show " 0554 "black areas when a recursive call is made instead of drawing the " 0555 "recursion ad infinitum. Note that " 0556 "the size of black areas often will be wrong, as inside recursive " 0557 "cycles the cost of calls cannot be determined; the error is small, " 0558 "however, for false cycles (see documentation).</p>" 0559 "<p>The correct handling for cycles is to detect them and collapse all " 0560 "functions of a cycle into an artificial function, which is done when this " 0561 "option is selected. Unfortunately, with GUI applications, this often will " 0562 "lead to huge false cycles, making the analysis impossible; therefore, there " 0563 "is the option to switch this off.</p>"); 0564 _taCycles->setWhatsThis( hint ); 0565 0566 _taHideTemplates = actionCollection()->add<KToggleAction>(QStringLiteral("hide_templates")); 0567 _taHideTemplates->setIcon(QIcon::fromTheme(QStringLiteral("hidetemplates"))); 0568 _taHideTemplates->setText(i18n( "Shorten Templates" )); 0569 connect(_taHideTemplates, &QAction::triggered, this, &TopLevel::toggleHideTemplates); 0570 _taHideTemplates->setToolTip(i18n( "Hide Template Parameters in C++ Symbols" )); 0571 hint = i18n("<b>Hide Template Parameters in C++ Symbols</b>" 0572 "<p>If this is switched on, every symbol displayed will have " 0573 "any C++ template parameters hidden, just showing <> " 0574 "instead of a potentially nested template parameter.</p>" 0575 "<p>In this mode, you can hover the mouse pointer over the " 0576 "activated symbol label to show a tooltip with the " 0577 "unabbreviated symbol.</p>"); 0578 _taHideTemplates->setWhatsThis(hint); 0579 0580 KStandardAction::quit(this, SLOT(close()), actionCollection()); 0581 KStandardAction::preferences(this, SLOT(configure()), actionCollection()); 0582 KStandardAction::keyBindings(this, SLOT(configureKeys()), actionCollection()); 0583 KStandardAction::configureToolbars(this,SLOT(configureToolbars()), 0584 actionCollection()); 0585 0586 _paUp = new KToolBarPopupAction( QIcon::fromTheme( QStringLiteral("go-up") ), 0587 i18n( "&Up" ), this ); 0588 connect( _paUp, &QAction::triggered, 0589 _stackSelection, &StackSelection::browserUp ); 0590 actionCollection()->addAction( QStringLiteral("go_up"), _paUp ); 0591 actionCollection()->setDefaultShortcut(_paUp, QKeySequence(Qt::ALT + Qt::Key_Up)); 0592 connect( _paUp->popupMenu(), &QMenu::aboutToShow, 0593 this, &TopLevel::upAboutToShow ); 0594 connect( _paUp->popupMenu(), &QMenu::triggered, 0595 this, &TopLevel::upTriggered ); 0596 hint = i18n("<b>Go Up</b>" 0597 "<p>Go to last selected caller of current function. " 0598 "If no caller was visited, use that with highest cost.</p>"); 0599 _paUp->setToolTip( hint ); 0600 _paUp->setWhatsThis( hint ); 0601 0602 QPair< KGuiItem, KGuiItem > backForward = KStandardGuiItem::backAndForward(); 0603 _paBack = new KToolBarPopupAction( backForward.first.icon(), 0604 backForward.first.text(), this ); 0605 connect( _paBack, &QAction::triggered, 0606 _stackSelection, &StackSelection::browserBack ); 0607 actionCollection()->addAction( QStringLiteral("go_back"), _paBack ); 0608 actionCollection()->setDefaultShortcut(_paBack, QKeySequence(Qt::ALT + Qt::Key_Left)); 0609 connect( _paBack->popupMenu(), &QMenu::aboutToShow, 0610 this, &TopLevel::backAboutToShow ); 0611 connect( _paBack->popupMenu(), &QMenu::triggered, 0612 this, &TopLevel::backTriggered ); 0613 hint = i18n("Go back in function selection history"); 0614 _paBack->setToolTip( hint ); 0615 _paBack->setWhatsThis( hint ); 0616 0617 _paForward = new KToolBarPopupAction( backForward.second.icon(), 0618 backForward.second.text(), this ); 0619 connect( _paForward, &QAction::triggered, 0620 _stackSelection, &StackSelection::browserForward ); 0621 actionCollection()->addAction( QStringLiteral("go_forward"), _paForward ); 0622 actionCollection()->setDefaultShortcut(_paForward, QKeySequence(Qt::ALT + Qt::Key_Right)); 0623 connect( _paForward->popupMenu(), &QMenu::aboutToShow, 0624 this, &TopLevel::forwardAboutToShow ); 0625 connect( _paForward->popupMenu(), &QMenu::triggered, 0626 this, &TopLevel::forwardTriggered ); 0627 hint = i18n("Go forward in function selection history"); 0628 _paForward->setToolTip( hint ); 0629 _paForward->setWhatsThis( hint ); 0630 0631 _saCost = actionCollection()->add<KSelectAction>(QStringLiteral("view_cost_type")); 0632 _saCost->setText(i18n("Primary Event Type")); 0633 hint = i18n("Select primary event type of costs"); 0634 _saCost->setComboWidth(300); 0635 _saCost->setToolTip( hint ); 0636 _saCost->setWhatsThis( hint ); 0637 0638 // This is needed because for KDE4, "_saCost->setComboWidth(300);" seems to 0639 // have no effect. Instead, list box entry widths are used to determine the 0640 // combobox width. However, at KCachegrind startup, we do not have yet 0641 // a list of event types, as this depends on the profile data. 0642 // In KDE 4.2, we used a translatable string, which did not really work as 0643 // the semantic is not known to translators. Instead, we use a 0644 // nontranslatable string now... 0645 QStringList dummyItems; 0646 dummyItems << QStringLiteral("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); 0647 _saCost->setItems(dummyItems); 0648 0649 // cost types are dependent on loaded data, thus KSelectAction 0650 // is filled in setData() 0651 connect( _saCost, &KSelectAction::textTriggered, 0652 this, &TopLevel::eventTypeSelected); 0653 0654 _saCost2 = actionCollection()->add<KSelectAction>(QStringLiteral("view_cost_type2")); 0655 _saCost2->setText(i18n("Secondary Event Type")); 0656 hint = i18n("Select secondary event type for cost e.g. shown in annotations"); 0657 _saCost2->setComboWidth(300); 0658 _saCost2->setToolTip( hint ); 0659 _saCost2->setWhatsThis( hint ); 0660 _saCost2->setItems(dummyItems); 0661 connect( _saCost2, &KSelectAction::textTriggered, 0662 this, &TopLevel::eventTypeSelected); 0663 0664 saGroup = actionCollection()->add<KSelectAction>(QStringLiteral("view_group_type")); 0665 saGroup->setText(i18n("Grouping")); 0666 0667 hint = i18n("Select how functions are grouped into higher level cost items"); 0668 saGroup->setToolTip( hint ); 0669 saGroup->setWhatsThis( hint ); 0670 0671 QStringList args; 0672 0673 args << i18n("(No Grouping)") 0674 << ProfileContext::i18nTypeName(ProfileContext::Object) 0675 << ProfileContext::i18nTypeName(ProfileContext::File) 0676 << ProfileContext::i18nTypeName(ProfileContext::Class) 0677 << ProfileContext::i18nTypeName(ProfileContext::FunctionCycle); 0678 0679 saGroup->setItems(args); 0680 connect( saGroup, &KSelectAction::indexTriggered, 0681 this, &TopLevel::groupTypeSelected); 0682 0683 _taSplit = actionCollection()->add<KToggleAction>(QStringLiteral("view_split")); 0684 _taSplit->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right"))); 0685 _taSplit->setText(i18n("Split")); 0686 connect(_taSplit, &QAction::triggered, this, &TopLevel::splitSlot); 0687 0688 hint = i18n("Show two information panels"); 0689 _taSplit->setToolTip( hint ); 0690 _taSplit->setWhatsThis( hint ); 0691 0692 _taSplitDir = actionCollection()->add<KToggleAction>(QStringLiteral("view_split_dir")); 0693 _taSplitDir->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right"))); 0694 _taSplitDir->setText(i18n("Split Horizontal")); 0695 connect(_taSplitDir, &QAction::triggered, this, &TopLevel::splitDirSlot); 0696 0697 hint = i18n("Change Split Orientation when main window is split."); 0698 _taSplitDir->setToolTip( hint ); 0699 _taSplitDir->setWhatsThis( hint ); 0700 } 0701 0702 void TopLevel::createActions() 0703 { 0704 createMiscActions(); 0705 createLayoutActions(); 0706 } 0707 0708 void TopLevel::toggleStatusBar() 0709 { 0710 if (statusBar()->isVisible()) 0711 statusBar()->hide(); 0712 else 0713 statusBar()->show(); 0714 } 0715 0716 void TopLevel::togglePartDock() 0717 { 0718 if (!_partDock->isVisible()) 0719 _partDock->show(); 0720 else 0721 _partDock->hide(); 0722 } 0723 0724 void TopLevel::toggleStackDock() 0725 { 0726 if (!_stackDock->isVisible()) 0727 _stackDock->show(); 0728 else 0729 _stackDock->hide(); 0730 } 0731 0732 void TopLevel::toggleDumpDock() 0733 { 0734 #if ENABLE_DUMPDOCK 0735 if (!_dumpDock->isVisible()) 0736 _dumpDock->show(); 0737 else 0738 _dumpDock->hide(); 0739 #endif 0740 } 0741 0742 void TopLevel::toggleFunctionDock() 0743 { 0744 if (!_functionDock->isVisible()) 0745 _functionDock->show(); 0746 else 0747 _functionDock->hide(); 0748 } 0749 0750 void TopLevel::togglePercentage() 0751 { 0752 setPercentage(_taPercentage->isChecked()); 0753 } 0754 0755 void TopLevel::setAbsoluteCost() 0756 { 0757 setPercentage(false); 0758 } 0759 0760 void TopLevel::setRelativeCost() 0761 { 0762 setPercentage(true); 0763 } 0764 0765 void TopLevel::updateViewsOnChange(int change) 0766 { 0767 _partSelection->notifyChange(change); 0768 _functionSelection->notifyChange(change); 0769 _multiView->notifyChange(change); 0770 } 0771 0772 void TopLevel::setPercentage(bool show) 0773 { 0774 if (_showPercentage == show) return; 0775 _showPercentage = show; 0776 if (_taPercentage->isChecked() != show) 0777 _taPercentage->setChecked(show); 0778 0779 GlobalConfig::setShowPercentage(_showPercentage); 0780 0781 _stackSelection->refresh(); 0782 0783 updateViewsOnChange(TraceItemView::configChanged); 0784 } 0785 0786 void TopLevel::toggleExpanded() 0787 { 0788 bool show = _taExpanded->isChecked(); 0789 if (_showExpanded == show) return; 0790 _showExpanded = show; 0791 0792 GlobalConfig::setShowExpanded(_showExpanded); 0793 0794 _stackSelection->refresh(); 0795 0796 updateViewsOnChange(TraceItemView::configChanged); 0797 } 0798 0799 void TopLevel::toggleCycles() 0800 { 0801 bool show = _taCycles->isChecked(); 0802 if (_showCycles == show) return; 0803 _showCycles = show; 0804 0805 GlobalConfig::setShowCycles(_showCycles); 0806 0807 if (!_data) return; 0808 0809 _data->invalidateDynamicCost(); 0810 _data->updateFunctionCycles(); 0811 0812 _stackSelection->rebuildStackList(); 0813 0814 updateViewsOnChange(TraceItemView::configChanged); 0815 } 0816 0817 void TopLevel::toggleHideTemplates() 0818 { 0819 bool b = _taHideTemplates->isChecked(); 0820 if (_hideTemplates == b) return; 0821 _hideTemplates = b; 0822 0823 GlobalConfig::setHideTemplates(_hideTemplates); 0824 0825 _stackSelection->refresh(); 0826 0827 updateViewsOnChange(TraceItemView::configChanged); 0828 } 0829 0830 void TopLevel::partVisibilityChanged(bool v) 0831 { 0832 _partDockShown->setChecked(v); 0833 } 0834 0835 void TopLevel::stackVisibilityChanged(bool v) 0836 { 0837 _stackDockShown->setChecked(v); 0838 } 0839 0840 #if ENABLE_DUMPDOCK 0841 void TopLevel::dumpVisibilityChanged(bool v) 0842 #else 0843 void TopLevel::dumpVisibilityChanged(bool) 0844 #endif 0845 { 0846 #if ENABLE_DUMPDOCK 0847 _dumpDockShown->setChecked(v); 0848 #endif 0849 } 0850 0851 void TopLevel::functionVisibilityChanged(bool v) 0852 { 0853 _functionDockShown->setChecked(v); 0854 if (v) 0855 _functionSelection->updateView(); 0856 } 0857 0858 0859 void TopLevel::querySlot() 0860 { 0861 _functionSelection->query(queryLineEdit->text()); 0862 } 0863 0864 void TopLevel::configureKeys() 0865 { 0866 KShortcutsDialog::showDialog(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this); 0867 } 0868 0869 0870 void TopLevel::configureToolbars() 0871 { 0872 KEditToolBar *dlg = new KEditToolBar(guiFactory(), this); 0873 0874 if (dlg->exec()) 0875 createGUI(); 0876 0877 delete dlg; 0878 } 0879 0880 0881 void TopLevel::newWindow() 0882 { 0883 TopLevel* t = new TopLevel(); 0884 t->show(); 0885 } 0886 0887 0888 void TopLevel::load() 0889 { 0890 QUrl url = QFileDialog::getOpenFileUrl(this, 0891 i18n("Select Callgrind Profile Data"), 0892 QUrl(), 0893 i18n("Callgrind Profile Data (cachegrind.out* callgrind.out*);;All Files (*)")); 0894 0895 load(url); 0896 } 0897 0898 void TopLevel::load(const QUrl& url) 0899 { 0900 if (url.isEmpty()) return; 0901 0902 QString tmpFileName; 0903 QTemporaryFile tmpFile; 0904 if (url.isLocalFile()) { 0905 tmpFileName = url.toLocalFile(); 0906 } 0907 else if (tmpFile.open()){ 0908 // network transparency 0909 tmpFileName = tmpFile.fileName(); 0910 KIO::FileCopyJob *job = KIO::file_copy(url, 0911 QUrl::fromLocalFile(tmpFileName)); 0912 KJobWidgets::setWindow(job, this); 0913 job->exec(); 0914 } 0915 if (!tmpFileName.isEmpty()) { 0916 _openRecent->addUrl(url); 0917 _openRecent->saveEntries( KConfigGroup( KSharedConfig::openConfig(), QString() ) ); 0918 0919 load(tmpFileName); 0920 } else { 0921 KMessageBox::error(this, i18n("Could not open the file \"%1\". " 0922 "Check it exists and you have enough " 0923 "permissions to read it.", 0924 url.toDisplayString())); 0925 } 0926 } 0927 0928 /* if file name is ".": load first file found in current directory, but do 0929 * not show an error message if nothing could be loaded 0930 */ 0931 void TopLevel::load(QString file) 0932 { 0933 if (file.isEmpty()) return; 0934 0935 bool showError = true; 0936 if (file == QLatin1Char('.')) 0937 showError = false; 0938 0939 if (_data && _data->parts().count()>0) { 0940 0941 // In new window 0942 TopLevel* t = new TopLevel(); 0943 t->show(); 0944 t->loadDelayed(file); 0945 return; 0946 } 0947 0948 bool loaded = openDataFile(file); 0949 if (!loaded && showError) 0950 KMessageBox::error(this, i18n("Could not open the file \"%1\". " 0951 "Check it exists and you have enough " 0952 "permissions to read it.", file)); 0953 } 0954 0955 0956 void TopLevel::add() 0957 { 0958 QUrl url = QFileDialog::getOpenFileUrl(this, 0959 i18n("Add Callgrind Profile Data"), 0960 QUrl(), 0961 i18n("Callgrind Profile Data (cachegrind.out* callgrind.out*);;All Files (*)")); 0962 0963 add(url); 0964 } 0965 0966 void TopLevel::add(const QUrl &url) 0967 { 0968 if (url.isEmpty()) return; 0969 0970 QString tmpFileName; 0971 QTemporaryFile tmpFile; 0972 if (url.isLocalFile()) { 0973 tmpFileName = url.toLocalFile(); 0974 } 0975 else if (tmpFile.open()){ 0976 // network transparency 0977 tmpFileName = tmpFile.fileName(); 0978 KIO::FileCopyJob *job = KIO::file_copy(url, 0979 QUrl::fromLocalFile(tmpFileName)); 0980 KJobWidgets::setWindow(job, this); 0981 job->exec(); 0982 } 0983 if (!tmpFileName.isEmpty()) { 0984 _openRecent->addUrl(url); 0985 _openRecent->saveEntries( KSharedConfig::openConfig()->group( QString() ) ); 0986 0987 add(tmpFileName); 0988 } 0989 } 0990 0991 void TopLevel::add(QString file) 0992 { 0993 if (file.isEmpty()) return; 0994 0995 if (_data) { 0996 _data->load(file); 0997 0998 // GUI update for added data 0999 configChanged(); 1000 return; 1001 } 1002 1003 openDataFile(file); 1004 } 1005 1006 1007 1008 void TopLevel::loadDelayed(QString file) 1009 { 1010 _loadFilesDelayed << file; 1011 QTimer::singleShot(0, this, &TopLevel::loadTraceDelayed); 1012 } 1013 1014 void TopLevel::loadDelayed(QStringList files) 1015 { 1016 _loadFilesDelayed << files; 1017 QTimer::singleShot(0, this, &TopLevel::loadTraceDelayed); 1018 } 1019 1020 void TopLevel::loadTraceDelayed() 1021 { 1022 if (_loadFilesDelayed.isEmpty()) return; 1023 1024 if (_loadFilesDelayed.count()>1) { 1025 // FIXME: we expect all files to be local and existing 1026 TraceData* d = new TraceData(this); 1027 d->load(_loadFilesDelayed); 1028 setData(d); 1029 } 1030 else { 1031 QString file = _loadFilesDelayed[0]; 1032 1033 // if URL scheme is missing (URL is relative), this is a local file 1034 QUrl u = QUrl::fromUserInput(file, QDir::currentPath(), QUrl::AssumeLocalFile); 1035 if (u.isLocalFile()) 1036 load(file); // special case for local file: maybe just prefix 1037 else 1038 load(u); 1039 } 1040 _loadFilesDelayed.clear(); 1041 } 1042 1043 1044 void TopLevel::reload() 1045 { 1046 QString trace; 1047 if (!_data || _data->parts().isEmpty()) 1048 trace = QStringLiteral("."); // open first trace found in dir 1049 else 1050 trace = _data->traceName(); 1051 1052 // this also keeps sure we have the same browsing position... 1053 openDataFile(trace); 1054 } 1055 1056 void TopLevel::exportGraph() 1057 { 1058 if (!_data || !_function) return; 1059 1060 GraphExporter::savePrompt(this, _data, _function, _eventType, _groupType, nullptr); 1061 } 1062 1063 1064 bool TopLevel::setEventType(QString s) 1065 { 1066 EventType* ct; 1067 1068 ct = (_data) ? _data->eventTypes()->type(s) : nullptr; 1069 1070 // if costtype with given name not found, use first available 1071 if (!ct && _data) ct = _data->eventTypes()->type(0); 1072 1073 return setEventType(ct); 1074 } 1075 1076 bool TopLevel::setEventType2(QString s) 1077 { 1078 EventType* ct; 1079 1080 // Special type i18n("(Hidden)") gives 0 1081 ct = (_data) ? _data->eventTypes()->type(s) : nullptr; 1082 1083 return setEventType2(ct); 1084 } 1085 1086 void TopLevel::eventTypeSelected(const QString& s) 1087 { 1088 EventType* ct; 1089 1090 ct = (_data) ? _data->eventTypes()->typeForLong(s) : nullptr; 1091 setEventType(ct); 1092 } 1093 1094 void TopLevel::eventType2Selected(const QString& s) 1095 { 1096 EventType* ct; 1097 1098 ct = (_data) ? _data->eventTypes()->typeForLong(s) : nullptr; 1099 setEventType2(ct); 1100 } 1101 1102 bool TopLevel::setEventType(EventType* ct) 1103 { 1104 if (_eventType == ct) return false; 1105 _eventType = ct; 1106 1107 if (ct) { 1108 QStringList l = _saCost->items(); 1109 int idx = l.indexOf(ct->longName()); 1110 if (idx >= 0) 1111 _saCost->setCurrentItem(idx); 1112 } 1113 1114 _partSelection->setEventType(_eventType); 1115 _stackSelection->setEventType(_eventType); 1116 _functionSelection->setEventType(_eventType); 1117 _multiView->setEventType(_eventType); 1118 1119 updateStatusBar(); 1120 1121 return true; 1122 } 1123 1124 bool TopLevel::setEventType2(EventType* ct) 1125 { 1126 if (_eventType2 == ct) return false; 1127 _eventType2 = ct; 1128 1129 QString longName = ct ? ct->longName() : i18n("(Hidden)"); 1130 QStringList l = _saCost2->items(); 1131 int idx = l.indexOf(longName); 1132 if (idx >= 0) 1133 _saCost2->setCurrentItem(idx); 1134 1135 _partSelection->setEventType2(_eventType2); 1136 _stackSelection->setEventType2(_eventType2); 1137 _functionSelection->setEventType2(_eventType2); 1138 _multiView->setEventType2(_eventType2); 1139 1140 updateStatusBar(); 1141 1142 return true; 1143 } 1144 1145 1146 void TopLevel::groupTypeSelected(int cg) 1147 { 1148 switch(cg) { 1149 case 0: setGroupType( ProfileContext::Function ); break; 1150 case 1: setGroupType( ProfileContext::Object ); break; 1151 case 2: setGroupType( ProfileContext::File ); break; 1152 case 3: setGroupType( ProfileContext::Class ); break; 1153 case 4: setGroupType( ProfileContext::FunctionCycle ); break; 1154 default: break; 1155 } 1156 } 1157 1158 bool TopLevel::setGroupType(QString s) 1159 { 1160 ProfileContext::Type gt; 1161 1162 gt = ProfileContext::type(s); 1163 // only allow Function/Object/File/Class as grouptype 1164 switch(gt) { 1165 case ProfileContext::Object: 1166 case ProfileContext::File: 1167 case ProfileContext::Class: 1168 case ProfileContext::FunctionCycle: 1169 break; 1170 default: 1171 gt = ProfileContext::Function; 1172 } 1173 1174 return setGroupType(gt); 1175 } 1176 1177 bool TopLevel::setGroupType(ProfileContext::Type gt) 1178 { 1179 if (_groupType == gt) return false; 1180 _groupType = gt; 1181 1182 int idx = -1; 1183 switch(gt) { 1184 case ProfileContext::Function: idx = 0; break; 1185 case ProfileContext::Object: idx = 1; break; 1186 case ProfileContext::File: idx = 2; break; 1187 case ProfileContext::Class: idx = 3; break; 1188 case ProfileContext::FunctionCycle: idx = 4; break; 1189 default: 1190 break; 1191 } 1192 1193 if (idx==-1) return false; 1194 1195 if (saGroup->currentItem() != idx) 1196 saGroup->setCurrentItem(idx); 1197 1198 _stackSelection->setGroupType(_groupType); 1199 _partSelection->set(_groupType); 1200 _functionSelection->set(_groupType); 1201 _multiView->set(_groupType); 1202 1203 updateStatusBar(); 1204 1205 return true; 1206 } 1207 1208 bool TopLevel::setGroup(QString s) 1209 { 1210 TraceCostItem* ci = _functionSelection->group(s); 1211 if (!ci) 1212 return false; 1213 1214 return setGroup(ci); 1215 } 1216 1217 1218 bool TopLevel::setGroup(TraceCostItem* g) 1219 { 1220 if (_group == g) return false; 1221 _group = g; 1222 1223 _functionSelection->setGroup(g); 1224 updateStatusBar(); 1225 1226 return true; 1227 } 1228 1229 bool TopLevel::setFunction(QString s) 1230 { 1231 if (!_data) return false; 1232 1233 ProfileCostArray* f = _data->search(ProfileContext::Function, s, _eventType); 1234 if (!f) return false; 1235 1236 return setFunction((TraceFunction*)f); 1237 } 1238 1239 bool TopLevel::setFunction(TraceFunction* f) 1240 { 1241 if (_function == f) return false; 1242 _function = f; 1243 1244 _multiView->activate(f); 1245 _functionSelection->activate(f); 1246 _partSelection->activate(f); 1247 _stackSelection->setFunction(_function); 1248 1249 StackBrowser* b = _stackSelection->browser(); 1250 if (b) { 1251 // do not disable up: a press forces stack-up extending... 1252 _paForward->setEnabled(b->canGoForward()); 1253 _paBack->setEnabled(b->canGoBack()); 1254 } 1255 1256 #if TRACE_UPDATES 1257 qDebug("TopLevel::setFunction(%s), lastSender %s", 1258 f ? f->prettyName().ascii() : "0", 1259 _lastSender ? _lastSender->name() :"0" ); 1260 #endif 1261 1262 return true; 1263 } 1264 1265 1266 /** 1267 * Delayed versions. 1268 * We always have a pair of slots: One receiver to start the 1269 * delay with a singleShot Timer. It stores the parameter into a 1270 * temporary variable. And one parameterless slot for 1271 * forwarding, using this temporary. 1272 */ 1273 void TopLevel::setEventTypeDelayed(EventType* ct) 1274 { 1275 _eventTypeDelayed = ct; 1276 QTimer::singleShot (0, this, SLOT(setEventTypeDelayed())); 1277 } 1278 1279 void TopLevel::setEventType2Delayed(EventType* ct) 1280 { 1281 _eventType2Delayed = ct; 1282 QTimer::singleShot (0, this, SLOT(setEventType2Delayed())); 1283 } 1284 1285 void TopLevel::setEventTypeDelayed() 1286 { 1287 setEventType(_eventTypeDelayed); 1288 } 1289 1290 void TopLevel::setEventType2Delayed() 1291 { 1292 setEventType2(_eventType2Delayed); 1293 } 1294 1295 void TopLevel::setGroupTypeDelayed(ProfileContext::Type gt) 1296 { 1297 _groupTypeDelayed = gt; 1298 QTimer::singleShot (0, this, SLOT(setGroupTypeDelayed())); 1299 } 1300 1301 void TopLevel::setGroupTypeDelayed() 1302 { 1303 setGroupType(_groupTypeDelayed); 1304 } 1305 1306 void TopLevel::setGroupDelayed(TraceCostItem* g) 1307 { 1308 #if TRACE_UPDATES 1309 qDebug("TopLevel::setGroupDelayed(%s), sender %s", 1310 g ? g->prettyName().ascii() : "0", 1311 _lastSender ? _lastSender->name() :"0" ); 1312 #endif 1313 1314 _groupDelayed = g; 1315 QTimer::singleShot (0, this, SLOT(setGroupDelayed())); 1316 } 1317 1318 void TopLevel::setGroupDelayed() 1319 { 1320 setGroup(_groupDelayed); 1321 } 1322 1323 void TopLevel::setDirectionDelayed(TraceItemView::Direction d) 1324 { 1325 _directionDelayed = d; 1326 QTimer::singleShot (0, this, SLOT(setDirectionDelayed())); 1327 } 1328 1329 void TopLevel::setDirectionDelayed() 1330 { 1331 switch(_directionDelayed) { 1332 case TraceItemView::Back: 1333 _stackSelection->browserBack(); 1334 break; 1335 1336 case TraceItemView::Forward: 1337 _stackSelection->browserForward(); 1338 break; 1339 1340 case TraceItemView::Up: 1341 { 1342 StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; 1343 HistoryItem* hi = b ? b->current() : nullptr; 1344 TraceFunction* f = hi ? hi->function() : nullptr; 1345 1346 if (!f) break; 1347 f = hi->stack()->caller(f, false); 1348 if (f) setFunction(f); 1349 } 1350 break; 1351 1352 default: break; 1353 } 1354 1355 _directionDelayed = TraceItemView::None; 1356 } 1357 1358 1359 void TopLevel::setTraceItemDelayed(CostItem* i) 1360 { 1361 // no need to select same item a 2nd time... 1362 if (_traceItemDelayed == i) return; 1363 _traceItemDelayed = i; 1364 _lastSender = sender(); 1365 1366 qDebug() << "Selected " << (i ? i->prettyName() : QStringLiteral("(none)")); 1367 1368 #if TRACE_UPDATES 1369 qDebug("TopLevel::setTraceItemDelayed(%s), sender %s", 1370 i ? i->prettyName().ascii() : "0", 1371 _lastSender ? _lastSender->name() :"0" ); 1372 #endif 1373 1374 QTimer::singleShot (0, this, SLOT(setTraceItemDelayed())); 1375 } 1376 1377 void TopLevel::setTraceItemDelayed() 1378 { 1379 if (!_traceItemDelayed) return; 1380 1381 switch(_traceItemDelayed->type()) { 1382 case ProfileContext::Function: 1383 case ProfileContext::FunctionCycle: 1384 setFunction((TraceFunction*)_traceItemDelayed); 1385 break; 1386 1387 case ProfileContext::Object: 1388 case ProfileContext::File: 1389 case ProfileContext::Class: 1390 _multiView->activate(_traceItemDelayed); 1391 break; 1392 1393 #if 0 1394 // this conflicts with the selection policy of InstrView ?!? 1395 case ProfileContext::Instr: 1396 case ProfileContext::Line: 1397 // only for multiview 1398 _multiView->activate(_traceItemDelayed); 1399 break; 1400 #endif 1401 1402 default: break; 1403 } 1404 1405 _traceItemDelayed = nullptr; 1406 _lastSender = nullptr; 1407 } 1408 1409 /** 1410 * A TraceData object cannot be viewed many times in different 1411 * toplevel windows. Thus, this toplevel window takes ownership 1412 * of the TraceData object: on closing the window or opening 1413 * another trace, the object is destroyed. 1414 */ 1415 void TopLevel::setData(TraceData* data) 1416 { 1417 if (data == _data) return; 1418 1419 _lastSender = nullptr; 1420 1421 saveTraceSettings(); 1422 1423 if (_data) { 1424 _partSelection->setData(nullptr); 1425 _stackSelection->setData(nullptr); 1426 _functionSelection->setData(nullptr); 1427 _multiView->setData(nullptr); 1428 _multiView->updateView(true); 1429 1430 // we are the owner... 1431 delete _data; 1432 } 1433 1434 // reset members 1435 resetState(); 1436 1437 _data = data; 1438 1439 // fill cost type list 1440 QStringList types; 1441 1442 if (_data) { 1443 /* add all supported virtual types */ 1444 EventTypeSet* m = _data->eventTypes(); 1445 m->addKnownDerivedTypes(); 1446 1447 /* first, fill selection list with available cost types */ 1448 for (int i=0;i<m->realCount();i++) 1449 types << m->realType(i)->longName(); 1450 for (int i=0;i<m->derivedCount();i++) 1451 types << m->derivedType(i)->longName(); 1452 } 1453 _saCost->setItems(types); 1454 _saCost->setComboWidth(300); 1455 1456 if (types.count()>0) { 1457 // second type list gets an additional "(Hidden)" 1458 types.prepend(i18n("(Hidden)")); 1459 } 1460 _saCost2->setItems(types); 1461 _saCost2->setComboWidth(300); 1462 // default is hidden 1463 if (types.count()>0) 1464 _saCost2->setCurrentItem(0); 1465 1466 _partSelection->setData(_data); 1467 _stackSelection->setData(_data); 1468 _functionSelection->setData(_data); 1469 _multiView->setData(_data); 1470 // Force update of _data in all children of _multiView 1471 // This is needed to make restoring of activeItem work! 1472 _multiView->updateView(true); 1473 1474 /* this is needed to let the other widgets know the types */ 1475 restoreTraceTypes(); 1476 1477 restoreTraceSettings(); 1478 1479 QString caption; 1480 if (_data) { 1481 caption = _data->traceName(); 1482 if (!_data->command().isEmpty()) 1483 caption += " [" + _data->command() + ']'; 1484 } 1485 setWindowTitle(caption); 1486 1487 if (!_data || (!_forcePartDock && _data->parts().count()<2)) { 1488 _partDock->hide(); 1489 _partDockShown->setChecked(false); 1490 } 1491 else { 1492 _partDock->show(); 1493 _partDockShown->setChecked(true); 1494 } 1495 1496 updateStatusBar(); 1497 } 1498 1499 void TopLevel::addEventTypeMenu(QMenu* popup, bool withCost2) 1500 { 1501 if (_data) { 1502 QMenu *popup1, *popup2 = nullptr; 1503 QAction* action; 1504 1505 popup1 = popup->addMenu(i18n("Primary Event Type")); 1506 connect(popup1, SIGNAL(triggered(QAction*)), 1507 this, SLOT(setEventType(QAction*))); 1508 1509 if (withCost2) { 1510 popup2 = popup->addMenu(i18n("Secondary Event Type")); 1511 connect(popup2, SIGNAL(triggered(QAction*)), 1512 this, SLOT(setEventType2(QAction*))); 1513 1514 if (_eventType2) { 1515 action = popup2->addAction(i18n("Hide")); 1516 action->setData(199); 1517 popup2->addSeparator(); 1518 } 1519 } 1520 1521 EventTypeSet* m = _data->eventTypes(); 1522 EventType* ct; 1523 for (int i=0;i<m->realCount();i++) { 1524 ct = m->realType(i); 1525 1526 action = popup1->addAction(ct->longName()); 1527 action->setCheckable(true); 1528 action->setData(100+i); 1529 if (_eventType == ct) action->setChecked(true); 1530 1531 if (popup2) { 1532 action = popup2->addAction(ct->longName()); 1533 action->setCheckable(true); 1534 action->setData(100+i); 1535 if (_eventType2 == ct) action->setChecked(true); 1536 } 1537 } 1538 1539 for (int i=0;i<m->derivedCount();i++) { 1540 ct = m->derivedType(i); 1541 1542 action = popup1->addAction(ct->longName()); 1543 action->setCheckable(true); 1544 action->setData(200+i); 1545 if (_eventType == ct) action->setChecked(true); 1546 1547 if (popup2) { 1548 action = popup2->addAction(ct->longName()); 1549 action->setCheckable(true); 1550 action->setData(200+i); 1551 if (_eventType2 == ct) action->setChecked(true); 1552 } 1553 } 1554 } 1555 1556 if (_showPercentage) 1557 popup->addAction(i18n("Show Absolute Cost"), 1558 this, SLOT(setAbsoluteCost())); 1559 else 1560 popup->addAction(i18n("Show Relative Cost"), 1561 this, SLOT(setRelativeCost())); 1562 } 1563 1564 bool TopLevel::setEventType(QAction* action) 1565 { 1566 if (!_data) return false; 1567 int id = action->data().toInt(nullptr); 1568 1569 EventTypeSet* m = _data->eventTypes(); 1570 EventType* ct=nullptr; 1571 if (id >=100 && id<199) ct = m->realType(id-100); 1572 if (id >=200 && id<299) ct = m->derivedType(id-200); 1573 1574 return ct ? setEventType(ct) : false; 1575 } 1576 1577 bool TopLevel::setEventType2(QAction* action) 1578 { 1579 if (!_data) return false; 1580 int id = action->data().toInt(nullptr); 1581 1582 EventTypeSet* m = _data->eventTypes(); 1583 EventType* ct=nullptr; 1584 if (id >=100 && id<199) ct = m->realType(id-100); 1585 if (id >=200 && id<299) ct = m->derivedType(id-200); 1586 1587 return setEventType2(ct); 1588 } 1589 1590 void TopLevel::addGoMenu(QMenu* popup) 1591 { 1592 popup->addAction(i18n("Go Back"), this, SLOT(goBack())); 1593 popup->addAction(i18n("Go Forward"), this, SLOT(goForward())); 1594 popup->addAction(i18n("Go Up"), this, SLOT(goUp())); 1595 } 1596 1597 void TopLevel::goBack() 1598 { 1599 setDirectionDelayed(TraceItemView::Back); 1600 } 1601 1602 void TopLevel::goForward() 1603 { 1604 setDirectionDelayed(TraceItemView::Forward); 1605 } 1606 1607 void TopLevel::goUp() 1608 { 1609 setDirectionDelayed(TraceItemView::Up); 1610 } 1611 1612 QString TopLevel::traceKey() 1613 { 1614 if (!_data || _data->command().isEmpty()) return QString(); 1615 1616 QString name = _data->command(); 1617 QString key; 1618 for (int l=0;l<name.length();l++) 1619 if (name[l].isLetterOrNumber()) key += name[l]; 1620 1621 return QStringLiteral("-") + key; 1622 } 1623 1624 1625 void TopLevel::restoreTraceTypes() 1626 { 1627 QString key = traceKey(); 1628 QString groupType, eventType, eventType2; 1629 1630 ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions")); 1631 groupType = pConfig->value(QStringLiteral("GroupType%1").arg(key),QString()).toString(); 1632 eventType = pConfig->value(QStringLiteral("EventType%1").arg(key),QString()).toString(); 1633 eventType2 = pConfig->value(QStringLiteral("EventType2%1").arg(key),QString()).toString(); 1634 delete pConfig; 1635 1636 ConfigGroup* cConfig = ConfigStorage::group(QStringLiteral("CurrentState")); 1637 if (groupType.isEmpty()) 1638 groupType = cConfig->value(QStringLiteral("GroupType"),QString()).toString(); 1639 if (eventType.isEmpty()) 1640 eventType = cConfig->value(QStringLiteral("EventType"),QString()).toString(); 1641 if (eventType2.isEmpty()) 1642 eventType2 = cConfig->value(QStringLiteral("EventType2"),QString()).toString(); 1643 delete cConfig; 1644 1645 setGroupType(groupType); 1646 setEventType(eventType); 1647 setEventType2(eventType2); 1648 1649 // if still no cost type set, use first available 1650 if (!_eventType && !_saCost->items().isEmpty()) 1651 eventTypeSelected(_saCost->items().first()); 1652 1653 ConfigGroup* aConfig = ConfigStorage::group(QStringLiteral("Layouts")); 1654 _layoutCount = aConfig->value(QStringLiteral("Count%1").arg(key), 0).toInt(); 1655 _layoutCurrent = aConfig->value(QStringLiteral("Current%1").arg(key), 0).toInt(); 1656 delete aConfig; 1657 1658 if (_layoutCount == 0) layoutRestore(); 1659 updateLayoutActions(); 1660 } 1661 1662 1663 /** 1664 * This must be called after setting group/cost types in the function 1665 * selection widget, because the group/function choosing depends on 1666 * filled lists in the function selection widget 1667 */ 1668 void TopLevel::restoreTraceSettings() 1669 { 1670 if (!_data) return; 1671 1672 QString key = traceKey(); 1673 1674 restoreCurrentState(key); 1675 1676 ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions")); 1677 QString group = pConfig->value(QStringLiteral("Group%1").arg(key),QString()).toString(); 1678 delete pConfig; 1679 if (!group.isEmpty()) setGroup(group); 1680 1681 1682 // restoreCurrentState() usually leads to a call to setTraceItemDelayed() 1683 // to restore last active item... 1684 if (!_traceItemDelayed) { 1685 // function not available any more.. try with "main" 1686 if (!setFunction(QStringLiteral("main"))) 1687 _functionSelection->selectTopFunction(); 1688 } 1689 } 1690 1691 1692 /* Layout */ 1693 1694 void TopLevel::layoutDuplicate() 1695 { 1696 // save current and allocate a new slot 1697 _multiView->saveLayout(QStringLiteral("Layout%1-MainView").arg(_layoutCurrent), 1698 traceKey()); 1699 _layoutCurrent = _layoutCount; 1700 _layoutCount++; 1701 1702 updateLayoutActions(); 1703 1704 if (0) qDebug() << "TopLevel::layoutDuplicate: count " << _layoutCount; 1705 } 1706 1707 void TopLevel::layoutRemove() 1708 { 1709 if (_layoutCount <2) return; 1710 1711 int from = _layoutCount-1; 1712 if (_layoutCurrent == from) { _layoutCurrent--; from--; } 1713 1714 // restore from last and decrement count 1715 _multiView->restoreLayout(QStringLiteral("Layout%1-MainView").arg(from), 1716 traceKey()); 1717 _layoutCount--; 1718 1719 updateLayoutActions(); 1720 } 1721 1722 void TopLevel::layoutNext() 1723 { 1724 if (_layoutCount <2) return; 1725 1726 QString key = traceKey(); 1727 QString layoutPrefix = QStringLiteral("Layout%1-MainView"); 1728 1729 _multiView->saveLayout(layoutPrefix.arg(_layoutCurrent), key); 1730 _layoutCurrent++; 1731 if (_layoutCurrent == _layoutCount) _layoutCurrent = 0; 1732 _multiView->restoreLayout(layoutPrefix.arg(_layoutCurrent), key); 1733 1734 if (0) qDebug() << "TopLevel::layoutNext: current " 1735 << _layoutCurrent; 1736 } 1737 1738 void TopLevel::layoutPrevious() 1739 { 1740 if (_layoutCount <2) return; 1741 1742 QString key = traceKey(); 1743 QString layoutPrefix = QStringLiteral("Layout%1-MainView"); 1744 1745 _multiView->saveLayout(layoutPrefix.arg(_layoutCurrent), key); 1746 _layoutCurrent--; 1747 if (_layoutCurrent <0) _layoutCurrent = _layoutCount-1; 1748 _multiView->restoreLayout(layoutPrefix.arg(_layoutCurrent), key); 1749 1750 if (0) qDebug() << "TopLevel::layoutPrevious: current " 1751 << _layoutCurrent; 1752 } 1753 1754 void TopLevel::layoutSave() 1755 { 1756 QString key = traceKey(); 1757 QString layoutPrefix = QStringLiteral("Layout%1-MainView"); 1758 1759 _multiView->saveLayout(layoutPrefix.arg(_layoutCurrent), key); 1760 1761 // save all layouts as defaults (ie. without any group name postfix) 1762 for(int i=0;i<_layoutCount;i++) { 1763 _multiView->restoreLayout(layoutPrefix.arg(i), key); 1764 _multiView->saveLayout(layoutPrefix.arg(i), QString()); 1765 } 1766 // restore the previously saved current layout 1767 _multiView->restoreLayout(layoutPrefix.arg(_layoutCurrent), key); 1768 1769 ConfigGroup* layoutConfig = ConfigStorage::group(QStringLiteral("Layouts")); 1770 layoutConfig->setValue(QStringLiteral("DefaultCount"), _layoutCount); 1771 layoutConfig->setValue(QStringLiteral("DefaultCurrent"), _layoutCurrent); 1772 delete layoutConfig; 1773 } 1774 1775 void TopLevel::layoutRestore() 1776 { 1777 KConfig *config = KSharedConfig::openConfig().data(); 1778 KConfigGroup aConfig(config, "Layouts"); 1779 _layoutCount = aConfig.readEntry("DefaultCount", 0); 1780 _layoutCurrent = aConfig.readEntry("DefaultCurrent", 0); 1781 if (_layoutCount == 0) { 1782 _layoutCount++; 1783 return; 1784 } 1785 1786 QString layoutPrefix = QStringLiteral("Layout%1-MainView"); 1787 _multiView->restoreLayout( layoutPrefix.arg(_layoutCurrent), traceKey()); 1788 1789 updateLayoutActions(); 1790 } 1791 1792 1793 void TopLevel::updateLayoutActions() 1794 { 1795 QAction* ka; 1796 1797 ka = actionCollection()->action(QStringLiteral("layout_next")); 1798 if (ka) ka->setEnabled(_layoutCount>1); 1799 1800 ka = actionCollection()->action(QStringLiteral("layout_previous")); 1801 if (ka) ka->setEnabled(_layoutCount>1); 1802 1803 ka = actionCollection()->action(QStringLiteral("layout_remove")); 1804 if (ka) ka->setEnabled(_layoutCount>1); 1805 1806 _statusbar->showMessage(i18n("Layout Count: %1", _layoutCount), 1000); 1807 } 1808 1809 1810 void TopLevel::updateStatusBar() 1811 { 1812 if (!_data || _data->parts().isEmpty()) { 1813 _statusLabel->setText(i18n("No profile data file loaded.")); 1814 return; 1815 } 1816 1817 QString status = QStringLiteral("%1 [%2] - ") 1818 .arg(_data->shortTraceName()) 1819 .arg(_data->activePartRange()); 1820 1821 if (_eventType) { 1822 status += i18n("Total %1 Cost: %2", 1823 _eventType->longName(), 1824 _data->prettySubCost(_eventType)); 1825 1826 /* this gets too long... 1827 if (_eventType2 && (_eventType2 != _eventType)) 1828 status += i18n(", %1 Cost: %2") 1829 .arg(_eventType2->longName()) 1830 .arg(_data->prettySubCost(_eventType2)); 1831 */ 1832 } 1833 else 1834 status += i18n("No event type selected"); 1835 1836 /* Not working... should give group of selected function 1837 1838 if (_groupType != ProfileContext::Function) { 1839 status += QString(" - %1 '%2'") 1840 .arg(ProfileContext::i18nTypeName(_groupType)) 1841 .arg(_group ? _group->prettyName() : i18n("(None)")); 1842 } 1843 */ 1844 1845 _statusLabel->setText(status); 1846 } 1847 1848 void TopLevel::configure() 1849 { 1850 if (ConfigDlg::configure( GlobalGUIConfig::config(), 1851 _data, this)) { 1852 GlobalGUIConfig::config()->saveOptions(); 1853 1854 configChanged(); 1855 } 1856 else 1857 GlobalGUIConfig::config()->readOptions(); 1858 } 1859 1860 bool TopLevel::queryClose() 1861 { 1862 saveTraceSettings(); 1863 1864 // save current toplevel options as defaults... 1865 GlobalConfig::setShowPercentage(_showPercentage); 1866 GlobalConfig::setShowExpanded(_showExpanded); 1867 GlobalConfig::setShowCycles(_showCycles); 1868 GlobalConfig::setHideTemplates(_hideTemplates); 1869 GlobalGUIConfig::config()->saveOptions(); 1870 1871 saveCurrentState(QString()); 1872 1873 // toolbar and dock positions are automatically stored 1874 1875 // if part dock was chosen visible even for only 1 part loaded, 1876 // keep this choice... 1877 _forcePartDock = false; 1878 if (_data && (_data->parts().count()<2) && _partDock->isVisible()) 1879 _forcePartDock=true; 1880 KConfigGroup dockConfig(KSharedConfig::openConfig(), "Docks"); 1881 dockConfig.writeEntry("ForcePartDockVisible", _forcePartDock); 1882 1883 return true; 1884 } 1885 1886 1887 void TopLevel::splitSlot() 1888 { 1889 int count = _multiView->childCount(); 1890 if (count<1) count = 1; 1891 if (count>2) count = 2; 1892 count = 3-count; 1893 _multiView->setChildCount(count); 1894 1895 _taSplit->setChecked(count>1); 1896 _taSplitDir->setEnabled(count>1); 1897 _taSplitDir->setChecked(_multiView->orientation() == Qt::Horizontal); 1898 } 1899 1900 void TopLevel::splitDirSlot() 1901 { 1902 _multiView->setOrientation( _taSplitDir->isChecked() ? 1903 Qt::Horizontal : Qt::Vertical ); 1904 } 1905 1906 1907 1908 // this is called after a config change in the dialog 1909 void TopLevel::configChanged() 1910 { 1911 // Invalidate found/cached dirs of source files if we have TraceData loaded. 1912 if (_data) { 1913 _data->resetSourceDirs(); 1914 } 1915 1916 _stackSelection->refresh(); 1917 1918 updateViewsOnChange(TraceItemView::configChanged); 1919 } 1920 1921 void TopLevel::dummySlot() 1922 { 1923 } 1924 1925 void TopLevel::activePartsChangedSlot(const TracePartList& list) 1926 { 1927 if (!_data) return; 1928 1929 if (!_data->activateParts(list)) { 1930 // qDebug("TopLevel::activePartsChangedSlot: No Change!"); 1931 return; 1932 } 1933 _activeParts = list; 1934 1935 _partSelection->set(list); 1936 _multiView->set(list); 1937 _functionSelection->set(list); 1938 _stackSelection->refresh(); 1939 1940 updateStatusBar(); 1941 } 1942 1943 void TopLevel::partsHideSelectedSlotDelayed() 1944 { 1945 QTimer::singleShot( 0, this, &TopLevel::partsHideSelectedSlot ); 1946 } 1947 1948 // this puts selected parts into hidden list, 1949 // deselects them and makes the remaining parts selected 1950 void TopLevel::partsHideSelectedSlot() 1951 { 1952 if (!_data) return; 1953 1954 TracePartList newHidden, newActive; 1955 foreach(TracePart* part, _data->parts()) { 1956 if (_activeParts.contains(part) || 1957 _hiddenParts.contains(part)) 1958 newHidden.append(part); 1959 else 1960 newActive.append(part); 1961 } 1962 1963 _hiddenParts = newHidden; 1964 _partSelection->hiddenPartsChangedSlot(_hiddenParts); 1965 1966 #if 0 1967 _mainWidget1->hiddenPartsChangedSlot(_hiddenParts); 1968 _mainWidget2->hiddenPartsChangedSlot(_hiddenParts); 1969 #endif 1970 1971 activePartsChangedSlot(newActive); 1972 } 1973 1974 void TopLevel::partsUnhideAllSlotDelayed() 1975 { 1976 QTimer::singleShot( 0, this, &TopLevel::partsUnhideAllSlot ); 1977 } 1978 1979 // this unhides all hidden parts. Does NOT change selection 1980 void TopLevel::partsUnhideAllSlot() 1981 { 1982 if (!_data) return; 1983 1984 _hiddenParts.clear(); 1985 _partSelection->hiddenPartsChangedSlot(_hiddenParts); 1986 #if 0 1987 _mainWidget1->hiddenPartsChangedSlot(_hiddenParts); 1988 _mainWidget2->hiddenPartsChangedSlot(_hiddenParts); 1989 #endif 1990 } 1991 1992 void TopLevel::forceTrace() 1993 { 1994 if (_ccProcess) { 1995 // callgrind_control still running, cancel request 1996 qDebug("TopLevel::forceTrace: killing old callgrind_control"); 1997 _ccProcess->kill(); 1998 delete _ccProcess; 1999 _ccProcess = nullptr; 2000 _ccOutput = QString(); 2001 } 2002 if (!_taDump->isChecked()) return; 2003 2004 // get PID of first loaded part 2005 int pid = 0; 2006 TracePart* p = nullptr; 2007 TracePartList pl; 2008 if (_data) pl = _data->parts(); 2009 if (!pl.isEmpty()) p = pl.first(); 2010 if (p) pid = p->processID(); 2011 if (pid == 0) { 2012 showMessage(i18n("Cannot determine receiver PID for dump request"), 2013 5000); 2014 _taDump->setChecked(false); 2015 return; 2016 } 2017 2018 qDebug("TopLevel::forceTrace: run 'callgrind_control -d %d'", pid); 2019 2020 _ccProcess = new QProcess(this); 2021 connect(_ccProcess, &QProcess::readyReadStandardOutput, 2022 this, &TopLevel::ccReadOutput); 2023 connect(_ccProcess, SIGNAL(error(QProcess::ProcessError)), 2024 SLOT(ccError(QProcess::ProcessError))); 2025 connect(_ccProcess, SIGNAL(finished(int,QProcess::ExitStatus)), 2026 SLOT(ccExit(int,QProcess::ExitStatus))); 2027 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 2028 _ccProcess->start(QStringLiteral("callgrind_control -d %1").arg(pid), 2029 QIODevice::ReadOnly); 2030 #else 2031 _ccProcess->startCommand(QStringLiteral("callgrind_control -d %1").arg(pid), 2032 QIODevice::ReadOnly); 2033 #endif 2034 } 2035 2036 void TopLevel::ccReadOutput() 2037 { 2038 QProcess* p = qobject_cast<QProcess*>(sender()); 2039 qDebug("TopLevel::ccReadOutput: QProcess %p", p); 2040 2041 // signal from old/uninteresting process? 2042 if (!_ccProcess) return; 2043 if (p != _ccProcess) return; 2044 2045 _ccOutput.append(QString::fromLocal8Bit(_ccProcess->readAllStandardOutput())); 2046 } 2047 2048 void TopLevel::ccError(QProcess::ProcessError e) 2049 { 2050 QProcess* p = qobject_cast<QProcess*>(sender()); 2051 qDebug("TopLevel::ccError: Got %d from QProcess %p", 2052 e, p); 2053 2054 // signal from old/uninteresting process? 2055 if (!_ccProcess) return; 2056 if (p != _ccProcess) return; 2057 2058 showMessage(i18n("Error running callgrind_control"), 5000); 2059 2060 _ccProcess->deleteLater(); 2061 _ccProcess = nullptr; 2062 } 2063 2064 void TopLevel::ccExit(int exitCode, QProcess::ExitStatus s) 2065 { 2066 QProcess* p = qobject_cast<QProcess*>(sender()); 2067 qDebug("TopLevel::ccExit: QProcess %p, exitCode %d", 2068 p, exitCode); 2069 2070 // signal from old/uninteresting process? 2071 if (!_ccProcess) return; 2072 if (p != _ccProcess) return; 2073 _ccProcess->deleteLater(); 2074 _ccProcess = nullptr; 2075 _taDump->setChecked(false); 2076 2077 // if not successful no need to reload 2078 if ((s == QProcess::CrashExit) || (exitCode != 0)) 2079 return; 2080 2081 // FIXME: Are we sure that file is completely 2082 // dumped after waiting one second? 2083 QTimer::singleShot( 1000, this, &TopLevel::reload ); 2084 } 2085 2086 void TopLevel::forwardAboutToShow() 2087 { 2088 QMenu *popup = _paForward->menu(); 2089 2090 popup->clear(); 2091 StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; 2092 HistoryItem* hi = b ? b->current() : nullptr; 2093 TraceFunction* f; 2094 QAction* action; 2095 2096 if (!hi) { 2097 popup->addAction(i18n("(No Stack)")); 2098 return; 2099 } 2100 2101 hi = hi->next(); 2102 if (!hi) { 2103 popup->addAction(i18n("(No next function)")); 2104 return; 2105 } 2106 2107 int count = 1; 2108 while (count<GlobalConfig::maxSymbolCount() && hi) { 2109 f = hi->function(); 2110 if (!f) break; 2111 2112 QString name = GlobalConfig::shortenSymbol(f->prettyName()); 2113 2114 //qDebug("forward: Adding %s", name.ascii()); 2115 action = popup->addAction(name); 2116 action->setData(count); 2117 2118 hi = hi->next(); 2119 count++; 2120 } 2121 } 2122 2123 void TopLevel::backAboutToShow() 2124 { 2125 QMenu *popup = _paBack->menu(); 2126 2127 popup->clear(); 2128 StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; 2129 HistoryItem* hi = b ? b->current() : nullptr; 2130 TraceFunction* f; 2131 QAction* action; 2132 2133 if (!hi) { 2134 popup->addAction(i18n("(No Stack)")); 2135 return; 2136 } 2137 2138 hi = hi->last(); 2139 if (!hi) { 2140 popup->addAction(i18n("(No previous function)")); 2141 return; 2142 } 2143 2144 int count = 1; 2145 while (count<GlobalConfig::maxSymbolCount() && hi) { 2146 f = hi->function(); 2147 if (!f) break; 2148 2149 QString name = GlobalConfig::shortenSymbol(f->prettyName()); 2150 2151 //qDebug("back: Adding %s", name.ascii()); 2152 action = popup->addAction(name); 2153 action->setData(count); 2154 2155 hi = hi->last(); 2156 count++; 2157 } 2158 } 2159 2160 void TopLevel::upAboutToShow() 2161 { 2162 QMenu *popup = _paUp->menu(); 2163 2164 popup->clear(); 2165 StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; 2166 HistoryItem* hi = b ? b->current() : nullptr; 2167 TraceFunction* f = hi ? hi->function() : nullptr; 2168 QAction* action; 2169 2170 if (!f) { 2171 popup->addAction(i18n("(No Stack)")); 2172 return; 2173 } 2174 f = hi->stack()->caller(f, false); 2175 if (!f) { 2176 popup->addAction(i18n("(No Function Up)")); 2177 return; 2178 } 2179 2180 int count = 1; 2181 while (count<GlobalConfig::maxSymbolCount() && f) { 2182 QString name = GlobalConfig::shortenSymbol(f->prettyName()); 2183 2184 action = popup->addAction(name); 2185 action->setData(count); 2186 2187 f = hi->stack()->caller(f, false); 2188 count++; 2189 } 2190 2191 } 2192 2193 void TopLevel::forwardTriggered(QAction* action) 2194 { 2195 int count = action->data().toInt(nullptr); 2196 //qDebug("forwardTriggered: %d", count); 2197 if( count <= 0) 2198 return; 2199 2200 StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; 2201 if (!b) return; 2202 2203 while (count>1) { 2204 b->goForward(); 2205 count--; 2206 } 2207 _stackSelection->browserForward(); 2208 } 2209 2210 void TopLevel::backTriggered(QAction* action) 2211 { 2212 int count = action->data().toInt(nullptr); 2213 //qDebug("backTriggered: %d", count); 2214 if( count <= 0) 2215 return; 2216 2217 StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; 2218 if (!b) return; 2219 2220 while (count>1) { 2221 b->goBack(); 2222 count--; 2223 } 2224 _stackSelection->browserBack(); 2225 } 2226 2227 void TopLevel::upTriggered(QAction* action) 2228 { 2229 int count = action->data().toInt(nullptr); 2230 //qDebug("upTriggered: %d", count); 2231 if( count <= 0) 2232 return; 2233 2234 StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; 2235 HistoryItem* hi = b ? b->current() : nullptr; 2236 if (!hi) return; 2237 2238 TraceFunction* f = hi->function(); 2239 2240 while (count>0 && f) { 2241 f = hi->stack()->caller(f, false); 2242 count--; 2243 } 2244 2245 //qDebug("upActivated: %s", f ? f->prettyName().ascii() : "??" ); 2246 if (f) 2247 setFunction(f); 2248 } 2249 2250 void TopLevel::showMessage(const QString& msg, int ms) 2251 { 2252 if (_statusbar) 2253 _statusbar->showMessage(msg, ms); 2254 } 2255 2256 void TopLevel::showStatus(const QString& msg, int progress) 2257 { 2258 static bool msgUpdateNeeded = true; 2259 2260 if (!_statusbar) return; 2261 2262 if (msg.isEmpty()) { 2263 //reset status 2264 if (_progressBar) { 2265 _statusbar->removeWidget(_progressBar); 2266 delete _progressBar; 2267 _progressBar = nullptr; 2268 } 2269 _statusbar->clearMessage(); 2270 _progressMsg = msg; 2271 return; 2272 } 2273 2274 if (_progressMsg.isEmpty()) 2275 _progressStart.start(); 2276 2277 if (msg != _progressMsg) { 2278 _progressMsg = msg; 2279 msgUpdateNeeded = true; 2280 } 2281 2282 // do nothing if last change was less than 0.5 seconds ago 2283 if (_progressStart.elapsed() < 500) 2284 return; 2285 2286 if (!_progressBar) { 2287 _progressBar = new QProgressBar(_statusbar); 2288 _progressBar->setMaximumSize(200, _statusbar->height()-4); 2289 _statusbar->addPermanentWidget(_progressBar, 1); 2290 _progressBar->show(); 2291 msgUpdateNeeded = true; 2292 } 2293 2294 _progressStart.restart(); 2295 2296 if (msgUpdateNeeded) { 2297 _statusbar->showMessage(msg); 2298 msgUpdateNeeded = false; 2299 } 2300 _progressBar->setValue(progress); 2301 2302 // let the progress bar update itself 2303 qApp->processEvents(QEventLoop::ExcludeUserInputEvents); 2304 } 2305 2306 void TopLevel::loadStart(const QString& filename) 2307 { 2308 showStatus(i18n("Loading %1", filename), 0); 2309 Logger::_filename = filename; 2310 } 2311 2312 void TopLevel::loadFinished(const QString& msg) 2313 { 2314 showStatus(QString(), 0); 2315 if (!msg.isEmpty()) 2316 showMessage(i18n("Error loading %1: %2", _filename, msg), 2317 2000); 2318 } 2319 2320 void TopLevel::loadProgress(int progress) 2321 { 2322 showStatus(i18n("Loading %1", _filename), progress); 2323 } 2324 2325 void TopLevel::loadError(int line, const QString& msg) 2326 { 2327 qCritical() << "Loading" << _filename << ":" << line << ": " << msg; 2328 } 2329 2330 void TopLevel::loadWarning(int line, const QString& msg) 2331 { 2332 qWarning() << "Loading" << _filename << ":" << line << ": " << msg; 2333 } 2334 2335 bool TopLevel::openDataFile(const QString& file) 2336 { 2337 TraceData* d = new TraceData(this); 2338 int filesLoaded; 2339 2340 // see whether this file is compressed, than take the direct route 2341 QMimeDatabase dataBase; 2342 QString mimeType = dataBase.mimeTypeForFile(file, QMimeDatabase::MatchContent).name(); 2343 2344 auto compressionType = KCompressionDevice::compressionTypeForMimeType(mimeType); 2345 KCompressionDevice* compressed; 2346 compressed = new KCompressionDevice(file, 2347 compressionType); 2348 if (compressed && 2349 (compressed->compressionType() != KCompressionDevice::None)) { 2350 filesLoaded = d->load(compressed, file); 2351 } else { 2352 // else fallback to string based method that can also find multi-part callgrind data. 2353 filesLoaded = d->load(file); 2354 } 2355 if (filesLoaded > 0) { 2356 setData(d); 2357 return true; 2358 } else { 2359 return false; 2360 } 2361 } 2362 2363 #include "moc_toplevel.cpp"