tst_dumpers.cpp 237 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Bill King's avatar
Bill King committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
Bill King's avatar
Bill King committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
Bill King's avatar
Bill King committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
Bill King's avatar
Bill King committed
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
Bill King's avatar
Bill King committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
Bill King's avatar
Bill King committed
25

hjk's avatar
hjk committed
26
#include "debuggerprotocol.h"
27
#include "simplifytype.h"
28 29
#include "watchdata.h"
#include "watchutils.h"
30

31
#ifdef Q_OS_WIN
32
#include <utils/environment.h>
33
#ifdef Q_CC_MSVC
34 35 36
#include <utils/qtcprocess.h>
#include <utils/fileutils.h>
#include <utils/synchronousprocess.h>
37
#endif // Q_CC_MSVC
38
#endif // Q_OS_WIN
39

40
#include <QtTest>
41
#include <math.h>
42

43
#define MSKIP_SINGLE(x) do { disarm(); QSKIP(x); } while (0)
44

45
using namespace Debugger;
hjk's avatar
hjk committed
46
using namespace Internal;
47

48
#ifdef Q_CC_MSVC
49

50
// Copied from abstractmsvctoolchain.cpp to avoid plugin dependency.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
static bool generateEnvironmentSettings(Utils::Environment &env,
                                        const QString &batchFile,
                                        const QString &batchArgs,
                                        QMap<QString, QString> &envPairs)
{
    // Create a temporary file name for the output. Use a temporary file here
    // as I don't know another way to do this in Qt...
    // Note, can't just use a QTemporaryFile all the way through as it remains open
    // internally so it can't be streamed to later.
    QString tempOutFile;
    QTemporaryFile* pVarsTempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/XXXXXX.txt"));
    pVarsTempFile->setAutoRemove(false);
    pVarsTempFile->open();
    pVarsTempFile->close();
    tempOutFile = pVarsTempFile->fileName();
    delete pVarsTempFile;

    // Create a batch file to create and save the env settings
    Utils::TempFileSaver saver(QDir::tempPath() + QLatin1String("/XXXXXX.bat"));

    QByteArray call = "call ";
    call += Utils::QtcProcess::quoteArg(batchFile).toLocal8Bit();
    if (!batchArgs.isEmpty()) {
        call += ' ';
        call += batchArgs.toLocal8Bit();
    }
    saver.write(call + "\r\n");

    const QByteArray redirect = "set > " + Utils::QtcProcess::quoteArg(
                                    QDir::toNativeSeparators(tempOutFile)).toLocal8Bit() + "\r\n";
    saver.write(redirect);
    if (!saver.finalize()) {
        qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString()));
        return false;
    }

    Utils::QtcProcess run;
    // As of WinSDK 7.1, there is logic preventing the path from being set
    // correctly if "ORIGINALPATH" is already set. That can cause problems
    // if Creator is launched within a session set up by setenv.cmd.
    env.unset(QLatin1String("ORIGINALPATH"));
    run.setEnvironment(env);
    const QString cmdPath = QString::fromLocal8Bit(qgetenv("COMSPEC"));
    // Windows SDK setup scripts require command line switches for environment expansion.
    QString cmdArguments = QLatin1String(" /E:ON /V:ON /c \"");
    cmdArguments += QDir::toNativeSeparators(saver.fileName());
    cmdArguments += QLatin1Char('"');
    run.setCommand(cmdPath, cmdArguments);
    run.start();

    if (!run.waitForStarted()) {
        qWarning("%s: Unable to run '%s': %s", Q_FUNC_INFO, qPrintable(batchFile),
                 qPrintable(run.errorString()));
        return false;
    }
    if (!run.waitForFinished()) {
        qWarning("%s: Timeout running '%s'", Q_FUNC_INFO, qPrintable(batchFile));
        Utils::SynchronousProcess::stopProcess(run);
        return false;
    }
    // The SDK/MSVC scripts do not return exit codes != 0. Check on stdout.
    const QByteArray stdOut = run.readAllStandardOutput();
    if (!stdOut.isEmpty() && (stdOut.contains("Unknown") || stdOut.contains("Error")))
        qWarning("%s: '%s' reports:\n%s", Q_FUNC_INFO, call.constData(), stdOut.constData());

    //
    // Now parse the file to get the environment settings
    QFile varsFile(tempOutFile);
    if (!varsFile.open(QIODevice::ReadOnly))
        return false;

    QRegExp regexp(QLatin1String("(\\w*)=(.*)"));
    while (!varsFile.atEnd()) {
        const QString line = QString::fromLocal8Bit(varsFile.readLine()).trimmed();
        if (regexp.exactMatch(line)) {
            const QString varName = regexp.cap(1);
            const QString varValue = regexp.cap(2);

            if (!varValue.isEmpty())
                envPairs.insert(varName, varValue);
        }
    }

    // Tidy up and remove the file
    varsFile.close();
    varsFile.remove();

    return true;
}

141 142 143 144 145

#ifndef CDBEXT_PATH
#define CDBEXT_PATH ""
#endif

