debuggerprotocol.cpp 28.4 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3
4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con 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.
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.
hjk's avatar
hjk 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
con's avatar
con committed
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

31
#include "debuggerprotocol.h"
hjk's avatar
hjk committed
32

33
34
#include <QCoreApplication>
#include <QDateTime>
35
#include <QDebug>
36
#include <QHostAddress>
37
#include <QRegExp>
38
#include <QTimeZone>
con's avatar
con committed
39

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
40
41
#include <ctype.h>

42
43
44
45
46
#define QTC_ASSERT_STRINGIFY_HELPER(x) #x
#define QTC_ASSERT_STRINGIFY(x) QTC_ASSERT_STRINGIFY_HELPER(x)
#define QTC_ASSERT_STRING(cond) qDebug("SOFT ASSERT: \"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
#define QTC_ASSERT(cond, action) if (cond) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)

con's avatar
con committed
47
48
49
namespace Debugger {
namespace Internal {

50
51
52
53
54
55
56
57
uchar fromhex(uchar c)
{
    if (c >= '0' && c <= '9')
        return c - '0';
    if (c >= 'a' && c <= 'z')
        return 10 + c - 'a';
    if (c >= 'A' && c <= 'Z')
        return 10 + c - 'A';
58
    return UCHAR_MAX;
59
60
}

61
62
63
64
65
66
void skipCommas(const char *&from, const char *to)
{
    while (*from == ',' && from != to)
        ++from;
}

67
void GdbMi::parseResultOrValue(const char *&from, const char *to)
con's avatar
con committed
68
{
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
69
    while (from != to && isspace(*from))
con's avatar
con committed
70
71
        ++from;

72
    //qDebug() << "parseResultOrValue: " << QByteArray(from, to - from);
con's avatar
con committed
73
74
    parseValue(from, to);
    if (isValid()) {
75
        //qDebug() << "no valid result in " << QByteArray(from, to - from);
con's avatar
con committed
76
77
78
79
        return;
    }
    if (from == to || *from == '(')
        return;
80
    const char *ptr = from;
con's avatar
con committed
81
82
83
84
85
86
87
88
89
90
91
92
    while (ptr < to && *ptr != '=') {
        //qDebug() << "adding" << QChar(*ptr) << "to name";
        ++ptr;
    }
    m_name = QByteArray(from, ptr - from);
    from = ptr;
    if (from < to && *from == '=') {
        ++from;
        parseValue(from, to);
    }
}

93
QByteArray GdbMi::parseCString(const char *&from, const char *to)
con's avatar
con committed
94
95
{
    QByteArray result;
96
    //qDebug() << "parseCString: " << QByteArray(from, to - from);
con's avatar
con committed
97
98
    if (*from != '"') {
        qDebug() << "MI Parse Error, double quote expected";
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
99
        ++from; // So we don't hang
con's avatar
con committed
100
101
        return QByteArray();
    }
102
    const char *ptr = from;
con's avatar
con committed
103
104
105
106
107
108
109
    ++ptr;
    while (ptr < to) {
        if (*ptr == '"') {
            ++ptr;
            result = QByteArray(from + 1, ptr - from - 2);
            break;
        }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
110
        if (*ptr == '\\') {
con's avatar
con committed
111
            ++ptr;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
112
113
114
115
116
117
            if (ptr == to) {
                qDebug() << "MI Parse Error, unterminated backslash escape";
                from = ptr; // So we don't hang
                return QByteArray();
            }
        }
con's avatar
con committed
118
119
        ++ptr;
    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
120
    from = ptr;
con's avatar
con committed
121

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
    int idx = result.indexOf('\\');
    if (idx >= 0) {
        char *dst = result.data() + idx;
        const char *src = dst + 1, *end = result.data() + result.length();
        do {
            char c = *src++;
            switch (c) {
                case 'a': *dst++ = '\a'; break;
                case 'b': *dst++ = '\b'; break;
                case 'f': *dst++ = '\f'; break;
                case 'n': *dst++ = '\n'; break;
                case 'r': *dst++ = '\r'; break;
                case 't': *dst++ = '\t'; break;
                case 'v': *dst++ = '\v'; break;
                case '"': *dst++ = '"'; break;
                case '\\': *dst++ = '\\'; break;
138
139
140
141
142
143
                case 'x': {
                        c = *src++;
                        int chars = 0;
                        uchar prod = 0;
                        while (true) {
                            uchar val = fromhex(c);
144
                            if (val == UCHAR_MAX)
145
146
147
148
149
150
151
152
153
154
155
156
157
                                break;
                            prod = prod * 16 + val;
                            if (++chars == 3 || src == end)
                                break;
                            c = *src++;
                        }
                        if (!chars) {
                            qDebug() << "MI Parse Error, unrecognized hex escape";
                            return QByteArray();
                        }
                        *dst++ = prod;
                        break;
                    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
                default:
                    {
                        int chars = 0;
                        uchar prod = 0;
                        forever {
                            if (c < '0' || c > '7') {
                                --src;
                                break;
                            }
                            prod = prod * 8 + c - '0';
                            if (++chars == 3 || src == end)
                                break;
                            c = *src++;
                        }
                        if (!chars) {
                            qDebug() << "MI Parse Error, unrecognized backslash escape";
                            return QByteArray();
                        }
                        *dst++ = prod;
                    }
            }
            while (src != end) {
                char c = *src++;
                if (c == '\\')
                    break;
                *dst++ = c;
            }
        } while (src != end);
        *dst = 0;
        result.truncate(dst - result.data());
con's avatar
con committed
188
189
190
191
192
    }

    return result;
}

193
void GdbMi::parseValue(const char *&from, const char *to)
con's avatar
con committed
194
{
195
    //qDebug() << "parseValue: " << QByteArray(from, to - from);
con's avatar
con committed
196
    switch (*from) {
hjk's avatar
hjk committed
197
198
199
200
201
202
203
204
205
206
207
208
        case '{':
            parseTuple(from, to);
            break;
        case '[':
            parseList(from, to);
            break;
        case '"':
            m_type = Const;
            m_data = parseCString(from, to);
            break;
        default:
            break;
con's avatar
con committed
209
210
211
212
    }
}


213
void GdbMi::parseTuple(const char *&from, const char *to)
con's avatar
con committed
214
{
215
    //qDebug() << "parseTuple: " << QByteArray(from, to - from);
216
    //QTC_CHECK(*from == '{');
con's avatar
con committed
217
218
219
220
    ++from;
    parseTuple_helper(from, to);
}

221
void GdbMi::parseTuple_helper(const char *&from, const char *to)
con's avatar
con committed
222
{
223
    skipCommas(from, to);
224
    //qDebug() << "parseTuple_helper: " << QByteArray(from, to - from);
con's avatar
con committed
225
226
227
228
229
230
231
232
233
234
235
    m_type = Tuple;
    while (from < to) {
        if (*from == '}') {
            ++from;
            break;
        }
        GdbMi child;
        child.parseResultOrValue(from, to);
        //qDebug() << "\n=======\n" << qPrintable(child.toString()) << "\n========\n";
        if (!child.isValid())
            return;
236
        m_children.push_back(child);
237
        skipCommas(from, to);
con's avatar
con committed
238
239
240
    }
}

241
void GdbMi::parseList(const char *&from, const char *to)
con's avatar
con committed
242
{
243
    //qDebug() << "parseList: " << QByteArray(from, to - from);
244
    //QTC_CHECK(*from == '[');
con's avatar
con committed
245
246
    ++from;
    m_type = List;
247
    skipCommas(from, to);
con's avatar
con committed
248
249
250
251
252
253
254
255
    while (from < to) {
        if (*from == ']') {
            ++from;
            break;
        }
        GdbMi child;
        child.parseResultOrValue(from, to);
        if (child.isValid())
256
            m_children.push_back(child);
257
        skipCommas(from, to);
con's avatar
con committed
258
259
260
261
262
263
264
265
266
267
    }
}

static QByteArray ind(int indent)
{
    return QByteArray(2 * indent, ' ');
}

void GdbMi::dumpChildren(QByteArray * str, bool multiline, int indent) const
{
268
    for (size_t i = 0; i < m_children.size(); ++i) {
con's avatar
con committed
269
270
271
272
273
274
275
276
277
278
279
        if (i != 0) {
            *str += ',';
            if (multiline)
                *str += '\n';
        }
        if (multiline)
            *str += ind(indent);
        *str += m_children.at(i).toString(multiline, indent);
    }
}

hjk's avatar
hjk committed
280
QByteArray GdbMi::escapeCString(const QByteArray &ba)
281
{
hjk's avatar
hjk committed
282
    QByteArray ret;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
283
284
    ret.reserve(ba.length() * 2);
    for (int i = 0; i < ba.length(); ++i) {
hjk's avatar
hjk committed
285
        const uchar c = ba.at(i);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
286
        switch (c) {
hjk's avatar
hjk committed
287
288
289
290
291
292
293
294
295
            case '\\': ret += "\\\\"; break;
            case '\a': ret += "\\a"; break;
            case '\b': ret += "\\b"; break;
            case '\f': ret += "\\f"; break;
            case '\n': ret += "\\n"; break;
            case '\r': ret += "\\r"; break;
            case '\t': ret += "\\t"; break;
            case '\v': ret += "\\v"; break;
            case '"': ret += "\\\""; break;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
296
297
            default:
                if (c < 32 || c == 127) {
hjk's avatar
hjk committed
298
299
300
301
                    ret += '\\';
                    ret += ('0' + (c >> 6));
                    ret += ('0' + ((c >> 3) & 7));
                    ret += ('0' + (c & 7));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
302
                } else {
hjk's avatar
hjk committed
303
                    ret += c;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
304
305
306
307
                }
        }
    }
    return ret;
308
309
}

con's avatar
con committed
310
311
312
313
QByteArray GdbMi::toString(bool multiline, int indent) const
{
    QByteArray result;
    switch (m_type) {
hjk's avatar
hjk committed
314
315
316
317
318
319
        case Invalid:
            if (multiline)
                result += ind(indent) + "Invalid\n";
            else
                result += "Invalid";
            break;
320
        case Const:
hjk's avatar
hjk committed
321
            if (!m_name.isEmpty())
322
323
                result += m_name + '=';
            result += '"' + escapeCString(m_data) + '"';
hjk's avatar
hjk committed
324
325
326
            break;
        case Tuple:
            if (!m_name.isEmpty())
327
                result += m_name + '=';
hjk's avatar
hjk committed
328
329
330
            if (multiline) {
                result += "{\n";
                dumpChildren(&result, multiline, indent + 1);
331
                result += '\n' + ind(indent) + '}';
hjk's avatar
hjk committed
332
            } else {
333
                result += '{';
hjk's avatar
hjk committed
334
                dumpChildren(&result, multiline, indent + 1);
335
                result += '}';
hjk's avatar
hjk committed
336
337
338
339
            }
            break;
        case List:
            if (!m_name.isEmpty())
340
                result += m_name + '=';
hjk's avatar
hjk committed
341
342
343
            if (multiline) {
                result += "[\n";
                dumpChildren(&result, multiline, indent + 1);
344
                result += '\n' + ind(indent) + ']';
hjk's avatar
hjk committed
345
            } else {
346
                result += '[';
hjk's avatar
hjk committed
347
                dumpChildren(&result, multiline, indent + 1);
348
                result += ']';
hjk's avatar
hjk committed
349
350
            }
            break;
con's avatar
con committed
351
352
353
354
355
356
    }
    return result;
}

void GdbMi::fromString(const QByteArray &ba)
{
357
358
    const char *from = ba.constBegin();
    const char *to = ba.constEnd();
con's avatar
con committed
359
360
361
    parseResultOrValue(from, to);
}

362
363
364
365
366
367
368
void GdbMi::fromStringMultiple(const QByteArray &ba)
{
    const char *from = ba.constBegin();
    const char *to = ba.constEnd();
    parseTuple_helper(from, to);
}

369
const GdbMi &GdbMi::operator[](const char *name) const
con's avatar
con committed
370
{
371
    static GdbMi empty;
372
    for (int i = 0, n = int(m_children.size()); i < n; ++i)
con's avatar
con committed
373
374
        if (m_children.at(i).m_name == name)
            return m_children.at(i);
375
    return empty;
con's avatar
con committed
376
377
}

378
379
380
381
382
383
384
qulonglong GdbMi::toAddress() const
{
    QByteArray ba = m_data;
    if (ba.endsWith('L'))
        ba.chop(1);
    if (ba.startsWith('*') || ba.startsWith('@'))
        ba = ba.mid(1);
385
    return ba.toULongLong(0, 0);
386
387
}

con's avatar
con committed
388
389
//////////////////////////////////////////////////////////////////////////////////
//
hjk's avatar
hjk committed
390
// GdbResponse
con's avatar
con committed
391
392
393
//
//////////////////////////////////////////////////////////////////////////////////

394
QByteArray DebuggerResponse::stringFromResultClass(ResultClass resultClass)
con's avatar
con committed
395
396
{
    switch (resultClass) {
397
398
399
400
401
        case ResultDone: return "done";
        case ResultRunning: return "running";
        case ResultConnected: return "connected";
        case ResultError: return "error";
        case ResultExit: return "exit";
con's avatar
con committed
402
403
        default: return "unknown";
    }
404
}
con's avatar
con committed
405

406
QByteArray DebuggerResponse::toString() const
con's avatar
con committed
407
408
409
410
411
412
413
414
415
416
417
418
{
    QByteArray result;
    if (token != -1)
        result = QByteArray::number(token);
    result += '^';
    result += stringFromResultClass(resultClass);
    if (data.isValid())
        result += ',' + data.toString();
    result += '\n';
    return result;
}

419
420
421
422
423
424
425

//////////////////////////////////////////////////////////////////////////////////
//
// GdbResponse
//
//////////////////////////////////////////////////////////////////////////////////

426
427
// Tested in tests/auto/debugger/tst_gdb.cpp

428
void extractGdbVersion(const QString &msg,
429
    int *gdbVersion, int *gdbBuildVersion, bool *isMacGdb, bool *isQnxGdb)
430
431
432
{
    const QChar dot(QLatin1Char('.'));

433
434
435
    const bool ignoreParenthesisContent = msg.contains(QLatin1String("rubenvb"))
                                       || msg.contains(QLatin1String("openSUSE"));

436
437
438
    const QChar parOpen(QLatin1Char('('));
    const QChar parClose(QLatin1Char(')'));

439
440
441
    QString cleaned;
    QString build;
    bool inClean = true;
442
    bool inParenthesis = false;
443
444
445
    foreach (QChar c, msg) {
        if (inClean && !cleaned.isEmpty() && c != dot && (c.isPunct() || c.isSpace()))
            inClean = false;
446
447
448
449
450
451
452
453
        if (ignoreParenthesisContent) {
            if (!inParenthesis && c == parOpen)
                inParenthesis = true;
            if (inParenthesis && c == parClose)
                inParenthesis = false;
            if (inParenthesis)
                continue;
        }
454
455
456
457
458
459
460
461
462
463
464
465
466
467
        if (inClean) {
            if (c.isDigit())
                cleaned.append(c);
            else if (!cleaned.isEmpty() && !cleaned.endsWith(dot))
                cleaned.append(dot);
        } else {
            if (c.isDigit())
                build.append(c);
            else if (!build.isEmpty() && !build.endsWith(dot))
                build.append(dot);
        }
    }

    *isMacGdb = msg.contains(QLatin1String("Apple version"));
468
    *isQnxGdb = msg.contains(QLatin1String("qnx"));
469
470
471
472
473
474
475
476
477
478
479
480
481

    *gdbVersion = 10000 * cleaned.section(dot, 0, 0).toInt()
                  + 100 * cleaned.section(dot, 1, 1).toInt()
                    + 1 * cleaned.section(dot, 2, 2).toInt();
    if (cleaned.count(dot) >= 3)
        *gdbBuildVersion = cleaned.section(dot, 3, 3).toInt();
    else
        *gdbBuildVersion = build.section(dot, 0, 0).toInt();

    if (*isMacGdb)
        *gdbBuildVersion = build.section(dot, 1, 1).toInt();
}

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
//////////////////////////////////////////////////////////////////////////////////
//
// Decoding
//
//////////////////////////////////////////////////////////////////////////////////

static QString quoteUnprintableLatin1(const QByteArray &ba)
{
    QString res;
    char buf[10];
    for (int i = 0, n = ba.size(); i != n; ++i) {
        const unsigned char c = ba.at(i);
        if (isprint(c)) {
            res += QLatin1Char(c);
        } else {
            qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
            res += QLatin1String(buf);
        }
    }
    return res;
}

static QDate dateFromData(int jd)
{
    return jd ? QDate::fromJulianDay(jd) : QDate();
}

static QTime timeFromData(int ms)
{
    return ms == -1 ? QTime() : QTime(0, 0, 0, 0).addMSecs(ms);
}

514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
// Stolen and adapted from qdatetime.cpp
static void getDateTime(qint64 msecs, int status, QDate *date, QTime *time)
{
    enum {
        SECS_PER_DAY = 86400,
        MSECS_PER_DAY = 86400000,
        SECS_PER_HOUR = 3600,
        MSECS_PER_HOUR = 3600000,
        SECS_PER_MIN = 60,
        MSECS_PER_MIN = 60000,
        TIME_T_MAX = 2145916799,  // int maximum 2037-12-31T23:59:59 UTC
        JULIAN_DAY_FOR_EPOCH = 2440588 // result of julianDayFromDate(1970, 1, 1)
    };

    // Status of date/time
    enum StatusFlag {
        NullDate            = 0x01,
        NullTime            = 0x02,
        ValidDate           = 0x04,
        ValidTime           = 0x08,
        ValidDateTime       = 0x10,
        TimeZoneCached      = 0x20,
        SetToStandardTime   = 0x40,
        SetToDaylightTime   = 0x80
    };

    qint64 jd = JULIAN_DAY_FOR_EPOCH;
    qint64 ds = 0;

    if (qAbs(msecs) >= MSECS_PER_DAY) {
        jd += (msecs / MSECS_PER_DAY);
        msecs %= MSECS_PER_DAY;
    }

    if (msecs < 0) {
        ds = MSECS_PER_DAY - msecs - 1;
        jd -= ds / MSECS_PER_DAY;
        ds = ds % MSECS_PER_DAY;
        ds = MSECS_PER_DAY - ds - 1;
    } else {
        ds = msecs;
    }

    *date = (status & NullDate) ? QDate() : QDate::fromJulianDay(jd);
    *time = (status & NullTime) ? QTime() : QTime::fromMSecsSinceStartOfDay(ds);
}

561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
QString decodeData(const QByteArray &ba, int encoding)
{
    switch (encoding) {
        case Unencoded8Bit: // 0
            return quoteUnprintableLatin1(ba);
        case Base64Encoded8BitWithQuotes: { // 1, used for QByteArray
            const QChar doubleQuote(QLatin1Char('"'));
            QString rc = doubleQuote;
            rc += quoteUnprintableLatin1(QByteArray::fromBase64(ba));
            rc += doubleQuote;
            return rc;
        }
        case Base64Encoded16BitWithQuotes: { // 2, used for QString
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromBase64(ba);
            QString rc = doubleQuote;
            rc += QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2);
            rc += doubleQuote;
            return rc;
        }
        case Base64Encoded32BitWithQuotes: { // 3
            const QByteArray decodedBa = QByteArray::fromBase64(ba);
            const QChar doubleQuote(QLatin1Char('"'));
            QString rc = doubleQuote;
            rc += QString::fromUcs4(reinterpret_cast<const uint *>
                (decodedBa.data()), decodedBa.size() / 4);
            rc += doubleQuote;
            return rc;
        }
        case Base64Encoded16Bit: { // 4, without quotes (see 2)
            const QByteArray decodedBa = QByteArray::fromBase64(ba);
            return QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2);
        }
        case Base64Encoded8Bit: { // 5, without quotes (see 1)
            return quoteUnprintableLatin1(QByteArray::fromBase64(ba));
        }
        case Hex2EncodedLatin1WithQuotes: { // 6, %02x encoded 8 bit Latin1 data
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromHex(ba);
602
            return doubleQuote + QString::fromLatin1(decodedBa, decodedBa.size()) + doubleQuote;
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
        }
        case Hex4EncodedLittleEndianWithQuotes: { // 7, %04x encoded 16 bit data
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            return doubleQuote + QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2) + doubleQuote;
        }
        case Hex8EncodedLittleEndianWithQuotes: { // 8, %08x encoded 32 bit data
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
                (decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
        }
        case Hex2EncodedUtf8WithQuotes: { // 9, %02x encoded 8 bit UTF-8 data
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            return doubleQuote + QString::fromUtf8(decodedBa) + doubleQuote;
        }
        case Hex8EncodedBigEndian: { // 10, %08x encoded 32 bit data
            const QChar doubleQuote(QLatin1Char('"'));
            QByteArray decodedBa = QByteArray::fromHex(ba);
            for (int i = 0; i < decodedBa.size() - 3; i += 4) {
                char c = decodedBa.at(i);
                decodedBa[i] = decodedBa.at(i + 3);
                decodedBa[i + 3] = c;
                c = decodedBa.at(i + 1);
                decodedBa[i + 1] = decodedBa.at(i + 2);
                decodedBa[i + 2] = c;
            }
            return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
                (decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
        }
        case Hex4EncodedBigEndianWithQuotes: { // 11, %04x encoded 16 bit data
            const QChar doubleQuote(QLatin1Char('"'));
            QByteArray decodedBa = QByteArray::fromHex(ba);
            for (int i = 0; i < decodedBa.size(); i += 2) {
                char c = decodedBa.at(i);
                decodedBa[i] = decodedBa.at(i + 1);
                decodedBa[i + 1] = c;
            }
            return doubleQuote + QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2) + doubleQuote;
        }
        case Hex4EncodedLittleEndianWithoutQuotes: { // 12, see 7, without quotes
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            return QString::fromUtf16(reinterpret_cast<const ushort *>
                (decodedBa.data()), decodedBa.size() / 2);
        }
        case Hex2EncodedLocal8BitWithQuotes: { // 13, %02x encoded 8 bit UTF-8 data
            const QChar doubleQuote(QLatin1Char('"'));
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            return doubleQuote + QString::fromLocal8Bit(decodedBa) + doubleQuote;
        }
        case JulianDate: { // 14, an integer count
            const QDate date = dateFromData(ba.toInt());
658
            return date.isValid() ? date.toString(Qt::TextDate) : QLatin1String("(invalid)");
659
660
661
        }
        case MillisecondsSinceMidnight: {
            const QTime time = timeFromData(ba.toInt());
662
            return time.isValid() ? time.toString(Qt::TextDate) : QLatin1String("(invalid)");
663
664
665
666
667
        }
        case JulianDateAndMillisecondsSinceMidnight: {
            const int p = ba.indexOf('/');
            const QDate date = dateFromData(ba.left(p).toInt());
            const QTime time = timeFromData(ba.mid(p + 1 ).toInt());
668
669
            const QDateTime dateTime = QDateTime(date, time);
            return dateTime.isValid() ? dateTime.toString(Qt::TextDate) : QLatin1String("(invalid)");
670
        }
671
672
673
674
675
676
677
678
679
680
681
682
        case IPv6AddressAndHexScopeId: { // 27, 16 hex-encoded bytes, "%" and the string-encoded scope
            const int p = ba.indexOf('%');
            QHostAddress ip6(QString::fromLatin1(p == -1 ? ba : ba.left(p)));
            if (ip6.isNull())
                break;

            const QByteArray scopeId = p == -1 ? QByteArray() : QByteArray::fromHex(ba.mid(p + 1));
            if (!scopeId.isEmpty())
                ip6.setScopeId(QString::fromUtf16(reinterpret_cast<const ushort *>(scopeId.constData()),
                                                  scopeId.length() / 2));
            return ip6.toString();
        }
683
684
685
686
        case Hex2EncodedUtf8WithoutQuotes: { // 28, %02x encoded 8 bit UTF-8 data without quotes
            const QByteArray decodedBa = QByteArray::fromHex(ba);
            return QString::fromUtf8(decodedBa);
        }
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
        case DateTimeInternal: { // 29, DateTimeInternal: msecs, spec, offset, tz, status
            int p0 = ba.indexOf('/');
            int p1 = ba.indexOf('/', p0 + 1);
            int p2 = ba.indexOf('/', p1 + 1);
            int p3 = ba.indexOf('/', p2 + 1);

            qint64 msecs = ba.left(p0).toLongLong();
            ++p0;
            Qt::TimeSpec spec = Qt::TimeSpec(ba.mid(p0, p1 - p0).toInt());
            ++p1;
            qulonglong offset = ba.mid(p1, p2 - p1).toInt();
            ++p2;
            QByteArray timeZoneId = QByteArray::fromHex(ba.mid(p2, p3 - p2));
            ++p3;
            int status = ba.mid(p3).toInt();

            QDate date;
            QTime time;
            getDateTime(msecs, status, &date, &time);

            QDateTime dateTime;
            if (spec == Qt::OffsetFromUTC) {
                dateTime = QDateTime(date, time, spec, offset);
            } else if (spec == Qt::TimeZone) {
                if (!QTimeZone::isTimeZoneIdAvailable(timeZoneId))
                    return QLatin1String("<unavailable>");
                dateTime = QDateTime(date, time, QTimeZone(timeZoneId));
            } else {
                dateTime = QDateTime(date, time, spec);
            }
            return dateTime.toString();
718
        }
hjk's avatar
hjk committed
719
720
721
722
723
724
725
726
727
728
729
730
        case Hex2EncodedFloat4: {
            const QByteArray s = QByteArray::fromHex(ba);
            QTC_ASSERT(s.size() == 4, break);
            union { char c[4]; float f; } u = { { s[3], s[2], s[1], s[0] } };
            return QString::number(u.f);
        }
        case Hex2EncodedFloat8: {
            const QByteArray s = QByteArray::fromHex(ba);
            QTC_ASSERT(s.size() == 8, break);
            union { char c[8]; double d; } u = { { s[7], s[6], s[5], s[4], s[3], s[2], s[1], s[0] } };
            return QString::number(u.d);
        }
731
732
733
734
735
736
737
738
739
740
741
742
743
        case SpecialEmptyValue: {
            return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<empty>");
        }
        case SpecialUninitializedValue:  {
            return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<uninitialized>");
        }
        case SpecialInvalidValue:  {
            return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<invalid>");
        }
        case SpecialNotAccessibleValue:  {
            return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<not accessible>");
        }
        case SpecialItemCountValue:  {
744
            return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<%n items>", 0, ba.toInt());
745
746
        }
        case SpecialMinimumItemCountValue:  {
747
            return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<at least %n items>", 0, ba.toInt());
748
749
750
751
752
753
754
755
756
757
758
759
760
        }
        case SpecialNotCallableValue:  {
            return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<not callable>");
        }
        case SpecialNullReferenceValue:  {
            return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<null reference>");
        }
        case SpecialOptimizedOutValue:  {
            return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<optimized out>");
        }
        case SpecialEmptyStructureValue:  {
            return QLatin1String("{...}");
        }
761
762
763
764
765
    }
    qDebug() << "ENCODING ERROR: " << encoding;
    return QCoreApplication::translate("Debugger", "<Encoding error>");
}

766
767
768
769
770
771
//////////////////////////////////////////////////////////////////////////////////
//
// DebuggerCommand
//
//////////////////////////////////////////////////////////////////////////////////

772
void DebuggerCommand::argHelper(const char *name, const QByteArray &data)
773
774
775
776
777
778
779
780
{
    args.append('"');
    args.append(name);
    args.append("\":");
    args.append(data);
    args.append(",");
}

781
void DebuggerCommand::arg(const char *name, int value)
782
{
783
    argHelper(name, QByteArray::number(value));
784
785
}

786
void DebuggerCommand::arg(const char *name, qlonglong value)
787
{
788
    argHelper(name, QByteArray::number(value));
789
790
}

791
void DebuggerCommand::arg(const char *name, qulonglong value)
792
{
793
    argHelper(name, QByteArray::number(value));
794
795
}

796
void DebuggerCommand::arg(const char *name, const QString &value)
797
{
798
    arg(name, value.toUtf8().data());
799
800
}

801
void DebuggerCommand::arg(const char *name, const QByteArray &value)
802
{
803
    arg(name, value.data());
804
805
}

806
void DebuggerCommand::arg(const char *name, const char *value)
807
808
809
810
811
812
813
814
{
    args.append('"');
    args.append(name);
    args.append("\":\"");
    args.append(value);
    args.append("\",");
}

815
816
817
818
819
820
821
822
823
824
void DebuggerCommand::arg(const char *name, const QList<int> &list)
{
    beginList(name);
    foreach (int item, list) {
        args.append(QByteArray::number(item));
        args.append(',');
    }
    endList();
}

825
void DebuggerCommand::arg(const char *value)
826
827
828
829
830
831
{
    args.append("\"");
    args.append(value);
    args.append("\",");
}

832
void DebuggerCommand::beginList(const char *name)
833
834
835
836
837
838
839
840
841
{
    if (name) {
        args += '"';
        args += name;
        args += "\":";
    }
    args += '[';
}

842
void DebuggerCommand::endList()
843
844
845
846
847
848
{
    if (args.endsWith(','))
        args.chop(1);
    args += "],";
}

849
void DebuggerCommand::beginGroup(const char *name)
850
851
852
853
854
855
856
857
858
{
    if (name) {
        args += '"';
        args += name;
        args += "\":";
    }
    args += '{';
}

859
void DebuggerCommand::endGroup()
860
861
862
863
864
865
{
    if (args.endsWith(','))
        args.chop(1);
    args += "},";
}

866
867
868
869
870
871
872
873
QByteArray DebuggerCommand::arguments() const
{
    QByteArray result = args;
    if (result.endsWith(','))
        result.chop(1);
    return result;
}

con's avatar
con committed
874
875
} // namespace Internal
} // namespace Debugger