tst_dumpers.cpp 258 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
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
{
190
    explicit QtVersion(int minimum = 0, int maximum = INT_MAX)
hjk's avatar
hjk committed
191
192
193
194
        : VersionBase(minimum, maximum)
    {}
};

hjk's avatar
hjk committed
195
196
struct DwarfVersion : VersionBase
{
197
    explicit DwarfVersion(int minimum = 0, int maximum = INT_MAX)
hjk's avatar
hjk committed
198
199
200
201
        : VersionBase(minimum, maximum)
    {}
};

hjk's avatar
hjk committed
202
203
struct GccVersion : VersionBase
{
204
    explicit GccVersion(int minimum = 0, int maximum = INT_MAX)
hjk's avatar
hjk committed
205
206
207
208
        : VersionBase(minimum, maximum)
    {}
};

209
210
struct ClangVersion : VersionBase
{
211
    explicit ClangVersion(int minimum = 0, int maximum = INT_MAX)
212
213
214
215
        : VersionBase(minimum, maximum)
    {}
};

hjk's avatar
hjk committed
216
217
struct GdbVersion : VersionBase
{
218
    explicit GdbVersion(int minimum = 0, int maximum = INT_MAX)
hjk's avatar
hjk committed
219
220
221
222
223
224
        : VersionBase(minimum, maximum)
    {}
};

struct LldbVersion : VersionBase
{
225
    explicit LldbVersion(int minimum = 0, int maximum = INT_MAX)
hjk's avatar
hjk committed
226
227
228
229
        : VersionBase(minimum, maximum)
    {}
};

230
231
struct BoostVersion : VersionBase
{
232
    explicit BoostVersion(int minimum = 0, int maximum = INT_MAX)
233
234
235
236
        : VersionBase(minimum, maximum)
    {}
};

hjk's avatar
hjk committed
237
static QString noValue = "\001";
238

hjk's avatar
hjk committed
239
240
struct Context
{
hjk's avatar
hjk committed
241
242
243
244
245
    QString nameSpace;
    int qtVersion = 0;
    int gccVersion = 0;
    int clangVersion = 0;
    int boostVersion = 0;
hjk's avatar
hjk committed
246
247
};

hjk's avatar
hjk committed
248
249
struct Name
{
hjk's avatar
hjk committed
250
    Name() : name(noValue) {}
hjk's avatar
hjk committed
251
252
    Name(const char *str) : name(QString::fromUtf8(str)) {}
    Name(const QString &ba) : name(ba) {}
hjk's avatar
hjk committed
253

hjk's avatar
hjk committed
254
    bool matches(const QString &actualName0, const Context &context) const
hjk's avatar
hjk committed
255
    {
hjk's avatar
hjk committed
256
257
        QString actualName = actualName0;
        QString expectedName = name;
hjk's avatar
hjk committed
258
        expectedName.replace("@Q", context.nameSpace + 'Q');
hjk's avatar
hjk committed
259
260
261
        return actualName == expectedName;
    }

hjk's avatar
hjk committed
262
    QString name;
hjk's avatar
hjk committed
263
264
};

hjk's avatar
hjk committed
265
static Name nameFromIName(const QString &iname)
hjk's avatar
hjk committed
266
267
{
    int pos = iname.lastIndexOf('.');
hjk's avatar
hjk committed
268
    return Name(pos == -1 ? iname : iname.mid(pos + 1));
hjk's avatar
hjk committed
269
270
}

hjk's avatar
hjk committed
271
static QString parentIName(const QString &iname)
hjk's avatar
hjk committed
272
273
{
    int pos = iname.lastIndexOf('.');
hjk's avatar
hjk committed
274
    return pos == -1 ? QString() : iname.left(pos);
hjk's avatar
hjk committed
275
276
}