146 147 148 149 150 151 152 153
static void setupCdb(QString *makeBinary, QProcessEnvironment *environment)
{
    QByteArray envBat = qgetenv("QTC_MSVC_ENV_BAT");
    QMap <QString, QString> envPairs;
    Utils::Environment env = Utils::Environment::systemEnvironment();
    QVERIFY(generateEnvironmentSettings(env, QString::fromLatin1(envBat), QString(), envPairs));
    for (QMap<QString,QString>::const_iterator envIt = envPairs.begin(); envIt != envPairs.end(); ++envIt)
            env.set(envIt.key(), envIt.value());
154
    const QByteArray cdbextPath = CDBEXT_PATH "\\qtcreatorcdbext64";
155
    QVERIFY(QFile::exists(QString::fromLatin1(cdbextPath + QByteArray("\\qtcreatorcdbext.dll"))));
156
    env.set(QLatin1String("_NT_DEBUGGER_EXTENSION_PATH"), QString::fromLatin1(cdbextPath));
Orgad Shaneh's avatar
Orgad Shaneh committed
157
    *makeBinary = env.searchInPath(QLatin1String("nmake.exe")).toString();
158 159 160 161 162 163 164 165 166 167
    *environment = env.toProcessEnvironment();
}

#else

static void setupCdb(QString *, QProcessEnvironment *) {}

#endif // Q_CC_MSVC


hjk's avatar
hjk committed
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
struct VersionBase
{
    // Minimum and maximum are inclusive.
    VersionBase(int minimum = 0, int maximum = INT_MAX)
    {
        isRestricted = minimum != 0 || maximum != INT_MAX;
        max = maximum;
        min = minimum;
    }

    bool covers(int what) const
    {
        return !isRestricted || (min <= what && what <= max);
    }

    bool isRestricted;
    int min;
    int max;
};

struct QtVersion : VersionBase
{
    QtVersion(int minimum = 0, int maximum = INT_MAX)
        : VersionBase(minimum, maximum)
    {}
};

struct GccVersion : VersionBase
{
    GccVersion(int minimum = 0, int maximum = INT_MAX)
        : VersionBase(minimum, maximum)
    {}
};

202 203 204 205 206 207 208
struct ClangVersion : VersionBase
{
    ClangVersion(int minimum = 0, int maximum = INT_MAX)
        : VersionBase(minimum, maximum)
    {}
};

hjk's avatar
hjk committed
209 210 211 212 213 214 215 216 217 218 219 220 221 222
struct GdbVersion : VersionBase
{
    GdbVersion(int minimum = 0, int maximum = INT_MAX)
        : VersionBase(minimum, maximum)
    {}
};

struct LldbVersion : VersionBase
{
    LldbVersion(int minimum = 0, int maximum = INT_MAX)
        : VersionBase(minimum, maximum)
    {}
};

223 224 225 226 227 228 229
struct BoostVersion : VersionBase
{
    BoostVersion(int minimum = 0, int maximum = INT_MAX)
        : VersionBase(minimum, maximum)
    {}
};

230 231
static QByteArray noValue = "\001";

232 233 234 235 236 237 238 239 240 241
static QString toHex(const QString &str)
{
    QString encoded;
    foreach (const QChar c, str) {
        encoded += QString::fromLatin1("%1")
            .arg(c.unicode(), 2, 16, QLatin1Char('0'));
    }
    return encoded;
}

hjk's avatar
hjk committed
242 243
struct Context
{
244
    Context() : qtVersion(0), gccVersion(0), clangVersion(0), boostVersion(0) {}
hjk's avatar
hjk committed
245 246 247

    QByteArray nameSpace;
    int qtVersion;
hjk's avatar
hjk committed
248
    int gccVersion;
249
    int clangVersion;
250
    int boostVersion;
hjk's avatar
hjk committed
251 252
};

hjk's avatar
hjk committed
253 254
struct Name
{
hjk's avatar
hjk committed
255 256 257 258 259
    Name() : name(noValue) {}
    Name(const char *str) : name(str) {}
    Name(const QByteArray &ba) : name(ba) {}

    bool matches(const QByteArray &actualName0, const Context &context) const
hjk's avatar
hjk committed
260 261 262
    {
        QByteArray actualName = actualName0;
        QByteArray expectedName = name;
hjk's avatar
hjk committed
263
        expectedName.replace("@Q", context.nameSpace + 'Q');
hjk's avatar
hjk committed
264 265 266 267 268 269 270
        return actualName == expectedName;
    }

    QByteArray name;
};

static Name nameFromIName(const QByteArray &iname)
hjk's avatar
hjk committed
271 272
{
    int pos = iname.lastIndexOf('.');
hjk's avatar
hjk committed
273
    return Name(pos == -1 ? iname : iname.mid(pos + 1));
hjk's avatar
hjk committed
274 275 276 277 278 279 280 281
}

static QByteArray parentIName(const QByteArray &iname)
{
    int pos = iname.lastIndexOf('.');
    return pos == -1 ? QByteArray() : iname.left(pos);
}

hjk's avatar
hjk committed
282
struct ValueBase
283
{
hjk's avatar
hjk committed
284 285 286 287
    ValueBase()
      : hasPtrSuffix(false), isFloatValue(false), substituteNamespace(true),
        qtVersion(0), minimalGccVersion(0)
    {}
hjk's avatar
hjk committed
288 289

    bool hasPtrSuffix;
290
    bool isFloatValue;
291
    bool substituteNamespace;
hjk's avatar
hjk committed
292 293
    int qtVersion;
    int minimalGccVersion;
hjk's avatar
hjk committed
294 295 296 297
};

