tst_dumpers.cpp 228 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Bill King's avatar
Bill King committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3
4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://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
Eike Ziller's avatar
Eike Ziller committed
12
13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
Bill King's avatar
Bill King committed
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
Bill King's avatar
Bill King committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25
26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
Bill King's avatar
Bill King committed
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
Bill King's avatar
Bill King committed
30

hjk's avatar
hjk committed
31
#include "debuggerprotocol.h"
32
#include "simplifytype.h"
33
34
#include "watchdata.h"
#include "watchutils.h"
35

36
#ifdef Q_CC_MSVC
37
#include <utils/environment.h>
38
39
40
#include <utils/qtcprocess.h>
#include <utils/fileutils.h>
#include <utils/synchronousprocess.h>
41
#endif // Q_CC_MSVC
42

43
#include <QtTest>
44
#include <math.h>
45

46
#define MSKIP_SINGLE(x) do { disarm(); QSKIP(x); } while (0)
47

48
using namespace Debugger;
hjk's avatar
hjk committed
49
using namespace Internal;
50

51
#ifdef Q_CC_MSVC
52

53
// Copied from abstractmsvctoolchain.cpp to avoid plugin dependency.
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
141
142
143
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;
}

144
145
146
147
148

#ifndef CDBEXT_PATH
#define CDBEXT_PATH ""
#endif

149
150
151
152
153
154
155
156
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());
157
    const QByteArray cdbextPath = CDBEXT_PATH "\\qtcreatorcdbext64";
158
    QVERIFY(QFile::exists(QString::fromLatin1(cdbextPath + QByteArray("\\qtcreatorcdbext.dll"))));
159
    env.set(QLatin1String("_NT_DEBUGGER_EXTENSION_PATH"), QString::fromLatin1(cdbextPath));
Orgad Shaneh's avatar
Orgad Shaneh committed
160
    *makeBinary = env.searchInPath(QLatin1String("nmake.exe")).toString();
161
162
163
164
165
166
167
168
169
170
    *environment = env.toProcessEnvironment();
}

#else

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

#endif // Q_CC_MSVC


hjk's avatar
hjk committed
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
202
203
204
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)
    {}
};

205
206
207
208
209
210
211
struct ClangVersion : VersionBase
{
    ClangVersion(int minimum = 0, int maximum = INT_MAX)
        : VersionBase(minimum, maximum)
    {}
};

hjk's avatar
hjk committed
212
213
214
215
216
217
218
219
220
221
222
223
224
225
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)
    {}
};

226
227
228
229
230
231
232
struct BoostVersion : VersionBase
{
    BoostVersion(int minimum = 0, int maximum = INT_MAX)
        : VersionBase(minimum, maximum)
    {}
};

233
234
static QByteArray noValue = "\001";

235
236
237
238
239
240
241
242
243
244
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
245
246
struct Context
{
247
    Context() : qtVersion(0), gccVersion(0), clangVersion(0) {}
hjk's avatar
hjk committed
248
249
250

    QByteArray nameSpace;
    int qtVersion;
hjk's avatar
hjk committed
251
    int gccVersion;
252
    int clangVersion;
hjk's avatar
hjk committed
253
254
};

hjk's avatar
hjk committed
255
256
struct Name
{
hjk's avatar
hjk committed
257
258
259
260
261
    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
262
263
264
    {
        QByteArray actualName = actualName0;
        QByteArray expectedName = name;
hjk's avatar
hjk committed
265
        expectedName.replace("@Q", context.nameSpace + 'Q');
hjk's avatar
hjk committed
266
267
268
269
270
271
272
        return actualName == expectedName;
    }

    QByteArray name;
};

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

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

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

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

struct Value : public ValueBase
{
300
301
302
303
    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) {}
304

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

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

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

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


351
        return actualValue == expectedValue;
352
353
    }

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

356
    QString value;