hjk's avatar
hjk committed
277
struct Value
278
{
hjk's avatar
hjk committed
279
    Value() : value(noValue) {}
280
281
    Value(const char *str) : value(QLatin1String(str)) {}
    Value(const QString &str) : value(str) {}
282

hjk's avatar
hjk committed
283
    bool matches(const QString &actualValue0, const Context &context) const
284
    {
hjk's avatar
hjk committed
285
        if (value == noValue)
286
            return true;
hjk's avatar
hjk committed
287
288

        if (context.qtVersion) {
hjk's avatar
hjk committed
289
            if (qtVersion == 4) {
hjk's avatar
hjk committed
290
291
292
293
                if (context.qtVersion < 0x40000 || context.qtVersion >= 0x50000) {
                    //QWARN("Qt 4 specific case skipped");
                    return true;
                }
hjk's avatar
hjk committed
294
            } else if (qtVersion == 5) {
hjk's avatar
hjk committed
295
296
297
298
299
300
                if (context.qtVersion < 0x50000 || context.qtVersion >= 0x60000) {
                    //QWARN("Qt 5 specific case skipped");
                    return true;
                }
            }
        }
301
        QString actualValue = actualValue0;
hjk's avatar
hjk committed
302
        if (actualValue == " ")
303
            actualValue.clear(); // FIXME: Remove later.
304
        QString expectedValue = value;
305
        if (substituteNamespace)
hjk's avatar
hjk committed
306
            expectedValue.replace('@', context.nameSpace);
hjk's avatar
hjk committed
307

308
309
310
311
312
313
314
315
316
317
        if (isPattern) {
            expectedValue.replace("(", "!");
            expectedValue.replace(")", "!");
            actualValue.replace("(", "!");
            actualValue.replace(")", "!");
            //QWARN(qPrintable("MATCH EXP: " + expectedValue + "   ACT: " + actualValue));
            //QWARN(QRegExp(expectedValue).exactMatch(actualValue) ? "OK" : "NOT OK");
            return QRegExp(expectedValue).exactMatch(actualValue);
        }

318
        if (hasPtrSuffix)
hjk's avatar
hjk committed
319
320
            return actualValue.startsWith(expectedValue + " @0x")
                || actualValue.startsWith(expectedValue + "@0x");
321
322
323
324
325
326
327
328
329
330
331
332

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


333
        return actualValue == expectedValue;
334
335
    }

336
    QString value;
hjk's avatar
hjk committed
337
338
339
    bool hasPtrSuffix = false;
    bool isFloatValue = false;
    bool substituteNamespace = true;
340
    bool isPattern = false;
hjk's avatar
hjk committed
341
    int qtVersion = 0;
342
343
};

344
345
346
347
348
struct ValuePattern : Value
{
    ValuePattern(const QString &ba) : Value(ba) { isPattern = true; }
};

349
350
351
struct Pointer : Value
{
    Pointer() { hasPtrSuffix = true; }
hjk's avatar
hjk committed
352
    Pointer(const QString &value) : Value(value) { hasPtrSuffix = true; }
353
354
};

355
356
357
struct FloatValue : Value
{
    FloatValue() { isFloatValue = true; }
hjk's avatar
hjk committed
358
    FloatValue(const QString &value) : Value(value) { isFloatValue = true; }
359
360
};

hjk's avatar
hjk committed
361
362
struct Value4 : Value
{
hjk's avatar
hjk committed
363
    Value4(const QString &value) : Value(value) { qtVersion = 4; }
hjk's avatar
hjk committed
364
365
366
367
};

struct Value5 : Value
{
hjk's avatar
hjk committed
368
    Value5(const QString &value) : Value(value) { qtVersion = 5; }
hjk's avatar
hjk committed
369
370
};

371
372
struct UnsubstitutedValue : Value
{
hjk's avatar
hjk committed
373
    UnsubstitutedValue(const QString &value) : Value(value) { substituteNamespace = false; }
374
375
};

376
377
struct Optional {};