struct Value : public ValueBase
{
298 299 300 301
    Value() : value(QString::fromLatin1(noValue)) {}
    Value(const char *str) : value(QLatin1String(str)) {}
    Value(const QByteArray &ba) : value(QString::fromLatin1(ba.data(), ba.size())) {}
    Value(const QString &str) : value(str) {}
302

hjk's avatar
hjk committed
303
    bool matches(const QString &actualValue0, const Context &context) const
304
    {
305
        if (value == QString::fromLatin1(noValue))
306
            return true;
hjk's avatar
hjk committed
307 308

        if (context.qtVersion) {
hjk's avatar
hjk committed
309
            if (qtVersion == 4) {
hjk's avatar
hjk committed
310 311 312 313
                if (context.qtVersion < 0x40000 || context.qtVersion >= 0x50000) {
                    //QWARN("Qt 4 specific case skipped");
                    return true;
                }
hjk's avatar
hjk committed
314
            } else if (qtVersion == 5) {
hjk's avatar
hjk committed
315 316 317 318 319 320
                if (context.qtVersion < 0x50000 || context.qtVersion >= 0x60000) {
                    //QWARN("Qt 5 specific case skipped");
                    return true;
                }
            }
        }
hjk's avatar
hjk committed
321 322 323 324 325 326
        if (minimalGccVersion && context.gccVersion) {
            if (minimalGccVersion >= context.gccVersion) {
                //QWARN("Current GCC is too old for this test.")
                return true;
            }
        }
327 328 329
        QString actualValue = actualValue0;
        if (actualValue == QLatin1String(" "))
            actualValue.clear(); // FIXME: Remove later.
330
        QString expectedValue = value;
331 332
        if (substituteNamespace)
            expectedValue.replace(QLatin1Char('@'), QString::fromLatin1(context.nameSpace));
hjk's avatar
hjk committed
333

334
        if (hasPtrSuffix)
335 336
            return actualValue.startsWith(expectedValue + QLatin1String(" @0x"))
                || actualValue.startsWith(expectedValue + QLatin1String("@0x"));
337 338 339 340 341 342 343 344 345 346 347 348

        if (isFloatValue) {
            double f1 = fabs(expectedValue.toDouble());
            double f2 = fabs(actualValue.toDouble());
            //qDebug() << "expected float: " << qPrintable(expectedValue) << f1;
            //qDebug() << "actual   float: " << qPrintable(actualValue) << f2;
            if (f1 < f2)
                std::swap(f1, f2);
            return f1 - f2 <= 0.01 * f2;
        }


349
        return actualValue == expectedValue;
350 351
    }

hjk's avatar
hjk committed
352 353
    void setMinimalGccVersion(int version) { minimalGccVersion = version; }

354
    QString value;
355 356 357 358 359 360 361 362
};

struct Pointer : Value
{
    Pointer() { hasPtrSuffix = true; }
    Pointer(const QByteArray &value) : Value(value) { hasPtrSuffix = true; }
};

363 364 365 366 367 368
struct FloatValue : Value
{
    FloatValue() { isFloatValue = true; }
    FloatValue(const QByteArray &value) : Value(value) { isFloatValue = true; }
};

hjk's avatar
hjk committed
369 370
struct Value4 : Value
{
hjk's avatar
hjk committed
371
    Value4(const QByteArray &value) : Value(value) { qtVersion = 4; }
hjk's avatar
hjk committed
372 373 374 375
};

struct Value5 : Value
{
hjk's avatar
hjk committed
376
    Value5(const QByteArray &value) : Value(value) { qtVersion = 5; }
hjk's avatar
hjk committed
377 378
};

379 380 381 382 383
struct UnsubstitutedValue : Value
{
    UnsubstitutedValue(const QByteArray &value) : Value(value) { substituteNamespace = false; }
};

384 385
struct Optional {};

