diff --git a/src/plugins/memcheck/Memcheck.pluginspec.in b/src/plugins/memcheck/Memcheck.pluginspec.in new file mode 100644 index 0000000000000000000000000000000000000000..ff4484e23b56d31729617727e681fe4cfdd66454 --- /dev/null +++ b/src/plugins/memcheck/Memcheck.pluginspec.in @@ -0,0 +1,21 @@ +<plugin name=\"Memcheck\" 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 Memcheck Tool Plugin</description> + <url>http://qt.nokia.com</url> + <dependencyList> + <dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/> + <dependency name=\"AnalyzerBase\" version=\"$$QTCREATOR_VERSION\"/> + <dependency name=\"ValgrindToolBase\" version=\"$$QTCREATOR_VERSION\"/> + </dependencyList> +</plugin> diff --git a/src/plugins/memcheck/memcheck.pri b/src/plugins/memcheck/memcheck.pri new file mode 100644 index 0000000000000000000000000000000000000000..85b4382fbd5b9f311eaa12469175a73a6c40a5f5 --- /dev/null +++ b/src/plugins/memcheck/memcheck.pri @@ -0,0 +1,5 @@ +include(memcheck_dependencies.pri) + +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +LIBS *= -l$$qtLibraryName(Memcheck) diff --git a/src/plugins/memcheck/memcheck.pro b/src/plugins/memcheck/memcheck.pro new file mode 100644 index 0000000000000000000000000000000000000000..e6843ed307ca8ea153d469698278acb641a755a5 --- /dev/null +++ b/src/plugins/memcheck/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/memcheck/memcheck_dependencies.pri b/src/plugins/memcheck/memcheck_dependencies.pri new file mode 100644 index 0000000000000000000000000000000000000000..5f4bbc642e503da808730f039066140daffc4dc7 --- /dev/null +++ b/src/plugins/memcheck/memcheck_dependencies.pri @@ -0,0 +1,3 @@ +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/analyzerbase/analyzerbase.pri) +include(../../plugins/valgrindtoolbase/valgrindtoolbase.pri) diff --git a/src/plugins/memcheck/memcheck_global.h b/src/plugins/memcheck/memcheck_global.h new file mode 100644 index 0000000000000000000000000000000000000000..504db90f4ba96afa10dba59f27c41bb468188a64 --- /dev/null +++ b/src/plugins/memcheck/memcheck_global.h @@ -0,0 +1,47 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef MEMCHECK_GLOBAL_H +#define MEMCHECK_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(MEMCHECK_LIBRARY) +# define MEMCHECKSHARED_EXPORT Q_DECL_EXPORT +#else +# define MEMCHECKSHARED_EXPORT Q_DECL_IMPORT +#endif + +#endif // MEMCHECK_GLOBAL_H diff --git a/src/plugins/memcheck/memcheckconfigwidget.cpp b/src/plugins/memcheck/memcheckconfigwidget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2bdd9dbea1dff8679a8d701c8f891c78459e33c7 --- /dev/null +++ b/src/plugins/memcheck/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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "memcheckconfigwidget.h" + +#include "ui_memcheckconfigwidget.h" + +#include "memchecksettings.h" + +#include <QStandardItemModel> +#include <QFileDialog> +#include <QDebug> + +using namespace Analyzer::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)), SLOT(setNumCallers(int))); + connect(m_settings, SIGNAL(numCallersChanged(int)), SLOT(setNumCallers(int))); + + m_ui->trackOrigins->setChecked(m_settings->trackOrigins()); + connect(m_ui->trackOrigins, SIGNAL(toggled(bool)), SLOT(setTrackOrigins(bool))); + connect(m_settings, SIGNAL(trackOriginsChanged(bool)), SLOT(setTrackOrigins(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)); +} + +MemcheckConfigWidget::~MemcheckConfigWidget() +{ + delete m_ui; +} + +void MemcheckConfigWidget::slotAddSuppression() +{ + QFileDialog dialog; + dialog.setAcceptMode(QFileDialog::AcceptOpen); + dialog.setFileMode(QFileDialog::ExistingFiles); + + if (dialog.exec() == QDialog::Accepted) { + foreach(const QString &file, dialog.selectedFiles()) + m_model->appendRow(new QStandardItem(file)); + + m_settings->addSuppressionFiles(dialog.selectedFiles()); + } +} + +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::setNumCallers(int callers) +{ + m_ui->numCallers->setValue(callers); + m_settings->setNumCallers(callers); +} + +int MemcheckConfigWidget::numCallers() const +{ + return m_ui->numCallers->value(); +} + +void MemcheckConfigWidget::setTrackOrigins(bool enable) +{ + m_ui->trackOrigins->setChecked(enable); + m_settings->setTrackOrigins(enable); +} + +bool MemcheckConfigWidget::trackOrigins() const +{ + return m_ui->trackOrigins->isChecked(); +} diff --git a/src/plugins/memcheck/memcheckconfigwidget.h b/src/plugins/memcheck/memcheckconfigwidget.h new file mode 100644 index 0000000000000000000000000000000000000000..5a6c39c82561be2e7f5783e7aaf4255ec0d4767d --- /dev/null +++ b/src/plugins/memcheck/memcheckconfigwidget.h @@ -0,0 +1,88 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + + +#ifndef ANALYZER_INTERNAL_MEMCHECKCONFIGWIDGET_H +#define ANALYZER_INTERNAL_MEMCHECKCONFIGWIDGET_H + +#include <QWidget> + +QT_BEGIN_NAMESPACE +class QStandardItemModel; + +namespace Ui { +class MemcheckConfigWidget; +} +QT_END_NAMESPACE + +namespace Analyzer { +namespace Internal { + +class AbstractMemcheckSettings; + +class MemcheckConfigWidget : public QWidget +{ + Q_OBJECT + +public: + MemcheckConfigWidget(AbstractMemcheckSettings *settings, QWidget *parent); + virtual ~MemcheckConfigWidget(); + + void setSuppressions(const QStringList &files); + QStringList suppressions() const; + + // ### remove the following? + int numCallers() const; + bool trackOrigins() const; + +public slots: + void setNumCallers(int callers); + void setTrackOrigins(bool enable); + + void slotAddSuppression(); + void slotRemoveSuppression(); + void slotSuppressionsRemoved(const QStringList &files); + void slotSuppressionsAdded(const QStringList &files); + +private: + AbstractMemcheckSettings *m_settings; + QStandardItemModel *m_model; + Ui::MemcheckConfigWidget *m_ui; +}; + +} +} + +#endif // ANALYZER_INTERNAL_MEMCHECKCONFIGWIDGET_H diff --git a/src/plugins/memcheck/memcheckconfigwidget.ui b/src/plugins/memcheck/memcheckconfigwidget.ui new file mode 100644 index 0000000000000000000000000000000000000000..016cb3254c8d9da03a55c4e7fd494a129cd6c219 --- /dev/null +++ b/src/plugins/memcheck/memcheckconfigwidget.ui @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MemcheckConfigWidget</class> + <widget class="QWidget" name="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 Analyzation 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>Suppressions:</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="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/memcheck/memcheckengine.cpp b/src/plugins/memcheck/memcheckengine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ab37a0a21d60645a86a42eeb232f2adce85574f5 --- /dev/null +++ b/src/plugins/memcheck/memcheckengine.cpp @@ -0,0 +1,142 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@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::Internal; +using namespace Valgrind::XmlProtocol; + +MemcheckEngine::MemcheckEngine(ProjectExplorer::RunConfiguration *runConfiguration) + : ValgrindEngine(runConfiguration) +{ + connect(&m_parser, SIGNAL(error(const Valgrind::XmlProtocol::Error &)), + SIGNAL(parserError(const 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 Valgrind::XmlProtocol::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); +} diff --git a/src/plugins/memcheck/memcheckengine.h b/src/plugins/memcheck/memcheckengine.h new file mode 100644 index 0000000000000000000000000000000000000000..7738937cd46ff49b0c376a3a4924cb1a08ed8956 --- /dev/null +++ b/src/plugins/memcheck/memcheckengine.h @@ -0,0 +1,79 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef MEMCHECKENGINE_H +#define MEMCHECKENGINE_H + +#include <valgrind/memcheck/memcheckrunner.h> +#include <valgrind/xmlprotocol/threadedparser.h> + +#include <valgrindtoolbase/valgrindengine.h> + +namespace Analyzer { +namespace Internal { + +class MemcheckEngine : public ValgrindEngine +{ + Q_OBJECT +public: + explicit MemcheckEngine(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); + +private slots: + void receiveLogMessage(const QByteArray &); + void status(const Valgrind::XmlProtocol::Status &status); + +private: + QString progressTitle() const; + QStringList toolArguments() const; + Valgrind::ValgrindRunner* runner(); + + Valgrind::XmlProtocol::ThreadedParser m_parser; + Valgrind::Memcheck::MemcheckRunner m_runner; +}; + +} // namespace Internal +} // namespace Analyzer + +#endif // MEMCHECKENGINE_H diff --git a/src/plugins/memcheck/memcheckerrorview.cpp b/src/plugins/memcheck/memcheckerrorview.cpp new file mode 100644 index 0000000000000000000000000000000000000000..36bdb92f2a675f62b051122aa61b336a3e329f03 --- /dev/null +++ b/src/plugins/memcheck/memcheckerrorview.cpp @@ -0,0 +1,503 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "memcheckerrorview.h" + +#include "suppressiondialog.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 <valgrindtoolbase/valgrindsettings.h> + +#include <texteditor/basetexteditor.h> + +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/project.h> +#include <coreplugin/coreconstants.h> + +#include <utils/qtcassert.h> + +#include <QDir> +#include <QLabel> +#include <QListView> +#include <QPainter> +#include <QScrollBar> +#include <QSortFilterProxyModel> +#include <QVBoxLayout> +#include <QDebug> +#include <QAction> +#include <QClipboard> +#include <QApplication> +#include <QMenu> + +using namespace Analyzer; +using namespace Analyzer::Internal; +using namespace Valgrind::XmlProtocol; + +MemcheckErrorDelegate::MemcheckErrorDelegate(QListView *parent) + : QStyledItemDelegate(parent), + m_detailsWidget(0) +{ + connect(parent->verticalScrollBar(), SIGNAL(valueChanged(int)), + SLOT(verticalScrolled())); +} + +MemcheckErrorDelegate::~MemcheckErrorDelegate() +{ +} + +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 QObject::tr("%1 in %2").arg(Qt::escape(fn), path); + else if (!path.isEmpty()) + return path; + else + 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 QObject::tr("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) + return; + + if (!m_detailsIndex.isValid()) + currentChanged(QModelIndex(), 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); +} + +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(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(); + QAction *suppress = menu.addAction(tr("Suppress Error(s)", "", errors.size())); + suppress->setIcon(QIcon(QLatin1String(":/qmldesigner/images/eye_crossed.png"))); + suppress->setEnabled(!errors.isEmpty()); + + if (QAction *executed = menu.exec(e->globalPos())) { + if (executed == suppress) { + SuppressionDialog *dialog = new SuppressionDialog(this); + dialog->setModal(true); + dialog->show(); + dialog->setAttribute(Qt::WA_DeleteOnClose, true); + } + } +} diff --git a/src/plugins/memcheck/memcheckerrorview.h b/src/plugins/memcheck/memcheckerrorview.h new file mode 100644 index 0000000000000000000000000000000000000000..ba5c8c110ddac899f5f2847d293199b3297a78cb --- /dev/null +++ b/src/plugins/memcheck/memcheckerrorview.h @@ -0,0 +1,127 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef MEMCHECKERRORVIEW_H +#define MEMCHECKERRORVIEW_H + +#include <QtGui/QListView> +#include <QtGui/QStyledItemDelegate> +#include <QtGui/QLabel> + +QT_BEGIN_NAMESPACE +class QListView; +class QVBoxLayout; +QT_END_NAMESPACE + +namespace ProjectExplorer { +class Project; +} + +namespace Analyzer { + +class AnalyzerSettings; + +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. + MemcheckErrorDelegate(QListView *parent); + virtual ~MemcheckErrorDelegate(); + + 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; + AnalyzerSettings *settings() const { return m_settings; } + +signals: + void resized(); + +public slots: + void settingsChanged(AnalyzerSettings* settings); + +protected: + void resizeEvent(QResizeEvent *e); + void contextMenuEvent(QContextMenuEvent *e); + +private: + QAction *m_copyAction; + QString m_defaultSuppFile; + AnalyzerSettings *m_settings; +}; + +} // namespace Internal +} // namespace Analyzer + +#endif // MEMCHECKERRORVIEW_H diff --git a/src/plugins/memcheck/memcheckplugin.cpp b/src/plugins/memcheck/memcheckplugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d39f68d0d90cf9400cf84f16c486f6b99eb630ac --- /dev/null +++ b/src/plugins/memcheck/memcheckplugin.cpp @@ -0,0 +1,76 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "memcheckplugin.h" + +#include <analyzerbase/analyzermanager.h> +#include <analyzerbase/analyzersettings.h> + +#include "memchecktool.h" +#include "memchecksettings.h" + +#include <QStringList> +#include <QtPlugin> + +using namespace Analyzer; +using namespace Analyzer::Internal; + +MemcheckPlugin::MemcheckPlugin() +{ + +} + +MemcheckPlugin::~MemcheckPlugin() +{ + +} + +bool MemcheckPlugin::initialize(const QStringList &/*arguments*/, QString */*errorString*/) +{ + typedef AnalyzerSubConfigFactory<MemcheckGlobalSettings, MemcheckProjectSettings> MemcheckConfigFactory; + AnalyzerGlobalSettings::instance()->registerSubConfigFactory(new MemcheckConfigFactory); + + AnalyzerManager::instance()->addTool(new MemcheckTool(this)); + + return true; +} + + +void MemcheckPlugin::extensionsInitialized() +{ + +} + +Q_EXPORT_PLUGIN(MemcheckPlugin) \ No newline at end of file diff --git a/src/plugins/memcheck/memcheckplugin.h b/src/plugins/memcheck/memcheckplugin.h new file mode 100644 index 0000000000000000000000000000000000000000..3848204bfa511a3d3b752e2ee218d82425081a8a --- /dev/null +++ b/src/plugins/memcheck/memcheckplugin.h @@ -0,0 +1,59 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef MEMCHECKPLUGIN_H +#define MEMCHECKPLUGIN_H + +#include <extensionsystem/iplugin.h> + +namespace Analyzer { +namespace Internal { + +class MemcheckPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + MemcheckPlugin(); + ~MemcheckPlugin(); + + virtual bool initialize(const QStringList &arguments, QString *errorString); + virtual void extensionsInitialized(); +}; + +} // namespace Internal +} // namespace Analyzer + +#endif // MEMCHECKPLUGIN_H diff --git a/src/plugins/memcheck/memchecksettings.cpp b/src/plugins/memcheck/memchecksettings.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9eac3c95305a7cd3497a613c72c73024cafc928e --- /dev/null +++ b/src/plugins/memcheck/memchecksettings.cpp @@ -0,0 +1,255 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "memchecksettings.h" +#include "memcheckconfigwidget.h" + +#include <valgrind/xmlprotocol/error.h> + +#include <utils/qtcassert.h> + +using namespace Analyzer::Internal; +using namespace Analyzer; + +static const QLatin1String numCallersC("Analyzer.Valgrind.NumCallers"); +static const QLatin1String trackOriginsC("Analyzer.Valgrind.TrackOrigins"); +static const QLatin1String suppressionFilesC("Analyzer.Valgrind.SupressionFiles"); +static const QLatin1String removedSuppressionFilesC("Analyzer.Valgrind.RemovedSupressionFiles"); +static const QLatin1String addedSuppressionFilesC("Analyzer.Valgrind.AddedSupressionFiles"); +static const QLatin1String filterExternalIssuesC("Analyzer.Valgrind.FilterExternalIssues"); +static const QLatin1String visibleErrorKindsC("Analyzer.Valgrind.VisibleErrorKinds"); + +AbstractMemcheckSettings::AbstractMemcheckSettings() +{ +} + +AbstractMemcheckSettings::~AbstractMemcheckSettings() +{ + +} + +QVariantMap AbstractMemcheckSettings::defaults() const +{ + QVariantMap map; + map.insert(numCallersC, 25); + map.insert(trackOriginsC, true); + map.insert(suppressionFilesC, QStringList()); + map.insert(filterExternalIssuesC, true); + + QVariantList defaultErrorKinds; + for(int i = 0; i < Valgrind::XmlProtocol::MemcheckErrorKindCount; ++i) + defaultErrorKinds << i; + map.insert(visibleErrorKindsC, defaultErrorKinds); + + return map; +} + +bool AbstractMemcheckSettings::fromMap(const QVariantMap &map) +{ + setIfPresent(map, numCallersC, &m_numCallers); + setIfPresent(map, trackOriginsC, &m_trackOrigins); + setIfPresent(map, filterExternalIssuesC, &m_filterExternalIssues); + + // if we get more of these try a template specialization of setIfPresent for lists... + if (map.contains(visibleErrorKindsC)) { + m_visibleErrorKinds.clear(); + foreach(const QVariant &val, map.value(visibleErrorKindsC).toList()) + m_visibleErrorKinds << val.toInt(); + } + + return true; +} + +QVariantMap AbstractMemcheckSettings::toMap() const +{ + QVariantMap map; + map.insert(numCallersC, m_numCallers); + map.insert(trackOriginsC, m_trackOrigins); + map.insert(filterExternalIssuesC, m_filterExternalIssues); + + QVariantList errorKinds; + foreach (int i, m_visibleErrorKinds) + errorKinds << i; + map.insert(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 Analyzation"); +} + +QWidget* AbstractMemcheckSettings::createConfigWidget(QWidget *parent) +{ + return new MemcheckConfigWidget(this, parent); +} + +MemcheckGlobalSettings::MemcheckGlobalSettings() +{ +} + +MemcheckGlobalSettings::~MemcheckGlobalSettings() +{ +} + +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); +} + +bool MemcheckGlobalSettings::fromMap(const QVariantMap &map) +{ + AbstractMemcheckSettings::fromMap(map); + m_suppressionFiles = map.value(suppressionFilesC).toStringList(); + return true; +} + +QVariantMap MemcheckGlobalSettings::toMap() const +{ + QVariantMap map = AbstractMemcheckSettings::toMap(); + map.insert(suppressionFilesC, m_suppressionFiles); + return map; +} + +MemcheckGlobalSettings* globalMemcheckSettings() +{ + MemcheckGlobalSettings* ret = AnalyzerGlobalSettings::instance()->subConfig<MemcheckGlobalSettings>(); + QTC_ASSERT(ret, return 0); + return ret; +} + +MemcheckProjectSettings::MemcheckProjectSettings() +{ + // take defaults from global settings + fromMap(globalMemcheckSettings()->toMap()); +} + +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; +} diff --git a/src/plugins/memcheck/memchecksettings.h b/src/plugins/memcheck/memchecksettings.h new file mode 100644 index 0000000000000000000000000000000000000000..143ed21dfb26a3f3417e6c1b7dc81e0b2989e8a5 --- /dev/null +++ b/src/plugins/memcheck/memchecksettings.h @@ -0,0 +1,145 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef ANALYZER_INTERNAL_MEMCHECKSETTINGS_H +#define ANALYZER_INTERNAL_MEMCHECKSETTINGS_H + +#include "analyzersettings.h" + +namespace Analyzer { +namespace Internal { + +/** + * Generic memcheck settings + */ +class AbstractMemcheckSettings : public AbstractAnalyzerSubConfig +{ + Q_OBJECT +public: + AbstractMemcheckSettings(); + virtual ~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 +{ + Q_OBJECT +public: + MemcheckGlobalSettings(); + virtual ~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; + +protected: + bool fromMap(const QVariantMap &map); + +private: + QStringList m_suppressionFiles; +}; + +/** + * Per-project memcheck settings, saves a diff to the global suppression files list + */ +class MemcheckProjectSettings : public AbstractMemcheckSettings +{ + Q_OBJECT +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; + +protected: + bool fromMap(const QVariantMap &map); + +private: + QStringList m_disabledGlobalSuppressionFiles; + QStringList m_addedSuppressionFiles; +}; + +} +} + +#endif // ANALYZER_INTERNAL_MEMCHECKSETTINGS_H diff --git a/src/plugins/memcheck/memchecktool.cpp b/src/plugins/memcheck/memchecktool.cpp new file mode 100644 index 0000000000000000000000000000000000000000..72a1737438627744c37da295ee5fb68abd276ae8 --- /dev/null +++ b/src/plugins/memcheck/memchecktool.cpp @@ -0,0 +1,561 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "memchecktool.h" +#include "memcheckengine.h" +#include "memcheckerrorview.h" +#include "memchecksettings.h" + +#include <analyzerbase/analyzermanager.h> +#include <analyzerbase/analyzerconstants.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 <valgrindtoolbase/valgrindsettings.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 <QString> +#include <QLatin1String> +#include <QFileInfo> +#include <QDockWidget> +#include <QFile> +#include <QDir> +#include <QHBoxLayout> +#include <QComboBox> +#include <QLabel> +#include <QSpinBox> +#include <QAction> +#include <QMenu> +#include <QMessageBox> +#include <QToolButton> +#include <QCheckBox> +#include <utils/stylehelper.h> + +using namespace Analyzer; +using namespace Analyzer::Internal; +using namespace Valgrind::XmlProtocol; + +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; +} + +MemcheckTool::MemcheckTool(QObject *parent) : + Analyzer::IAnalyzerTool(parent), + m_settings(0), + m_errorModel(0), + m_errorProxyModel(0), + m_errorView(0), + m_prevAction(0), + m_nextAction(0), + m_clearAction(0), + m_filterProjectAction(0) +{ + connect(ProjectExplorer::ProjectExplorerPlugin::instance(), + SIGNAL(updateRunActions()), SLOT(maybeActiveRunConfigurationChanged())); +} + +void MemcheckTool::settingsDestroyed(QObject *settings) +{ + Q_ASSERT(m_settings == settings); + 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; +} + +namespace Analyzer { +namespace Internal { +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 + 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; +}; +} +} + +static void initKindFilterAction(QAction *action, const QList<int> &kinds) +{ + action->setCheckable(true); + QVariantList data; + foreach (int kind, kinds) + data << kind; + action->setData(data); +} + +void MemcheckTool::initialize(ExtensionSystem::IPlugin */*plugin*/) +{ + AnalyzerManager *am = AnalyzerManager::instance(); + + m_errorView = new MemcheckErrorView; + m_errorView->setFrameStyle(QFrame::NoFrame); + m_errorView->setAttribute(Qt::WA_MacShowFocusRect, false); + m_errorModel = new ErrorListModel(m_errorView); + m_frameFinder = new FrameFinder; + m_errorModel->setRelevantFrameFinder(QSharedPointer<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"); + + am->createDockWidget(this, tr("Memory Errors"), m_errorView, Qt::BottomDockWidgetArea); + + QHBoxLayout *layout = new QHBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + + { // clear / next / prev + QToolButton *button = 0; + + m_clearAction = new QAction(this); + m_clearAction->setIcon(QIcon(Core::Constants::ICON_CLEAN_PANE)); + m_clearAction->setText(tr("Clear")); + connect(m_clearAction, SIGNAL(triggered()), this, SLOT(slotClear())); + button = new QToolButton; + button->setDefaultAction(m_clearAction); + layout->addWidget(button); + + m_prevAction = new QAction(this); + m_prevAction->setIcon(QIcon(QLatin1String(Core::Constants::ICON_PREV))); + m_prevAction->setText(tr("Previous Item")); + connect(m_prevAction, SIGNAL(triggered()), this, SLOT(slotPrev())); + button = new QToolButton; + button->setDefaultAction(m_prevAction); + layout->addWidget(button); + + m_nextAction = new QAction(this); + m_nextAction->setIcon(QIcon(QLatin1String(Core::Constants::ICON_NEXT))); + m_nextAction->setText(tr("Next Item")); + connect(m_nextAction, SIGNAL(triggered()), this, SLOT(slotNext())); + button = new QToolButton; + button->setDefaultAction(m_nextAction); + layout->addWidget(button); + } + { + // 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); + + QAction *a = filterMenu->addAction(tr("Definite Memory Leaks")); + initKindFilterAction(a, QList<int>() << Leak_DefinitelyLost << Leak_IndirectlyLost); + m_errorFilterActions << a; + + a = filterMenu->addAction(tr("Possible Memory Leaks")); + initKindFilterAction(a, QList<int>() << Leak_PossiblyLost << Leak_StillReachable); + m_errorFilterActions << a; + + a = filterMenu->addAction(tr("Use of Uninitialized Memory")); + initKindFilterAction(a, QList<int>() << InvalidRead << InvalidWrite << InvalidJump << Overlap + << InvalidMemPool << UninitCondition << UninitValue + << SyscallParam << ClientCheck); + m_errorFilterActions << a; + + a = filterMenu->addAction(tr("Invalid Frees")); + initKindFilterAction(a, QList<int>() << InvalidFree << MismatchedFree); + m_errorFilterActions << a; + + filterMenu->addSeparator(); + + m_filterProjectAction = filterMenu->addAction(tr("External Errors")); + m_filterProjectAction->setToolTip(tr("Show issues originating outside currently opened projects.")); + m_filterProjectAction->setCheckable(true); + + m_suppressionSeparator = filterMenu->addSeparator(); + m_suppressionSeparator->setText(tr("Suppressions")); + m_suppressionSeparator->setToolTip(tr("These suppression files where used in the last memory analyzer run.")); + + connect(filterMenu, SIGNAL(triggered(QAction *)), SLOT(updateErrorFilter())); + filterButton->setMenu(filterMenu); + layout->addWidget(filterButton); + } + + layout->addStretch(); + QWidget *toolbar = new QWidget; + toolbar->setLayout(layout); + am->setToolbar(this, toolbar); + + // register shortcuts + Core::ActionManager *actionManager = Core::ICore::instance()->actionManager(); + const Core::Context analyzeContext(Constants::C_ANALYZEMODE); + + Core::Command *cmd; + + cmd = actionManager->registerAction(m_prevAction, "Analyzer.MemcheckTool.previtem", analyzeContext); + cmd->setDefaultKeySequence(QKeySequence("Shift+F10")); + + cmd = actionManager->registerAction(m_nextAction, "Analyzer.MemcheckTool.nextitem", analyzeContext); + cmd->setDefaultKeySequence(QKeySequence("F10")); + + maybeActiveRunConfigurationChanged(); +} + +IAnalyzerEngine *MemcheckTool::createEngine(ProjectExplorer::RunConfiguration *runConfiguration) +{ + m_frameFinder->setFiles(runConfiguration->target()->project()->files(ProjectExplorer::Project::AllFiles)); + + MemcheckEngine *engine = new MemcheckEngine(runConfiguration); + + connect(engine, SIGNAL(starting(const IAnalyzerEngine*)), + this, SLOT(engineStarting(const IAnalyzerEngine*))); + connect(engine, SIGNAL(parserError(Valgrind::XmlProtocol::Error)), + this, SLOT(parserError(Valgrind::XmlProtocol::Error))); + connect(engine, SIGNAL(internalParserError(QString)), + this, SLOT(internalParserError(QString))); + return engine; +} + +void MemcheckTool::engineStarting(const IAnalyzerEngine *engine) +{ + slotClear(); + + const QString dir = engine->runConfiguration()->target()->project()->projectDirectory(); + const MemcheckEngine *mEngine = dynamic_cast<const MemcheckEngine*>(engine); + QTC_ASSERT(mEngine, return); + const QString name = QFileInfo(mEngine->executable()).fileName(); + + m_errorView->setDefaultSuppressionFile(dir + QDir::separator() + name + QLatin1String(".supp")); + + QMenu *menu = qobject_cast<QMenu*>(m_suppressionSeparator->parentWidget()); + 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; + } +} + +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::slotNext() +{ + QModelIndex current = m_errorView->selectionModel()->currentIndex(); + if (!current.isValid()) { + if (!m_errorView->model()->rowCount()) + return; + + current = m_errorView->model()->index(0, 0); + } else if (current.row() < m_errorView->model()->rowCount(current.parent()) - 1) { + current = m_errorView->model()->index(current.row() + 1, 0); + } else { + return; + } + + m_errorView->selectionModel()->setCurrentIndex(current, QItemSelectionModel::ClearAndSelect); + m_errorView->scrollTo(current); +} + +void MemcheckTool::slotPrev() +{ + QModelIndex current = m_errorView->selectionModel()->currentIndex(); + if (!current.isValid()) { + if (!m_errorView->model()->rowCount()) + return; + current = m_errorView->model()->index(m_errorView->model()->rowCount() - 1, 0); + } else if (current.row() > 0) { + current = m_errorView->model()->index(current.row() - 1, 0); + } else { + return; + } + + m_errorView->selectionModel()->setCurrentIndex(current, QItemSelectionModel::ClearAndSelect); + m_errorView->scrollTo(current); +} + +void MemcheckTool::slotClear() +{ + m_errorModel->clear(); + + qDeleteAll(m_suppressionActions); + m_suppressionActions.clear(); + QTC_ASSERT(m_suppressionSeparator->parentWidget()->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); +} diff --git a/src/plugins/memcheck/memchecktool.h b/src/plugins/memcheck/memchecktool.h new file mode 100644 index 0000000000000000000000000000000000000000..29d211e6e550e14f0b2c4f68bc313eafca1693a0 --- /dev/null +++ b/src/plugins/memcheck/memchecktool.h @@ -0,0 +1,135 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef MEMCHECKTOOL_H +#define MEMCHECKTOOL_H + +#include "ianalyzertool.h" + +#include <QSortFilterProxyModel> +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE +class QItemSelection; +class QTreeView; +class QModelIndex; +class QAction; +class QSpinBox; +class QCheckBox; +QT_END_NAMESPACE + +namespace Valgrind { +namespace XmlProtocol { +class ErrorListModel; +class Error; +} +} + +namespace Analyzer { + +class AnalyzerSettings; + +namespace Internal { + +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 IAnalyzerTool +{ + Q_OBJECT +public: + explicit MemcheckTool(QObject *parent = 0); + + QString id() const; + QString displayName() const; + ToolMode mode() const; + + void initialize(ExtensionSystem::IPlugin *plugin); + + IAnalyzerEngine *createEngine(ProjectExplorer::RunConfiguration *runConfiguration); + +private slots: + void settingsDestroyed(QObject *settings); + void maybeActiveRunConfigurationChanged(); + + void engineStarting(const IAnalyzerEngine *engine); + void parserError(const Valgrind::XmlProtocol::Error &error); + void internalParserError(const QString &errorString); + + void slotNext(); + void slotPrev(); + void slotClear(); + + void updateErrorFilter(); + void suppressionActionTriggered(); + +private: + AnalyzerSettings *m_settings; + + FrameFinder *m_frameFinder; + Valgrind::XmlProtocol::ErrorListModel *m_errorModel; + MemcheckErrorFilterProxyModel *m_errorProxyModel; + MemcheckErrorView *m_errorView; + + QAction *m_prevAction; + QAction *m_nextAction; + QAction *m_clearAction; + QList<QAction *> m_errorFilterActions; + QAction *m_filterProjectAction; + QList<QAction *> m_suppressionActions; + QAction *m_suppressionSeparator; +}; + +} // namespace Internal +} // namespace Analyzer + +#endif // MEMCHECKTOOL_H diff --git a/src/plugins/memcheck/suppressiondialog.cpp b/src/plugins/memcheck/suppressiondialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..07024ae058096c7fe55e15496b42997fd81ead54 --- /dev/null +++ b/src/plugins/memcheck/suppressiondialog.cpp @@ -0,0 +1,246 @@ +/************************************************************************** +** +** 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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@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/qtcassert.h> + +#include <QFile> +#include <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 Analyzer::Internal; +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 inline bool equalSuppression(const Error &error, const Error &suppressed) +{ + if (error.kind() != suppressed.kind() || error.suppression().isNull()) + return false; + + const QVector< SuppressionFrame > errorFrames = error.suppression().frames(); + const QVector< SuppressionFrame > 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(); +} + +} + +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; + foreach(const QModelIndex &index, m_view->selectionModel()->selectedRows()) { + 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")); +} + +void SuppressionDialog::accept() +{ + const QString path = m_ui->fileChooser->path(); + QTC_ASSERT(!path.isEmpty(), return); + QTC_ASSERT(!m_ui->suppressionEdit->toPlainText().trimmed().isEmpty(), return); + + QFile file(path); + bool opened = file.open(QIODevice::WriteOnly | QIODevice::Append); + QTC_ASSERT(opened, return); + + QTextStream stream(&file); + stream << m_ui->suppressionEdit->toPlainText(); + file.close(); + + // 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); +} diff --git a/src/plugins/memcheck/suppressiondialog.h b/src/plugins/memcheck/suppressiondialog.h new file mode 100644 index 0000000000000000000000000000000000000000..89c504c56a456ddd11716386e6841a714a919b7d --- /dev/null +++ b/src/plugins/memcheck/suppressiondialog.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 (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + + +#ifndef ANALYZER_VALGRIND_INTERNAL_SUPPRESSIONDIALOG_H +#define ANALYZER_VALGRIND_INTERNAL_SUPPRESSIONDIALOG_H + +#include <QDialog> + +#include <valgrind/xmlprotocol/error.h> + +QT_BEGIN_NAMESPACE +namespace Ui { +class SuppressionDialog; +} +QT_END_NAMESPACE + +namespace Analyzer { + +class AnalyzerSettings; + +namespace Internal { + +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(); + +private slots: + void validate(); + +private: + MemcheckErrorView *m_view; + Ui::SuppressionDialog *m_ui; + AnalyzerSettings *m_settings; + bool m_cleanupIfCanceled; + QList<Valgrind::XmlProtocol::Error> m_errors; +}; + +} +} + +#endif // ANALYZER_VALGRIND_INTERNAL_SUPPRESSIONDIALOG_H diff --git a/src/plugins/memcheck/suppressiondialog.ui b/src/plugins/memcheck/suppressiondialog.ui new file mode 100644 index 0000000000000000000000000000000000000000..ad3e6df70c373516343bedf013cdbb086b22e2cd --- /dev/null +++ b/src/plugins/memcheck/suppressiondialog.ui @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SuppressionDialog</class> + <widget class="QDialog" name="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>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>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/plugins.pro b/src/plugins/plugins.pro index 9e7a2aa7011584ee20af069d2e06830aef7cf5b6..209562857c00306c7d5cc1d2f8540209bf561dde 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -43,7 +43,8 @@ SUBDIRS = plugin_coreplugin \ debugger/dumper.pro !win32 { - SUBDIRS += plugin_valgrindtoolbase + SUBDIRS += plugin_valgrindtoolbase \ + plugin_memcheck } linux-* { @@ -255,6 +256,11 @@ plugin_analyzerbase.depends += plugin_projectexplorer plugin_valgrindtoolbase.subdir = valgrindtoolbase plugin_valgrindtoolbase.depends = plugin_coreplugin plugin_valgrindtoolbase.depends += plugin_analyzerbase + + plugin_memcheck.subdir = memcheck + plugin_memcheck.depends = plugin_coreplugin + plugin_memcheck.depends += plugin_analyzerbase + plugin_memcheck.depends += plugin_valgrindtoolbase } plugin_qmljstools.subdir = qmljstools