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

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

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

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

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

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

50
#ifdef Q_CC_MSVC
51

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

143
144
145
146
147

#ifndef CDBEXT_PATH
#define CDBEXT_PATH ""
#endif

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

#else

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

#endif // Q_CC_MSVC


hjk's avatar
hjk committed
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
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)
    {}
};

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

218
219
220
221
222
223
224
struct BoostVersion : VersionBase
{
    BoostVersion(int minimum = 0, int maximum = INT_MAX)
        : VersionBase(minimum, maximum)
    {}
};

225
226
static QByteArray noValue = "\001";

227
228
229
230
231
232
233
234
235
236
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
237
238
struct Context
{
hjk's avatar
hjk committed
239
    Context() : qtVersion(0), gccVersion(0) {}
hjk's avatar
hjk committed
240
241
242

    QByteArray nameSpace;
    int qtVersion;
hjk's avatar
hjk committed
243
    int gccVersion;
hjk's avatar
hjk committed
244
245
};

hjk's avatar
hjk committed
246
247
struct Name
{
hjk's avatar
hjk committed
248
249
250
251
252
    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
253
254
255
    {
        QByteArray actualName = actualName0;
        QByteArray expectedName = name;
hjk's avatar
hjk committed
256
        expectedName.replace("@Q", context.nameSpace + 'Q');
hjk's avatar
hjk committed
257
258
259
260
261
262
263
        return actualName == expectedName;
    }

    QByteArray name;
};

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

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

hjk's avatar
hjk committed
275
struct ValueBase
276
{
hjk's avatar
hjk committed
277
278
279
280
    ValueBase()
      : hasPtrSuffix(false), isFloatValue(false), substituteNamespace(true),
        qtVersion(0), minimalGccVersion(0)
    {}
hjk's avatar
hjk committed
281
282

    bool hasPtrSuffix;
283
    bool isFloatValue;
284
    bool substituteNamespace;
hjk's avatar
hjk committed
285
286
    int qtVersion;
    int minimalGccVersion;
hjk's avatar
hjk committed
287
288
289
290
};

struct Value : public ValueBase
{
291
292
293
294
    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) {}
295

hjk's avatar
hjk committed
296
    bool matches(const QString &actualValue0, const Context &context) const
297
    {
298
        if (value == QString::fromLatin1(noValue))
299
            return true;
hjk's avatar
hjk committed
300
301

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

327
        if (hasPtrSuffix)
328
329
            return actualValue.startsWith(expectedValue + QLatin1String(" @0x"))
                || actualValue.startsWith(expectedValue + QLatin1String("@0x"));
330
331
332
333
334
335
336
337
338
339
340
341

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


342
        return actualValue == expectedValue;
343
344
    }

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

347
    QString value;
348
349
350
351
352
353
354
355
};

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

356
357
358
359
360
361
struct FloatValue : Value
{
    FloatValue() { isFloatValue = true; }
    FloatValue(const QByteArray &value) : Value(value) { isFloatValue = true; }
};

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

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

372
373
374
375
376
struct UnsubstitutedValue : Value
{
    UnsubstitutedValue(const QByteArray &value) : Value(value) { substituteNamespace = false; }
};

377
378
struct Type
{
379
380
381
    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
382
383

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

struct Type4 : Type
{
hjk's avatar
hjk committed
420
    Type4(const QByteArray &ba) : Type(ba) { qtVersion = 4; }
421
422
423
424
};

struct Type5 : Type
{
hjk's avatar
hjk committed
425
    Type5(const QByteArray &ba) : Type(ba) { qtVersion = 5; }
426
427
};

428
429
430
431
432
struct Pattern : Type
{
    Pattern(const QByteArray &ba) : Type(ba) { isPattern = true; }
};

433
434
enum DebuggerEngine
{
hjk's avatar
hjk committed
435
436
437
438
439
440
441
442
443
    GdbEngine = 0x01,
    CdbEngine = 0x02,
    LldbEngine = 0x04,

    AllEngines = GdbEngine | CdbEngine | LldbEngine,