386 387
struct Type
{
388 389 390
    Type() : qtVersion(0), isPattern(false) {}
    Type(const char *str) : type(str), qtVersion(0), isPattern(false) {}
    Type(const QByteArray &ba) : type(ba), qtVersion(0), isPattern(false) {}
hjk's avatar
hjk committed
391

392
    bool matches(const QByteArray &actualType0, const Context &context, bool fullNamespaceMatch = true) const
393
    {
394
        if (context.qtVersion) {
hjk's avatar
hjk committed
395
            if (qtVersion == 4) {
396 397 398 399
                if (context.qtVersion < 0x40000 || context.qtVersion >= 0x50000) {
                    //QWARN("Qt 4 specific case skipped");
                    return true;
                }
hjk's avatar
hjk committed
400
            } else if (qtVersion == 5) {
401 402 403 404 405 406
                if (context.qtVersion < 0x50000 || context.qtVersion >= 0x60000) {
                    //QWARN("Qt 5 specific case skipped");
                    return true;
                }
            }
        }
407
        QByteArray actualType =
408
            simplifyType(QString::fromLatin1(actualType0)).toLatin1();
409
        actualType.replace(' ', "");
hjk's avatar
hjk committed
410
        actualType.replace("const", "");
411 412
        QByteArray expectedType = type;
        expectedType.replace(' ', "");
413
        expectedType.replace("const", "");
hjk's avatar
hjk committed
414
        expectedType.replace('@', context.nameSpace);
415

416 417 418 419 420
        if (isPattern) {
            QString actual = QString::fromLatin1(actualType);
            QString expected = QString::fromLatin1(expectedType);
            return QRegExp(expected).exactMatch(actual);
        }
421

422 423 424 425 426
        if (fullNamespaceMatch)
            expectedType.replace('?', context.nameSpace);
        else
            expectedType.replace('?', "");

427 428 429 430 431 432 433 434 435 436 437
        if (actualType == expectedType)
            return true;

        // LLDB 3.7 on Linux doesn't get the namespace right in QMapNode:
        // t = lldb.target.FindFirstType('Myns::QMapNode<int, CustomStruct>')
        // t.GetName() -> QMapNode<int, CustomStruct> (no Myns::)
        // So try again without namespace.
        if (fullNamespaceMatch)
            return matches(actualType0, context, false);

        return false;
438
    }
439

440
    QByteArray type;
hjk's avatar
hjk committed
441
    int qtVersion;
442
    bool isPattern;
443 444 445 446
};

struct Type4 : Type
{
hjk's avatar
hjk committed
447
    Type4(const QByteArray &ba) : Type(ba) { qtVersion = 4; }
448 449 450 451
};

struct Type5 : Type
{
hjk's avatar
hjk committed
452
    Type5(const QByteArray &ba) : Type(ba) { qtVersion = 5; }
453 454
};

455 456 457 458 459
struct Pattern : Type
{
    Pattern(const QByteArray &ba) : Type(ba) { isPattern = true; }
};

460 461
enum DebuggerEngine
{
hjk's avatar
hjk committed
462 463 464 465 466 467 468 469 470
    GdbEngine = 0x01,
    CdbEngine = 0x02,
    LldbEngine = 0x04,

    AllEngines = GdbEngine | CdbEngine | LldbEngine,

    NoCdbEngine = AllEngines & (~CdbEngine),
    NoLldbEngine = AllEngines & (~LldbEngine),
    NoGdbEngine = AllEngines & (~GdbEngine)
471 472 473 474
};

struct CheckBase
{
475
    CheckBase() : enginesForCheck(AllEngines), optionallyPresent(false) {}
476
    mutable int enginesForCheck;
hjk's avatar
hjk committed
477
    mutable VersionBase debuggerVersionForCheck;
478 479
    mutable VersionBase gccVersionForCheck;
    mutable VersionBase clangVersionForCheck;
hjk's avatar
hjk committed
480
    mutable QtVersion qtVersionForCheck;
481
    mutable BoostVersion boostVersionForCheck;
482
    mutable bool optionallyPresent;
483 484 485
};

struct Check : CheckBase
486 487 488
{
    Check() {}

hjk's avatar
hjk committed
489
    Check(const QByteArray &iname, const Value &value, const Type &type)
hjk's avatar
hjk committed
490
        : iname(iname), expectedName(nameFromIName(iname)),
491
          expectedValue(value), expectedType(type)
hjk's avatar
hjk committed
492 493
    {}

hjk's avatar
hjk committed
494
    Check(const QByteArray &iname, const Name &name,
495
         const Value &value, const Type &type)
496
        : iname(iname), expectedName(name),
497
          expectedValue(value), expectedType(type)
498 499
    {}

500
    bool matches(DebuggerEngine engine, int debuggerVersion, const Context &context) const
501
    {
hjk's avatar
hjk committed
502 503
        return (engine & enginesForCheck)
            && debuggerVersionForCheck.covers(debuggerVersion)
504 505 506
            && gccVersionForCheck.covers(context.gccVersion)
            && clangVersionForCheck.covers(context.clangVersion)
            && qtVersionForCheck.covers(context.qtVersion);
507 508
    }

509 510 511 512 513 514
    const Check &operator%(Optional) const
    {
        optionallyPresent = true;
        return *this;
    }

hjk's avatar
hjk committed
515
    const Check &operator%(DebuggerEngine engine)
516
    {
hjk's avatar
hjk committed
517
        enginesForCheck = engine;
518
        return *this;
519
    }
520

hjk's avatar
hjk committed
521 522 523 524 525 526 527
    const Check &operator%(GdbVersion version)
    {
        enginesForCheck = GdbEngine;
        debuggerVersionForCheck = version;
        return *this;
    }

528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
    const Check &operator%(LldbVersion version)
    {
        enginesForCheck = LldbEngine;
        debuggerVersionForCheck = version;
        return *this;
    }

    const Check &operator%(GccVersion version)
    {
        enginesForCheck = GdbEngine;
        gccVersionForCheck = version;
        return *this;
    }

    const Check &operator%(ClangVersion version)
    {
        enginesForCheck = GdbEngine;
        clangVersionForCheck = version;
        return *this;
    }

549 550 551 552 553 554
    const Check &operator%(BoostVersion version)
    {
        boostVersionForCheck = version;
        return *this;
    }

555
    QByteArray iname;
hjk's avatar
hjk committed
556
    Name expectedName;
557
    Value expectedValue;
558
    Type expectedType;
559 560
};

