File indexing completed on 2024-04-28 17:02:19
0001 /* 0002 This file is part of Massif Visualizer 0003 0004 Copyright 2010 Milian Wolff <mail@milianw.de> 0005 Copyright 2013 Arnold Dumas <contact@arnolddumas.fr> 0006 0007 This program is free software; you can redistribute it and/or 0008 modify it under the terms of the GNU General Public License as 0009 published by the Free Software Foundation; either version 2 of 0010 the License or (at your option) version 3 or any later version 0011 accepted by the membership of KDE e.V. (or its successor approved 0012 by the membership of KDE e.V.), which shall act as a proxy 0013 defined in Section 14 of version 3 of the license. 0014 0015 This program is distributed in the hope that it will be useful, 0016 but WITHOUT ANY WARRANTY; without even the implied warranty of 0017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0018 GNU General Public License for more details. 0019 0020 You should have received a copy of the GNU General Public License 0021 along with this program. If not, see <http://www.gnu.org/licenses/>. 0022 */ 0023 0024 #include "documentwidget.h" 0025 0026 #include <QApplication> 0027 0028 #include "massifdata/filedata.h" 0029 #include "massifdata/parser.h" 0030 #include "massifdata/parseworker.h" 0031 #include "massifdata/snapshotitem.h" 0032 #include "massifdata/treeleafitem.h" 0033 #include "massifdata/util.h" 0034 0035 #include <KStandardAction> 0036 #include <KColorScheme> 0037 0038 #include <KLocalizedString> 0039 // forward include not available until later KDE versions... 0040 #include <kmessagewidget.h> 0041 #include <KXMLGUIFactory> 0042 0043 #include <QDebug> 0044 #include <QLabel> 0045 #include <QProgressBar> 0046 #include <QStackedWidget> 0047 #include <QTabWidget> 0048 #include <QToolButton> 0049 #include <QVBoxLayout> 0050 #include <QIcon> 0051 0052 #include "charttab.h" 0053 #include "allocatorstab.h" 0054 0055 #ifdef HAVE_KGRAPHVIEWER 0056 #include "callgraphtab.h" 0057 0058 #include <KPluginLoader> 0059 #include <KPluginFactory> 0060 #include <KParts/ReadOnlyPart> 0061 #endif 0062 0063 using namespace Massif; 0064 0065 DocumentWidget::DocumentWidget(const QUrl& file, const QStringList& customAllocators, 0066 KXMLGUIClient* guiParent, QWidget* parent) 0067 : QWidget(parent) 0068 , KXMLGUIClient(guiParent) 0069 , m_data(0) 0070 , m_parseWorker(new ParseWorker) 0071 , m_file(file) 0072 , m_currentTab(0) 0073 , m_stackedWidget(new QStackedWidget(this)) 0074 , m_tabs(new QTabWidget(m_stackedWidget)) 0075 , m_errorMessage(0) 0076 , m_loadingMessage(0) 0077 , m_loadingProgressBar(0) 0078 , m_stopParserButton(0) 0079 , m_isLoaded(false) 0080 { 0081 connect(m_parseWorker, &ParseWorker::finished, 0082 this, &DocumentWidget::parserFinished); 0083 connect(m_parseWorker, &ParseWorker::error, 0084 this, &DocumentWidget::showError); 0085 connect(m_parseWorker, &ParseWorker::progressRange, 0086 this, &DocumentWidget::setRange); 0087 connect(m_parseWorker, &ParseWorker::progress, 0088 this, &DocumentWidget::setProgress); 0089 0090 // Create dedicated thread for this document. 0091 // TODO: use ThreadWeaver 0092 QThread* thread = new QThread(this); 0093 thread->start(); 0094 m_parseWorker->moveToThread(thread); 0095 m_parseWorker->parse(file, customAllocators); 0096 0097 setXMLFile(QStringLiteral("documentwidgetui.rc"), true); 0098 0099 // Set m_stackedWidget as the main widget. 0100 setLayout(new QVBoxLayout(this)); 0101 layout()->addWidget(m_stackedWidget); 0102 0103 m_tabs->setTabPosition(QTabWidget::South); 0104 m_stackedWidget->addWidget(m_tabs); 0105 0106 // Second widget : loadingPage 0107 QWidget* loadingPage = new QWidget(m_stackedWidget); 0108 QVBoxLayout* verticalLayout = new QVBoxLayout(loadingPage); 0109 QSpacerItem* upperSpacerItem = new QSpacerItem(20, 247, QSizePolicy::Minimum, QSizePolicy::Expanding); 0110 verticalLayout->addItem(upperSpacerItem); 0111 0112 m_loadingMessage = new QLabel(loadingPage); 0113 m_loadingMessage->setText(i18n("loading file <i>%1</i>...", file.toString())); 0114 m_loadingMessage->setAlignment(Qt::AlignCenter); 0115 verticalLayout->addWidget(m_loadingMessage); 0116 0117 m_loadingProgressBar = new QProgressBar(loadingPage); 0118 m_loadingProgressBar->setValue(24); 0119 m_loadingProgressBar->setRange(0, 0); 0120 verticalLayout->addWidget(m_loadingProgressBar); 0121 0122 QWidget* stopParserWidget = new QWidget(loadingPage); 0123 stopParserWidget->setLayoutDirection(Qt::LeftToRight); 0124 QHBoxLayout* stopParserWidgetLayout = new QHBoxLayout(stopParserWidget); 0125 m_stopParserButton = new QToolButton(stopParserWidget); 0126 m_stopParserButton->setObjectName(QStringLiteral("stopParsing")); 0127 m_stopParserButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); 0128 m_stopParserButton->setIcon(QIcon::fromTheme(QStringLiteral("process-stop"))); 0129 m_stopParserButton->setIconSize(QSize(48, 48)); 0130 connect(m_stopParserButton, &QToolButton::clicked, 0131 this, &DocumentWidget::requestClose); 0132 stopParserWidgetLayout->addWidget(m_stopParserButton); 0133 verticalLayout->addWidget(stopParserWidget); 0134 0135 QSpacerItem* bottomSpacerItem = new QSpacerItem(20, 230, QSizePolicy::Minimum, QSizePolicy::Expanding); 0136 verticalLayout->addItem(bottomSpacerItem); 0137 m_stackedWidget->addWidget(loadingPage); 0138 0139 // By default we show the loadingPage. 0140 m_stackedWidget->setCurrentIndex(1); 0141 } 0142 0143 DocumentWidget::~DocumentWidget() 0144 { 0145 stopParser(); 0146 if (m_data) { 0147 delete m_data; 0148 m_data = 0; 0149 m_file.clear(); 0150 } 0151 } 0152 0153 FileData* DocumentWidget::data() const 0154 { 0155 return m_data; 0156 } 0157 0158 QUrl DocumentWidget::file() const 0159 { 0160 return m_file; 0161 } 0162 0163 bool DocumentWidget::isLoaded() const 0164 { 0165 return m_isLoaded; 0166 } 0167 0168 void DocumentWidget::settingsChanged() 0169 { 0170 for (int i = 0; i < m_tabs->count(); ++i) { 0171 static_cast<DocumentTabInterface*>(m_tabs->widget(i))->settingsChanged(); 0172 } 0173 } 0174 0175 void DocumentWidget::parserFinished(const QUrl& file, FileData* data) 0176 { 0177 Q_ASSERT(data->peak()); 0178 0179 qDebug() << "loaded massif file:" << file; 0180 qDebug() << "description:" << data->description(); 0181 qDebug() << "command:" << data->cmd(); 0182 qDebug() << "time unit:" << data->timeUnit(); 0183 qDebug() << "snapshots:" << data->snapshots().size(); 0184 qDebug() << "peak: snapshot #" << data->peak()->number() << "after" << data->peak()->time() << data->timeUnit(); 0185 qDebug() << "peak cost:" << prettyCost(data->peak()->memHeap()) << "heap" 0186 << prettyCost(data->peak()->memHeapExtra()) << "heap extra" 0187 << prettyCost(data->peak()->memStacks()) << "stacks"; 0188 0189 m_data = data; 0190 m_file = file; 0191 0192 m_tabs->addTab(new ChartTab(m_data, this, this), QIcon::fromTheme(QStringLiteral("office-chart-area-stacked")), 0193 i18n("Memory Chart")); 0194 0195 #ifdef HAVE_KGRAPHVIEWER 0196 static KPluginFactory *factory = KPluginLoader("kgraphviewerpart").factory(); 0197 if (factory) { 0198 KParts::ReadOnlyPart* part = factory->create<KParts::ReadOnlyPart>("kgraphviewerpart", this); 0199 if (part) { 0200 m_tabs->addTab(new CallGraphTab(m_data, part, this, this), QIcon::fromTheme(QStringLiteral("kgraphviewer")), 0201 i18n("Callgraph")); 0202 } 0203 } 0204 #endif 0205 0206 m_tabs->addTab(new AllocatorsTab(m_data, this, this), QIcon::fromTheme(QStringLiteral("view-list-text")), 0207 i18n("Allocators")); 0208 0209 for (int i = 0; i < m_tabs->count(); ++i) { 0210 DocumentTabInterface* tab = static_cast<DocumentTabInterface*>(m_tabs->widget(i)); 0211 connect(tab, &DocumentTabInterface::modelItemSelected, 0212 this, &DocumentWidget::modelItemSelected); 0213 connect(tab, &DocumentTabInterface::contextMenuRequested, 0214 this, &DocumentWidget::contextMenuRequested); 0215 } 0216 0217 m_tabs->setCurrentIndex(0); 0218 connect(m_tabs, &QTabWidget::currentChanged, 0219 this, &DocumentWidget::slotTabChanged); 0220 slotTabChanged(0); 0221 0222 m_isLoaded = true; 0223 0224 // Switch to the display page and notify that everything is setup. 0225 m_stackedWidget->setCurrentIndex(0); 0226 emit loadingFinished(); 0227 } 0228 0229 void DocumentWidget::addGuiActions(KXMLGUIFactory* factory) 0230 { 0231 factory->addClient(this); 0232 0233 // ensure only the current tab's client is in the factory 0234 // otherwise the actions from the other tabs are visible 0235 const int current = m_tabs->currentIndex(); 0236 for (int i = 0; i < m_tabs->count(); ++i) { 0237 if (i != current) { 0238 factory->removeClient(static_cast<DocumentTabInterface*>(m_tabs->widget(i))); 0239 } 0240 } 0241 } 0242 0243 void DocumentWidget::clearGuiActions(KXMLGUIFactory* factory) 0244 { 0245 factory->removeClient(this); 0246 } 0247 0248 void DocumentWidget::slotTabChanged(int tab) 0249 { 0250 if (!factory()) { 0251 return; 0252 } 0253 0254 if (m_currentTab) { 0255 factory()->removeClient(m_currentTab); 0256 } 0257 0258 if (tab >= 0 && tab < m_tabs->count()) { 0259 m_currentTab = static_cast<DocumentTabInterface*>(m_tabs->widget(tab)); 0260 factory()->addClient(m_currentTab); 0261 } 0262 } 0263 0264 void DocumentWidget::selectModelItem(const ModelItem& item) 0265 { 0266 for (int i = 0; i < m_tabs->count(); ++i) { 0267 static_cast<DocumentTabInterface*>(m_tabs->widget(i))->selectModelItem(item); 0268 } 0269 } 0270 0271 void DocumentWidget::setProgress(int value) 0272 { 0273 m_loadingProgressBar->setValue(value); 0274 } 0275 0276 void DocumentWidget::setRange(int minimum, int maximum) 0277 { 0278 m_loadingProgressBar->setRange(minimum, maximum); 0279 } 0280 0281 void DocumentWidget::showError(const QString& title, const QString& error) 0282 { 0283 if (!m_errorMessage) { 0284 m_errorMessage = new KMessageWidget(m_stackedWidget); 0285 m_stackedWidget->addWidget(m_errorMessage); 0286 m_errorMessage->setWordWrap(true); 0287 m_errorMessage->setMessageType(KMessageWidget::Error); 0288 m_errorMessage->setCloseButtonVisible(false); 0289 } 0290 m_errorMessage->setText(QString::fromLatin1("<b>%1</b><p style=\"text-align:left\">%2</p>").arg(title).arg(error)); 0291 m_stackedWidget->setCurrentWidget(m_errorMessage); 0292 } 0293 0294 void DocumentWidget::stopParser() 0295 { 0296 if (!m_parseWorker) { 0297 return; 0298 } 0299 0300 QThread* thread = m_parseWorker->thread(); 0301 m_parseWorker->stop(); 0302 m_parseWorker->deleteLater(); 0303 m_parseWorker = 0; 0304 thread->quit(); 0305 thread->wait(); 0306 }