357
358
359
360
361
362
363
364
};

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

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

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

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

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

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) 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
        if (isPattern) {
            QString actual = QString::fromLatin1(actualType);
            QString expected = QString::fromLatin1(expectedType);
            return QRegExp(expected).exactMatch(actual);
        }
420
421
422
        return actualType == expectedType;
    }
    QByteArray type;
hjk's avatar
hjk committed
423
    int qtVersion;
424
    bool isPattern;
425
426
427
428
};

struct Type4 : Type
{
hjk's avatar
hjk committed
429
    Type4(const QByteArray &ba) : Type(ba) { qtVersion = 4; }
430
431
432
433
};

struct Type5 : Type
{
hjk's avatar
hjk committed
434
    Type5(const QByteArray &ba) : Type(ba) { qtVersion = 5; }
435
436
};

437
438
439
440
441
struct Pattern : Type
{
    Pattern(const QByteArray &ba) : Type(ba) { isPattern = true; }
};

442
443
enum DebuggerEngine
{
hjk's avatar
hjk committed
444
445
446
447
448
449
450
451
452
    GdbEngine = 0x01,
    CdbEngine = 0x02,
    LldbEngine = 0x04,

    AllEngines = GdbEngine | CdbEngine | LldbEngine,

    NoCdbEngine = AllEngines & (~CdbEngine),
    NoLldbEngine = AllEngines & (~LldbEngine),
    NoGdbEngine = AllEngines & (~GdbEngine)
453
454
455
456
};

struct CheckBase
{
hjk's avatar
hjk committed
457
    CheckBase() : enginesForCheck(AllEngines) {}
458
    mutable int enginesForCheck;
hjk's avatar
hjk committed
459
    mutable VersionBase debuggerVersionForCheck;
460
461
    mutable VersionBase gccVersionForCheck;
    mutable VersionBase clangVersionForCheck;
hjk's avatar
hjk committed
462
    mutable QtVersion qtVersionForCheck;
463
464
465
};

struct Check : CheckBase
466
467
468
{
    Check() {}

hjk's avatar
hjk committed
469
    Check(const QByteArray &iname, const Value &value, const Type &type)
hjk's avatar
hjk committed
470
        : iname(iname), expectedName(nameFromIName(iname)),
471
          expectedValue(value), expectedType(type)
hjk's avatar
hjk committed
472
473
    {}

hjk's avatar
hjk committed
474
    Check(const QByteArray &iname, const Name &name,
475
         const Value &value, const Type &type)
476
        : iname(iname), expectedName(name),
477
          expectedValue(value), expectedType(type)
478
479
    {}

480
    bool matches(DebuggerEngine engine, int debuggerVersion, const Context &context) const
481
    {
hjk's avatar
hjk committed
482
483
        return (engine & enginesForCheck)
            && debuggerVersionForCheck.covers(debuggerVersion)
484
485
486
            && gccVersionForCheck.covers(context.gccVersion)
            && clangVersionForCheck.covers(context.clangVersion)
            && qtVersionForCheck.covers(context.qtVersion);
487
488
    }

hjk's avatar
hjk committed
489
    const Check &operator%(DebuggerEngine engine)
490
    {
hjk's avatar
hjk committed
491
        enginesForCheck = engine;
492
        return *this;
493
    }
494

hjk's avatar
hjk committed
495
496
497
498
499
500
501
    const Check &operator%(GdbVersion version)
    {
        enginesForCheck = GdbEngine;
        debuggerVersionForCheck = version;
        return *this;
    }

502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
    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;
    }

523
    QByteArray iname;
hjk's avatar
hjk committed
524
    Name expectedName;
525
    Value expectedValue;
526
    Type expectedType;
527
528
};

hjk's avatar
hjk committed
529
struct CheckType : public Check
530
{
hjk's avatar
hjk committed
531
    CheckType(const QByteArray &iname, const Name &name,
532
         const Type &type)
533
534
        : Check(iname, name, noValue, type)
    {}
535

536
    CheckType(const QByteArray &iname, const Type &type)
hjk's avatar
hjk committed
537
538
539
540
        : Check(iname, noValue, type)
    {}
};