    NoCdbEngine = AllEngines & (~CdbEngine),
    NoLldbEngine = AllEngines & (~LldbEngine),
    NoGdbEngine = AllEngines & (~GdbEngine)
444
445
446
447
};

struct CheckBase
{
hjk's avatar
hjk committed
448
    CheckBase() : enginesForCheck(AllEngines) {}
449
    mutable int enginesForCheck;
hjk's avatar
hjk committed
450
451
    mutable VersionBase debuggerVersionForCheck;
    mutable QtVersion qtVersionForCheck;
452
453
454
};

struct Check : CheckBase
455
456
457
{
    Check() {}

hjk's avatar
hjk committed
458
    Check(const QByteArray &iname, const Value &value, const Type &type)
hjk's avatar
hjk committed
459
        : iname(iname), expectedName(nameFromIName(iname)),
460
          expectedValue(value), expectedType(type)
hjk's avatar
hjk committed
461
462
    {}

hjk's avatar
hjk committed
463
    Check(const QByteArray &iname, const Name &name,
464
         const Value &value, const Type &type)
465
        : iname(iname), expectedName(name),
466
          expectedValue(value), expectedType(type)
467
468
    {}

hjk's avatar
hjk committed
469
    bool matches(DebuggerEngine engine, int debuggerVersion, int qtVersion) const
470
    {
hjk's avatar
hjk committed
471
472
473
        return (engine & enginesForCheck)
            && debuggerVersionForCheck.covers(debuggerVersion)
            && qtVersionForCheck.covers(qtVersion);
474
475
    }

hjk's avatar
hjk committed
476
    const Check &operator%(DebuggerEngine engine)
477
    {
hjk's avatar
hjk committed
478
        enginesForCheck = engine;
479
        return *this;
480
    }
481

hjk's avatar
hjk committed
482
483
484
485
486
487
488
    const Check &operator%(GdbVersion version)
    {
        enginesForCheck = GdbEngine;
        debuggerVersionForCheck = version;
        return *this;
    }

489
    QByteArray iname;
hjk's avatar
hjk committed
490
    Name expectedName;
491
    Value expectedValue;
492
    Type expectedType;
493
494
};

hjk's avatar
hjk committed
495
struct CheckType : public Check
496
{
hjk's avatar
hjk committed
497
    CheckType(const QByteArray &iname, const Name &name,
498
         const Type &type)
499
500
        : Check(iname, name, noValue, type)
    {}
501

502
    CheckType(const QByteArray &iname, const Type &type)
hjk's avatar
hjk committed
503
504
505
506
        : Check(iname, noValue, type)
    {}
};

hjk's avatar
hjk committed
507
508
509
const QtVersion Qt4 = QtVersion(0, 0x4ffff);
const QtVersion Qt5 = QtVersion(0x50000);

510
511
512
struct Check4 : Check
{
    Check4(const QByteArray &iname, const Value &value, const Type &type)
hjk's avatar
hjk committed
513
        : Check(iname, value, type) { qtVersionForCheck = Qt4; }
514
515

    Check4(const QByteArray &iname, const Name &name, const Value &value, const Type &type)
hjk's avatar
hjk committed
516
        : Check(iname, name, value, type) { qtVersionForCheck = Qt4; }
517
518
519
520
521
};

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

    Check5(const QByteArray &iname, const Name &name, const Value &value, const Type &type)
hjk's avatar
hjk committed
525
        : Check(iname, name, value, type) { qtVersionForCheck = Qt5; }
526
527
528

};

hjk's avatar
hjk committed
529
struct Profile
hjk's avatar
hjk committed
530
531
532
533
{
    Profile(const QByteArray &contents) : contents(contents) {}

    QByteArray contents;
534
535
};

hjk's avatar
hjk committed
536

hjk's avatar
hjk committed
537
538
struct Cxx11Profile : public Profile
{
539
540
541
542
543
544
    Cxx11Profile()
      : Profile("greaterThan(QT_MAJOR_VERSION,4): CONFIG += c++11\n"
                "else: QMAKE_CXXFLAGS += -std=c++0x\n")
    {}
};

545
546
547
548
549
550
551
struct BoostProfile : public Profile
{
    BoostProfile()
      : Profile("macx:INCLUDEPATH += /usr/local/include")
    {}
};

