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