hjk's avatar
hjk committed
541
542
543
const QtVersion Qt4 = QtVersion(0, 0x4ffff);
const QtVersion Qt5 = QtVersion(0x50000);

544
545
546
struct Check4 : Check
{
    Check4(const QByteArray &iname, const Value &value, const Type &type)
hjk's avatar
hjk committed
547
        : Check(iname, value, type) { qtVersionForCheck = Qt4; }
548
549

    Check4(const QByteArray &iname, const Name &name, const Value &value, const Type &type)
hjk's avatar
hjk committed
550
        : Check(iname, name, value, type) { qtVersionForCheck = Qt4; }
551
552
553
554
555
};

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

    Check5(const QByteArray &iname, const Name &name, const Value &value, const Type &type)
hjk's avatar
hjk committed
559
        : Check(iname, name, value, type) { qtVersionForCheck = Qt5; }
560
561
562

};

hjk's avatar
hjk committed
563
struct Profile
hjk's avatar
hjk committed
564
565
566
567
{
    Profile(const QByteArray &contents) : contents(contents) {}

    QByteArray contents;
568
569
};

hjk's avatar
hjk committed
570

hjk's avatar
hjk committed
571
572
struct Cxx11Profile : public Profile
{
573
574
575
576
577
578
    Cxx11Profile()
      : Profile("greaterThan(QT_MAJOR_VERSION,4): CONFIG += c++11\n"
                "else: QMAKE_CXXFLAGS += -std=c++0x\n")
    {}
};

579
580
581
582
583
584
585
struct BoostProfile : public Profile
{
    BoostProfile()
      : Profile("macx:INCLUDEPATH += /usr/local/include")
    {}
};

586
struct MacLibCppProfile : public Profile
587
{
588
    MacLibCppProfile()
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
      : 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
604
605
};

606
struct ForceC {};
hjk's avatar
hjk committed
607
struct EigenProfile {};
608
struct UseDebugImage {};
609

hjk's avatar
hjk committed
610
611
612
struct CoreProfile {};
struct CorePrivateProfile {};
struct GuiProfile {};
hjk's avatar
hjk committed
613
struct NetworkProfile {};
hjk's avatar
hjk committed
614

615
616
struct BigArrayProfile {};

hjk's avatar
hjk committed
617
618
struct DataBase
{
619
620
    DataBase()
      : useQt(false), useQHash(false),
hjk's avatar
hjk committed
621
        forceC(false), engines(AllEngines),
622
623
        glibcxxDebug(false), useDebugImage(false),
        bigArray(false)
624
    {}
hjk's avatar
hjk committed
625
626

    mutable bool useQt;
627
    mutable bool useQHash;
hjk's avatar
hjk committed
628
    mutable bool forceC;
629
    mutable int engines;
630
    mutable bool glibcxxDebug;
631
    mutable bool useDebugImage;
632
    mutable bool bigArray;
633
    mutable GdbVersion neededGdbVersion;     // DEC. 70600
hjk's avatar
hjk committed
634
    mutable LldbVersion neededLldbVersion;
635
    mutable QtVersion neededQtVersion;       // HEX! 0x50300
636
    mutable GccVersion neededGccVersion;     // DEC. 40702  for 4.7.2
637
    mutable ClangVersion neededClangVersion; // DEC.
638
    mutable BoostVersion neededBoostVersion; // DEC. 105400 for 1.54.0
hjk's avatar
hjk committed
639
640
641
};