hjk's avatar
hjk committed
561
struct CheckType : public Check
562
{
hjk's avatar
hjk committed
563
    CheckType(const QByteArray &iname, const Name &name,
564
         const Type &type)
565 566
        : Check(iname, name, noValue, type)
    {}
567

568
    CheckType(const QByteArray &iname, const Type &type)
hjk's avatar
hjk committed
569 570 571 572
        : Check(iname, noValue, type)
    {}
};

hjk's avatar
hjk committed
573 574 575
const QtVersion Qt4 = QtVersion(0, 0x4ffff);
const QtVersion Qt5 = QtVersion(0x50000);

576 577 578
struct Check4 : Check
{
    Check4(const QByteArray &iname, const Value &value, const Type &type)
hjk's avatar
hjk committed
579
        : Check(iname, value, type) { qtVersionForCheck = Qt4; }
580 581

    Check4(const QByteArray &iname, const Name &name, const Value &value, const Type &type)
hjk's avatar
hjk committed
582
        : Check(iname, name, value, type) { qtVersionForCheck = Qt4; }
583 584 585 586 587
};

struct Check5 : Check
{
    Check5(const QByteArray &iname, const Value &value, const Type &type)
hjk's avatar
hjk committed
588
        : Check(iname, value, type) { qtVersionForCheck = Qt5; }
589 590

    Check5(const QByteArray &iname, const Name &name, const Value &value, const Type &type)
hjk's avatar
hjk committed
591
        : Check(iname, name, value, type) { qtVersionForCheck = Qt5; }
592 593 594

};

hjk's avatar
hjk committed
595
struct Profile
hjk's avatar
hjk committed
596 597 598 599
{
    Profile(const QByteArray &contents) : contents(contents) {}

    QByteArray contents;
600 601
};

hjk's avatar
hjk committed
602

hjk's avatar
hjk committed
603 604
struct Cxx11Profile : public Profile
{
605 606 607 608 609 610
    Cxx11Profile()
      : Profile("greaterThan(QT_MAJOR_VERSION,4): CONFIG += c++11\n"
                "else: QMAKE_CXXFLAGS += -std=c++0x\n")
    {}
};

611 612 613
struct BoostProfile : public Profile
{
    BoostProfile()
614 615 616 617 618 619 620 621
      : Profile(QByteArray())
    {
        const QByteArray &boostIncPath = qgetenv("QTC_BOOST_INCLUDE_PATH_FOR_TEST");
        if (!boostIncPath.isEmpty())
            contents = QByteArray("INCLUDEPATH += ") + boostIncPath.constData();
        else
            contents = "macx:INCLUDEPATH += /usr/local/include";
    }
622 623
};

624
struct MacLibCppProfile : public Profile
625
{
626
    MacLibCppProfile()
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
      : Profile("macx {\n"
                "QMAKE_CXXFLAGS += -stdlib=libc++\n"
                "LIBS += -stdlib=libc++\n"
                "QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7\n"
                "QMAKE_IOS_DEPLOYMENT_TARGET = 10.7\n"
                "QMAKE_CFLAGS -= -mmacosx-version-min=10.6\n"
                "QMAKE_CFLAGS += -mmacosx-version-min=10.7\n"
                "QMAKE_CXXFLAGS -= -mmacosx-version-min=10.6\n"
                "QMAKE_CXXFLAGS += -mmacosx-version-min=10.7\n"
                "QMAKE_OBJECTIVE_CFLAGS -= -mmacosx-version-min=10.6\n"
                "QMAKE_OBJECTIVE_CFLAGS += -mmacosx-version-min=10.7\n"
                "QMAKE_LFLAGS -= -mmacosx-version-min=10.6\n"
                "QMAKE_LFLAGS += -mmacosx-version-min=10.7\n"
                "}")
    {}
hjk's avatar
hjk committed
642 643
};

644
struct ForceC {};
hjk's avatar
hjk committed
645
struct EigenProfile {};
646
struct UseDebugImage {};
647

hjk's avatar
hjk committed
648 649 650
struct CoreProfile {};
struct CorePrivateProfile {};
struct GuiProfile {};
hjk's avatar
hjk committed
651
struct NetworkProfile {};
hjk's avatar
hjk committed
652

653 654
struct BigArrayProfile {};

hjk's avatar
hjk committed
655 656
struct DataBase
{
657 658
    DataBase()
      : useQt(false), useQHash(false),
hjk's avatar
hjk committed
659
        forceC(false), engines(AllEngines),
660 661
        glibcxxDebug(false), useDebugImage(false),
        bigArray(false)
662
    {}
hjk's avatar
hjk committed
663 664

    mutable bool useQt;
665
    mutable bool useQHash;
hjk's avatar
hjk committed
666
    mutable bool forceC;
667
    mutable int engines;
668
    mutable bool glibcxxDebug;
669
    mutable bool useDebugImage;
670
    mutable bool bigArray;
671
    mutable GdbVersion neededGdbVersion;     // DEC. 70600
hjk's avatar
hjk committed
672
    mutable LldbVersion neededLldbVersion;
673
    mutable QtVersion neededQtVersion;       // HEX! 0x50300
674
    mutable GccVersion neededGccVersion;     // DEC. 40702  for 4.7.2
675
    mutable ClangVersion neededClangVersion; // DEC.
676
    mutable BoostVersion neededBoostVersion; // DEC. 105400 for 1.54.0
hjk's avatar
hjk committed
677 678 679
};