552
struct MacLibCppProfile : public Profile
553
{
554
    MacLibCppProfile()
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
      : 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
570
571
};

572
struct ForceC {};
hjk's avatar
hjk committed
573
struct EigenProfile {};
574
struct UseDebugImage {};
575

hjk's avatar
hjk committed
576
577
578
struct CoreProfile {};
struct CorePrivateProfile {};
struct GuiProfile {};
hjk's avatar
hjk committed
579
struct NetworkProfile {};
hjk's avatar
hjk committed
580

581
582
struct BigArrayProfile {};

hjk's avatar
hjk committed
583
584
struct DataBase
{
585
586
    DataBase()
      : useQt(false), useQHash(false),
hjk's avatar
hjk committed
587
        forceC(false), engines(AllEngines),
588
589
        glibcxxDebug(false), useDebugImage(false),
        bigArray(false)
590
    {}
hjk's avatar
hjk committed
591
592

    mutable bool useQt;
593
    mutable bool useQHash;
hjk's avatar
hjk committed
594
    mutable bool forceC;
595
    mutable int engines;
596
    mutable bool glibcxxDebug;
597
    mutable bool useDebugImage;
598
    mutable bool bigArray;
599
    mutable GdbVersion neededGdbVersion;     // DEC. 70600
hjk's avatar
hjk committed
600
    mutable LldbVersion neededLldbVersion;
601
602
    mutable QtVersion neededQtVersion;       // HEX! 0x50300
    mutable GccVersion neededGccVersion;     // DEC. 40702
603
604
605
    mutable BoostVersion neededBoostVersion; //  ((BOOST_VERSION >> 20) & 0xF) << "."
                                             // << ((BOOST_VERSION >> 8) & 0xFFF) << "."
                                             //  << (BOOST_VERSION & 0xFF)
hjk's avatar
hjk committed
606
607
608
};