class Data : public DataBase
642
{
hjk's avatar
hjk committed
643
644
public:
    Data() {}
hjk's avatar
hjk committed
645
    Data(const QByteArray &code) : code(code) {}
646

647
    Data(const QByteArray &includes, const QByteArray &code)
hjk's avatar
hjk committed
648
        : includes(includes), code(code)
hjk's avatar
hjk committed
649
    {}
650

hjk's avatar
hjk committed
651
    const Data &operator+(const Check &check) const
652
    {
653
        checks.append(check);
654
655
656
        return *this;
    }

hjk's avatar
hjk committed
657
    const Data &operator+(const Profile &profile) const
hjk's avatar
hjk committed
658
659
660
661
662
    {
        profileExtra += profile.contents;
        return *this;
    }

hjk's avatar
hjk committed
663
    const Data &operator+(const QtVersion &qtVersion) const
664
665
666
667
668
    {
        neededQtVersion = qtVersion;
        return *this;
    }

hjk's avatar
hjk committed
669
    const Data &operator+(const GccVersion &gccVersion) const
670
671
672
673
674
    {
        neededGccVersion = gccVersion;
        return *this;
    }

675
676
677
678
679
680
    const Data &operator+(const ClangVersion &clangVersion) const
    {
        neededClangVersion = clangVersion;
        return *this;
    }

hjk's avatar
hjk committed
681
    const Data &operator+(const GdbVersion &gdbVersion) const
682
683
684
685
686
    {
        neededGdbVersion = gdbVersion;
        return *this;
    }

hjk's avatar
hjk committed
687
    const Data &operator+(const LldbVersion &lldbVersion) const
hjk's avatar
hjk committed
688
689
690
691
692
    {
        neededLldbVersion = lldbVersion;
        return *this;
    }

693
694
695
696
697
698
    const Data &operator+(const BoostVersion &boostVersion) const
    {
        neededBoostVersion = boostVersion;
        return *this;
    }

hjk's avatar
hjk committed
699
    const Data &operator+(const DebuggerEngine &enginesForTest) const
hjk's avatar
hjk committed
700
    {
701
        engines = enginesForTest;
hjk's avatar
hjk committed
702
703
704
        return *this;
    }

hjk's avatar
hjk committed
705
    const Data &operator+(const EigenProfile &) const
hjk's avatar
hjk committed
706
707
    {
        profileExtra +=
hjk's avatar
hjk committed
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
            "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
725
726
727
728

        return *this;
    }

hjk's avatar
hjk committed
729
    const Data &operator+(const UseDebugImage &) const
730
731
732
733
734
    {
        useDebugImage = true;
        return *this;
    }

hjk's avatar
hjk committed
735
    const Data &operator+(const CoreProfile &) const
hjk's avatar
hjk committed
736
737
738
    {
        profileExtra +=
            "CONFIG += QT\n"
739
            "QT += core\n";
hjk's avatar
hjk committed
740
741

        useQt = true;
742
743
        useQHash = true;

hjk's avatar
hjk committed
744
745
746
        return *this;
    }

hjk's avatar
hjk committed
747
    const Data &operator+(const NetworkProfile &) const
hjk's avatar
hjk committed
748
749
750
751
752
753
754
755
756
757
758
    {
        profileExtra +=
            "CONFIG += QT\n"
            "QT += core network\n";

        useQt = true;
        useQHash = true;

        return *this;
    }

759
760
761
762
763
764
    const Data &operator+(const BigArrayProfile &) const
    {
        this->bigArray = true;
        return *this;
    }

hjk's avatar
hjk committed
765
    const Data &operator+(const GuiProfile &) const
hjk's avatar
hjk committed
766
    {
hjk's avatar
hjk committed
767
        this->operator+(CoreProfile());
hjk's avatar
hjk committed
768
769
770
771
772
773
774
        profileExtra +=
            "QT += gui\n"
            "greaterThan(QT_MAJOR_VERSION, 4):QT *= widgets\n";

        return *this;
    }

hjk's avatar
hjk committed
775
    const Data &operator+(const CorePrivateProfile &) const
hjk's avatar
hjk committed
776
    {
hjk's avatar
hjk committed
777
        this->operator+(CoreProfile());
hjk's avatar
hjk committed
778
779
        profileExtra +=
            "greaterThan(QT_MAJOR_VERSION, 4) {\n"
780
            "  QT += core-private\n"
hjk's avatar
hjk committed
781
782
783
784
785
786
            "  CONFIG += no_private_qt_headers_warning\n"
            "}";

        return *this;
    }

hjk's avatar
hjk committed
787
    const Data &operator+(const ForceC &) const
788
789
790
791
792
    {
        forceC = true;
        return *this;
    }

hjk's avatar
hjk committed
793
public:
hjk's avatar
hjk committed
794
    mutable QByteArray profileExtra;
795
796
    mutable QByteArray includes;
    mutable QByteArray code;
797
    mutable QList<Check> checks;
hjk's avatar
hjk committed
798
799
};