378
379
struct Type
{
380
381
382
    Type() {}
    Type(const char *str) : type(QString::fromUtf8(str)) {}
    Type(const QString &ba) : type(ba) {}
hjk's avatar
hjk committed
383

hjk's avatar
hjk committed
384
385
    bool matches(const QString &actualType0, const Context &context,
                 bool fullNamespaceMatch = true) const
386
    {
387
        if (context.qtVersion) {
hjk's avatar
hjk committed
388
            if (qtVersion == 4) {
389
390
391
392
                if (context.qtVersion < 0x40000 || context.qtVersion >= 0x50000) {
                    //QWARN("Qt 4 specific case skipped");
                    return true;
                }
hjk's avatar
hjk committed
393
            } else if (qtVersion == 5) {
394
395
396
397
398
399
                if (context.qtVersion < 0x50000 || context.qtVersion >= 0x60000) {
                    //QWARN("Qt 5 specific case skipped");
                    return true;
                }
            }
        }
hjk's avatar
hjk committed
400
        QString actualType = simplifyType(actualType0);
401
        actualType.replace(' ', "");
hjk's avatar
hjk committed
402
        actualType.replace("const", "");
hjk's avatar
hjk committed
403
        QString expectedType = type;
404
        expectedType.replace(' ', "");
405
        expectedType.replace("const", "");
hjk's avatar
hjk committed
406
        expectedType.replace('@', context.nameSpace);
407

hjk's avatar
hjk committed
408
409
        if (isPattern)
            return QRegExp(expectedType).exactMatch(actualType);
410

411
412
413
414
415
        if (fullNamespaceMatch)
            expectedType.replace('?', context.nameSpace);
        else
            expectedType.replace('?', "");

416
417
418
419
420
421
422
423
424
425
426
        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;
427
    }
428

hjk's avatar
hjk committed
429
    QString type;
430
431
    int qtVersion = 0;
    bool isPattern = false;
432
433
434
435
};

struct Type4 : Type
{
hjk's avatar
hjk committed
436
    Type4(const QString &ba) : Type(ba) { qtVersion = 4; }
437
438
439
440
};

struct Type5 : Type
{
hjk's avatar
hjk committed
441
    Type5(const QString &ba) : Type(ba) { qtVersion = 5; }
442
443
};

hjk's avatar
hjk committed
444
struct TypePattern : Type
445
{
hjk's avatar
hjk committed
446
    TypePattern(const QString &ba) : Type(ba) { isPattern = true; }
447
448
};

449
450
enum DebuggerEngine
{
hjk's avatar
hjk committed
451
452
453
454
455
456
457
458
459
    GdbEngine = 0x01,
    CdbEngine = 0x02,
    LldbEngine = 0x04,

    AllEngines = GdbEngine | CdbEngine | LldbEngine,

    NoCdbEngine = AllEngines & (~CdbEngine),
    NoLldbEngine = AllEngines & (~LldbEngine),
    NoGdbEngine = AllEngines & (~GdbEngine)
460
461
};

hjk's avatar
hjk committed
462
struct Check
463
464
465
{
    Check() {}

hjk's avatar
hjk committed
466
    Check(const QString &iname, const Value &value, const Type &type)
hjk's avatar
hjk committed
467
        : iname(iname), expectedName(nameFromIName(iname)),
468
          expectedValue(value), expectedType(type)
hjk's avatar
hjk committed
469
470
    {}

hjk's avatar
hjk committed
471
    Check(const QString &iname, const Name &name,
472
         const Value &value, const Type &type)
473
        : iname(iname), expectedName(name),
474
          expectedValue(value), expectedType(type)
475
476
    {}

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

486
487
488
489
490
491
    const Check &operator%(Optional) const
    {
        optionallyPresent = true;
        return *this;
    }

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

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

505
    const Check &operator%(LldbVersion version) const
506
507
508
509
510
511
    {
        enginesForCheck = LldbEngine;
        debuggerVersionForCheck = version;
        return *this;
    }

512
    const Check &operator%(GccVersion version) const
513
514
515
516
517
518
    {
        enginesForCheck = GdbEngine;
        gccVersionForCheck = version;
        return *this;
    }

519
    const Check &operator%(ClangVersion version) const
520
521
522
523
524
525
    {
        enginesForCheck = GdbEngine;
        clangVersionForCheck = version;
        return *this;
    }

526
    const Check &operator%(BoostVersion version) const
527
528
529
530
531
    {
        boostVersionForCheck = version;
        return *this;
    }

532
    const Check &operator%(QtVersion version) const
533
534
535
536
537
    {
        qtVersionForCheck = version;
        return *this;
    }

hjk's avatar
hjk committed
538
    QString iname;
hjk's avatar
hjk committed
539
    Name expectedName;
540
    Value expectedValue;
541
    Type expectedType;
hjk's avatar
hjk committed
542
543
544
545
546
547
548
549

    mutable int enginesForCheck = AllEngines;
    mutable VersionBase debuggerVersionForCheck;
    mutable VersionBase gccVersionForCheck;
    mutable VersionBase clangVersionForCheck;
    mutable QtVersion qtVersionForCheck;
    mutable BoostVersion boostVersionForCheck;
    mutable bool optionallyPresent = false;
550
551
};