class Data : public DataBase
609
{
hjk's avatar
hjk committed
610
611
public:
    Data() {}
hjk's avatar
hjk committed
612
    Data(const QByteArray &code) : code(code) {}
613

614
    Data(const QByteArray &includes, const QByteArray &code)
hjk's avatar
hjk committed
615
        : includes(includes), code(code)
hjk's avatar
hjk committed
616
    {}
617

hjk's avatar
hjk committed
618
    const Data &operator+(const Check &check) const
619
    {
620
        checks.append(check);
621
622
623
        return *this;
    }

hjk's avatar
hjk committed
624
    const Data &operator+(const Profile &profile) const
hjk's avatar
hjk committed
625
626
627
628
629
    {
        profileExtra += profile.contents;
        return *this;
    }

hjk's avatar
hjk committed
630
    const Data &operator+(const QtVersion &qtVersion) const
631
632
633
634
635
    {
        neededQtVersion = qtVersion;
        return *this;
    }

hjk's avatar
hjk committed
636
    const Data &operator+(const GccVersion &gccVersion) const
637
638
639
640
641
    {
        neededGccVersion = gccVersion;
        return *this;
    }

hjk's avatar
hjk committed
642
    const Data &operator+(const GdbVersion &gdbVersion) const
643
644
645
646
647
    {
        neededGdbVersion = gdbVersion;
        return *this;
    }

hjk's avatar
hjk committed
648
    const Data &operator+(const LldbVersion &lldbVersion) const
hjk's avatar
hjk committed
649
650
651
652
653
    {
        neededLldbVersion = lldbVersion;
        return *this;
    }

654
655
656
657
658
659
    const Data &operator+(const BoostVersion &boostVersion) const
    {
        neededBoostVersion = boostVersion;
        return *this;
    }

hjk's avatar
hjk committed
660
    const Data &operator+(const DebuggerEngine &enginesForTest) const
hjk's avatar
hjk committed
661
    {
662
        engines = enginesForTest;
hjk's avatar
hjk committed
663
664
665
        return *this;
    }

hjk's avatar
hjk committed
666
    const Data &operator+(const EigenProfile &) const
hjk's avatar
hjk committed
667
668
    {
        profileExtra +=
hjk's avatar
hjk committed
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
            "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
686
687
688
689

        return *this;
    }

hjk's avatar
hjk committed
690
    const Data &operator+(const UseDebugImage &) const
691
692
693
694
695
    {
        useDebugImage = true;
        return *this;
    }

hjk's avatar
hjk committed
696
    const Data &operator+(const CoreProfile &) const
hjk's avatar
hjk committed
697
698
699
    {
        profileExtra +=
            "CONFIG += QT\n"
700
            "QT += core\n";
hjk's avatar
hjk committed
701
702

        useQt = true;
703
704
        useQHash = true;

hjk's avatar
hjk committed
705
706
707
        return *this;
    }

hjk's avatar
hjk committed
708
    const Data &operator+(const NetworkProfile &) const
hjk's avatar
hjk committed
709
710
711
712
713
714
715
716
717
718
719
    {
        profileExtra +=
            "CONFIG += QT\n"
            "QT += core network\n";

        useQt = true;
        useQHash = true;

        return *this;
    }

720
721
722
723
724
725
    const Data &operator+(const BigArrayProfile &) const
    {
        this->bigArray = true;
        return *this;
    }

hjk's avatar
hjk committed
726
    const Data &operator+(const GuiProfile &) const
hjk's avatar
hjk committed
727
    {
hjk's avatar
hjk committed
728
        this->operator+(CoreProfile());
hjk's avatar
hjk committed
729
730
731
732
733
734
735
        profileExtra +=
            "QT += gui\n"
            "greaterThan(QT_MAJOR_VERSION, 4):QT *= widgets\n";

        return *this;
    }

hjk's avatar
hjk committed
736
    const Data &operator+(const CorePrivateProfile &) const
hjk's avatar
hjk committed
737
    {
hjk's avatar
hjk committed
738
        this->operator+(CoreProfile());
hjk's avatar
hjk committed
739
740
        profileExtra +=
            "greaterThan(QT_MAJOR_VERSION, 4) {\n"
741
            "  QT += core-private\n"
hjk's avatar
hjk committed
742
743
744
745
746
747
            "  CONFIG += no_private_qt_headers_warning\n"
            "}";

        return *this;
    }

hjk's avatar
hjk committed
748
    const Data &operator+(const ForceC &) const
749
750
751
752
753
    {
        forceC = true;
        return *this;
    }

hjk's avatar
hjk committed
754
public:
hjk's avatar
hjk committed
755
    mutable QByteArray profileExtra;
756
757
    mutable QByteArray includes;
    mutable QByteArray code;
758
    mutable QList<Check> checks;
hjk's avatar
hjk committed
759
760
};

761
762
763
764
struct TempStuff
{
    TempStuff() : buildTemp(QLatin1String("qt_tst_dumpers_"))
    {
765
        buildPath = QDir::currentPath() + QLatin1Char('/')  + buildTemp.path();
766
767
768
769
770
771
772
773
774
        buildTemp.setAutoRemove(false);
        QVERIFY(!buildPath.isEmpty());
    }

    QByteArray input;
    QTemporaryDir buildTemp;
    QString buildPath;
};

hjk's avatar
hjk committed
775
Q_DECLARE_METATYPE(Data)
hjk's avatar
hjk committed
776

777
class tst_Dumpers : public QObject
778
779
780
781
{
    Q_OBJECT

public:
782
783
784
    tst_Dumpers()
    {
        t = 0;
785
        m_keepTemp = true;
786
        m_forceKeepTemp = false;
hjk's avatar
hjk committed
787
        m_debuggerVersion = 0;
788
        m_gdbBuildVersion = 0;
789
        m_qtVersion = 0;
790
        m_gccVersion = 0;
791
792
        m_isMacGdb = false;
        m_isQnxGdb = false;
793
        m_useGLibCxxDebug = false;
794
    }
795
796

private slots:
hjk's avatar
hjk committed
797
    void initTestCase();
hjk's avatar
hjk committed
798
799
    void dumper();
    void dumper_data();
800
801
    void init();
    void cleanup();
hjk's avatar
hjk committed
802
803

private:
804
    void disarm() { t->buildTemp.setAutoRemove(!keepTemp()); }
805
    bool keepTemp() const { return m_keepTemp || m_forceKeepTemp; }
806
    TempStuff *t;
hjk's avatar
hjk committed
807
808
    QByteArray m_debuggerBinary;
    QByteArray m_qmakeBinary;
809
    QProcessEnvironment m_env;
810
    DebuggerEngine m_debuggerEngine;
811
    QString m_makeBinary;
hjk's avatar
hjk committed
812
    bool m_keepTemp;
813
    bool m_forceKeepTemp;
hjk's avatar
hjk committed
814
    int m_debuggerVersion; // GDB: 7.5.1 -> 70501
815
    int m_gdbBuildVersion;
816
    int m_qtVersion; // 5.2.0 -> 50200
817
    int m_gccVersion;
818
819
    bool m_isMacGdb;
    bool m_isQnxGdb;
820
    bool m_useGLibCxxDebug;
hjk's avatar
hjk committed
821
822
823
824
};