800
801
struct TempStuff
{
802
803
804
    TempStuff(const char *tag) : buildTemp(QLatin1String("qt_tst_dumpers_")
                                           + QLatin1String(tag)
                                           + QLatin1Char('_'))
805
    {
806
        buildPath = QDir::currentPath() + QLatin1Char('/')  + buildTemp.path();
807
808
809
810
811
812
813
814
815
        buildTemp.setAutoRemove(false);
        QVERIFY(!buildPath.isEmpty());
    }

    QByteArray input;
    QTemporaryDir buildTemp;
    QString buildPath;
};

hjk's avatar
hjk committed
816
Q_DECLARE_METATYPE(Data)
hjk's avatar
hjk committed
817

818
class tst_Dumpers : public QObject
819
820
821
822
{
    Q_OBJECT

public:
823
824
825
    tst_Dumpers()
    {
        t = 0;
826
        m_keepTemp = true;
827
        m_forceKeepTemp = false;
hjk's avatar
hjk committed
828
        m_debuggerVersion = 0;
829
        m_gdbBuildVersion = 0;
830
        m_qtVersion = 0;
831
        m_gccVersion = 0;
832
833
        m_isMacGdb = false;
        m_isQnxGdb = false;
834
        m_useGLibCxxDebug = false;
835
    }
836
837

private slots:
hjk's avatar
hjk committed
838
    void initTestCase();
hjk's avatar
hjk committed
839
840
    void dumper();
    void dumper_data();
841
842
    void init();
    void cleanup();
hjk's avatar
hjk committed
843
844

private:
845
    void disarm() { t->buildTemp.setAutoRemove(!keepTemp()); }
846
    bool keepTemp() const { return m_keepTemp || m_forceKeepTemp; }
847
    TempStuff *t;
hjk's avatar
hjk committed
848
849
    QByteArray m_debuggerBinary;
    QByteArray m_qmakeBinary;
850
    QProcessEnvironment m_env;
851
    DebuggerEngine m_debuggerEngine;
852
    QString m_makeBinary;
hjk's avatar
hjk committed
853
    bool m_keepTemp;
854
    bool m_forceKeepTemp;
hjk's avatar
hjk committed
855
    int m_debuggerVersion; // GDB: 7.5.1 -> 70501
856
    int m_gdbBuildVersion;
857
    int m_qtVersion; // 5.2.0 -> 50200
858
    int m_gccVersion;
859
860
    bool m_isMacGdb;
    bool m_isQnxGdb;
861
    bool m_useGLibCxxDebug;
hjk's avatar
hjk committed
862
863
864
865
};