hjk's avatar
hjk committed
552
struct CheckType : public Check
553
{
hjk's avatar
hjk committed
554
    CheckType(const QByteArray &iname, const Name &name,
555
         const Type &type)
556
557
        : Check(iname, name, noValue, type)
    {}
558

559
    CheckType(const QByteArray &iname, const Type &type)
hjk's avatar
hjk committed
560
561
562
563
        : Check(iname, noValue, type)
    {}
};

hjk's avatar
hjk committed
564
565
566
const QtVersion Qt4 = QtVersion(0, 0x4ffff);
const QtVersion Qt5 = QtVersion(0x50000);

567
568
569
struct Check4 : Check
{
    Check4(const QByteArray &iname, const Value &value, const Type &type)
hjk's avatar
hjk committed
570
        : Check(iname, value, type) { qtVersionForCheck = Qt4; }
571
572

    Check4(const QByteArray &iname, const Name &name, const Value &value, const Type &type)
hjk's avatar
hjk committed
573
        : Check(iname, name, value, type) { qtVersionForCheck = Qt4; }
574
575
576
577
578
};

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

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

};

hjk's avatar
hjk committed
586
struct Profile
hjk's avatar
hjk committed
587
588
589
{
    Profile(const QByteArray &contents) : contents(contents) {}

590
    QByteArray includes;
hjk's avatar
hjk committed
591
    QByteArray contents;
592
593
};

hjk's avatar
hjk committed
594

hjk's avatar
hjk committed
595
596
struct Cxx11Profile : public Profile
{
597
598
599
600
601
602
    Cxx11Profile()
      : Profile("greaterThan(QT_MAJOR_VERSION,4): CONFIG += c++11\n"
                "else: QMAKE_CXXFLAGS += -std=c++0x\n")
    {}
};

603
604
605
struct BoostProfile : public Profile
{
    BoostProfile()
606
607
608
609
610
611
612
      : 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";
613
        includes = "#include <boost/version.hpp>\n";
614
    }
615
616
};

617
struct MacLibCppProfile : public Profile
618
{
619
    MacLibCppProfile()
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
      : 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
635
636
};

637
struct ForceC {};
hjk's avatar
hjk committed
638
struct EigenProfile {};
639
struct UseDebugImage {};
hjk's avatar
hjk committed
640
struct DwarfProfile { explicit DwarfProfile(int v) : version(v) {} int version; };
641

hjk's avatar
hjk committed
642
643
644
struct CoreProfile {};
struct CorePrivateProfile {};
struct GuiProfile {};
645
struct GuiPrivateProfile {};
hjk's avatar
hjk committed
646
struct NetworkProfile {};
647
648
struct QmlProfile {};
struct QmlPrivateProfile {};
hjk's avatar
hjk committed
649

650
struct NimProfile {};
651

652
struct BigArrayProfile {};
hjk's avatar
hjk committed
653

