From 3b703588faacbf4ff2e963f6cb6839ece94a90b3 Mon Sep 17 00:00:00 2001 From: Mike McQuaid <mike@mikemcquaid.com> Date: Fri, 4 Mar 2011 12:15:18 +0100 Subject: [PATCH] Add libvalgrind tests. Merge-request: 260 Reviewed-by: hjk <qtc-committer@nokia.com> --- .gitignore | 16 +- tests/tests.pro | 3 + tests/valgrind/README | 6 + tests/valgrind/memcheck/memcheck.pro | 3 + tests/valgrind/memcheck/modeldemo.cpp | 99 +++ tests/valgrind/memcheck/modeldemo.h | 80 ++ tests/valgrind/memcheck/modeldemo.pro | 17 + tests/valgrind/memcheck/parsertests.cpp | 508 +++++++++++++ tests/valgrind/memcheck/parsertests.h | 186 +++++ tests/valgrind/memcheck/parsertests.pro | 19 + .../memcheck/testapps/free1/free1.pro | 8 + .../valgrind/memcheck/testapps/free1/main.cpp | 9 + .../memcheck/testapps/free2/free2.pro | 8 + .../valgrind/memcheck/testapps/free2/main.cpp | 8 + .../testapps/invalidjump/invalidjump.pro | 8 + .../memcheck/testapps/invalidjump/main.cpp | 8 + .../memcheck/testapps/leak1/leak1.pro | 8 + .../valgrind/memcheck/testapps/leak1/main.cpp | 7 + .../memcheck/testapps/leak2/leak2.pro | 8 + .../valgrind/memcheck/testapps/leak2/main.cpp | 13 + .../memcheck/testapps/leak3/leak3.pro | 8 + .../valgrind/memcheck/testapps/leak3/main.cpp | 10 + .../memcheck/testapps/leak4/leak4.pro | 8 + .../valgrind/memcheck/testapps/leak4/main.cpp | 17 + .../memcheck/testapps/overlap/main.cpp | 9 + .../memcheck/testapps/overlap/overlap.pro | 8 + .../memcheck/testapps/syscall/main.cpp | 5 + .../memcheck/testapps/syscall/syscall.pro | 8 + tests/valgrind/memcheck/testapps/testapps.pro | 4 + .../memcheck/testapps/uninit1/main.cpp | 9 + .../memcheck/testapps/uninit1/uninit1.pro | 8 + .../memcheck/testapps/uninit2/main.cpp | 7 + .../memcheck/testapps/uninit2/uninit2.pro | 8 + .../memcheck/testapps/uninit3/main.cpp | 5 + .../memcheck/testapps/uninit3/uninit3.pro | 8 + tests/valgrind/memcheck/testrunner.cpp | 703 ++++++++++++++++++ tests/valgrind/memcheck/testrunner.h | 98 +++ tests/valgrind/memcheck/testrunner.pro | 18 + tests/valgrind/valgrind.pro | 3 + 39 files changed, 1965 insertions(+), 1 deletion(-) create mode 100644 tests/valgrind/README create mode 100644 tests/valgrind/memcheck/memcheck.pro create mode 100644 tests/valgrind/memcheck/modeldemo.cpp create mode 100644 tests/valgrind/memcheck/modeldemo.h create mode 100644 tests/valgrind/memcheck/modeldemo.pro create mode 100644 tests/valgrind/memcheck/parsertests.cpp create mode 100644 tests/valgrind/memcheck/parsertests.h create mode 100644 tests/valgrind/memcheck/parsertests.pro create mode 100644 tests/valgrind/memcheck/testapps/free1/free1.pro create mode 100644 tests/valgrind/memcheck/testapps/free1/main.cpp create mode 100644 tests/valgrind/memcheck/testapps/free2/free2.pro create mode 100644 tests/valgrind/memcheck/testapps/free2/main.cpp create mode 100644 tests/valgrind/memcheck/testapps/invalidjump/invalidjump.pro create mode 100644 tests/valgrind/memcheck/testapps/invalidjump/main.cpp create mode 100644 tests/valgrind/memcheck/testapps/leak1/leak1.pro create mode 100644 tests/valgrind/memcheck/testapps/leak1/main.cpp create mode 100644 tests/valgrind/memcheck/testapps/leak2/leak2.pro create mode 100644 tests/valgrind/memcheck/testapps/leak2/main.cpp create mode 100644 tests/valgrind/memcheck/testapps/leak3/leak3.pro create mode 100644 tests/valgrind/memcheck/testapps/leak3/main.cpp create mode 100644 tests/valgrind/memcheck/testapps/leak4/leak4.pro create mode 100644 tests/valgrind/memcheck/testapps/leak4/main.cpp create mode 100644 tests/valgrind/memcheck/testapps/overlap/main.cpp create mode 100644 tests/valgrind/memcheck/testapps/overlap/overlap.pro create mode 100644 tests/valgrind/memcheck/testapps/syscall/main.cpp create mode 100644 tests/valgrind/memcheck/testapps/syscall/syscall.pro create mode 100644 tests/valgrind/memcheck/testapps/testapps.pro create mode 100644 tests/valgrind/memcheck/testapps/uninit1/main.cpp create mode 100644 tests/valgrind/memcheck/testapps/uninit1/uninit1.pro create mode 100644 tests/valgrind/memcheck/testapps/uninit2/main.cpp create mode 100644 tests/valgrind/memcheck/testapps/uninit2/uninit2.pro create mode 100644 tests/valgrind/memcheck/testapps/uninit3/main.cpp create mode 100644 tests/valgrind/memcheck/testapps/uninit3/uninit3.pro create mode 100644 tests/valgrind/memcheck/testrunner.cpp create mode 100644 tests/valgrind/memcheck/testrunner.h create mode 100644 tests/valgrind/memcheck/testrunner.pro create mode 100644 tests/valgrind/valgrind.pro diff --git a/.gitignore b/.gitignore index 84a479d1f72..074cf5526f2 100644 --- a/.gitignore +++ b/.gitignore @@ -110,4 +110,18 @@ tests/auto/qml/qmldesigner/coretests/tst_qmldesigner_core tests/auto/qml/qmldesigner/propertyeditortests/tst_propertyeditor tests/auto/profilewriter/tst_profilewriter tests/auto/externaltool/tst_externaltool - +tests/valgrind/memcheck/modeldemo +tests/valgrind/memcheck/parsertests +tests/valgrind/memcheck/testapps/free1/free1 +tests/valgrind/memcheck/testapps/free2/free2 +tests/valgrind/memcheck/testapps/invalidjump/invalidjump +tests/valgrind/memcheck/testapps/leak1/leak1 +tests/valgrind/memcheck/testapps/leak2/leak2 +tests/valgrind/memcheck/testapps/leak3/leak3 +tests/valgrind/memcheck/testapps/leak4/leak4 +tests/valgrind/memcheck/testapps/overlap/overlap +tests/valgrind/memcheck/testapps/syscall/syscall +tests/valgrind/memcheck/testapps/uninit1/uninit1 +tests/valgrind/memcheck/testapps/uninit2/uninit2 +tests/valgrind/memcheck/testapps/uninit3/uninit3 +tests/valgrind/memcheck/testrunner diff --git a/tests/tests.pro b/tests/tests.pro index 78b78c84526..782ca4eaf4f 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -1,2 +1,5 @@ TEMPLATE=subdirs SUBDIRS += auto manual tools +!win32 { + SUBDIRS += valgrind +} diff --git a/tests/valgrind/README b/tests/valgrind/README new file mode 100644 index 00000000000..66f9bd24bbd --- /dev/null +++ b/tests/valgrind/README @@ -0,0 +1,6 @@ +# How to Compile + +cd qtc-build # go to your build folder of qtc +mkdir valgrind-test +cd valgrind-test +qmake CONFIG+=debug IDE_BUILD_TREE=$(readlink -f ..) ../../path/to/qtc/tests/valgrind diff --git a/tests/valgrind/memcheck/memcheck.pro b/tests/valgrind/memcheck/memcheck.pro new file mode 100644 index 00000000000..ee72ee00107 --- /dev/null +++ b/tests/valgrind/memcheck/memcheck.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS += parsertests.pro modeldemo.pro testapps testrunner.pro diff --git a/tests/valgrind/memcheck/modeldemo.cpp b/tests/valgrind/memcheck/modeldemo.cpp new file mode 100644 index 00000000000..17c54a4c1f5 --- /dev/null +++ b/tests/valgrind/memcheck/modeldemo.cpp @@ -0,0 +1,99 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Frank Osterfeld, KDAB (frank.osterfeld@kdab.com) +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include <valgrind/xmlprotocol/frame.h> +#include <valgrind/xmlprotocol/parser.h> +#include <valgrind/xmlprotocol/stack.h> +#include <valgrind/xmlprotocol/status.h> +#include <valgrind/xmlprotocol/threadedparser.h> + +#include "modeldemo.h" + +#include <QtGui/QApplication> +#include <QtGui/QTreeView> + +using namespace Valgrind; +using namespace Valgrind::XmlProtocol; + +static QString fakeValgrindExecutable() +{ + return QCoreApplication::applicationDirPath() + QLatin1String("/../../valgrind-fake/valgrind-fake"); +} + +static QString dataFile(const QLatin1String &file) +{ + return QLatin1String(PARSERTESTS_DATA_DIR) + QLatin1String("/") + file; +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + qRegisterMetaType<Valgrind::XmlProtocol::Error>(); + + ThreadedParser parser; + + Valgrind::Memcheck::MemcheckRunner runner; + runner.setValgrindExecutable(fakeValgrindExecutable()); + runner.setValgrindArguments(QStringList() << QLatin1String("-i") << dataFile(QLatin1String("memcheck-output-sample1.xml")) ); + runner.setParser(&parser); + + ModelDemo demo(&runner); + runner.connect(&runner, SIGNAL(finished()), &demo, SLOT(finished())); + ErrorListModel model; + parser.connect(&parser, SIGNAL(error(Valgrind::XmlProtocol::Error)), + &model, SLOT(addError(Valgrind::XmlProtocol::Error)), + Qt::QueuedConnection); + + QTreeView errorview; + errorview.setSelectionMode(QAbstractItemView::SingleSelection); + errorview.setSelectionBehavior(QAbstractItemView::SelectRows); + errorview.setModel(&model); + errorview.show(); + + StackModel stackModel; + demo.stackModel = &stackModel; + + QTreeView stackView; + stackView.setModel(&stackModel); + stackView.show(); + + errorview.connect(errorview.selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + &demo, SLOT(selectionChanged(QItemSelection,QItemSelection))); + + + runner.start(); + + return app.exec(); +} diff --git a/tests/valgrind/memcheck/modeldemo.h b/tests/valgrind/memcheck/modeldemo.h new file mode 100644 index 00000000000..fdc8c03c2ff --- /dev/null +++ b/tests/valgrind/memcheck/modeldemo.h @@ -0,0 +1,80 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Frank Osterfeld, KDAB (frank.osterfeld@kdab.com) +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef MODELDEMO_H +#define MODELDEMO_H + +#include <QObject> +#include <QDebug> +#include <QItemSelection> +#include <QModelIndex> + +#include <valgrind/xmlprotocol/error.h> +#include <valgrind/xmlprotocol/errorlistmodel.h> +#include <valgrind/xmlprotocol/stackmodel.h> +#include <valgrind/memcheck/memcheckrunner.h> + +class ModelDemo : public QObject +{ + Q_OBJECT +public: + explicit ModelDemo(Valgrind::Memcheck::MemcheckRunner *r, QObject *parent = 0) + : QObject(parent) + , runner(r) + { + } + + Valgrind::XmlProtocol::StackModel* stackModel; + +public Q_SLOTS: + void finished() { + qDebug() << runner->errorString(); + } + + void selectionChanged(const QItemSelection &sel, const QItemSelection &) { + if (sel.indexes().isEmpty()) + return; + const QModelIndex idx = sel.indexes().first(); + const Valgrind::XmlProtocol::Error err = idx.data(Valgrind::XmlProtocol::ErrorListModel::ErrorRole).value<Valgrind::XmlProtocol::Error>(); + qDebug() << idx.row() << err.what(); + stackModel->setError(err); + } + + +private: + Valgrind::Memcheck::MemcheckRunner *runner; +}; + +#endif // MODELDEMO_H diff --git a/tests/valgrind/memcheck/modeldemo.pro b/tests/valgrind/memcheck/modeldemo.pro new file mode 100644 index 00000000000..a0a9773c420 --- /dev/null +++ b/tests/valgrind/memcheck/modeldemo.pro @@ -0,0 +1,17 @@ +TEMPLATE = app +TARGET = modeldemo + +macx:CONFIG -= app_bundle + +QT += gui network + +DEFINES += "PARSERTESTS_DATA_DIR=\"\\\"$$PWD/data\\\"\"" + +!win32 { + include(../../../qtcreator.pri) + include(../../../src/libs/valgrind/valgrind.pri) +} + +SOURCES += modeldemo.cpp + +HEADERS += modeldemo.h diff --git a/tests/valgrind/memcheck/parsertests.cpp b/tests/valgrind/memcheck/parsertests.cpp new file mode 100644 index 00000000000..5d1b38e6c32 --- /dev/null +++ b/tests/valgrind/memcheck/parsertests.cpp @@ -0,0 +1,508 @@ +/************************************************************************** +** +** This file is part of Qt Creator Analyzer Tools +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Frank Osterfeld, KDAB (frank.osterfeld@kdab.com) +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include <valgrind/xmlprotocol/frame.h> +#include <valgrind/xmlprotocol/parser.h> +#include <valgrind/xmlprotocol/stack.h> +#include <valgrind/xmlprotocol/suppression.h> + +#include "parsertests.h" + +#include <QCoreApplication> +#include <QDebug> +#include <QFile> +#include <QFileInfo> +#include <QString> +#include <QTest> +#include <QTcpServer> +#include <QTcpSocket> +#include <QSignalSpy> + +#include <iostream> +#include <QProcess> + +using namespace Valgrind; +using namespace Valgrind::XmlProtocol; + +QT_BEGIN_NAMESPACE +namespace QTest { + +template<> +inline bool qCompare(int const &t1, Valgrind::XmlProtocol::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 + +void dumpFrame(const Frame &f) +{ + qDebug() << f.instructionPointer() << f.directory() << f.file() << 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(); + Q_FOREACH(const Stack& s, e.stacks()) { + qDebug() << s.auxWhat() << s.directory() << s.file() << s.line() << s.helgrindThreadId(); + qDebug() << "frames:"; + Q_FOREACH(const Frame& f, s.frames()) { + dumpFrame(f); + } + } +} + +static QString fakeValgrindExecutable() +{ + QString ret(VALGRIND_FAKE_PATH); + QFileInfo fileInfo(ret); + Q_ASSERT(fileInfo.isExecutable()); + Q_ASSERT(!fileInfo.isDir()); + return ret; +} + +static QString dataFile(const QLatin1String &file) +{ + return QLatin1String(PARSERTESTS_DATA_DIR) + QLatin1String("/") + file; +} + +void ParserTests::initTestCase() +{ + m_server = new QTcpServer(this); + QVERIFY(m_server->listen()); + + m_socket = 0; + m_process = 0; +} + +void ParserTests::initTest(const QLatin1String &testfile, const QStringList &otherArgs) +{ + QVERIFY(!m_server->hasPendingConnections()); + + m_process = new QProcess(m_server); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + m_process->start( + fakeValgrindExecutable(), + QStringList() + << QString("--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 ParserTests::cleanup() +{ + if (m_socket) { + delete m_socket; + m_socket = 0; + } + if (m_process) { + delete m_process; + m_process = 0; + } +} + +void ParserTests::testHelgrindSample1() +{ + 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.setFile(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.setFile(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.setFile(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.setFile(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.setFile(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.setFile(QLatin1String("qmutex.h")); + frame32.setLine(122); + + stack3.setFrames(QVector<Frame>() << frame31 << frame32); + error1.setStacks(QVector<Stack>() << stack1 << stack2 << stack3); + expectedErrors.append(error1); + } + + Valgrind::XmlProtocol::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 ParserTests::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.setFile(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.setFile(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.setFile(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))); + + Valgrind::XmlProtocol::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 ParserTests::testMemcheckSample2() +{ + initTest(QLatin1String("memcheck-output-sample2.xml")); + + Valgrind::XmlProtocol::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 ParserTests::testMemcheckSample3() +{ + initTest(QLatin1String("memcheck-output-sample3.xml")); + + Valgrind::XmlProtocol::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 ParserTests::testMemcheckCharm() +{ + // a somewhat larger file, to make sure buffering and partial I/O works ok + initTest(QLatin1String("memcheck-output-charm.xml")); + + Valgrind::XmlProtocol::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 ParserTests::testValgrindCrash() +{ + initTest(QLatin1String("memcheck-output-sample1.xml"), QStringList() << "--crash"); + + Valgrind::XmlProtocol::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 ParserTests::testValgrindGarbage() +{ + initTest(QLatin1String("memcheck-output-sample1.xml"), QStringList() << "--garbage"); + + Valgrind::XmlProtocol::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 ParserTests::testParserStop() +{ + ThreadedParser parser; + Valgrind::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.start(); + QTest::qWait(500); + runner.stop(); +} + + +void ParserTests::testRealValgrind() +{ + QString executable = QProcessEnvironment::systemEnvironment().value("VALGRIND_TEST_BIN", fakeValgrindExecutable()); + qDebug() << "running exe:" << executable << " HINT: set VALGRIND_TEST_BIN to change this"; + ThreadedParser parser; + + Valgrind::Memcheck::MemcheckRunner runner; + runner.setValgrindExecutable(QString("valgrind")); + runner.setDebuggeeExecutable(executable); + runner.setParser(&parser); + RunnerDumper dumper(&runner, &parser); + runner.start(); + runner.waitForFinished(); +} + +void ParserTests::testValgrindStartError_data() +{ + QTest::addColumn<QString>("valgrindExe"); + QTest::addColumn<QStringList>("valgrindArgs"); + QTest::addColumn<QString>("debuggee"); + QTest::addColumn<QString>("debuggeeArgs"); + + QTest::newRow("invalid_client") << QString("valgrind") << QStringList() + << QString("please-dont-let-this-app-exist") << QString(); + + QTest::newRow("invalid_valgrind") << QString("valgrind-that-does-not-exist") << QStringList() + << fakeValgrindExecutable() << QString(); + + QTest::newRow("invalid_valgrind_args") << QString("valgrind") << (QStringList() << "--foobar-fail") + << fakeValgrindExecutable() << QString(); +} + +void ParserTests::testValgrindStartError() +{ + QFETCH(QString, valgrindExe); + QFETCH(QStringList, valgrindArgs); + QFETCH(QString, debuggee); + QFETCH(QString, debuggeeArgs); + + ThreadedParser parser; + + Valgrind::Memcheck::MemcheckRunner runner; + runner.setParser(&parser); + runner.setValgrindExecutable(valgrindExe); + runner.setValgrindArguments(valgrindArgs); + runner.setDebuggeeExecutable(debuggee); + runner.setDebuggeeArguments(debuggeeArgs); + RunnerDumper dumper(&runner, &parser); + runner.start(); + runner.waitForFinished(); + QVERIFY(dumper.m_errorReceived); + // just finish without deadlock and we are fine +} + +QTEST_MAIN(ParserTests) diff --git a/tests/valgrind/memcheck/parsertests.h b/tests/valgrind/memcheck/parsertests.h new file mode 100644 index 00000000000..42f564413bf --- /dev/null +++ b/tests/valgrind/memcheck/parsertests.h @@ -0,0 +1,186 @@ +/************************************************************************** +** +** This file is part of Qt Creator Analyzer Tools +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Frank Osterfeld, KDAB (frank.osterfeld@kdab.com) +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PARSERTESTS_H +#define PARSERTESTS_H + +#include <QtCore/QObject> +#include <QtCore/QPair> +#include <QtCore/QStringList> +#include <QtCore/QVector> +#include <QtCore/QDebug> + +#include <valgrind/xmlprotocol/error.h> +#include <valgrind/xmlprotocol/status.h> +#include <valgrind/xmlprotocol/threadedparser.h> +#include <valgrind/xmlprotocol/parser.h> +#include <valgrind/memcheck/memcheckrunner.h> + +QT_BEGIN_NAMESPACE +class QTcpServer; +class QTcpSocket; +class QProcess; +QT_END_NAMESPACE + +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, SIGNAL(error(Valgrind::XmlProtocol::Error)), + this, SLOT(error(Valgrind::XmlProtocol::Error))); + connect(parser, SIGNAL(errorCount(qint64, qint64)), + this, SLOT(errorCount(qint64, qint64))); + connect(parser, SIGNAL(suppressionCount(QString, qint64)), + this, SLOT(suppressionCount(QString, qint64))); + } + + 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 uniq, qint64 count) + { + errorcounts.push_back(qMakePair(uniq, 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, SIGNAL(error(Valgrind::XmlProtocol::Error)), + this, SLOT(error(Valgrind::XmlProtocol::Error))); + connect(parser, SIGNAL(internalError(QString)), + this, SLOT(internalError(QString))); + connect(parser, SIGNAL(status(Valgrind::XmlProtocol::Status)), + this, SLOT(status(Valgrind::XmlProtocol::Status))); + connect(runner, SIGNAL(standardErrorReceived(QByteArray)), + this, SLOT(standardErrorReceived(QByteArray))); + connect(runner, SIGNAL(standardOutputReceived(QByteArray)), + this, SLOT(standardOutputReceived(QByteArray))); + connect(runner, SIGNAL(logMessageReceived(QByteArray)), + this, SLOT(logMessageReceived(QByteArray))); + connect(runner, SIGNAL(processErrorReceived(QString, QProcess::ProcessError)), + this, SLOT(processErrorReceived(QString))); + } + +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 standardErrorReceived(const QByteArray &err) + { + Q_UNUSED(err); + // qDebug() << "STDERR received:" << err; // this can be a lot of text + } + void standardOutputReceived(const QByteArray &out) + { + qDebug() << "STDOUT received:" << out; + } + 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 ParserTests : 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; + QProcess *m_process; + QTcpSocket *m_socket; +}; + +#endif // PARSERTESTS_H diff --git a/tests/valgrind/memcheck/parsertests.pro b/tests/valgrind/memcheck/parsertests.pro new file mode 100644 index 00000000000..92eb6eca8c7 --- /dev/null +++ b/tests/valgrind/memcheck/parsertests.pro @@ -0,0 +1,19 @@ +TEMPLATE = app +TARGET = parsertests + +macx:CONFIG -= app_bundle + +QT += testlib network + +DEFINES += "PARSERTESTS_DATA_DIR=\"\\\"$$PWD/data\\\"\"" + +DEFINES += "VALGRIND_FAKE_PATH="\\\"$$IDE_BUILD_TREE/src/tools/valgrindfake/valgrind-fake\\\"" + +!win32 { + include(../../../qtcreator.pri) + include(../../../src/libs/valgrind/valgrind.pri) +} + +SOURCES += parsertests.cpp + +HEADERS += parsertests.h diff --git a/tests/valgrind/memcheck/testapps/free1/free1.pro b/tests/valgrind/memcheck/testapps/free1/free1.pro new file mode 100644 index 00000000000..0b7ac8d8089 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/free1/free1.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = free1 + +QT -= core gui + +macx:CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/tests/valgrind/memcheck/testapps/free1/main.cpp b/tests/valgrind/memcheck/testapps/free1/main.cpp new file mode 100644 index 00000000000..7b847565586 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/free1/main.cpp @@ -0,0 +1,9 @@ +#include <cstdlib> + +int main() +{ + int *p = new int; + delete p; + delete p; + return 0; +} diff --git a/tests/valgrind/memcheck/testapps/free2/free2.pro b/tests/valgrind/memcheck/testapps/free2/free2.pro new file mode 100644 index 00000000000..1c1f7a5098a --- /dev/null +++ b/tests/valgrind/memcheck/testapps/free2/free2.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = free2 + +QT -= core gui + +macx:CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/tests/valgrind/memcheck/testapps/free2/main.cpp b/tests/valgrind/memcheck/testapps/free2/main.cpp new file mode 100644 index 00000000000..cb4e295c61c --- /dev/null +++ b/tests/valgrind/memcheck/testapps/free2/main.cpp @@ -0,0 +1,8 @@ +#include <cstdlib> + +int main() +{ + int *p = new int; + free(p); + return 0; +} diff --git a/tests/valgrind/memcheck/testapps/invalidjump/invalidjump.pro b/tests/valgrind/memcheck/testapps/invalidjump/invalidjump.pro new file mode 100644 index 00000000000..7f7a231327f --- /dev/null +++ b/tests/valgrind/memcheck/testapps/invalidjump/invalidjump.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = invalidjump + +QT -= core gui + +macx:CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/tests/valgrind/memcheck/testapps/invalidjump/main.cpp b/tests/valgrind/memcheck/testapps/invalidjump/main.cpp new file mode 100644 index 00000000000..7c608d40234 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/invalidjump/main.cpp @@ -0,0 +1,8 @@ +void foo() { } + +int main() +{ + void (*fooPtr)() = 0; + fooPtr(); + return 0; +} diff --git a/tests/valgrind/memcheck/testapps/leak1/leak1.pro b/tests/valgrind/memcheck/testapps/leak1/leak1.pro new file mode 100644 index 00000000000..2b5b6b8be8c --- /dev/null +++ b/tests/valgrind/memcheck/testapps/leak1/leak1.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = leak1 + +QT += core + +macx:CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/tests/valgrind/memcheck/testapps/leak1/main.cpp b/tests/valgrind/memcheck/testapps/leak1/main.cpp new file mode 100644 index 00000000000..540a6806f0d --- /dev/null +++ b/tests/valgrind/memcheck/testapps/leak1/main.cpp @@ -0,0 +1,7 @@ +#include <qglobal.h> + +int main() +{ + qint64 *i = new qint64; + return 0; +} diff --git a/tests/valgrind/memcheck/testapps/leak2/leak2.pro b/tests/valgrind/memcheck/testapps/leak2/leak2.pro new file mode 100644 index 00000000000..8b9b2717a79 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/leak2/leak2.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = leak2 + +QT += core + +macx:CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/tests/valgrind/memcheck/testapps/leak2/main.cpp b/tests/valgrind/memcheck/testapps/leak2/main.cpp new file mode 100644 index 00000000000..6a6ec0745bc --- /dev/null +++ b/tests/valgrind/memcheck/testapps/leak2/main.cpp @@ -0,0 +1,13 @@ +#include <string.h> + +char *lower; + +int main() +{ + lower = strdup("asdf"); + + while (*lower) + *(lower++); + + return 0; +} diff --git a/tests/valgrind/memcheck/testapps/leak3/leak3.pro b/tests/valgrind/memcheck/testapps/leak3/leak3.pro new file mode 100644 index 00000000000..aaefab13dfd --- /dev/null +++ b/tests/valgrind/memcheck/testapps/leak3/leak3.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = leak3 + +QT += core + +macx:CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/tests/valgrind/memcheck/testapps/leak3/main.cpp b/tests/valgrind/memcheck/testapps/leak3/main.cpp new file mode 100644 index 00000000000..13735b67087 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/leak3/main.cpp @@ -0,0 +1,10 @@ +#include <string.h> + +char *lower; + +int main() +{ + lower = strdup("asdf"); + + return 0; +} diff --git a/tests/valgrind/memcheck/testapps/leak4/leak4.pro b/tests/valgrind/memcheck/testapps/leak4/leak4.pro new file mode 100644 index 00000000000..ebbafbd51a0 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/leak4/leak4.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = leak4 + +QT += core + +macx:CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/tests/valgrind/memcheck/testapps/leak4/main.cpp b/tests/valgrind/memcheck/testapps/leak4/main.cpp new file mode 100644 index 00000000000..d0874353399 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/leak4/main.cpp @@ -0,0 +1,17 @@ +#include <qglobal.h> + +struct Foo +{ + Foo() + : num(new qint64) + {} + + qint64 *num; +}; + +int main() +{ + Foo *f = new Foo; + + return 0; +} diff --git a/tests/valgrind/memcheck/testapps/overlap/main.cpp b/tests/valgrind/memcheck/testapps/overlap/main.cpp new file mode 100644 index 00000000000..0066239a106 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/overlap/main.cpp @@ -0,0 +1,9 @@ +#include <string.h> + +int main() +{ + int *i = new int[10]; + memcpy(i, &i[1], 20); + delete[] i; + return 0; +} diff --git a/tests/valgrind/memcheck/testapps/overlap/overlap.pro b/tests/valgrind/memcheck/testapps/overlap/overlap.pro new file mode 100644 index 00000000000..cad970b1fab --- /dev/null +++ b/tests/valgrind/memcheck/testapps/overlap/overlap.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = overlap + +QT -= core gui + +macx:CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/tests/valgrind/memcheck/testapps/syscall/main.cpp b/tests/valgrind/memcheck/testapps/syscall/main.cpp new file mode 100644 index 00000000000..fcf3363791d --- /dev/null +++ b/tests/valgrind/memcheck/testapps/syscall/main.cpp @@ -0,0 +1,5 @@ +int main() +{ + int i; + return i; +} diff --git a/tests/valgrind/memcheck/testapps/syscall/syscall.pro b/tests/valgrind/memcheck/testapps/syscall/syscall.pro new file mode 100644 index 00000000000..e54d4d379e2 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/syscall/syscall.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = syscall + +QT += core + +macx:CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/tests/valgrind/memcheck/testapps/testapps.pro b/tests/valgrind/memcheck/testapps/testapps.pro new file mode 100644 index 00000000000..90490cf34c7 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/testapps.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS += leak1 leak2 leak3 leak4 uninit1 uninit2 syscall free1 uninit3 free2 invalidjump \ + overlap diff --git a/tests/valgrind/memcheck/testapps/uninit1/main.cpp b/tests/valgrind/memcheck/testapps/uninit1/main.cpp new file mode 100644 index 00000000000..ab571da1804 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/uninit1/main.cpp @@ -0,0 +1,9 @@ +int main() +{ + bool b; + if (b) { + return 1; + } + + return 0; +} diff --git a/tests/valgrind/memcheck/testapps/uninit1/uninit1.pro b/tests/valgrind/memcheck/testapps/uninit1/uninit1.pro new file mode 100644 index 00000000000..68843829d56 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/uninit1/uninit1.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = uninit1 + +QT += core + +macx:CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/tests/valgrind/memcheck/testapps/uninit2/main.cpp b/tests/valgrind/memcheck/testapps/uninit2/main.cpp new file mode 100644 index 00000000000..75cc9bdfa34 --- /dev/null +++ b/tests/valgrind/memcheck/testapps/uninit2/main.cpp @@ -0,0 +1,7 @@ +int main() +{ + int *i; + *i = 5; + + return 0; +} diff --git a/tests/valgrind/memcheck/testapps/uninit2/uninit2.pro b/tests/valgrind/memcheck/testapps/uninit2/uninit2.pro new file mode 100644 index 00000000000..7e5f89e0b5a --- /dev/null +++ b/tests/valgrind/memcheck/testapps/uninit2/uninit2.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = uninit2 + +QT -= core gui + +macx:CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/tests/valgrind/memcheck/testapps/uninit3/main.cpp b/tests/valgrind/memcheck/testapps/uninit3/main.cpp new file mode 100644 index 00000000000..b0c392d211a --- /dev/null +++ b/tests/valgrind/memcheck/testapps/uninit3/main.cpp @@ -0,0 +1,5 @@ +int main() +{ + int *i; + return *i; +} diff --git a/tests/valgrind/memcheck/testapps/uninit3/uninit3.pro b/tests/valgrind/memcheck/testapps/uninit3/uninit3.pro new file mode 100644 index 00000000000..102d0cae83b --- /dev/null +++ b/tests/valgrind/memcheck/testapps/uninit3/uninit3.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = uninit3 + +QT -= core gui + +macx:CONFIG -= app_bundle + +SOURCES += main.cpp diff --git a/tests/valgrind/memcheck/testrunner.cpp b/tests/valgrind/memcheck/testrunner.cpp new file mode 100644 index 00000000000..5d6f5bc19a4 --- /dev/null +++ b/tests/valgrind/memcheck/testrunner.cpp @@ -0,0 +1,703 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#include "testrunner.h" + +#include <valgrind/xmlprotocol/frame.h> +#include <valgrind/xmlprotocol/stack.h> +#include <valgrind/xmlprotocol/suppression.h> +#include <valgrind/xmlprotocol/threadedparser.h> +#include <valgrind/xmlprotocol/parser.h> +#include <valgrind/memcheck/memcheckrunner.h> + +#include <QDebug> +#include <QTest> +#include <QDir> +#include <QSignalSpy> + +const QString appSrcDir(TESTRUNNER_SRC_DIR); +const QString appBinDir(TESTRUNNER_APP_DIR); + +QString srcDirForApp(const QString &app) +{ + return appSrcDir + QDir::separator() + app; +} + +QTEST_MAIN(Valgrind::TestRunner) + +using namespace Valgrind; +using namespace Valgrind::XmlProtocol; +using namespace Valgrind::Memcheck; + +//BEGIN Test Helpers and boilterplate code + +TestRunner::TestRunner(QObject *parent) + : QObject(parent), + m_parser(0), + m_runner(0) +{ + qRegisterMetaType<Error>(); +} + +QString TestRunner::runTestBinary(const QString &binary, const QStringList &vArgs) +{ + const QString binPath = appBinDir + QDir::separator() + binary; + Q_ASSERT(QFileInfo(binPath).isExecutable()); + m_runner->setValgrindArguments(QStringList() << "--num-callers=50" << "--track-origins=yes" << vArgs); + m_runner->setDebuggeeExecutable(binPath); + m_runner->start(); + m_runner->waitForFinished(); + return binPath; +} + +void TestRunner::logMessageReceived(const QByteArray &message) +{ + qDebug() << "log message received:" << message; + m_logMessages << message; +} + +void TestRunner::internalError(const QString &error) +{ + if (!m_expectCrash) + QFAIL(qPrintable(error)); + else + qDebug() << "expected crash:" << error; +} + +void TestRunner::error(const Error &error) +{ + m_errors << error; +} + +void TestRunner::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 TestRunner::init() +{ + 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, SIGNAL(logMessageReceived(QByteArray)), + this, SLOT(logMessageReceived(QByteArray))); + connect(m_runner, SIGNAL(processErrorReceived(QString, QProcess::ProcessError)), + this, SLOT(internalError(QString))); + Q_ASSERT(!m_parser); + m_parser = new ThreadedParser; + connect(m_parser, SIGNAL(internalError(QString)), + this, SLOT(internalError(QString))); + connect(m_parser, SIGNAL(error(Valgrind::XmlProtocol::Error)), + this, SLOT(error(Valgrind::XmlProtocol::Error))); + + m_runner->setParser(m_parser); +} + +//BEGIN: Actual test cases + +void TestRunner::testLeak1() +{ + const QString binary = runTestBinary(QLatin1String("leak1/leak1")); + + 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); + QCOMPARE(frame.functionName(), QString("operator new(unsigned long)")); + } + { + const Frame frame = stack.frames().at(1); + QCOMPARE(frame.functionName(), QString("main")); + QCOMPARE(frame.line(), 5); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDirForApp("leak1")); + } +} + +void TestRunner::testLeak2() +{ + const QString binary = runTestBinary(QLatin1String("leak2/leak2")); + + 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(), QString("malloc")); + } + { + const Frame frame = stack.frames().at(1); + QCOMPARE(frame.functionName(), QString("strdup")); + } + { + const Frame frame = stack.frames().at(2); + QCOMPARE(frame.functionName(), QString("main")); + QCOMPARE(frame.line(), 7); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDirForApp("leak2")); + } +} + +void TestRunner::testLeak3() +{ + const QString binary = runTestBinary(QLatin1String("leak3/leak3"), QStringList() << "--show-reachable=yes"); + + 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(), QString("malloc")); + } + { + const Frame frame = stack.frames().at(1); + QCOMPARE(frame.functionName(), QString("strdup")); + } + { + const Frame frame = stack.frames().at(2); + QCOMPARE(frame.functionName(), QString("main")); + QCOMPARE(frame.line(), 7); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDirForApp("leak3")); + } +} + +void TestRunner::testLeak4() +{ + const QString app("leak4"); + const QString binary = runTestBinary(app + QDir::separator() + app, + QStringList() << "--show-reachable=yes"); + const QString srcDir = srcDirForApp("leak4"); + + QVERIFY(m_logMessages.isEmpty()); + + QCOMPARE(m_errors.count(), 2); + //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); + QCOMPARE(frame.functionName(), QString("operator new(unsigned long)")); + } + { + const Frame frame = stack.frames().at(2); + QCOMPARE(frame.functionName(), QString("main")); + QCOMPARE(frame.line(), 13); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDir); + } + { + const Frame frame = stack.frames().at(1); + QCOMPARE(frame.functionName(), QString("foo::foo()")); + QCOMPARE(frame.line(), 5); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDir); + } + { + const Frame frame = stack.frames().at(2); + QCOMPARE(frame.functionName(), QString("main")); + QCOMPARE(frame.line(), 13); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDir); + } + } + //BEGIN second error + { + const Error error = m_errors.last(); + QCOMPARE(error.kind(), int(Leak_DefinitelyLost)); + QCOMPARE(error.leakedBlocks(), qint64(1)); + QCOMPARE(error.leakedBytes(), quint64(16)); + 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); + QCOMPARE(frame.functionName(), QString("operator new(unsigned long)")); + } + { + const Frame frame = stack.frames().at(1); + QCOMPARE(frame.functionName(), QString("main")); + QCOMPARE(frame.line(), 13); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDir); + } + } +} + +void TestRunner::uninit1() +{ + const QString app("uninit1"); + const QString binary = runTestBinary(app + QDir::separator() + app); + 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(), QString("main")); + QCOMPARE(frame.line(), 4); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), 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(), QString("main")); + QCOMPARE(frame.line(), 2); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDir); + } +} + +void TestRunner::uninit2() +{ + const QString app("uninit2"); + m_expectCrash = true; + const QString binary = runTestBinary(app + QDir::separator() + app); + 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(), QString("main")); + QCOMPARE(frame.line(), 4); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), 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(), QString("main")); + QCOMPARE(frame.line(), 2); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), 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(), QString("main")); + QCOMPARE(frame.line(), 4); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDir); + } +} + +void TestRunner::uninit3() +{ + const QString app("uninit3"); + m_expectCrash = true; + const QString binary = runTestBinary(app + QDir::separator() + app); + 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(), QString("main")); + QCOMPARE(frame.line(), 3); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), 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(), QString("main")); + QCOMPARE(frame.line(), 1); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), 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(), QString("main")); + QCOMPARE(frame.line(), 3); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDir); + } +} + +void TestRunner::syscall() +{ + const QString app("syscall"); + const QString binary = runTestBinary(app + QDir::separator() + app); + 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)); + QCOMPARE(stack.frames().count(), 3); + + { + ///TODO: is this platform specific? + const Frame frame = stack.frames().at(0); + QCOMPARE(frame.functionName(), QString("_Exit")); + } + { + const Frame frame = stack.frames().at(1); + QCOMPARE(frame.functionName(), QString("exit")); + } + { + const Frame frame = stack.frames().at(2); + QCOMPARE(frame.functionName(), QString("(below main)")); + } + } + //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(), QString("main")); + QCOMPARE(frame.line(), 2); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDir); + } +} + +void TestRunner::free1() +{ + const QString app("free1"); + const QString binary = runTestBinary(app + QDir::separator() + app); + 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(), QString("operator delete(void*)")); + } + { + const Frame frame = stack.frames().last(); + QCOMPARE(frame.functionName(), QString("main")); + QCOMPARE(frame.line(), 7); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), 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(), QString("operator delete(void*)")); + } + { + const Frame frame = stack.frames().last(); + QCOMPARE(frame.functionName(), QString("main")); + QCOMPARE(frame.line(), 6); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDir); + } + } +} + +void TestRunner::free2() +{ + const QString app("free2"); + const QString binary = runTestBinary(app + QDir::separator() + app); + 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(), QString("free")); + } + { + const Frame frame = stack.frames().last(); + QCOMPARE(frame.functionName(), QString("main")); + QCOMPARE(frame.line(), 6); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), 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(), QString("operator new(unsigned long)")); + } + { + const Frame frame = stack.frames().last(); + QCOMPARE(frame.functionName(), QString("main")); + QCOMPARE(frame.line(), 5); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDir); + } + } +} + +void TestRunner::invalidjump() +{ + const QString app("invalidjump"); + m_expectCrash = true; + const QString binary = runTestBinary(app + QDir::separator() + app); + const QString srcDir = srcDirForApp(app); + + 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(), QString("(below main)")); + } +} + + +void TestRunner::overlap() +{ + const QString app("overlap"); + m_expectCrash = true; + const QString binary = runTestBinary(app + QDir::separator() + app); + 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); + QCOMPARE(frame.functionName(), QString("memcpy")); + } + { + const Frame frame = stack.frames().last(); + QCOMPARE(frame.functionName(), QString("main")); + QCOMPARE(frame.line(), 6); + + QCOMPARE(frame.object(), binary); + QCOMPARE(frame.file(), QLatin1String("main.cpp")); + QCOMPARE(QDir::cleanPath(frame.directory()), srcDir); + } +} diff --git a/tests/valgrind/memcheck/testrunner.h b/tests/valgrind/memcheck/testrunner.h new file mode 100644 index 00000000000..64d0d00ad3d --- /dev/null +++ b/tests/valgrind/memcheck/testrunner.h @@ -0,0 +1,98 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Author: Milian Wolff, KDAB (milian.wolff@kdab.com) +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** No Commercial Usage +** +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +**************************************************************************/ + +#ifndef TESTRUNNER_H +#define TESTRUNNER_H + +#include <QObject> +#include <QStringList> + +#include <valgrind/xmlprotocol/error.h> + +namespace Valgrind { + +namespace XmlProtocol { +class ThreadedParser; +} + +namespace Memcheck { +class MemcheckRunner; +} + +class TestRunner : public QObject +{ + Q_OBJECT + +public: + explicit TestRunner(QObject *parent = 0); + +private Q_SLOTS: + void init(); + void cleanup(); + + void testLeak1(); + void testLeak2(); + void testLeak3(); + void testLeak4(); + + void uninit1(); + void uninit2(); + void uninit3(); + + void free1(); + void free2(); + + void invalidjump(); + void syscall(); + void overlap(); + +private Q_SLOTS: + 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; + Memcheck::MemcheckRunner *m_runner; + QList<QByteArray> m_logMessages; + QList<XmlProtocol::Error> m_errors; + bool m_expectCrash; +}; + +} // namespace Valgrind + +#endif // TESTRUNNER_H diff --git a/tests/valgrind/memcheck/testrunner.pro b/tests/valgrind/memcheck/testrunner.pro new file mode 100644 index 00000000000..03af4451984 --- /dev/null +++ b/tests/valgrind/memcheck/testrunner.pro @@ -0,0 +1,18 @@ +TEMPLATE = app +TARGET = testrunner + +macx:CONFIG -= app_bundle + +QT += testlib network + +DEFINES += "TESTRUNNER_SRC_DIR=\"\\\"$$_PRO_FILE_PWD_/testapps\\\"\"" +DEFINES += "TESTRUNNER_APP_DIR=\"\\\"$(PWD)/testapps\\\"\"" + +!win32 { + include(../../../qtcreator.pri) + include(../../../src/libs/valgrind/valgrind.pri) +} + +SOURCES += testrunner.cpp + +HEADERS += testrunner.h diff --git a/tests/valgrind/valgrind.pro b/tests/valgrind/valgrind.pro new file mode 100644 index 00000000000..62bb9ec985f --- /dev/null +++ b/tests/valgrind/valgrind.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS += memcheck -- GitLab