class Data : public DataBase
680
{
hjk's avatar
hjk committed
681 682
public:
    Data() {}
hjk's avatar
hjk committed
683
    Data(const QByteArray &code) : code(code) {}
684

685
    Data(const QByteArray &includes, const QByteArray &code)
hjk's avatar
hjk committed
686
        : includes(includes), code(code)
hjk's avatar
hjk committed
687
    {}
688

hjk's avatar
hjk committed
689
    const Data &operator+(const Check &check) const
690
    {
691
        checks.append(check);
692 693 694
        return *this;
    }

hjk's avatar
hjk committed
695
    const Data &operator+(const Profile &profile) const
hjk's avatar
hjk committed
696 697 698 699 700
    {
        profileExtra += profile.contents;
        return *this;
    }

hjk's avatar
hjk committed
701
    const Data &operator+(const QtVersion &qtVersion) const
702 703 704 705 706
    {
        neededQtVersion = qtVersion;
        return *this;
    }

hjk's avatar
hjk committed
707
    const Data &operator+(const GccVersion &gccVersion) const
708 709 710 711 712
    {
        neededGccVersion = gccVersion;
        return *this;
    }

713 714 715 716 717 718
    const Data &operator+(const ClangVersion &clangVersion) const
    {
        neededClangVersion = clangVersion;
        return *this;
    }

hjk's avatar
hjk committed
719
    const Data &operator+(const GdbVersion &gdbVersion) const
720 721 722 723 724
    {
        neededGdbVersion = gdbVersion;
        return *this;
    }

hjk's avatar
hjk committed
725
    const Data &operator+(const LldbVersion &lldbVersion) const
hjk's avatar
hjk committed
726 727 728 729 730
    {
        neededLldbVersion = lldbVersion;
        return *this;
    }

731 732 733 734 735 736
    const Data &operator+(const BoostVersion &boostVersion) const
    {
        neededBoostVersion = boostVersion;
        return *this;
    }

hjk's avatar
hjk committed
737
    const Data &operator+(const DebuggerEngine &enginesForTest) const
hjk's avatar
hjk committed
738
    {
739
        engines = enginesForTest;
hjk's avatar
hjk committed
740 741 742
        return *this;
    }

hjk's avatar
hjk committed
743
    const Data &operator+(const EigenProfile &) const
hjk's avatar
hjk committed
744 745
    {
        profileExtra +=
hjk's avatar
hjk committed
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
            "exists(/usr/include/eigen3/Eigen/Core) {\n"
            "    DEFINES += HAS_EIGEN3\n"
            "    INCLUDEPATH += /usr/include/eigen3\n"
            "}\n"
            "exists(/usr/local/include/eigen3/Eigen/Core) {\n"
            "    DEFINES += HAS_EIGEN3\n"
            "    INCLUDEPATH += /usr/local/include/eigen3\n"
            "}\n"
            "\n"
            "exists(/usr/include/eigen2/Eigen/Core) {\n"
            "    DEFINES += HAS_EIGEN2\n"
            "    INCLUDEPATH += /usr/include/eigen2\n"
            "}\n"
            "exists(/usr/local/include/eigen2/Eigen/Core) {\n"
            "    DEFINES += HAS_EIGEN2\n"
            "    INCLUDEPATH += /usr/local/include/eigen2\n"
            "}\n";
hjk's avatar
hjk committed
763 764 765 766

        return *this;
    }

hjk's avatar
hjk committed
767
    const Data &operator+(const UseDebugImage &) const
768 769 770 771 772
    {
        useDebugImage = true;
        return *this;
    }

hjk's avatar
hjk committed
773
    const Data &operator+(const CoreProfile &) const
hjk's avatar
hjk committed
774 775 776
    {
        profileExtra +=
            "CONFIG += QT\n"
777
            "QT += core\n";
hjk's avatar
hjk committed
778 779

        useQt = true;
780 781
        useQHash = true;

hjk's avatar
hjk committed
782 783 784
        return *this;
    }

hjk's avatar
hjk committed
785
    const Data &operator+(const NetworkProfile &) const
hjk's avatar
hjk committed
786 787 788 789 790 791 792 793 794 795 796
    {
        profileExtra +=
            "CONFIG += QT\n"
            "QT += core network\n";

        useQt = true;
        useQHash = true;

        return *this;
    }

797 798 799 800 801 802
    const Data &operator+(const BigArrayProfile &) const
    {
        this->bigArray = true;
        return *this;
    }

hjk's avatar
hjk committed
803
    const Data &operator+(const GuiProfile &) const
hjk's avatar
hjk committed
804
    {
hjk's avatar
hjk committed
805
        this->operator+(CoreProfile());
hjk's avatar
hjk committed
806 807 808 809 810 811 812
        profileExtra +=
            "QT += gui\n"
            "greaterThan(QT_MAJOR_VERSION, 4):QT *= widgets\n";

        return *this;
    }

hjk's avatar
hjk committed
813
    const Data &operator+(const CorePrivateProfile &) const
hjk's avatar
hjk committed
814
    {
hjk's avatar
hjk committed
815
        this->operator+(CoreProfile());
hjk's avatar
hjk committed
816 817
        profileExtra +=
            "greaterThan(QT_MAJOR_VERSION, 4) {\n"
818
            "  QT += core-private\n"
hjk's avatar
hjk committed
819 820 821 822 823 824
            "  CONFIG += no_private_qt_headers_warning\n"
            "}";

        return *this;
    }

hjk's avatar
hjk committed
825
    const Data &operator+(const ForceC &) const
826 827 828 829 830
    {
        forceC = true;
        return *this;
    }

hjk's avatar
hjk committed
831
public:
hjk's avatar
hjk committed
832
    mutable QByteArray profileExtra;
833 834
    mutable QByteArray includes;
    mutable QByteArray code;
835
    mutable QList<Check> checks;
hjk's avatar
hjk committed
836 837
};