654
class Data
655
{
hjk's avatar
hjk committed
656
657
public:
    Data() {}
hjk's avatar
hjk committed
658
    Data(const QString &code) : code(code) {}
659

hjk's avatar
hjk committed
660
    Data(const QString &includes, const QString &code)
hjk's avatar
hjk committed
661
        : includes(includes), code(code)
hjk's avatar
hjk committed
662
    {}
663

hjk's avatar
hjk committed
664
    const Data &operator+(const Check &check) const
665
    {
666
        checks.append(check);
667
668
669
        return *this;
    }

hjk's avatar
hjk committed
670
    const Data &operator+(const Profile &profile) const
hjk's avatar
hjk committed
671
672
    {
        profileExtra += profile.contents;
673
        includes += profile.includes;
hjk's avatar
hjk committed
674
675
676
        return *this;
    }

hjk's avatar
hjk committed
677
    const Data &operator+(const QtVersion &qtVersion) const
678
679
680
681
682
    {
        neededQtVersion = qtVersion;
        return *this;
    }

hjk's avatar
hjk committed
683
    const Data &operator+(const GccVersion &gccVersion) const
684
685
686
687
688
    {
        neededGccVersion = gccVersion;
        return *this;
    }

689
690
691
692
693
694
    const Data &operator+(const ClangVersion &clangVersion) const
    {
        neededClangVersion = clangVersion;
        return *this;
    }

hjk's avatar
hjk committed
695
    const Data &operator+(const GdbVersion &gdbVersion) const
696
697
698
699
700
    {
        neededGdbVersion = gdbVersion;
        return *this;
    }

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

707
708
709
710
711
712
    const Data &operator+(const BoostVersion &boostVersion) const
    {
        neededBoostVersion = boostVersion;
        return *this;
    }

hjk's avatar
hjk committed
713
714
715
716
717
718
    const Data &operator+(const DwarfVersion &dwarfVersion) const
    {
        neededDwarfVersion = dwarfVersion;
        return *this;
    }

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

hjk's avatar
hjk committed
725
    const Data &operator+(const EigenProfile &) const
hjk's avatar
hjk committed
726
727
    {
        profileExtra +=
hjk's avatar
hjk committed
728
            "exists(/usr/include/eigen3/Eigen/Core) {\n"
729
            "    DEFINES += HAS_EIGEN\n"
hjk's avatar
hjk committed
730
731
732
            "    INCLUDEPATH += /usr/include/eigen3\n"
            "}\n"
            "exists(/usr/local/include/eigen3/Eigen/Core) {\n"
733
            "    DEFINES += HAS_EIGEN\n"
hjk's avatar
hjk committed
734
735
736
737
            "    INCLUDEPATH += /usr/local/include/eigen3\n"
            "}\n"
            "\n"
            "exists(/usr/include/eigen2/Eigen/Core) {\n"
738
            "    DEFINES += HAS_EIGEN\n"
hjk's avatar
hjk committed
739
740
741
            "    INCLUDEPATH += /usr/include/eigen2\n"
            "}\n"
            "exists(/usr/local/include/eigen2/Eigen/Core) {\n"
742
            "    DEFINES += HAS_EIGEN\n"
hjk's avatar
hjk committed
743
744
            "    INCLUDEPATH += /usr/local/include/eigen2\n"
            "}\n";
hjk's avatar
hjk committed
745
746
747
748

        return *this;
    }

hjk's avatar
hjk committed
749
750
751
752
753
754
755
756
    const Data &operator+(const DwarfProfile &p)
    {
        profileExtra +=
            "QMAKE_CXXFLAGS -= -g\n"
            "QMAKE_CXXFLAGS += -gdwarf-" + QString::number(p.version) + "\n";
        return *this;
    }

hjk's avatar
hjk committed
757
    const Data &operator+(const UseDebugImage &) const
758
759
760
761
762
    {
        useDebugImage = true;
        return *this;
    }

hjk's avatar
hjk committed
763
    const Data &operator+(const CoreProfile &) const
hjk's avatar
hjk committed
764
765
766
    {
        profileExtra +=
            "CONFIG += QT\n"
767
            "QT += core\n";
hjk's avatar
hjk committed
768
769

        useQt = true;
770
771
        useQHash = true;

hjk's avatar
hjk committed
772
773
774
        return *this;
    }

hjk's avatar
hjk committed
775
    const Data &operator+(const NetworkProfile &) const
hjk's avatar
hjk committed
776
777
778
779
780
781
782
783
784
785
786
    {
        profileExtra +=
            "CONFIG += QT\n"
            "QT += core network\n";

        useQt = true;
        useQHash = true;

        return *this;
    }

787
788
789
790
791
792
    const Data &operator+(const BigArrayProfile &) const
    {
        this->bigArray = true;
        return *this;
    }

hjk's avatar
hjk committed
793
    const Data &operator+(const GuiProfile &) const
hjk's avatar
hjk committed
794
    {
hjk's avatar
hjk committed
795
        this->operator+(CoreProfile());
hjk's avatar
hjk committed
796
797
798
799
800
801
802
        profileExtra +=
            "QT += gui\n"
            "greaterThan(QT_MAJOR_VERSION, 4):QT *= widgets\n";

        return *this;
    }

hjk's avatar
hjk committed
803
    const Data &operator+(const CorePrivateProfile &) const
hjk's avatar
hjk committed
804
    {
hjk's avatar
hjk committed
805
        this->operator+(CoreProfile());
hjk's avatar
hjk committed
806
807
        profileExtra +=
            "greaterThan(QT_MAJOR_VERSION, 4) {\n"
808
            "  QT += core-private\n"
hjk's avatar
hjk committed
809
810
811
812
813
814
            "  CONFIG += no_private_qt_headers_warning\n"
            "}";

        return *this;
    }

815
816
817
818
819
820
821
822
823
824
825
826
    const Data &operator+(const GuiPrivateProfile &) const
    {
        this->operator+(GuiProfile());
        profileExtra +=
            "greaterThan(QT_MAJOR_VERSION, 4) {\n"
            "  QT += gui-private\n"
            "  CONFIG += no_private_qt_headers_warning\n"
            "}";

        return *this;
    }

827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
    const Data &operator+(const QmlProfile &) const
    {
        profileExtra +=
            "greaterThan(QT_MAJOR_VERSION, 4) {\n"
            "  QT += qml\n"
            "}";

        return *this;
    }

    const Data &operator+(const QmlPrivateProfile &) const
    {
        this->operator+(QmlProfile());
        profileExtra +=
            "greaterThan(QT_MAJOR_VERSION, 4) {\n"
            "  QT += qml-private\n"
            "  CONFIG += no_private_qt_headers_warning\n"
            "}";

        return *this;
    }

hjk's avatar
hjk committed
849
    const Data &operator+(const ForceC &) const
850
    {
851
        mainFile = "main.c";
852
853
854
        return *this;
    }

hjk's avatar
hjk committed
855
public:
856
857
858
859
860
861
862
863
864
865
866
867
868
    mutable bool useQt = false;
    mutable bool useQHash = false;
    mutable int engines = AllEngines;
    mutable int skipLevels = 0;              // Levels to go 'up' before dumping variables.
    mutable bool glibcxxDebug = false;
    mutable bool useDebugImage = false;
    mutable bool bigArray = false;
    mutable GdbVersion neededGdbVersion;     // DEC. 70600
    mutable LldbVersion neededLldbVersion;
    mutable QtVersion neededQtVersion;       // HEX! 0x50300
    mutable GccVersion neededGccVersion;     // DEC. 40702  for 4.7.2
    mutable ClangVersion neededClangVersion; // DEC.
    mutable BoostVersion neededBoostVersion; // DEC. 105400 for 1.54.0
hjk's avatar
hjk committed
869
    mutable DwarfVersion neededDwarfVersion; // DEC. 105400 for 1.54.0
870
871
872
873
874
875
876
877
878

    mutable QString configTest;

    mutable QString allProfile;      // Overrides anything below if not empty.
    mutable QString allCode;         // Overrides anything below if not empty.

    mutable QString mainFile = "main.cpp";
    mutable QString projectFile = "doit.pro";

hjk's avatar
hjk committed
879
880
881
    mutable QString profileExtra;
    mutable QString includes;
    mutable QString code;
882

883
    mutable QList<Check> checks;
hjk's avatar
hjk committed
884
885
};

