diff options
Diffstat (limited to 'src/plugins/valgrind')
52 files changed, 7453 insertions, 0 deletions
diff --git a/src/plugins/valgrind/Valgrind.pluginspec.in b/src/plugins/valgrind/Valgrind.pluginspec.in new file mode 100644 index 0000000000..31a1de78e7 --- /dev/null +++ b/src/plugins/valgrind/Valgrind.pluginspec.in @@ -0,0 +1,20 @@ +<plugin name=\"Valgrind\" version=\"$$QTCREATOR_VERSION\" compatVersion=\"$$QTCREATOR_VERSION\"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2011 Nokia Corporation</copyright> + <license> +Commercial Usage + +Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Nokia. + +GNU Lesser General Public License Usage + +Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. 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. + </license> + <category>Code Analyzer</category> + <description>Valgrind Plugin</description> + <url>http://qt.nokia.com</url> + <dependencyList> + <dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/> + <dependency name=\"AnalyzerBase\" version=\"$$QTCREATOR_VERSION\"/> + </dependencyList> +</plugin> diff --git a/src/plugins/valgrind/callgrindconfigwidget.cpp b/src/plugins/valgrind/callgrindconfigwidget.cpp new file mode 100644 index 0000000000..280fd4353d --- /dev/null +++ b/src/plugins/valgrind/callgrindconfigwidget.cpp @@ -0,0 +1,94 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "callgrindconfigwidget.h" + +#include "ui_callgrindconfigwidget.h" + +#include "callgrindsettings.h" + +using namespace Valgrind::Internal; + +CallgrindConfigWidget::CallgrindConfigWidget(AbstractCallgrindSettings *settings, QWidget *parent) + : QWidget(parent) + , m_ui(new Ui::CallgrindConfigWidget) + , m_settings(settings) +{ + m_ui->setupUi(this); + + m_ui->enableCacheSim->setChecked(m_settings->enableCacheSim()); + connect(m_ui->enableCacheSim, SIGNAL(toggled(bool)), + m_settings, SLOT(setEnableCacheSim(bool))); + connect(m_settings, SIGNAL(enableCacheSimChanged(bool)), + m_ui->enableCacheSim, SLOT(setChecked(bool))); + + m_ui->enableBranchSim->setChecked(m_settings->enableBranchSim()); + connect(m_ui->enableBranchSim, SIGNAL(toggled(bool)), + m_settings, SLOT(setEnableBranchSim(bool))); + connect(m_settings, SIGNAL(enableBranchSimChanged(bool)), + m_ui->enableBranchSim, SLOT(setChecked(bool))); + + m_ui->collectSystime->setChecked(m_settings->collectSystime()); + connect(m_ui->collectSystime, SIGNAL(toggled(bool)), + m_settings, SLOT(setCollectSystime(bool))); + connect(m_settings, SIGNAL(collectSystimeChanged(bool)), + m_ui->collectSystime, SLOT(setChecked(bool))); + + m_ui->collectBusEvents->setChecked(m_settings->collectBusEvents()); + connect(m_ui->collectBusEvents, SIGNAL(toggled(bool)), + m_settings, SLOT(setCollectBusEvents(bool))); + connect(m_settings, SIGNAL(collectBusEventsChanged(bool)), + m_ui->collectBusEvents, SLOT(setChecked(bool))); + + m_ui->enableEventToolTips->setChecked(m_settings->enableEventToolTips()); + connect(m_ui->enableEventToolTips, SIGNAL(toggled(bool)), + m_settings, SLOT(setEnableEventToolTips(bool))); + connect(m_settings, SIGNAL(enableEventToolTipsChanged(bool)), + m_ui->enableEventToolTips, SLOT(setChecked(bool))); + + m_ui->minimumInclusiveCostRatio->setValue(m_settings->minimumInclusiveCostRatio()); + connect(m_ui->minimumInclusiveCostRatio, SIGNAL(valueChanged(double)), + m_settings, SLOT(setMinimumInclusiveCostRatio(double))); + connect(m_settings, SIGNAL(minimumInclusiveCostRatioChanged(double)), + m_ui->minimumInclusiveCostRatio, SLOT(setValue(double))); + + m_ui->visualisationMinimumInclusiveCostRatio->setValue(m_settings->visualisationMinimumInclusiveCostRatio()); + connect(m_ui->visualisationMinimumInclusiveCostRatio, SIGNAL(valueChanged(double)), + m_settings, SLOT(setVisualisationMinimumInclusiveCostRatio(double))); + connect(m_settings, SIGNAL(visualisationMinimumInclusiveCostRatioChanged(double)), + m_ui->visualisationMinimumInclusiveCostRatio, SLOT(setValue(double))); +} + +CallgrindConfigWidget::~CallgrindConfigWidget() +{ + delete m_ui; +} diff --git a/src/plugins/valgrind/callgrindconfigwidget.h b/src/plugins/valgrind/callgrindconfigwidget.h new file mode 100644 index 0000000000..b76495f7f2 --- /dev/null +++ b/src/plugins/valgrind/callgrindconfigwidget.h @@ -0,0 +1,61 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef ANALYZER_INTERNAL_CALLGRINDCONFIGWIDGET_H +#define ANALYZER_INTERNAL_CALLGRINDCONFIGWIDGET_H + +#include <QtGui/QWidget> + +namespace Valgrind { +namespace Internal { + +namespace Ui { +class CallgrindConfigWidget; +} + +class AbstractCallgrindSettings; + +class CallgrindConfigWidget : public QWidget +{ +public: + CallgrindConfigWidget(AbstractCallgrindSettings *settings, QWidget *parent); + virtual ~CallgrindConfigWidget(); + +private: + Ui::CallgrindConfigWidget *m_ui; + AbstractCallgrindSettings *m_settings; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // ANALYZER_INTERNAL_CALLGRINDCONFIGWIDGET_H diff --git a/src/plugins/valgrind/callgrindconfigwidget.ui b/src/plugins/valgrind/callgrindconfigwidget.ui new file mode 100644 index 0000000000..1e4c42614a --- /dev/null +++ b/src/plugins/valgrind/callgrindconfigwidget.ui @@ -0,0 +1,174 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Valgrind::Internal::CallgrindConfigWidget</class> + <widget class="QWidget" name="Valgrind::Internal::CallgrindConfigWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>416</width> + <height>565</height> + </rect> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0" colspan="2"> + <widget class="QGroupBox" name="memcheckOptions"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Profiling Options</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="minimumInclusiveCostRatioLabel"> + <property name="toolTip"> + <string>This option limits the amount of results the profiler will give you. A lower limit will likely increase performance.</string> + </property> + <property name="text"> + <string>Result view: Show events with inclusive costs higher than:</string> + </property> + <property name="buddy"> + <cstring>minimumInclusiveCostRatio</cstring> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QDoubleSpinBox" name="minimumInclusiveCostRatio"> + <property name="suffix"> + <string>%</string> + </property> + <property name="decimals"> + <number>2</number> + </property> + <property name="maximum"> + <double>10.000000000000000</double> + </property> + <property name="singleStep"> + <double>0.100000000000000</double> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QGroupBox" name="enableEventToolTips"> + <property name="title"> + <string>Show additional information for events in tooltips</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QCheckBox" name="enableCacheSim"> + <property name="toolTip"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> +<p>Specify if you want to do full cache simulation.</p> +<p>By default, only instruction read accesses will be counted ("Ir").</p> + +<p> +With cache simulation, further event counters are enabled: +<ul><li>Cache misses on instruction reads ("I1mr"/"I2mr")</li> +<li>Data read accesses ("Dr") and related cache misses ("D1mr"/"D2mr")</li> +<li>Data write accesses ("Dw") and related cache misses ("D1mw"/"D2mw")</li></ul> +</p> + +For more information, see ???.</p></body></html></string> + </property> + <property name="text"> + <string>Enable cache simulation</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="enableBranchSim"> + <property name="toolTip"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p>Specify if you want to do branch prediction simulation.</p> +<p>Further event counters are enabled: </p> +<ul><li>Number of executed conditional branches and related predictor misses ( +"Bc"/"Bcm")</li> +<li>Executed indirect jumps and related misses of the jump address predictor ( +"Bi"/"Bim")</li></ul></body></html></string> + </property> + <property name="text"> + <string>Enable branch prediction simulation</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="collectSystime"> + <property name="toolTip"> + <string>This specifies whether information for system call times should be collected.</string> + </property> + <property name="text"> + <string>Collect system call time</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QCheckBox" name="collectBusEvents"> + <property name="toolTip"> + <string>This specifies whether the number of global bus events executed should be collected. The event type "Ge" is used for these events.</string> + </property> + <property name="text"> + <string>Collect global bus events:</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="visualisationMinimumInclusiveCostRatioLabel"> + <property name="text"> + <string>Visualisation: Show events with inclusive costs higher than:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QDoubleSpinBox" name="visualisationMinimumInclusiveCostRatio"> + <property name="prefix"> + <string/> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="minimum"> + <double>0.000000000000000</double> + </property> + <property name="maximum"> + <double>50.000000000000000</double> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/valgrind/callgrindcostdelegate.cpp b/src/plugins/valgrind/callgrindcostdelegate.cpp new file mode 100644 index 0000000000..b99da1ebc1 --- /dev/null +++ b/src/plugins/valgrind/callgrindcostdelegate.cpp @@ -0,0 +1,181 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "callgrindcostdelegate.h" + +#include "callgrindcostview.h" +#include "callgrindhelper.h" + +#include <valgrind/callgrind/callgrindabstractmodel.h> +#include <valgrind/callgrind/callgrindparsedata.h> + +#include <utils/qtcassert.h> + +#include <QtGui/QApplication> +#include <QtGui/QPainter> + +using namespace Valgrind::Callgrind; + +namespace Valgrind { +namespace Internal { + +class CostDelegate::Private +{ +public: + Private(); + + QAbstractItemModel *m_model; + CostDelegate::CostFormat m_format; + + static int toNativeRole(CostFormat format); + float relativeCost(const QModelIndex &index) const; + QString displayText(const QModelIndex &index, const QLocale &locale) const; +}; + +CostDelegate::Private::Private() + : m_model(0) + , m_format(CostDelegate::FormatAbsolute) +{} + +int CostDelegate::Private::toNativeRole(CostDelegate::CostFormat format) +{ + switch (format) + { + case FormatAbsolute: + case FormatRelative: + return Valgrind::Callgrind::RelativeTotalCostRole; + case FormatRelativeToParent: + return Valgrind::Callgrind::RelativeParentCostRole; + default: + return -1; + } +} + +float CostDelegate::Private::relativeCost(const QModelIndex &index) const +{ + bool ok = false; + float cost = index.data(toNativeRole(m_format)).toFloat(&ok); + QTC_ASSERT(ok, return 0); + return cost; +} + +QString CostDelegate::Private::displayText(const QModelIndex &index, const QLocale &locale) const +{ + switch (m_format) { + case FormatAbsolute: + return locale.toString(index.data().toULongLong()); + case FormatRelative: + case FormatRelativeToParent: + if (!m_model) + break; + float cost = relativeCost(index) * 100.0f; + return CallgrindHelper::toPercent(cost, locale); + } + + return QString(); +} +//END CostDelegate::Private + +//BEGIN CostDelegate +CostDelegate::CostDelegate(QObject *parent) + : QStyledItemDelegate(parent) + , d(new Private) +{ + +} + +CostDelegate::~CostDelegate() +{ + delete d; +} + +void CostDelegate::setModel(QAbstractItemModel *model) +{ + d->m_model = model; +} + +void CostDelegate::setFormat(CostFormat format) +{ + d->m_format = format; +} + +CostDelegate::CostFormat CostDelegate::format() const +{ + return d->m_format; +} + +void CostDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + // init + QStyleOptionViewItemV4 opt(option); + initStyleOption(&opt, index); + + QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); + + // draw controls, but no text + opt.text.clear(); + style->drawControl(QStyle::CE_ItemViewItem, &opt, painter); + + painter->save(); + + // draw bar + float ratio = qBound(0.0f, d->relativeCost(index), 1.0f); + QRect barRect = opt.rect; + barRect.setWidth(opt.rect.width() * ratio); + painter->setPen(Qt::NoPen); + painter->setBrush(CallgrindHelper::colorForCostRatio(ratio)); + painter->drawRect(barRect); + + // draw text + const QString text = d->displayText(index, opt.locale); + const QBrush &textBrush = (option.state & QStyle::State_Selected ? opt.palette.highlightedText() : opt.palette.text()); + painter->setBrush(Qt::NoBrush); + painter->setPen(textBrush.color()); + painter->drawText(opt.rect, Qt::AlignRight, text); + + painter->restore(); +} + +QSize CostDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV4 opt(option); + initStyleOption(&opt, index); + + const QString text = d->displayText(index, opt.locale); + const QSize size = QSize(option.fontMetrics.width(text), + option.fontMetrics.height()); + return size; +} + +} // namespace Internal +} // namespace Valgrind diff --git a/src/plugins/valgrind/callgrindcostdelegate.h b/src/plugins/valgrind/callgrindcostdelegate.h new file mode 100644 index 0000000000..a7c358e67e --- /dev/null +++ b/src/plugins/valgrind/callgrindcostdelegate.h @@ -0,0 +1,75 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef CALLGRINDCOSTDELEGATE_H +#define CALLGRINDCOSTDELEGATE_H + +#include <QtGui/QStyledItemDelegate> + +namespace Valgrind { +namespace Internal { + +class CostDelegate : public QStyledItemDelegate +{ +public: + explicit CostDelegate(QObject *parent = 0); + virtual ~CostDelegate(); + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + + void setModel(QAbstractItemModel *model); + + enum CostFormat { + /// show absolute numbers + FormatAbsolute, + /// show percentages relative to the total inclusive cost + FormatRelative, + /// show percentages relative to the parent cost + FormatRelativeToParent + }; + + void setFormat(CostFormat format); + CostFormat format() const; + +private: + class Private; + Private *d; +}; + +} // namespace Internal +} // namespace Valgrind + +Q_DECLARE_METATYPE(Valgrind::Internal::CostDelegate::CostFormat) + +#endif // CALLGRINDCOSTDELEGATE_H diff --git a/src/plugins/valgrind/callgrindcostview.cpp b/src/plugins/valgrind/callgrindcostview.cpp new file mode 100644 index 0000000000..fb32e37082 --- /dev/null +++ b/src/plugins/valgrind/callgrindcostview.cpp @@ -0,0 +1,134 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "callgrindcostview.h" + +#include "callgrindnamedelegate.h" + +#include <valgrind/callgrind/callgrindabstractmodel.h> +#include <valgrind/callgrind/callgrinddatamodel.h> +#include <valgrind/callgrind/callgrindfunction.h> +#include <valgrind/callgrind/callgrindcallmodel.h> + +#include <QtGui/QAbstractProxyModel> +#include <QtGui/QHeaderView> +#include <QtCore/QDebug> + +using namespace Valgrind::Callgrind; + +namespace Valgrind { +namespace Internal { + +class CostView::Private +{ +public: + explicit Private(CostView *qq); + + CostDelegate *m_costDelegate; + NameDelegate *m_nameDelegate; +}; + +CostView::Private::Private(CostView *qq) + : m_costDelegate(new CostDelegate(qq)) + , m_nameDelegate(new NameDelegate(qq)) +{} + + +CostView::CostView(QWidget *parent) + : QTreeView(parent) + , d(new Private(this)) +{ + setSelectionMode(QAbstractItemView::ExtendedSelection); + setSelectionBehavior(QAbstractItemView::SelectRows); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + setAutoScroll(false); + setSortingEnabled(true); + setRootIsDecorated(false); +} + +CostView::~CostView() +{ + delete d; +} + +void CostView::setModel(QAbstractItemModel *model) +{ + QTreeView::setModel(model); + + forever { + QAbstractProxyModel *proxy = qobject_cast<QAbstractProxyModel *>(model); + if (proxy) + model = proxy->sourceModel(); + else + break; + } + + QHeaderView *headerView = header(); + setItemDelegate(new QStyledItemDelegate(this)); + headerView->setResizeMode(QHeaderView::Interactive); + headerView->setStretchLastSection(false); + + if (qobject_cast<CallModel *>(model)) { + headerView->setResizeMode(CallModel::CalleeColumn, QHeaderView::Stretch); + headerView->setResizeMode(CallModel::CallerColumn, QHeaderView::Stretch); + headerView->setResizeMode(CallModel::CallsColumn, QHeaderView::ResizeToContents); + headerView->setResizeMode(CallModel::CostColumn, QHeaderView::ResizeToContents); + setItemDelegateForColumn(CallModel::CalleeColumn, d->m_nameDelegate); + setItemDelegateForColumn(CallModel::CallerColumn, d->m_nameDelegate); + setItemDelegateForColumn(CallModel::CostColumn, d->m_costDelegate); + } else if (qobject_cast<DataModel *>(model)) { + headerView->setResizeMode(DataModel::InclusiveCostColumn, QHeaderView::ResizeToContents); + headerView->setResizeMode(DataModel::LocationColumn, QHeaderView::Stretch); + headerView->setResizeMode(DataModel::NameColumn, QHeaderView::Stretch); + headerView->setResizeMode(DataModel::SelfCostColumn, QHeaderView::ResizeToContents); + setItemDelegateForColumn(DataModel::InclusiveCostColumn, d->m_costDelegate); + setItemDelegateForColumn(DataModel::NameColumn, d->m_nameDelegate); + setItemDelegateForColumn(DataModel::SelfCostColumn, d->m_costDelegate); + } + + d->m_costDelegate->setModel(model); +} + +void CostView::setCostFormat(CostDelegate::CostFormat format) +{ + d->m_costDelegate->setFormat(format); + viewport()->update(); +} + +CostDelegate::CostFormat CostView::costFormat() const +{ + return d->m_costDelegate->format(); +} + +} // namespace Internal +} // namespace Valgrind diff --git a/src/plugins/valgrind/callgrindcostview.h b/src/plugins/valgrind/callgrindcostview.h new file mode 100644 index 0000000000..3f41d2df74 --- /dev/null +++ b/src/plugins/valgrind/callgrindcostview.h @@ -0,0 +1,71 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef CALLGRINDCOSTVIEW_H +#define CALLGRINDCOSTVIEW_H + +#include <QtGui/QTreeView> + +#include "callgrindcostdelegate.h" + +namespace Valgrind { +namespace Internal { + +class CostView : public QTreeView +{ + Q_OBJECT + +public: + explicit CostView(QWidget *parent = 0); + virtual ~CostView(); + + /** + * Overload automatically updates the cost delegate + * and sets it for the cost columns of DataModel and CallModel. + */ + virtual void setModel(QAbstractItemModel *model); + + /** + * How to format cost data columns in the view. + */ + void setCostFormat(CostDelegate::CostFormat format); + CostDelegate::CostFormat costFormat() const; + +private: + class Private; + Private *d; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // CALLGRINDCOSTVIEW_H diff --git a/src/plugins/valgrind/callgrindengine.cpp b/src/plugins/valgrind/callgrindengine.cpp new file mode 100644 index 0000000000..ab6d266c61 --- /dev/null +++ b/src/plugins/valgrind/callgrindengine.cpp @@ -0,0 +1,170 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "callgrindengine.h" + +#include "callgrindsettings.h" + +#include <valgrind/callgrind/callgrindcontroller.h> +#include <valgrind/callgrind/callgrindparser.h> + +#include <analyzerbase/analyzermanager.h> + +#include <utils/qtcassert.h> + +using namespace Analyzer; +using namespace Valgrind; +using namespace Valgrind::Internal; + +CallgrindEngine::CallgrindEngine(const AnalyzerStartParameters &sp, + ProjectExplorer::RunConfiguration *runConfiguration) + : ValgrindEngine(sp, runConfiguration) + , m_markAsPaused(false) +{ + connect(&m_runner, SIGNAL(finished()), this, SLOT(slotFinished())); + connect(&m_runner, SIGNAL(started()), this, SLOT(slotStarted())); + + connect(m_runner.parser(), SIGNAL(parserDataReady()), this, SLOT(slotFinished())); + + connect(&m_runner, SIGNAL(statusMessage(QString)), + Analyzer::AnalyzerManager::instance(), SLOT(showStatusMessage(QString))); + + m_progress->setProgressRange(0, 2); +} + +QStringList CallgrindEngine::toolArguments() const +{ + QStringList arguments; + + AbstractCallgrindSettings *callgrindSettings = m_settings->subConfig<AbstractCallgrindSettings>(); + QTC_ASSERT(callgrindSettings, return arguments); + + if (callgrindSettings->enableCacheSim()) + arguments << "--cache-sim=yes"; + + if (callgrindSettings->enableBranchSim()) + arguments << "--branch-sim=yes"; + + if (callgrindSettings->collectBusEvents()) + arguments << "--collect-bus=yes"; + + if (callgrindSettings->collectSystime()) + arguments << "--collect-systime=yes"; + + if (m_markAsPaused) + arguments << "--instr-atstart=no"; + + // add extra arguments + arguments << extraArguments(); + + return arguments; +} + +QString CallgrindEngine::progressTitle() const +{ + return tr("Profiling"); +} + +Valgrind::ValgrindRunner * CallgrindEngine::runner() +{ + return &m_runner; +} + +void CallgrindEngine::start() +{ + emit standardOutputReceived(tr("Profiling %1").arg(executable())); + ValgrindEngine::start(); +} + +void CallgrindEngine::dump() +{ + m_runner.controller()->run(Valgrind::Callgrind::CallgrindController::Dump); +} + +void CallgrindEngine::setExtraArguments(const QStringList &extraArguments) +{ + m_extraArguments = extraArguments; +} + +void CallgrindEngine::setPaused(bool paused) +{ + if (m_markAsPaused == paused) + return; + + m_markAsPaused = paused; + + // call controller only if it is attached to a valgrind process + if (m_runner.controller()->valgrindProcess()) { + if (paused) + pause(); + else + unpause(); + } +} + +void CallgrindEngine::setToggleCollectFunction(const QString &toggleCollectFunction) +{ + if (toggleCollectFunction.isEmpty()) + return; + + m_extraArguments << QString("--toggle-collect=%1").arg(toggleCollectFunction); +} + +void CallgrindEngine::reset() +{ + m_runner.controller()->run(Valgrind::Callgrind::CallgrindController::ResetEventCounters); +} + +void CallgrindEngine::pause() +{ + m_runner.controller()->run(Valgrind::Callgrind::CallgrindController::Pause); +} + +void CallgrindEngine::unpause() +{ + m_runner.controller()->run(Valgrind::Callgrind::CallgrindController::UnPause); +} + +Valgrind::Callgrind::ParseData *CallgrindEngine::takeParserData() +{ + return m_runner.parser()->takeData(); +} + +void CallgrindEngine::slotFinished() +{ + emit parserDataReady(this); +} + +void CallgrindEngine::slotStarted() +{ + m_progress->setProgressValue(1);; +} diff --git a/src/plugins/valgrind/callgrindengine.h b/src/plugins/valgrind/callgrindengine.h new file mode 100644 index 0000000000..50bc5e9a98 --- /dev/null +++ b/src/plugins/valgrind/callgrindengine.h @@ -0,0 +1,94 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef CALLGRINDENGINE_H +#define CALLGRINDENGINE_H + +#include <valgrind/valgrindengine.h> + +#include <valgrind/callgrind/callgrindrunner.h> +#include <valgrind/callgrind/callgrindparsedata.h> + +namespace Valgrind { +namespace Internal { + +class CallgrindEngine : public Valgrind::Internal::ValgrindEngine +{ + Q_OBJECT + +public: + explicit CallgrindEngine(const Analyzer::AnalyzerStartParameters &sp, + ProjectExplorer::RunConfiguration *runConfiguration); + + void start(); + + Valgrind::Callgrind::ParseData *takeParserData(); + +public slots: + // controller actions + void dump(); + void reset(); + void pause(); + void unpause(); + + /// marks the callgrind process as paused + /// calls pause() and unpause() if there's an active run + void setPaused(bool paused); + + void setToggleCollectFunction(const QString &toggleCollectFunction); + +protected: + void setExtraArguments(const QStringList &extraArguments); + inline QStringList extraArguments() const { return m_extraArguments; } + + virtual QStringList toolArguments() const; + virtual QString progressTitle() const; + virtual Valgrind::ValgrindRunner *runner(); + +signals: + void parserDataReady(CallgrindEngine *engine); + +private: + Valgrind::Callgrind::CallgrindRunner m_runner; + bool m_markAsPaused; + + QStringList m_extraArguments; + +private slots: + void slotFinished(); + void slotStarted(); +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // CALLGRINDENGINE_H diff --git a/src/plugins/valgrind/callgrindhelper.cpp b/src/plugins/valgrind/callgrindhelper.cpp new file mode 100644 index 0000000000..3b362f3d35 --- /dev/null +++ b/src/plugins/valgrind/callgrindhelper.cpp @@ -0,0 +1,77 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "callgrindhelper.h" + +#include <cstdlib> + +#include <QtGui/QColor> +#include <QtCore/QMap> +#include <QtCore/QString> + +namespace Valgrind { +namespace Internal { + +QColor CallgrindHelper::colorForString(const QString &text) +{ + static QMap<QString, QColor> colorCache; + + if (colorCache.contains(text)) + return colorCache.value(text); + + // Minimum lightness of 100 to be readable with black text. + const QColor color = QColor::fromHsl(((qreal)qrand() / RAND_MAX * 359), + ((qreal)qrand() / RAND_MAX * 255), + ((qreal)qrand() / RAND_MAX * 127) + 128); + colorCache[text] = color; + return color; +} + +QColor CallgrindHelper::colorForCostRatio(qreal ratio) +{ + ratio = qBound(0.0, ratio, 1.0); + return QColor::fromHsv(120 - ratio * 120, 255, 255, (-((ratio-1) * (ratio-1))) * 120 + 120); +} + +QString CallgrindHelper::toPercent(float costs, const QLocale &locale) +{ + if (costs > 99.9f) + return locale.toString(100) + locale.percent(); + if (costs > 9.99f) + return locale.toString(costs, 'f', 1) + locale.percent(); + if (costs > 0.009f) + return locale.toString(costs, 'f', 2) + locale.percent(); + return QString("<") + locale.toString(0.01f) + locale.percent(); +} + +} // namespace Internal +} // namespace Valgrind diff --git a/src/plugins/valgrind/callgrindhelper.h b/src/plugins/valgrind/callgrindhelper.h new file mode 100644 index 0000000000..9431293a5a --- /dev/null +++ b/src/plugins/valgrind/callgrindhelper.h @@ -0,0 +1,68 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef CALLGRINDHELPER_H +#define CALLGRINDHELPER_H + +#include <QtCore/QLocale> + +QT_BEGIN_NAMESPACE +class QColor; +class QString; +QT_END_NAMESPACE + +namespace Valgrind { +namespace Internal { + +namespace CallgrindHelper +{ + /** + * Returns color for a specific string, the string<->color mapping is cached + */ + QColor colorForString(const QString &text); + + /** + * Returns color for a specific cost ratio + * \param ratio The cost ratio, ratio should be of [0,1] + */ + QColor colorForCostRatio(qreal ratio); + + /** + * Returns a proper percent representation of a float limited to 5 chars + */ + QString toPercent(float costs, const QLocale &locale = QLocale()); +} + +} // namespace Internal +} // namespace Valgrind + +#endif // CALLGRINDHELPER_H diff --git a/src/plugins/valgrind/callgrindnamedelegate.cpp b/src/plugins/valgrind/callgrindnamedelegate.cpp new file mode 100644 index 0000000000..b59bc82c50 --- /dev/null +++ b/src/plugins/valgrind/callgrindnamedelegate.cpp @@ -0,0 +1,95 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "callgrindnamedelegate.h" + +#include "callgrindhelper.h" + +#include <QtGui/QApplication> +#include <QtGui/QPainter> + +namespace Valgrind { +namespace Internal { + +NameDelegate::NameDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ + +} + +NameDelegate::~NameDelegate() +{ +} + +void NameDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + // init + QStyleOptionViewItemV4 opt(option); + initStyleOption(&opt, index); + + const int margin = 2; + const int size = 10; + + const QString text = index.data().toString(); + QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); + + // draw controls, but no text + opt.text.clear(); + style->drawControl(QStyle::CE_ItemViewItem, &opt, painter); + + // draw bar in the first few pixels + painter->save(); + const QRectF barRect = opt.rect.adjusted( + margin, margin, -opt.rect.width() + size - margin, -margin); + painter->setPen(Qt::black); + painter->setBrush(CallgrindHelper::colorForString(text)); + painter->drawRect(barRect); + + // move cell rect to right + opt.rect.adjust(size+margin, 0, 0, 0); + + // draw text + const QString elidedText = painter->fontMetrics().elidedText(text, Qt::ElideRight, + opt.rect.width()); + + const QBrush &textBrush = (option.state & QStyle::State_Selected) + ? opt.palette.highlightedText() + : opt.palette.text(); + painter->setBrush(Qt::NoBrush); + painter->setPen(textBrush.color()); + painter->drawText(opt.rect, elidedText); + painter->restore(); +} + +} // namespace Internal +} // namespace Valgrind diff --git a/src/plugins/valgrind/callgrindnamedelegate.h b/src/plugins/valgrind/callgrindnamedelegate.h new file mode 100644 index 0000000000..719aa7422b --- /dev/null +++ b/src/plugins/valgrind/callgrindnamedelegate.h @@ -0,0 +1,56 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef CALLGRINDNAMEDELEGATE_H +#define CALLGRINDNAMEDELEGATE_H + +#include <QtGui/QStyledItemDelegate> + +namespace Valgrind { +namespace Internal { + +class NameDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + explicit NameDelegate(QObject *parent = 0); + virtual ~NameDelegate(); + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // CALLGRINDNAMEDELEGATE_H diff --git a/src/plugins/valgrind/callgrindsettings.cpp b/src/plugins/valgrind/callgrindsettings.cpp new file mode 100644 index 0000000000..58505cebdb --- /dev/null +++ b/src/plugins/valgrind/callgrindsettings.cpp @@ -0,0 +1,226 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "callgrindsettings.h" + +#include "callgrindconfigwidget.h" + +#include <QtCore/QDebug> + +using namespace Analyzer; + +static const char callgrindEnableCacheSimC[] = "Analyzer.Valgrind.Callgrind.EnableCacheSim"; +static const char callgrindEnableBranchSimC[] = "Analyzer.Valgrind.Callgrind.EnableBranchSim"; +static const char callgrindCollectSystimeC[] = "Analyzer.Valgrind.Callgrind.CollectSystime"; +static const char callgrindCollectBusEventsC[] = "Analyzer.Valgrind.Callgrind.CollectBusEvents"; +static const char callgrindEnableEventToolTipsC[] = "Analyzer.Valgrind.Callgrind.EnableEventToolTips"; +static const char callgrindMinimumCostRatioC[] = "Analyzer.Valgrind.Callgrind.MinimumCostRatio"; +static const char callgrindVisualisationMinimumCostRatioC[] = "Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio"; + +static const char callgrindCycleDetectionC[] = "Analyzer.Valgrind.Callgrind.CycleDetection"; +static const char callgrindCostFormatC[] = "Analyzer.Valgrind.Callgrind.CostFormat"; + +namespace Valgrind { +namespace Internal { + +void AbstractCallgrindSettings::setEnableCacheSim(bool enable) +{ + if (m_enableCacheSim == enable) + return; + + m_enableCacheSim = enable; + emit enableCacheSimChanged(enable); +} + +void AbstractCallgrindSettings::setEnableBranchSim(bool enable) +{ + if (m_enableBranchSim == enable) + return; + + m_enableBranchSim = enable; + emit enableBranchSimChanged(enable); +} + +void AbstractCallgrindSettings::setCollectSystime(bool collect) +{ + if (m_collectSystime == collect) + return; + + m_collectSystime = collect; + emit collectSystimeChanged(collect); +} + +void AbstractCallgrindSettings::setCollectBusEvents(bool collect) +{ + if (m_collectBusEvents == collect) + return; + + m_collectBusEvents = collect; + emit collectBusEventsChanged(collect); +} + +void AbstractCallgrindSettings::setEnableEventToolTips(bool enable) +{ + if (m_enableEventToolTips == enable) + return; + + m_enableEventToolTips = enable; + emit enableEventToolTipsChanged(enable); +} + +void AbstractCallgrindSettings::setMinimumInclusiveCostRatio( + double minimumInclusiveCostRatio) +{ + if (m_minimumInclusiveCostRatio == minimumInclusiveCostRatio) + return; + + m_minimumInclusiveCostRatio = qBound(0.0, minimumInclusiveCostRatio, 100.0); + emit minimumInclusiveCostRatioChanged(minimumInclusiveCostRatio); +} + +void AbstractCallgrindSettings::setVisualisationMinimumInclusiveCostRatio( + double minimumInclusiveCostRatio) +{ + if (m_visualisationMinimumInclusiveCostRatio == minimumInclusiveCostRatio) + return; + + m_visualisationMinimumInclusiveCostRatio = qBound(0.0, minimumInclusiveCostRatio, 100.0); + emit visualisationMinimumInclusiveCostRatioChanged(minimumInclusiveCostRatio); +} + +QVariantMap AbstractCallgrindSettings::defaults() const +{ + QVariantMap map; + map.insert(QLatin1String(callgrindEnableCacheSimC), false); + map.insert(QLatin1String(callgrindEnableBranchSimC), false); + map.insert(QLatin1String(callgrindCollectSystimeC), false); + map.insert(QLatin1String(callgrindCollectBusEventsC), false); + map.insert(QLatin1String(callgrindEnableEventToolTipsC), true); + map.insert(QLatin1String(callgrindMinimumCostRatioC), 0.01); + map.insert(QLatin1String(callgrindVisualisationMinimumCostRatioC), 10.0); + return map; +} + +bool AbstractCallgrindSettings::fromMap(const QVariantMap &map) +{ + setIfPresent(map, QLatin1String(callgrindEnableCacheSimC), &m_enableCacheSim); + setIfPresent(map, QLatin1String(callgrindEnableBranchSimC), &m_enableBranchSim); + setIfPresent(map, QLatin1String(callgrindCollectSystimeC), &m_collectSystime); + setIfPresent(map, QLatin1String(callgrindCollectBusEventsC), &m_collectBusEvents); + setIfPresent(map, QLatin1String(callgrindEnableEventToolTipsC), &m_enableEventToolTips); + setIfPresent(map, QLatin1String(callgrindMinimumCostRatioC), &m_minimumInclusiveCostRatio); + setIfPresent(map, QLatin1String(callgrindVisualisationMinimumCostRatioC), + &m_visualisationMinimumInclusiveCostRatio); + return true; +} + +QVariantMap AbstractCallgrindSettings::toMap() const +{ + QVariantMap map; + map.insert(QLatin1String(callgrindEnableCacheSimC), m_enableCacheSim); + map.insert(QLatin1String(callgrindEnableBranchSimC), m_enableBranchSim); + map.insert(QLatin1String(callgrindCollectSystimeC), m_collectSystime); + map.insert(QLatin1String(callgrindCollectBusEventsC), m_collectBusEvents); + map.insert(QLatin1String(callgrindEnableEventToolTipsC), m_enableEventToolTips); + map.insert(QLatin1String(callgrindMinimumCostRatioC), m_minimumInclusiveCostRatio); + map.insert(QLatin1String(callgrindVisualisationMinimumCostRatioC), + m_visualisationMinimumInclusiveCostRatio); + return map; +} + +QString AbstractCallgrindSettings::id() const +{ + return QLatin1String("Analyzer.Valgrind.Settings.Callgrind"); +} + +QString AbstractCallgrindSettings::displayName() const +{ + return tr("Profiling"); +} + +QWidget *AbstractCallgrindSettings::createConfigWidget(QWidget *parent) +{ + return new CallgrindConfigWidget(this, parent); +} + + +QVariantMap CallgrindGlobalSettings::defaults() const +{ + QVariantMap map = AbstractCallgrindSettings::defaults(); + map.insert(QLatin1String(callgrindCostFormatC), CostDelegate::FormatRelative); + map.insert(QLatin1String(callgrindCycleDetectionC), true); + return map; +} + +bool CallgrindGlobalSettings::fromMap(const QVariantMap &map) +{ + AbstractCallgrindSettings::fromMap(map); + // special code as the default one does not cope with the enum properly + if (map.contains(QLatin1String(callgrindCostFormatC))) { + m_costFormat = static_cast<CostDelegate::CostFormat>(map.value(QLatin1String(callgrindCostFormatC)).toInt()); + } + setIfPresent(map, QLatin1String(callgrindCycleDetectionC), &m_detectCycles); + return true; +} + +QVariantMap CallgrindGlobalSettings::toMap() const +{ + QVariantMap map = AbstractCallgrindSettings::toMap(); + map.insert(QLatin1String(callgrindCostFormatC), m_costFormat); + map.insert(QLatin1String(callgrindCycleDetectionC), m_detectCycles); + return map; +} + +CostDelegate::CostFormat CallgrindGlobalSettings::costFormat() const +{ + return m_costFormat; +} + +void CallgrindGlobalSettings::setCostFormat(CostDelegate::CostFormat format) +{ + m_costFormat = format; + AnalyzerGlobalSettings::instance()->writeSettings(); +} + +bool CallgrindGlobalSettings::detectCycles() const +{ + return m_detectCycles; +} + +void CallgrindGlobalSettings::setDetectCycles(bool detect) +{ + m_detectCycles = detect; + AnalyzerGlobalSettings::instance()->writeSettings(); +} + +} // namespace Internal +} // namespace Valgrind diff --git a/src/plugins/valgrind/callgrindsettings.h b/src/plugins/valgrind/callgrindsettings.h new file mode 100644 index 0000000000..1a81bf8712 --- /dev/null +++ b/src/plugins/valgrind/callgrindsettings.h @@ -0,0 +1,153 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef ANALYZER_INTERNAL_CALLGRINDSETTINGS_H +#define ANALYZER_INTERNAL_CALLGRINDSETTINGS_H + +#include "callgrindcostdelegate.h" + +#include <analyzerbase/analyzersettings.h> + +#include <QtCore/QString> + +namespace Valgrind { +namespace Internal { + +/** + * Generic callgrind settings + */ +class AbstractCallgrindSettings : public Analyzer::AbstractAnalyzerSubConfig +{ + Q_OBJECT + +public: + AbstractCallgrindSettings() {} + + bool enableCacheSim() const { return m_enableCacheSim; } + bool enableBranchSim() const { return m_enableBranchSim; } + bool collectSystime() const { return m_collectSystime; } + bool collectBusEvents() const { return m_collectBusEvents; } + bool enableEventToolTips() const { return m_enableEventToolTips; } + + /// \return Minimum cost ratio, range [0.0..100.0] + double minimumInclusiveCostRatio() const { return m_minimumInclusiveCostRatio; } + + /// \return Minimum cost ratio, range [0.0..100.0] + double visualisationMinimumInclusiveCostRatio() const { return m_visualisationMinimumInclusiveCostRatio; } + + // abstract virtual methods from base class + virtual bool fromMap(const QVariantMap &map); + + virtual QVariantMap defaults() const; + + virtual QString id() const; + virtual QString displayName() const; + virtual QWidget *createConfigWidget(QWidget *parent); + +public slots: + void setEnableCacheSim(bool enable); + void setEnableBranchSim(bool enable); + void setCollectSystime(bool collect); + void setCollectBusEvents(bool collect); + void setEnableEventToolTips(bool enable); + + /// \param minimumInclusiveCostRatio Minimum inclusive cost ratio, valid values are [0.0..100.0] + void setMinimumInclusiveCostRatio(double minimumInclusiveCostRatio); + + /// \param minimumInclusiveCostRatio Minimum inclusive cost ratio, valid values are [0.0..100.0] + void setVisualisationMinimumInclusiveCostRatio(double minimumInclusiveCostRatio); + +signals: + void enableCacheSimChanged(bool); + void enableBranchSimChanged(bool); + void collectSystimeChanged(bool); + void collectBusEventsChanged(bool); + void enableEventToolTipsChanged(bool); + void minimumInclusiveCostRatioChanged(double); + void visualisationMinimumInclusiveCostRatioChanged(double); + +protected: + virtual QVariantMap toMap() const; + +private: + bool m_enableCacheSim; + bool m_collectSystime; + bool m_collectBusEvents; + bool m_enableBranchSim; + bool m_enableEventToolTips; + double m_minimumInclusiveCostRatio; + double m_visualisationMinimumInclusiveCostRatio; +}; + +/** + * Global callgrind settings + */ +class CallgrindGlobalSettings : public AbstractCallgrindSettings +{ + Q_OBJECT + +public: + CallgrindGlobalSettings() {} + + virtual bool fromMap(const QVariantMap &map); + virtual QVariantMap defaults() const; + + CostDelegate::CostFormat costFormat() const; + bool detectCycles() const; + +public slots: + void setCostFormat(Valgrind::Internal::CostDelegate::CostFormat format); + void setDetectCycles(bool detect); + +protected: + virtual QVariantMap toMap() const; + +private: + CostDelegate::CostFormat m_costFormat; + bool m_detectCycles; +}; + +/** + * Per-project callgrind settings, saves a diff to the global suppression files list + */ +class CallgrindProjectSettings : public AbstractCallgrindSettings +{ + Q_OBJECT + +public: + CallgrindProjectSettings() {} +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // ANALYZER_INTERNAL_CALLGRINDSETTINGS_H diff --git a/src/plugins/valgrind/callgrindtextmark.cpp b/src/plugins/valgrind/callgrindtextmark.cpp new file mode 100644 index 0000000000..fa2c4e8aa6 --- /dev/null +++ b/src/plugins/valgrind/callgrindtextmark.cpp @@ -0,0 +1,107 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "callgrindtextmark.h" + +#include "callgrindhelper.h" + +#include <valgrind/callgrind/callgrinddatamodel.h> +#include <valgrind/callgrind/callgrindfunction.h> + +#include <QtCore/QDebug> +#include <QtGui/QPainter> + +#include <utils/qtcassert.h> + +using namespace Valgrind::Internal; +using namespace Valgrind::Callgrind; + +CallgrindTextMark::CallgrindTextMark(const QPersistentModelIndex &index, + const QString &fileName, int lineNumber) + : m_modelIndex(index) +{ + setLocation(fileName, lineNumber); + setPriority(TextEditor::ITextMark::HighPriority); +} + +void CallgrindTextMark::paint(QPainter *painter, const QRect &paintRect) const +{ + if (!m_modelIndex.isValid()) + return; + + bool ok; + qreal costs = m_modelIndex.data(RelativeTotalCostRole).toReal(&ok); + QTC_ASSERT(ok, return) + QTC_ASSERT(costs >= 0.0 && costs <= 100.0, return) + + painter->save(); + + // set up + painter->setPen(Qt::black); + + // draw bar + QRect fillRect = paintRect; + fillRect.setWidth(paintRect.width() * costs); + painter->fillRect(paintRect, Qt::white); + painter->fillRect(fillRect, CallgrindHelper::colorForCostRatio(costs)); + painter->drawRect(paintRect); + + // draw text + const QTextOption flags = Qt::AlignHCenter | Qt::AlignVCenter; + const QString text = CallgrindHelper::toPercent(costs * 100.0f); + + // decrease font size if paint rect is too small (very unlikely, but may happen) + QFont font = painter->font(); + QFontMetrics fm = font; + while (fm.boundingRect(text).width() > paintRect.width()) { + font.setPointSize(font.pointSize() - 1); + fm = font; + } + painter->setFont(font); + + painter->drawText(paintRect, text, flags); + + painter->restore(); +} + +const Function *CallgrindTextMark::function() const +{ + if (!m_modelIndex.isValid()) + return 0; + + return m_modelIndex.data(DataModel::FunctionRole).value<const Function *>(); +} + +double CallgrindTextMark::widthFactor() const +{ + return 4.0; +} diff --git a/src/plugins/valgrind/callgrindtextmark.h b/src/plugins/valgrind/callgrindtextmark.h new file mode 100644 index 0000000000..d779287340 --- /dev/null +++ b/src/plugins/valgrind/callgrindtextmark.h @@ -0,0 +1,74 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef CALLGRINDTEXTMARK_H +#define CALLGRINDTEXTMARK_H + +#include <texteditor/basetextmark.h> + +#include <QtCore/QPersistentModelIndex> + +namespace Valgrind { + +namespace Callgrind { +class Function; +} + +namespace Internal { + +class CallgrindTextMark : public TextEditor::BaseTextMark +{ + Q_OBJECT + +public: + /** + * This creates a callgrind text mark for a specific Function + * + * \param index DataModel model index + * \note The index parameter must refer to one of the DataModel cost columns + */ + explicit CallgrindTextMark(const QPersistentModelIndex &index, + const QString &fileName, int lineNumber); + + const Valgrind::Callgrind::Function *function() const; + + virtual double widthFactor() const; + virtual void paint(QPainter *painter, const QRect &paintRect) const; + +private: + QPersistentModelIndex m_modelIndex; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // CALLGRINDTEXTMARK_H diff --git a/src/plugins/valgrind/callgrindtool.cpp b/src/plugins/valgrind/callgrindtool.cpp new file mode 100644 index 0000000000..3378668689 --- /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 (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#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" diff --git a/src/plugins/valgrind/callgrindtool.h b/src/plugins/valgrind/callgrindtool.h new file mode 100644 index 0000000000..fcb4f617c4 --- /dev/null +++ b/src/plugins/valgrind/callgrindtool.h @@ -0,0 +1,73 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef CALLGRINDTOOL_H +#define CALLGRINDTOOL_H + +#include <analyzerbase/ianalyzertool.h> + +namespace Valgrind { +namespace Internal { + +class CallgrindToolPrivate; + +class CallgrindTool : public Analyzer::IAnalyzerTool +{ + Q_OBJECT + +public: + explicit CallgrindTool(QObject *parent = 0); + ~CallgrindTool(); + + QString id() const; + QString displayName() const; + ToolMode mode() const; + + void initialize(); + void extensionsInitialized(); + void initializeDockWidgets(); + + Analyzer::IAnalyzerEngine *createEngine(const Analyzer::AnalyzerStartParameters &sp, + ProjectExplorer::RunConfiguration *runConfiguration = 0); + QWidget *createControlWidget(); + + bool canRunRemotely() const { return true; } + bool needsOutputPane() const { return false; } + +private: + CallgrindToolPrivate *d; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // CALLGRINDTOOL_H diff --git a/src/plugins/valgrind/callgrindvisualisation.cpp b/src/plugins/valgrind/callgrindvisualisation.cpp new file mode 100644 index 0000000000..adfb7f6f04 --- /dev/null +++ b/src/plugins/valgrind/callgrindvisualisation.cpp @@ -0,0 +1,464 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "callgrindvisualisation.h" + +#include "callgrindhelper.h" + +#include <valgrind/callgrind/callgrindabstractmodel.h> +#include <valgrind/callgrind/callgrinddatamodel.h> +#include <valgrind/callgrind/callgrindfunction.h> +#include <valgrind/callgrind/callgrindproxymodel.h> +#include <utils/qtcassert.h> + +#include <QtGui/QGraphicsRectItem> +#include <QtGui/QGraphicsScene> +#include <QtGui/QGraphicsSimpleTextItem> +#include <QtGui/QMouseEvent> +#include <QtGui/QStaticText> +#include <QtGui/QStyleOptionGraphicsItem> +#include <QtCore/QPair> +#include <QtCore/QPersistentModelIndex> +#include <QtCore/QLinkedList> +#include <QtCore/QAbstractItemModel> +#include <QtCore/QDebug> + +#define VISUALISATION_DEBUG 0 +// Margin from hardcoded value in: +// QGraphicsView::fitInView(const QRectF &rect, +// Qt::AspectRatioMode aspectRatioMode) +// Bug report here: http://bugreports.qt.nokia.com/browse/QTBUG-11945 +static const int FIT_IN_VIEW_MARGIN = 2; + +using namespace Valgrind::Callgrind; + +namespace Valgrind { +namespace Internal { + +class FunctionGraphicsTextItem : public QAbstractGraphicsShapeItem +{ +public: + FunctionGraphicsTextItem(const QString &text, QGraphicsItem *parent); + + virtual void paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget); + virtual QRectF boundingRect() const; + +private: + QString m_text; + QStaticText m_staticText; + qreal m_previousViewportDimension; +}; + +class FunctionGraphicsItem : public QGraphicsRectItem +{ +public: + enum DataKey { + FunctionCallKey + }; + + FunctionGraphicsItem(const QString &text, qreal x, qreal y, + qreal width, qreal height, QGraphicsItem *parent = 0); + + virtual void paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget); + FunctionGraphicsTextItem *textItem() const; + +private: + FunctionGraphicsTextItem *m_text; +}; + +FunctionGraphicsTextItem::FunctionGraphicsTextItem(const QString &text, + QGraphicsItem *parent) + : QAbstractGraphicsShapeItem(parent) + , m_text(text) + , m_previousViewportDimension(0) +{ + setFlag(QGraphicsItem::ItemIgnoresTransformations); + setAcceptedMouseButtons(0); // do not steal focus from parent item + setToolTip(text); +} + +void FunctionGraphicsTextItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *, + QWidget *widget) +{ + const qreal textHeight = painter->fontMetrics().height(); + // Magic number based on what looked best. + const int margin = 2 + FIT_IN_VIEW_MARGIN; + const QRectF viewportRect = + widget->rect().adjusted(margin, margin, -margin, -margin); + + const qreal maxWidth = viewportRect.width() + * parentItem()->boundingRect().width() + / scene()->sceneRect().width(); + const qreal maxHeight = viewportRect.height() + * parentItem()->boundingRect().height() + / scene()->sceneRect().height(); + + if (textHeight > maxHeight) + return; + + if (viewportRect.width() != m_previousViewportDimension) { + const QString &elidedText = + painter->fontMetrics().elidedText(m_text, Qt::ElideRight, + maxWidth); + m_staticText.setText(elidedText); + m_staticText.prepare(); + + m_previousViewportDimension = viewportRect.width(); + } + +#if VISUALISATION_DEBUG + painter->setPen(Qt::red); + painter->drawRect(boundingRect()); +#endif + + painter->save(); + int textLeft = 0; + int textTop = 0; + const int textWidth = painter->fontMetrics().width(m_staticText.text()); + textLeft = -textWidth/2; + textTop = (maxHeight - textHeight)/2; + painter->drawStaticText(textLeft, textTop, m_staticText); + + painter->restore(); +} + +QRectF FunctionGraphicsTextItem::boundingRect() const +{ + return mapRectFromParent(parentItem()->boundingRect()); +} + +FunctionGraphicsItem::FunctionGraphicsItem(const QString &text, + qreal x, qreal y, qreal width, + qreal height, QGraphicsItem *parent) + : QGraphicsRectItem(x, y, width, height, parent) + , m_text(0) +{ + setFlag(QGraphicsItem::ItemIsSelectable); + setFlag(QGraphicsItem::ItemClipsToShape); + setFlag(QGraphicsItem::ItemClipsChildrenToShape); + setToolTip(text); + + m_text = new FunctionGraphicsTextItem(text, this); + m_text->setPos(rect().center().x(), y); +} + +FunctionGraphicsTextItem *FunctionGraphicsItem::textItem() const +{ + return m_text; +} + +void FunctionGraphicsItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *) +{ + painter->save(); + + QRectF rect = this->rect(); + const QColor &color = brush().color(); + if (option->state & QStyle::State_Selected) { + QLinearGradient gradient(0, 0, rect.width(), 0); + gradient.setColorAt(0, color.darker(100)); + gradient.setColorAt(0.5, color.lighter(200)); + gradient.setColorAt(1, color.darker(100)); + painter->setBrush(gradient); + } + else { + painter->setBrush(color); + } + +#if VISUALISATION_DEBUG + painter->setPen(Qt::blue); + painter->drawRect(boundingRect()); +#endif + + QPen pen = painter->pen(); + pen.setColor(color.darker()); + pen.setWidthF(0.5); + painter->setPen(pen); + qreal halfPenWidth = pen.widthF()/2.0; + rect.adjust(halfPenWidth, halfPenWidth, -halfPenWidth, -halfPenWidth); + painter->drawRect(rect); + + painter->restore(); +} + +class Visualisation::Private +{ +public: + Private(Visualisation *qq); + + void handleMousePressEvent(QMouseEvent *event, bool doubleClicked); + qreal sceneHeight() const; + qreal sceneWidth() const; + + Visualisation *q; + DataProxyModel *m_model; + QGraphicsScene m_scene; +}; + +Visualisation::Private::Private(Visualisation *qq) + : q(qq) + , m_model(new DataProxyModel(qq)) +{ + // setup scene + m_scene.setObjectName("Visualisation Scene"); + ///NOTE: with size 100x100 the Qt-internal mouse selection fails... + m_scene.setSceneRect(0, 0, 1024, 1024); + + // setup model + m_model->setMinimumInclusiveCostRatio(0.1); + connect(m_model, + SIGNAL(filterFunctionChanged(const Function*,const Function*)), + qq, SLOT(populateScene())); +} + +void Visualisation::Private::handleMousePressEvent(QMouseEvent *event, + bool doubleClicked) +{ + // find the first item that accepts mouse presses under the cursor position + QGraphicsItem *itemAtPos = 0; + foreach (QGraphicsItem *item, q->items(event->pos())) { + if (!(item->acceptedMouseButtons() & event->button())) + continue; + + itemAtPos = item; + break; + } + + // if there is an item, select it + if (itemAtPos) { + const Function *func = q->functionForItem(itemAtPos); + + if (doubleClicked) { + q->functionActivated(func); + } + else { + q->scene()->clearSelection(); + itemAtPos->setSelected(true); + q->functionSelected(func); + } + } + +} + +qreal Visualisation::Private::sceneHeight() const +{ + return m_scene.height() - FIT_IN_VIEW_MARGIN; +} + +qreal Visualisation::Private::sceneWidth() const +{ + // Magic number to improve margins appearance + return m_scene.width() + 1; +} + +Visualisation::Visualisation(QWidget *parent) + : QGraphicsView(parent) + , d(new Private(this)) +{ + setObjectName("Visualisation View"); + setScene(&d->m_scene); + setRenderHint(QPainter::Antialiasing); +} + +Visualisation::~Visualisation() +{ + delete d; +} + +const Function *Visualisation::functionForItem(QGraphicsItem *item) const +{ + return item->data(FunctionGraphicsItem::FunctionCallKey).value<const Function *>(); +} + +QGraphicsItem *Visualisation::itemForFunction(const Function *function) const +{ + foreach (QGraphicsItem *item, items()) { + if (functionForItem(item) == function) + return item; + } + return 0; +} + +void Visualisation::setFunction(const Function *function) +{ + d->m_model->setFilterFunction(function); +} + +const Function *Visualisation::function() const +{ + return d->m_model->filterFunction(); +} + +void Visualisation::setMinimumInclusiveCostRatio(double ratio) +{ + d->m_model->setMinimumInclusiveCostRatio(ratio); +} + +void Visualisation::setModel(QAbstractItemModel *model) +{ + QTC_ASSERT(!d->m_model->sourceModel() && model, return); // only set once! + d->m_model->setSourceModel(model); + + connect(model, + SIGNAL(columnsInserted(QModelIndex,int,int)), + SLOT(populateScene())); + connect(model, + SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), + SLOT(populateScene())); + connect(model, + SIGNAL(columnsRemoved(QModelIndex,int,int)), + SLOT(populateScene())); + connect(model, + SIGNAL(dataChanged(QModelIndex,QModelIndex)), + SLOT(populateScene())); + connect(model, + SIGNAL(headerDataChanged(Qt::Orientation,int,int)), + SLOT(populateScene())); + connect(model, SIGNAL(layoutChanged()), SLOT(populateScene())); + connect(model, SIGNAL(modelReset()), SLOT(populateScene())); + connect(model, + SIGNAL(rowsInserted(QModelIndex,int,int)), + SLOT(populateScene())); + connect(model, + SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + SLOT(populateScene())); + connect(model, + SIGNAL(rowsRemoved(QModelIndex,int,int)), + SLOT(populateScene())); + + populateScene(); +} + +void Visualisation::setText(const QString &message) +{ + d->m_scene.clear(); + + QGraphicsSimpleTextItem *textItem = d->m_scene.addSimpleText(message); + textItem->setBrush(palette().foreground()); + textItem->setPos((d->sceneWidth() - textItem->boundingRect().width()) / 2, + (d->sceneHeight() - textItem->boundingRect().height()) / 2); + textItem->setFlag(QGraphicsItem::ItemIgnoresTransformations); +} + +void Visualisation::populateScene() +{ + // reset scene first + d->m_scene.clear(); + + const qreal sceneWidth = d->sceneWidth(); + const qreal sceneHeight = d->sceneHeight(); + + // cache costs of each element, calculate total costs + qreal total = 0; + + typedef QPair<QModelIndex, qreal> Pair; + QLinkedList<Pair> costs; + for (int row = 0; row < d->m_model->rowCount(); ++row) { + const QModelIndex index = d->m_model->index(row, DataModel::InclusiveCostColumn); + + bool ok = false; + const qreal cost = index.data().toReal(&ok); + QTC_ASSERT(ok, continue); + costs << QPair<QModelIndex, qreal>(d->m_model->index(row, 0), cost); + total += cost; + } + + if (!costs.isEmpty() || d->m_model->filterFunction()) { + // item showing the current filter function + + QString text; + if (d->m_model->filterFunction()) { + text = d->m_model->filterFunction()->name(); + } else { + const float ratioPercent = d->m_model->minimumInclusiveCostRatio() * 100; + QString ratioPercentString = QString::number(ratioPercent); + ratioPercentString.append(QLocale::system().percent()); + const int hiddenFunctions = d->m_model->sourceModel()->rowCount() - d->m_model->rowCount(); + text = tr("All functions with an inclusive cost ratio higher than %1 (%2 are hidden)") + .arg(ratioPercentString) + .arg(hiddenFunctions); + } + + const qreal height = sceneHeight * (costs.isEmpty() ? 1.0 : 0.1); + FunctionGraphicsItem *item = new FunctionGraphicsItem(text, 0, 0, sceneWidth, height); + const QColor background = CallgrindHelper::colorForString(text); + item->setBrush(background); + item->setData(FunctionGraphicsItem::FunctionCallKey, QVariant::fromValue(d->m_model->filterFunction())); + // NOTE: workaround wrong tooltip being show, no idea why... + item->setZValue(-1); + d->m_scene.addItem(item); + } + + // add the canvas elements to the scene + qreal used = sceneHeight * 0.1; + foreach (const Pair &cost, costs) { + const QModelIndex &index = cost.first; + const QString text = index.data().toString(); + + const qreal height = (sceneHeight * 0.9 * cost.second) / total; + + FunctionGraphicsItem *item = new FunctionGraphicsItem(text, 0, used, sceneWidth, height); + const QColor background = CallgrindHelper::colorForString(text); + item->setBrush(background); + item->setData(FunctionGraphicsItem::FunctionCallKey, index.data(DataModel::FunctionRole)); + d->m_scene.addItem(item); + used += height; + } +} + +void Visualisation::mousePressEvent(QMouseEvent *event) +{ + d->handleMousePressEvent(event, false); + + QGraphicsView::mousePressEvent(event); +} + +void Visualisation::mouseDoubleClickEvent(QMouseEvent *event) +{ + d->handleMousePressEvent(event, true); + + QGraphicsView::mouseDoubleClickEvent(event); +} + +void Visualisation::resizeEvent(QResizeEvent *event) +{ + fitInView(sceneRect()); + + QGraphicsView::resizeEvent(event); +} + +} // namespace Internal +} // namespace Valgrind diff --git a/src/plugins/valgrind/callgrindvisualisation.h b/src/plugins/valgrind/callgrindvisualisation.h new file mode 100644 index 0000000000..ef3a08925e --- /dev/null +++ b/src/plugins/valgrind/callgrindvisualisation.h @@ -0,0 +1,94 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef CALLGRINDVISUALISATION_H +#define CALLGRINDVISUALISATION_H + +#include <QtGui/QGraphicsView> + +QT_BEGIN_NAMESPACE +class QAbstractItemModel; +class QModelIndex; +QT_END_NAMESPACE + +namespace Valgrind { +namespace Callgrind { +class Function; +} +} + +namespace Valgrind { +namespace Internal { + +class Visualisation : public QGraphicsView +{ + Q_OBJECT + +public: + explicit Visualisation(QWidget *parent = 0); + virtual ~Visualisation(); + + void setModel(QAbstractItemModel *model); + + const Valgrind::Callgrind::Function *functionForItem(QGraphicsItem *item) const; + QGraphicsItem *itemForFunction(const Valgrind::Callgrind::Function *function) const; + + void setFunction(const Valgrind::Callgrind::Function *function); + const Valgrind::Callgrind::Function *function() const; + + void setMinimumInclusiveCostRatio(double ratio); + +public slots: + void setText(const QString &message); + +signals: + void functionActivated(const Valgrind::Callgrind::Function *); + void functionSelected(const Valgrind::Callgrind::Function *); + +protected slots: + void populateScene(); + + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseDoubleClickEvent(QMouseEvent *event); + +protected: + virtual void resizeEvent(QResizeEvent *event); + +private: + class Private; + Private *d; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // VALGRIND_CALLGRIND_CALLGRINDVISUALISATION_H diff --git a/src/plugins/valgrind/memcheck.pro b/src/plugins/valgrind/memcheck.pro new file mode 100644 index 0000000000..e6843ed307 --- /dev/null +++ b/src/plugins/valgrind/memcheck.pro @@ -0,0 +1,32 @@ +TEMPLATE = lib +TARGET = Memcheck + +DEFINES += MEMCHECK_LIBRARY + +include(../../qtcreatorplugin.pri) +include(memcheck_dependencies.pri) + +# Memcheck files + +HEADERS += \ + memcheckplugin.h \ + memcheck_global.h \ + memchecktool.h \ + memcheckengine.h \ + memcheckerrorview.h \ + memchecksettings.h \ + memcheckconfigwidget.h \ + suppressiondialog.h + +SOURCES += \ + memcheckplugin.cpp \ + memchecktool.cpp \ + memcheckengine.cpp \ + memcheckerrorview.cpp \ + memchecksettings.cpp \ + memcheckconfigwidget.cpp \ + suppressiondialog.cpp + +FORMS += \ + suppressiondialog.ui \ + memcheckconfigwidget.ui \ diff --git a/src/plugins/valgrind/memcheckconfigwidget.cpp b/src/plugins/valgrind/memcheckconfigwidget.cpp new file mode 100644 index 0000000000..d573178764 --- /dev/null +++ b/src/plugins/valgrind/memcheckconfigwidget.cpp @@ -0,0 +1,182 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "memcheckconfigwidget.h" + +#include "ui_memcheckconfigwidget.h" + +#include "memchecksettings.h" + +#include <utils/qtcassert.h> + +#include <QtGui/QStandardItemModel> +#include <QtGui/QFileDialog> +#include <QtCore/QDebug> + +namespace Valgrind { +namespace Internal { + +MemcheckConfigWidget::MemcheckConfigWidget(AbstractMemcheckSettings *settings, QWidget *parent) + : QWidget(parent), + m_settings(settings), + m_model(new QStandardItemModel(this)), + m_ui(new Ui::MemcheckConfigWidget) +{ + m_ui->setupUi(this); + + m_ui->suppressionList->setModel(m_model); + m_ui->suppressionList->setSelectionMode(QAbstractItemView::MultiSelection); + + connect(m_ui->addSuppression, SIGNAL(clicked()), + this, SLOT(slotAddSuppression())); + connect(m_ui->removeSuppression, SIGNAL(clicked()), + this, SLOT(slotRemoveSuppression())); + + m_ui->numCallers->setValue(m_settings->numCallers()); + connect(m_ui->numCallers, SIGNAL(valueChanged(int)), m_settings, SLOT(setNumCallers(int))); + connect(m_settings, SIGNAL(numCallersChanged(int)), m_ui->numCallers, SLOT(setValue(int))); + + m_ui->trackOrigins->setChecked(m_settings->trackOrigins()); + connect(m_ui->trackOrigins, SIGNAL(toggled(bool)), m_settings, SLOT(setTrackOrigins(bool))); + connect(m_settings, SIGNAL(trackOriginsChanged(bool)), m_ui->trackOrigins, SLOT(setChecked(bool))); + + connect(m_settings, SIGNAL(suppressionFilesRemoved(QStringList)), + this, SLOT(slotSuppressionsRemoved(QStringList))); + connect(m_settings, SIGNAL(suppressionFilesAdded(QStringList)), + this, SLOT(slotSuppressionsAdded(QStringList))); + + m_model->clear(); + foreach (const QString &file, m_settings->suppressionFiles()) + m_model->appendRow(new QStandardItem(file)); + + connect(m_ui->suppressionList->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(slotSuppressionSelectionChanged())); + slotSuppressionSelectionChanged(); +} + +MemcheckConfigWidget::~MemcheckConfigWidget() +{ + delete m_ui; +} + +void MemcheckConfigWidget::slotAddSuppression() +{ + QFileDialog dialog; + dialog.setNameFilter(tr("Valgrind Suppression File (*.supp);;All Files (*)")); + dialog.setAcceptMode(QFileDialog::AcceptOpen); + dialog.setFileMode(QFileDialog::ExistingFiles); + MemcheckGlobalSettings *conf = Analyzer::AnalyzerGlobalSettings::instance()->subConfig<MemcheckGlobalSettings>(); + QTC_ASSERT(conf, return); + dialog.setDirectory(conf->lastSuppressionDialogDirectory()); + dialog.setHistory(conf->lastSuppressionDialogHistory()); + + if (dialog.exec() == QDialog::Accepted) { + foreach (const QString &file, dialog.selectedFiles()) + m_model->appendRow(new QStandardItem(file)); + + m_settings->addSuppressionFiles(dialog.selectedFiles()); + } + + conf->setLastSuppressionDialogDirectory(dialog.directory().absolutePath()); + conf->setLastSuppressionDialogHistory(dialog.history()); +} + +void MemcheckConfigWidget::slotSuppressionsAdded(const QStringList &files) +{ + QStringList filesToAdd = files; + for (int i = 0, c = m_model->rowCount(); i < c; ++i) + filesToAdd.removeAll(m_model->item(i)->text()); + + foreach (const QString &file, filesToAdd) + m_model->appendRow(new QStandardItem(file)); +} + +bool sortReverse(int l, int r) +{ + return l > r; +} + +void MemcheckConfigWidget::slotRemoveSuppression() +{ + // remove from end so no rows get invalidated + QList<int> rows; + + QStringList removed; + foreach (const QModelIndex &index, m_ui->suppressionList->selectionModel()->selectedIndexes()) { + rows << index.row(); + removed << index.data().toString(); + } + + qSort(rows.begin(), rows.end(), sortReverse); + + foreach (int row, rows) + m_model->removeRow(row); + + m_settings->removeSuppressionFiles(removed); +} + +void MemcheckConfigWidget::slotSuppressionsRemoved(const QStringList &files) +{ + for (int i = 0; i < m_model->rowCount(); ++i) { + if (files.contains(m_model->item(i)->text())) { + m_model->removeRow(i); + --i; + } + } +} + +void MemcheckConfigWidget::setSuppressions(const QStringList &files) +{ + m_model->clear(); + foreach (const QString &file, files) + m_model->appendRow(new QStandardItem(file)); +} + +QStringList MemcheckConfigWidget::suppressions() const +{ + QStringList ret; + + for (int i = 0; i < m_model->rowCount(); ++i) + ret << m_model->item(i)->text(); + + return ret; +} + +void MemcheckConfigWidget::slotSuppressionSelectionChanged() +{ + m_ui->removeSuppression->setEnabled(m_ui->suppressionList->selectionModel()->hasSelection()); +} + +} // namespace Internal +} // namespace Valgrind diff --git a/src/plugins/valgrind/memcheckconfigwidget.h b/src/plugins/valgrind/memcheckconfigwidget.h new file mode 100644 index 0000000000..e3d806d75a --- /dev/null +++ b/src/plugins/valgrind/memcheckconfigwidget.h @@ -0,0 +1,80 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef ANALYZER_INTERNAL_MEMCHECKCONFIGWIDGET_H +#define ANALYZER_INTERNAL_MEMCHECKCONFIGWIDGET_H + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE +class QStandardItemModel; +QT_END_NAMESPACE + +namespace Valgrind { +namespace Internal { + +namespace Ui { +class MemcheckConfigWidget; +} + +class AbstractMemcheckSettings; + +class MemcheckConfigWidget : public QWidget +{ + Q_OBJECT + +public: + MemcheckConfigWidget(AbstractMemcheckSettings *settings, QWidget *parent); + virtual ~MemcheckConfigWidget(); + + void setSuppressions(const QStringList &files); + QStringList suppressions() const; + +public Q_SLOTS: + void slotAddSuppression(); + void slotRemoveSuppression(); + void slotSuppressionsRemoved(const QStringList &files); + void slotSuppressionsAdded(const QStringList &files); + void slotSuppressionSelectionChanged(); + +private: + AbstractMemcheckSettings *m_settings; + QStandardItemModel *m_model; + Ui::MemcheckConfigWidget *m_ui; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // ANALYZER_INTERNAL_MEMCHECKCONFIGWIDGET_H diff --git a/src/plugins/valgrind/memcheckconfigwidget.ui b/src/plugins/valgrind/memcheckconfigwidget.ui new file mode 100644 index 0000000000..1763dbfbfb --- /dev/null +++ b/src/plugins/valgrind/memcheckconfigwidget.ui @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Valgrind::Internal::MemcheckConfigWidget</class> + <widget class="QWidget" name="Valgrind::Internal::MemcheckConfigWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>416</width> + <height>565</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="memcheckOptions"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Memory Analysis Options</string> + </property> + <layout class="QFormLayout" name="formLayout_4"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <item row="2" column="0"> + <widget class="QLabel" name="numCallersLabel"> + <property name="text"> + <string>Backtrace frame count:</string> + </property> + <property name="buddy"> + <cstring>numCallers</cstring> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Suppression files:</string> + </property> + <property name="buddy"> + <cstring>suppressionList</cstring> + </property> + </widget> + </item> + <item row="4" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QListView" name="suppressionList"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,1"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinimumSize</enum> + </property> + <item> + <widget class="QPushButton" name="addSuppression"> + <property name="text"> + <string>Add...</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removeSuppression"> + <property name="text"> + <string>Remove</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + <item row="2" column="1"> + <widget class="QSpinBox" name="numCallers"> + <property name="minimum"> + <number>5</number> + </property> + <property name="maximum"> + <number>50</number> + </property> + <property name="value"> + <number>12</number> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="QCheckBox" name="trackOrigins"> + <property name="text"> + <string>Track origins of uninitialized memory</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/valgrind/memcheckengine.cpp b/src/plugins/valgrind/memcheckengine.cpp new file mode 100644 index 0000000000..96d6edd32c --- /dev/null +++ b/src/plugins/valgrind/memcheckengine.cpp @@ -0,0 +1,148 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Nicolas Arnaud-Cormos, KDAB (nicolas.arnaud-cormos@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "memcheckengine.h" + +#include "memchecksettings.h" + +#include <analyzerbase/analyzersettings.h> + +#include <valgrind/xmlprotocol/error.h> +#include <valgrind/xmlprotocol/status.h> + +#include <utils/qtcassert.h> + +using namespace Analyzer; +using namespace Valgrind::XmlProtocol; + +namespace Valgrind { +namespace Internal { + +MemcheckEngine::MemcheckEngine(const Analyzer::AnalyzerStartParameters &sp, + ProjectExplorer::RunConfiguration *runConfiguration) + : ValgrindEngine(sp, runConfiguration) +{ + connect(&m_parser, SIGNAL(error(Valgrind::XmlProtocol::Error)), + SIGNAL(parserError(Valgrind::XmlProtocol::Error))); + connect(&m_parser, SIGNAL(suppressionCount(QString,qint64)), + SIGNAL(suppressionCount(QString,qint64))); + connect(&m_parser, SIGNAL(internalError(QString)), + SIGNAL(internalParserError(QString))); + connect(&m_parser, SIGNAL(status(Valgrind::XmlProtocol::Status)), + SLOT(status(Valgrind::XmlProtocol::Status))); + + m_progress->setProgressRange(0, Valgrind::XmlProtocol::Status::Finished + 1); +} + +QString MemcheckEngine::progressTitle() const +{ + return tr("Analyzing Memory"); +} + +Valgrind::ValgrindRunner *MemcheckEngine::runner() +{ + return &m_runner; +} + +void MemcheckEngine::start() +{ + m_runner.setParser(&m_parser); + + emit standardOutputReceived(tr("Analyzing memory of %1").arg(executable())); + ValgrindEngine::start(); +} + +void MemcheckEngine::stop() +{ + disconnect(&m_parser, SIGNAL(internalError(QString)), + this, SIGNAL(internalParserError(QString))); + ValgrindEngine::stop(); +} + +QStringList MemcheckEngine::toolArguments() const +{ + QStringList arguments; + arguments << QLatin1String("--gen-suppressions=all"); + + AbstractMemcheckSettings *memcheckSettings = m_settings->subConfig<AbstractMemcheckSettings>(); + QTC_ASSERT(memcheckSettings, return arguments); + + if (memcheckSettings->trackOrigins()) + arguments << QLatin1String("--track-origins=yes"); + + foreach (const QString &file, memcheckSettings->suppressionFiles()) + arguments << QString("--suppressions=%1").arg(file); + + arguments << QString("--num-callers=%1").arg(memcheckSettings->numCallers()); + return arguments; +} + +QStringList MemcheckEngine::suppressionFiles() const +{ + return m_settings->subConfig<AbstractMemcheckSettings>()->suppressionFiles(); +} + +void MemcheckEngine::status(const Status &status) +{ + m_progress->setProgressValue(status.state() + 1); +} + +void MemcheckEngine::receiveLogMessage(const QByteArray &b) +{ + QString error = QString::fromLocal8Bit(b); + // workaround https://bugs.kde.org/show_bug.cgi?id=255888 + error.remove(QRegExp("==*== </valgrindoutput>", Qt::CaseSensitive, QRegExp::Wildcard)); + + error = error.trimmed(); + + if (error.isEmpty()) + return; + + stop(); + + QString file; + int line = -1; + + const QRegExp suppressionError(QLatin1String("in suppressions file \"([^\"]+)\" near line (\\d+)"), + Qt::CaseSensitive, QRegExp::RegExp2); + if (suppressionError.indexIn(error) != -1) { + file = suppressionError.cap(1); + line = suppressionError.cap(2).toInt(); + } + + emit taskToBeAdded(ProjectExplorer::Task::Error, error, file, line); +} + +} // namespace Internal +} // namespace Valgrind diff --git a/src/plugins/valgrind/memcheckengine.h b/src/plugins/valgrind/memcheckengine.h new file mode 100644 index 0000000000..de21bdf5cd --- /dev/null +++ b/src/plugins/valgrind/memcheckengine.h @@ -0,0 +1,80 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Nicolas Arnaud-Cormos, KDAB (nicolas.arnaud-cormos@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef MEMCHECKENGINE_H +#define MEMCHECKENGINE_H + +#include "valgrindengine.h" + +#include <valgrind/memcheck/memcheckrunner.h> +#include <valgrind/xmlprotocol/threadedparser.h> + +namespace Valgrind { +namespace Internal { + +class MemcheckEngine : public ValgrindEngine +{ + Q_OBJECT +public: + explicit MemcheckEngine(const Analyzer::AnalyzerStartParameters &sp, + ProjectExplorer::RunConfiguration *runConfiguration); + + void start(); + void stop(); + + QStringList suppressionFiles() const; + +signals: + void internalParserError(const QString &errorString); + void parserError(const Valgrind::XmlProtocol::Error &error); + void suppressionCount(const QString &name, qint64 count); + +protected: + virtual QString progressTitle() const; + virtual QStringList toolArguments() const; + virtual Valgrind::ValgrindRunner *runner(); + +private slots: + void receiveLogMessage(const QByteArray &); + void status(const Valgrind::XmlProtocol::Status &status); + +private: + Valgrind::XmlProtocol::ThreadedParser m_parser; + Valgrind::Memcheck::MemcheckRunner m_runner; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // MEMCHECKENGINE_H diff --git a/src/plugins/valgrind/memcheckerrorview.cpp b/src/plugins/valgrind/memcheckerrorview.cpp new file mode 100644 index 0000000000..e89595a4fd --- /dev/null +++ b/src/plugins/valgrind/memcheckerrorview.cpp @@ -0,0 +1,511 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Andreas Hartmetz, KDAB (andreas.hartmetz@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "memcheckerrorview.h" + +#include "suppressiondialog.h" +#include "valgrindsettings.h" + +#include <valgrind/xmlprotocol/error.h> +#include <valgrind/xmlprotocol/errorlistmodel.h> +#include <valgrind/xmlprotocol/frame.h> +#include <valgrind/xmlprotocol/stack.h> +#include <valgrind/xmlprotocol/modelhelpers.h> +#include <valgrind/xmlprotocol/suppression.h> + +#include <texteditor/basetexteditor.h> + +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/project.h> +#include <coreplugin/coreconstants.h> + +#include <utils/qtcassert.h> + +#include <QtCore/QDir> +#include <QtCore/QDebug> + +#include <QtGui/QLabel> +#include <QtGui/QListView> +#include <QtGui/QPainter> +#include <QtGui/QScrollBar> +#include <QtGui/QSortFilterProxyModel> +#include <QtGui/QVBoxLayout> +#include <QtGui/QAction> +#include <QtGui/QClipboard> +#include <QtGui/QApplication> +#include <QtGui/QMenu> + +using namespace Valgrind::XmlProtocol; + +namespace Valgrind { +namespace Internal { + +MemcheckErrorDelegate::MemcheckErrorDelegate(QListView *parent) + : QStyledItemDelegate(parent), + m_detailsWidget(0) +{ + connect(parent->verticalScrollBar(), SIGNAL(valueChanged(int)), + SLOT(verticalScrolled())); +} + +QSize MemcheckErrorDelegate::sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const +{ + const QListView *view = qobject_cast<const QListView *>(parent()); + const int viewportWidth = view->viewport()->width(); + const bool isSelected = view->selectionModel()->currentIndex() == index; + + int dy = 2 * s_itemMargin; + + if (!isSelected) { + QFontMetrics fm(opt.font); + return QSize(viewportWidth, fm.height() + dy); + } + + if (m_detailsWidget && m_detailsIndex != index) { + m_detailsWidget->deleteLater(); + m_detailsWidget = 0; + } + + if (!m_detailsWidget) { + m_detailsWidget = createDetailsWidget(index, view->viewport()); + QTC_ASSERT(m_detailsWidget->parent() == view->viewport(), + m_detailsWidget->setParent(view->viewport())); + m_detailsIndex = index; + } else { + QTC_ASSERT(m_detailsIndex == index, qt_noop()); + } + const int widthExcludingMargins = viewportWidth - 2 * s_itemMargin; + m_detailsWidget->setFixedWidth(widthExcludingMargins); + + m_detailsWidgetHeight = m_detailsWidget->heightForWidth(widthExcludingMargins); + // HACK: it's a bug in QLabel(?) that we have to force the widget to have the size it said + // it would have. + m_detailsWidget->setFixedHeight(m_detailsWidgetHeight); + return QSize(viewportWidth, dy + m_detailsWidget->heightForWidth(widthExcludingMargins)); +} + +static QString makeFrameName(const Frame &frame, const QString &relativeTo, + bool link = true, const QString &linkAttr = QString()) +{ + const QString d = frame.directory(); + const QString f = frame.file(); + const QString fn = frame.functionName(); + const QString fullPath = d + QDir::separator() + f; + + QString path; + if (!d.isEmpty() && !f.isEmpty()) + path = fullPath; + else + path = frame.object(); + + if (QFile::exists(path)) + path = QFileInfo(path).canonicalFilePath(); + + if (path.startsWith(relativeTo)) + path.remove(0, relativeTo.length()); + + if (frame.line() != -1) + path += QLatin1Char(':') + QString::number(frame.line()); + + path = Qt::escape(path); + + if (link && !f.isEmpty() && QFile::exists(fullPath)) { + // make a hyperlink label + path = QString("<a href=\"file://%1:%2\" %4>%3</a>") + .arg(fullPath, QString::number(frame.line()), path, linkAttr); + } + + if (!fn.isEmpty()) + return QCoreApplication::translate("Valgrind::Internal", "%1 in %2").arg(Qt::escape(fn), path); + if (!path.isEmpty()) + return path; + return QString("0x%1").arg(frame.instructionPointer(), 0, 16); +} + +QString relativeToPath() +{ + // project for which we insert the snippet + const ProjectExplorer::Project *project = + ProjectExplorer::ProjectExplorerPlugin::instance()->startupProject(); + + QString relativeTo( project ? project->projectDirectory() : QDir::homePath() ); + if (!relativeTo.endsWith(QDir::separator())) + relativeTo.append(QDir::separator()); + + return relativeTo; +} + +QString errorLocation(const QModelIndex &index, const Error &error, + bool link = false, const QString &linkAttr = QString()) +{ + const ErrorListModel *model = 0; + const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel *>(index.model()); + while (!model && proxy) { + model = qobject_cast<const ErrorListModel *>(proxy->sourceModel()); + proxy = qobject_cast<const QAbstractProxyModel *>(proxy->sourceModel()); + } + QTC_ASSERT(model, return QString()); + + return QCoreApplication::translate("Valgrind::Internal", "in %1"). + arg(makeFrameName(model->findRelevantFrame(error), relativeToPath(), + link, linkAttr)); +} + +QWidget *MemcheckErrorDelegate::createDetailsWidget(const QModelIndex &errorIndex, QWidget *parent) const +{ + QWidget *widget = new QWidget(parent); + QVBoxLayout *layout = new QVBoxLayout; + // code + white-space:pre so the padding (see below) works properly + // don't include frameName here as it should wrap if required and pre-line is not supported + // by Qt yet it seems + const QString displayTextTemplate = QString("<code style='white-space:pre'>%1:</code> %2"); + + QString relativeTo = relativeToPath(); + + const Error error = errorIndex.data(ErrorListModel::ErrorRole).value<Error>(); + + QLabel *errorLabel = new QLabel(); + errorLabel->setWordWrap(true); + errorLabel->setContentsMargins(0, 0, 0, 0); + errorLabel->setMargin(0); + errorLabel->setIndent(0); + QPalette p = errorLabel->palette(); + QColor lc = p.color(QPalette::Text); + QString linkStyle = QString("style=\"color:rgba(%1, %2, %3, %4);\"") + .arg(lc.red()).arg(lc.green()).arg(lc.blue()).arg(int(0.7 * 255)); + p.setBrush(QPalette::Text, p.highlightedText()); + errorLabel->setPalette(p); + errorLabel->setText(QString("%1 <span %4>%2</span>") + .arg(error.what(), errorLocation(errorIndex, error, true, linkStyle), + linkStyle)); + connect(errorLabel, SIGNAL(linkActivated(QString)), SLOT(openLinkInEditor(QString))); + layout->addWidget(errorLabel); + + const QVector<Stack> stacks = error.stacks(); + for (int i = 0; i < stacks.count(); ++i) { + const Stack &stack = stacks.at(i); + // auxwhat for additional stacks + if (i > 0) { + QLabel *stackLabel = new QLabel(stack.auxWhat()); + stackLabel->setWordWrap(true); + stackLabel->setContentsMargins(0, 0, 0, 0); + stackLabel->setMargin(0); + stackLabel->setIndent(0); + QPalette p = stackLabel->palette(); + p.setBrush(QPalette::Text, p.highlightedText()); + stackLabel->setPalette(p); + layout->addWidget(stackLabel); + } + int frameNr = 1; + foreach (const Frame &frame, stack.frames()) { + QString frameName = makeFrameName(frame, relativeTo); + QTC_ASSERT(!frameName.isEmpty(), qt_noop()); + + QLabel *frameLabel = new QLabel(widget); + frameLabel->setAutoFillBackground(true); + if (frameNr % 2 == 0) { + // alternating rows + QPalette p = frameLabel->palette(); + p.setBrush(QPalette::Base, p.alternateBase()); + frameLabel->setPalette(p); + } + frameLabel->setFont(QFont("monospace")); + connect(frameLabel, SIGNAL(linkActivated(QString)), SLOT(openLinkInEditor(QString))); + // pad frameNr to 2 chars since only 50 frames max are supported by valgrind + const QString displayText = displayTextTemplate + .arg(frameNr++, 2).arg(frameName); + frameLabel->setText(displayText); + + frameLabel->setToolTip(Valgrind::XmlProtocol::toolTipForFrame(frame)); + frameLabel->setWordWrap(true); + frameLabel->setContentsMargins(0, 0, 0, 0); + frameLabel->setMargin(0); + frameLabel->setIndent(10); + layout->addWidget(frameLabel); + } + } + + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + widget->setLayout(layout); + return widget; +} + +void MemcheckErrorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &basicOption, + const QModelIndex &index) const +{ + QStyleOptionViewItemV4 opt(basicOption); + initStyleOption(&opt, index); + + const QListView *const view = qobject_cast<const QListView *>(parent()); + const bool isSelected = view->selectionModel()->currentIndex() == index; + + QFontMetrics fm(opt.font); + QPoint pos = opt.rect.topLeft(); + + painter->save(); + + const QColor bgColor = isSelected ? opt.palette.highlight().color() : opt.palette.background().color(); + painter->setBrush(bgColor); + + // clear background + painter->setPen(Qt::NoPen); + painter->drawRect(opt.rect); + + pos.rx() += s_itemMargin; + pos.ry() += s_itemMargin; + + const Error error = index.data(ErrorListModel::ErrorRole).value<Error>(); + + if (isSelected) { + // only show detailed widget and let it handle everything + QTC_ASSERT(m_detailsIndex == index, qt_noop()); + QTC_ASSERT(m_detailsWidget, return); // should have been set in sizeHint() + m_detailsWidget->move(pos); + // when scrolling quickly, the widget can get stuck in a visible part of the scroll area + // even though it should not be visible. therefore we hide it every time the scroll value + // changes and un-hide it when the item with details widget is paint()ed, i.e. visible. + m_detailsWidget->show(); + + const int viewportWidth = view->viewport()->width(); + const int widthExcludingMargins = viewportWidth - 2 * s_itemMargin; + QTC_ASSERT(m_detailsWidget->width() == widthExcludingMargins, qt_noop()); + QTC_ASSERT(m_detailsWidgetHeight == m_detailsWidget->height(), qt_noop()); + } else { + // the reference coordinate for text drawing is the text baseline; move it inside the view rect. + pos.ry() += fm.ascent(); + + const QColor textColor = opt.palette.text().color(); + painter->setPen(textColor); + // draw only text + location + const QString what = error.what(); + painter->drawText(pos, what); + + const QString name = errorLocation(index, error); + const int whatWidth = QFontMetrics(opt.font).width(what); + const int space = 10; + const int widthLeft = opt.rect.width() - (pos.x() + whatWidth + space + s_itemMargin); + if (widthLeft > 0) { + QFont monospace = opt.font; + monospace.setFamily("monospace"); + QFontMetrics metrics(monospace); + QColor nameColor = textColor; + nameColor.setAlphaF(0.7); + + painter->setFont(monospace); + painter->setPen(nameColor); + + QPoint namePos = pos; + namePos.rx() += whatWidth + space; + painter->drawText(namePos, metrics.elidedText(name, Qt::ElideLeft, widthLeft)); + } + } + + // Separator lines (like build issues pane) + painter->setPen(QColor::fromRgb(150,150,150)); + painter->drawLine(0, opt.rect.bottom(), opt.rect.right(), opt.rect.bottom()); + + painter->restore(); +} + +void MemcheckErrorDelegate::currentChanged(const QModelIndex &now, const QModelIndex &previous) +{ + if (m_detailsWidget) { + m_detailsWidget->deleteLater(); + m_detailsWidget = 0; + } + + m_detailsIndex = QModelIndex(); + if (now.isValid()) + emit sizeHintChanged(now); + if (previous.isValid()) + emit sizeHintChanged(previous); +} + +void MemcheckErrorDelegate::layoutChanged() +{ + if (m_detailsWidget) { + m_detailsWidget->deleteLater(); + m_detailsWidget = 0; + m_detailsIndex = QModelIndex(); + } +} + +void MemcheckErrorDelegate::viewResized() +{ + const QListView *view = qobject_cast<const QListView *>(parent()); + if (m_detailsWidget) + emit sizeHintChanged(view->selectionModel()->currentIndex()); +} + +void MemcheckErrorDelegate::verticalScrolled() +{ + if (m_detailsWidget) + m_detailsWidget->hide(); +} + +void MemcheckErrorDelegate::copy() +{ + QTC_ASSERT(m_detailsIndex.isValid(), return); + + QString content; + QTextStream stream(&content); + const Error error = m_detailsIndex.data(ErrorListModel::ErrorRole).value<Error>(); + + stream << error.what() << "\n"; + stream << " " << errorLocation(m_detailsIndex, error) << "\n"; + + const QString relativeTo = relativeToPath(); + + foreach (const Stack &stack, error.stacks()) { + if (!stack.auxWhat().isEmpty()) { + stream << stack.auxWhat(); + } + int i = 1; + foreach (const Frame &frame, stack.frames()) { + stream << " " << i++ << ": " << makeFrameName(frame, relativeTo) << "\n"; + } + } + + stream.flush(); + QApplication::clipboard()->setText(content); +} + +void MemcheckErrorDelegate::openLinkInEditor(const QString &link) +{ + const int pathStart = strlen("file://"); + const int pathEnd = link.lastIndexOf(':'); + const QString path = link.mid(pathStart, pathEnd - pathStart); + const int line = link.mid(pathEnd + 1).toInt(0); + TextEditor::BaseTextEditorWidget::openEditorAt(path, qMax(line, 0)); +} + +MemcheckErrorView::MemcheckErrorView(QWidget *parent) + : QListView(parent), + m_settings(0) +{ + setItemDelegate(new MemcheckErrorDelegate(this)); + connect(this, SIGNAL(resized()), itemDelegate(), SLOT(viewResized())); + + m_copyAction = new QAction(this); + m_copyAction->setText(tr("Copy Selection")); + m_copyAction->setIcon(QIcon(Core::Constants::ICON_COPY)); + m_copyAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C)); + m_copyAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(m_copyAction, SIGNAL(triggered()), itemDelegate(), SLOT(copy())); + addAction(m_copyAction); + + m_suppressAction = new QAction(this); + m_suppressAction->setText(tr("Suppress Error")); + m_suppressAction->setIcon(QIcon(QLatin1String(":/qmldesigner/images/eye_crossed.png"))); + m_suppressAction->setShortcut(QKeySequence(Qt::Key_Delete)); + m_suppressAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(m_suppressAction, SIGNAL(triggered()), this, SLOT(suppressError())); + addAction(m_suppressAction); +} + +MemcheckErrorView::~MemcheckErrorView() +{ + itemDelegate()->deleteLater(); +} + +void MemcheckErrorView::setModel(QAbstractItemModel *model) +{ + QListView::setModel(model); + connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + itemDelegate(), SLOT(currentChanged(QModelIndex,QModelIndex))); + + connect(model, SIGNAL(layoutChanged()), + itemDelegate(), SLOT(layoutChanged())); +} + +void MemcheckErrorView::resizeEvent(QResizeEvent *e) +{ + emit resized(); + QListView::resizeEvent(e); +} + +void MemcheckErrorView::setDefaultSuppressionFile(const QString &suppFile) +{ + m_defaultSuppFile = suppFile; +} + +QString MemcheckErrorView::defaultSuppressionFile() const +{ + return m_defaultSuppFile; +} + +// slot, can (for now) be invoked either when the settings were modified *or* when the active +// settings object has changed. +void MemcheckErrorView::settingsChanged(Analyzer::AnalyzerSettings *settings) +{ + QTC_ASSERT(settings, return); + m_settings = settings; +} + +void MemcheckErrorView::contextMenuEvent(QContextMenuEvent *e) +{ + const QModelIndexList indizes = selectionModel()->selectedRows(); + if (indizes.isEmpty()) + return; + + QList<Error> errors; + foreach (const QModelIndex &index, indizes) { + Error error = model()->data(index, ErrorListModel::ErrorRole).value<Error>(); + if (!error.suppression().isNull()) + errors << error; + } + + QMenu menu; + menu.addAction(m_copyAction); + menu.addSeparator(); + menu.addAction(m_suppressAction); + m_suppressAction->setEnabled(!errors.isEmpty()); + menu.exec(e->globalPos()); +} + +void MemcheckErrorView::suppressError() +{ + SuppressionDialog *dialog = new SuppressionDialog(this); + if (dialog->shouldShow()) { + dialog->setModal(true); + dialog->show(); + dialog->setAttribute(Qt::WA_DeleteOnClose, true); + } else { + delete dialog; + } +} + +} // namespace Internal +} // namespace Valgrind diff --git a/src/plugins/valgrind/memcheckerrorview.h b/src/plugins/valgrind/memcheckerrorview.h new file mode 100644 index 0000000000..d36453684a --- /dev/null +++ b/src/plugins/valgrind/memcheckerrorview.h @@ -0,0 +1,125 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Andreas Hartmetz, KDAB (andreas.hartmetz@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef MEMCHECKERRORVIEW_H +#define MEMCHECKERRORVIEW_H + +#include <QtGui/QListView> +#include <QtGui/QStyledItemDelegate> +#include <QtGui/QLabel> + +namespace Analyzer { +class AnalyzerSettings; +} + +namespace ProjectExplorer { +class Project; +} + +namespace Valgrind { +namespace Internal { + +class MemcheckErrorDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + /// This delegate can only work on one view at a time, parent. parent will also be the parent + /// in the QObject parent-child system. + explicit MemcheckErrorDelegate(QListView *parent); + + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + +public slots: + void currentChanged(const QModelIndex &now, const QModelIndex &previous); + void viewResized(); + void layoutChanged(); + void copy(); + +private slots: + void verticalScrolled(); + void openLinkInEditor(const QString &link); + +private: + // the constness of this method is a necessary lie because it is called from paint() const. + QWidget *createDetailsWidget(const QModelIndex &errorIndex, QWidget *parent) const; + + static const int s_itemMargin = 2; + mutable QPersistentModelIndex m_detailsIndex; + mutable QWidget *m_detailsWidget; + mutable int m_detailsWidgetHeight; +}; + +class MemcheckErrorView : public QListView +{ + Q_OBJECT + +public: + MemcheckErrorView(QWidget *parent = 0); + ~MemcheckErrorView(); + + // reimplemented to connect delegate to connection model after it has been set by + // superclass implementation + void setModel(QAbstractItemModel *model); + + void setDefaultSuppressionFile(const QString &suppFile); + QString defaultSuppressionFile() const; + Analyzer::AnalyzerSettings *settings() const { return m_settings; } + +signals: + void resized(); + +public slots: + void settingsChanged(Analyzer::AnalyzerSettings *settings); + +private slots: + void suppressError(); + +protected: + void resizeEvent(QResizeEvent *e); + void contextMenuEvent(QContextMenuEvent *e); + +private: + QAction *m_copyAction; + QAction *m_suppressAction; + QString m_defaultSuppFile; + Analyzer::AnalyzerSettings *m_settings; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // MEMCHECKERRORVIEW_H diff --git a/src/plugins/valgrind/memchecksettings.cpp b/src/plugins/valgrind/memchecksettings.cpp new file mode 100644 index 0000000000..664f59973b --- /dev/null +++ b/src/plugins/valgrind/memchecksettings.cpp @@ -0,0 +1,280 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "memchecksettings.h" +#include "memcheckconfigwidget.h" + +#include <valgrind/xmlprotocol/error.h> + +#include <utils/qtcassert.h> + +using namespace Analyzer; + +static const char numCallersC[] = "Analyzer.Valgrind.NumCallers"; +static const char trackOriginsC[] = "Analyzer.Valgrind.TrackOrigins"; +static const char suppressionFilesC[] = "Analyzer.Valgrind.SupressionFiles"; +static const char removedSuppressionFilesC[] = "Analyzer.Valgrind.RemovedSuppressionFiles"; +static const char addedSuppressionFilesC[] = "Analyzer.Valgrind.AddedSuppressionFiles"; +static const char filterExternalIssuesC[] = "Analyzer.Valgrind.FilterExternalIssues"; +static const char visibleErrorKindsC[] = "Analyzer.Valgrind.VisibleErrorKinds"; + +static const char lastSuppressionDirectoryC[] = "Analyzer.Valgrind.LastSuppressionDirectory"; +static const char lastSuppressionHistoryC[] = "Analyzer.Valgrind.LastSuppressionHistory"; + +namespace Valgrind { +namespace Internal { + +QVariantMap AbstractMemcheckSettings::defaults() const +{ + QVariantMap map; + map.insert(QLatin1String(numCallersC), 25); + map.insert(QLatin1String(trackOriginsC), true); + map.insert(QLatin1String(filterExternalIssuesC), true); + + QVariantList defaultErrorKinds; + for (int i = 0; i < Valgrind::XmlProtocol::MemcheckErrorKindCount; ++i) + defaultErrorKinds << i; + map.insert(QLatin1String(visibleErrorKindsC), defaultErrorKinds); + + return map; +} + +bool AbstractMemcheckSettings::fromMap(const QVariantMap &map) +{ + setIfPresent(map, QLatin1String(numCallersC), &m_numCallers); + setIfPresent(map, QLatin1String(trackOriginsC), &m_trackOrigins); + setIfPresent(map, QLatin1String(filterExternalIssuesC), &m_filterExternalIssues); + + // if we get more of these try a template specialization of setIfPresent for lists... + if (map.contains(QLatin1String(visibleErrorKindsC))) { + m_visibleErrorKinds.clear(); + foreach (const QVariant &val, map.value(QLatin1String(visibleErrorKindsC)).toList()) + m_visibleErrorKinds << val.toInt(); + } + + return true; +} + +QVariantMap AbstractMemcheckSettings::toMap() const +{ + QVariantMap map; + map.insert(QLatin1String(numCallersC), m_numCallers); + map.insert(QLatin1String(trackOriginsC), m_trackOrigins); + map.insert(QLatin1String(filterExternalIssuesC), m_filterExternalIssues); + + QVariantList errorKinds; + foreach (int i, m_visibleErrorKinds) + errorKinds << i; + map.insert(QLatin1String(visibleErrorKindsC), errorKinds); + + return map; +} + +void AbstractMemcheckSettings::setNumCallers(int numCallers) +{ + if (m_numCallers != numCallers) { + m_numCallers = numCallers; + emit numCallersChanged(numCallers); + } +} + +void AbstractMemcheckSettings::setTrackOrigins(bool trackOrigins) +{ + if (m_trackOrigins != trackOrigins) { + m_trackOrigins = trackOrigins; + emit trackOriginsChanged(trackOrigins); + } +} + +void AbstractMemcheckSettings::setFilterExternalIssues(bool filterExternalIssues) +{ + if (m_filterExternalIssues != filterExternalIssues) { + m_filterExternalIssues = filterExternalIssues; + emit filterExternalIssuesChanged(filterExternalIssues); + } +} + +void AbstractMemcheckSettings::setVisibleErrorKinds(const QList<int> &visibleErrorKinds) +{ + if (m_visibleErrorKinds != visibleErrorKinds) { + m_visibleErrorKinds = visibleErrorKinds; + emit visibleErrorKindsChanged(visibleErrorKinds); + } +} + +QString AbstractMemcheckSettings::id() const +{ + return "Analyzer.Valgrind.Settings.Memcheck"; +} + +QString AbstractMemcheckSettings::displayName() const +{ + return tr("Memory Analysis"); +} + +QWidget *AbstractMemcheckSettings::createConfigWidget(QWidget *parent) +{ + return new MemcheckConfigWidget(this, parent); +} + +QStringList MemcheckGlobalSettings::suppressionFiles() const +{ + return m_suppressionFiles; +} + +void MemcheckGlobalSettings::addSuppressionFiles(const QStringList &suppressions) +{ + foreach (const QString &s, suppressions) + if (!m_suppressionFiles.contains(s)) + m_suppressionFiles.append(s); +} + +void MemcheckGlobalSettings::removeSuppressionFiles(const QStringList &suppressions) +{ + foreach (const QString &s, suppressions) + m_suppressionFiles.removeAll(s); +} + +QString MemcheckGlobalSettings::lastSuppressionDialogDirectory() const +{ + return m_lastSuppressionDirectory; +} + +void MemcheckGlobalSettings::setLastSuppressionDialogDirectory(const QString &directory) +{ + m_lastSuppressionDirectory = directory; +} + +QStringList MemcheckGlobalSettings::lastSuppressionDialogHistory() const +{ + return m_lastSuppressionHistory; +} + +void MemcheckGlobalSettings::setLastSuppressionDialogHistory(const QStringList &history) +{ + m_lastSuppressionHistory = history; +} + +QVariantMap MemcheckGlobalSettings::defaults() const +{ + QVariantMap ret = AbstractMemcheckSettings::defaults(); + ret.insert(suppressionFilesC, QStringList()); + ret.insert(lastSuppressionDirectoryC, QString()); + ret.insert(lastSuppressionHistoryC, QStringList()); + return ret; +} + +bool MemcheckGlobalSettings::fromMap(const QVariantMap &map) +{ + AbstractMemcheckSettings::fromMap(map); + m_suppressionFiles = map.value(suppressionFilesC).toStringList(); + m_lastSuppressionDirectory = map.value(lastSuppressionDirectoryC).toString(); + m_lastSuppressionHistory = map.value(lastSuppressionHistoryC).toStringList(); + return true; +} + +QVariantMap MemcheckGlobalSettings::toMap() const +{ + QVariantMap map = AbstractMemcheckSettings::toMap(); + map.insert(suppressionFilesC, m_suppressionFiles); + map.insert(lastSuppressionDirectoryC, m_lastSuppressionDirectory); + map.insert(lastSuppressionHistoryC, m_lastSuppressionHistory); + return map; +} + +MemcheckGlobalSettings *globalMemcheckSettings() +{ + MemcheckGlobalSettings *ret = AnalyzerGlobalSettings::instance()->subConfig<MemcheckGlobalSettings>(); + QTC_ASSERT(ret, return 0); + return ret; +} + + +QVariantMap MemcheckProjectSettings::defaults() const +{ + QVariantMap ret = AbstractMemcheckSettings::defaults(); + ret.insert(addedSuppressionFilesC, QStringList()); + ret.insert(removedSuppressionFilesC, QStringList()); + return ret; +} + +bool MemcheckProjectSettings::fromMap(const QVariantMap &map) +{ + AbstractMemcheckSettings::fromMap(map); + setIfPresent(map, addedSuppressionFilesC, &m_addedSuppressionFiles); + setIfPresent(map, removedSuppressionFilesC, &m_disabledGlobalSuppressionFiles); + return true; +} + +QVariantMap MemcheckProjectSettings::toMap() const +{ + QVariantMap map = AbstractMemcheckSettings::toMap(); + map.insert(addedSuppressionFilesC, m_addedSuppressionFiles); + map.insert(removedSuppressionFilesC, m_disabledGlobalSuppressionFiles); + return map; +} + +void MemcheckProjectSettings::addSuppressionFiles(const QStringList &suppressions) +{ + QStringList globalSuppressions = globalMemcheckSettings()->suppressionFiles(); + foreach (const QString &s, suppressions) { + if (m_addedSuppressionFiles.contains(s)) + continue; + m_disabledGlobalSuppressionFiles.removeAll(s); + if (!globalSuppressions.contains(s)) + m_addedSuppressionFiles.append(s); + } +} + +void MemcheckProjectSettings::removeSuppressionFiles(const QStringList &suppressions) +{ + QStringList globalSuppressions = globalMemcheckSettings()->suppressionFiles(); + foreach (const QString &s, suppressions) { + m_addedSuppressionFiles.removeAll(s); + if (globalSuppressions.contains(s)) + m_disabledGlobalSuppressionFiles.append(s); + } +} + +QStringList MemcheckProjectSettings::suppressionFiles() const +{ + QStringList ret = globalMemcheckSettings()->suppressionFiles(); + foreach (const QString &s, m_disabledGlobalSuppressionFiles) + ret.removeAll(s); + ret.append(m_addedSuppressionFiles); + return ret; +} + +} // namespace Internal +} // namespace Valgrind diff --git a/src/plugins/valgrind/memchecksettings.h b/src/plugins/valgrind/memchecksettings.h new file mode 100644 index 0000000000..7d4a2f3f79 --- /dev/null +++ b/src/plugins/valgrind/memchecksettings.h @@ -0,0 +1,152 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef ANALYZER_INTERNAL_MEMCHECKSETTINGS_H +#define ANALYZER_INTERNAL_MEMCHECKSETTINGS_H + +#include <analyzerbase/analyzersettings.h> + +namespace Valgrind { +namespace Internal { + +/** + * Generic memcheck settings + */ +class AbstractMemcheckSettings : public Analyzer::AbstractAnalyzerSubConfig +{ + Q_OBJECT + +public: + AbstractMemcheckSettings() {} + + virtual bool fromMap(const QVariantMap &map); + + int numCallers() const { return m_numCallers; } + bool trackOrigins() const { return m_trackOrigins; } + bool filterExternalIssues() const { return m_filterExternalIssues; } + QList<int> visibleErrorKinds() const { return m_visibleErrorKinds; } + + virtual QStringList suppressionFiles() const = 0; + virtual void addSuppressionFiles(const QStringList &) = 0; + virtual void removeSuppressionFiles(const QStringList &) = 0; + + virtual QVariantMap defaults() const; + + virtual QString id() const; + virtual QString displayName() const; + virtual QWidget *createConfigWidget(QWidget *parent); + +public slots: + void setNumCallers(int); + void setTrackOrigins(bool); + void setFilterExternalIssues(bool); + void setVisibleErrorKinds(const QList<int> &); + +signals: + void numCallersChanged(int); + void trackOriginsChanged(bool); + void filterExternalIssuesChanged(bool); + void visibleErrorKindsChanged(const QList<int> &); + void suppressionFilesRemoved(const QStringList &); + void suppressionFilesAdded(const QStringList &); + +protected: + virtual QVariantMap toMap() const; + + int m_numCallers; + bool m_trackOrigins; + bool m_filterExternalIssues; + QList<int> m_visibleErrorKinds; +}; + +/** + * Global memcheck settings + */ +class MemcheckGlobalSettings : public AbstractMemcheckSettings +{ +public: + MemcheckGlobalSettings() {} + + QStringList suppressionFiles() const; + // in the global settings we change the internal list directly + void addSuppressionFiles(const QStringList &); + void removeSuppressionFiles(const QStringList &); + + QVariantMap toMap() const; + QVariantMap defaults() const; + + // internal settings which don't require any UI + void setLastSuppressionDialogDirectory(const QString &directory); + QString lastSuppressionDialogDirectory() const; + + void setLastSuppressionDialogHistory(const QStringList &history); + QStringList lastSuppressionDialogHistory() const; + +protected: + bool fromMap(const QVariantMap &map); + +private: + QStringList m_suppressionFiles; + QString m_lastSuppressionDirectory; + QStringList m_lastSuppressionHistory; +}; + +/** + * Per-project memcheck settings, saves a diff to the global suppression files list + */ +class MemcheckProjectSettings : public AbstractMemcheckSettings +{ +public: + MemcheckProjectSettings() {} + + QStringList suppressionFiles() const; + // in the project-specific settings we store a diff to the global list + void addSuppressionFiles(const QStringList &suppressions); + void removeSuppressionFiles(const QStringList &suppressions); + + QVariantMap toMap() const; + QVariantMap defaults() const; + +protected: + bool fromMap(const QVariantMap &map); + +private: + QStringList m_disabledGlobalSuppressionFiles; + QStringList m_addedSuppressionFiles; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // ANALYZER_INTERNAL_MEMCHECKSETTINGS_H diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp new file mode 100644 index 0000000000..5388662e89 --- /dev/null +++ b/src/plugins/valgrind/memchecktool.cpp @@ -0,0 +1,549 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Nicolas Arnaud-Cormos, KDAB (nicolas.arnaud-cormos@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "memchecktool.h" +#include "memcheckengine.h" +#include "memcheckerrorview.h" +#include "memchecksettings.h" +#include "valgrindsettings.h" + +#include <analyzerbase/analyzermanager.h> +#include <analyzerbase/analyzerconstants.h> +#include <analyzerbase/ianalyzeroutputpaneadapter.h> + +#include <valgrind/xmlprotocol/errorlistmodel.h> +#include <valgrind/xmlprotocol/stackmodel.h> +#include <valgrind/xmlprotocol/error.h> +#include <valgrind/xmlprotocol/frame.h> +#include <valgrind/xmlprotocol/stack.h> +#include <valgrind/xmlprotocol/suppression.h> + +#include <extensionsystem/iplugin.h> +#include <extensionsystem/pluginmanager.h> + +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/project.h> +#include <projectexplorer/runconfiguration.h> +#include <projectexplorer/target.h> +#include <projectexplorer/session.h> +#include <projectexplorer/buildconfiguration.h> + +#include <coreplugin/coreconstants.h> +#include <coreplugin/icore.h> +#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/actionmanager/actioncontainer.h> +#include <coreplugin/actionmanager/command.h> +#include <coreplugin/uniqueidmanager.h> + +#include <texteditor/basetexteditor.h> + +#include <utils/fancymainwindow.h> +#include <utils/styledbar.h> +#include <utils/qtcassert.h> + +#include <QtCore/QString> +#include <QtCore/QLatin1String> +#include <QtCore/QFileInfo> +#include <QtCore/QFile> +#include <QtCore/QDir> + +#include <QtGui/QDockWidget> +#include <QtGui/QHBoxLayout> +#include <QtGui/QComboBox> +#include <QtGui/QLabel> +#include <QtGui/QSpinBox> +#include <QtGui/QAction> +#include <QtGui/QMenu> +#include <QtGui/QMessageBox> +#include <QtGui/QToolButton> +#include <QtGui/QCheckBox> +#include <utils/stylehelper.h> + +using namespace Analyzer; +using namespace Valgrind::XmlProtocol; + +namespace Valgrind { +namespace Internal { + +// Adapter for output pane. +class MemCheckOutputPaneAdapter : public Analyzer::ListItemViewOutputPaneAdapter +{ +public: + explicit MemCheckOutputPaneAdapter(MemcheckTool *mct) : + ListItemViewOutputPaneAdapter(mct), m_tool(mct) {} + + virtual QWidget *toolBarWidget() { return m_tool->createPaneToolBarWidget(); } + virtual void clearContents() { m_tool->clearErrorView(); } + +protected: + virtual QAbstractItemView *createItemView() { return m_tool->ensurePaneErrorView(); } + +private: + MemcheckTool *m_tool; +}; + +// ---------------------------- MemcheckErrorFilterProxyModel +MemcheckErrorFilterProxyModel::MemcheckErrorFilterProxyModel(QObject *parent) + : QSortFilterProxyModel(parent), + m_filterExternalIssues(false) +{ +} + +void MemcheckErrorFilterProxyModel::setAcceptedKinds(const QList<int> &acceptedKinds) +{ + if (m_acceptedKinds != acceptedKinds) { + m_acceptedKinds = acceptedKinds; + invalidate(); + } +} + +void MemcheckErrorFilterProxyModel::setFilterExternalIssues(bool filter) +{ + if (m_filterExternalIssues != filter) { + m_filterExternalIssues = filter; + invalidate(); + } +} + +bool MemcheckErrorFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + // we only deal with toplevel items + if (sourceParent.isValid()) + return true; + + // because toplevel items have no parent, we can't use sourceParent to find them. we just use + // sourceParent as an invalid index, telling the model that the index we're looking for has no + // parent. + QAbstractItemModel *model = sourceModel(); + QModelIndex sourceIndex = model->index(sourceRow, filterKeyColumn(), sourceParent); + if (!sourceIndex.isValid()) + return true; + + const Error error = sourceIndex.data(ErrorListModel::ErrorRole).value<Error>(); + + // filter on kind + if (!m_acceptedKinds.contains(error.kind())) + return false; + + // filter non-project stuff + if (m_filterExternalIssues && !error.stacks().isEmpty()) { + // ALGORITHM: look at last five stack frames, if none of these is inside any open projects, + // assume this error was created by an external library + ProjectExplorer::SessionManager *session + = ProjectExplorer::ProjectExplorerPlugin::instance()->session(); + QSet<QString> validFolders; + foreach (ProjectExplorer::Project *project, session->projects()) { + validFolders << project->projectDirectory(); + foreach (ProjectExplorer::Target *target, project->targets()) { + foreach (ProjectExplorer::BuildConfiguration *config, target->buildConfigurations()) { + validFolders << config->buildDirectory(); + } + } + } + + const QVector< Frame > frames = error.stacks().first().frames(); + + const int framesToLookAt = qMin(6, frames.size()); + + bool inProject = false; + for ( int i = 0; i < framesToLookAt; ++i ) { + const Frame &frame = frames.at(i); + foreach (const QString &folder, validFolders) { + if (frame.object().startsWith(folder)) { + inProject = true; + break; + } + } + } + if (!inProject) + return false; + } + + return true; +} + +static void initKindFilterAction(QAction *action, const QList<int> &kinds) +{ + action->setCheckable(true); + QVariantList data; + foreach (int kind, kinds) + data << kind; + action->setData(data); +} + +MemcheckTool::MemcheckTool(QObject *parent) : + Analyzer::IAnalyzerTool(parent), + m_settings(0), + m_errorModel(0), + m_errorProxyModel(0), + m_errorView(0), + m_filterProjectAction(new QAction(tr("External Errors"), this)), + m_suppressionSeparator(new QAction(tr("Suppressions"), this)), + m_outputPaneAdapter(0) +{ + setObjectName(QLatin1String("MemcheckTool")); + connect(ProjectExplorer::ProjectExplorerPlugin::instance(), + SIGNAL(updateRunActions()), SLOT(maybeActiveRunConfigurationChanged())); + + QAction *a = new QAction(tr("Definite Memory Leaks"), this); + initKindFilterAction(a, QList<int>() << Leak_DefinitelyLost << Leak_IndirectlyLost); + m_errorFilterActions << a; + + a = new QAction(tr("Possible Memory Leaks"), this); + initKindFilterAction(a, QList<int>() << Leak_PossiblyLost << Leak_StillReachable); + m_errorFilterActions << a; + + a = new QAction(tr("Use of Uninitialized Memory"), this); + initKindFilterAction(a, QList<int>() << InvalidRead << InvalidWrite << InvalidJump << Overlap + << InvalidMemPool << UninitCondition << UninitValue + << SyscallParam << ClientCheck); + m_errorFilterActions << a; + + a = new QAction(tr("Invalid Frees"), this); + initKindFilterAction(a, QList<int>() << InvalidFree << MismatchedFree); + m_errorFilterActions << a; + + m_filterProjectAction->setToolTip(tr("Show issues originating outside currently opened projects.")); + m_filterProjectAction->setCheckable(true); + + m_suppressionSeparator->setSeparator(true); + m_suppressionSeparator->setToolTip(tr("These suppression files were used in the last memory analyzer run.")); +} + +void MemcheckTool::settingsDestroyed(QObject *settings) +{ + QTC_ASSERT(m_settings == settings, return); + m_settings = AnalyzerGlobalSettings::instance(); +} + +void MemcheckTool::maybeActiveRunConfigurationChanged() +{ + AnalyzerSettings *settings = 0; + ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance(); + if (ProjectExplorer::Project *project = pe->startupProject()) { + if (ProjectExplorer::Target *target = project->activeTarget()) { + if (ProjectExplorer::RunConfiguration *rc = target->activeRunConfiguration()) { + settings = rc->extraAspect<AnalyzerProjectSettings>(); + } + } + } + + if (!settings) // fallback to global settings + settings = AnalyzerGlobalSettings::instance(); + + if (m_settings == settings) + return; + + // disconnect old settings class if any + if (m_settings) { + m_settings->disconnect(this); + m_settings->disconnect(m_errorProxyModel); + } + + // now make the new settings current, update and connect input widgets + m_settings = settings; + QTC_ASSERT(m_settings, return); + + connect(m_settings, SIGNAL(destroyed(QObject *)), SLOT(settingsDestroyed(QObject *))); + + AbstractMemcheckSettings *memcheckSettings = m_settings->subConfig<AbstractMemcheckSettings>(); + QTC_ASSERT(memcheckSettings, return); + + foreach (QAction *action, m_errorFilterActions) { + bool contained = true; + foreach (const QVariant &v, action->data().toList()) { + bool ok; + int kind = v.toInt(&ok); + if (ok && !memcheckSettings->visibleErrorKinds().contains(kind)) + contained = false; + } + action->setChecked(contained); + } + + m_filterProjectAction->setChecked(!memcheckSettings->filterExternalIssues()); + + m_errorView->settingsChanged(m_settings); + + connect(memcheckSettings, SIGNAL(visibleErrorKindsChanged(QList<int>)), + m_errorProxyModel, SLOT(setAcceptedKinds(QList<int>))); + m_errorProxyModel->setAcceptedKinds(memcheckSettings->visibleErrorKinds()); + + connect(memcheckSettings, SIGNAL(filterExternalIssuesChanged(bool)), + m_errorProxyModel, SLOT(setFilterExternalIssues(bool))); + m_errorProxyModel->setFilterExternalIssues(memcheckSettings->filterExternalIssues()); +} + +QString MemcheckTool::id() const +{ + return "Memcheck"; +} + +QString MemcheckTool::displayName() const +{ + return tr("Analyze Memory"); +} + +IAnalyzerTool::ToolMode MemcheckTool::mode() const +{ + return DebugMode; +} + +class FrameFinder : public ErrorListModel::RelevantFrameFinder +{ +public: + Frame findRelevant(const Error &error) const + { + const QVector<Stack> stacks = error.stacks(); + if (stacks.isEmpty()) + return Frame(); + const Stack &stack = stacks[0]; + const QVector<Frame> frames = stack.frames(); + if (frames.isEmpty()) + return Frame(); + + //find the first frame belonging to the project + if (!m_projectFiles.isEmpty()) { + foreach (const Frame &frame, frames) { + if (frame.directory().isEmpty() || frame.file().isEmpty()) + continue; + + //filepaths can contain "..", clean them: + const QString f = QFileInfo(frame.directory() + QLatin1Char('/') + frame.file()).absoluteFilePath(); + if (m_projectFiles.contains(f)) + return frame; + } + } + + //if no frame belonging to the project was found, return the first one that is not malloc/new + foreach (const Frame &frame, frames) { + if (!frame.functionName().isEmpty() && frame.functionName() != QLatin1String("malloc") + && !frame.functionName().startsWith("operator new(") ) + { + return frame; + } + } + + //else fallback to the first frame + return frames.first(); + } + void setFiles(const QStringList &files) + { + m_projectFiles = files; + } +private: + QStringList m_projectFiles; +}; + +MemcheckErrorView *MemcheckTool::ensurePaneErrorView() +{ + if (!m_errorView) { + m_errorView = new MemcheckErrorView; + m_errorView->setObjectName(QLatin1String("MemcheckErrorView")); + m_errorView->setFrameStyle(QFrame::NoFrame); + m_errorView->setAttribute(Qt::WA_MacShowFocusRect, false); + m_errorModel = new ErrorListModel(m_errorView); + m_frameFinder = new Internal::FrameFinder; + m_errorModel->setRelevantFrameFinder(QSharedPointer<Internal::FrameFinder>(m_frameFinder)); + m_errorProxyModel = new MemcheckErrorFilterProxyModel(m_errorView); + m_errorProxyModel->setSourceModel(m_errorModel); + m_errorProxyModel->setDynamicSortFilter(true); + m_errorView->setModel(m_errorProxyModel); + m_errorView->setSelectionMode(QAbstractItemView::ExtendedSelection); + // make m_errorView->selectionModel()->selectedRows() return something + m_errorView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_errorView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + m_errorView->setAutoScroll(false); + m_errorView->setObjectName("Valgrind.MemcheckTool.ErrorView"); + } + return m_errorView; +} + +QWidget *MemcheckTool::createPaneToolBarWidget() +{ + QWidget *toolbarWidget = new QWidget; + toolbarWidget->setObjectName(QLatin1String("MemCheckToolBarWidget")); + QHBoxLayout *layout = new QHBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + // filter + QToolButton *filterButton = new QToolButton; + filterButton->setIcon(QIcon(Core::Constants::ICON_FILTER)); + filterButton->setText(tr("Error Filter")); + filterButton->setPopupMode(QToolButton::InstantPopup); + QMenu *filterMenu = new QMenu(filterButton); + foreach (QAction *filterAction, m_errorFilterActions) + filterMenu->addAction(filterAction); + filterMenu->addSeparator(); + filterMenu->addAction(m_filterProjectAction); + filterMenu->addAction(m_suppressionSeparator); + connect(filterMenu, SIGNAL(triggered(QAction *)), SLOT(updateErrorFilter())); + filterButton->setMenu(filterMenu); + layout->addWidget(filterButton); + layout->addStretch(); + toolbarWidget->setLayout(layout); + return toolbarWidget; +} + +void MemcheckTool::initialize() +{ + ensurePaneErrorView(); + // register shortcuts + maybeActiveRunConfigurationChanged(); +} + +IAnalyzerEngine *MemcheckTool::createEngine(const AnalyzerStartParameters &sp, + ProjectExplorer::RunConfiguration *runConfiguration) +{ + m_frameFinder->setFiles(runConfiguration ? runConfiguration->target()->project()->files(ProjectExplorer::Project::AllFiles) : QStringList()); + + MemcheckEngine *engine = new MemcheckEngine(sp, runConfiguration); + + connect(engine, SIGNAL(starting(const Analyzer::IAnalyzerEngine*)), + this, SLOT(engineStarting(const Analyzer::IAnalyzerEngine*))); + connect(engine, SIGNAL(parserError(Valgrind::XmlProtocol::Error)), + this, SLOT(parserError(Valgrind::XmlProtocol::Error))); + connect(engine, SIGNAL(internalParserError(QString)), + this, SLOT(internalParserError(QString))); + connect(engine, SIGNAL(finished()), this, SLOT(finished())); + AnalyzerManager::instance()->showStatusMessage(AnalyzerManager::msgToolStarted(displayName())); + return engine; +} + +void MemcheckTool::engineStarting(const IAnalyzerEngine *engine) +{ + clearErrorView(); + + QString dir; + if (ProjectExplorer::RunConfiguration *rc = engine->runConfiguration()) + dir = rc->target()->project()->projectDirectory() + QDir::separator(); + + const MemcheckEngine *mEngine = dynamic_cast<const MemcheckEngine *>(engine); + QTC_ASSERT(mEngine, return); + const QString name = QFileInfo(mEngine->executable()).fileName(); + + m_errorView->setDefaultSuppressionFile(dir + name + QLatin1String(".supp")); + + QMenu *menu = filterMenu(); + QTC_ASSERT(menu, return); + foreach (const QString &file, mEngine->suppressionFiles()) { + QAction *action = menu->addAction(QFileInfo(file).fileName()); + action->setToolTip(file); + action->setData(file); + connect(action, SIGNAL(triggered(bool)), + this, SLOT(suppressionActionTriggered())); + m_suppressionActions << action; + } +} + +QMenu *MemcheckTool::filterMenu() const +{ + QTC_ASSERT(m_suppressionSeparator, return 0); + foreach (QWidget *w, m_suppressionSeparator->associatedWidgets()) + if (QMenu *menu = qobject_cast<QMenu *>(w)) + return menu; + return 0; +} + +void MemcheckTool::suppressionActionTriggered() +{ + QAction *action = qobject_cast<QAction *>(sender()); + QTC_ASSERT(action, return); + const QString file = action->data().toString(); + QTC_ASSERT(!file.isEmpty(), return); + + TextEditor::BaseTextEditorWidget::openEditorAt(file, 0); +} + +void MemcheckTool::parserError(const Valgrind::XmlProtocol::Error &error) +{ + m_errorModel->addError(error); +} + +void MemcheckTool::internalParserError(const QString &errorString) +{ + QMessageBox::critical(m_errorView, tr("Internal Error"), + tr("Error occurred parsing valgrind output: %1").arg(errorString)); +} + +void MemcheckTool::clearErrorView() +{ + m_errorModel->clear(); + + qDeleteAll(m_suppressionActions); + m_suppressionActions.clear(); + QTC_ASSERT(filterMenu()->actions().last() == m_suppressionSeparator, qt_noop()); +} + +void MemcheckTool::updateErrorFilter() +{ + QTC_ASSERT(m_settings, return); + + AbstractMemcheckSettings *memcheckSettings = m_settings->subConfig<AbstractMemcheckSettings>(); + QTC_ASSERT(memcheckSettings, return); + memcheckSettings->setFilterExternalIssues(!m_filterProjectAction->isChecked()); + + QList<int> errorKinds; + foreach (QAction *a, m_errorFilterActions) { + if (!a->isChecked()) + continue; + foreach (const QVariant &v, a->data().toList()) { + bool ok; + int kind = v.toInt(&ok); + if (ok) + errorKinds << kind; + } + } + memcheckSettings->setVisibleErrorKinds(errorKinds); +} + +IAnalyzerOutputPaneAdapter *MemcheckTool::outputPaneAdapter() +{ + if (!m_outputPaneAdapter) + m_outputPaneAdapter = new MemCheckOutputPaneAdapter(this); + return m_outputPaneAdapter; +} + +void MemcheckTool::finished() +{ + const QString msg = AnalyzerManager::msgToolFinished(displayName(), m_errorModel->rowCount()); + AnalyzerManager::instance()->showStatusMessage(msg); +} + +bool MemcheckTool::canRunRemotely() const +{ + return true; +} + +} // namespace Internal +} // namespace Valgrind diff --git a/src/plugins/valgrind/memchecktool.h b/src/plugins/valgrind/memchecktool.h new file mode 100644 index 0000000000..35262a9993 --- /dev/null +++ b/src/plugins/valgrind/memchecktool.h @@ -0,0 +1,149 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Nicolas Arnaud-Cormos, KDAB (nicolas.arnaud-cormos@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef MEMCHECKTOOL_H +#define MEMCHECKTOOL_H + +#include <analyzerbase/ianalyzertool.h> + +#include <QtGui/QSortFilterProxyModel> +#include <QtCore/QSharedPointer> + +QT_BEGIN_NAMESPACE +class QItemSelection; +class QTreeView; +class QModelIndex; +class QAction; +class QSpinBox; +class QCheckBox; +class QMenu; +QT_END_NAMESPACE + +namespace Valgrind { +namespace XmlProtocol { +class ErrorListModel; +class Error; +} +} + +namespace Analyzer +{ +class AnalyzerSettings; +} + +namespace Valgrind { +namespace Internal { + +class MemCheckOutputPaneAdapter; +class MemcheckErrorView; +class FrameFinder; + +class MemcheckErrorFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + MemcheckErrorFilterProxyModel(QObject *parent = 0); + +public slots: + void setAcceptedKinds(const QList<int> &acceptedKinds); + void setFilterExternalIssues(bool filter); + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + +private: + QList<int> m_acceptedKinds; + bool m_filterExternalIssues; +}; + +class MemcheckTool : public Analyzer::IAnalyzerTool +{ + Q_OBJECT + +public: + explicit MemcheckTool(QObject *parent = 0); + + QString id() const; + QString displayName() const; + ToolMode mode() const; + + void initialize(); + void extensionsInitialized() {} + + Analyzer::IAnalyzerOutputPaneAdapter *outputPaneAdapter(); + Analyzer::IAnalyzerEngine *createEngine(const Analyzer::AnalyzerStartParameters &sp, + ProjectExplorer::RunConfiguration *runConfiguration = 0); + + // For the output pane adapter. + MemcheckErrorView *ensurePaneErrorView(); + QWidget *createPaneToolBarWidget(); + void clearErrorView(); + + bool canRunRemotely() const; + bool needsOutputPane() const { return true; } + +private slots: + void settingsDestroyed(QObject *settings); + void maybeActiveRunConfigurationChanged(); + + void engineStarting(const Analyzer::IAnalyzerEngine *engine); + void finished(); + + void parserError(const Valgrind::XmlProtocol::Error &error); + void internalParserError(const QString &errorString); + void updateErrorFilter(); + void suppressionActionTriggered(); + +private: + QMenu *filterMenu() const; + + Analyzer::AnalyzerSettings *m_settings; + + FrameFinder *m_frameFinder; + Valgrind::XmlProtocol::ErrorListModel *m_errorModel; + MemcheckErrorFilterProxyModel *m_errorProxyModel; + MemcheckErrorView *m_errorView; + + QList<QAction *> m_errorFilterActions; + QAction *m_filterProjectAction; + QList<QAction *> m_suppressionActions; + QAction *m_suppressionSeparator; + MemCheckOutputPaneAdapter *m_outputPaneAdapter; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // MEMCHECKTOOL_H diff --git a/src/plugins/valgrind/suppressiondialog.cpp b/src/plugins/valgrind/suppressiondialog.cpp new file mode 100644 index 0000000000..17d315109c --- /dev/null +++ b/src/plugins/valgrind/suppressiondialog.cpp @@ -0,0 +1,259 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "suppressiondialog.h" +#include "ui_suppressiondialog.h" + +#include "memcheckerrorview.h" +#include "memchecksettings.h" + +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/session.h> +#include <projectexplorer/project.h> +#include <projectexplorer/projectnodes.h> + +#include <utils/pathchooser.h> +#include <utils/fileutils.h> +#include <utils/qtcassert.h> + +#include <QtCore/QFile> +#include <QtGui/QPushButton> + +#include <valgrind/xmlprotocol/suppression.h> +#include <valgrind/xmlprotocol/errorlistmodel.h> +#include <valgrind/xmlprotocol/stack.h> +#include <valgrind/xmlprotocol/frame.h> + +using namespace Analyzer; +using namespace Valgrind::XmlProtocol; + +namespace { + +QString suppressionText(const Error &error) +{ + Suppression sup(error.suppression()); + + // workaround: https://bugs.kde.org/show_bug.cgi?id=255822 + if (sup.frames().size() >= 24) { + sup.setFrames(sup.frames().mid(0, 23)); + } + QTC_ASSERT(sup.frames().size() < 24, qt_noop()); + + // try to set some useful name automatically, instead of "insert_name_here" + // we take the last stack frame and append the suppression kind, e.g.: + // QDebug::operator<<(bool) [Memcheck:Cond] + if (!error.stacks().isEmpty() && !error.stacks().first().frames().isEmpty()) { + const Frame &frame = error.stacks().first().frames().first(); + + QString newName; + if (!frame.functionName().isEmpty()) + newName = frame.functionName(); + else if (!frame.object().isEmpty()) + newName = frame.object(); + + if (!newName.isEmpty()) { + sup.setName(newName + '[' + sup.kind() + ']'); + } + } + + return sup.toString(); +} + +/// @p error input error, which might get hidden when it has the same stack +/// @p suppressed the error that got suppressed already +static bool equalSuppression(const Error &error, const Error &suppressed) +{ + if (error.kind() != suppressed.kind() || error.suppression().isNull()) + return false; + + const SuppressionFrames errorFrames = error.suppression().frames(); + const SuppressionFrames suppressedFrames = suppressed.suppression().frames(); + + // limit to 23 frames, see: https://bugs.kde.org/show_bug.cgi?id=255822 + if (qMin(23, suppressedFrames.size()) > errorFrames.size()) + return false; + + int frames = 23; + if (errorFrames.size() < frames) + frames = errorFrames.size(); + + if (suppressedFrames.size() < frames) + frames = suppressedFrames.size(); + + for (int i = 0; i < frames; ++i) { + if (errorFrames.at(i) != suppressedFrames.at(i)) + return false; + } + + return true; +} + +bool sortIndizesReverse(const QModelIndex &l, const QModelIndex &r) +{ + return l.row() > r.row(); +} + +} // namespace anoe + +namespace Valgrind { +namespace Internal { + +SuppressionDialog::SuppressionDialog(MemcheckErrorView *view, QWidget *parent, Qt::WindowFlags f) + : QDialog(parent, f), + m_view(view), + m_ui(new Ui::SuppressionDialog), + m_settings(view->settings()), + m_cleanupIfCanceled(false) +{ + m_ui->setupUi(this); + + ///NOTE: pathchooser requires existing files... + QFile defaultSuppFile(view->defaultSuppressionFile()); + if (!defaultSuppFile.exists()) { + if (defaultSuppFile.open(QIODevice::WriteOnly)) { + defaultSuppFile.close(); + m_cleanupIfCanceled = true; + } + } + + //NOTE: first set kind, then set path since otherwise the file will be seen as "invalid" + m_ui->fileChooser->setExpectedKind(Utils::PathChooser::File); + m_ui->fileChooser->setPath(defaultSuppFile.fileName()); + m_ui->fileChooser->setPromptDialogFilter(QLatin1String("*.supp")); + m_ui->fileChooser->setPromptDialogTitle(tr("Select Suppression File")); + connect(m_ui->fileChooser, SIGNAL(validChanged()), + SLOT(validate())); + connect(m_ui->suppressionEdit->document(), SIGNAL(contentsChanged()), + SLOT(validate())); + + QString suppressions; + QModelIndexList indizes = m_view->selectionModel()->selectedRows(); + if (indizes.isEmpty() && m_view->selectionModel()->currentIndex().isValid()) { + // can happen when using arrow keys to navigate and shortcut to trigger suppression + indizes << m_view->selectionModel()->currentIndex(); + } + foreach (const QModelIndex &index, indizes) { + Error error = m_view->model()->data(index, ErrorListModel::ErrorRole).value<Error>(); + if (!error.suppression().isNull()) + m_errors << error; + } + + foreach (const Error &error, m_errors) + suppressions += suppressionText(error); + + m_ui->suppressionEdit->setPlainText(suppressions); + + setWindowTitle(tr("Save Suppression")); +} + +bool SuppressionDialog::shouldShow() const +{ + return !m_errors.isEmpty(); +} + +void SuppressionDialog::accept() +{ + const QString path = m_ui->fileChooser->path(); + QTC_ASSERT(!path.isEmpty(), return); + QTC_ASSERT(!m_ui->suppressionEdit->toPlainText().trimmed().isEmpty(), return); + + Utils::FileSaver saver(path, QIODevice::Append); + QTextStream stream(saver.file()); + stream << m_ui->suppressionEdit->toPlainText(); + saver.setResult(&stream); + if (!saver.finalize(this)) + return; + + // add file to project (if there is a project that contains this file on the file system) + ProjectExplorer::SessionManager *session = ProjectExplorer::ProjectExplorerPlugin::instance()->session(); + if (!session->projectForFile(path)) { + foreach (ProjectExplorer::Project *p, session->projects()) { + if (path.startsWith(p->projectDirectory())) { + p->rootProjectNode()->addFiles(ProjectExplorer::UnknownFileType, QStringList() << path); + break; + } + } + } + + m_settings->subConfig<AbstractMemcheckSettings>()->addSuppressionFiles(QStringList(path)); + + QModelIndexList indizes = m_view->selectionModel()->selectedRows(); + qSort(indizes.begin(), indizes.end(), sortIndizesReverse); + foreach (const QModelIndex &index, indizes) { + bool removed = m_view->model()->removeRow(index.row()); + QTC_ASSERT(removed, qt_noop()); + Q_UNUSED(removed); + } + + // one suppression might hide multiple rows, care for that + for (int row = 0; row < m_view->model()->rowCount(); ++row ) { + const Error rowError = m_view->model()->data( + m_view->model()->index(row, 0), ErrorListModel::ErrorRole).value<Error>(); + + foreach (const Error &error, m_errors) { + if (equalSuppression(rowError, error)) { + bool removed = m_view->model()->removeRow(row); + QTC_ASSERT(removed, qt_noop()); + Q_UNUSED(removed); + // gets increased in the for loop again + --row; + break; + } + } + } + + // select a new item + m_view->setCurrentIndex(indizes.first()); + + QDialog::accept(); +} + +void SuppressionDialog::reject() +{ + if (m_cleanupIfCanceled) + QFile::remove(m_view->defaultSuppressionFile()); + + QDialog::reject(); +} + +void SuppressionDialog::validate() +{ + bool valid = m_ui->fileChooser->isValid() + && !m_ui->suppressionEdit->toPlainText().trimmed().isEmpty(); + + m_ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(valid); +} + +} // namespace Internal +} // namespace Valgrind diff --git a/src/plugins/valgrind/suppressiondialog.h b/src/plugins/valgrind/suppressiondialog.h new file mode 100644 index 0000000000..93fce056e6 --- /dev/null +++ b/src/plugins/valgrind/suppressiondialog.h @@ -0,0 +1,81 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef ANALYZER_VALGRIND_INTERNAL_SUPPRESSIONDIALOG_H +#define ANALYZER_VALGRIND_INTERNAL_SUPPRESSIONDIALOG_H + +#include <QtGui/QDialog> + +#include <valgrind/xmlprotocol/error.h> + +namespace Analyzer { +class AnalyzerSettings; +} + +namespace Valgrind { +namespace Internal { + +namespace Ui { +class SuppressionDialog; +} + +class MemcheckErrorView; + +class SuppressionDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SuppressionDialog(MemcheckErrorView *view, QWidget *parent = 0, + Qt::WindowFlags f = 0); + virtual void accept(); + virtual void reject(); + + bool shouldShow() const; + +private slots: + void validate(); + +private: + MemcheckErrorView *m_view; + Ui::SuppressionDialog *m_ui; + Analyzer::AnalyzerSettings *m_settings; + bool m_cleanupIfCanceled; + QList<Valgrind::XmlProtocol::Error> m_errors; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // ANALYZER_VALGRIND_INTERNAL_SUPPRESSIONDIALOG_H diff --git a/src/plugins/valgrind/suppressiondialog.ui b/src/plugins/valgrind/suppressiondialog.ui new file mode 100644 index 0000000000..3026263192 --- /dev/null +++ b/src/plugins/valgrind/suppressiondialog.ui @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Valgrind::Internal::SuppressionDialog</class> + <widget class="QDialog" name="Valgrind::Internal::SuppressionDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>718</width> + <height>424</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="fileLabel"> + <property name="text"> + <string>Suppression File:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="Utils::PathChooser" name="fileChooser" native="true"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="suppressionsLabel"> + <property name="text"> + <string>Suppression:</string> + </property> + <property name="buddy"> + <cstring>suppressionEdit</cstring> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QPlainTextEdit" name="suppressionEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <family>Monospace</family> + </font> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Utils::PathChooser</class> + <extends>QWidget</extends> + <header location="global">utils/pathchooser.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Memcheck::Internal::SuppressionDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>322</x> + <y>391</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Memcheck::Internal::SuppressionDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>254</x> + <y>391</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/valgrind/valgrind.pri b/src/plugins/valgrind/valgrind.pri new file mode 100644 index 0000000000..1841a4670f --- /dev/null +++ b/src/plugins/valgrind/valgrind.pri @@ -0,0 +1,4 @@ +include(valgrind_dependencies.pri) + +INCLUDEPATH += $$PWD +LIBS *= -l$$qtLibraryName(Valgrind) diff --git a/src/plugins/valgrind/valgrind.pro b/src/plugins/valgrind/valgrind.pro new file mode 100644 index 0000000000..18bdb64441 --- /dev/null +++ b/src/plugins/valgrind/valgrind.pro @@ -0,0 +1,67 @@ +TEMPLATE = lib +TARGET = Valgrind + +DEFINES += VALGRIND_LIBRARY + +include(../../qtcreatorplugin.pri) +include(valgrind.pri) + +QT += network + +HEADERS += \ + valgrindplugin.h \ + valgrind_global.h \ + valgrindengine.h \ + valgrindconfigwidget.h \ + valgrindsettings.h \ + \ + callgrindconfigwidget.h \ + callgrindcostdelegate.h \ + callgrindcostview.h \ + callgrindhelper.h \ + callgrindnamedelegate.h \ + callgrindsettings.h \ + callgrindtool.h \ + callgrindvisualisation.h \ + callgrindengine.h \ + workarounds.h \ + callgrindtextmark.h \ + \ + memchecktool.h \ + memcheckengine.h \ + memcheckerrorview.h \ + memchecksettings.h \ + memcheckconfigwidget.h \ + suppressiondialog.h + +SOURCES += \ + valgrindplugin.cpp \ + valgrindengine.cpp \ + valgrindconfigwidget.cpp \ + valgrindsettings.cpp \ + \ + callgrindconfigwidget.cpp \ + callgrindcostdelegate.cpp \ + callgrindcostview.cpp \ + callgrindhelper.cpp \ + callgrindnamedelegate.cpp \ + callgrindsettings.cpp \ + callgrindtool.cpp \ + callgrindvisualisation.cpp \ + callgrindengine.cpp \ + workarounds.cpp \ + callgrindtextmark.cpp \ + \ + memchecktool.cpp \ + memcheckengine.cpp \ + memcheckerrorview.cpp \ + memchecksettings.cpp \ + memcheckconfigwidget.cpp \ + suppressiondialog.cpp + +FORMS += \ + valgrindconfigwidget.ui \ + callgrindconfigwidget.ui \ + suppressiondialog.ui \ + memcheckconfigwidget.ui + diff --git a/src/plugins/valgrind/valgrind_dependencies.pri b/src/plugins/valgrind/valgrind_dependencies.pri new file mode 100644 index 0000000000..04786e9e89 --- /dev/null +++ b/src/plugins/valgrind/valgrind_dependencies.pri @@ -0,0 +1,4 @@ +include(../../libs/cplusplus/cplusplus.pri) +include(../../plugins/analyzerbase/analyzerbase.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/texteditor/texteditor.pri) diff --git a/src/plugins/valgrind/valgrind_global.h b/src/plugins/valgrind/valgrind_global.h new file mode 100644 index 0000000000..27ce52a837 --- /dev/null +++ b/src/plugins/valgrind/valgrind_global.h @@ -0,0 +1,46 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Nicolas Arnaud-Cormos, KDAB (nicolas.arnaud-cormos@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef VALGRINDTOOLBASE_GLOBAL_H +#define VALGRINDTOOLBASE_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(VALGRINDTOOLBASE_LIBRARY) +# define VALGRINDTOOLBASE_EXPORT Q_DECL_EXPORT +#else +# define VALGRINDTOOLBASE_EXPORT Q_DECL_IMPORT +#endif + +#endif // VALGRINDTOOLBASE_GLOBAL_H diff --git a/src/plugins/valgrind/valgrindconfigwidget.cpp b/src/plugins/valgrind/valgrindconfigwidget.cpp new file mode 100644 index 0000000000..c03775c7cf --- /dev/null +++ b/src/plugins/valgrind/valgrindconfigwidget.cpp @@ -0,0 +1,63 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "valgrindconfigwidget.h" + +#include "ui_valgrindconfigwidget.h" + +#include "valgrindsettings.h" + +#include <QtCore/QDebug> + +using namespace Valgrind::Internal; + +ValgrindConfigWidget::ValgrindConfigWidget(ValgrindSettings *settings, QWidget *parent) + : QWidget(parent), + m_settings(settings), + m_ui(new Ui::ValgrindConfigWidget) +{ + m_ui->setupUi(this); + + m_ui->valgrindExeChooser->setExpectedKind(Utils::PathChooser::ExistingCommand); + m_ui->valgrindExeChooser->setPromptDialogTitle(tr("Valgrind Command")); + + m_ui->valgrindExeChooser->setPath(m_settings->valgrindExecutable()); + connect(m_ui->valgrindExeChooser, SIGNAL(changed(QString)), m_settings, SLOT(setValgrindExecutable(QString))); + connect(m_settings, SIGNAL(valgrindExecutableChanged(QString)), m_ui->valgrindExeChooser, SLOT(setPath(QString))); +} + +ValgrindConfigWidget::~ValgrindConfigWidget() +{ + delete m_ui; +} diff --git a/src/plugins/valgrind/valgrindconfigwidget.h b/src/plugins/valgrind/valgrindconfigwidget.h new file mode 100644 index 0000000000..b63b88248f --- /dev/null +++ b/src/plugins/valgrind/valgrindconfigwidget.h @@ -0,0 +1,66 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + + +#ifndef ANALYZER_INTERNAL_VALGRINDCONFIGWIDGET_H +#define ANALYZER_INTERNAL_VALGRINDCONFIGWIDGET_H + +#include <QtGui/QWidget> + +namespace Valgrind { +namespace Internal { + +namespace Ui { +class ValgrindConfigWidget; +} + +class ValgrindSettings; + +class ValgrindConfigWidget : public QWidget +{ + Q_OBJECT + +public: + ValgrindConfigWidget(ValgrindSettings *settings, QWidget *parent); + virtual ~ValgrindConfigWidget(); + +private: + ValgrindSettings *m_settings; + Ui::ValgrindConfigWidget *m_ui; +}; + +} +} + +#endif // ANALYZER_INTERNAL_VALGRINDCONFIGWIDGET_H diff --git a/src/plugins/valgrind/valgrindconfigwidget.ui b/src/plugins/valgrind/valgrindconfigwidget.ui new file mode 100644 index 0000000000..2b929bfc4c --- /dev/null +++ b/src/plugins/valgrind/valgrindconfigwidget.ui @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Valgrind::Internal::ValgrindConfigWidget</class> + <widget class="QWidget" name="Valgrind::Internal::ValgrindConfigWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>445</width> + <height>543</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="commonValgrindOptions"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Generic Settings</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="valgrindExeLabel"> + <property name="text"> + <string>Valgrind executable:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="Utils::PathChooser" name="valgrindExeChooser" native="true"/> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Utils::PathChooser</class> + <extends>QWidget</extends> + <header location="global">utils/pathchooser.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/valgrind/valgrindengine.cpp b/src/plugins/valgrind/valgrindengine.cpp new file mode 100644 index 0000000000..71c9a9743a --- /dev/null +++ b/src/plugins/valgrind/valgrindengine.cpp @@ -0,0 +1,197 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Nicolas Arnaud-Cormos, KDAB (nicolas.arnaud-cormos@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "valgrindengine.h" +#include "valgrindsettings.h" + +#include <coreplugin/icore.h> +#include <coreplugin/ioutputpane.h> +#include <coreplugin/progressmanager/progressmanager.h> +#include <coreplugin/progressmanager/futureprogress.h> +#include <extensionsystem/pluginmanager.h> +#include <projectexplorer/applicationrunconfiguration.h> +#include <analyzerbase/analyzermanager.h> + +#include <QtGui/QApplication> +#include <QtGui/QMainWindow> + +#define VALGRIND_DEBUG_OUTPUT 0 + +using namespace Analyzer; +using namespace Valgrind::Internal; +using namespace Utils; + +ValgrindEngine::ValgrindEngine(const AnalyzerStartParameters &sp, + ProjectExplorer::RunConfiguration *runConfiguration) + : IAnalyzerEngine(sp, runConfiguration), + m_settings(0), + m_progress(new QFutureInterface<void>()), + m_progressWatcher(new QFutureWatcher<void>()), + m_isStopping(false) +{ + if (runConfiguration) + m_settings = runConfiguration->extraAspect<AnalyzerProjectSettings>(); + + if (!m_settings) + m_settings = AnalyzerGlobalSettings::instance(); + + connect(m_progressWatcher, SIGNAL(canceled()), + this, SLOT(handleProgressCanceled())); + connect(m_progressWatcher, SIGNAL(finished()), + this, SLOT(handleProgressFinished())); +} + +ValgrindEngine::~ValgrindEngine() +{ + delete m_progress; +} + +void ValgrindEngine::start() +{ + emit starting(this); + + Core::FutureProgress *fp = Core::ICore::instance()->progressManager()->addTask(m_progress->future(), + progressTitle(), "valgrind"); + fp->setKeepOnFinish(Core::FutureProgress::DontKeepOnFinish); + m_progress->reportStarted(); + m_progressWatcher->setFuture(m_progress->future()); + +#if VALGRIND_DEBUG_OUTPUT + emit standardOutputReceived(tr("Valgrind options: %1").arg(toolArguments().join(" "))); + emit standardOutputReceived(tr("Working directory: %1").arg(m_workingDirectory)); + emit standardOutputReceived(tr("Command-line arguments: %1").arg(m_commandLineArguments)); +#endif + + const AnalyzerStartParameters &sp = startParameters(); + runner()->setWorkingDirectory(sp.workingDirectory); + QString valgrindExe = m_settings->subConfig<ValgrindSettings>()->valgrindExecutable(); + if (!sp.analyzerCmdPrefix.isEmpty()) + valgrindExe = sp.analyzerCmdPrefix + ' ' + valgrindExe; + runner()->setValgrindExecutable(valgrindExe); + runner()->setValgrindArguments(toolArguments()); + runner()->setDebuggeeExecutable(sp.debuggee); + runner()->setDebuggeeArguments(sp.debuggeeArgs); + runner()->setEnvironment(sp.environment); + + connect(runner(), SIGNAL(standardOutputReceived(QByteArray)), + SLOT(receiveStandardOutput(QByteArray))); + connect(runner(), SIGNAL(standardErrorReceived(QByteArray)), + SLOT(receiveStandardError(QByteArray))); + connect(runner(), SIGNAL(processErrorReceived(QString, QProcess::ProcessError)), + SLOT(receiveProcessError(QString, QProcess::ProcessError))); + connect(runner(), SIGNAL(finished()), + SLOT(runnerFinished())); + + if (sp.startMode == StartRemote) + runner()->startRemotely(sp.connParams); + else + runner()->start(); +} + +void ValgrindEngine::stop() +{ + m_isStopping = true; + runner()->stop(); +} + +QString ValgrindEngine::executable() const +{ + return startParameters().debuggee; +} + +void ValgrindEngine::handleProgressCanceled() +{ + AnalyzerManager::instance()->stopTool(); +} + +void ValgrindEngine::handleProgressFinished() +{ + QApplication::alert(Core::ICore::instance()->mainWindow(), 3000); +} + +void ValgrindEngine::runnerFinished() +{ + emit standardOutputReceived(tr("** Analyzing finished **")); + emit finished(); + + m_progress->reportFinished(); + + disconnect(runner(), SIGNAL(standardOutputReceived(QByteArray)), + this, SLOT(receiveStandardOutput(QByteArray))); + disconnect(runner(), SIGNAL(standardErrorReceived(QByteArray)), + this, SLOT(receiveStandardError(QByteArray))); + disconnect(runner(), SIGNAL(processErrorReceived(QString, QProcess::ProcessError)), + this, SLOT(receiveProcessError(QString, QProcess::ProcessError))); + disconnect(runner(), SIGNAL(finished()), + this, SLOT(runnerFinished())); +} + +void ValgrindEngine::receiveStandardOutput(const QByteArray &b) +{ + emit standardOutputReceived(QString::fromLocal8Bit(b)); +} + +void ValgrindEngine::receiveStandardError(const QByteArray &b) +{ + emit standardErrorReceived(QString::fromLocal8Bit(b)); +} + +void ValgrindEngine::receiveProcessError(const QString &error, QProcess::ProcessError e) +{ + if (e == QProcess::FailedToStart) { + const QString &valgrind = m_settings->subConfig<ValgrindSettings>()->valgrindExecutable(); + if (!valgrind.isEmpty()) { + emit standardErrorReceived(tr("** Error: \"%1\" could not be started: %2 **").arg(valgrind).arg(error)); + } else { + emit standardErrorReceived(tr("** Error: no valgrind executable set **")); + } + } else if (m_isStopping && e == QProcess::Crashed) { // process gets killed on stop + emit standardErrorReceived(tr("** Process Terminated **")); + } else { + emit standardErrorReceived(QString("** %1 **").arg(error)); + } + + if (m_isStopping) + return; + + ///FIXME: get a better API for this into Qt Creator + ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); + QList<Core::IOutputPane *> panes = pm->getObjects<Core::IOutputPane>(); + foreach (Core::IOutputPane *pane, panes) { + if (pane->displayName() == tr("Application Output")) { + pane->popup(false); + break; + } + } +} diff --git a/src/plugins/valgrind/valgrindengine.h b/src/plugins/valgrind/valgrindengine.h new file mode 100644 index 0000000000..a34273d3f0 --- /dev/null +++ b/src/plugins/valgrind/valgrindengine.h @@ -0,0 +1,97 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Nicolas Arnaud-Cormos, KDAB (nicolas.arnaud-cormos@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef VALGRINDENGINE_H +#define VALGRINDENGINE_H + +#include "valgrind_global.h" + +#include <analyzerbase/ianalyzerengine.h> +#include <analyzerbase/ianalyzerengine.h> + +#include <utils/environment.h> + +#include <valgrind/valgrindrunner.h> + +#include <QtCore/QString> +#include <QtCore/QByteArray> +#include <QtCore/QFutureInterface> +#include <QtCore/QFutureWatcher> + +namespace Analyzer { +class AnalyzerSettings; +} + +namespace Valgrind { +namespace Internal { + +class VALGRINDTOOLBASE_EXPORT ValgrindEngine : public Analyzer::IAnalyzerEngine +{ + Q_OBJECT +public: + explicit ValgrindEngine(const Analyzer::AnalyzerStartParameters &sp, + ProjectExplorer::RunConfiguration *runConfiguration); + virtual ~ValgrindEngine(); + + void start(); + void stop(); + + QString executable() const; + +protected: + virtual QString progressTitle() const = 0; + virtual QStringList toolArguments() const = 0; + virtual Valgrind::ValgrindRunner *runner() = 0; + + Analyzer::AnalyzerSettings *m_settings; + QFutureInterface<void> *m_progress; + QFutureWatcher<void> *m_progressWatcher; + +private slots: + void handleProgressCanceled(); + void handleProgressFinished(); + void runnerFinished(); + + void receiveStandardOutput(const QByteArray &); + void receiveStandardError(const QByteArray &); + void receiveProcessError(const QString &, QProcess::ProcessError); + +private: + bool m_isStopping; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // VALGRINDENGINE_H diff --git a/src/plugins/valgrind/valgrindplugin.cpp b/src/plugins/valgrind/valgrindplugin.cpp new file mode 100644 index 0000000000..e9f13d0265 --- /dev/null +++ b/src/plugins/valgrind/valgrindplugin.cpp @@ -0,0 +1,93 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "valgrindplugin.h" +#include "valgrindsettings.h" +#include "callgrindtool.h" +#include "callgrindsettings.h" +#include "memchecktool.h" +#include "memchecksettings.h" + +#include <analyzerbase/analyzersettings.h> +#include <analyzerbase/analyzermanager.h> + +#include <QtCore/QStringList> +#include <QtCore/QtPlugin> + +using namespace Analyzer; +using namespace Valgrind::Internal; + + +static AbstractAnalyzerSubConfig *valgrindConfigFactory() +{ + return new ValgrindSettings(); +} + +static AbstractAnalyzerSubConfig *globalCallgrindFactory() +{ + return new CallgrindGlobalSettings(); +} + +static AbstractAnalyzerSubConfig *projectCallgrindFactory() +{ + return new CallgrindProjectSettings(); +} + +static AbstractAnalyzerSubConfig *globalMemcheckFactory() +{ + return new MemcheckGlobalSettings(); +} + +static AbstractAnalyzerSubConfig *projectMemcheckFactory() +{ + return new MemcheckProjectSettings(); +} + +bool ValgrindPlugin::initialize(const QStringList &, QString *) +{ + AnalyzerGlobalSettings::instance()->registerSubConfigs(&valgrindConfigFactory, &valgrindConfigFactory); + AnalyzerGlobalSettings::instance()->registerSubConfigs(&globalCallgrindFactory, &projectCallgrindFactory); + AnalyzerGlobalSettings::instance()->registerSubConfigs(&globalMemcheckFactory, &projectMemcheckFactory); + + AnalyzerManager::instance()->addTool(new MemcheckTool(this)); + AnalyzerManager::instance()->addTool(new CallgrindTool(this)); + return true; +} + + +void ValgrindPlugin::extensionsInitialized() +{ +} + +Q_EXPORT_PLUGIN(Valgrind::Internal::ValgrindPlugin) diff --git a/src/plugins/valgrind/valgrindplugin.h b/src/plugins/valgrind/valgrindplugin.h new file mode 100644 index 0000000000..6dee7dd544 --- /dev/null +++ b/src/plugins/valgrind/valgrindplugin.h @@ -0,0 +1,57 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef VALGRINDTOOLBASEPLUGIN_H +#define VALGRINDTOOLBASEPLUGIN_H + +#include <extensionsystem/iplugin.h> + +namespace Valgrind { +namespace Internal { + +class ValgrindPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + ValgrindPlugin() {} + + virtual bool initialize(const QStringList &arguments, QString *errorString); + virtual void extensionsInitialized(); +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // VALGRINDTOOLBASEPLUGIN_H diff --git a/src/plugins/valgrind/valgrindsettings.cpp b/src/plugins/valgrind/valgrindsettings.cpp new file mode 100644 index 0000000000..69b7eecb71 --- /dev/null +++ b/src/plugins/valgrind/valgrindsettings.cpp @@ -0,0 +1,98 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "valgrindsettings.h" + +#include "valgrindconfigwidget.h" + +#include <coreplugin/icore.h> +#include <utils/qtcassert.h> + +#include <QtCore/QSettings> + +using namespace Valgrind::Internal; +using namespace Analyzer; + +static const QLatin1String groupC("Analyzer"); + +static const QLatin1String valgrindExeC("Analyzer.Valgrind.ValgrindExecutable"); + +QVariantMap ValgrindSettings::defaults() const +{ + QVariantMap map; + map.insert(valgrindExeC, QLatin1String("valgrind")); + return map; +} + +bool ValgrindSettings::fromMap(const QVariantMap &map) +{ + setIfPresent(map, valgrindExeC, &m_valgrindExecutable); + return true; +} + +QVariantMap ValgrindSettings::toMap() const +{ + QVariantMap map; + map.insert(valgrindExeC, m_valgrindExecutable); + + return map; +} + +void ValgrindSettings::setValgrindExecutable(const QString &valgrindExecutable) +{ + if (m_valgrindExecutable != valgrindExecutable) { + m_valgrindExecutable = valgrindExecutable; + emit valgrindExecutableChanged(valgrindExecutable); + } +} + +QString ValgrindSettings::valgrindExecutable() const +{ + return m_valgrindExecutable; +} + +QString ValgrindSettings::id() const +{ + return "Analyzer.Valgrind.Settings.Generic"; +} + +QString ValgrindSettings::displayName() const +{ + return tr("Generic Settings"); +} + +QWidget *ValgrindSettings::createConfigWidget(QWidget *parent) +{ + return new ValgrindConfigWidget(this, parent); +} diff --git a/src/plugins/valgrind/valgrindsettings.h b/src/plugins/valgrind/valgrindsettings.h new file mode 100644 index 0000000000..c01fc74595 --- /dev/null +++ b/src/plugins/valgrind/valgrindsettings.h @@ -0,0 +1,82 @@ +/************************************************************************** +** +** This file is part of Qt Creator Instrumentation Tools +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef ANALYZER_INTERNAL_VALGRINDSETTINGS_H +#define ANALYZER_INTERNAL_VALGRINDSETTINGS_H + +#include "valgrind_global.h" + +#include <analyzerbase/analyzersettings.h> + +#include <QtCore/QObject> +#include <QtCore/QVariant> + +namespace Valgrind { +namespace Internal { + +/** + * Generic Valgrind settings shared by all tools. + */ +class VALGRINDTOOLBASE_EXPORT ValgrindSettings : public Analyzer::AbstractAnalyzerSubConfig +{ + Q_OBJECT +public: + ValgrindSettings() {} + + virtual QVariantMap toMap() const; + virtual QVariantMap defaults() const; + + QString valgrindExecutable() const; + + virtual QString id() const; + virtual QString displayName() const; + virtual QWidget *createConfigWidget(QWidget *parent); + +public slots: + void setValgrindExecutable(const QString &); + +signals: + void valgrindExecutableChanged(const QString &); + +protected: + virtual bool fromMap(const QVariantMap &map); + +private: + QString m_valgrindExecutable; +}; + +} // namespace Internal +} // namespace Valgrind + +#endif // VALGRIND_INTERNAL_ANALZYZERSETTINGS_H diff --git a/src/plugins/valgrind/workarounds.cpp b/src/plugins/valgrind/workarounds.cpp new file mode 100644 index 0000000000..ef4b998929 --- /dev/null +++ b/src/plugins/valgrind/workarounds.cpp @@ -0,0 +1,51 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#include "workarounds.h" + +#include <QtGui/QPalette> + +#include <utils/stylehelper.h> + +QPalette panelPalette(const QPalette &oldPalette, bool lightColored) +{ + QColor color = Utils::StyleHelper::panelTextColor(lightColored); + QPalette pal = oldPalette; + pal.setBrush(QPalette::All, QPalette::WindowText, color); + pal.setBrush(QPalette::All, QPalette::ButtonText, color); + pal.setBrush(QPalette::All, QPalette::Foreground, color); + color.setAlpha(100); + pal.setBrush(QPalette::Disabled, QPalette::WindowText, color); + pal.setBrush(QPalette::Disabled, QPalette::ButtonText, color); + pal.setBrush(QPalette::Disabled, QPalette::Foreground, color); + return pal; +} diff --git a/src/plugins/valgrind/workarounds.h b/src/plugins/valgrind/workarounds.h new file mode 100644 index 0000000000..819bc9533e --- /dev/null +++ b/src/plugins/valgrind/workarounds.h @@ -0,0 +1,45 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** 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 info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef WORKAROUNDS_H +#define WORKAROUNDS_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE +class QPalette; +QT_END_NAMESPACE + +///FIXME: remove this once http://bugreports.qt.nokia.com/browse/QTCREATORBUG-3247 gets fixed +QPalette panelPalette(const QPalette &oldPalette, bool lightColored = false); + +#endif // WORKAROUNDS_H |