2011-03-04 12:15:19 +01:00
|
|
|
/**************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** 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)
|
|
|
|
|
**
|
2011-11-02 15:59:12 +01:00
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
2011-03-04 12:15:19 +01:00
|
|
|
**
|
|
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
|
**
|
2011-04-13 08:42:33 +02:00
|
|
|
** 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.
|
2011-03-04 12:15:19 +01:00
|
|
|
**
|
|
|
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
2011-04-13 08:42:33 +02:00
|
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
2011-03-04 12:15:19 +01:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
2011-04-13 08:42:33 +02:00
|
|
|
** Other Usage
|
|
|
|
|
**
|
|
|
|
|
** Alternatively, this file may be used in accordance with the terms and
|
|
|
|
|
** conditions contained in a signed written agreement between you and Nokia.
|
|
|
|
|
**
|
2011-03-04 12:15:19 +01:00
|
|
|
** If you have questions regarding the use of this file, please contact
|
2011-11-02 15:59:12 +01:00
|
|
|
** Nokia at qt-info@nokia.com.
|
2011-03-04 12:15:19 +01:00
|
|
|
**
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "memchecktool.h"
|
|
|
|
|
#include "memcheckengine.h"
|
|
|
|
|
#include "memcheckerrorview.h"
|
2011-05-23 13:50:28 +02:00
|
|
|
#include "valgrindsettings.h"
|
2011-07-04 10:50:44 +02:00
|
|
|
#include "valgrindplugin.h"
|
2011-03-04 12:15:19 +01:00
|
|
|
|
|
|
|
|
#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 <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>
|
2011-09-05 16:10:37 +02:00
|
|
|
#include <coreplugin/id.h>
|
2011-03-04 12:15:19 +01:00
|
|
|
|
|
|
|
|
#include <texteditor/basetexteditor.h>
|
|
|
|
|
|
|
|
|
|
#include <utils/fancymainwindow.h>
|
|
|
|
|
#include <utils/styledbar.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
2011-03-08 13:56:52 +01:00
|
|
|
#include <QtCore/QString>
|
|
|
|
|
#include <QtCore/QLatin1String>
|
|
|
|
|
#include <QtCore/QFileInfo>
|
|
|
|
|
#include <QtCore/QFile>
|
|
|
|
|
#include <QtCore/QDir>
|
|
|
|
|
|
|
|
|
|
#include <QtGui/QDockWidget>
|
|
|
|
|
#include <QtGui/QHBoxLayout>
|
|
|
|
|
#include <QtGui/QComboBox>
|
|
|
|
|
#include <QtGui/QLabel>
|
|
|
|
|
#include <QtGui/QSpinBox>
|
|
|
|
|
#include <QtGui/QAction>
|
|
|
|
|
#include <QtGui/QMenu>
|
|
|
|
|
#include <QtGui/QMessageBox>
|
|
|
|
|
#include <QtGui/QToolButton>
|
|
|
|
|
#include <QtGui/QCheckBox>
|
2011-03-04 12:15:19 +01:00
|
|
|
#include <utils/stylehelper.h>
|
|
|
|
|
|
2011-04-04 14:39:29 +02:00
|
|
|
using namespace Analyzer;
|
2011-03-04 12:15:19 +01:00
|
|
|
using namespace Valgrind::XmlProtocol;
|
|
|
|
|
|
2011-05-23 13:50:28 +02:00
|
|
|
namespace Valgrind {
|
2011-03-10 16:11:20 +01:00
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
// ---------------------------- MemcheckErrorFilterProxyModel
|
2011-03-04 12:15:19 +01:00
|
|
|
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;
|
2011-05-11 16:26:34 +02:00
|
|
|
foreach (ProjectExplorer::Project *project, session->projects()) {
|
2011-03-04 12:15:19 +01:00
|
|
|
validFolders << project->projectDirectory();
|
2011-05-11 16:26:34 +02:00
|
|
|
foreach (ProjectExplorer::Target *target, project->targets()) {
|
|
|
|
|
foreach (ProjectExplorer::BuildConfiguration *config, target->buildConfigurations()) {
|
2011-03-04 12:15:19 +01:00
|
|
|
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);
|
2011-05-11 16:26:34 +02:00
|
|
|
foreach (const QString &folder, validFolders) {
|
2011-03-04 12:15:19 +01:00
|
|
|
if (frame.object().startsWith(folder)) {
|
|
|
|
|
inProject = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!inProject)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-10 16:11:20 +01:00
|
|
|
static void initKindFilterAction(QAction *action, const QList<int> &kinds)
|
|
|
|
|
{
|
|
|
|
|
action->setCheckable(true);
|
|
|
|
|
QVariantList data;
|
|
|
|
|
foreach (int kind, kinds)
|
|
|
|
|
data << kind;
|
|
|
|
|
action->setData(data);
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-30 18:55:48 +02:00
|
|
|
MemcheckTool::MemcheckTool(QObject *parent)
|
2011-12-27 22:41:31 +01:00
|
|
|
: ValgrindTool(parent)
|
2011-03-04 12:15:19 +01:00
|
|
|
{
|
2011-06-27 15:43:15 +02:00
|
|
|
m_settings = 0;
|
|
|
|
|
m_errorModel = 0;
|
|
|
|
|
m_errorProxyModel = 0;
|
|
|
|
|
m_errorView = 0;
|
|
|
|
|
m_filterMenu = 0;
|
2011-03-10 16:11:20 +01:00
|
|
|
setObjectName(QLatin1String("MemcheckTool"));
|
2011-06-27 15:43:15 +02:00
|
|
|
|
|
|
|
|
m_filterProjectAction = new QAction(tr("External Errors"), this);
|
2011-06-28 19:14:08 +02:00
|
|
|
m_filterProjectAction->setToolTip(
|
|
|
|
|
tr("Show issues originating outside currently opened projects."));
|
2011-06-27 15:43:15 +02:00
|
|
|
m_filterProjectAction->setCheckable(true);
|
|
|
|
|
|
|
|
|
|
m_suppressionSeparator = new QAction(tr("Suppressions"), this);
|
|
|
|
|
m_suppressionSeparator->setSeparator(true);
|
2011-06-28 19:14:08 +02:00
|
|
|
m_suppressionSeparator->setToolTip(
|
|
|
|
|
tr("These suppression files were used in the last memory analyzer run."));
|
2011-03-10 16:11:20 +01:00
|
|
|
|
|
|
|
|
QAction *a = new QAction(tr("Definite Memory Leaks"), this);
|
|
|
|
|
initKindFilterAction(a, QList<int>() << Leak_DefinitelyLost << Leak_IndirectlyLost);
|
2011-06-27 15:43:15 +02:00
|
|
|
m_errorFilterActions.append(a);
|
2011-03-10 16:11:20 +01:00
|
|
|
|
|
|
|
|
a = new QAction(tr("Possible Memory Leaks"), this);
|
|
|
|
|
initKindFilterAction(a, QList<int>() << Leak_PossiblyLost << Leak_StillReachable);
|
2011-06-27 15:43:15 +02:00
|
|
|
m_errorFilterActions.append(a);
|
2011-03-10 16:11:20 +01:00
|
|
|
|
|
|
|
|
a = new QAction(tr("Use of Uninitialized Memory"), this);
|
|
|
|
|
initKindFilterAction(a, QList<int>() << InvalidRead << InvalidWrite << InvalidJump << Overlap
|
|
|
|
|
<< InvalidMemPool << UninitCondition << UninitValue
|
|
|
|
|
<< SyscallParam << ClientCheck);
|
2011-06-27 15:43:15 +02:00
|
|
|
m_errorFilterActions.append(a);
|
2011-03-10 16:11:20 +01:00
|
|
|
|
2011-06-27 15:43:15 +02:00
|
|
|
a = new QAction(tr("Invalid Calls to \"free()\""), this);
|
2011-03-10 16:11:20 +01:00
|
|
|
initKindFilterAction(a, QList<int>() << InvalidFree << MismatchedFree);
|
2011-06-27 15:43:15 +02:00
|
|
|
m_errorFilterActions.append(a);
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckTool::settingsDestroyed(QObject *settings)
|
|
|
|
|
{
|
2011-04-04 14:39:29 +02:00
|
|
|
QTC_ASSERT(m_settings == settings, return);
|
2011-03-04 12:15:19 +01:00
|
|
|
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 *)));
|
|
|
|
|
|
2011-07-12 16:47:32 +02:00
|
|
|
ValgrindBaseSettings *memcheckSettings = m_settings->subConfig<ValgrindBaseSettings>();
|
2011-03-04 12:15:19 +01:00
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-11 12:16:20 +01:00
|
|
|
Core::Id MemcheckTool::id() const
|
2011-03-04 12:15:19 +01:00
|
|
|
{
|
2011-07-12 16:47:32 +02:00
|
|
|
return "Memcheck";
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString MemcheckTool::displayName() const
|
|
|
|
|
{
|
2011-07-12 16:47:32 +02:00
|
|
|
return tr("Valgrind Memory Analyzer");
|
2011-06-28 19:14:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString MemcheckTool::description() const
|
|
|
|
|
{
|
|
|
|
|
return tr("Valgrind Analyze Memory uses the \"memcheck\" tool to find "
|
|
|
|
|
"memory leaks");
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
|
2011-07-01 14:19:12 +02:00
|
|
|
IAnalyzerTool::ToolMode MemcheckTool::toolMode() const
|
2011-03-04 12:15:19 +01:00
|
|
|
{
|
|
|
|
|
return DebugMode;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-20 09:12:34 +02:00
|
|
|
class FrameFinder : public ErrorListModel::RelevantFrameFinder
|
|
|
|
|
{
|
2011-03-04 12:15:19 +01:00
|
|
|
public:
|
2011-05-20 09:12:34 +02:00
|
|
|
Frame findRelevant(const Error &error) const
|
|
|
|
|
{
|
2011-03-04 12:15:19 +01:00
|
|
|
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
|
2011-04-04 14:39:29 +02:00
|
|
|
if (!m_projectFiles.isEmpty()) {
|
2011-05-11 16:26:34 +02:00
|
|
|
foreach (const Frame &frame, frames) {
|
2011-04-04 14:39:29 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//if no frame belonging to the project was found, return the first one that is not malloc/new
|
2011-05-11 16:26:34 +02:00
|
|
|
foreach (const Frame &frame, frames) {
|
2011-03-04 12:15:19 +01:00
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2011-07-06 16:52:14 +02:00
|
|
|
QWidget *MemcheckTool::createWidgets()
|
2011-03-04 12:15:19 +01:00
|
|
|
{
|
2011-07-06 16:52:14 +02:00
|
|
|
QTC_ASSERT(!m_errorView, return 0);
|
2011-06-27 15:43:15 +02:00
|
|
|
|
2011-06-30 13:44:22 +02:00
|
|
|
Utils::FancyMainWindow *mw = AnalyzerManager::mainWindow();
|
2011-06-27 15:43:15 +02:00
|
|
|
|
|
|
|
|
m_errorView = new MemcheckErrorView;
|
|
|
|
|
m_errorView->setObjectName(QLatin1String("MemcheckErrorView"));
|
|
|
|
|
m_errorView->setFrameStyle(QFrame::NoFrame);
|
|
|
|
|
m_errorView->setAttribute(Qt::WA_MacShowFocusRect, false);
|
|
|
|
|
m_errorModel = new ErrorListModel(m_errorView);
|
|
|
|
|
m_frameFinder = new Internal::FrameFinder;
|
|
|
|
|
m_errorModel->setRelevantFrameFinder(QSharedPointer<Internal::FrameFinder>(m_frameFinder));
|
|
|
|
|
m_errorProxyModel = new MemcheckErrorFilterProxyModel(m_errorView);
|
|
|
|
|
m_errorProxyModel->setSourceModel(m_errorModel);
|
|
|
|
|
m_errorProxyModel->setDynamicSortFilter(true);
|
|
|
|
|
m_errorView->setModel(m_errorProxyModel);
|
|
|
|
|
m_errorView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
|
|
|
// make m_errorView->selectionModel()->selectedRows() return something
|
|
|
|
|
m_errorView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
|
|
|
m_errorView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
|
|
|
|
m_errorView->setAutoScroll(false);
|
|
|
|
|
m_errorView->setObjectName("Valgrind.MemcheckTool.ErrorView");
|
|
|
|
|
|
2011-06-30 13:44:22 +02:00
|
|
|
QDockWidget *errorDock = AnalyzerManager::createDockWidget
|
|
|
|
|
(this, tr("Memory Issues"), m_errorView, Qt::BottomDockWidgetArea);
|
2011-07-13 09:27:25 +02:00
|
|
|
errorDock->show();
|
2011-06-27 15:43:15 +02:00
|
|
|
mw->splitDockWidget(mw->toolBarDockWidget(), errorDock, Qt::Vertical);
|
|
|
|
|
|
|
|
|
|
connect(ProjectExplorer::ProjectExplorerPlugin::instance(),
|
|
|
|
|
SIGNAL(updateRunActions()), SLOT(maybeActiveRunConfigurationChanged()));
|
|
|
|
|
|
2011-07-06 16:52:14 +02:00
|
|
|
//
|
|
|
|
|
// The Control Widget.
|
|
|
|
|
//
|
2011-06-27 15:43:15 +02:00
|
|
|
QAction *action = 0;
|
2011-03-04 12:15:19 +01:00
|
|
|
QHBoxLayout *layout = new QHBoxLayout;
|
2011-06-27 15:43:15 +02:00
|
|
|
QToolButton *button = 0;
|
|
|
|
|
|
2011-03-04 12:15:19 +01:00
|
|
|
layout->setMargin(0);
|
|
|
|
|
layout->setSpacing(0);
|
2011-06-27 15:43:15 +02:00
|
|
|
|
|
|
|
|
// Go to previous leak.
|
|
|
|
|
action = new QAction(this);
|
|
|
|
|
action->setDisabled(true);
|
|
|
|
|
action->setIcon(QIcon(QLatin1String(":core/images/prev.png")));
|
|
|
|
|
action->setToolTip(tr("Go to previous leak."));
|
|
|
|
|
connect(action, SIGNAL(triggered(bool)), m_errorView, SLOT(goBack()));
|
|
|
|
|
button = new QToolButton;
|
|
|
|
|
button->setDefaultAction(action);
|
|
|
|
|
layout->addWidget(button);
|
|
|
|
|
m_goBack = action;
|
|
|
|
|
|
|
|
|
|
// Go to next leak.
|
|
|
|
|
action = new QAction(this);
|
|
|
|
|
action->setDisabled(true);
|
|
|
|
|
action->setIcon(QIcon(QLatin1String(":core/images/next.png")));
|
|
|
|
|
action->setToolTip(tr("Go to next leak."));
|
|
|
|
|
connect(action, SIGNAL(triggered(bool)), m_errorView, SLOT(goNext()));
|
|
|
|
|
button = new QToolButton;
|
|
|
|
|
button->setDefaultAction(action);
|
|
|
|
|
layout->addWidget(button);
|
|
|
|
|
m_goNext = action;
|
|
|
|
|
|
2011-03-04 12:15:19 +01:00
|
|
|
QToolButton *filterButton = new QToolButton;
|
|
|
|
|
filterButton->setIcon(QIcon(Core::Constants::ICON_FILTER));
|
|
|
|
|
filterButton->setText(tr("Error Filter"));
|
|
|
|
|
filterButton->setPopupMode(QToolButton::InstantPopup);
|
2011-06-27 15:43:15 +02:00
|
|
|
|
|
|
|
|
m_filterMenu = new QMenu(filterButton);
|
2011-03-10 16:11:20 +01:00
|
|
|
foreach (QAction *filterAction, m_errorFilterActions)
|
2011-06-27 15:43:15 +02:00
|
|
|
m_filterMenu->addAction(filterAction);
|
|
|
|
|
m_filterMenu->addSeparator();
|
|
|
|
|
m_filterMenu->addAction(m_filterProjectAction);
|
|
|
|
|
m_filterMenu->addAction(m_suppressionSeparator);
|
|
|
|
|
connect(m_filterMenu, SIGNAL(triggered(QAction *)), SLOT(updateErrorFilter()));
|
|
|
|
|
filterButton->setMenu(m_filterMenu);
|
2011-03-04 12:15:19 +01:00
|
|
|
layout->addWidget(filterButton);
|
|
|
|
|
|
2011-06-27 15:43:15 +02:00
|
|
|
layout->addStretch();
|
|
|
|
|
QWidget *widget = new QWidget;
|
|
|
|
|
widget->setObjectName(QLatin1String("MemCheckToolBarWidget"));
|
|
|
|
|
widget->setLayout(layout);
|
|
|
|
|
return widget;
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
|
2011-04-04 14:39:29 +02:00
|
|
|
IAnalyzerEngine *MemcheckTool::createEngine(const AnalyzerStartParameters &sp,
|
|
|
|
|
ProjectExplorer::RunConfiguration *runConfiguration)
|
2011-03-04 12:15:19 +01:00
|
|
|
{
|
2011-06-30 13:44:22 +02:00
|
|
|
m_frameFinder->setFiles(runConfiguration ? runConfiguration->target()
|
|
|
|
|
->project()->files(ProjectExplorer::Project::AllFiles) : QStringList());
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2011-06-30 13:44:22 +02:00
|
|
|
MemcheckEngine *engine = new MemcheckEngine(this, sp, runConfiguration);
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2011-04-04 14:39:29 +02:00
|
|
|
connect(engine, SIGNAL(starting(const Analyzer::IAnalyzerEngine*)),
|
|
|
|
|
this, SLOT(engineStarting(const Analyzer::IAnalyzerEngine*)));
|
2011-03-04 12:15:19 +01:00
|
|
|
connect(engine, SIGNAL(parserError(Valgrind::XmlProtocol::Error)),
|
|
|
|
|
this, SLOT(parserError(Valgrind::XmlProtocol::Error)));
|
|
|
|
|
connect(engine, SIGNAL(internalParserError(QString)),
|
|
|
|
|
this, SLOT(internalParserError(QString)));
|
2011-03-16 13:49:28 +01:00
|
|
|
connect(engine, SIGNAL(finished()), this, SLOT(finished()));
|
2011-06-30 13:44:22 +02:00
|
|
|
AnalyzerManager::showStatusMessage(AnalyzerManager::msgToolStarted(displayName()));
|
2011-03-04 12:15:19 +01:00
|
|
|
return engine;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-04 18:33:47 +02:00
|
|
|
void MemcheckTool::startTool(StartMode mode)
|
|
|
|
|
{
|
|
|
|
|
ValgrindPlugin::startValgrindTool(this, mode);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-04 12:15:19 +01:00
|
|
|
void MemcheckTool::engineStarting(const IAnalyzerEngine *engine)
|
|
|
|
|
{
|
2011-12-13 14:01:52 +01:00
|
|
|
setBusyCursor(true);
|
2011-03-10 16:11:20 +01:00
|
|
|
clearErrorView();
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2011-04-04 14:39:29 +02:00
|
|
|
QString dir;
|
|
|
|
|
if (ProjectExplorer::RunConfiguration *rc = engine->runConfiguration())
|
|
|
|
|
dir = rc->target()->project()->projectDirectory() + QDir::separator();
|
|
|
|
|
|
|
|
|
|
const MemcheckEngine *mEngine = dynamic_cast<const MemcheckEngine *>(engine);
|
2011-03-04 12:15:19 +01:00
|
|
|
QTC_ASSERT(mEngine, return);
|
|
|
|
|
const QString name = QFileInfo(mEngine->executable()).fileName();
|
|
|
|
|
|
2011-04-04 14:39:29 +02:00
|
|
|
m_errorView->setDefaultSuppressionFile(dir + name + QLatin1String(".supp"));
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2011-05-11 16:26:34 +02:00
|
|
|
foreach (const QString &file, mEngine->suppressionFiles()) {
|
2011-06-27 15:43:15 +02:00
|
|
|
QAction *action = m_filterMenu->addAction(QFileInfo(file).fileName());
|
2011-03-04 12:15:19 +01:00
|
|
|
action->setToolTip(file);
|
|
|
|
|
action->setData(file);
|
|
|
|
|
connect(action, SIGNAL(triggered(bool)),
|
|
|
|
|
this, SLOT(suppressionActionTriggered()));
|
2011-06-27 15:43:15 +02:00
|
|
|
m_suppressionActions.append(action);
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckTool::suppressionActionTriggered()
|
|
|
|
|
{
|
2011-04-04 14:39:29 +02:00
|
|
|
QAction *action = qobject_cast<QAction *>(sender());
|
2011-03-04 12:15:19 +01:00
|
|
|
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)
|
|
|
|
|
{
|
2011-05-20 09:12:34 +02:00
|
|
|
QMessageBox::critical(m_errorView, tr("Internal Error"),
|
|
|
|
|
tr("Error occurred parsing valgrind output: %1").arg(errorString));
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
|
2011-03-10 16:11:20 +01:00
|
|
|
void MemcheckTool::clearErrorView()
|
2011-03-04 12:15:19 +01:00
|
|
|
{
|
2011-07-06 16:52:14 +02:00
|
|
|
QTC_ASSERT(m_errorView, return);
|
2011-03-04 12:15:19 +01:00
|
|
|
m_errorModel->clear();
|
|
|
|
|
|
|
|
|
|
qDeleteAll(m_suppressionActions);
|
|
|
|
|
m_suppressionActions.clear();
|
2011-06-27 15:43:15 +02:00
|
|
|
//QTC_ASSERT(filterMenu()->actions().last() == m_suppressionSeparator, qt_noop());
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckTool::updateErrorFilter()
|
|
|
|
|
{
|
2011-07-06 16:52:14 +02:00
|
|
|
QTC_ASSERT(m_errorView, return);
|
2011-03-04 12:15:19 +01:00
|
|
|
QTC_ASSERT(m_settings, return);
|
|
|
|
|
|
2011-07-12 16:47:32 +02:00
|
|
|
ValgrindBaseSettings *memcheckSettings = m_settings->subConfig<ValgrindBaseSettings>();
|
2011-03-04 12:15:19 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2011-03-10 16:11:20 +01:00
|
|
|
|
2011-03-16 13:49:28 +01:00
|
|
|
void MemcheckTool::finished()
|
|
|
|
|
{
|
2011-06-27 15:43:15 +02:00
|
|
|
const int n = m_errorModel->rowCount();
|
2011-07-11 14:56:57 +02:00
|
|
|
m_goBack->setEnabled(n > 1);
|
|
|
|
|
m_goNext->setEnabled(n > 1);
|
2011-06-27 15:43:15 +02:00
|
|
|
const QString msg = AnalyzerManager::msgToolFinished(displayName(), n);
|
2011-06-30 13:44:22 +02:00
|
|
|
AnalyzerManager::showStatusMessage(msg);
|
2011-12-13 14:01:52 +01:00
|
|
|
setBusyCursor(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckTool::setBusyCursor(bool busy)
|
|
|
|
|
{
|
|
|
|
|
QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
|
|
|
|
|
m_errorView->setCursor(cursor);
|
2011-06-29 18:48:08 +02:00
|
|
|
}
|
|
|
|
|
|
2011-03-10 16:11:20 +01:00
|
|
|
} // namespace Internal
|
2011-05-23 13:50:28 +02:00
|
|
|
} // namespace Valgrind
|