886
887
struct TempStuff
{
888
889
890
    TempStuff(const char *tag) : buildTemp(QLatin1String("qt_tst_dumpers_")
                                           + QLatin1String(tag)
                                           + QLatin1Char('_'))
891
    {
892
        buildPath = QDir::currentPath() + QLatin1Char('/')  + buildTemp.path();
893
894
895
896
        buildTemp.setAutoRemove(false);
        QVERIFY(!buildPath.isEmpty());
    }

hjk's avatar
hjk committed
897
    QString input;
898
899
900
901
    QTemporaryDir buildTemp;
    QString buildPath;
};

hjk's avatar
hjk committed
902
Q_DECLARE_METATYPE(Data)
hjk's avatar
hjk committed
903

904
class tst_Dumpers : public QObject
905
906
907
908
{
    Q_OBJECT

public:
hjk's avatar
hjk committed
909
    tst_Dumpers() {}
910
911

private slots:
hjk's avatar
hjk committed
912
    void initTestCase();
hjk's avatar
hjk committed
913
914
    void dumper();
    void dumper_data();
915
916
    void init();
    void cleanup();
hjk's avatar
hjk committed
917
918

private:
919
    void disarm() { t->buildTemp.setAutoRemove(!keepTemp()); }
920
    bool keepTemp() const { return m_keepTemp || m_forceKeepTemp; }
hjk's avatar
hjk committed
921
    TempStuff *t = 0;
hjk's avatar
hjk committed
922
923
    QString m_debuggerBinary;
    QString m_qmakeBinary;
924
    QProcessEnvironment m_env;
925
    DebuggerEngine m_debuggerEngine;
926
    QString m_makeBinary;
hjk's avatar
hjk committed
927
928
929
930
931
932
933
934
935
    bool m_keepTemp = true;
    bool m_forceKeepTemp = false;
    int m_debuggerVersion = 0; // GDB: 7.5.1 -> 70501
    int m_gdbBuildVersion = 0;
    int m_qtVersion = 0; // 5.2.0 -> 50200
    int m_gccVersion = 0;
    bool m_isMacGdb = false;
    bool m_isQnxGdb = false;
    bool m_useGLibCxxDebug = false;
hjk's avatar
hjk committed
936
937
938
939
};