838 839
struct TempStuff
{
840 841 842
    TempStuff(const char *tag) : buildTemp(QLatin1String("qt_tst_dumpers_")
                                           + QLatin1String(tag)
                                           + QLatin1Char('_'))
843
    {
844
        buildPath = QDir::currentPath() + QLatin1Char('/')  + buildTemp.path();
845 846 847 848 849 850 851 852 853
        buildTemp.setAutoRemove(false);
        QVERIFY(!buildPath.isEmpty());
    }

    QByteArray input;
    QTemporaryDir buildTemp;
    QString buildPath;
};

hjk's avatar
hjk committed
854
Q_DECLARE_METATYPE(Data)
hjk's avatar
hjk committed
855

856
class tst_Dumpers : public QObject
857 858 859 860
{
    Q_OBJECT

public:
861 862 863
    tst_Dumpers()
    {
        t = 0;
864
        m_keepTemp = true;
865
        m_forceKeepTemp = false;
hjk's avatar
hjk committed
866
        m_debuggerVersion = 0;
867
        m_gdbBuildVersion = 0;
868
        m_qtVersion = 0;
869
        m_gccVersion = 0;
870 871
        m_isMacGdb = false;
        m_isQnxGdb = false;
872
        m_useGLibCxxDebug = false;
873
    }
874 875

private slots:
hjk's avatar
hjk committed
876
    void initTestCase();
hjk's avatar
hjk committed
877 878
    void dumper();
    void dumper_data();
879 880
    void init();
    void cleanup();
hjk's avatar
hjk committed
881 882

private:
883
    void disarm() { t->buildTemp.setAutoRemove(!keepTemp()); }
884
    bool keepTemp() const { return m_keepTemp || m_forceKeepTemp; }
885
    TempStuff *t;
hjk's avatar
hjk committed
886 887
    QByteArray m_debuggerBinary;
    QByteArray m_qmakeBinary;
888
    QProcessEnvironment m_env;
889
    DebuggerEngine m_debuggerEngine;
890
    QString m_makeBinary;
hjk's avatar
hjk committed
891
    bool m_keepTemp;
892
    bool m_forceKeepTemp;
hjk's avatar
hjk committed
893
    int m_debuggerVersion; // GDB: 7.5.1 -> 70501
894
    int m_gdbBuildVersion;
895
    int m_qtVersion; // 5.2.0 -> 50200
896
    int m_gccVersion;
897 898
    bool m_isMacGdb;
    bool m_isQnxGdb;
899
    bool m_useGLibCxxDebug;
hjk's avatar
hjk committed
900 901 902 903
};

