tst_dumpers.cpp 239 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
{
1014
    t = new TempStuff(QTest::currentDataTag());
hjk's avatar
hjk committed
1015
1016
}

1017
void tst_Dumpers::cleanup()
hjk's avatar
hjk committed
1018
{
1019
1020
    if (!t->buildTemp.autoRemove()) {
        QFile logger(t->buildPath + QLatin1String("/input.txt"));
hjk's avatar
hjk committed
1021
        logger.open(QIODevice::ReadWrite);
1022
        logger.write(t->input);
hjk's avatar
hjk committed
1023
    }
1024
1025
    delete t;
}
1026

hjk's avatar
hjk committed
1027
1028
1029
1030
void tst_Dumpers::dumper()
{
    QFETCH(Data, data);

1031
1032
1033
    if (!(data.engines & m_debuggerEngine))
        MSKIP_SINGLE("The test is excluded for this debugger engine.");

hjk's avatar
hjk committed
1034
    if (data.neededGdbVersion.isRestricted && m_debuggerEngine == GdbEngine) {
hjk's avatar
hjk committed
1035
        if (data.neededGdbVersion.min > m_debuggerVersion)
hjk's avatar
hjk committed
1036
1037
            MSKIP_SINGLE("Need minimum GDB version "
                + QByteArray::number(data.neededGdbVersion.min));
hjk's avatar
hjk committed
1038
        if (data.neededGdbVersion.max < m_debuggerVersion)
hjk's avatar
hjk committed
1039
1040
1041
1042
            MSKIP_SINGLE("Need maximum GDB version "
                + QByteArray::number(data.neededGdbVersion.max));
    }

hjk's avatar
hjk committed
1043
    if (data.neededLldbVersion.isRestricted && m_debuggerEngine == LldbEngine) {
hjk's avatar
hjk committed
1044
        if (data.neededLldbVersion.min > m_debuggerVersion)
hjk's avatar
hjk committed
1045
1046
            MSKIP_SINGLE("Need minimum LLDB version "
                + QByteArray::number(data.neededLldbVersion.min));
hjk's avatar
hjk committed
1047
        if (data.neededLldbVersion.max < m_debuggerVersion)
hjk's avatar
hjk committed
1048
1049
            MSKIP_SINGLE("Need maximum LLDB version "
                + QByteArray::number(data.neededLldbVersion.max));
1050
    }
1051

hjk's avatar
hjk committed
1052
1053
1054
1055
    QString cmd;
    QByteArray output;
    QByteArray error;

1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
    if (data.neededQtVersion.isRestricted) {
        QProcess qmake;
        qmake.setWorkingDirectory(t->buildPath);
        cmd = QString::fromLatin1(m_qmakeBinary);
        qmake.start(cmd, QStringList(QLatin1String("--version")));
        QVERIFY(qmake.waitForFinished());
        output = qmake.readAllStandardOutput();
        error = qmake.readAllStandardError();
        int pos0 = output.indexOf("Qt version");
        if (pos0 == -1) {
            qDebug() << "Output: " << output;
            qDebug() << "Error: " << error;
            QVERIFY(false);
        }
        pos0 += 11;
        int pos1 = output.indexOf('.', pos0 + 1);
        int major = output.mid(pos0, pos1++ - pos0).toInt();
        int pos2 = output.indexOf('.', pos1 + 1);
        int minor = output.mid(pos1, pos2++ - pos1).toInt();
        int pos3 = output.indexOf(' ', pos2 + 1);
        int patch = output.mid(pos2, pos3++ - pos2).toInt();
1077
        m_qtVersion = 0x10000 * major + 0x100 * minor + patch;
1078
1079
1080

        if (data.neededQtVersion.min > m_qtVersion)
            MSKIP_SINGLE("Need minimum Qt version "
1081
                + QByteArray::number(data.neededQtVersion.min, 16));
1082
1083
        if (data.neededQtVersion.max < m_qtVersion)
            MSKIP_SINGLE("Need maximum Qt version "
1084
                + QByteArray::number(data.neededQtVersion.max, 16));
1085
1086
    }

hjk's avatar