void tst_Dumpers::initTestCase()
{
866
    m_debuggerBinary = qgetenv("QTC_DEBUGGER_PATH_FOR_TEST");
867
868
869
870
    if (m_debuggerBinary.isEmpty()) {
#ifdef Q_OS_MAC
        m_debuggerBinary = "/Applications/Xcode.app/Contents/Developer/usr/bin/lldb";
#else
hjk's avatar
hjk committed
871
        m_debuggerBinary = "gdb";
872
873
#endif
    }
874
    qDebug() << "Debugger           : " << m_debuggerBinary.constData();
875

hjk's avatar
hjk committed
876
    m_debuggerEngine = GdbEngine;
877
    if (m_debuggerBinary.endsWith("cdb.exe"))
hjk's avatar
hjk committed
878
        m_debuggerEngine = CdbEngine;
879

880
881
    QString base = QFileInfo(QString::fromLatin1(m_debuggerBinary)).baseName();
    if (base.startsWith(QLatin1String("lldb")))
hjk's avatar
hjk committed
882
        m_debuggerEngine = LldbEngine;
hjk's avatar
hjk committed
883

hjk's avatar
hjk committed
884
    m_qmakeBinary = qgetenv("QTC_QMAKE_PATH_FOR_TEST");
hjk's avatar
hjk committed
885
886
    if (m_qmakeBinary.isEmpty())
        m_qmakeBinary = "qmake";
887
888
889
890
891
892
893
    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
894

hjk's avatar
hjk committed
895
    if (m_debuggerEngine == GdbEngine) {
896
897
898
899
900
901
902
903
904
        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;
905
906
        bool usePython = !output.contains("Python scripting is not supported in this copy of GDB");
        qDebug() << "Python             : " << (usePython ? "ok" : "*** not ok ***");
907
        qDebug() << "Dumper dir         : " << DUMPERDIR;
908
        QVERIFY(usePython);
909
910
911
912
913
914
915
916
917

        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
918
        extractGdbVersion(version, &m_debuggerVersion,
919
            &m_gdbBuildVersion, &m_isMacGdb, &m_isQnxGdb);
920
        m_env = QProcessEnvironment::systemEnvironment();
921
922
        m_makeBinary = QString::fromLocal8Bit(qgetenv("QTC_MAKE_PATH_FOR_TEST"));
#ifdef Q_OS_WIN
Christian Stenger's avatar
Christian Stenger committed
923
        if (m_makeBinary.isEmpty())
924
925
926
927
928
929
930
931
            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
932
        if (m_makeBinary.isEmpty())
933
934
935
            m_makeBinary = QLatin1String("make");
#endif
        qDebug() << "Make path          : " << m_makeBinary;
hjk's avatar
hjk committed
936
        qDebug() << "Gdb version        : " << m_debuggerVersion;
hjk's avatar
hjk committed
937
    } else if (m_debuggerEngine == CdbEngine) {
938
        setupCdb(&m_makeBinary, &m_env);
hjk's avatar
hjk committed
939
    } else if (m_debuggerEngine == LldbEngine) {
940
        qDebug() << "Dumper dir         : " << DUMPERDIR;
941
        QProcess debugger;
942
943
        QString cmd = QString::fromUtf8(m_debuggerBinary + " -v");
        debugger.start(cmd);
944
945
946
947
948
        bool ok = debugger.waitForFinished(2000);
        QVERIFY(ok);
        QByteArray output = debugger.readAllStandardOutput();
        output += debugger.readAllStandardError();
        output = output.trimmed();
949
950
        // Should be something like "LLDB-178" (Mac OS X 10.8)
        // or "lldb version 3.5 ( revision )" (Ubuntu 13.10)
951
952
953
954
        QByteArray ba = output.mid(output.indexOf('-') + 1);
        int pos = ba.indexOf('.');
        if (pos >= 0)
            ba = ba.left(pos);
hjk's avatar
hjk committed
955
956
        m_debuggerVersion = ba.toInt();
        if (!m_debuggerVersion) {
957
958
959
960
961
            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
962
                m_debuggerVersion = 100 * major + 10 * minor;
963
964
965
            }
        }

hjk's avatar
hjk committed
966
967
        qDebug() << "Lldb version       :" << output << ba << m_debuggerVersion;
        QVERIFY(m_debuggerVersion);
968
969
970

        m_env = QProcessEnvironment::systemEnvironment();
        m_makeBinary = QLatin1String("make");
971
    }
hjk's avatar
hjk committed
972
973
}