void tst_Dumpers::initTestCase()
{
825
    m_debuggerBinary = qgetenv("QTC_DEBUGGER_PATH_FOR_TEST");
826
827
828
829
    if (m_debuggerBinary.isEmpty()) {
#ifdef Q_OS_MAC
        m_debuggerBinary = "/Applications/Xcode.app/Contents/Developer/usr/bin/lldb";
#else
hjk's avatar
hjk committed
830
        m_debuggerBinary = "gdb";
831
832
#endif
    }
833
    qDebug() << "Debugger           : " << m_debuggerBinary.constData();
834

hjk's avatar
hjk committed
835
    m_debuggerEngine = GdbEngine;
836
    if (m_debuggerBinary.endsWith("cdb.exe"))
hjk's avatar
hjk committed
837
        m_debuggerEngine = CdbEngine;
838

839
840
    QString base = QFileInfo(QString::fromLatin1(m_debuggerBinary)).baseName();
    if (base.startsWith(QLatin1String("lldb")))
hjk's avatar
hjk committed
841
        m_debuggerEngine = LldbEngine;
hjk's avatar
hjk committed
842

hjk's avatar
hjk committed
843
    m_qmakeBinary = qgetenv("QTC_QMAKE_PATH_FOR_TEST");
hjk's avatar
hjk committed
844
845
    if (m_qmakeBinary.isEmpty())
        m_qmakeBinary = "qmake";
846
847
848
849
850
851
852
    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
853

hjk's avatar
hjk committed
854
    if (m_debuggerEngine == GdbEngine) {
855
856
857
858
859
860
861
862
863
        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;
864
865
        bool usePython = !output.contains("Python scripting is not supported in this copy of GDB");
        qDebug() << "Python             : " << (usePython ? "ok" : "*** not ok ***");
866
        qDebug() << "Dumper dir         : " << DUMPERDIR;
867
        QVERIFY(usePython);
868
869
870
871
872
873
874
875
876

        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
877
        extractGdbVersion(version, &m_debuggerVersion,
878
            &m_gdbBuildVersion, &m_isMacGdb, &m_isQnxGdb);
879
        m_env = QProcessEnvironment::systemEnvironment();
880
881
        m_makeBinary = QString::fromLocal8Bit(qgetenv("QTC_MAKE_PATH_FOR_TEST"));
#ifdef Q_OS_WIN
Christian Stenger's avatar
Christian Stenger committed
882
        if (m_makeBinary.isEmpty())
883
884
885
886
887
888
889
890
            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
891
        if (m_makeBinary.isEmpty())
892
893
894
            m_makeBinary = QLatin1String("make");
#endif
        qDebug() << "Make path          : " << m_makeBinary;
hjk's avatar
hjk committed
895
        qDebug() << "Gdb version        : " << m_debuggerVersion;
hjk's avatar
hjk committed
896
    } else if (m_debuggerEngine == CdbEngine) {
897
        setupCdb(&m_makeBinary, &m_env);
hjk's avatar
hjk committed
898
    } else if (m_debuggerEngine == LldbEngine) {
899
        QProcess debugger;
900
901
        QString cmd = QString::fromUtf8(m_debuggerBinary + " -v");
        debugger.start(cmd);
902
903
904
905
906
        bool ok = debugger.waitForFinished(2000);
        QVERIFY(ok);
        QByteArray output = debugger.readAllStandardOutput();
        output += debugger.readAllStandardError();
        output = output.trimmed();
907
908
        // Should be something like "LLDB-178" (Mac OS X 10.8)
        // or "lldb version 3.5 ( revision )" (Ubuntu 13.10)
909
910
911
912
        QByteArray ba = output.mid(output.indexOf('-') + 1);
        int pos = ba.indexOf('.');
        if (pos >= 0)
            ba = ba.left(pos);
hjk's avatar
hjk committed
913
914
        m_debuggerVersion = ba.toInt();
        if (!m_debuggerVersion) {
915
916
917
918
919
            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
920
                m_debuggerVersion = 100 * major + 10 * minor;
921
922
923
            }
        }

hjk's avatar
hjk committed
924
925
        qDebug() << "Lldb version       :" << output << ba << m_debuggerVersion;
        QVERIFY(m_debuggerVersion);
926
927
928

        m_env = QProcessEnvironment::systemEnvironment();
        m_makeBinary = QLatin1String("make");
929
    }