void tst_Dumpers::initTestCase()
{
904
    m_debuggerBinary = qgetenv("QTC_DEBUGGER_PATH_FOR_TEST");
905 906 907 908
    if (m_debuggerBinary.isEmpty()) {
#ifdef Q_OS_MAC
        m_debuggerBinary = "/Applications/Xcode.app/Contents/Developer/usr/bin/lldb";
#else
hjk's avatar
hjk committed
909
        m_debuggerBinary = "gdb";
910 911
#endif
    }
912
    qDebug() << "Debugger           : " << m_debuggerBinary.constData();
913

hjk's avatar
hjk committed
914
    m_debuggerEngine = GdbEngine;
915
    if (m_debuggerBinary.endsWith("cdb.exe"))
hjk's avatar
hjk committed
916
        m_debuggerEngine = CdbEngine;
917

918 919
    QString base = QFileInfo(QString::fromLatin1(m_debuggerBinary)).baseName();
    if (base.startsWith(QLatin1String("lldb")))
hjk's avatar
hjk committed
920
        m_debuggerEngine = LldbEngine;
hjk's avatar
hjk committed
921

hjk's avatar
hjk committed
922
    m_qmakeBinary = qgetenv("QTC_QMAKE_PATH_FOR_TEST");
hjk's avatar
hjk committed
923 924
    if (m_qmakeBinary.isEmpty())
        m_qmakeBinary = "qmake";
925 926 927 928 929 930 931
    qDebug() << "QMake              : " << m_qmakeBinary.constData();

    m_useGLibCxxDebug = qgetenv("QTC_USE_GLIBCXXDEBUG_FOR_TEST").toInt();
    qDebug() << "Use _GLIBCXX_DEBUG : " << m_useGLibCxxDebug;

    m_forceKeepTemp = qgetenv("QTC_KEEP_TEMP_FOR_TEST").toInt();
    qDebug() << "Force keep temp    : " << m_forceKeepTemp;
hjk's avatar
hjk committed
932

hjk's avatar
hjk committed
933
    if (m_debuggerEngine == GdbEngine) {
934 935 936 937 938 939 940 941 942
        QProcess debugger;
        debugger.start(QString::fromLatin1(m_debuggerBinary)
                       + QLatin1String(" -i mi -quiet -nx"));
        bool ok = debugger.waitForStarted();
        debugger.write("set confirm off\npython print 43\nshow version\nquit\n");
        ok = debugger.waitForFinished();
        QVERIFY(ok);
        QByteArray output = debugger.readAllStandardOutput();
        //qDebug() << "stdout: " << output;
943 944
        bool usePython = !output.contains("Python scripting is not supported in this copy of GDB");
        qDebug() << "Python             : " << (usePython ? "ok" : "*** not ok ***");
945
        qDebug() << "Dumper dir         : " << DUMPERDIR;
946
        QVERIFY(usePython);
947 948 949 950 951 952 953 954 955

        QString version = QString::fromLocal8Bit(output);
        int pos1 = version.indexOf(QLatin1String("&\"show version\\n"));
        QVERIFY(pos1 != -1);
        pos1 += 20;
        int pos2 = version.indexOf(QLatin1String("~\"Copyright (C) "), pos1);
        QVERIFY(pos2 != -1);
        pos2 -= 4;
        version = version.mid(pos1, pos2 - pos1);
hjk's avatar
hjk committed
956
        extractGdbVersion(version, &m_debuggerVersion,
957
            &m_gdbBuildVersion, &m_isMacGdb, &m_isQnxGdb);
958
        m_env = QProcessEnvironment::systemEnvironment();
959 960
        m_makeBinary = QString::fromLocal8Bit(qgetenv("QTC_MAKE_PATH_FOR_TEST"));
#ifdef Q_OS_WIN
Christian Stenger's avatar
Christian Stenger committed
961
        if (m_makeBinary.isEmpty())
962 963 964 965 966 967 968 969
            m_makeBinary = QLatin1String("mingw32-make");
        // if qmake is not in PATH make sure the correct libs for inferior are prepended to PATH
        if (m_qmakeBinary != "qmake") {
            Utils::Environment env = Utils::Environment::systemEnvironment();
            env.prependOrSetPath(QDir::toNativeSeparators(QFileInfo(QLatin1String(m_qmakeBinary)).absolutePath()));
            m_env = env.toProcessEnvironment();
        }
#else
Christian Stenger's avatar
Christian Stenger committed
970
        if (m_makeBinary.isEmpty())
971 972 973
            m_makeBinary = QLatin1String("make");
#endif
        qDebug() << "Make path          : " << m_makeBinary;
hjk's avatar
hjk committed
974
        qDebug() << "Gdb version        : " << m_debuggerVersion;
hjk's avatar
hjk committed
975
    } else if (m_debuggerEngine == CdbEngine) {
976
        setupCdb(&m_makeBinary, &m_env);
hjk's avatar
hjk committed
977
    } else if (m_debuggerEngine == LldbEngine) {
978
        qDebug() << "Dumper dir         : " << DUMPERDIR;
979
        QProcess debugger;
980 981
        QString cmd = QString::fromUtf8(m_debuggerBinary + " -v");
        debugger.start(cmd);
982 983 984 985 986
        bool ok = debugger.waitForFinished(2000);
        QVERIFY(ok);
        QByteArray output = debugger.readAllStandardOutput();
        output += debugger.readAllStandardError();
        output = output.trimmed();
987 988
        // Should be something like "LLDB-178" (Mac OS X 10.8)
        // or "lldb version 3.5 ( revision )" (Ubuntu 13.10)
989 990 991 992
        QByteArray ba = output.mid(output.indexOf('-') + 1);
        int pos = ba.indexOf('.');
        if (pos >= 0)
            ba = ba.left(pos);
hjk's avatar
hjk committed
993 994
        m_debuggerVersion = ba.toInt();
        if (!m_debuggerVersion) {
995 996 997 998 999
            if (output.startsWith("lldb version")) {
                int pos1 = output.indexOf('.', 13);
                int major = output.mid(13, pos1++ - 13).toInt();
                int pos2 = output.indexOf(' ', pos1);
                int minor = output.mid(pos1, pos2++ - pos1).toInt();
hjk's avatar
hjk committed
1000
                m_debuggerVersion = 100 * major + 10 * minor;
1001 1002 1003
            }
        }

hjk's avatar
hjk committed
1004 1005
        qDebug() << "Lldb version       :" << output << ba << m_debuggerVersion;
        QVERIFY(m_debuggerVersion);
1006 1007 1008

        m_env = QProcessEnvironment::systemEnvironment();
        m_makeBinary = QLatin1String("make");
1009
    }
hjk's avatar
hjk committed
1010 1011
}

1012
void tst_Dumpers::init()
hjk's avatar
hjk committed
1013
{