974
void tst_Dumpers::init()
hjk's avatar
hjk committed
975
{
976
    t = new TempStuff(QTest::currentDataTag());
hjk's avatar
hjk committed
977
978
}

979
void tst_Dumpers::cleanup()
hjk's avatar
hjk committed
980
{
981
982
    if (!t->buildTemp.autoRemove()) {
        QFile logger(t->buildPath + QLatin1String("/input.txt"));
hjk's avatar
hjk committed
983
        logger.open(QIODevice::ReadWrite);
984
        logger.write(t->input);
hjk's avatar
hjk committed
985
    }
986
987
    delete t;
}
988

hjk's avatar
hjk committed
989
990
991
992
void tst_Dumpers::dumper()
{
    QFETCH(Data, data);

993
994
995
    if (!(data.engines & m_debuggerEngine))
        MSKIP_SINGLE("The test is excluded for this debugger engine.");

hjk's avatar
hjk committed
996
    if (data.neededGdbVersion.isRestricted && m_debuggerEngine == GdbEngine) {
hjk's avatar
hjk committed
997
        if (data.neededGdbVersion.min > m_debuggerVersion)
hjk's avatar
hjk committed
998
999
            MSKIP_SINGLE("Need minimum GDB version "
                + QByteArray::number(data.neededGdbVersion.min));
hjk's avatar
hjk committed
1000
        if (data.neededGdbVersion.max < m_debuggerVersion)
hjk's avatar
hjk committed
1001
1002
1003
1004
            MSKIP_SINGLE("Need maximum GDB version "
                + QByteArray::number(data.neededGdbVersion.max));
    }

hjk's avatar
hjk committed
1005
    if (data.neededLldbVersion.isRestricted && m_debuggerEngine == LldbEngine) {
hjk's avatar
hjk committed
1006
        if (data.neededLldbVersion.min > m_debuggerVersion)
hjk's avatar
hjk committed
1007
1008
            MSKIP_SINGLE("Need minimum LLDB version "
                + QByteArray::number(data.neededLldbVersion.min));
hjk's avatar
hjk committed
1009
        if (data.neededLldbVersion.max < m_debuggerVersion)
hjk's avatar
hjk committed
1010
1011
            MSKIP_SINGLE("Need maximum LLDB version "
                + QByteArray::number(data.neededLldbVersion.max));
1012
    }
1013

hjk's avatar
hjk committed
1014
1015
1016
1017
    QString cmd;
    QByteArray output;
    QByteArray error;

1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
    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();
1039
        m_qtVersion = 0x10000 * major + 0x100 * minor + patch;
1040
1041
1042

        if (data.neededQtVersion.min > m_qtVersion)
            MSKIP_SINGLE("Need minimum Qt version "
1043
                + QByteArray::number(data.neededQtVersion.min, 16));
1044
1045
        if (data.neededQtVersion.max < m_qtVersion)
            MSKIP_SINGLE("Need maximum Qt version "
1046
                + QByteArray::number(data.neededQtVersion.max, 16));
1047
1048
    }

1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
    if (data.neededGccVersion.isRestricted) {
        QProcess gcc;
        gcc.setWorkingDirectory(t->buildPath);
        cmd = QLatin1String("gcc");
        gcc.start(cmd, QStringList(QLatin1String("--version")));
        QVERIFY(gcc.waitForFinished());
        output = gcc.readAllStandardOutput();
        error = gcc.readAllStandardError();
        int pos = output.indexOf('\n');
        if (pos != -1)
            output = output.left(pos);
        qDebug() << "Extracting GCC version from: " << output;
        pos = output.lastIndexOf(' ');
        output = output.mid(pos + 1);
        int pos1 = output.indexOf('.');
        int major = output.left(pos1++).toInt();
        int pos2 = output.indexOf('.', pos1 + 1);
        int minor = output.mid(pos1, pos2++ - pos1).toInt();
        int patch = output.mid(pos2).toInt();
        m_gccVersion = 10000 * major + 100 *