hjk's avatar
hjk committed
930
931
}

932
void tst_Dumpers::init()
hjk's avatar
hjk committed
933
{
934
    t = new TempStuff();
hjk's avatar
hjk committed
935
936
}

937
void tst_Dumpers::cleanup()
hjk's avatar
hjk committed
938
{
939
940
    if (!t->buildTemp.autoRemove()) {
        QFile logger(t->buildPath + QLatin1String("/input.txt"));
hjk's avatar
hjk committed
941
        logger.open(QIODevice::ReadWrite);
942
        logger.write(t->input);
hjk's avatar
hjk committed
943
    }
944
945
    delete t;
}
946

hjk's avatar
hjk committed
947
948
949
950
void tst_Dumpers::dumper()
{
    QFETCH(Data, data);

951
952
953
    if (!(data.engines & m_debuggerEngine))
        MSKIP_SINGLE("The test is excluded for this debugger engine.");

hjk's avatar
hjk committed
954
    if (data.neededGdbVersion.isRestricted && m_debuggerEngine == GdbEngine) {
hjk's avatar
hjk committed
955
        if (data.neededGdbVersion.min > m_debuggerVersion)
hjk's avatar
hjk committed
956
957
            MSKIP_SINGLE("Need minimum GDB version "
                + QByteArray::number(data.neededGdbVersion.min));
hjk's avatar
hjk committed
958
        if (data.neededGdbVersion.max < m_debuggerVersion)
hjk's avatar
hjk committed
959
960
961
962
            MSKIP_SINGLE("Need maximum GDB version "
                + QByteArray::number(data.neededGdbVersion.max));
    }

hjk's avatar
hjk committed
963
    if (data.neededLldbVersion.isRestricted && m_debuggerEngine == LldbEngine) {
hjk's avatar
hjk committed
964
        if (data.neededLldbVersion.min > m_debuggerVersion)
hjk's avatar
hjk committed
965
966
            MSKIP_SINGLE("Need minimum LLDB version "
                + QByteArray::number(data.neededLldbVersion.min));
hjk's avatar
hjk committed
967
        if (data.neededLldbVersion.max < m_debuggerVersion)
hjk's avatar
hjk committed
968
969
            MSKIP_SINGLE("Need maximum LLDB version "
                + QByteArray::number(data.neededLldbVersion.max));
970
    }
971

hjk's avatar
hjk committed
972
973
974
975
    QString cmd;
    QByteArray output;
    QByteArray error;

976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
    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();
997
        m_qtVersion = 0x10000 * major + 0x100 * minor + patch;
998
999
1000

        if (data.neededQtVersion.min > m_qtVersion)
            MSKIP_SINGLE("Need minimum Qt version "
1001
                + QByteArray::number(data.neededQtVersion.min, 16));
1002
1003
        if (data.neededQtVersion.max < m_qtVersion)
            MSKIP_SINGLE("Need maximum Qt version "
1004
                + QByteArray::number(data.neededQtVersion.max, 16));
1005
1006
    }

