forked from qt-creator/qt-creator
Valgrind: Move some memcheck auto tests into plugin
Due to changed dependencies it is nowadays necessary to have more of the plugin infrastructure at hand. Moving memcheck related tests to be able to execute them as QC plugin unit test. Change-Id: Iab492b3cb87728425b950ca9387edf292d895350 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
370
src/plugins/valgrind/unit_testdata/memcheck-output-sample1.xml
Normal file
370
src/plugins/valgrind/unit_testdata/memcheck-output-sample1.xml
Normal file
@@ -0,0 +1,370 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<valgrindoutput>
|
||||
|
||||
<protocolversion>4</protocolversion>
|
||||
<protocoltool>memcheck</protocoltool>
|
||||
|
||||
<preamble>
|
||||
<line>Memcheck, a memory error detector</line>
|
||||
<line>Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.</line>
|
||||
<line>Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info</line>
|
||||
<line>Command: kate</line>
|
||||
</preamble>
|
||||
|
||||
<pid>22733</pid>
|
||||
<ppid>17584</ppid>
|
||||
<tool>memcheck</tool>
|
||||
|
||||
<args>
|
||||
<vargv>
|
||||
<exe>/usr/bin/valgrind.bin</exe>
|
||||
<arg>--suppressions=/usr/lib/valgrind/debian-libc6-dbg.supp</arg>
|
||||
<arg>--xml=yes</arg>
|
||||
<arg>--xml-file=test.xml</arg>
|
||||
<arg>--track-origins=yes</arg>
|
||||
</vargv>
|
||||
<argv>
|
||||
<exe>kate</exe>
|
||||
</argv>
|
||||
</args>
|
||||
|
||||
<status>
|
||||
<state>RUNNING</state>
|
||||
<time>00:00:00:00.241 </time>
|
||||
</status>
|
||||
|
||||
<error>
|
||||
<unique>0x9</unique>
|
||||
<tid>1</tid>
|
||||
<kind>InvalidRead</kind>
|
||||
<what>Invalid read of size 4</what>
|
||||
<stack>
|
||||
<frame>
|
||||
<ip>0x6E47964</ip>
|
||||
<obj>/usr/lib/libQtGui.so.4.7.0</obj>
|
||||
<fn>QFrame::frameStyle() const</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/gui/widgets</dir>
|
||||
<file>qframe.cpp</file>
|
||||
<line>252</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x118F2AF7</ip>
|
||||
<obj>/usr/lib/kde4/plugins/styles/oxygen.so</obj>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x6A81671</ip>
|
||||
<obj>/usr/lib/libQtGui.so.4.7.0</obj>
|
||||
<fn>QWidget::event(QEvent*)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/gui/kernel</dir>
|
||||
<file>qwidget.cpp</file>
|
||||
<line>8273</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x6A2B6EB</ip>
|
||||
<obj>/usr/lib/libQtGui.so.4.7.0</obj>
|
||||
<fn>QApplicationPrivate::notify_helper(QObject*, QEvent*)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/gui/kernel</dir>
|
||||
<file>qapplication.cpp</file>
|
||||
<line>4396</line>
|
||||
</frame>
|
||||
<!--
|
||||
<frame>
|
||||
<ip>0x6A311DC</ip>
|
||||
<obj>/usr/lib/libQtGui.so.4.7.0</obj>
|
||||
<fn>QApplication::notify(QObject*, QEvent*)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/gui/kernel</dir>
|
||||
<file>qapplication.cpp</file>
|
||||
<line>4277</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x6443535</ip>
|
||||
<obj>/usr/lib/libkdeui.so.5.5.0</obj>
|
||||
<fn>KApplication::notify(QObject*, QEvent*)</fn>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x83690AB</ip>
|
||||
<obj>/usr/lib/libQtCore.so.4.7.0</obj>
|
||||
<fn>QCoreApplication::notifyInternal(QObject*, QEvent*)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/corelib/kernel</dir>
|
||||
<file>qcoreapplication.cpp</file>
|
||||
<line>732</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x6A77600</ip>
|
||||
<obj>/usr/lib/libQtGui.so.4.7.0</obj>
|
||||
<fn>QWidget::ensurePolished() const</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/gui/../../include/QtCore/../../src/corelib/kernel</dir>
|
||||
<file>qcoreapplication.h</file>
|
||||
<line>215</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x6A869B2</ip>
|
||||
<obj>/usr/lib/libQtGui.so.4.7.0</obj>
|
||||
<fn>QWidget::setVisible(bool)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/gui/kernel</dir>
|
||||
<file>qwidget.cpp</file>
|
||||
<line>7539</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x18B1ED35</ip>
|
||||
<obj>/home/milian/projects/compiled/kde4/lib/libkatepartinterfaces.so.4.5.0</obj>
|
||||
<fn>QWidget::show()</fn>
|
||||
<dir>/usr/include/qt4/QtGui</dir>
|
||||
<file>qwidget.h</file>
|
||||
<line>487</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x18C23615</ip>
|
||||
<obj>/home/milian/projects/compiled/kde4/lib/libkatepartinterfaces.so.4.5.0</obj>
|
||||
<fn>KateViewInternal::KateViewInternal(KateView*)</fn>
|
||||
<dir>/home/milian/projects/kde4/kate/part/view</dir>
|
||||
<file>kateviewinternal.cpp</file>
|
||||
<line>144</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x18C0DA68</ip>
|
||||
<obj>/home/milian/projects/compiled/kde4/lib/libkatepartinterfaces.so.4.5.0</obj>
|
||||
<fn>KateView::KateView(KateDocument*, QWidget*)</fn>
|
||||
<dir>/home/milian/projects/kde4/kate/part/view</dir>
|
||||
<file>kateview.cpp</file>
|
||||
<line>136</line>
|
||||
</frame>
|
||||
-->
|
||||
</stack>
|
||||
<auxwhat>Address 0x11527cb8 is not stack'd, malloc'd or (recently) free'd</auxwhat>
|
||||
</error>
|
||||
|
||||
|
||||
<status>
|
||||
<state>FINISHED</state>
|
||||
<time>00:00:01:49.732 </time>
|
||||
</status>
|
||||
|
||||
<error>
|
||||
<unique>0x13</unique>
|
||||
<tid>1</tid>
|
||||
<kind>Leak_PossiblyLost</kind>
|
||||
<xwhat>
|
||||
<text>2 bytes in 1 blocks are possibly lost in loss record 2 of 2,003</text>
|
||||
<leakedbytes>2</leakedbytes>
|
||||
<leakedblocks>1</leakedblocks>
|
||||
</xwhat>
|
||||
<stack>
|
||||
<frame>
|
||||
<ip>0x4C284A8</ip>
|
||||
<obj>/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so</obj>
|
||||
<fn>malloc</fn>
|
||||
<dir>/build/buildd/valgrind-3.6.0~svn20100212/coregrind/m_replacemalloc</dir>
|
||||
<file>vg_replace_malloc.c</file>
|
||||
<line>236</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0xD4D7754</ip>
|
||||
<obj>/lib/libglib-2.0.so.0.2400.1</obj>
|
||||
<fn>g_malloc</fn>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0xD4EF11D</ip>
|
||||
<obj>/lib/libglib-2.0.so.0.2400.1</obj>
|
||||
<fn>g_strdup</fn>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0xD503DC4</ip>
|
||||
<obj>/lib/libglib-2.0.so.0.2400.1</obj>
|
||||
<fn>g_get_language_names</fn>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0xD4F89A9</ip>
|
||||
<obj>/lib/libglib-2.0.so.0.2400.1</obj>
|
||||
<fn>g_thread_init_glib</fn>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x8396569</ip>
|
||||
<obj>/usr/lib/libQtCore.so.4.7.0</obj>
|
||||
<fn>QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(_GMainContext*)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/corelib/kernel</dir>
|
||||
<file>qeventdispatcher_glib.cpp</file>
|
||||
<line>299</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x6ADDBEE</ip>
|
||||
<obj>/usr/lib/libQtGui.so.4.7.0</obj>
|
||||
<fn>QGuiEventDispatcherGlibPrivate::QGuiEventDispatcherGlibPrivate()</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/gui/kernel</dir>
|
||||
<file>qguieventdispatcher_glib.cpp</file>
|
||||
<line>171</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x6ADDCDD</ip>
|
||||
<obj>/usr/lib/libQtGui.so.4.7.0</obj>
|
||||
<fn>QGuiEventDispatcherGlib::QGuiEventDispatcherGlib(QObject*)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/gui/kernel</dir>
|
||||
<file>qguieventdispatcher_glib.cpp</file>
|
||||
<line>186</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x6AA5152</ip>
|
||||
<obj>/usr/lib/libQtGui.so.4.7.0</obj>
|
||||
<fn>QApplicationPrivate::createEventDispatcher()</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/gui/kernel</dir>
|
||||
<file>qapplication_x11.cpp</file>
|
||||
<line>605</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x836D069</ip>
|
||||
<obj>/usr/lib/libQtCore.so.4.7.0</obj>
|
||||
<fn>QCoreApplication::init()</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/corelib/kernel</dir>
|
||||
<file>qcoreapplication.cpp</file>
|
||||
<line>552</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x836D134</ip>
|
||||
<obj>/usr/lib/libQtCore.so.4.7.0</obj>
|
||||
<fn>QCoreApplication::QCoreApplication(QCoreApplicationPrivate&)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/corelib/kernel</dir>
|
||||
<file>qcoreapplication.cpp</file>
|
||||
<line>477</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x6A3815A</ip>
|
||||
<obj>/usr/lib/libQtGui.so.4.7.0</obj>
|
||||
<fn>QApplication::QApplication(int&, char**, bool, int)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/gui/kernel</dir>
|
||||
<file>qapplication.cpp</file>
|
||||
<line>745</line>
|
||||
</frame>
|
||||
</stack>
|
||||
</error>
|
||||
|
||||
<error>
|
||||
<unique>0x7e4</unique>
|
||||
<tid>1</tid>
|
||||
<kind>Leak_DefinitelyLost</kind>
|
||||
<xwhat>
|
||||
<text>544,542 (56 direct, 544,486 indirect) bytes in 1 blocks are definitely lost in loss record 2,003 of 2,003</text>
|
||||
<leakedbytes>544542</leakedbytes>
|
||||
<leakedblocks>1</leakedblocks>
|
||||
</xwhat>
|
||||
<stack>
|
||||
<frame>
|
||||
<ip>0x4C284A8</ip>
|
||||
<obj>/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so</obj>
|
||||
<fn>malloc</fn>
|
||||
<dir>/build/buildd/valgrind-3.6.0~svn20100212/coregrind/m_replacemalloc</dir>
|
||||
<file>vg_replace_malloc.c</file>
|
||||
<line>236</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x82A1A6C</ip>
|
||||
<obj>/usr/lib/libQtCore.so.4.7.0</obj>
|
||||
<fn>QMapData::node_create(QMapData::Node**, int, int)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/corelib/tools</dir>
|
||||
<file>qmap.cpp</file>
|
||||
<line>140</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x8336F68</ip>
|
||||
<obj>/usr/lib/libQtCore.so.4.7.0</obj>
|
||||
<fn>QMap<QSettingsKey, QVariant>::detach_helper()</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/corelib/../../include/QtCore/../../src/corelib/tools</dir>
|
||||
<file>qmap.h</file>
|
||||
<line>449</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x832C564</ip>
|
||||
<obj>/usr/lib/libQtCore.so.4.7.0</obj>
|
||||
<fn>QConfFile::mergedKeyMap() const</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/corelib/../../include/QtCore/../../src/corelib/tools</dir>
|
||||
<file>qmap.h</file>
|
||||
<line>202</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x833305A</ip>
|
||||
<obj>/usr/lib/libQtCore.so.4.7.0</obj>
|
||||
<fn>QConfFileSettingsPrivate::syncConfFile(int)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/corelib/io</dir>
|
||||
<file>qsettings.cpp</file>
|
||||
<line>1569</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x8333D5B</ip>
|
||||
<obj>/usr/lib/libQtCore.so.4.7.0</obj>
|
||||
<fn>QConfFileSettingsPrivate::sync()</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/corelib/io</dir>
|
||||
<file>qsettings.cpp</file>
|
||||
<line>1386</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x83260D9</ip>
|
||||
<obj>/usr/lib/libQtCore.so.4.7.0</obj>
|
||||
<fn>QSettingsPrivate::update()</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/corelib/io</dir>
|
||||
<file>qsettings.cpp</file>
|
||||
<line>415</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x83267C7</ip>
|
||||
<obj>/usr/lib/libQtCore.so.4.7.0</obj>
|
||||
<fn>QSettings::event(QEvent*)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/corelib/io</dir>
|
||||
<file>qsettings.cpp</file>
|
||||
<line>3326</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x6A2B6EB</ip>
|
||||
<obj>/usr/lib/libQtGui.so.4.7.0</obj>
|
||||
<fn>QApplicationPrivate::notify_helper(QObject*, QEvent*)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/gui/kernel</dir>
|
||||
<file>qapplication.cpp</file>
|
||||
<line>4396</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x6A311DC</ip>
|
||||
<obj>/usr/lib/libQtGui.so.4.7.0</obj>
|
||||
<fn>QApplication::notify(QObject*, QEvent*)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/gui/kernel</dir>
|
||||
<file>qapplication.cpp</file>
|
||||
<line>4277</line>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x6443535</ip>
|
||||
<obj>/usr/lib/libkdeui.so.5.5.0</obj>
|
||||
<fn>KApplication::notify(QObject*, QEvent*)</fn>
|
||||
</frame>
|
||||
<frame>
|
||||
<ip>0x83690AB</ip>
|
||||
<obj>/usr/lib/libQtCore.so.4.7.0</obj>
|
||||
<fn>QCoreApplication::notifyInternal(QObject*, QEvent*)</fn>
|
||||
<dir>/build/buildd/qt4-x11-4.7.0/src/corelib/kernel</dir>
|
||||
<file>qcoreapplication.cpp</file>
|
||||
<line>732</line>
|
||||
</frame>
|
||||
</stack>
|
||||
</error>
|
||||
|
||||
<errorcounts>
|
||||
<pair>
|
||||
<count>2</count>
|
||||
<unique>0x9</unique>
|
||||
</pair>
|
||||
</errorcounts>
|
||||
|
||||
<suppcounts>
|
||||
<pair>
|
||||
<count>12</count>
|
||||
<name>X on SUSE11 writev uninit padding</name>
|
||||
</pair>
|
||||
<pair>
|
||||
<count>2</count>
|
||||
<name>dl-hack3-cond-1</name>
|
||||
</pair>
|
||||
<pair>
|
||||
<count>2</count>
|
||||
<name>glibc-2.5.x-on-SUSE-10.2-(PPC)-2a</name>
|
||||
</pair>
|
||||
</suppcounts>
|
||||
|
||||
</valgrindoutput>
|
||||
|
||||
@@ -55,3 +55,16 @@ FORMS += \
|
||||
|
||||
RESOURCES += \
|
||||
valgrind.qrc
|
||||
|
||||
equals(TEST, 1) {
|
||||
DEFINES += "PARSERTESTS_DATA_DIR=\\\"$$_PRO_FILE_PWD_/unit_testdata\\\""
|
||||
DEFINES += "VALGRIND_FAKE_PATH=\\\"$$IDE_BUILD_TREE/src/tools/valgrindfake\\\""
|
||||
DEFINES += "TESTRUNNER_SRC_DIR=\\\"$$_PRO_FILE_PWD_/../../../tests/auto/valgrind/memcheck/testapps\\\""
|
||||
DEFINES += "TESTRUNNER_APP_DIR=\\\"$(PWD)/../../../tests/auto/valgrind/memcheck/testapps\\\""
|
||||
|
||||
HEADERS += valgrindmemcheckparsertest.h \
|
||||
valgrindtestrunnertest.h
|
||||
|
||||
SOURCES += valgrindmemcheckparsertest.cpp \
|
||||
valgrindtestrunnertest.cpp
|
||||
}
|
||||
|
||||
@@ -88,4 +88,15 @@ QtcPlugin {
|
||||
"threadedparser.cpp", "threadedparser.h",
|
||||
]
|
||||
}
|
||||
|
||||
Group {
|
||||
name: "Test sources"
|
||||
condition: qtc.testsEnabled
|
||||
files: [
|
||||
"valgrindmemcheckparsertest.cpp",
|
||||
"valgrindmemcheckparsertest.h",
|
||||
"valgrindtestrunnertest.cpp",
|
||||
"valgrindtestrunnertest.h",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
543
src/plugins/valgrind/valgrindmemcheckparsertest.cpp
Normal file
543
src/plugins/valgrind/valgrindmemcheckparsertest.cpp
Normal file
@@ -0,0 +1,543 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Author: Frank Osterfeld, KDAB (frank.osterfeld@kdab.com)
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** 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
|
||||
** 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.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "valgrindmemcheckparsertest.h"
|
||||
|
||||
#include "xmlprotocol/frame.h"
|
||||
#include "xmlprotocol/parser.h"
|
||||
#include "xmlprotocol/stack.h"
|
||||
#include "xmlprotocol/suppression.h"
|
||||
|
||||
#include <projectexplorer/devicesupport/devicemanager.h>
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/runnables.h>
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QTest>
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QSignalSpy>
|
||||
|
||||
using namespace Valgrind::XmlProtocol;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace QTest {
|
||||
|
||||
template<>
|
||||
inline bool qCompare(int const &t1, MemcheckErrorKind const &t2,
|
||||
char const *actual, char const *expected, char const *file, int line)
|
||||
{
|
||||
return qCompare(t1, int(t2), actual, expected, file, line);
|
||||
}
|
||||
|
||||
} // namespace QTest
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Valgrind {
|
||||
namespace Test {
|
||||
|
||||
static void dumpFrame(const Frame &f)
|
||||
{
|
||||
qDebug() << f.instructionPointer() << f.directory() << f.fileName() << f.functionName()
|
||||
<< f.line() << f.object();
|
||||
}
|
||||
|
||||
void dumpError(const Error &e)
|
||||
{
|
||||
qDebug() << e.kind() << e.leakedBlocks() << e.leakedBytes() << e.what() << e.tid() << e.unique();
|
||||
qDebug() << "stacks:" << e.stacks().size();
|
||||
for (const Stack &s : e.stacks()) {
|
||||
qDebug() << s.auxWhat() << s.directory() << s.file() << s.line() << s.helgrindThreadId();
|
||||
qDebug() << "frames:";
|
||||
for (const Frame &f : s.frames()) {
|
||||
dumpFrame(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static QString fakeValgrindExecutable()
|
||||
{
|
||||
QString valgrindFakePath(VALGRIND_FAKE_PATH);
|
||||
if (Utils::HostOsInfo::isWindowsHost()) {
|
||||
QFileInfo fi(QString(valgrindFakePath + "/debug"), "valgrind-fake.exe");
|
||||
if (fi.exists())
|
||||
return fi.canonicalFilePath();
|
||||
fi = QFileInfo(QString(valgrindFakePath + "/release"), "valgrind-fake.exe");
|
||||
if (fi.exists())
|
||||
return fi.canonicalFilePath();
|
||||
qFatal("Neither debug nor release build valgrind-fake found.");
|
||||
}
|
||||
return valgrindFakePath + "/valgrind-fake";
|
||||
}
|
||||
|
||||
static QString dataFile(const QLatin1String &file)
|
||||
{
|
||||
return QLatin1String(PARSERTESTS_DATA_DIR) + QLatin1String("/") + file;
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::initTestCase()
|
||||
{
|
||||
m_server = new QTcpServer(this);
|
||||
QVERIFY(m_server->listen());
|
||||
|
||||
m_socket = 0;
|
||||
m_process = 0;
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::initTest(const QLatin1String &testfile, const QStringList &otherArgs)
|
||||
{
|
||||
QVERIFY(!m_server->hasPendingConnections());
|
||||
|
||||
m_process = new QProcess(m_server);
|
||||
m_process->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
const QString fakeValgrind = fakeValgrindExecutable();
|
||||
QFileInfo fileInfo(fakeValgrind);
|
||||
QVERIFY2(fileInfo.isExecutable(), qPrintable(fakeValgrind));
|
||||
QVERIFY2(!fileInfo.isDir(), qPrintable(fakeValgrind));
|
||||
|
||||
m_process->start(
|
||||
fakeValgrind,
|
||||
QStringList()
|
||||
<< QString::fromLatin1("--xml-socket=127.0.0.1:%1").arg(m_server->serverPort())
|
||||
<< QLatin1String("-i")
|
||||
<< dataFile(testfile)
|
||||
<< otherArgs
|
||||
);
|
||||
|
||||
QVERIFY(m_process->waitForStarted(5000));
|
||||
QCOMPARE(m_process->state(), QProcess::Running);
|
||||
QVERIFY2(m_process->error() == QProcess::UnknownError, qPrintable(m_process->errorString()));
|
||||
QVERIFY(m_server->waitForNewConnection(5000));
|
||||
m_socket = m_server->nextPendingConnection();
|
||||
QVERIFY(m_socket);
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::cleanup()
|
||||
{
|
||||
if (m_socket) {
|
||||
delete m_socket;
|
||||
m_socket = 0;
|
||||
}
|
||||
if (m_process) {
|
||||
delete m_process;
|
||||
m_process = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::testHelgrindSample1()
|
||||
{
|
||||
QSKIP("testfile does not exist");
|
||||
|
||||
initTest(QLatin1String("helgrind-output-sample1.xml"));
|
||||
|
||||
QList<Error> expectedErrors;
|
||||
{
|
||||
Error error1;
|
||||
error1.setUnique(0x0);
|
||||
error1.setTid(1);
|
||||
error1.setKind(LockOrder);
|
||||
error1.setWhat(QLatin1String("Thread #1: lock order \"0xA39C270 before 0xA3AC010\" violated"));
|
||||
error1.setHelgrindThreadId(1);
|
||||
Stack stack1;
|
||||
Frame frame11;
|
||||
frame11.setInstructionPointer(0x4C2B806);
|
||||
frame11.setObject(QLatin1String("/usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so"));
|
||||
frame11.setFunctionName(QLatin1String("QMutex::lock()"));
|
||||
frame11.setDirectory(QLatin1String("/build/buildd/valgrind-3.6.0~svn20100212/helgrind"));
|
||||
frame11.setFileName(QLatin1String("hg_intercepts.c"));
|
||||
frame11.setLine(1988);
|
||||
Frame frame12;
|
||||
frame12.setInstructionPointer(0x72E57EE);
|
||||
frame12.setObject(QLatin1String("/home/frank/local/qt4-4.6.3-shared-debug/lib/libQtCore.so.4.6.3"));
|
||||
frame12.setFunctionName(QLatin1String("QMutexLocker::relock()"));
|
||||
frame12.setDirectory(QLatin1String("/home/frank/source/tarballs/qt-4.6.3-build/src/corelib/../../include/QtCore/../../src/corelib/thread"));
|
||||
frame12.setFileName(QLatin1String("qmutex.h"));
|
||||
frame12.setLine(120);
|
||||
stack1.setFrames(QVector<Frame>() << frame11 << frame12);
|
||||
|
||||
Stack stack2;
|
||||
stack2.setAuxWhat(QLatin1String("Required order was established by acquisition of lock at 0xA39C270"));
|
||||
Frame frame21;
|
||||
frame21.setInstructionPointer(0x4C2B806);
|
||||
frame21.setObject(QLatin1String("/usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so"));
|
||||
frame21.setFunctionName(QLatin1String("QMutex::lock()"));
|
||||
frame21.setDirectory(QLatin1String("/build/buildd/valgrind-3.6.0~svn20100212/helgrind"));
|
||||
frame21.setFileName(QLatin1String("hg_intercepts.c"));
|
||||
frame21.setLine(1989);
|
||||
Frame frame22;
|
||||
frame22.setInstructionPointer(0x72E57EE);
|
||||
frame22.setObject(QLatin1String("/home/frank/local/qt4-4.6.3-shared-debug/lib/libQtCore.so.4.6.3"));
|
||||
frame22.setFunctionName(QLatin1String("QMutexLocker::relock()"));
|
||||
frame22.setDirectory(QLatin1String("/home/frank/source/tarballs/qt-4.6.3-build/src/corelib/../../include/QtCore/../../src/corelib/thread"));
|
||||
frame22.setFileName(QLatin1String("qmutex.h"));
|
||||
frame22.setLine(121);
|
||||
stack2.setFrames(QVector<Frame>() << frame21 << frame22);
|
||||
|
||||
Stack stack3;
|
||||
stack3.setAuxWhat(QLatin1String("followed by a later acquisition of lock at 0xA3AC010"));
|
||||
Frame frame31;
|
||||
frame31.setInstructionPointer(0x4C2B806);
|
||||
frame31.setObject(QLatin1String("/usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so"));
|
||||
frame31.setFunctionName(QLatin1String("QMutex::lock()"));
|
||||
frame31.setDirectory(QLatin1String("/build/buildd/valgrind-3.6.0~svn20100212/helgrind"));
|
||||
frame31.setFileName(QLatin1String("hg_intercepts.c"));
|
||||
frame31.setLine(1990);
|
||||
Frame frame32;
|
||||
frame32.setInstructionPointer(0x72E57EE);
|
||||
frame32.setObject(QLatin1String("/home/frank/local/qt4-4.6.3-shared-debug/lib/libQtCore.so.4.6.3"));
|
||||
frame32.setFunctionName(QLatin1String("QMutexLocker::relock()"));
|
||||
frame32.setDirectory(QLatin1String("/home/frank/source/tarballs/qt-4.6.3-build/src/corelib/../../include/QtCore/../../src/corelib/thread"));
|
||||
frame32.setFileName(QLatin1String("qmutex.h"));
|
||||
frame32.setLine(122);
|
||||
|
||||
stack3.setFrames(QVector<Frame>() << frame31 << frame32);
|
||||
error1.setStacks(QVector<Stack>() << stack1 << stack2 << stack3);
|
||||
expectedErrors.append(error1);
|
||||
}
|
||||
|
||||
Parser parser;
|
||||
Recorder rec(&parser);
|
||||
|
||||
parser.parse(m_socket);
|
||||
|
||||
m_process->waitForFinished();
|
||||
QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
|
||||
QCOMPARE(m_process->state(), QProcess::NotRunning);
|
||||
|
||||
QVERIFY2(parser.errorString().isEmpty(), qPrintable(parser.errorString()));
|
||||
const QList<Error> actualErrors = rec.errors;
|
||||
|
||||
if (actualErrors.first() != expectedErrors.first()) {
|
||||
dumpError(actualErrors.first());
|
||||
dumpError(expectedErrors.first());
|
||||
}
|
||||
|
||||
QCOMPARE(actualErrors.first(), expectedErrors.first());
|
||||
|
||||
QCOMPARE(actualErrors.size(), 1);
|
||||
|
||||
// QCOMPARE(rec.errorcounts, expectedErrorCounts);
|
||||
// QCOMPARE(rec.suppcounts, expectedSuppCounts);
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::testMemcheckSample1()
|
||||
{
|
||||
initTest(QLatin1String("memcheck-output-sample1.xml"));
|
||||
|
||||
QList<Error> expectedErrors;
|
||||
{
|
||||
Error error;
|
||||
error.setKind(InvalidRead);
|
||||
error.setWhat(QLatin1String("Invalid read of size 4"));
|
||||
error.setUnique(0x9);
|
||||
error.setTid(1);
|
||||
Frame f1;
|
||||
f1.setInstructionPointer(0x6E47964);
|
||||
f1.setObject(QLatin1String("/usr/lib/libQtGui.so.4.7.0"));
|
||||
f1.setFunctionName(QLatin1String("QFrame::frameStyle() const"));
|
||||
f1.setDirectory(QLatin1String("/build/buildd/qt4-x11-4.7.0/src/gui/widgets"));
|
||||
f1.setFileName(QLatin1String("qframe.cpp"));
|
||||
f1.setLine(252);
|
||||
Frame f2;
|
||||
f2.setInstructionPointer(0x118F2AF7);
|
||||
f2.setObject(QLatin1String("/usr/lib/kde4/plugins/styles/oxygen.so"));
|
||||
Frame f3;
|
||||
f3.setInstructionPointer(0x6A81671);
|
||||
f3.setObject(QLatin1String("/usr/lib/libQtGui.so.4.7.0"));
|
||||
f3.setFunctionName(QLatin1String("QWidget::event(QEvent*)"));
|
||||
f3.setDirectory(QLatin1String("/build/buildd/qt4-x11-4.7.0/src/gui/kernel"));
|
||||
f3.setFileName(QLatin1String("qwidget.cpp"));
|
||||
f3.setLine(8273);
|
||||
Frame f4;
|
||||
f4.setInstructionPointer(0x6A2B6EB);
|
||||
f4.setObject(QLatin1String("/usr/lib/libQtGui.so.4.7.0"));
|
||||
f4.setDirectory(QLatin1String("/build/buildd/qt4-x11-4.7.0/src/gui/kernel"));
|
||||
f4.setFileName(QLatin1String("qapplication.cpp"));
|
||||
f4.setFunctionName(QLatin1String("QApplicationPrivate::notify_helper(QObject*, QEvent*)"));
|
||||
f4.setLine(4396);
|
||||
Stack s1;
|
||||
s1.setAuxWhat(QLatin1String("Address 0x11527cb8 is not stack'd, malloc'd or (recently) free'd"));
|
||||
s1.setFrames(QVector<Frame>() << f1 << f2 << f3 << f4);
|
||||
error.setStacks( QVector<Stack>() << s1 );
|
||||
|
||||
expectedErrors << error;
|
||||
}
|
||||
|
||||
QVector<QPair<qint64,qint64> > expectedErrorCounts;
|
||||
expectedErrorCounts.push_back(QPair<qint64,qint64>(9, 2));
|
||||
|
||||
QVector<QPair<QString,qint64> > expectedSuppCounts;
|
||||
expectedSuppCounts.push_back(qMakePair(QString::fromLatin1("X on SUSE11 writev uninit padding"), static_cast<qint64>(12)));
|
||||
expectedSuppCounts.push_back(qMakePair(QString::fromLatin1("dl-hack3-cond-1"), static_cast<qint64>(2)));
|
||||
expectedSuppCounts.push_back(qMakePair(QString::fromLatin1("glibc-2.5.x-on-SUSE-10.2-(PPC)-2a"), static_cast<qint64>(2)));
|
||||
|
||||
Parser parser;
|
||||
Recorder rec(&parser);
|
||||
|
||||
parser.parse(m_socket);
|
||||
|
||||
m_process->waitForFinished();
|
||||
QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
|
||||
QCOMPARE(m_process->state(), QProcess::NotRunning);
|
||||
|
||||
QVERIFY2(parser.errorString().isEmpty(), qPrintable(parser.errorString()));
|
||||
const QList<Error> actualErrors = rec.errors;
|
||||
|
||||
if (actualErrors.first() != expectedErrors.first()) {
|
||||
dumpError(actualErrors.first());
|
||||
dumpError(expectedErrors.first());
|
||||
}
|
||||
|
||||
QCOMPARE(actualErrors.first(), expectedErrors.first());
|
||||
|
||||
QCOMPARE(actualErrors.size(), 3);
|
||||
|
||||
QCOMPARE(rec.errorcounts, expectedErrorCounts);
|
||||
QCOMPARE(rec.suppcounts, expectedSuppCounts);
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::testMemcheckSample2()
|
||||
{
|
||||
QSKIP("testfile does not exist");
|
||||
|
||||
initTest(QLatin1String("memcheck-output-sample2.xml"));
|
||||
|
||||
Parser parser;
|
||||
Recorder rec(&parser);
|
||||
|
||||
parser.parse(m_socket);
|
||||
|
||||
m_process->waitForFinished();
|
||||
QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
|
||||
QCOMPARE(m_process->state(), QProcess::NotRunning);
|
||||
QVERIFY2(parser.errorString().isEmpty(), qPrintable(parser.errorString()));
|
||||
|
||||
//tests: multiple stacks with auxwhat == stack count - 1.
|
||||
//the first auxwhat should be assigned to the _second_ stack.
|
||||
const QList<Error> errors = rec.errors;
|
||||
QCOMPARE(errors.size(), 1);
|
||||
const QVector<Stack> stacks = errors.first().stacks();
|
||||
QCOMPARE(stacks.size(), 2);
|
||||
QCOMPARE(stacks.first().auxWhat(), QString());
|
||||
QCOMPARE(stacks.last().auxWhat(), QLatin1String("Address 0x11b66c50 is 0 bytes inside a block of size 16 free'd"));
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::testMemcheckSample3()
|
||||
{
|
||||
QSKIP("testfile does not exist");
|
||||
|
||||
initTest(QLatin1String("memcheck-output-sample3.xml"));
|
||||
|
||||
Parser parser;
|
||||
Recorder rec(&parser);
|
||||
|
||||
parser.parse(m_socket);
|
||||
|
||||
m_process->waitForFinished();
|
||||
QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
|
||||
QCOMPARE(m_process->state(), QProcess::NotRunning);
|
||||
QVERIFY2(parser.errorString().isEmpty(), qPrintable(parser.errorString()));
|
||||
|
||||
const QList<Error> errors = rec.errors;
|
||||
QCOMPARE(errors.size(), 6);
|
||||
|
||||
{
|
||||
const Error error = errors.at(0);
|
||||
const QVector<Stack> stacks = error.stacks();
|
||||
|
||||
QCOMPARE(error.unique(), 0x1ll);
|
||||
QCOMPARE(error.what(), QLatin1String("Conditional jump or move depends on uninitialised value(s)"));
|
||||
QCOMPARE(error.kind(), UninitCondition);
|
||||
QCOMPARE(stacks.size(), 1);
|
||||
QCOMPARE(stacks.first().frames().size(), 12);
|
||||
QVERIFY(!error.suppression().isNull());
|
||||
QCOMPARE(error.suppression().frames().count(), stacks.first().frames().size());
|
||||
QCOMPARE(error.suppression().kind(), QLatin1String("Memcheck:Cond"));
|
||||
QVERIFY(!error.suppression().rawText().trimmed().isEmpty());
|
||||
|
||||
// rawtext contains <...> while <name></name> does not
|
||||
QCOMPARE(error.suppression().name(), QLatin1String("insert_a_suppression_name_here"));
|
||||
Suppression sup = error.suppression();
|
||||
sup.setName(QLatin1String("<insert_a_suppression_name_here>"));
|
||||
QCOMPARE(sup.toString().trimmed(), sup.rawText().trimmed());
|
||||
|
||||
QCOMPARE(error.suppression().frames().first().object(),
|
||||
QLatin1String("/usr/lib/kde4/plugins/styles/qtcurve.so"));
|
||||
QVERIFY(error.suppression().frames().first().function().isEmpty());
|
||||
QCOMPARE(error.suppression().frames().last().function(), QLatin1String("main"));
|
||||
QVERIFY(error.suppression().frames().last().object().isEmpty());
|
||||
}
|
||||
|
||||
QCOMPARE(rec.suppcounts.count(), 3);
|
||||
QCOMPARE(rec.suppcounts.at(0).second, qint64(1));
|
||||
QCOMPARE(rec.suppcounts.at(1).second, qint64(2));
|
||||
QCOMPARE(rec.suppcounts.at(2).second, qint64(3));
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::testMemcheckCharm()
|
||||
{
|
||||
QSKIP("testfile does not exist");
|
||||
|
||||
// a somewhat larger file, to make sure buffering and partial I/O works ok
|
||||
initTest(QLatin1String("memcheck-output-untitled.xml"));
|
||||
|
||||
Parser parser;
|
||||
Recorder rec(&parser);
|
||||
|
||||
parser.parse(m_socket);
|
||||
|
||||
m_process->waitForFinished();
|
||||
QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
|
||||
QCOMPARE(m_process->state(), QProcess::NotRunning);
|
||||
|
||||
const QList<Error> errors = rec.errors;
|
||||
QCOMPARE(errors.size(), 102);
|
||||
QVERIFY2(parser.errorString().isEmpty(), qPrintable(parser.errorString()));
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::testValgrindCrash()
|
||||
{
|
||||
initTest(QLatin1String("memcheck-output-sample1.xml"), QStringList() << "--crash");
|
||||
|
||||
Parser parser;
|
||||
parser.parse(m_socket);
|
||||
m_process->waitForFinished();
|
||||
QCOMPARE(m_process->state(), QProcess::NotRunning);
|
||||
QCOMPARE(m_process->exitStatus(), QProcess::CrashExit);
|
||||
|
||||
QVERIFY(!parser.errorString().isEmpty());
|
||||
QCOMPARE(m_socket->error(), QAbstractSocket::RemoteHostClosedError);
|
||||
QCOMPARE(parser.errorString(), m_socket->errorString());
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::testValgrindGarbage()
|
||||
{
|
||||
initTest(QLatin1String("memcheck-output-sample1.xml"), QStringList() << "--garbage");
|
||||
|
||||
Parser parser;
|
||||
parser.parse(m_socket);
|
||||
m_process->waitForFinished();
|
||||
QCOMPARE(m_process->state(), QProcess::NotRunning);
|
||||
QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
|
||||
|
||||
QVERIFY(!parser.errorString().isEmpty());
|
||||
qDebug() << parser.errorString();
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::testParserStop()
|
||||
{
|
||||
ThreadedParser parser;
|
||||
Memcheck::MemcheckRunner runner;
|
||||
runner.setValgrindExecutable(fakeValgrindExecutable());
|
||||
runner.setParser(&parser);
|
||||
runner.setValgrindArguments(QStringList() << QLatin1String("-i")
|
||||
<< dataFile(QLatin1String("memcheck-output-sample1.xml"))
|
||||
<< "--wait" << "5");
|
||||
runner.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
|
||||
runner.setDevice(ProjectExplorer::DeviceManager::instance()->defaultDevice(
|
||||
Core::Id(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)));
|
||||
runner.start();
|
||||
QTest::qWait(500);
|
||||
runner.stop();
|
||||
}
|
||||
|
||||
|
||||
void ValgrindMemcheckParserTest::testRealValgrind()
|
||||
{
|
||||
const Utils::Environment &sysEnv = Utils::Environment::systemEnvironment();
|
||||
auto fileName = sysEnv.searchInPath("valgrind");
|
||||
if (fileName.isEmpty())
|
||||
QSKIP("This test needs valgrind in PATH");
|
||||
QString executable = QProcessEnvironment::systemEnvironment().value("VALGRIND_TEST_BIN", fakeValgrindExecutable());
|
||||
qDebug() << "running exe:" << executable << " HINT: set VALGRIND_TEST_BIN to change this";
|
||||
ThreadedParser parser;
|
||||
|
||||
ProjectExplorer::StandardRunnable debuggee;
|
||||
debuggee.executable = executable;
|
||||
debuggee.environment = sysEnv;
|
||||
Memcheck::MemcheckRunner runner;
|
||||
runner.setValgrindExecutable(QLatin1String("valgrind"));
|
||||
runner.setDebuggee(debuggee);
|
||||
runner.setDevice(ProjectExplorer::DeviceManager::instance()->defaultDevice(
|
||||
Core::Id(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)));
|
||||
runner.setParser(&parser);
|
||||
RunnerDumper dumper(&runner, &parser);
|
||||
runner.start();
|
||||
runner.waitForFinished();
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::testValgrindStartError_data()
|
||||
{
|
||||
QTest::addColumn<QString>("valgrindExe");
|
||||
QTest::addColumn<QStringList>("valgrindArgs");
|
||||
QTest::addColumn<QString>("debuggee");
|
||||
QTest::addColumn<QString>("debuggeeArgs");
|
||||
|
||||
QTest::newRow("invalid_client") << QString::fromLatin1("valgrind") << QStringList()
|
||||
<< QString::fromLatin1("please-dont-let-this-app-exist") << QString();
|
||||
|
||||
QTest::newRow("invalid_valgrind") << QString::fromLatin1("valgrind-that-does-not-exist") << QStringList()
|
||||
<< fakeValgrindExecutable() << QString();
|
||||
|
||||
QTest::newRow("invalid_valgrind_args") << QString::fromLatin1("valgrind")
|
||||
<< (QStringList() << QString::fromLatin1("--foobar-fail"))
|
||||
<< fakeValgrindExecutable() << QString();
|
||||
}
|
||||
|
||||
void ValgrindMemcheckParserTest::testValgrindStartError()
|
||||
{
|
||||
QFETCH(QString, valgrindExe);
|
||||
QFETCH(QStringList, valgrindArgs);
|
||||
QFETCH(QString, debuggee);
|
||||
QFETCH(QString, debuggeeArgs);
|
||||
|
||||
ThreadedParser parser;
|
||||
|
||||
ProjectExplorer::StandardRunnable debuggeeExecutable;
|
||||
debuggeeExecutable.executable = debuggee;
|
||||
debuggeeExecutable.environment = Utils::Environment::systemEnvironment();
|
||||
debuggeeExecutable.commandLineArguments = debuggeeArgs;
|
||||
|
||||
Memcheck::MemcheckRunner runner;
|
||||
runner.setParser(&parser);
|
||||
runner.setValgrindExecutable(valgrindExe);
|
||||
runner.setValgrindArguments(valgrindArgs);
|
||||
runner.setDebuggee(debuggeeExecutable);
|
||||
runner.setDevice(ProjectExplorer::DeviceManager::instance()->defaultDevice(
|
||||
Core::Id(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)));
|
||||
RunnerDumper dumper(&runner, &parser);
|
||||
runner.start();
|
||||
runner.waitForFinished();
|
||||
QVERIFY(dumper.m_errorReceived);
|
||||
// just finish without deadlock and we are fine
|
||||
}
|
||||
|
||||
} // namespace Test
|
||||
} // namespace Valgrind
|
||||
172
src/plugins/valgrind/valgrindmemcheckparsertest.h
Normal file
172
src/plugins/valgrind/valgrindmemcheckparsertest.h
Normal file
@@ -0,0 +1,172 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Author: Frank Osterfeld, KDAB (frank.osterfeld@kdab.com)
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** 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
|
||||
** 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.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
#include <QDebug>
|
||||
|
||||
#include "xmlprotocol/error.h"
|
||||
#include "xmlprotocol/status.h"
|
||||
#include "xmlprotocol/threadedparser.h"
|
||||
#include "xmlprotocol/parser.h"
|
||||
#include "memcheck/memcheckrunner.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTcpServer;
|
||||
class QTcpSocket;
|
||||
class QProcess;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
namespace Valgrind {
|
||||
namespace Test {
|
||||
|
||||
void dumpError(const Valgrind::XmlProtocol::Error &e);
|
||||
|
||||
class Recorder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Recorder(Valgrind::XmlProtocol::Parser *parser, QObject *parent = 0)
|
||||
: QObject(parent)
|
||||
{
|
||||
connect(parser, &Valgrind::XmlProtocol::Parser::error,
|
||||
this, &Recorder::error);
|
||||
connect(parser, &Valgrind::XmlProtocol::Parser::errorCount,
|
||||
this, &Recorder::errorCount);
|
||||
connect(parser, &Valgrind::XmlProtocol::Parser::suppressionCount,
|
||||
this, &Recorder::suppressionCount);
|
||||
}
|
||||
|
||||
QList<Valgrind::XmlProtocol::Error> errors;
|
||||
QVector<QPair<qint64,qint64> > errorcounts;
|
||||
QVector<QPair<QString,qint64> > suppcounts;
|
||||
|
||||
public Q_SLOTS:
|
||||
void error(const Valgrind::XmlProtocol::Error &err)
|
||||
{
|
||||
errors.append(err);
|
||||
}
|
||||
|
||||
void errorCount(qint64 unique, qint64 count)
|
||||
{
|
||||
errorcounts.push_back(qMakePair(unique, count));
|
||||
}
|
||||
|
||||
void suppressionCount(const QString &name, qint64 count)
|
||||
{
|
||||
suppcounts.push_back(qMakePair(name, count));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class RunnerDumper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RunnerDumper(Valgrind::Memcheck::MemcheckRunner *runner, Valgrind::XmlProtocol::ThreadedParser *parser)
|
||||
: QObject()
|
||||
, m_errorReceived(false)
|
||||
{
|
||||
connect(parser, &Valgrind::XmlProtocol::ThreadedParser::error,
|
||||
this, &RunnerDumper::error);
|
||||
connect(parser, &Valgrind::XmlProtocol::ThreadedParser::internalError,
|
||||
this, &RunnerDumper::internalError);
|
||||
connect(parser, &Valgrind::XmlProtocol::ThreadedParser::status,
|
||||
this, &RunnerDumper::status);
|
||||
connect(runner, &Valgrind::Memcheck::MemcheckRunner::logMessageReceived,
|
||||
this, &RunnerDumper::logMessageReceived);
|
||||
connect(runner, &Valgrind::ValgrindRunner::processErrorReceived,
|
||||
this, &RunnerDumper::processErrorReceived);
|
||||
}
|
||||
|
||||
public slots:
|
||||
void error(const Valgrind::XmlProtocol::Error &e)
|
||||
{
|
||||
qDebug() << "error received";
|
||||
dumpError(e);
|
||||
}
|
||||
void internalError(const QString& error)
|
||||
{
|
||||
qDebug() << "internal error received:" << error;
|
||||
}
|
||||
void status(const Valgrind::XmlProtocol::Status &status)
|
||||
{
|
||||
qDebug() << "status received:" << status.state() << status.time();
|
||||
}
|
||||
void logMessageReceived(const QByteArray &log)
|
||||
{
|
||||
qDebug() << "log message received:" << log;
|
||||
}
|
||||
void processErrorReceived(const QString &s)
|
||||
{
|
||||
Q_UNUSED(s);
|
||||
// qDebug() << "error received:" << s; // this can be a lot of text
|
||||
m_errorReceived = true;
|
||||
}
|
||||
|
||||
public:
|
||||
bool m_errorReceived;
|
||||
|
||||
};
|
||||
|
||||
class ValgrindMemcheckParserTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanup();
|
||||
|
||||
void testMemcheckSample1();
|
||||
void testMemcheckSample2();
|
||||
void testMemcheckSample3();
|
||||
void testMemcheckCharm();
|
||||
void testHelgrindSample1();
|
||||
|
||||
void testValgrindCrash();
|
||||
void testValgrindGarbage();
|
||||
|
||||
void testParserStop();
|
||||
void testRealValgrind();
|
||||
void testValgrindStartError_data();
|
||||
void testValgrindStartError();
|
||||
|
||||
private:
|
||||
void initTest(const QLatin1String &testfile, const QStringList &otherArgs = QStringList());
|
||||
|
||||
QTcpServer *m_server = 0;
|
||||
QProcess *m_process = 0;
|
||||
QTcpSocket *m_socket = 0;
|
||||
};
|
||||
|
||||
} // namespace Test
|
||||
} // namespace Valgrind
|
||||
@@ -32,6 +32,11 @@
|
||||
#include "valgrindsettings.h"
|
||||
#include "valgrindconfigwidget.h"
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
# include "valgrindmemcheckparsertest.h"
|
||||
# include "valgrindtestrunnertest.h"
|
||||
#endif
|
||||
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
#include <coreplugin/icontext.h>
|
||||
#include <coreplugin/icore.h>
|
||||
@@ -117,5 +122,14 @@ ValgrindGlobalSettings *ValgrindPlugin::globalSettings()
|
||||
return theGlobalSettings;
|
||||
}
|
||||
|
||||
QList<QObject *> ValgrindPlugin::createTestObjects() const
|
||||
{
|
||||
QList<QObject *> tests;
|
||||
#ifdef WITH_TESTS
|
||||
tests << new Test::ValgrindMemcheckParserTest << new Test::ValgrindTestRunnerTest;
|
||||
#endif
|
||||
return tests;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Valgrind
|
||||
|
||||
@@ -48,6 +48,8 @@ public:
|
||||
ShutdownFlag aboutToShutdown() override;
|
||||
|
||||
static ValgrindGlobalSettings *globalSettings();
|
||||
private:
|
||||
QList<QObject *> createTestObjects() const override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
775
src/plugins/valgrind/valgrindtestrunnertest.cpp
Normal file
775
src/plugins/valgrind/valgrindtestrunnertest.cpp
Normal file
@@ -0,0 +1,775 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Author: Milian Wolff, KDAB (milian.wolff@kdab.com)
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** 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
|
||||
** 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.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "valgrindtestrunnertest.h"
|
||||
|
||||
#include "xmlprotocol/frame.h"
|
||||
#include "xmlprotocol/stack.h"
|
||||
#include "xmlprotocol/suppression.h"
|
||||
#include "xmlprotocol/threadedparser.h"
|
||||
#include "xmlprotocol/parser.h"
|
||||
#include "memcheck/memcheckrunner.h"
|
||||
|
||||
#include <projectexplorer/devicesupport/devicemanager.h>
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/runnables.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTest>
|
||||
#include <QDir>
|
||||
#include <QSignalSpy>
|
||||
|
||||
#define HEADER_LENGTH 25
|
||||
|
||||
using namespace Valgrind::XmlProtocol;
|
||||
using namespace Valgrind::Memcheck;
|
||||
|
||||
namespace Valgrind {
|
||||
namespace Test {
|
||||
|
||||
//BEGIN Test Helpers and boilerplate code
|
||||
|
||||
static const QString appSrcDir(TESTRUNNER_SRC_DIR);
|
||||
static const QString appBinDir(TESTRUNNER_APP_DIR);
|
||||
|
||||
static bool on64bit()
|
||||
{
|
||||
return sizeof(char*) == 8;
|
||||
}
|
||||
|
||||
static QString srcDirForApp(const QString &app)
|
||||
{
|
||||
return QDir::cleanPath(appSrcDir + QLatin1Char('/') + app);
|
||||
}
|
||||
|
||||
ValgrindTestRunnerTest::ValgrindTestRunnerTest(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
qRegisterMetaType<Error>();
|
||||
}
|
||||
|
||||
QString ValgrindTestRunnerTest::runTestBinary(const QString &binary, const QStringList &vArgs)
|
||||
{
|
||||
const QFileInfo binPathFileInfo(appBinDir, binary);
|
||||
if (!binPathFileInfo.isExecutable())
|
||||
return QString();
|
||||
ProjectExplorer::StandardRunnable debuggee;
|
||||
const QString &binPath = binPathFileInfo.canonicalFilePath();
|
||||
debuggee.executable = binPath;
|
||||
debuggee.environment = Utils::Environment::systemEnvironment();
|
||||
m_runner->setValgrindArguments(QStringList() << "--num-callers=50" << "--track-origins=yes" << vArgs);
|
||||
m_runner->setDebuggee(debuggee);
|
||||
m_runner->setDevice(ProjectExplorer::DeviceManager::instance()->defaultDevice(
|
||||
Core::Id(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)));
|
||||
m_runner->start();
|
||||
m_runner->waitForFinished();
|
||||
return binPath;
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::logMessageReceived(const QByteArray &message)
|
||||
{
|
||||
qDebug() << "log message received:" << message;
|
||||
m_logMessages << message;
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::internalError(const QString &error)
|
||||
{
|
||||
if (!m_expectCrash)
|
||||
QFAIL(qPrintable(error));
|
||||
else
|
||||
qDebug() << "expected crash:" << error;
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::error(const Error &error)
|
||||
{
|
||||
m_errors << error;
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::cleanup()
|
||||
{
|
||||
Q_ASSERT(m_runner);
|
||||
delete m_runner;
|
||||
m_runner = 0;
|
||||
Q_ASSERT(m_parser);
|
||||
delete m_parser;
|
||||
m_parser = 0;
|
||||
|
||||
m_logMessages.clear();
|
||||
m_errors.clear();
|
||||
m_expectCrash = false;
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::init()
|
||||
{
|
||||
const Utils::Environment &sysEnv = Utils::Environment::systemEnvironment();
|
||||
auto fileName = sysEnv.searchInPath("valgrind");
|
||||
if (fileName.isEmpty())
|
||||
QSKIP("This test needs valgrind in PATH");
|
||||
Q_ASSERT(m_logMessages.isEmpty());
|
||||
|
||||
Q_ASSERT(!m_runner);
|
||||
m_runner = new MemcheckRunner;
|
||||
m_runner->setValgrindExecutable(QLatin1String("valgrind"));
|
||||
m_runner->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
connect(m_runner, &MemcheckRunner::logMessageReceived,
|
||||
this, &ValgrindTestRunnerTest::logMessageReceived);
|
||||
connect(m_runner, &ValgrindRunner::processErrorReceived,
|
||||
this, &ValgrindTestRunnerTest::internalError);
|
||||
Q_ASSERT(!m_parser);
|
||||
m_parser = new ThreadedParser;
|
||||
connect(m_parser, &ThreadedParser::internalError,
|
||||
this, &ValgrindTestRunnerTest::internalError);
|
||||
connect(m_parser, &ThreadedParser::error,
|
||||
this, &ValgrindTestRunnerTest::error);
|
||||
|
||||
m_runner->setParser(m_parser);
|
||||
}
|
||||
|
||||
//BEGIN: Actual test cases
|
||||
|
||||
void ValgrindTestRunnerTest::testLeak1()
|
||||
{
|
||||
const QString binary = runTestBinary(QLatin1String("leak1/leak1"));
|
||||
if (binary.isEmpty())
|
||||
QSKIP("You need to pass BUILD_TESTS when building Qt Creator or build valgrind testapps "
|
||||
"manually before executing this test.");
|
||||
|
||||
QVERIFY(m_logMessages.isEmpty());
|
||||
|
||||
QCOMPARE(m_errors.count(), 1);
|
||||
const Error error = m_errors.first();
|
||||
QCOMPARE(error.kind(), int(Leak_DefinitelyLost));
|
||||
QCOMPARE(error.leakedBlocks(), qint64(1));
|
||||
QCOMPARE(error.leakedBytes(), quint64(8));
|
||||
QCOMPARE(error.stacks().count(), 1);
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 2);
|
||||
{
|
||||
const Frame frame = stack.frames().at(0);
|
||||
if (on64bit())
|
||||
QCOMPARE(frame.functionName(), QLatin1String("operator new(unsigned long)"));
|
||||
else
|
||||
QCOMPARE(frame.functionName(), QLatin1String("operator new(unsigned int)"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().at(1);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 5 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDirForApp("leak1"));
|
||||
}
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::testLeak2()
|
||||
{
|
||||
const QString binary = runTestBinary(QLatin1String("leak2/leak2"));
|
||||
if (binary.isEmpty())
|
||||
QSKIP("You need to pass BUILD_TESTS when building Qt Creator or build valgrind testapps "
|
||||
"manually before executing this test.");
|
||||
|
||||
QVERIFY(m_logMessages.isEmpty());
|
||||
QCOMPARE(m_errors.count(), 1);
|
||||
const Error error = m_errors.first();
|
||||
QCOMPARE(error.kind(), int(Leak_PossiblyLost));
|
||||
QCOMPARE(error.leakedBlocks(), qint64(1));
|
||||
QCOMPARE(error.leakedBytes(), quint64(5));
|
||||
QCOMPARE(error.stacks().count(), 1);
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 3);
|
||||
{
|
||||
const Frame frame = stack.frames().at(0);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("malloc"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().at(1);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("strdup"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().at(2);
|
||||
if (on64bit()) {
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 7 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDirForApp("leak2"));
|
||||
} else {
|
||||
QCOMPARE(frame.functionName(), QLatin1String("(below main)"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::testLeak3()
|
||||
{
|
||||
const QString binary = runTestBinary(QLatin1String("leak3/leak3"), QStringList() << "--show-reachable=yes");
|
||||
if (binary.isEmpty())
|
||||
QSKIP("You need to pass BUILD_TESTS when building Qt Creator or build valgrind testapps "
|
||||
"manually before executing this test.");
|
||||
QVERIFY(m_logMessages.isEmpty());
|
||||
|
||||
QCOMPARE(m_errors.count(), 1);
|
||||
const Error error = m_errors.first();
|
||||
QCOMPARE(error.kind(), int(Leak_StillReachable));
|
||||
QCOMPARE(error.leakedBlocks(), qint64(1));
|
||||
QCOMPARE(error.leakedBytes(), quint64(5));
|
||||
QCOMPARE(error.stacks().count(), 1);
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 3);
|
||||
{
|
||||
const Frame frame = stack.frames().at(0);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("malloc"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().at(1);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("strdup"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().at(2);
|
||||
if (on64bit()) {
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 7 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDirForApp("leak3"));
|
||||
} else {
|
||||
QCOMPARE(frame.functionName(), QLatin1String("(below main)"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::testLeak4()
|
||||
{
|
||||
const QString app("leak4");
|
||||
const QString binary = runTestBinary(app + QLatin1Char('/') + app,
|
||||
QStringList() << "--show-reachable=yes");
|
||||
if (binary.isEmpty())
|
||||
QSKIP("You need to pass BUILD_TESTS when building Qt Creator or build valgrind testapps "
|
||||
"manually before executing this test.");
|
||||
const QString srcDir = srcDirForApp("leak4");
|
||||
|
||||
QVERIFY(m_logMessages.isEmpty());
|
||||
|
||||
QCOMPARE(m_errors.count(), 3);
|
||||
//BEGIN first error
|
||||
{
|
||||
const Error error = m_errors.first();
|
||||
QCOMPARE(error.kind(), int(Leak_IndirectlyLost));
|
||||
QCOMPARE(error.leakedBlocks(), qint64(1));
|
||||
QCOMPARE(error.leakedBytes(), quint64(8));
|
||||
QCOMPARE(error.stacks().count(), 1);
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 3);
|
||||
{
|
||||
const Frame frame = stack.frames().at(0);
|
||||
if (on64bit())
|
||||
QCOMPARE(frame.functionName(), QLatin1String("operator new(unsigned long)"));
|
||||
else
|
||||
QCOMPARE(frame.functionName(), QLatin1String("operator new(unsigned int)"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().at(1);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("Foo::Foo()"));
|
||||
QCOMPARE(frame.line(), 6 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().at(2);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 14 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
//BEGIN second error
|
||||
{
|
||||
const Error error = m_errors.at(1);
|
||||
QCOMPARE(error.kind(), int(Leak_DefinitelyLost));
|
||||
QCOMPARE(error.leakedBlocks(), qint64(1));
|
||||
if (on64bit())
|
||||
QCOMPARE(error.leakedBytes(), quint64(16));
|
||||
else
|
||||
QCOMPARE(error.leakedBytes(), quint64(12));
|
||||
QCOMPARE(error.stacks().count(), 1);
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 2);
|
||||
{
|
||||
const Frame frame = stack.frames().at(0);
|
||||
if (on64bit())
|
||||
QCOMPARE(frame.functionName(), QLatin1String("operator new(unsigned long)"));
|
||||
else
|
||||
QCOMPARE(frame.functionName(), QLatin1String("operator new(unsigned int)"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().at(1);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 14 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
// TODO add third error check
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::testUninit1()
|
||||
{
|
||||
const QString app("uninit1");
|
||||
const QString binary = runTestBinary(app + QLatin1Char('/') + app);
|
||||
if (binary.isEmpty())
|
||||
QSKIP("You need to pass BUILD_TESTS when building Qt Creator or build valgrind testapps "
|
||||
"manually before executing this test.");
|
||||
const QString srcDir = srcDirForApp(app);
|
||||
|
||||
QVERIFY(m_logMessages.isEmpty());
|
||||
|
||||
QCOMPARE(m_errors.count(), 1);
|
||||
const Error error = m_errors.first();
|
||||
QCOMPARE(error.kind(), int(UninitCondition));
|
||||
QCOMPARE(error.stacks().count(), 2);
|
||||
//BEGIN first stack
|
||||
{
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 1);
|
||||
|
||||
const Frame frame = stack.frames().first();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 4 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
//BEGIN second stack
|
||||
{
|
||||
const Stack stack = error.stacks().last();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 1);
|
||||
|
||||
const Frame frame = stack.frames().first();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 2 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::testUninit2()
|
||||
{
|
||||
const QString app("uninit2");
|
||||
m_expectCrash = true;
|
||||
const QString binary = runTestBinary(app + QLatin1Char('/') + app);
|
||||
if (binary.isEmpty())
|
||||
QSKIP("You need to pass BUILD_TESTS when building Qt Creator or build valgrind testapps "
|
||||
"manually before executing this test.");
|
||||
const QString srcDir = srcDirForApp(app);
|
||||
|
||||
QVERIFY(m_logMessages.isEmpty());
|
||||
|
||||
QCOMPARE(m_errors.count(), 2);
|
||||
//BEGIN first error
|
||||
{
|
||||
const Error error = m_errors.first();
|
||||
QCOMPARE(error.kind(), int(UninitValue));
|
||||
QCOMPARE(error.stacks().count(), 2);
|
||||
//BEGIN first stack
|
||||
{
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 1);
|
||||
|
||||
const Frame frame = stack.frames().first();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 4 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
//BEGIN second stack
|
||||
{
|
||||
const Stack stack = error.stacks().last();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 1);
|
||||
|
||||
const Frame frame = stack.frames().first();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 2 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
//BEGIN second error
|
||||
{
|
||||
const Error error = m_errors.last();
|
||||
QCOMPARE(error.kind(), int(InvalidWrite));
|
||||
QCOMPARE(error.stacks().count(), 1);
|
||||
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 1);
|
||||
|
||||
const Frame frame = stack.frames().first();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 4 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::testUninit3()
|
||||
{
|
||||
const QString app("uninit3");
|
||||
m_expectCrash = true;
|
||||
const QString binary = runTestBinary(app + QLatin1Char('/') + app);
|
||||
if (binary.isEmpty())
|
||||
QSKIP("You need to pass BUILD_TESTS when building Qt Creator or build valgrind testapps "
|
||||
"manually before executing this test.");
|
||||
const QString srcDir = srcDirForApp(app);
|
||||
|
||||
QVERIFY(m_logMessages.isEmpty());
|
||||
|
||||
QCOMPARE(m_errors.count(), 2);
|
||||
//BEGIN first error
|
||||
{
|
||||
const Error error = m_errors.first();
|
||||
QCOMPARE(error.kind(), int(UninitValue));
|
||||
QCOMPARE(error.stacks().count(), 2);
|
||||
//BEGIN first stack
|
||||
{
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 1);
|
||||
|
||||
const Frame frame = stack.frames().first();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 4 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
//BEGIN second stack
|
||||
{
|
||||
const Stack stack = error.stacks().last();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 1);
|
||||
|
||||
const Frame frame = stack.frames().first();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 2 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
//BEGIN second error
|
||||
{
|
||||
const Error error = m_errors.last();
|
||||
QCOMPARE(error.kind(), int(InvalidRead));
|
||||
QCOMPARE(error.stacks().count(), 1);
|
||||
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 1);
|
||||
|
||||
const Frame frame = stack.frames().first();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 4 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::testSyscall()
|
||||
{
|
||||
const QString app("syscall");
|
||||
const QString binary = runTestBinary(app + QLatin1Char('/') + app);
|
||||
if (binary.isEmpty())
|
||||
QSKIP("You need to pass BUILD_TESTS when building Qt Creator or build valgrind testapps "
|
||||
"manually before executing this test.");
|
||||
const QString srcDir = srcDirForApp(app);
|
||||
|
||||
QVERIFY(m_logMessages.isEmpty());
|
||||
|
||||
QCOMPARE(m_errors.count(), 1);
|
||||
const Error error = m_errors.first();
|
||||
QCOMPARE(error.kind(), int(SyscallParam));
|
||||
QCOMPARE(error.stacks().count(), 2);
|
||||
//BEGIN first stack
|
||||
{
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
if (on64bit()) {
|
||||
QCOMPARE(stack.frames().count(), 4);
|
||||
|
||||
{
|
||||
const Frame frame = stack.frames().at(0);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("_Exit"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().at(1);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("__run_exit_handlers"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().at(2);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("exit"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().at(3);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("(below main)"));
|
||||
}
|
||||
} else {
|
||||
QCOMPARE(stack.frames().count(), 1);
|
||||
{
|
||||
const Frame frame = stack.frames().at(0);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("_Exit"));
|
||||
}
|
||||
}
|
||||
}
|
||||
//BEGIN second stack
|
||||
{
|
||||
const Stack stack = error.stacks().last();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 1);
|
||||
|
||||
const Frame frame = stack.frames().first();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 2 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::testFree1()
|
||||
{
|
||||
const QString app("free1");
|
||||
const QString binary = runTestBinary(app + QLatin1Char('/') + app);
|
||||
if (binary.isEmpty())
|
||||
QSKIP("You need to pass BUILD_TESTS when building Qt Creator or build valgrind testapps "
|
||||
"manually before executing this test.");
|
||||
const QString srcDir = srcDirForApp(app);
|
||||
|
||||
QVERIFY(m_logMessages.isEmpty());
|
||||
|
||||
QCOMPARE(m_errors.count(), 1);
|
||||
const Error error = m_errors.first();
|
||||
QCOMPARE(error.kind(), int(InvalidFree));
|
||||
QCOMPARE(error.stacks().count(), 2);
|
||||
//BEGIN first stack
|
||||
{
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 2);
|
||||
|
||||
{
|
||||
const Frame frame = stack.frames().first();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("operator delete(void*)"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().last();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 7 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
//BEGIN second stack
|
||||
{
|
||||
const Stack stack = error.stacks().last();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 2);
|
||||
|
||||
{
|
||||
const Frame frame = stack.frames().first();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("operator delete(void*)"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().last();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 6 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::testFree2()
|
||||
{
|
||||
const QString app("free2");
|
||||
const QString binary = runTestBinary(app + QLatin1Char('/') + app);
|
||||
if (binary.isEmpty())
|
||||
QSKIP("You need to pass BUILD_TESTS when building Qt Creator or build valgrind testapps "
|
||||
"manually before executing this test.");
|
||||
const QString srcDir = srcDirForApp(app);
|
||||
|
||||
QVERIFY(m_logMessages.isEmpty());
|
||||
|
||||
QCOMPARE(m_errors.count(), 1);
|
||||
const Error error = m_errors.first();
|
||||
QCOMPARE(error.kind(), int(MismatchedFree));
|
||||
QCOMPARE(error.stacks().count(), 2);
|
||||
//BEGIN first stack
|
||||
{
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 2);
|
||||
|
||||
{
|
||||
const Frame frame = stack.frames().first();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("free"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().last();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 6 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
//BEGIN second stack
|
||||
{
|
||||
const Stack stack = error.stacks().last();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 2);
|
||||
|
||||
|
||||
{
|
||||
const Frame frame = stack.frames().first();
|
||||
if (on64bit())
|
||||
QCOMPARE(frame.functionName(), QLatin1String("operator new(unsigned long)"));
|
||||
else
|
||||
QCOMPARE(frame.functionName(), QLatin1String("operator new(unsigned int)"));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().last();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 5 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ValgrindTestRunnerTest::testInvalidjump()
|
||||
{
|
||||
const QString app("invalidjump");
|
||||
m_expectCrash = true;
|
||||
const QString binary = runTestBinary(app + QLatin1Char('/') + app);
|
||||
if (binary.isEmpty())
|
||||
QSKIP("You need to pass BUILD_TESTS when building Qt Creator or build valgrind testapps "
|
||||
"manually before executing this test.");
|
||||
|
||||
QVERIFY(m_logMessages.isEmpty());
|
||||
|
||||
QCOMPARE(m_errors.count(), 1);
|
||||
const Error error = m_errors.first();
|
||||
QCOMPARE(error.kind(), int(InvalidJump));
|
||||
QCOMPARE(error.stacks().count(), 1);
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 2);
|
||||
QVERIFY(!stack.auxWhat().isEmpty());
|
||||
{
|
||||
const Frame frame = stack.frames().at(0);
|
||||
QCOMPARE(frame.instructionPointer(), quint64(0));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().at(1);
|
||||
QCOMPARE(frame.functionName(), QLatin1String("(below main)"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ValgrindTestRunnerTest::testOverlap()
|
||||
{
|
||||
const QString app("overlap");
|
||||
m_expectCrash = true;
|
||||
const QString binary = runTestBinary(app + QLatin1Char('/') + app);
|
||||
if (binary.isEmpty())
|
||||
QSKIP("You need to pass BUILD_TESTS when building Qt Creator or build valgrind testapps "
|
||||
"manually before executing this test.");
|
||||
const QString srcDir = srcDirForApp(app);
|
||||
|
||||
QVERIFY(m_logMessages.isEmpty());
|
||||
|
||||
QCOMPARE(m_errors.count(), 1);
|
||||
const Error error = m_errors.first();
|
||||
QCOMPARE(error.kind(), int(Overlap));
|
||||
QCOMPARE(error.stacks().count(), 1);
|
||||
const Stack stack = error.stacks().first();
|
||||
QCOMPARE(stack.line(), qint64(-1));
|
||||
QCOMPARE(stack.frames().count(), 2);
|
||||
{
|
||||
const Frame frame = stack.frames().at(0);
|
||||
QVERIFY(frame.functionName().startsWith(QLatin1String("memcpy")));
|
||||
}
|
||||
{
|
||||
const Frame frame = stack.frames().last();
|
||||
QCOMPARE(frame.functionName(), QLatin1String("main"));
|
||||
QCOMPARE(frame.line(), 6 + HEADER_LENGTH);
|
||||
|
||||
QCOMPARE(frame.object(), binary);
|
||||
QCOMPARE(frame.fileName(), QLatin1String("main.cpp"));
|
||||
QCOMPARE(QDir::cleanPath(frame.directory()), srcDir);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Test
|
||||
} // namespace Valgrind
|
||||
88
src/plugins/valgrind/valgrindtestrunnertest.h
Normal file
88
src/plugins/valgrind/valgrindtestrunnertest.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Author: Milian Wolff, KDAB (milian.wolff@kdab.com)
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** 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
|
||||
** 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.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
#include "xmlprotocol/error.h"
|
||||
|
||||
namespace Valgrind {
|
||||
|
||||
namespace XmlProtocol {
|
||||
class ThreadedParser;
|
||||
}
|
||||
|
||||
namespace Memcheck {
|
||||
class MemcheckRunner;
|
||||
}
|
||||
|
||||
namespace Test {
|
||||
|
||||
class ValgrindTestRunnerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ValgrindTestRunnerTest(QObject *parent = 0);
|
||||
|
||||
private slots:
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void testLeak1();
|
||||
void testLeak2();
|
||||
void testLeak3();
|
||||
void testLeak4();
|
||||
|
||||
void testUninit1();
|
||||
void testUninit2();
|
||||
void testUninit3();
|
||||
|
||||
void testFree1();
|
||||
void testFree2();
|
||||
|
||||
void testInvalidjump();
|
||||
void testSyscall();
|
||||
void testOverlap();
|
||||
|
||||
void logMessageReceived(const QByteArray &message);
|
||||
void internalError(const QString &error);
|
||||
void error(const Valgrind::XmlProtocol::Error &error);
|
||||
|
||||
private:
|
||||
QString runTestBinary(const QString &binary, const QStringList &vArgs = QStringList());
|
||||
|
||||
XmlProtocol::ThreadedParser *m_parser = 0;
|
||||
Memcheck::MemcheckRunner *m_runner = 0;
|
||||
QList<QByteArray> m_logMessages;
|
||||
QList<XmlProtocol::Error> m_errors;
|
||||
bool m_expectCrash = false;
|
||||
};
|
||||
|
||||
} // namespace Test
|
||||
} // namespace Valgrind
|
||||
Reference in New Issue
Block a user