aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/valgrind/callgrindtool.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/valgrind/callgrindtool.cpp')
-rw-r--r--src/plugins/valgrind/callgrindtool.cpp1003
1 files changed, 1003 insertions, 0 deletions
diff --git a/src/plugins/valgrind/callgrindtool.cpp b/src/plugins/valgrind/callgrindtool.cpp
new file mode 100644
index 00000000000..3378668689b
--- /dev/null
+++ b/src/plugins/valgrind/callgrindtool.cpp
@@ -0,0 +1,1003 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation ([email protected])
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at [email protected].
+**
+**************************************************************************/
+
+#include "callgrindtool.h"
+
+#include "callgrindcostdelegate.h"
+#include "callgrindcostview.h"
+#include "callgrindengine.h"
+#include "callgrindsettings.h"
+#include "callgrindtextmark.h"
+#include "callgrindvisualisation.h"
+
+#include <valgrind/callgrind/callgrindcallmodel.h>
+#include <valgrind/callgrind/callgrindcostitem.h>
+#include <valgrind/callgrind/callgrinddatamodel.h>
+#include <valgrind/callgrind/callgrindfunction.h>
+#include <valgrind/callgrind/callgrindfunctioncall.h>
+#include <valgrind/callgrind/callgrindparsedata.h>
+#include <valgrind/callgrind/callgrindproxymodel.h>
+#include <valgrind/callgrind/callgrindstackbrowser.h>
+
+#include <analyzerbase/analyzermanager.h>
+#include <analyzerbase/analyzersettings.h>
+#include <analyzerbase/analyzerutils.h>
+#include <analyzerbase/analyzerconstants.h>
+
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/command.h>
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/icontext.h>
+#include <coreplugin/icore.h>
+
+#include <cplusplus/LookupContext.h>
+#include <cplusplus/Overview.h>
+#include <cppeditor/cppeditorconstants.h>
+#include <extensionsystem/iplugin.h>
+#include <texteditor/basetexteditor.h>
+
+#include <utils/qtcassert.h>
+#include <utils/fancymainwindow.h>
+#include <utils/styledbar.h>
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/projectexplorer.h>
+
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTimer>
+
+#include <QtGui/QAction>
+#include <QtGui/QActionGroup>
+#include <QtGui/QCheckBox>
+#include <QtGui/QComboBox>
+#include <QtGui/QDockWidget>
+#include <QtGui/QGraphicsItem>
+#include <QtGui/QHBoxLayout>
+#include <QtGui/QLineEdit>
+#include <QtGui/QMenu>
+#include <QtGui/QSortFilterProxyModel>
+#include <QtGui/QToolBar>
+#include <QtGui/QToolButton>
+#include <QtGui/QVBoxLayout>
+
+// shared/cplusplus includes
+#include <Symbols.h>
+
+using namespace Analyzer;
+using namespace Core;
+using namespace Valgrind::Callgrind;
+
+namespace Valgrind {
+namespace Internal {
+
+class CallgrindToolPrivate : public QObject
+{
+ Q_OBJECT
+
+public:
+ friend class CallgrindTool;
+
+ explicit CallgrindToolPrivate(CallgrindTool *parent);
+ virtual ~CallgrindToolPrivate();
+
+ void setParseData(ParseData *data);
+
+ CostDelegate::CostFormat costFormat() const;
+ QWidget *createControlWidget();
+ void initializeDockWidgets();
+
+ void ensureDockWidgets();
+ void doClear(bool clearParseData);
+ void updateEventCombo();
+
+ IAnalyzerEngine *createEngine(const AnalyzerStartParameters &sp,
+ ProjectExplorer::RunConfiguration *runConfiguration);
+
+signals:
+ void cycleDetectionEnabled(bool enabled);
+ void dumpRequested();
+ void resetRequested();
+ void pauseToggled(bool checked);
+
+public slots:
+ void slotClear();
+ void slotRequestDump();
+
+ void selectFunction(const Valgrind::Callgrind::Function *);
+ void setCostFormat(Valgrind::Internal::CostDelegate::CostFormat format);
+ void enableCycleDetection(bool enabled);
+ void setCostEvent(int index);
+
+ /// This function will add custom text marks to the editor
+ /// \note Call this after the data model has been populated
+ void createTextMarks();
+
+ /// This function will clear all text marks from the editor
+ void clearTextMarks();
+
+ void updateFilterString();
+ void updateCostFormat();
+
+ void handleFilterProjectCosts();
+ void handleShowCostsAction();
+ void handleShowCostsOfFunction();
+
+ void slotGoToOverview();
+ void stackBrowserChanged();
+
+ /// If \param busy is true, all widgets get a busy cursor when hovered
+ void setBusy(bool busy);
+
+ void dataFunctionSelected(const QModelIndex &index);
+ void calleeFunctionSelected(const QModelIndex &index);
+ void callerFunctionSelected(const QModelIndex &index);
+ void visualisationFunctionSelected(const Valgrind::Callgrind::Function *function);
+ void showParserResults(const Valgrind::Callgrind::ParseData *data);
+
+ void takeParserData(CallgrindEngine *engine);
+ void engineStarting(const Analyzer::IAnalyzerEngine *);
+ void engineFinished();
+
+ void editorOpened(Core::IEditor *);
+ void requestContextMenu(TextEditor::ITextEditor *editor, int line, QMenu *menu);
+
+public:
+ CallgrindTool *q;
+
+ DataModel *m_dataModel;
+ DataProxyModel *m_proxyModel;
+ StackBrowser *m_stackBrowser;
+
+ CallModel *m_callersModel;
+ CallModel *m_calleesModel;
+
+ // callgrind widgets
+ CostView *m_flatView;
+ CostView *m_callersView;
+ CostView *m_calleesView;
+ Visualisation *m_visualisation;
+
+ // navigation
+ QAction *m_goToOverview;
+ QAction *m_goBack;
+ QLineEdit *m_searchFilter;
+
+ // cost formatting
+ QAction *m_filterProjectCosts;
+ QAction *m_costAbsolute;
+ QAction *m_costRelative;
+ QAction *m_costRelativeToParent;
+ QAction *m_cycleDetection;
+ QComboBox *m_eventCombo;
+
+ QTimer *m_updateTimer;
+
+ QVector<CallgrindTextMark *> m_textMarks;
+
+ QAction *m_dumpAction;
+ QAction *m_resetAction;
+ QAction *m_pauseAction;
+ QAction *m_showCostsOfFunctionAction;
+
+ QString m_toggleCollectFunction;
+ CallgrindGlobalSettings *m_settings; // Not owned
+};
+
+
+CallgrindToolPrivate::CallgrindToolPrivate(CallgrindTool *parent)
+ : q(parent)
+ , m_dataModel(new DataModel(this))
+ , m_proxyModel(new DataProxyModel(this))
+ , m_stackBrowser(new StackBrowser(this))
+ , m_callersModel(new CallModel(this))
+ , m_calleesModel(new CallModel(this))
+ , m_flatView(0)
+ , m_callersView(0)
+ , m_calleesView(0)
+ , m_visualisation(0)
+ , m_goToOverview(0)
+ , m_goBack(0)
+ , m_searchFilter(0)
+ , m_filterProjectCosts(0)
+ , m_costAbsolute(0)
+ , m_costRelative(0)
+ , m_costRelativeToParent(0)
+ , m_eventCombo(0)
+ , m_updateTimer(new QTimer(this))
+ , m_dumpAction(0)
+ , m_resetAction(0)
+ , m_pauseAction(0)
+ , m_showCostsOfFunctionAction(0)
+ , m_settings(0)
+{
+ connect(m_stackBrowser, SIGNAL(currentChanged()), this, SLOT(stackBrowserChanged()));
+
+ m_updateTimer->setInterval(200);
+ m_updateTimer->setSingleShot(true);
+ connect(m_updateTimer, SIGNAL(timeout()), SLOT(updateFilterString()));
+
+ m_proxyModel->setSourceModel(m_dataModel);
+ m_proxyModel->setDynamicSortFilter(true);
+ m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
+ m_proxyModel->setFilterKeyColumn(DataModel::NameColumn);
+ m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+
+ m_settings = AnalyzerGlobalSettings::instance()->subConfig<CallgrindGlobalSettings>();
+}
+
+CallgrindToolPrivate::~CallgrindToolPrivate()
+{
+ qDeleteAll(m_textMarks);
+ doClear(false);
+}
+
+void CallgrindToolPrivate::slotGoToOverview()
+{
+ selectFunction(0);
+}
+
+void CallgrindToolPrivate::slotClear()
+{
+ doClear(true);
+}
+
+void CallgrindToolPrivate::doClear(bool clearParseData)
+{
+ if (clearParseData) // Crashed when done from destructor.
+ setParseData(0);
+
+ // clear filters
+ if (m_filterProjectCosts)
+ m_filterProjectCosts->setChecked(false);
+ m_proxyModel->setFilterBaseDir(QString());
+ if (m_searchFilter)
+ m_searchFilter->setText(QString());
+ m_proxyModel->setFilterFixedString(QString());
+}
+
+void CallgrindToolPrivate::setBusy(bool busy)
+{
+ QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
+ m_flatView->setCursor(cursor);
+ m_calleesView->setCursor(cursor);
+ m_callersView->setCursor(cursor);
+ m_visualisation->setCursor(cursor);
+}
+
+void CallgrindToolPrivate::selectFunction(const Function *func)
+{
+ if (!func) {
+ m_goToOverview->setDisabled(true);
+ m_flatView->clearSelection();
+ m_visualisation->setFunction(0);
+ m_callersModel->clear();
+ m_calleesModel->clear();
+ return;
+ }
+
+ m_goToOverview->setEnabled(true);
+
+ const QModelIndex index = m_dataModel->indexForObject(func);
+ const QModelIndex proxyIndex = m_proxyModel->mapFromSource(index);
+ m_flatView->selectionModel()->clearSelection();
+ m_flatView->selectionModel()->setCurrentIndex(proxyIndex,
+ QItemSelectionModel::ClearAndSelect |
+ QItemSelectionModel::Rows);
+ m_flatView->scrollTo(proxyIndex);
+
+ m_callersModel->setCalls(func->incomingCalls(), func);
+ m_calleesModel->setCalls(func->outgoingCalls(), func);
+ m_visualisation->setFunction(func);
+
+ const Function *item = m_stackBrowser->current();
+ if (!item || item != func)
+ m_stackBrowser->select(func);
+
+ if (QFile::exists(func->file())) {
+ ///TODO: custom position support?
+ int line = func->lineNumber();
+ TextEditor::BaseTextEditorWidget::openEditorAt(func->file(), qMax(line, 0));
+ }
+}
+
+void CallgrindToolPrivate::stackBrowserChanged()
+{
+ const Function *item = m_stackBrowser->current();
+ m_goBack->setEnabled(item != 0);
+ selectFunction(item);
+}
+
+void CallgrindToolPrivate::updateFilterString()
+{
+ m_proxyModel->setFilterFixedString(m_searchFilter->text());
+}
+
+void CallgrindToolPrivate::setCostFormat(CostDelegate::CostFormat format)
+{
+ switch (format) {
+ case CostDelegate::FormatAbsolute:
+ m_costAbsolute->setChecked(true);
+ break;
+ case CostDelegate::FormatRelative:
+ m_costRelative->setChecked(true);
+ break;
+ case CostDelegate::FormatRelativeToParent:
+ m_costRelativeToParent->setChecked(true);
+ break;
+ }
+}
+
+void CallgrindToolPrivate::setCostEvent(int index)
+{
+ // prevent assert in model, don't try to set event to -1
+ // happens when we clear the eventcombo
+ if (index == -1)
+ index = 0;
+
+ m_dataModel->setCostEvent(index);
+ m_calleesModel->setCostEvent(index);
+ m_callersModel->setCostEvent(index);
+}
+
+void CallgrindToolPrivate::enableCycleDetection(bool enabled)
+{
+ m_cycleDetection->setChecked(enabled);
+}
+
+// Following functions can be called with actions=0 or widgets=0
+// depending on initialization sequence (whether callgrind was current).
+CostDelegate::CostFormat CallgrindToolPrivate::costFormat() const
+{
+ if (m_costRelativeToParent && m_costRelativeToParent->isChecked())
+ return CostDelegate::FormatRelativeToParent;
+ if (m_costRelative && m_costRelative->isChecked())
+ return CostDelegate::FormatRelative;
+ return CostDelegate::FormatAbsolute;
+}
+
+void CallgrindToolPrivate::updateCostFormat()
+{
+ const CostDelegate::CostFormat format = costFormat();
+ if (m_flatView)
+ m_flatView->setCostFormat(format);
+ if (m_calleesView) {
+ m_calleesView->setCostFormat(format);
+ m_callersView->setCostFormat(format);
+ }
+ if (m_settings)
+ m_settings->setCostFormat(format);
+}
+
+void CallgrindToolPrivate::handleFilterProjectCosts()
+{
+ ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance();
+ ProjectExplorer::Project *pro = pe->currentProject();
+ QTC_ASSERT(pro, return)
+
+ if (m_filterProjectCosts->isChecked()) {
+ const QString projectDir = pro->projectDirectory();
+ m_proxyModel->setFilterBaseDir(projectDir);
+ }
+ else {
+ m_proxyModel->setFilterBaseDir(QString());
+ }
+}
+
+void CallgrindToolPrivate::dataFunctionSelected(const QModelIndex &index)
+{
+ const Function *func = index.data(DataModel::FunctionRole).value<const Function *>();
+ QTC_ASSERT(func, return);
+
+ selectFunction(func);
+}
+
+void CallgrindToolPrivate::calleeFunctionSelected(const QModelIndex &index)
+{
+ const FunctionCall *call = index.data(CallModel::FunctionCallRole).value<const FunctionCall *>();
+ QTC_ASSERT(call, return);
+
+ selectFunction(call->callee());
+}
+
+void CallgrindToolPrivate::callerFunctionSelected(const QModelIndex &index)
+{
+ const FunctionCall *call = index.data(CallModel::FunctionCallRole).value<const FunctionCall *>();
+ QTC_ASSERT(call, return);
+
+ selectFunction(call->caller());
+}
+
+void CallgrindToolPrivate::visualisationFunctionSelected(const Function *function)
+{
+ if (function && function == m_visualisation->function())
+ // up-navigation when the initial function was activated
+ m_stackBrowser->goBack();
+ else
+ selectFunction(function);
+}
+
+void CallgrindToolPrivate::setParseData(ParseData *data)
+{
+ // we have new parse data, invalidate filters in the proxy model
+ m_visualisation->setFunction(0);
+
+ // invalidate parse data in the data model
+ delete m_dataModel->parseData();
+
+ if (data && data->events().isEmpty()) {
+ // might happen if the user cancelled the profile run
+ // callgrind then sometimes produces empty callgrind.out.PID files
+ delete data;
+ data = 0;
+ }
+ m_dataModel->setParseData(data);
+ m_calleesModel->setParseData(data);
+ m_callersModel->setParseData(data);
+
+ updateEventCombo();
+
+ // clear history for new data
+ m_stackBrowser->clear();
+
+ // unset busy state
+ setBusy(false);
+}
+
+void CallgrindToolPrivate::updateEventCombo()
+{
+ QTC_ASSERT(m_eventCombo, return)
+
+ m_eventCombo->clear();
+
+ const ParseData *data = m_dataModel->parseData();
+ if (!data || data->events().isEmpty()) {
+ m_eventCombo->hide();
+ return;
+ }
+
+ m_eventCombo->show();
+ foreach (const QString &event, data->events())
+ m_eventCombo->addItem(ParseData::prettyStringForEvent(event));
+}
+
+static QToolButton *createToolButton(QAction *action)
+{
+ QToolButton *button = new QToolButton;
+ button->setDefaultAction(action);
+ button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+ return button;
+}
+
+CallgrindTool::CallgrindTool(QObject *parent)
+ : Analyzer::IAnalyzerTool(parent)
+{
+ d = new CallgrindToolPrivate(this);
+ Core::ICore *core = Core::ICore::instance();
+
+ // EditorManager
+ QObject *editorManager = core->editorManager();
+ connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
+ d, SLOT(editorOpened(Core::IEditor*)));
+}
+
+CallgrindTool::~CallgrindTool()
+{
+ delete d;
+}
+
+QString CallgrindTool::id() const
+{
+ return "Callgrind";
+}
+
+QString CallgrindTool::displayName() const
+{
+ return tr("Profile");
+}
+
+IAnalyzerTool::ToolMode CallgrindTool::mode() const
+{
+ return ReleaseMode;
+}
+
+void CallgrindTool::initialize()
+{
+}
+
+void CallgrindTool::extensionsInitialized()
+{
+ Core::ICore *core = Core::ICore::instance();
+ Core::ActionManager *actionManager = core->actionManager();
+
+ Core::Context analyzerContext = Core::Context(Analyzer::Constants::C_ANALYZEMODE);
+
+ // check if there is a CppEditor context menu, if true, add our own context menu actions
+ if (Core::ActionContainer *editorContextMenu =
+ actionManager->actionContainer(CppEditor::Constants::M_CONTEXT)) {
+ QAction *action = 0;
+ Core::Command *cmd = 0;
+
+ action = new QAction(this);
+ action->setSeparator(true);
+ cmd = actionManager->registerAction(action, "Analyzer.Callgrind.ContextMenu.Sep",
+ analyzerContext);
+ editorContextMenu->addAction(cmd);
+
+ action = new QAction(tr("Profile Costs of this Function and its Callees"), this);
+ action->setIcon(QIcon(Analyzer::Constants::ANALYZER_CONTROL_START_ICON));
+ connect(action, SIGNAL(triggered()), d, SLOT(handleShowCostsOfFunction()));
+ cmd = actionManager->registerAction(action, "Analyzer.Callgrind.ShowCostsOfFunction",
+ analyzerContext);
+ editorContextMenu->addAction(cmd);
+ cmd->setAttribute(Core::Command::CA_Hide);
+ cmd->setAttribute(Core::Command::CA_NonConfigurable);
+ d->m_showCostsOfFunctionAction = action;
+ }
+}
+
+void CallgrindTool::initializeDockWidgets()
+{
+ d->initializeDockWidgets();
+}
+
+void CallgrindToolPrivate::initializeDockWidgets()
+{
+ AnalyzerManager *am = AnalyzerManager::instance();
+ Utils::FancyMainWindow *mw = am->mainWindow();
+ m_visualisation = new Visualisation(mw);
+ m_visualisation->setFrameStyle(QFrame::NoFrame);
+ m_visualisation->setObjectName("Valgrind.CallgrindToolPrivate.Visualisation");
+ m_visualisation->setModel(m_dataModel);
+ connect(m_visualisation, SIGNAL(functionActivated(const Valgrind::Callgrind::Function*)),
+ this, SLOT(visualisationFunctionSelected(const Valgrind::Callgrind::Function*)));
+
+ m_callersView = new CostView(mw);
+ m_callersView->sortByColumn(CallModel::CostColumn);
+ m_callersView->setObjectName("Valgrind.CallgrindToolPrivate.CallersView");
+ m_callersView->setFrameStyle(QFrame::NoFrame);
+ // enable sorting
+ QSortFilterProxyModel *callerProxy = new QSortFilterProxyModel(m_callersModel);
+ callerProxy->setSourceModel(m_callersModel);
+ m_callersView->setModel(callerProxy);
+ m_callersView->hideColumn(CallModel::CalleeColumn);
+ connect(m_callersView, SIGNAL(activated(QModelIndex)),
+ this, SLOT(callerFunctionSelected(QModelIndex)));
+
+ m_calleesView = new CostView(mw);
+ m_calleesView->sortByColumn(CallModel::CostColumn);
+ m_calleesView->setObjectName("Valgrind.CallgrindToolPrivate.CalleesView");
+ m_calleesView->setFrameStyle(QFrame::NoFrame);
+ // enable sorting
+ QSortFilterProxyModel *calleeProxy = new QSortFilterProxyModel(m_calleesModel);
+ calleeProxy->setSourceModel(m_calleesModel);
+ m_calleesView->setModel(calleeProxy);
+ m_calleesView->hideColumn(CallModel::CallerColumn);
+ connect(m_calleesView, SIGNAL(activated(QModelIndex)),
+ this, SLOT(calleeFunctionSelected(QModelIndex)));
+
+ m_flatView = new CostView(mw);
+ m_flatView->sortByColumn(DataModel::SelfCostColumn);
+ m_flatView->setFrameStyle(QFrame::NoFrame);
+ m_flatView->setAttribute(Qt::WA_MacShowFocusRect, false);
+ m_flatView->setModel(m_proxyModel);
+ m_flatView->setObjectName("Valgrind.CallgrindToolPrivate.FlatView");
+ connect(m_flatView, SIGNAL(activated(QModelIndex)),
+ this, SLOT(dataFunctionSelected(QModelIndex)));
+
+ updateCostFormat();
+
+ QDockWidget *callersDock =
+ am->createDockWidget(q, tr("Callers"), m_callersView,
+ Qt::BottomDockWidgetArea);
+
+ QDockWidget *flatDock =
+ am->createDockWidget(q, tr("Functions"), m_flatView,
+ Qt::BottomDockWidgetArea);
+
+ QDockWidget *calleesDock =
+ am->createDockWidget(q, tr("Callees"), m_calleesView,
+ Qt::BottomDockWidgetArea);
+
+ QDockWidget *visualizationDock =
+ am->createDockWidget(q, tr("Visualization"), m_visualisation,
+ Qt::RightDockWidgetArea);
+ visualizationDock->hide();
+
+ mw->splitDockWidget(mw->toolBarDockWidget(), calleesDock, Qt::Vertical);
+ mw->splitDockWidget(mw->toolBarDockWidget(), callersDock, Qt::Vertical);
+ mw->splitDockWidget(mw->toolBarDockWidget(), flatDock, Qt::Vertical);
+ mw->tabifyDockWidget(callersDock, calleesDock);
+}
+
+IAnalyzerEngine *CallgrindTool::createEngine(const AnalyzerStartParameters &sp,
+ ProjectExplorer::RunConfiguration *runConfiguration)
+{
+ return d->createEngine(sp, runConfiguration);
+}
+
+IAnalyzerEngine *CallgrindToolPrivate::createEngine(const AnalyzerStartParameters &sp,
+ ProjectExplorer::RunConfiguration *runConfiguration)
+{
+ CallgrindEngine *engine = new CallgrindEngine(sp, runConfiguration);
+
+ connect(engine, SIGNAL(parserDataReady(CallgrindEngine *)),
+ SLOT(takeParserData(CallgrindEngine *)));
+ connect(engine, SIGNAL(starting(const Analyzer::IAnalyzerEngine*)),
+ SLOT(engineStarting(const Analyzer::IAnalyzerEngine*)));
+ connect(engine, SIGNAL(finished()),
+ SLOT(engineFinished()));
+
+ connect(this, SIGNAL(dumpRequested()), engine, SLOT(dump()));
+ connect(this, SIGNAL(resetRequested()), engine, SLOT(reset()));
+ connect(this, SIGNAL(pauseToggled(bool)), engine, SLOT(setPaused(bool)));
+
+ // initialize engine
+ engine->setPaused(m_pauseAction->isChecked());
+
+ // we may want to toggle collect for one function only in this run
+ engine->setToggleCollectFunction(m_toggleCollectFunction);
+ m_toggleCollectFunction.clear();
+
+ AnalyzerManager::instance()->showStatusMessage(AnalyzerManager::msgToolStarted(q->displayName()));
+
+ // apply project settings
+ AnalyzerProjectSettings *analyzerSettings = runConfiguration->extraAspect<AnalyzerProjectSettings>();
+ CallgrindProjectSettings *settings = analyzerSettings->subConfig<CallgrindProjectSettings>();
+ QTC_ASSERT(settings, return engine)
+
+ QTC_ASSERT(m_visualisation, return engine);
+ m_visualisation->setMinimumInclusiveCostRatio(settings->visualisationMinimumInclusiveCostRatio() / 100.0);
+ m_proxyModel->setMinimumInclusiveCostRatio(settings->minimumInclusiveCostRatio() / 100.0);
+ m_dataModel->setVerboseToolTipsEnabled(settings->enableEventToolTips());
+
+ return engine;
+}
+
+QWidget *CallgrindTool::createControlWidget()
+{
+ return d->createControlWidget();
+}
+
+QWidget *CallgrindToolPrivate::createControlWidget()
+{
+ QAction *action = 0;
+ QWidget *widget = new QWidget;
+ QHBoxLayout *layout = new QHBoxLayout;
+
+ layout->setMargin(0);
+ layout->setSpacing(0);
+ widget->setLayout(layout);
+
+ // dump action
+ action = new QAction(this);
+ action->setDisabled(true);
+ action->setIcon(QIcon(QLatin1String(Core::Constants::ICON_REDO)));
+ action->setText(tr("Dump"));
+ action->setToolTip(tr("Request the dumping of profile information. This will update the callgrind visualization."));
+ connect(action, SIGNAL(triggered()), this, SLOT(slotRequestDump()));
+ layout->addWidget(createToolButton(action));
+ m_dumpAction = action;
+
+ // reset action
+ action = new QAction(this);
+ action->setDisabled(true);
+ action->setIcon(QIcon(QLatin1String(Core::Constants::ICON_CLEAR)));
+ action->setText(tr("Reset"));
+ action->setToolTip(tr("Zero all event counters."));
+ connect(action, SIGNAL(triggered()), this, SIGNAL(resetRequested()));
+ layout->addWidget(createToolButton(action));
+ m_resetAction = action;
+
+ // pause action
+ action = new QAction(this);
+ action->setCheckable(true);
+ action->setIcon(QIcon(QLatin1String(":/qml/images/pause-small.png")));
+ action->setText(tr("Ignore"));
+ action->setToolTip(tr("If enabled, no events are counted which will speed up program execution during profiling."));
+ connect(action, SIGNAL(toggled(bool)), this, SIGNAL(pauseToggled(bool)));
+ layout->addWidget(createToolButton(action));
+ m_pauseAction = action;
+
+ // navigation
+ // go back
+ action = new QAction(this);
+ action->setDisabled(true);
+ action->setIcon(QIcon::fromTheme("go-previous"));
+ action->setText(tr("Back"));
+ action->setToolTip(tr("Go back one step in history. This will select the previously selected item."));
+ connect(action, SIGNAL(triggered(bool)), m_stackBrowser, SLOT(goBack()));
+ layout->addWidget(createToolButton(action));
+ m_goBack = action;
+
+ // overview
+ action = new QAction(this);
+ action->setDisabled(true);
+ action->setIcon(QIcon::fromTheme("go-up"));
+ action->setText(tr("All Functions"));
+ action->setToolTip(tr("Show the overview of all function calls."));
+ connect(action, SIGNAL(triggered(bool)), this, SLOT(slotGoToOverview()));
+ layout->addWidget(createToolButton(action));
+ m_goToOverview = action;
+
+ layout->addWidget(new Utils::StyledSeparator);
+
+ // event selection
+ m_eventCombo = new QComboBox;
+ m_eventCombo->setToolTip(tr("Selects which events from the profiling data are shown and visualized."));
+ connect(m_eventCombo, SIGNAL(currentIndexChanged(int)),
+ this, SLOT(setCostEvent(int)));
+ updateEventCombo();
+ layout->addWidget(m_eventCombo);
+
+ // cost formatting
+ {
+ QMenu *menu = new QMenu(layout->parentWidget());
+ QActionGroup *group = new QActionGroup(this);
+
+ // show costs as absolute numbers
+ m_costAbsolute = new QAction(tr("Absolute Costs"), this);
+ ///FIXME: icon
+ m_costAbsolute->setToolTip(tr("Show costs as absolute numbers."));
+ m_costAbsolute->setCheckable(true);
+ m_costAbsolute->setChecked(true);
+ connect(m_costAbsolute, SIGNAL(toggled(bool)), SLOT(updateCostFormat()));
+ group->addAction(m_costAbsolute);
+ menu->addAction(m_costAbsolute);
+
+ // show costs in percentages
+ m_costRelative = new QAction(tr("Relative Costs"), this);
+ ///FIXME: icon (percentage sign?)
+ m_costRelative->setToolTip(tr("Show costs relative to total inclusive cost."));
+ m_costRelative->setCheckable(true);
+ connect(m_costRelative, SIGNAL(toggled(bool)), SLOT(updateCostFormat()));
+ group->addAction(m_costRelative);
+ menu->addAction(m_costRelative);
+
+ // show costs relative to parent
+ m_costRelativeToParent = new QAction(tr("Relative Costs to Parent"), this);
+ ///FIXME: icon
+ m_costRelativeToParent->setToolTip(tr("Show costs relative to parent functions inclusive cost."));
+ m_costRelativeToParent->setCheckable(true);
+ connect(m_costRelativeToParent, SIGNAL(toggled(bool)), SLOT(updateCostFormat()));
+ group->addAction(m_costRelativeToParent);
+ menu->addAction(m_costRelativeToParent);
+
+ QToolButton *button = new QToolButton;
+ button->setMenu(menu);
+ button->setPopupMode(QToolButton::InstantPopup);
+ button->setText(tr("Cost Format"));
+ layout->addWidget(button);
+ }
+
+ // cycle detection
+ action = new QAction(tr("Cycle Detection"), this); ///FIXME: icon
+ action->setToolTip(tr("Enable cycle detection to properly handle recursive or circular function calls."));
+ action->setCheckable(true);
+ connect(action, SIGNAL(toggled(bool)), m_dataModel, SLOT(enableCycleDetection(bool)));
+ connect(action, SIGNAL(toggled(bool)), m_settings, SLOT(setDetectCycles(bool)));
+ layout->addWidget(createToolButton(action));
+ m_cycleDetection = action;
+
+ // filtering
+ action = new QAction(tr("Show Project Costs Only"), this);
+ action->setIcon(QIcon(Core::Constants::ICON_FILTER));
+ action->setToolTip(tr("Show only profiling info that originated from this project source."));
+ action->setCheckable(true);
+ connect(action, SIGNAL(toggled(bool)), this, SLOT(handleFilterProjectCosts()));
+ layout->addWidget(createToolButton(action));
+ m_filterProjectCosts = action;
+
+ // filter
+ ///FIXME: find workaround for http://bugreports.qt.nokia.com/browse/QTCREATORBUG-3247
+ QLineEdit *filter = new QLineEdit;
+ filter->setPlaceholderText(tr("Filter..."));
+ connect(filter, SIGNAL(textChanged(QString)), m_updateTimer, SLOT(start()));
+ layout->addWidget(filter);
+ m_searchFilter = filter;
+
+ setCostFormat(m_settings->costFormat());
+ enableCycleDetection(m_settings->detectCycles());
+
+ layout->addWidget(new Utils::StyledSeparator);
+ layout->addStretch();
+
+ return widget;
+}
+
+void CallgrindToolPrivate::clearTextMarks()
+{
+ qDeleteAll(m_textMarks);
+ m_textMarks.clear();
+}
+
+void CallgrindToolPrivate::engineStarting(const Analyzer::IAnalyzerEngine *)
+{
+ // enable/disable actions
+ m_resetAction->setEnabled(true);
+ m_dumpAction->setEnabled(true);
+ clearTextMarks();
+ slotClear();
+}
+
+void CallgrindToolPrivate::engineFinished()
+{
+ // enable/disable actions
+ m_resetAction->setEnabled(false);
+ m_dumpAction->setEnabled(false);
+
+ const ParseData *data = m_dataModel->parseData();
+ if (data)
+ showParserResults(data);
+ else
+ AnalyzerManager::instance()->showStatusMessage(tr("Profiling aborted."));
+}
+
+void CallgrindToolPrivate::showParserResults(const ParseData *data)
+{
+ QString msg;
+ if (data) {
+ // be careful, the list of events might be empty
+ if (data->events().isEmpty()) {
+ msg = tr("Parsing finished, no data.");
+ } else {
+ const QString costStr = QString("%1 %2").arg(QString::number(data->totalCost(0)), data->events().first());
+ msg = tr("Parsing finished, total cost of %1 reported.").arg(costStr);
+ }
+ } else {
+ msg = tr("Parsing failed.");
+ }
+ AnalyzerManager::instance()->showStatusMessage(msg);
+}
+
+void CallgrindToolPrivate::editorOpened(Core::IEditor *editor)
+{
+ TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
+ if (!textEditor)
+ return;
+
+ connect(textEditor,
+ SIGNAL(markContextMenuRequested(TextEditor::ITextEditor*,int,QMenu*)),
+ SLOT(requestContextMenu(TextEditor::ITextEditor*,int,QMenu*)));
+}
+
+void CallgrindToolPrivate::requestContextMenu(TextEditor::ITextEditor *editor, int line, QMenu *menu)
+{
+ // find callgrind text mark that corresponds to this editor's file and line number
+ const Function *func = 0;
+ foreach (CallgrindTextMark *textMark, m_textMarks) {
+ if (textMark->fileName() == editor->file()->fileName() && textMark->lineNumber() == line) {
+ func = textMark->function();
+ break;
+ }
+ }
+ if (!func)
+ return; // no callgrind text mark under cursor, return
+
+ // add our action to the context menu
+ QAction *action = new QAction(tr("Select this Function in the Analyzer Output"), menu);
+ connect(action, SIGNAL(triggered()), SLOT(handleShowCostsAction()));
+ action->setData(QVariant::fromValue<const Function *>(func));
+ menu->addAction(action);
+}
+
+void CallgrindToolPrivate::handleShowCostsAction()
+{
+ const QAction *action = qobject_cast<QAction *>(sender());
+ QTC_ASSERT(action, return)
+
+ const Function *func = action->data().value<const Function *>();
+ QTC_ASSERT(func, return)
+
+ selectFunction(func);
+}
+
+void CallgrindToolPrivate::handleShowCostsOfFunction()
+{
+ CPlusPlus::Symbol *symbol = AnalyzerUtils::findSymbolUnderCursor();
+ if (!symbol)
+ return;
+
+ if (!symbol->isFunction())
+ return;
+
+ CPlusPlus::Overview view;
+ const QString qualifiedFunctionName = view.prettyName(CPlusPlus::LookupContext::fullyQualifiedName(symbol));
+
+ m_toggleCollectFunction = QString("%1()").arg(qualifiedFunctionName);
+
+ AnalyzerManager::instance()->selectTool(q);
+ AnalyzerManager::instance()->startTool();
+}
+
+void CallgrindToolPrivate::slotRequestDump()
+{
+ setBusy(true);
+ m_visualisation->setText(tr("Populating..."));
+ dumpRequested();
+}
+
+void CallgrindToolPrivate::takeParserData(CallgrindEngine *engine)
+{
+ ParseData *data = engine->takeParserData();
+ showParserResults(data);
+
+ if (!data)
+ return;
+
+ // clear first
+ clearTextMarks();
+ slotClear();
+
+ setParseData(data);
+ createTextMarks();
+}
+
+void CallgrindToolPrivate::createTextMarks()
+{
+ DataModel *model = m_dataModel;
+ QTC_ASSERT(model, return)
+
+ QList<QString> locations;
+ for (int row = 0; row < model->rowCount(); ++row) {
+ const QModelIndex index = model->index(row, DataModel::InclusiveCostColumn);
+
+ QString fileName = index.data(DataModel::FileNameRole).toString();
+ if (fileName.isEmpty() || fileName == "???")
+ continue;
+
+ bool ok = false;
+ const int lineNumber = index.data(DataModel::LineNumberRole).toInt(&ok);
+ QTC_ASSERT(ok, continue);
+
+ // sanitize filename, text marks need a canonical (i.e. no ".."s) path
+ // BaseTextMark::editorOpened(Core::IEditor *editor) compares file names on string basis
+ QFileInfo info(fileName);
+ fileName = info.canonicalFilePath();
+ if (fileName.isEmpty())
+ continue; // isEmpty == true => file does not exist, continue then
+
+ // create only one text mark per location
+ const QString location = QString("%1:%2").arg(fileName, QString::number(lineNumber));
+ if (locations.contains(location))
+ continue;
+ locations << location;
+
+ m_textMarks.append(new CallgrindTextMark(index, fileName, lineNumber));
+ }
+}
+
+} // namespace Internal
+} // namespace Valgrind
+
+#include "callgrindtool.moc"