void tst_Dumpers::initTestCase()
{
hjk's avatar
hjk committed
940
    m_debuggerBinary = QString::fromLocal8Bit(qgetenv("QTC_DEBUGGER_PATH_FOR_TEST"));
941
942
943
944
    if (m_debuggerBinary.isEmpty()) {
#ifdef Q_OS_MAC
        m_debuggerBinary = "/Applications/Xcode.app/Contents/Developer/usr/bin/lldb";
#else
hjk's avatar
hjk committed
945
        m_debuggerBinary = "gdb";
946
947
#endif
    }
hjk's avatar
hjk committed
948
    qDebug() << "Debugger           : " << m_debuggerBinary;
949

hjk's avatar
hjk committed
950
    m_debuggerEngine = GdbEngine;
951
    if (m_debuggerBinary.endsWith("cdb.exe"))
hjk's avatar
hjk committed
952
        m_debuggerEngine = CdbEngine;
953

hjk's avatar
hjk committed
954
955
    QString base = QFileInfo(m_debuggerBinary).baseName();
    if (base.startsWith("lldb"))
hjk's avatar
hjk committed
956
        m_debuggerEngine = LldbEngine;
hjk's avatar
hjk committed
957

hjk's avatar
hjk committed
958
    m_qmakeBinary = QString::fromLocal8Bit(qgetenv("QTC_QMAKE_PATH_FOR_TEST"));
hjk's avatar
hjk committed
959
960
    if (m_qmakeBinary.isEmpty())
        m_qmakeBinary = "qmake";
hjk's avatar
hjk committed
961
    qDebug() << "QMake              : " << m_qmakeBinary;
962
963
964
965
966
967

    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
968

hjk's avatar
hjk committed
969
    if (m_debuggerEngine == GdbEngine) {
970
        QProcess debugger;
hjk's avatar
hjk committed
971
        debugger.start(m_debuggerBinary + " -i mi -quiet -nx");
972
973
974
975
976
977
        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;
978
979
        bool usePython = !output.contains("Python scripting is not supported in this copy of GDB");
        qDebug() << "Python             : " << (usePython ? "ok" : "*** not ok ***");
980
        qDebug() << "Dumper dir         : " << DUMPERDIR;
981
        QVERIFY(usePython);
982
983

        QString version = QString::fromLocal8Bit(output);
hjk's avatar
hjk committed
984
        int pos1 = version.indexOf("&\"show version\\n");
985
986
        QVERIFY(pos1 != -1);
        pos1 += 20;
hjk's avatar
hjk committed
987
        int pos2 = version.indexOf("~\"Copyright (C) ", pos1);
988
989
990
        QVERIFY(pos2 != -1);
        pos2 -= 4;
        version = version.mid(pos1, pos2 - pos1);
hjk's avatar
hjk committed
991
        extractGdbVersion(version, &m_debuggerVersion,
992
            &m_gdbBuildVersion, &m_isMacGdb, &m_isQnxGdb);
993
        m_env = QProcessEnvironment::systemEnvironment();
994
995
        m_makeBinary = QString::fromLocal8Bit(qgetenv("QTC_MAKE_PATH_FOR_TEST"));
#ifdef Q_OS_WIN
Christian Stenger's avatar
Christian Stenger committed
996
        if (m_makeBinary.isEmpty())
hjk's avatar
hjk committed
997
            m_makeBinary = "mingw32-make";
998
999
1000
        // 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();
For faster browsing, not all history is shown. View entire blame