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:
Christian Stenger
2016-12-06 12:05:05 +01:00
parent 996a697094
commit 62118e9d78
16 changed files with 604 additions and 1186 deletions

View 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&amp;)</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&amp;, 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&lt;QSettingsKey, QVariant&gt;::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>

View File

@@ -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
}

View File

@@ -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",
]
}
}

View 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

View 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

View File

@@ -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

View File

@@ -48,6 +48,8 @@ public:
ShutdownFlag aboutToShutdown() override;
static ValgrindGlobalSettings *globalSettings();
private:
QList<QObject *> createTestObjects() const override;
};
} // namespace Internal

View 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

View 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