1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
    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 * minor + patch;
        qDebug() << "GCC version: " << m_gccVersion;

        if (data.neededGccVersion.min > m_gccVersion)
            MSKIP_SINGLE("Need minimum GCC version "
                + QByteArray::number(data.neededGccVersion.min));
        if (data.neededGccVersion.max < m_gccVersion)
            MSKIP_SINGLE("Need maximum GCC version "
                + QByteArray::number(data.neededGccVersion.max));
    }

1037
1038
    const char *mainFile = data.forceC ? "main.c" : "main.cpp";

1039
1040
    QFile proFile(t->buildPath + QLatin1String("/doit.pro"));
    QVERIFY(proFile.open(QIODevice::ReadWrite));
1041
1042
1043
    proFile.write("SOURCES = ");
    proFile.write(mainFile);
    proFile.write("\nTARGET = doit\n");
1044
    proFile.write("\nCONFIG -= app_bundle\n");
1045
1046
    proFile.write("\nCONFIG -= release\n");
    proFile.write("\nCONFIG += debug\n");
hjk's avatar
hjk committed
1047
1048
1049
1050
    if (data.useQt)
        proFile.write("QT -= widgets gui\n");
    else
        proFile.write("CONFIG -= QT\n");
1051
1052
    if (m_useGLibCxxDebug)
        proFile.write("DEFINES += _GLIBCXX_DEBUG\n");
hjk's avatar
hjk committed
1053
    if (m_debuggerEngine == GdbEngine && m_debuggerVersion < 70500)
1054
        proFile.write("QMAKE_CXXFLAGS += -gdwarf-3\n");
hjk's avatar
hjk committed
1055
    proFile.write(data.profileExtra);
hjk's avatar
hjk committed
1056
1057
    proFile.close();

1058
1059
    QFile source(t->buildPath + QLatin1Char('/') + QLatin1String(mainFile));
    QVERIFY(source.open(QIODevice::ReadWrite));
1060
1061
1062
1063
    QByteArray fullCode = QByteArray() +
            "\n\n#if defined(_MSC_VER)" + (data.useQt ?
                "\n#include <qt_windows.h>" :
                "\n#include <Windows.h>") +
1064
                "\n#define BREAK int *nullPtr = 0; *nullPtr = 0;"
1065
                "\n\nvoid unused(const void *first,...) { (void) first; }"
1066
            "\n#else"
1067
1068
1069
1070
1071
1072
1073
1074
                "\n#include <stdint.h>\n"
                "\n#define BREAK do { asm(\"int $3\"); } while (0)"
                "\n"
                "\nstatic volatile int64_t unused_dummy;\n"
                "\nvoid __attribute__((optimize(\"O0\"))) unused(const void *first,...)\n"
                "\n{\n"
                "\n    unused_dummy += (int64_t)first;\n"
                "\n}\n"
1075
1076
            "\n#endif"
            "\n"
hjk's avatar
hjk committed
1077
            "\n\n" + data.includes +
1078
1079
1080
1081
1082
1083
1084
            "\n\n" + (data.useQHash ?
                "\n#include <QByteArray>"
                "\n#if QT_VERSION >= 0x050000"
                "\nQT_BEGIN_NAMESPACE"
                "\nQ_CORE_EXPORT extern QBasicAtomicInt qt_qhash_seed; // from qhash.cpp"
                "\nQT_END_NAMESPACE"
                "\n#endif" : "") +
hjk's avatar
hjk committed
1085
            "\n\nint main(int argc, char *argv[])"
hjk's avatar
hjk committed
1086
            "\n{"
1087
            "\n    int qtversion = " + (data.useQt ? "QT_VERSION" : "0") + ";"
hjk's avatar
hjk committed
1088
1089
1090
1091
1092
            "\n#ifdef __GNUC__"
            "\n    int gccversion = 0x10000 * __GNUC__ + 0x100 * __GNUC_MINOR__;"
            "\n#else"
            "\n    int gccversion = 0;"
            "\n#endif"
1093
1094
            "\n" + (data.useQHash ?
                "\n#if QT_VERSION >= 0x050000"
1095
                "\nqt_qhash_seed.store(0);"