forked from qt-creator/qt-creator
Valgrind: Add heob support
Change-Id: Ia5957058c59ae2a607620915c2c68f2cfd5ac5f1 Reviewed-by: Orgad Shaneh <orgads@gmail.com> Reviewed-by: David Schulz <david.schulz@qt.io> Reviewed-by: André Hartmann <aha_1980@gmx.de> Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -69,6 +69,7 @@
|
|||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/id.h>
|
#include <coreplugin/id.h>
|
||||||
|
#include <coreplugin/modemanager.h>
|
||||||
|
|
||||||
#include <ssh/sshconnection.h>
|
#include <ssh/sshconnection.h>
|
||||||
|
|
||||||
@@ -86,6 +87,18 @@
|
|||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QWinEventNotifier>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
using namespace Debugger;
|
using namespace Debugger;
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
@@ -406,6 +419,8 @@ public:
|
|||||||
|
|
||||||
RunWorker *createRunWorker(RunControl *runControl);
|
RunWorker *createRunWorker(RunControl *runControl);
|
||||||
|
|
||||||
|
void loadShowXmlLogFile(const QString &filePath, const QString &exitMsg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateRunActions();
|
void updateRunActions();
|
||||||
void settingsDestroyed(QObject *settings);
|
void settingsDestroyed(QObject *settings);
|
||||||
@@ -419,6 +434,7 @@ private:
|
|||||||
void updateErrorFilter();
|
void updateErrorFilter();
|
||||||
|
|
||||||
void loadExternalXmlLogFile();
|
void loadExternalXmlLogFile();
|
||||||
|
void loadXmlLogFile(const QString &filePath);
|
||||||
|
|
||||||
void setBusyCursor(bool busy);
|
void setBusyCursor(bool busy);
|
||||||
|
|
||||||
@@ -426,6 +442,8 @@ private:
|
|||||||
void updateFromSettings();
|
void updateFromSettings();
|
||||||
int updateUiAfterFinishedHelper();
|
int updateUiAfterFinishedHelper();
|
||||||
|
|
||||||
|
void heobAction();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ValgrindBaseSettings *m_settings;
|
ValgrindBaseSettings *m_settings;
|
||||||
QMenu *m_filterMenu = 0;
|
QMenu *m_filterMenu = 0;
|
||||||
@@ -445,8 +463,57 @@ private:
|
|||||||
QAction *m_goBack;
|
QAction *m_goBack;
|
||||||
QAction *m_goNext;
|
QAction *m_goNext;
|
||||||
bool m_toolBusy = false;
|
bool m_toolBusy = false;
|
||||||
|
|
||||||
|
QString m_exitMsg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
class HeobDialog : public QDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HeobDialog(QWidget *parent);
|
||||||
|
|
||||||
|
QString arguments() const;
|
||||||
|
QString xmlName() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateEnabled();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLineEdit *m_xmlEdit = nullptr;
|
||||||
|
QCheckBox *m_pidWaitCheck = 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;
|
||||||
|
QLineEdit *m_extraArgsEdit = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HeobData : public QObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HeobData(MemcheckTool *mcTool, const QString &xmlPath);
|
||||||
|
~HeobData();
|
||||||
|
|
||||||
|
bool createErrorPipe(DWORD heobPid);
|
||||||
|
void readExitData();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void processFinished();
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
MemcheckTool::MemcheckTool()
|
MemcheckTool::MemcheckTool()
|
||||||
{
|
{
|
||||||
m_settings = ValgrindPlugin::globalSettings();
|
m_settings = ValgrindPlugin::globalSettings();
|
||||||
@@ -589,6 +656,12 @@ MemcheckTool::MemcheckTool()
|
|||||||
QObject::connect(m_startWithGdbAction, &QAction::changed, action, [action, this] {
|
QObject::connect(m_startWithGdbAction, &QAction::changed, action, [action, this] {
|
||||||
action->setEnabled(m_startWithGdbAction->isEnabled());
|
action->setEnabled(m_startWithGdbAction->isEnabled());
|
||||||
});
|
});
|
||||||
|
} 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
action = new QAction(this);
|
action = new QAction(this);
|
||||||
@@ -629,6 +702,147 @@ MemcheckTool::MemcheckTool()
|
|||||||
maybeActiveRunConfigurationChanged();
|
maybeActiveRunConfigurationChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MemcheckTool::heobAction()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
StandardRunnable sr;
|
||||||
|
Abi abi;
|
||||||
|
bool hasLocalRc = false;
|
||||||
|
if (Project *project = SessionManager::startupProject()) {
|
||||||
|
if (Target *target = project->activeTarget()) {
|
||||||
|
if (RunConfiguration *rc = target->activeRunConfiguration()) {
|
||||||
|
if (Kit *kit = target->kit()) {
|
||||||
|
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
|
||||||
|
HeobData *hd = new HeobData(this, xmlPath);
|
||||||
|
if (!hd->createErrorPipe(pi.dwProcessId)) {
|
||||||
|
delete hd;
|
||||||
|
hd = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResumeThread(pi.hThread);
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
|
||||||
|
if (hd)
|
||||||
|
hd->readExitData();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void MemcheckTool::updateRunActions()
|
void MemcheckTool::updateRunActions()
|
||||||
{
|
{
|
||||||
if (m_toolBusy) {
|
if (m_toolBusy) {
|
||||||
@@ -750,6 +964,18 @@ RunWorker *MemcheckTool::createRunWorker(RunControl *runControl)
|
|||||||
return runTool;
|
return runTool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
void MemcheckTool::loadExternalXmlLogFile()
|
void MemcheckTool::loadExternalXmlLogFile()
|
||||||
{
|
{
|
||||||
const QString filePath = QFileDialog::getOpenFileName(
|
const QString filePath = QFileDialog::getOpenFileName(
|
||||||
@@ -760,12 +986,20 @@ void MemcheckTool::loadExternalXmlLogFile()
|
|||||||
if (filePath.isEmpty())
|
if (filePath.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
m_exitMsg.clear();
|
||||||
|
loadXmlLogFile(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemcheckTool::loadXmlLogFile(const QString &filePath)
|
||||||
|
{
|
||||||
QFile *logFile = new QFile(filePath);
|
QFile *logFile = new QFile(filePath);
|
||||||
if (!logFile->open(QIODevice::ReadOnly | QIODevice::Text)) {
|
if (!logFile->open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
delete logFile;
|
delete logFile;
|
||||||
QString msg = tr("Memcheck: Failed to open file for reading: %1").arg(filePath);
|
QString msg = tr("Memcheck: Failed to open file for reading: %1").arg(filePath);
|
||||||
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
|
TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID);
|
||||||
TaskHub::requestPopup();
|
TaskHub::requestPopup();
|
||||||
|
if (!m_exitMsg.isEmpty())
|
||||||
|
Debugger::showPermanentStatusMessage(m_exitMsg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -854,8 +1088,10 @@ void MemcheckTool::engineFinished()
|
|||||||
void MemcheckTool::loadingExternalXmlLogFileFinished()
|
void MemcheckTool::loadingExternalXmlLogFileFinished()
|
||||||
{
|
{
|
||||||
const int issuesFound = updateUiAfterFinishedHelper();
|
const int issuesFound = updateUiAfterFinishedHelper();
|
||||||
Debugger::showPermanentStatusMessage(
|
QString statusMessage = tr("Log file processed, %n issues were found.", 0, issuesFound);
|
||||||
tr("Log file processed, %n issues were found.", 0, issuesFound));
|
if (!m_exitMsg.isEmpty())
|
||||||
|
statusMessage += ' ' + m_exitMsg;
|
||||||
|
Debugger::showPermanentStatusMessage(statusMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemcheckTool::setBusyCursor(bool busy)
|
void MemcheckTool::setBusyCursor(bool busy)
|
||||||
@@ -882,6 +1118,336 @@ void destroyMemcheckTool()
|
|||||||
theMemcheckTool = nullptr;
|
theMemcheckTool = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
static QString upperHexNum(unsigned num)
|
||||||
|
{
|
||||||
|
return QString("%1").arg(num, 8, 16, QChar('0')).toUpper();
|
||||||
|
}
|
||||||
|
|
||||||
|
HeobDialog::HeobDialog(QWidget *parent) :
|
||||||
|
QDialog(parent)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
m_xmlEdit->setText("leaks.xml");
|
||||||
|
xmlLayout->addWidget(m_xmlEdit);
|
||||||
|
layout->addLayout(xmlLayout);
|
||||||
|
|
||||||
|
m_pidWaitCheck = new QCheckBox(tr("show process ID and wait"));
|
||||||
|
layout->addWidget(m_pidWaitCheck);
|
||||||
|
|
||||||
|
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"));
|
||||||
|
m_handleExceptionCombo->setCurrentIndex(1);
|
||||||
|
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"));
|
||||||
|
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"));
|
||||||
|
layout->addWidget(m_freedProtectionCheck);
|
||||||
|
|
||||||
|
m_breakpointCheck = new QCheckBox(tr("raise breakpoint exception on error"));
|
||||||
|
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)"));
|
||||||
|
m_leakDetailCombo->setCurrentIndex(1);
|
||||||
|
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);
|
||||||
|
m_leakSizeSpin->setValue(0);
|
||||||
|
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)"));
|
||||||
|
m_leakRecordingCombo->setCurrentIndex(2);
|
||||||
|
leakRecordingLayout->addWidget(m_leakRecordingCombo);
|
||||||
|
layout->addLayout(leakRecordingLayout);
|
||||||
|
|
||||||
|
QHBoxLayout *extraArgsLayout = new QHBoxLayout;
|
||||||
|
QLabel *extraArgsLabel = new QLabel(tr("extra arguments:"));
|
||||||
|
extraArgsLayout->addWidget(extraArgsLabel);
|
||||||
|
m_extraArgsEdit = new QLineEdit;
|
||||||
|
extraArgsLayout->addWidget(m_extraArgsEdit);
|
||||||
|
layout->addLayout(extraArgsLayout);
|
||||||
|
|
||||||
|
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 pidWait = m_pidWaitCheck->isChecked() ? 1 : 0;
|
||||||
|
args += QString(" -P%1").arg(pidWait);
|
||||||
|
|
||||||
|
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(' ', '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
HeobData::HeobData(MemcheckTool *mcTool, const QString &xmlPath)
|
||||||
|
: m_mcTool(mcTool), m_xmlPath(xmlPath), m_ov(), m_data()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
DWORD access = PIPE_ACCESS_INBOUND;
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Valgrind
|
} // namespace Valgrind
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user