2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2011-03-04 12:15:19 +01:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
2011-03-04 12:15:19 +01:00
|
|
|
** Author: Nicolas Arnaud-Cormos, KDAB (nicolas.arnaud-cormos@kdab.com)
|
2016-01-15 14:57:40 +01:00
|
|
|
** Contact: https://www.qt.io/licensing/
|
2011-03-04 12:15:19 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2011-03-04 12:15:19 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2016-01-15 14:57:40 +01:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2011-03-04 12:15:19 +01:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
2011-03-04 12:15:19 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2011-03-04 12:15:19 +01:00
|
|
|
|
|
|
|
|
#include "memchecktool.h"
|
2017-09-21 12:33:36 +02:00
|
|
|
|
2011-03-04 12:15:19 +01:00
|
|
|
#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"
|
2017-09-21 12:33:36 +02:00
|
|
|
#include "valgrindengine.h"
|
|
|
|
|
#include "valgrindsettings.h"
|
|
|
|
|
#include "valgrindrunner.h"
|
|
|
|
|
|
|
|
|
|
#include "xmlprotocol/error.h"
|
|
|
|
|
#include "xmlprotocol/error.h"
|
|
|
|
|
#include "xmlprotocol/errorlistmodel.h"
|
|
|
|
|
#include "xmlprotocol/frame.h"
|
|
|
|
|
#include "xmlprotocol/stack.h"
|
|
|
|
|
#include "xmlprotocol/stackmodel.h"
|
|
|
|
|
#include "xmlprotocol/status.h"
|
|
|
|
|
#include "xmlprotocol/suppression.h"
|
|
|
|
|
#include "xmlprotocol/threadedparser.h"
|
|
|
|
|
|
|
|
|
|
#include <debugger/debuggerkitinformation.h>
|
|
|
|
|
#include <debugger/debuggerruncontrol.h>
|
2016-03-02 12:05:30 +01:00
|
|
|
#include <debugger/analyzer/analyzerconstants.h>
|
2016-02-24 14:42:52 +01:00
|
|
|
#include <debugger/analyzer/analyzermanager.h>
|
2016-03-02 12:05:30 +01:00
|
|
|
#include <debugger/analyzer/startremotedialog.h>
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2017-09-21 12:33:36 +02:00
|
|
|
#include <projectexplorer/buildconfiguration.h>
|
2014-04-24 16:35:07 +02:00
|
|
|
#include <projectexplorer/deploymentdata.h>
|
2017-09-21 12:33:36 +02:00
|
|
|
#include <projectexplorer/kitinformation.h>
|
2011-03-04 12:15:19 +01:00
|
|
|
#include <projectexplorer/project.h>
|
2017-09-21 12:33:36 +02:00
|
|
|
#include <projectexplorer/projectexplorer.h>
|
2011-03-04 12:15:19 +01:00
|
|
|
#include <projectexplorer/runconfiguration.h>
|
2017-09-21 12:33:36 +02:00
|
|
|
#include <projectexplorer/session.h>
|
2011-03-04 12:15:19 +01:00
|
|
|
#include <projectexplorer/target.h>
|
2016-05-31 16:09:48 +02:00
|
|
|
#include <projectexplorer/taskhub.h>
|
2017-09-21 12:33:36 +02:00
|
|
|
#include <projectexplorer/toolchain.h>
|
|
|
|
|
|
|
|
|
|
#include <extensionsystem/iplugin.h>
|
|
|
|
|
#include <extensionsystem/pluginmanager.h>
|
2011-03-04 12:15:19 +01:00
|
|
|
|
|
|
|
|
#include <coreplugin/actionmanager/actioncontainer.h>
|
2013-05-30 17:26:51 +02:00
|
|
|
#include <coreplugin/actionmanager/actionmanager.h>
|
2011-03-04 12:15:19 +01:00
|
|
|
#include <coreplugin/actionmanager/command.h>
|
2013-05-30 17:26:51 +02:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
|
|
|
|
#include <coreplugin/icore.h>
|
2011-09-05 16:10:37 +02:00
|
|
|
#include <coreplugin/id.h>
|
2017-12-08 20:27:21 +01:00
|
|
|
#include <coreplugin/modemanager.h>
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2017-09-26 16:35:34 +02:00
|
|
|
#include <ssh/sshconnection.h>
|
|
|
|
|
|
2011-03-04 12:15:19 +01:00
|
|
|
#include <utils/fancymainwindow.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
2016-05-23 14:15:06 +02:00
|
|
|
#include <utils/utilsicons.h>
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2016-03-02 12:05:30 +01:00
|
|
|
#include <QAction>
|
|
|
|
|
#include <QFile>
|
|
|
|
|
#include <QFileDialog>
|
|
|
|
|
#include <QFileInfo>
|
2017-09-26 16:35:34 +02:00
|
|
|
#include <QHostAddress>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QLabel>
|
|
|
|
|
#include <QMenu>
|
|
|
|
|
#include <QToolButton>
|
2017-09-21 12:33:36 +02:00
|
|
|
#include <QSortFilterProxyModel>
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
#include <QCheckBox>
|
|
|
|
|
#include <QComboBox>
|
|
|
|
|
#include <QLineEdit>
|
|
|
|
|
#include <QPushButton>
|
|
|
|
|
#include <QSpinBox>
|
|
|
|
|
#include <QStandardPaths>
|
|
|
|
|
#include <QWinEventNotifier>
|
|
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-04-12 10:06:41 +02:00
|
|
|
using namespace Core;
|
2016-03-02 13:57:37 +01:00
|
|
|
using namespace Debugger;
|
2013-01-10 11:36:15 +01:00
|
|
|
using namespace ProjectExplorer;
|
2016-03-02 13:57:37 +01:00
|
|
|
using namespace Utils;
|
2011-03-04 12:15:19 +01:00
|
|
|
using namespace Valgrind::XmlProtocol;
|
2017-09-21 12:33:36 +02:00
|
|
|
|
2016-03-08 15:03:31 +01:00
|
|
|
using namespace std::placeholders;
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2011-05-23 13:50:28 +02:00
|
|
|
namespace Valgrind {
|
2011-03-10 16:11:20 +01:00
|
|
|
namespace Internal {
|
|
|
|
|
|
2016-03-07 17:33:58 +01:00
|
|
|
const char MEMCHECK_RUN_MODE[] = "MemcheckTool.MemcheckRunMode";
|
|
|
|
|
const char MEMCHECK_WITH_GDB_RUN_MODE[] = "MemcheckTool.MemcheckWithGdbRunMode";
|
|
|
|
|
|
|
|
|
|
const char MemcheckPerspectiveId[] = "Memcheck.Perspective";
|
|
|
|
|
const char MemcheckErrorDockId[] = "Memcheck.Dock.Error";
|
|
|
|
|
|
2017-09-21 12:33:36 +02:00
|
|
|
|
|
|
|
|
class MemcheckToolRunner : public ValgrindToolRunner
|
|
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
explicit MemcheckToolRunner(ProjectExplorer::RunControl *runControl,
|
|
|
|
|
bool withGdb = false);
|
|
|
|
|
|
|
|
|
|
void start() override;
|
|
|
|
|
void stop() override;
|
|
|
|
|
|
|
|
|
|
QStringList suppressionFiles() const;
|
|
|
|
|
|
|
|
|
|
signals:
|
|
|
|
|
void internalParserError(const QString &errorString);
|
|
|
|
|
void parserError(const Valgrind::XmlProtocol::Error &error);
|
|
|
|
|
void suppressionCount(const QString &name, qint64 count);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QString progressTitle() const override;
|
|
|
|
|
QStringList toolArguments() const override;
|
|
|
|
|
|
|
|
|
|
void startDebugger(qint64 valgrindPid);
|
|
|
|
|
void appendLog(const QByteArray &data);
|
|
|
|
|
|
|
|
|
|
const bool m_withGdb;
|
|
|
|
|
QHostAddress m_localServerAddress;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class LocalAddressFinder : public RunWorker
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
LocalAddressFinder(RunControl *runControl, QHostAddress *localServerAddress)
|
|
|
|
|
: RunWorker(runControl), connection(device()->sshParameters())
|
|
|
|
|
{
|
|
|
|
|
connect(&connection, &QSsh::SshConnection::connected, this, [this, localServerAddress] {
|
|
|
|
|
*localServerAddress = connection.connectionInfo().localAddress;
|
|
|
|
|
reportStarted();
|
|
|
|
|
});
|
|
|
|
|
connect(&connection, &QSsh::SshConnection::error, this, [this] {
|
|
|
|
|
reportFailure();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void start() override
|
|
|
|
|
{
|
|
|
|
|
connection.connectToHost();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QSsh::SshConnection connection;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
MemcheckToolRunner::MemcheckToolRunner(RunControl *runControl, bool withGdb)
|
|
|
|
|
: ValgrindToolRunner(runControl),
|
|
|
|
|
m_withGdb(withGdb),
|
|
|
|
|
m_localServerAddress(QHostAddress::LocalHost)
|
|
|
|
|
{
|
|
|
|
|
setDisplayName("MemcheckToolRunner");
|
|
|
|
|
connect(m_runner.parser(), &XmlProtocol::ThreadedParser::error,
|
|
|
|
|
this, &MemcheckToolRunner::parserError);
|
|
|
|
|
connect(m_runner.parser(), &XmlProtocol::ThreadedParser::suppressionCount,
|
|
|
|
|
this, &MemcheckToolRunner::suppressionCount);
|
|
|
|
|
|
|
|
|
|
if (withGdb) {
|
|
|
|
|
connect(&m_runner, &ValgrindRunner::valgrindStarted,
|
|
|
|
|
this, &MemcheckToolRunner::startDebugger);
|
|
|
|
|
connect(&m_runner, &ValgrindRunner::logMessageReceived,
|
|
|
|
|
this, &MemcheckToolRunner::appendLog);
|
|
|
|
|
// m_runner.disableXml();
|
|
|
|
|
} else {
|
|
|
|
|
connect(m_runner.parser(), &XmlProtocol::ThreadedParser::internalError,
|
|
|
|
|
this, &MemcheckToolRunner::internalParserError);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We need a real address to connect to from the outside.
|
|
|
|
|
if (device()->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)
|
|
|
|
|
addStartDependency(new LocalAddressFinder(runControl, &m_localServerAddress));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString MemcheckToolRunner::progressTitle() const
|
|
|
|
|
{
|
|
|
|
|
return tr("Analyzing Memory");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckToolRunner::start()
|
|
|
|
|
{
|
|
|
|
|
m_runner.setLocalServerAddress(m_localServerAddress);
|
|
|
|
|
ValgrindToolRunner::start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckToolRunner::stop()
|
|
|
|
|
{
|
|
|
|
|
disconnect(m_runner.parser(), &ThreadedParser::internalError,
|
|
|
|
|
this, &MemcheckToolRunner::internalParserError);
|
|
|
|
|
ValgrindToolRunner::stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList MemcheckToolRunner::toolArguments() const
|
|
|
|
|
{
|
|
|
|
|
QStringList arguments = {"--tool=memcheck", "--gen-suppressions=all"};
|
|
|
|
|
|
|
|
|
|
QTC_ASSERT(m_settings, return arguments);
|
|
|
|
|
|
|
|
|
|
if (m_settings->trackOrigins())
|
|
|
|
|
arguments << "--track-origins=yes";
|
|
|
|
|
|
|
|
|
|
if (m_settings->showReachable())
|
|
|
|
|
arguments << "--show-reachable=yes";
|
|
|
|
|
|
|
|
|
|
QString leakCheckValue;
|
|
|
|
|
switch (m_settings->leakCheckOnFinish()) {
|
|
|
|
|
case ValgrindBaseSettings::LeakCheckOnFinishNo:
|
|
|
|
|
leakCheckValue = "no";
|
|
|
|
|
break;
|
|
|
|
|
case ValgrindBaseSettings::LeakCheckOnFinishYes:
|
|
|
|
|
leakCheckValue = "full";
|
|
|
|
|
break;
|
|
|
|
|
case ValgrindBaseSettings::LeakCheckOnFinishSummaryOnly:
|
|
|
|
|
default:
|
|
|
|
|
leakCheckValue = "summary";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
arguments << "--leak-check=" + leakCheckValue;
|
|
|
|
|
|
|
|
|
|
foreach (const QString &file, m_settings->suppressionFiles())
|
|
|
|
|
arguments << QString("--suppressions=%1").arg(file);
|
|
|
|
|
|
|
|
|
|
arguments << QString("--num-callers=%1").arg(m_settings->numCallers());
|
|
|
|
|
|
|
|
|
|
if (m_withGdb)
|
|
|
|
|
arguments << "--vgdb=yes" << "--vgdb-error=0";
|
|
|
|
|
|
|
|
|
|
return arguments;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList MemcheckToolRunner::suppressionFiles() const
|
|
|
|
|
{
|
|
|
|
|
return m_settings->suppressionFiles();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckToolRunner::startDebugger(qint64 valgrindPid)
|
|
|
|
|
{
|
|
|
|
|
auto debugger = new Debugger::DebuggerRunTool(runControl());
|
|
|
|
|
debugger->setStartMode(Debugger::AttachToRemoteServer);
|
|
|
|
|
debugger->setRunControlName(QString("VGdb %1").arg(valgrindPid));
|
|
|
|
|
debugger->setRemoteChannel(QString("| vgdb --pid=%1").arg(valgrindPid));
|
|
|
|
|
debugger->setUseContinueInsteadOfRun(true);
|
|
|
|
|
debugger->addExpectedSignal("SIGTRAP");
|
|
|
|
|
|
|
|
|
|
connect(runControl(), &RunControl::stopped, debugger, &RunControl::deleteLater);
|
|
|
|
|
|
|
|
|
|
debugger->initiateStart();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckToolRunner::appendLog(const QByteArray &data)
|
|
|
|
|
{
|
|
|
|
|
appendMessage(QString::fromUtf8(data), Utils::StdOutFormat);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-03-08 15:03:31 +01:00
|
|
|
static ErrorListModel::RelevantFrameFinder makeFrameFinder(const QStringList &projectFiles)
|
2016-03-02 13:57:37 +01:00
|
|
|
{
|
2016-03-08 15:03:31 +01:00
|
|
|
return [projectFiles](const Error &error) {
|
2016-04-08 12:29:22 +02:00
|
|
|
const Frame defaultFrame = Frame();
|
2016-03-02 13:57:37 +01:00
|
|
|
const QVector<Stack> stacks = error.stacks();
|
|
|
|
|
if (stacks.isEmpty())
|
2016-04-08 12:29:22 +02:00
|
|
|
return defaultFrame;
|
2016-03-02 13:57:37 +01:00
|
|
|
const Stack &stack = stacks[0];
|
|
|
|
|
const QVector<Frame> frames = stack.frames();
|
|
|
|
|
if (frames.isEmpty())
|
2016-04-08 12:29:22 +02:00
|
|
|
return defaultFrame;
|
2016-03-02 13:57:37 +01:00
|
|
|
|
|
|
|
|
//find the first frame belonging to the project
|
2016-03-08 15:03:31 +01:00
|
|
|
if (!projectFiles.isEmpty()) {
|
2016-03-02 13:57:37 +01:00
|
|
|
foreach (const Frame &frame, frames) {
|
|
|
|
|
if (frame.directory().isEmpty() || frame.fileName().isEmpty())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
//filepaths can contain "..", clean them:
|
|
|
|
|
const QString f = QFileInfo(frame.filePath()).absoluteFilePath();
|
2016-03-08 15:03:31 +01:00
|
|
|
if (projectFiles.contains(f))
|
2016-03-02 13:57:37 +01:00
|
|
|
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(QLatin1String("operator new(")))
|
|
|
|
|
{
|
|
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//else fallback to the first frame
|
|
|
|
|
return frames.first();
|
2016-03-08 15:03:31 +01:00
|
|
|
};
|
|
|
|
|
}
|
2016-03-02 13:57:37 +01:00
|
|
|
|
2016-03-02 12:05:30 +01:00
|
|
|
|
|
|
|
|
class MemcheckErrorFilterProxyModel : public QSortFilterProxyModel
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
void setAcceptedKinds(const QList<int> &acceptedKinds);
|
|
|
|
|
void setFilterExternalIssues(bool filter);
|
|
|
|
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QList<int> m_acceptedKinds;
|
2016-03-08 15:03:31 +01:00
|
|
|
bool m_filterExternalIssues = false;
|
2016-03-02 12:05:30 +01:00
|
|
|
};
|
|
|
|
|
|
2011-03-04 12:15:19 +01:00
|
|
|
void MemcheckErrorFilterProxyModel::setAcceptedKinds(const QList<int> &acceptedKinds)
|
|
|
|
|
{
|
|
|
|
|
if (m_acceptedKinds != acceptedKinds) {
|
|
|
|
|
m_acceptedKinds = acceptedKinds;
|
2016-05-10 12:57:20 +02:00
|
|
|
invalidateFilter();
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckErrorFilterProxyModel::setFilterExternalIssues(bool filter)
|
|
|
|
|
{
|
|
|
|
|
if (m_filterExternalIssues != filter) {
|
|
|
|
|
m_filterExternalIssues = filter;
|
2016-05-10 12:57:20 +02:00
|
|
|
invalidateFilter();
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MemcheckErrorFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
|
|
|
|
{
|
2013-01-10 11:36:15 +01:00
|
|
|
// We only deal with toplevel items.
|
2011-03-04 12:15:19 +01:00
|
|
|
if (sourceParent.isValid())
|
|
|
|
|
return true;
|
|
|
|
|
|
2013-01-10 11:36:15 +01:00
|
|
|
// Because toplevel items have no parent, we can't use sourceParent to find them. we just use
|
2011-03-04 12:15:19 +01:00
|
|
|
// 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>();
|
|
|
|
|
|
2013-01-10 11:36:15 +01:00
|
|
|
// Filter on kind
|
2011-03-04 12:15:19 +01:00
|
|
|
if (!m_acceptedKinds.contains(error.kind()))
|
|
|
|
|
return false;
|
|
|
|
|
|
2013-01-10 11:36:15 +01:00
|
|
|
// Filter non-project stuff
|
2011-03-04 12:15:19 +01:00
|
|
|
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
|
|
|
|
|
QSet<QString> validFolders;
|
2017-03-01 17:53:15 +01:00
|
|
|
for (Project *project : SessionManager::projects()) {
|
2014-05-02 12:53:36 +02:00
|
|
|
validFolders << project->projectDirectory().toString();
|
2013-01-10 11:36:15 +01:00
|
|
|
foreach (Target *target, project->targets()) {
|
2015-02-03 23:56:02 +02:00
|
|
|
foreach (const DeployableFile &file,
|
2014-04-24 16:35:07 +02:00
|
|
|
target->deploymentData().allFiles()) {
|
|
|
|
|
if (file.isExecutable())
|
|
|
|
|
validFolders << file.remoteDirectory();
|
|
|
|
|
}
|
2013-01-10 11:36:15 +01:00
|
|
|
foreach (BuildConfiguration *config, target->buildConfigurations())
|
2013-08-16 17:45:16 +02:00
|
|
|
validFolders << config->buildDirectory().toString();
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QVector< Frame > frames = error.stacks().first().frames();
|
|
|
|
|
|
|
|
|
|
const int framesToLookAt = qMin(6, frames.size());
|
|
|
|
|
|
|
|
|
|
bool inProject = false;
|
2013-01-10 11:36:15 +01:00
|
|
|
for (int i = 0; i < framesToLookAt; ++i) {
|
2011-03-04 12:15:19 +01:00
|
|
|
const Frame &frame = frames.at(i);
|
2011-05-11 16:26:34 +02:00
|
|
|
foreach (const QString &folder, validFolders) {
|
2014-07-09 10:11:56 +03:00
|
|
|
if (frame.directory().startsWith(folder)) {
|
2011-03-04 12:15:19 +01:00
|
|
|
inProject = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!inProject)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-19 00:59:04 +01:00
|
|
|
static void initKindFilterAction(QAction *action, const QVariantList &kinds)
|
2011-03-10 16:11:20 +01:00
|
|
|
{
|
|
|
|
|
action->setCheckable(true);
|
2015-03-19 00:59:04 +01:00
|
|
|
action->setData(kinds);
|
2011-03-10 16:11:20 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-02 12:05:30 +01:00
|
|
|
class MemcheckTool : public QObject
|
|
|
|
|
{
|
|
|
|
|
Q_DECLARE_TR_FUNCTIONS(Valgrind::Internal::MemcheckTool)
|
|
|
|
|
|
|
|
|
|
public:
|
2017-07-03 13:55:25 +02:00
|
|
|
MemcheckTool();
|
2016-03-02 12:05:30 +01:00
|
|
|
|
2017-05-09 10:25:11 +02:00
|
|
|
RunWorker *createRunWorker(RunControl *runControl);
|
2016-03-02 12:05:30 +01:00
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
void loadShowXmlLogFile(const QString &filePath, const QString &exitMsg);
|
|
|
|
|
|
2016-03-02 12:05:30 +01:00
|
|
|
private:
|
2016-03-02 13:57:37 +01:00
|
|
|
void updateRunActions();
|
2016-03-02 12:05:30 +01:00
|
|
|
void settingsDestroyed(QObject *settings);
|
|
|
|
|
void maybeActiveRunConfigurationChanged();
|
|
|
|
|
|
|
|
|
|
void engineFinished();
|
|
|
|
|
void loadingExternalXmlLogFileFinished();
|
|
|
|
|
|
|
|
|
|
void parserError(const Valgrind::XmlProtocol::Error &error);
|
|
|
|
|
void internalParserError(const QString &errorString);
|
|
|
|
|
void updateErrorFilter();
|
|
|
|
|
|
|
|
|
|
void loadExternalXmlLogFile();
|
2017-12-08 20:27:21 +01:00
|
|
|
void loadXmlLogFile(const QString &filePath);
|
2016-03-02 12:05:30 +01:00
|
|
|
|
|
|
|
|
void setBusyCursor(bool busy);
|
|
|
|
|
|
|
|
|
|
void clearErrorView();
|
|
|
|
|
void updateFromSettings();
|
|
|
|
|
int updateUiAfterFinishedHelper();
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
void heobAction();
|
|
|
|
|
|
2016-03-02 12:05:30 +01:00
|
|
|
private:
|
|
|
|
|
ValgrindBaseSettings *m_settings;
|
2016-03-08 15:03:31 +01:00
|
|
|
QMenu *m_filterMenu = 0;
|
2016-03-02 12:05:30 +01:00
|
|
|
|
2016-03-08 15:03:31 +01:00
|
|
|
Valgrind::XmlProtocol::ErrorListModel m_errorModel;
|
|
|
|
|
MemcheckErrorFilterProxyModel m_errorProxyModel;
|
|
|
|
|
MemcheckErrorView *m_errorView = 0;
|
2016-03-02 12:05:30 +01:00
|
|
|
|
|
|
|
|
QList<QAction *> m_errorFilterActions;
|
|
|
|
|
QAction *m_filterProjectAction;
|
|
|
|
|
QList<QAction *> m_suppressionActions;
|
2016-03-02 13:57:37 +01:00
|
|
|
QAction *m_startAction;
|
|
|
|
|
QAction *m_startWithGdbAction;
|
|
|
|
|
QAction *m_stopAction;
|
2016-03-02 12:05:30 +01:00
|
|
|
QAction *m_suppressionSeparator;
|
|
|
|
|
QAction *m_loadExternalLogFile;
|
|
|
|
|
QAction *m_goBack;
|
|
|
|
|
QAction *m_goNext;
|
2016-03-02 13:57:37 +01:00
|
|
|
bool m_toolBusy = false;
|
2017-12-08 20:27:21 +01:00
|
|
|
|
|
|
|
|
QString m_exitMsg;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
class HeobDialog : public QDialog
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
HeobDialog(QWidget *parent);
|
|
|
|
|
|
|
|
|
|
QString arguments() const;
|
|
|
|
|
QString xmlName() const;
|
2017-12-14 20:12:41 +01:00
|
|
|
bool attach() const;
|
2017-12-08 20:27:21 +01:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void updateEnabled();
|
2017-12-20 17:26:57 +01:00
|
|
|
void saveOptions();
|
2017-12-08 20:27:21 +01:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QLineEdit *m_xmlEdit = nullptr;
|
|
|
|
|
QComboBox *m_handleExceptionCombo = nullptr;
|
|
|
|
|
QComboBox *m_pageProtectionCombo = nullptr;
|
|
|
|
|
QCheckBox *m_freedProtectionCheck = nullptr;
|
|
|
|
|
QCheckBox *m_breakpointCheck = nullptr;
|
|
|
|
|
QComboBox *m_leakDetailCombo = nullptr;
|
|
|
|
|
QSpinBox *m_leakSizeSpin = nullptr;
|
|
|
|
|
QComboBox *m_leakRecordingCombo = nullptr;
|
2017-12-14 20:12:41 +01:00
|
|
|
QCheckBox *m_attachCheck = nullptr;
|
2017-12-08 20:27:21 +01:00
|
|
|
QLineEdit *m_extraArgsEdit = nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class HeobData : public QObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
2017-12-14 20:12:41 +01:00
|
|
|
HeobData(MemcheckTool *mcTool, const QString &xmlPath, Kit *kit, bool attach);
|
2017-12-08 20:27:21 +01:00
|
|
|
~HeobData();
|
|
|
|
|
|
|
|
|
|
bool createErrorPipe(DWORD heobPid);
|
|
|
|
|
void readExitData();
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void processFinished();
|
|
|
|
|
|
2017-12-14 20:12:41 +01:00
|
|
|
void sendHeobAttachPid(DWORD pid);
|
|
|
|
|
void debugStarted();
|
|
|
|
|
void debugStopped();
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
private:
|
|
|
|
|
HANDLE m_errorPipe = INVALID_HANDLE_VALUE;
|
|
|
|
|
OVERLAPPED m_ov;
|
|
|
|
|
unsigned m_data[2];
|
|
|
|
|
QWinEventNotifier *m_processFinishedNotifier = nullptr;
|
|
|
|
|
MemcheckTool *m_mcTool = nullptr;
|
|
|
|
|
QString m_xmlPath;
|
2017-12-14 20:12:41 +01:00
|
|
|
Kit *m_kit = nullptr;
|
|
|
|
|
bool m_attach = false;
|
|
|
|
|
RunControl *m_runControl = nullptr;
|
2016-03-02 12:05:30 +01:00
|
|
|
};
|
2017-12-08 20:27:21 +01:00
|
|
|
#endif
|
2016-03-02 12:05:30 +01:00
|
|
|
|
2017-07-03 13:55:25 +02:00
|
|
|
MemcheckTool::MemcheckTool()
|
2011-03-04 12:15:19 +01:00
|
|
|
{
|
2016-02-29 12:48:58 +01:00
|
|
|
m_settings = ValgrindPlugin::globalSettings();
|
2014-07-23 01:00:51 +02:00
|
|
|
|
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);
|
2017-02-22 15:09:35 +01:00
|
|
|
initKindFilterAction(a, {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);
|
2017-02-22 15:09:35 +01:00
|
|
|
initKindFilterAction(a, {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);
|
2017-02-22 15:09:35 +01:00
|
|
|
initKindFilterAction(a, {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);
|
2015-03-19 00:59:04 +01:00
|
|
|
initKindFilterAction(a, { InvalidFree, MismatchedFree });
|
2011-06-27 15:43:15 +02:00
|
|
|
m_errorFilterActions.append(a);
|
2016-03-02 12:05:30 +01:00
|
|
|
|
2016-03-02 13:57:37 +01:00
|
|
|
m_errorView = new MemcheckErrorView;
|
|
|
|
|
m_errorView->setObjectName(QLatin1String("MemcheckErrorView"));
|
|
|
|
|
m_errorView->setFrameStyle(QFrame::NoFrame);
|
|
|
|
|
m_errorView->setAttribute(Qt::WA_MacShowFocusRect, false);
|
2016-03-08 15:03:31 +01:00
|
|
|
m_errorModel.setRelevantFrameFinder(makeFrameFinder(QStringList()));
|
|
|
|
|
m_errorProxyModel.setSourceModel(&m_errorModel);
|
|
|
|
|
m_errorProxyModel.setDynamicSortFilter(true);
|
|
|
|
|
m_errorView->setModel(&m_errorProxyModel);
|
2016-03-02 13:57:37 +01:00
|
|
|
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(QLatin1String("Valgrind.MemcheckTool.ErrorView"));
|
|
|
|
|
m_errorView->setWindowTitle(tr("Memory Issues"));
|
|
|
|
|
|
2016-05-12 11:57:29 +02:00
|
|
|
Debugger::registerPerspective(MemcheckPerspectiveId, new Perspective (tr("Memcheck"), {
|
2017-02-22 15:09:35 +01:00
|
|
|
{MemcheckErrorDockId, m_errorView, {}, Perspective::SplitVertical}
|
2016-05-12 11:57:29 +02:00
|
|
|
}));
|
2016-03-02 13:57:37 +01:00
|
|
|
|
|
|
|
|
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::updateRunActions,
|
|
|
|
|
this, &MemcheckTool::maybeActiveRunConfigurationChanged);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// The Control Widget.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
m_startAction = Debugger::createStartAction();
|
|
|
|
|
m_startWithGdbAction = Debugger::createStartAction();
|
|
|
|
|
m_stopAction = Debugger::createStopAction();
|
|
|
|
|
|
|
|
|
|
// Load external XML log file
|
|
|
|
|
auto action = new QAction(this);
|
2017-09-15 11:25:39 +02:00
|
|
|
action->setIcon(Icons::OPENFILE.icon());
|
2016-03-02 13:57:37 +01:00
|
|
|
action->setToolTip(tr("Load External XML Log File"));
|
|
|
|
|
connect(action, &QAction::triggered, this, &MemcheckTool::loadExternalXmlLogFile);
|
|
|
|
|
m_loadExternalLogFile = action;
|
|
|
|
|
|
|
|
|
|
// Go to previous leak.
|
|
|
|
|
action = new QAction(this);
|
|
|
|
|
action->setDisabled(true);
|
2017-09-15 11:25:39 +02:00
|
|
|
action->setIcon(Icons::PREV_TOOLBAR.icon());
|
2016-03-02 13:57:37 +01:00
|
|
|
action->setToolTip(tr("Go to previous leak."));
|
|
|
|
|
connect(action, &QAction::triggered, m_errorView, &MemcheckErrorView::goBack);
|
|
|
|
|
m_goBack = action;
|
|
|
|
|
|
|
|
|
|
// Go to next leak.
|
|
|
|
|
action = new QAction(this);
|
|
|
|
|
action->setDisabled(true);
|
2017-09-15 11:25:39 +02:00
|
|
|
action->setIcon(Icons::NEXT_TOOLBAR.icon());
|
2016-03-02 13:57:37 +01:00
|
|
|
action->setToolTip(tr("Go to next leak."));
|
|
|
|
|
connect(action, &QAction::triggered, m_errorView, &MemcheckErrorView::goNext);
|
|
|
|
|
m_goNext = action;
|
|
|
|
|
|
|
|
|
|
auto filterButton = new QToolButton;
|
2017-09-15 11:25:39 +02:00
|
|
|
filterButton->setIcon(Icons::FILTER.icon());
|
2016-03-02 13:57:37 +01:00
|
|
|
filterButton->setText(tr("Error Filter"));
|
|
|
|
|
filterButton->setPopupMode(QToolButton::InstantPopup);
|
|
|
|
|
filterButton->setProperty("noArrow", true);
|
|
|
|
|
|
|
|
|
|
m_filterMenu = new QMenu(filterButton);
|
|
|
|
|
foreach (QAction *filterAction, m_errorFilterActions)
|
|
|
|
|
m_filterMenu->addAction(filterAction);
|
|
|
|
|
m_filterMenu->addSeparator();
|
|
|
|
|
m_filterMenu->addAction(m_filterProjectAction);
|
|
|
|
|
m_filterMenu->addAction(m_suppressionSeparator);
|
|
|
|
|
connect(m_filterMenu, &QMenu::triggered, this, &MemcheckTool::updateErrorFilter);
|
|
|
|
|
filterButton->setMenu(m_filterMenu);
|
2016-03-02 12:05:30 +01:00
|
|
|
|
2017-04-12 10:06:41 +02:00
|
|
|
ActionContainer *menu = ActionManager::actionContainer(Debugger::Constants::M_DEBUG_ANALYZER);
|
|
|
|
|
QString toolTip = tr("Valgrind Analyze Memory uses the Memcheck tool to find memory leaks.");
|
2016-03-02 12:05:30 +01:00
|
|
|
|
2017-09-15 11:25:39 +02:00
|
|
|
if (!HostOsInfo::isWindowsHost()) {
|
2017-04-12 10:06:41 +02:00
|
|
|
action = new QAction(this);
|
|
|
|
|
action->setText(tr("Valgrind Memory Analyzer"));
|
|
|
|
|
action->setToolTip(toolTip);
|
|
|
|
|
menu->addAction(ActionManager::registerAction(action, "Memcheck.Local"),
|
|
|
|
|
Debugger::Constants::G_ANALYZER_TOOLS);
|
|
|
|
|
QObject::connect(action, &QAction::triggered, this, [action] {
|
|
|
|
|
if (!Debugger::wantRunTool(DebugMode, action->text()))
|
|
|
|
|
return;
|
|
|
|
|
TaskHub::clearTasks(Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
Debugger::selectPerspective(MemcheckPerspectiveId);
|
|
|
|
|
ProjectExplorerPlugin::runStartupProject(MEMCHECK_RUN_MODE);
|
|
|
|
|
});
|
|
|
|
|
QObject::connect(m_startAction, &QAction::triggered, action, &QAction::triggered);
|
|
|
|
|
QObject::connect(m_startAction, &QAction::changed, action, [action, this] {
|
|
|
|
|
action->setEnabled(m_startAction->isEnabled());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
action = new QAction(this);
|
|
|
|
|
action->setText(tr("Valgrind Memory Analyzer with GDB"));
|
|
|
|
|
action->setToolTip(tr("Valgrind Analyze Memory with GDB uses the "
|
2016-03-02 12:05:30 +01:00
|
|
|
"Memcheck tool to find memory leaks.\nWhen a problem is detected, "
|
|
|
|
|
"the application is interrupted and can be debugged."));
|
2017-04-12 10:06:41 +02:00
|
|
|
menu->addAction(ActionManager::registerAction(action, "MemcheckWithGdb.Local"),
|
|
|
|
|
Debugger::Constants::G_ANALYZER_TOOLS);
|
|
|
|
|
QObject::connect(action, &QAction::triggered, this, [action] {
|
|
|
|
|
if (!Debugger::wantRunTool(DebugMode, action->text()))
|
|
|
|
|
return;
|
|
|
|
|
TaskHub::clearTasks(Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
Debugger::selectPerspective(MemcheckPerspectiveId);
|
|
|
|
|
ProjectExplorerPlugin::runStartupProject(MEMCHECK_WITH_GDB_RUN_MODE);
|
|
|
|
|
});
|
|
|
|
|
QObject::connect(m_startWithGdbAction, &QAction::triggered, action, &QAction::triggered);
|
|
|
|
|
QObject::connect(m_startWithGdbAction, &QAction::changed, action, [action, this] {
|
|
|
|
|
action->setEnabled(m_startWithGdbAction->isEnabled());
|
|
|
|
|
});
|
2017-12-08 20:27:21 +01:00
|
|
|
} else {
|
|
|
|
|
action = new QAction(tr("heob"), this);
|
|
|
|
|
Core::Command *cmd = Core::ActionManager::registerAction(action, "Memcheck.Local");
|
|
|
|
|
cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+H")));
|
|
|
|
|
connect(action, &QAction::triggered, this, &MemcheckTool::heobAction);
|
|
|
|
|
menu->addAction(cmd, Debugger::Constants::G_ANALYZER_TOOLS);
|
2018-01-06 17:31:05 +01:00
|
|
|
connect(m_startAction, &QAction::changed, action, [action, this] {
|
|
|
|
|
action->setEnabled(m_startAction->isEnabled());
|
|
|
|
|
});
|
2016-03-02 12:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
2017-04-12 10:06:41 +02:00
|
|
|
action = new QAction(this);
|
|
|
|
|
action->setText(tr("Valgrind Memory Analyzer (External Application)"));
|
|
|
|
|
action->setToolTip(toolTip);
|
|
|
|
|
menu->addAction(ActionManager::registerAction(action, "Memcheck.Remote"),
|
|
|
|
|
Debugger::Constants::G_ANALYZER_REMOTE_TOOLS);
|
2017-09-07 17:05:47 +02:00
|
|
|
QObject::connect(action, &QAction::triggered, this, [action] {
|
2017-06-30 08:41:08 +02:00
|
|
|
auto runConfig = RunConfiguration::startupRunConfiguration();
|
2017-04-12 10:06:41 +02:00
|
|
|
if (!runConfig) {
|
|
|
|
|
showCannotStartDialog(action->text());
|
|
|
|
|
return;
|
|
|
|
|
}
|
2016-03-02 12:05:30 +01:00
|
|
|
StartRemoteDialog dlg;
|
|
|
|
|
if (dlg.exec() != QDialog::Accepted)
|
|
|
|
|
return;
|
2017-04-12 10:06:41 +02:00
|
|
|
TaskHub::clearTasks(Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
Debugger::selectPerspective(MemcheckPerspectiveId);
|
2017-05-09 10:25:11 +02:00
|
|
|
RunControl *rc = new RunControl(runConfig, MEMCHECK_RUN_MODE);
|
|
|
|
|
rc->createWorker(MEMCHECK_RUN_MODE);
|
2016-03-02 12:05:30 +01:00
|
|
|
const auto runnable = dlg.runnable();
|
|
|
|
|
rc->setRunnable(runnable);
|
|
|
|
|
rc->setDisplayName(runnable.executable);
|
2017-04-11 14:36:03 +02:00
|
|
|
ProjectExplorerPlugin::startRunControl(rc);
|
2016-03-02 12:05:30 +01:00
|
|
|
});
|
2016-03-02 13:57:37 +01:00
|
|
|
|
|
|
|
|
ToolbarDescription toolbar;
|
|
|
|
|
toolbar.addAction(m_startAction);
|
|
|
|
|
//toolbar.addAction(m_startWithGdbAction);
|
|
|
|
|
toolbar.addAction(m_stopAction);
|
|
|
|
|
toolbar.addAction(m_loadExternalLogFile);
|
|
|
|
|
toolbar.addAction(m_goBack);
|
|
|
|
|
toolbar.addAction(m_goNext);
|
|
|
|
|
toolbar.addWidget(filterButton);
|
|
|
|
|
Debugger::registerToolbar(MemcheckPerspectiveId, toolbar);
|
2016-03-08 13:43:09 +01:00
|
|
|
|
|
|
|
|
updateFromSettings();
|
|
|
|
|
maybeActiveRunConfigurationChanged();
|
2016-03-02 13:57:37 +01:00
|
|
|
}
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
void MemcheckTool::heobAction()
|
|
|
|
|
{
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
StandardRunnable sr;
|
|
|
|
|
Abi abi;
|
|
|
|
|
bool hasLocalRc = false;
|
2017-12-14 20:12:41 +01:00
|
|
|
Kit *kit = nullptr;
|
2017-12-08 20:27:21 +01:00
|
|
|
if (Project *project = SessionManager::startupProject()) {
|
|
|
|
|
if (Target *target = project->activeTarget()) {
|
|
|
|
|
if (RunConfiguration *rc = target->activeRunConfiguration()) {
|
2017-12-14 20:12:41 +01:00
|
|
|
if (kit = target->kit()) {
|
2017-12-08 20:27:21 +01:00
|
|
|
abi = ToolChainKitInformation::targetAbi(kit);
|
|
|
|
|
|
|
|
|
|
const Runnable runnable = rc->runnable();
|
|
|
|
|
if (runnable.is<StandardRunnable>()) {
|
|
|
|
|
sr = runnable.as<StandardRunnable>();
|
|
|
|
|
const IDevice::ConstPtr device = sr.device;
|
|
|
|
|
hasLocalRc = device && device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE;
|
|
|
|
|
if (!hasLocalRc)
|
|
|
|
|
hasLocalRc = DeviceTypeKitInformation::deviceTypeId(kit) == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!hasLocalRc) {
|
|
|
|
|
const QString msg = tr("heob: No local run configuration available");
|
|
|
|
|
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
TaskHub::requestPopup();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (abi.architecture() != Abi::X86Architecture
|
|
|
|
|
|| abi.os() != Abi::WindowsOS
|
|
|
|
|
|| abi.binaryFormat() != Abi::PEFormat
|
|
|
|
|
|| (abi.wordWidth() != 32 && abi.wordWidth() != 64)) {
|
|
|
|
|
const QString msg = tr("heob: No toolchain available");
|
|
|
|
|
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
TaskHub::requestPopup();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString executable = sr.executable;
|
|
|
|
|
const QString workingDirectory = Utils::FileUtils::normalizePathName(sr.workingDirectory);
|
|
|
|
|
const QString commandLineArguments = sr.commandLineArguments;
|
|
|
|
|
const QStringList envStrings = sr.environment.toStringList();
|
|
|
|
|
|
|
|
|
|
// target executable
|
|
|
|
|
if (executable.isEmpty()) {
|
|
|
|
|
const QString msg = tr("heob: No executable set");
|
|
|
|
|
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
TaskHub::requestPopup();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!QFile::exists(executable)) {
|
|
|
|
|
const QString msg = tr("heob: Can't find %1").arg(executable);
|
|
|
|
|
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
TaskHub::requestPopup();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// heob executable
|
|
|
|
|
const QString heob = QString("heob%1.exe").arg(abi.wordWidth());
|
|
|
|
|
const QString heobPath = QStandardPaths::findExecutable(heob);
|
|
|
|
|
if (heobPath.isEmpty()) {
|
|
|
|
|
const QString msg = tr("heob: Can't find %1").arg(heob);
|
|
|
|
|
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
TaskHub::requestPopup();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// make executable a relative path if possible
|
|
|
|
|
const QString wdSlashed = workingDirectory + '/';
|
|
|
|
|
if (executable.startsWith(wdSlashed, Qt::CaseInsensitive))
|
|
|
|
|
executable.remove(0, wdSlashed.size());
|
|
|
|
|
|
|
|
|
|
// heob arguments
|
|
|
|
|
HeobDialog dialog(Core::ICore::mainWindow());
|
|
|
|
|
if (!dialog.exec())
|
|
|
|
|
return;
|
|
|
|
|
const QString heobArguments = dialog.arguments();
|
|
|
|
|
|
|
|
|
|
// output xml file
|
|
|
|
|
QDir wdDir(workingDirectory);
|
|
|
|
|
const QString xmlPath = wdDir.absoluteFilePath(dialog.xmlName());
|
|
|
|
|
QFile::remove(xmlPath);
|
|
|
|
|
|
|
|
|
|
// full command line
|
|
|
|
|
QString arguments = heob + heobArguments + " \"" + executable + '\"';
|
|
|
|
|
if (!commandLineArguments.isEmpty())
|
|
|
|
|
arguments += ' ' + commandLineArguments;
|
|
|
|
|
QByteArray argumentsCopy(reinterpret_cast<const char *>(arguments.utf16()), arguments.size() * 2 + 2);
|
|
|
|
|
|
|
|
|
|
// process environment
|
|
|
|
|
QByteArray env;
|
|
|
|
|
void *envPtr = nullptr;
|
|
|
|
|
if (!envStrings.isEmpty()) {
|
|
|
|
|
uint pos = 0;
|
|
|
|
|
for (const QString &par : envStrings) {
|
|
|
|
|
uint parsize = par.size() * 2 + 2;
|
|
|
|
|
env.resize(env.size() + parsize);
|
|
|
|
|
memcpy(env.data() + pos, par.utf16(), parsize);
|
|
|
|
|
pos += parsize;
|
|
|
|
|
}
|
|
|
|
|
env.resize(env.size() + 2);
|
|
|
|
|
env[pos++] = 0;
|
|
|
|
|
env[pos++] = 0;
|
|
|
|
|
|
|
|
|
|
envPtr = env.data();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// heob process
|
|
|
|
|
STARTUPINFO si;
|
|
|
|
|
PROCESS_INFORMATION pi;
|
|
|
|
|
memset(&si, 0, sizeof(STARTUPINFO));
|
|
|
|
|
si.cb = sizeof(STARTUPINFO);
|
|
|
|
|
if (!CreateProcess(reinterpret_cast<LPCWSTR>(heobPath.utf16()),
|
|
|
|
|
reinterpret_cast<LPWSTR>(argumentsCopy.data()), 0, 0, FALSE,
|
|
|
|
|
CREATE_UNICODE_ENVIRONMENT | CREATE_SUSPENDED | CREATE_NEW_CONSOLE, envPtr,
|
|
|
|
|
reinterpret_cast<LPCWSTR>(workingDirectory.utf16()), &si, &pi)) {
|
|
|
|
|
DWORD e = GetLastError();
|
|
|
|
|
const QString msg = tr("heob: Can't create %1 process (%2)").arg(heob).arg(qt_error_string(e));
|
|
|
|
|
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
TaskHub::requestPopup();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// heob finished signal handler
|
2017-12-14 20:12:41 +01:00
|
|
|
HeobData *hd = new HeobData(this, xmlPath, kit, dialog.attach());
|
2017-12-08 20:27:21 +01:00
|
|
|
if (!hd->createErrorPipe(pi.dwProcessId)) {
|
|
|
|
|
delete hd;
|
|
|
|
|
hd = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ResumeThread(pi.hThread);
|
|
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
|
CloseHandle(pi.hProcess);
|
|
|
|
|
|
|
|
|
|
if (hd)
|
|
|
|
|
hd->readExitData();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-02 13:57:37 +01:00
|
|
|
void MemcheckTool::updateRunActions()
|
|
|
|
|
{
|
|
|
|
|
if (m_toolBusy) {
|
|
|
|
|
m_startAction->setEnabled(false);
|
|
|
|
|
m_startAction->setToolTip(tr("A Valgrind Memcheck analysis is still in progress."));
|
|
|
|
|
m_startWithGdbAction->setEnabled(false);
|
|
|
|
|
m_startWithGdbAction->setToolTip(tr("A Valgrind Memcheck analysis is still in progress."));
|
|
|
|
|
m_stopAction->setEnabled(true);
|
|
|
|
|
} else {
|
2016-03-09 15:34:55 +01:00
|
|
|
QString whyNot = tr("Start a Valgrind Memcheck analysis.");
|
|
|
|
|
bool canRun = ProjectExplorerPlugin::canRunStartupProject(MEMCHECK_RUN_MODE, &whyNot);
|
|
|
|
|
m_startAction->setToolTip(whyNot);
|
|
|
|
|
m_startAction->setEnabled(canRun);
|
|
|
|
|
whyNot = tr("Start a Valgrind Memcheck with GDB analysis.");
|
|
|
|
|
canRun = ProjectExplorerPlugin::canRunStartupProject(MEMCHECK_WITH_GDB_RUN_MODE, &whyNot);
|
|
|
|
|
m_startWithGdbAction->setToolTip(whyNot);
|
|
|
|
|
m_startWithGdbAction->setEnabled(canRun);
|
|
|
|
|
m_stopAction->setEnabled(false);
|
2016-03-02 13:57:37 +01:00
|
|
|
}
|
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);
|
2013-08-08 17:37:37 +02:00
|
|
|
m_settings = ValgrindPlugin::globalSettings();
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
|
2013-09-09 14:34:19 +02:00
|
|
|
void MemcheckTool::updateFromSettings()
|
|
|
|
|
{
|
|
|
|
|
foreach (QAction *action, m_errorFilterActions) {
|
|
|
|
|
bool contained = true;
|
|
|
|
|
foreach (const QVariant &v, action->data().toList()) {
|
|
|
|
|
bool ok;
|
|
|
|
|
int kind = v.toInt(&ok);
|
|
|
|
|
if (ok && !m_settings->visibleErrorKinds().contains(kind))
|
|
|
|
|
contained = false;
|
|
|
|
|
}
|
|
|
|
|
action->setChecked(contained);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_filterProjectAction->setChecked(!m_settings->filterExternalIssues());
|
|
|
|
|
m_errorView->settingsChanged(m_settings);
|
|
|
|
|
|
2015-01-30 11:02:24 +01:00
|
|
|
connect(m_settings, &ValgrindBaseSettings::visibleErrorKindsChanged,
|
2016-03-08 15:03:31 +01:00
|
|
|
&m_errorProxyModel, &MemcheckErrorFilterProxyModel::setAcceptedKinds);
|
|
|
|
|
m_errorProxyModel.setAcceptedKinds(m_settings->visibleErrorKinds());
|
2013-09-09 14:34:19 +02:00
|
|
|
|
2015-01-30 11:02:24 +01:00
|
|
|
connect(m_settings, &ValgrindBaseSettings::filterExternalIssuesChanged,
|
2016-03-08 15:03:31 +01:00
|
|
|
&m_errorProxyModel, &MemcheckErrorFilterProxyModel::setFilterExternalIssues);
|
|
|
|
|
m_errorProxyModel.setFilterExternalIssues(m_settings->filterExternalIssues());
|
2013-09-09 14:34:19 +02:00
|
|
|
}
|
|
|
|
|
|
2011-03-04 12:15:19 +01:00
|
|
|
void MemcheckTool::maybeActiveRunConfigurationChanged()
|
|
|
|
|
{
|
2016-03-02 13:57:37 +01:00
|
|
|
updateRunActions();
|
|
|
|
|
|
2013-08-08 17:37:37 +02:00
|
|
|
ValgrindBaseSettings *settings = 0;
|
2013-09-05 11:46:07 +02:00
|
|
|
if (Project *project = SessionManager::startupProject())
|
2013-01-10 11:36:15 +01:00
|
|
|
if (Target *target = project->activeTarget())
|
|
|
|
|
if (RunConfiguration *rc = target->activeRunConfiguration())
|
2013-08-12 17:04:10 +02:00
|
|
|
if (IRunConfigurationAspect *aspect = rc->extraAspect(ANALYZER_VALGRIND_SETTINGS))
|
|
|
|
|
settings = qobject_cast<ValgrindBaseSettings *>(aspect->currentSettings());
|
2011-03-04 12:15:19 +01:00
|
|
|
|
|
|
|
|
if (!settings) // fallback to global settings
|
2013-08-08 17:37:37 +02:00
|
|
|
settings = ValgrindPlugin::globalSettings();
|
2011-03-04 12:15:19 +01:00
|
|
|
|
|
|
|
|
if (m_settings == settings)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// disconnect old settings class if any
|
|
|
|
|
if (m_settings) {
|
|
|
|
|
m_settings->disconnect(this);
|
2016-03-08 15:03:31 +01:00
|
|
|
m_settings->disconnect(&m_errorProxyModel);
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// now make the new settings current, update and connect input widgets
|
|
|
|
|
m_settings = settings;
|
|
|
|
|
QTC_ASSERT(m_settings, return);
|
2015-01-30 11:02:24 +01:00
|
|
|
connect(m_settings, &ValgrindBaseSettings::destroyed, this, &MemcheckTool::settingsDestroyed);
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2013-09-09 14:34:19 +02:00
|
|
|
updateFromSettings();
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
|
2017-05-09 10:25:11 +02:00
|
|
|
RunWorker *MemcheckTool::createRunWorker(RunControl *runControl)
|
2011-03-04 12:15:19 +01:00
|
|
|
{
|
2017-12-06 10:27:27 +01:00
|
|
|
m_errorModel.setRelevantFrameFinder(makeFrameFinder(transform(runControl->project()->files(Project::AllFiles),
|
|
|
|
|
&FileName::toString)));
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2017-06-21 07:54:54 +02:00
|
|
|
auto runTool = new MemcheckToolRunner(runControl, runControl->runMode() == MEMCHECK_WITH_GDB_RUN_MODE);
|
2016-03-02 13:57:37 +01:00
|
|
|
|
2017-04-27 17:23:28 +02:00
|
|
|
connect(runTool, &MemcheckToolRunner::parserError, this, &MemcheckTool::parserError);
|
|
|
|
|
connect(runTool, &MemcheckToolRunner::internalParserError, this, &MemcheckTool::internalParserError);
|
2017-05-16 07:53:03 +02:00
|
|
|
connect(runTool, &MemcheckToolRunner::stopped, this, &MemcheckTool::engineFinished);
|
2017-04-27 17:23:28 +02:00
|
|
|
|
2017-07-13 15:24:04 +02:00
|
|
|
m_stopAction->disconnect();
|
2017-06-26 18:40:11 +02:00
|
|
|
connect(m_stopAction, &QAction::triggered, runControl, &RunControl::initiateStop);
|
2016-03-02 13:57:37 +01:00
|
|
|
|
|
|
|
|
m_toolBusy = true;
|
|
|
|
|
updateRunActions();
|
|
|
|
|
|
2011-12-13 14:01:52 +01:00
|
|
|
setBusyCursor(true);
|
2011-03-10 16:11:20 +01:00
|
|
|
clearErrorView();
|
2013-09-09 14:34:19 +02:00
|
|
|
m_loadExternalLogFile->setDisabled(true);
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2017-09-15 11:25:39 +02:00
|
|
|
QString dir = runControl->project()->projectDirectory().toString() + '/';
|
|
|
|
|
const QString name = FileName::fromString(runTool->executable()).fileName();
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2017-09-15 11:25:39 +02:00
|
|
|
m_errorView->setDefaultSuppressionFile(dir + name + ".supp");
|
2011-03-04 12:15:19 +01:00
|
|
|
|
2017-04-27 17:23:28 +02:00
|
|
|
foreach (const QString &file, runTool->suppressionFiles()) {
|
2017-09-15 11:25:39 +02:00
|
|
|
QAction *action = m_filterMenu->addAction(FileName::fromString(file).fileName());
|
2011-03-04 12:15:19 +01:00
|
|
|
action->setToolTip(file);
|
2017-09-15 11:25:39 +02:00
|
|
|
connect(action, &QAction::triggered, this, [file] {
|
|
|
|
|
EditorManager::openEditorAt(file, 0);
|
2016-01-20 23:48:30 +01:00
|
|
|
});
|
2011-06-27 15:43:15 +02:00
|
|
|
m_suppressionActions.append(action);
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
2017-06-27 17:30:32 +02:00
|
|
|
|
|
|
|
|
return runTool;
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
void MemcheckTool::loadShowXmlLogFile(const QString &filePath, const QString &exitMsg)
|
|
|
|
|
{
|
|
|
|
|
clearErrorView();
|
|
|
|
|
m_settings->setFilterExternalIssues(false);
|
|
|
|
|
m_filterProjectAction->setChecked(true);
|
|
|
|
|
Debugger::selectPerspective(MemcheckPerspectiveId);
|
|
|
|
|
Core::ModeManager::activateMode(Debugger::Constants::MODE_DEBUG);
|
|
|
|
|
|
|
|
|
|
m_exitMsg = exitMsg;
|
|
|
|
|
loadXmlLogFile(filePath);
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-09 14:34:19 +02:00
|
|
|
void MemcheckTool::loadExternalXmlLogFile()
|
|
|
|
|
{
|
2013-09-29 14:41:37 +03:00
|
|
|
const QString filePath = QFileDialog::getOpenFileName(
|
2017-09-15 11:25:39 +02:00
|
|
|
ICore::mainWindow(),
|
2013-09-29 14:41:37 +03:00
|
|
|
tr("Open Memcheck XML Log File"),
|
|
|
|
|
QString(),
|
|
|
|
|
tr("XML Files (*.xml);;All Files (*)"));
|
2013-09-09 14:34:19 +02:00
|
|
|
if (filePath.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
m_exitMsg.clear();
|
|
|
|
|
loadXmlLogFile(filePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckTool::loadXmlLogFile(const QString &filePath)
|
|
|
|
|
{
|
2013-09-09 14:34:19 +02:00
|
|
|
QFile *logFile = new QFile(filePath);
|
|
|
|
|
if (!logFile->open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
|
|
|
delete logFile;
|
2016-05-31 16:09:48 +02:00
|
|
|
QString msg = tr("Memcheck: Failed to open file for reading: %1").arg(filePath);
|
|
|
|
|
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
TaskHub::requestPopup();
|
2017-12-08 20:27:21 +01:00
|
|
|
if (!m_exitMsg.isEmpty())
|
|
|
|
|
Debugger::showPermanentStatusMessage(m_exitMsg);
|
2013-09-09 14:34:19 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setBusyCursor(true);
|
|
|
|
|
clearErrorView();
|
|
|
|
|
m_loadExternalLogFile->setDisabled(true);
|
|
|
|
|
|
|
|
|
|
if (!m_settings || m_settings != ValgrindPlugin::globalSettings()) {
|
|
|
|
|
m_settings = ValgrindPlugin::globalSettings();
|
|
|
|
|
m_errorView->settingsChanged(m_settings);
|
|
|
|
|
updateFromSettings();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ThreadedParser *parser = new ThreadedParser;
|
2015-01-30 11:02:24 +01:00
|
|
|
connect(parser, &ThreadedParser::error, this, &MemcheckTool::parserError);
|
|
|
|
|
connect(parser, &ThreadedParser::internalError, this, &MemcheckTool::internalParserError);
|
|
|
|
|
connect(parser, &ThreadedParser::finished, this, &MemcheckTool::loadingExternalXmlLogFileFinished);
|
|
|
|
|
connect(parser, &ThreadedParser::finished, parser, &ThreadedParser::deleteLater);
|
2013-09-09 14:34:19 +02:00
|
|
|
|
|
|
|
|
parser->parse(logFile); // ThreadedParser owns the file
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:56:02 +02:00
|
|
|
void MemcheckTool::parserError(const Error &error)
|
2011-03-04 12:15:19 +01:00
|
|
|
{
|
2016-03-08 15:03:31 +01:00
|
|
|
m_errorModel.addError(error);
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckTool::internalParserError(const QString &errorString)
|
|
|
|
|
{
|
2016-05-31 16:09:48 +02:00
|
|
|
QString msg = tr("Memcheck: Error occurred parsing Valgrind output: %1").arg(errorString);
|
|
|
|
|
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
TaskHub::requestPopup();
|
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);
|
2016-03-08 15:03:31 +01:00
|
|
|
m_errorModel.clear();
|
2011-03-04 12:15:19 +01:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2013-08-08 17:37:37 +02:00
|
|
|
m_settings->setFilterExternalIssues(!m_filterProjectAction->isChecked());
|
2011-03-04 12:15:19 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-08 17:37:37 +02:00
|
|
|
m_settings->setVisibleErrorKinds(errorKinds);
|
2011-03-04 12:15:19 +01:00
|
|
|
}
|
2011-03-10 16:11:20 +01:00
|
|
|
|
2013-09-09 14:34:19 +02:00
|
|
|
int MemcheckTool::updateUiAfterFinishedHelper()
|
2011-03-16 13:49:28 +01:00
|
|
|
{
|
2016-03-08 15:03:31 +01:00
|
|
|
const int issuesFound = m_errorModel.rowCount();
|
2013-08-02 12:08:27 +02:00
|
|
|
m_goBack->setEnabled(issuesFound > 1);
|
|
|
|
|
m_goNext->setEnabled(issuesFound > 1);
|
2013-09-09 14:34:19 +02:00
|
|
|
m_loadExternalLogFile->setEnabled(true);
|
|
|
|
|
setBusyCursor(false);
|
|
|
|
|
return issuesFound;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckTool::engineFinished()
|
|
|
|
|
{
|
2016-03-02 13:57:37 +01:00
|
|
|
m_toolBusy = false;
|
|
|
|
|
updateRunActions();
|
|
|
|
|
|
2013-09-09 14:34:19 +02:00
|
|
|
const int issuesFound = updateUiAfterFinishedHelper();
|
2016-03-02 13:57:37 +01:00
|
|
|
Debugger::showPermanentStatusMessage(
|
|
|
|
|
tr("Memory Analyzer Tool finished, %n issues were found.", 0, issuesFound));
|
2013-09-09 14:34:19 +02:00
|
|
|
}
|
2013-08-02 12:08:27 +02:00
|
|
|
|
2013-09-09 14:34:19 +02:00
|
|
|
void MemcheckTool::loadingExternalXmlLogFileFinished()
|
|
|
|
|
{
|
|
|
|
|
const int issuesFound = updateUiAfterFinishedHelper();
|
2017-12-08 20:27:21 +01:00
|
|
|
QString statusMessage = tr("Log file processed, %n issues were found.", 0, issuesFound);
|
|
|
|
|
if (!m_exitMsg.isEmpty())
|
|
|
|
|
statusMessage += ' ' + m_exitMsg;
|
|
|
|
|
Debugger::showPermanentStatusMessage(statusMessage);
|
2011-12-13 14:01:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemcheckTool::setBusyCursor(bool busy)
|
|
|
|
|
{
|
|
|
|
|
QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
|
|
|
|
|
m_errorView->setCursor(cursor);
|
2011-06-29 18:48:08 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-02 12:05:30 +01:00
|
|
|
|
2017-07-03 13:55:25 +02:00
|
|
|
static MemcheckTool *theMemcheckTool;
|
2016-03-07 17:33:58 +01:00
|
|
|
|
|
|
|
|
void initMemcheckTool()
|
2016-03-02 12:05:30 +01:00
|
|
|
{
|
2017-07-03 13:55:25 +02:00
|
|
|
theMemcheckTool = new MemcheckTool;
|
|
|
|
|
|
|
|
|
|
auto producer = std::bind(&MemcheckTool::createRunWorker, theMemcheckTool, _1);
|
|
|
|
|
RunControl::registerWorker(MEMCHECK_RUN_MODE, producer);
|
|
|
|
|
RunControl::registerWorker(MEMCHECK_WITH_GDB_RUN_MODE, producer);
|
2016-03-02 12:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void destroyMemcheckTool()
|
|
|
|
|
{
|
2017-07-03 13:55:25 +02:00
|
|
|
delete theMemcheckTool;
|
|
|
|
|
theMemcheckTool = nullptr;
|
2016-03-02 12:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN
|
2017-12-20 17:26:57 +01:00
|
|
|
const char heobXmlC[] = "heob/Xml";
|
|
|
|
|
const char heobHandleExceptionC[] = "heob/HandleException";
|
|
|
|
|
const char heobPageProtectionC[] = "heob/PageProtection";
|
|
|
|
|
const char heobFreedProtectionC[] = "heob/FreedProtection";
|
|
|
|
|
const char heobBreakpointC[] = "heob/Breakpoint";
|
|
|
|
|
const char heobLeakDetailC[] = "heob/LeakDetail";
|
|
|
|
|
const char heobLeakSizeC[] = "heob/LeakSize";
|
|
|
|
|
const char heobLeakRecordingC[] = "heob/LeakRecording";
|
|
|
|
|
const char heobAttachC[] = "heob/Attach";
|
|
|
|
|
const char heobExtraArgsC[] = "heob/ExtraArgs";
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
static QString upperHexNum(unsigned num)
|
|
|
|
|
{
|
|
|
|
|
return QString("%1").arg(num, 8, 16, QChar('0')).toUpper();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HeobDialog::HeobDialog(QWidget *parent) :
|
|
|
|
|
QDialog(parent)
|
|
|
|
|
{
|
2017-12-20 17:26:57 +01:00
|
|
|
QSettings *settings = Core::ICore::settings();
|
|
|
|
|
const QString xml = settings->value(heobXmlC, "leaks.xml").toString();
|
|
|
|
|
int handleException = settings->value(heobHandleExceptionC, 1).toInt();
|
|
|
|
|
int pageProtection = settings->value(heobPageProtectionC, 0).toInt();
|
|
|
|
|
bool freedProtection = settings->value(heobFreedProtectionC, false).toBool();
|
|
|
|
|
bool breakpoint = settings->value(heobBreakpointC, false).toBool();
|
|
|
|
|
int leakDetail = settings->value(heobLeakDetailC, 1).toInt();
|
|
|
|
|
int leakSize = settings->value(heobLeakSizeC, 0).toInt();
|
|
|
|
|
int leakRecording = settings->value(heobLeakRecordingC, 2).toInt();
|
|
|
|
|
bool attach = settings->value(heobAttachC, false).toBool();
|
|
|
|
|
const QString extraArgs = settings->value(heobExtraArgsC).toString();
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
QVBoxLayout *layout = new QVBoxLayout;
|
|
|
|
|
// disable resizing
|
|
|
|
|
layout->setSizeConstraint(QLayout::SetFixedSize);
|
|
|
|
|
|
|
|
|
|
QHBoxLayout *xmlLayout = new QHBoxLayout;
|
|
|
|
|
QLabel *xmlLabel = new QLabel(tr("xml output file:"));
|
|
|
|
|
xmlLayout->addWidget(xmlLabel);
|
|
|
|
|
m_xmlEdit = new QLineEdit;
|
2017-12-20 17:26:57 +01:00
|
|
|
m_xmlEdit->setText(xml);
|
2017-12-08 20:27:21 +01:00
|
|
|
xmlLayout->addWidget(m_xmlEdit);
|
|
|
|
|
layout->addLayout(xmlLayout);
|
|
|
|
|
|
|
|
|
|
QHBoxLayout *handleExceptionLayout = new QHBoxLayout;
|
|
|
|
|
QLabel *handleExceptionLabel = new QLabel(tr("handle exceptions:"));
|
|
|
|
|
handleExceptionLayout->addWidget(handleExceptionLabel);
|
|
|
|
|
m_handleExceptionCombo = new QComboBox;
|
|
|
|
|
m_handleExceptionCombo->addItem(tr("off"));
|
|
|
|
|
m_handleExceptionCombo->addItem(tr("on"));
|
|
|
|
|
m_handleExceptionCombo->addItem(tr("only"));
|
2017-12-20 17:26:57 +01:00
|
|
|
m_handleExceptionCombo->setCurrentIndex(handleException);
|
2017-12-08 20:27:21 +01:00
|
|
|
connect(m_handleExceptionCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
|
|
|
|
this, &HeobDialog::updateEnabled);
|
|
|
|
|
handleExceptionLayout->addWidget(m_handleExceptionCombo);
|
|
|
|
|
layout->addLayout(handleExceptionLayout);
|
|
|
|
|
|
|
|
|
|
QHBoxLayout *pageProtectionLayout = new QHBoxLayout;
|
|
|
|
|
QLabel *pageProtectionLabel = new QLabel(tr("page protection:"));
|
|
|
|
|
pageProtectionLayout->addWidget(pageProtectionLabel);
|
|
|
|
|
m_pageProtectionCombo = new QComboBox;
|
|
|
|
|
m_pageProtectionCombo->addItem(tr("off"));
|
|
|
|
|
m_pageProtectionCombo->addItem(tr("after"));
|
|
|
|
|
m_pageProtectionCombo->addItem(tr("before"));
|
2017-12-20 17:26:57 +01:00
|
|
|
m_pageProtectionCombo->setCurrentIndex(pageProtection);
|
2017-12-08 20:27:21 +01:00
|
|
|
connect(m_pageProtectionCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
|
|
|
|
this, &HeobDialog::updateEnabled);
|
|
|
|
|
pageProtectionLayout->addWidget(m_pageProtectionCombo);
|
|
|
|
|
layout->addLayout(pageProtectionLayout);
|
|
|
|
|
|
|
|
|
|
m_freedProtectionCheck = new QCheckBox(tr("freed memory protection"));
|
2017-12-20 17:26:57 +01:00
|
|
|
m_freedProtectionCheck->setChecked(freedProtection);
|
2017-12-08 20:27:21 +01:00
|
|
|
layout->addWidget(m_freedProtectionCheck);
|
|
|
|
|
|
|
|
|
|
m_breakpointCheck = new QCheckBox(tr("raise breakpoint exception on error"));
|
2017-12-20 17:26:57 +01:00
|
|
|
m_breakpointCheck->setChecked(breakpoint);
|
2017-12-08 20:27:21 +01:00
|
|
|
layout->addWidget(m_breakpointCheck);
|
|
|
|
|
|
|
|
|
|
QHBoxLayout *leakDetailLayout = new QHBoxLayout;
|
|
|
|
|
QLabel *leakDetailLabel = new QLabel(tr("leak details:"));
|
|
|
|
|
leakDetailLayout->addWidget(leakDetailLabel);
|
|
|
|
|
m_leakDetailCombo = new QComboBox;
|
|
|
|
|
m_leakDetailCombo->addItem(tr("none"));
|
|
|
|
|
m_leakDetailCombo->addItem(tr("simple"));
|
|
|
|
|
m_leakDetailCombo->addItem(tr("detect leak types"));
|
|
|
|
|
m_leakDetailCombo->addItem(tr("detect leak types (show reachable)"));
|
|
|
|
|
m_leakDetailCombo->addItem(tr("fuzzy detect leak types"));
|
|
|
|
|
m_leakDetailCombo->addItem(tr("fuzzy detect leak types (show reachable)"));
|
2017-12-20 17:26:57 +01:00
|
|
|
m_leakDetailCombo->setCurrentIndex(leakDetail);
|
2017-12-08 20:27:21 +01:00
|
|
|
connect(m_leakDetailCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
|
|
|
|
this, &HeobDialog::updateEnabled);
|
|
|
|
|
leakDetailLayout->addWidget(m_leakDetailCombo);
|
|
|
|
|
layout->addLayout(leakDetailLayout);
|
|
|
|
|
|
|
|
|
|
QHBoxLayout *leakSizeLayout = new QHBoxLayout;
|
|
|
|
|
QLabel *leakSizeLabel = new QLabel(tr("minimum leak size:"));
|
|
|
|
|
leakSizeLayout->addWidget(leakSizeLabel);
|
|
|
|
|
m_leakSizeSpin = new QSpinBox;
|
|
|
|
|
m_leakSizeSpin->setMinimum(0);
|
|
|
|
|
m_leakSizeSpin->setMaximum(INT_MAX);
|
|
|
|
|
m_leakSizeSpin->setSingleStep(1000);
|
2017-12-20 17:26:57 +01:00
|
|
|
m_leakSizeSpin->setValue(leakSize);
|
2017-12-08 20:27:21 +01:00
|
|
|
leakSizeLayout->addWidget(m_leakSizeSpin);
|
|
|
|
|
layout->addLayout(leakSizeLayout);
|
|
|
|
|
|
|
|
|
|
QHBoxLayout *leakRecordingLayout = new QHBoxLayout;
|
|
|
|
|
QLabel *leakRecordingLabel = new QLabel(tr("control leak recording:"));
|
|
|
|
|
leakRecordingLayout->addWidget(leakRecordingLabel);
|
|
|
|
|
m_leakRecordingCombo = new QComboBox;
|
|
|
|
|
m_leakRecordingCombo->addItem(tr("off"));
|
|
|
|
|
m_leakRecordingCombo->addItem(tr("on (start disabled)"));
|
|
|
|
|
m_leakRecordingCombo->addItem(tr("on (start enabled)"));
|
2017-12-20 17:26:57 +01:00
|
|
|
m_leakRecordingCombo->setCurrentIndex(leakRecording);
|
2017-12-08 20:27:21 +01:00
|
|
|
leakRecordingLayout->addWidget(m_leakRecordingCombo);
|
|
|
|
|
layout->addLayout(leakRecordingLayout);
|
|
|
|
|
|
2017-12-14 20:12:41 +01:00
|
|
|
m_attachCheck = new QCheckBox(tr("Run with debugger"));
|
2017-12-20 17:26:57 +01:00
|
|
|
m_attachCheck->setChecked(attach);
|
2017-12-14 20:12:41 +01:00
|
|
|
layout->addWidget(m_attachCheck);
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
QHBoxLayout *extraArgsLayout = new QHBoxLayout;
|
|
|
|
|
QLabel *extraArgsLabel = new QLabel(tr("extra arguments:"));
|
|
|
|
|
extraArgsLayout->addWidget(extraArgsLabel);
|
|
|
|
|
m_extraArgsEdit = new QLineEdit;
|
2017-12-20 17:26:57 +01:00
|
|
|
m_extraArgsEdit->setText(extraArgs);
|
2017-12-08 20:27:21 +01:00
|
|
|
extraArgsLayout->addWidget(m_extraArgsEdit);
|
|
|
|
|
layout->addLayout(extraArgsLayout);
|
|
|
|
|
|
2017-12-20 17:26:57 +01:00
|
|
|
QHBoxLayout *saveLayout = new QHBoxLayout;
|
|
|
|
|
saveLayout->addStretch(1);
|
|
|
|
|
QToolButton *saveButton = new QToolButton;
|
|
|
|
|
saveButton->setToolTip(tr("Save current settings as default."));
|
|
|
|
|
saveButton->setIcon(style()->standardIcon(QStyle::SP_DialogSaveButton));
|
|
|
|
|
connect(saveButton, &QAbstractButton::clicked, this, &HeobDialog::saveOptions);
|
|
|
|
|
saveLayout->addWidget(saveButton);
|
|
|
|
|
layout->addLayout(saveLayout);
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
QHBoxLayout *okLayout = new QHBoxLayout;
|
|
|
|
|
okLayout->addStretch(1);
|
|
|
|
|
QPushButton *okButton = new QPushButton(tr("OK"));
|
|
|
|
|
connect(okButton, &QAbstractButton::clicked, this, &QDialog::accept);
|
|
|
|
|
okLayout->addWidget(okButton);
|
|
|
|
|
okLayout->addStretch(1);
|
|
|
|
|
layout->addLayout(okLayout);
|
|
|
|
|
|
|
|
|
|
setLayout(layout);
|
|
|
|
|
|
|
|
|
|
updateEnabled();
|
|
|
|
|
|
|
|
|
|
setWindowTitle(tr("heob"));
|
|
|
|
|
|
|
|
|
|
// disable context help button
|
|
|
|
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString HeobDialog::arguments() const
|
|
|
|
|
{
|
|
|
|
|
QString args;
|
|
|
|
|
|
|
|
|
|
args += " -A";
|
|
|
|
|
|
|
|
|
|
const QString xml = xmlName();
|
|
|
|
|
if (!xml.isEmpty())
|
|
|
|
|
args += " -x" + xml;
|
|
|
|
|
|
|
|
|
|
int handleException = m_handleExceptionCombo->currentIndex();
|
|
|
|
|
args += QString(" -h%1").arg(handleException);
|
|
|
|
|
|
|
|
|
|
int pageProtection = m_pageProtectionCombo->currentIndex();
|
|
|
|
|
args += QString(" -p%1").arg(pageProtection);
|
|
|
|
|
|
|
|
|
|
int freedProtection = m_freedProtectionCheck->isChecked() ? 1 : 0;
|
|
|
|
|
args += QString(" -f%1").arg(freedProtection);
|
|
|
|
|
|
|
|
|
|
int breakpoint = m_breakpointCheck->isChecked() ? 1 : 0;
|
|
|
|
|
args += QString(" -r%1").arg(breakpoint);
|
|
|
|
|
|
|
|
|
|
int leakDetail = m_leakDetailCombo->currentIndex();
|
|
|
|
|
args += QString(" -l%1").arg(leakDetail);
|
|
|
|
|
|
|
|
|
|
int leakSize = m_leakSizeSpin->value();
|
|
|
|
|
args += QString(" -z%1").arg(leakSize);
|
|
|
|
|
|
|
|
|
|
int leakRecording = m_leakRecordingCombo->currentIndex();
|
|
|
|
|
args += QString(" -k%1").arg(leakRecording);
|
|
|
|
|
|
|
|
|
|
const QString extraArgs = m_extraArgsEdit->text();
|
|
|
|
|
if (!extraArgs.isEmpty())
|
|
|
|
|
args += ' ' + extraArgs;
|
|
|
|
|
|
|
|
|
|
return args;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString HeobDialog::xmlName() const
|
|
|
|
|
{
|
|
|
|
|
return m_xmlEdit->text().replace(' ', '_');
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-14 20:12:41 +01:00
|
|
|
bool HeobDialog::attach() const
|
|
|
|
|
{
|
|
|
|
|
return m_attachCheck->isChecked();
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
void HeobDialog::updateEnabled()
|
|
|
|
|
{
|
|
|
|
|
bool enableHeob = m_handleExceptionCombo->currentIndex() < 2;
|
|
|
|
|
bool enableLeakDetection = enableHeob && m_leakDetailCombo->currentIndex() > 0;
|
|
|
|
|
bool enablePageProtection = enableHeob && m_pageProtectionCombo->currentIndex() > 0;
|
|
|
|
|
|
|
|
|
|
m_leakDetailCombo->setEnabled(enableHeob);
|
|
|
|
|
m_pageProtectionCombo->setEnabled(enableHeob);
|
|
|
|
|
m_breakpointCheck->setEnabled(enableHeob);
|
|
|
|
|
|
|
|
|
|
m_leakSizeSpin->setEnabled(enableLeakDetection);
|
|
|
|
|
m_leakRecordingCombo->setEnabled(enableLeakDetection);
|
|
|
|
|
|
|
|
|
|
m_freedProtectionCheck->setEnabled(enablePageProtection);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-20 17:26:57 +01:00
|
|
|
void HeobDialog::saveOptions()
|
|
|
|
|
{
|
|
|
|
|
QSettings *settings = Core::ICore::settings();
|
|
|
|
|
settings->setValue(heobXmlC, m_xmlEdit->text());
|
|
|
|
|
settings->setValue(heobHandleExceptionC, m_handleExceptionCombo->currentIndex());
|
|
|
|
|
settings->setValue(heobPageProtectionC, m_pageProtectionCombo->currentIndex());
|
|
|
|
|
settings->setValue(heobFreedProtectionC, m_freedProtectionCheck->isChecked());
|
|
|
|
|
settings->setValue(heobBreakpointC, m_breakpointCheck->isChecked());
|
|
|
|
|
settings->setValue(heobLeakDetailC, m_leakDetailCombo->currentIndex());
|
|
|
|
|
settings->setValue(heobLeakSizeC, m_leakSizeSpin->value());
|
|
|
|
|
settings->setValue(heobLeakRecordingC, m_leakRecordingCombo->currentIndex());
|
|
|
|
|
settings->setValue(heobAttachC, m_attachCheck->isChecked());
|
|
|
|
|
settings->setValue(heobExtraArgsC, m_extraArgsEdit->text());
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-14 20:12:41 +01:00
|
|
|
HeobData::HeobData(MemcheckTool *mcTool, const QString &xmlPath, Kit *kit, bool attach)
|
|
|
|
|
: m_mcTool(mcTool), m_xmlPath(xmlPath), m_kit(kit), m_attach(attach), m_ov(), m_data()
|
2017-12-08 20:27:21 +01:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HeobData::~HeobData()
|
|
|
|
|
{
|
|
|
|
|
delete m_processFinishedNotifier;
|
|
|
|
|
if (m_errorPipe != INVALID_HANDLE_VALUE)
|
|
|
|
|
CloseHandle(m_errorPipe);
|
|
|
|
|
if (m_ov.hEvent)
|
|
|
|
|
CloseHandle(m_ov.hEvent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool HeobData::createErrorPipe(DWORD heobPid)
|
|
|
|
|
{
|
|
|
|
|
const QString pipeName = QString("\\\\.\\Pipe\\heob.error.%1").arg(upperHexNum(heobPid));
|
2017-12-14 20:12:41 +01:00
|
|
|
DWORD access = m_attach ? PIPE_ACCESS_DUPLEX : PIPE_ACCESS_INBOUND;
|
2017-12-08 20:27:21 +01:00
|
|
|
m_errorPipe = CreateNamedPipe(reinterpret_cast<LPCWSTR>(pipeName.utf16()),
|
|
|
|
|
access | FILE_FLAG_OVERLAPPED,
|
|
|
|
|
PIPE_TYPE_BYTE, 1, 1024, 1024, 0, NULL);
|
|
|
|
|
return m_errorPipe != INVALID_HANDLE_VALUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HeobData::readExitData()
|
|
|
|
|
{
|
|
|
|
|
m_ov.Offset = m_ov.OffsetHigh = 0;
|
|
|
|
|
m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
|
bool pipeConnected = ConnectNamedPipe(m_errorPipe, &m_ov);
|
|
|
|
|
if (!pipeConnected) {
|
|
|
|
|
DWORD error = GetLastError();
|
|
|
|
|
if (error == ERROR_PIPE_CONNECTED) {
|
|
|
|
|
pipeConnected = true;
|
|
|
|
|
} else if (error == ERROR_IO_PENDING) {
|
|
|
|
|
if (WaitForSingleObject(m_ov.hEvent, 1000) == WAIT_OBJECT_0)
|
|
|
|
|
pipeConnected = true;
|
|
|
|
|
else
|
|
|
|
|
CancelIo(m_errorPipe);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (pipeConnected) {
|
|
|
|
|
if (ReadFile(m_errorPipe, m_data, sizeof(m_data), NULL, &m_ov)
|
|
|
|
|
|| GetLastError() == ERROR_IO_PENDING) {
|
|
|
|
|
m_processFinishedNotifier = new QWinEventNotifier(m_ov.hEvent);
|
|
|
|
|
connect(m_processFinishedNotifier, &QWinEventNotifier::activated, this, &HeobData::processFinished);
|
|
|
|
|
m_processFinishedNotifier->setEnabled(true);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// connection to heob error pipe failed
|
|
|
|
|
delete this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
HEOB_OK,
|
|
|
|
|
HEOB_HELP,
|
|
|
|
|
HEOB_BAD_ARG,
|
|
|
|
|
HEOB_PROCESS_FAIL,
|
|
|
|
|
HEOB_WRONG_BITNESS,
|
|
|
|
|
HEOB_PROCESS_KILLED,
|
|
|
|
|
HEOB_NO_CRT,
|
|
|
|
|
HEOB_EXCEPTION,
|
|
|
|
|
HEOB_OUT_OF_MEMORY,
|
|
|
|
|
HEOB_UNEXPECTED_END,
|
|
|
|
|
HEOB_TRACE,
|
|
|
|
|
HEOB_CONSOLE,
|
|
|
|
|
HEOB_PID_ATTACH = 0x10000000,
|
|
|
|
|
};
|
|
|
|
|
|
2017-12-14 20:12:41 +01:00
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
HEOB_CONTROL_NONE,
|
|
|
|
|
HEOB_CONTROL_ATTACH,
|
|
|
|
|
};
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
void HeobData::processFinished()
|
|
|
|
|
{
|
|
|
|
|
m_processFinishedNotifier->setEnabled(false);
|
|
|
|
|
|
|
|
|
|
QString exitMsg;
|
|
|
|
|
bool needErrorMsg = true;
|
|
|
|
|
DWORD didread;
|
|
|
|
|
if (GetOverlappedResult(m_errorPipe, &m_ov, &didread, TRUE) && didread == sizeof(m_data)) {
|
2017-12-14 20:12:41 +01:00
|
|
|
if (m_data[0] >= HEOB_PID_ATTACH) {
|
|
|
|
|
m_runControl = new RunControl(nullptr, ProjectExplorer::Constants::DEBUG_RUN_MODE);
|
|
|
|
|
auto debugger = new DebuggerRunTool(m_runControl, m_kit);
|
|
|
|
|
debugger->setAttachPid(ProcessHandle(m_data[1]));
|
|
|
|
|
debugger->setRunControlName(tr("Process %1").arg(m_data[1]));
|
|
|
|
|
debugger->setInferiorDevice(DeviceKitInformation::device(m_kit));
|
|
|
|
|
debugger->setStartMode(AttachExternal);
|
|
|
|
|
debugger->setCloseMode(DetachAtClose);
|
|
|
|
|
debugger->setContinueAfterAttach(true);
|
|
|
|
|
|
|
|
|
|
HANDLE p = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, m_data[1]);
|
|
|
|
|
if (p != NULL) {
|
|
|
|
|
wchar_t path[MAX_PATH];
|
|
|
|
|
DWORD pathLen = MAX_PATH;
|
|
|
|
|
if (QueryFullProcessImageName(p, 0, path, &pathLen))
|
|
|
|
|
debugger->setInferiorExecutable(QString::fromWCharArray(path));
|
|
|
|
|
CloseHandle(p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connect(m_runControl, &RunControl::started, this, &HeobData::debugStarted);
|
|
|
|
|
connect(m_runControl, &RunControl::stopped, this, &HeobData::debugStopped);
|
|
|
|
|
debugger->startRunControl();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-08 20:27:21 +01:00
|
|
|
switch (m_data[0]) {
|
|
|
|
|
case HEOB_OK:
|
|
|
|
|
exitMsg = tr("Process finished with exit code %1 (0x%2).").arg(m_data[1]).arg(upperHexNum(m_data[1]));
|
|
|
|
|
needErrorMsg = false;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case HEOB_BAD_ARG:
|
|
|
|
|
exitMsg = tr("Unknown argument: -%1").arg((char)m_data[1]);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case HEOB_PROCESS_FAIL:
|
|
|
|
|
exitMsg = tr("Can't create target process");
|
|
|
|
|
if (m_data[1])
|
|
|
|
|
exitMsg += " (" + qt_error_string(m_data[1]) + ')';
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case HEOB_WRONG_BITNESS:
|
|
|
|
|
exitMsg = tr("Wrong bitness");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case HEOB_PROCESS_KILLED:
|
|
|
|
|
exitMsg = tr("Process killed");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case HEOB_NO_CRT:
|
|
|
|
|
exitMsg = tr("Only works with dynamically linked CRT");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case HEOB_EXCEPTION:
|
|
|
|
|
exitMsg = tr("Process stopped with unhandled exception code 0x%1.").arg(upperHexNum(m_data[1]));
|
|
|
|
|
needErrorMsg = false;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case HEOB_OUT_OF_MEMORY:
|
|
|
|
|
exitMsg = tr("Not enough memory to keep track of allocations");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case HEOB_UNEXPECTED_END:
|
|
|
|
|
exitMsg = tr("Unexpected end of application");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case HEOB_CONSOLE:
|
|
|
|
|
exitMsg = tr("Extra console");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case HEOB_HELP:
|
|
|
|
|
case HEOB_TRACE:
|
|
|
|
|
deleteLater();
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
exitMsg = tr("Unknown exit reason");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
exitMsg = tr("Unexpected end of heob");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (needErrorMsg) {
|
|
|
|
|
const QString msg = tr("heob: %1").arg(exitMsg);
|
|
|
|
|
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
TaskHub::requestPopup();
|
|
|
|
|
} else {
|
|
|
|
|
m_mcTool->loadShowXmlLogFile(m_xmlPath, exitMsg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
deleteLater();
|
|
|
|
|
}
|
2017-12-14 20:12:41 +01:00
|
|
|
|
|
|
|
|
void HeobData::sendHeobAttachPid(DWORD pid)
|
|
|
|
|
{
|
|
|
|
|
m_runControl->disconnect(this);
|
|
|
|
|
|
|
|
|
|
m_data[0] = HEOB_CONTROL_ATTACH;
|
|
|
|
|
m_data[1] = pid;
|
|
|
|
|
DWORD e = 0;
|
|
|
|
|
if (WriteFile(m_errorPipe, m_data, sizeof(m_data), NULL, &m_ov)
|
|
|
|
|
|| (e = GetLastError()) == ERROR_IO_PENDING) {
|
|
|
|
|
DWORD didwrite;
|
|
|
|
|
if (GetOverlappedResult(m_errorPipe, &m_ov, &didwrite, TRUE)) {
|
|
|
|
|
if (didwrite == sizeof(m_data)) {
|
|
|
|
|
if (ReadFile(m_errorPipe, m_data, sizeof(m_data), NULL, &m_ov)
|
|
|
|
|
|| (e = GetLastError()) == ERROR_IO_PENDING) {
|
|
|
|
|
m_processFinishedNotifier->setEnabled(true);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
e = ERROR_BAD_LENGTH;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
e = GetLastError();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString msg = tr("heob: Failure in process attach handshake (%1)").arg(qt_error_string(e));
|
|
|
|
|
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
|
|
|
|
|
TaskHub::requestPopup();
|
|
|
|
|
deleteLater();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HeobData::debugStarted()
|
|
|
|
|
{
|
|
|
|
|
sendHeobAttachPid(GetCurrentProcessId());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HeobData::debugStopped()
|
|
|
|
|
{
|
|
|
|
|
sendHeobAttachPid(0);
|
|
|
|
|
}
|
2017-12-08 20:27:21 +01:00
|
|
|
#endif
|
|
|
|
|
|
2011-03-10 16:11:20 +01:00
|
|
|
} // namespace Internal
|
2011-05-23 13:50:28 +02:00
|
|
|
} // namespace Valgrind
|
2017-09-21 12:33:36 +02:00
|
|
|
|
|
|
|
|
#include "memchecktool.moc"
|