1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "debuggerprotocol.h"
27
28 #include <QCoreApplication>
29 #include <QDateTime>
30 #include <QDebug>
31 #include <QHostAddress>
32 #include <QTimeZone>
33 #include <QJsonArray>
34 #include <QJsonDocument>
35
36 #include <ctype.h>
37
38 #include <utils/processhandle.h>
39
40 #define QTC_ASSERT_STRINGIFY_HELPER(x) #x
41 #define QTC_ASSERT_STRINGIFY(x) QTC_ASSERT_STRINGIFY_HELPER(x)
42 #define QTC_ASSERT_STRING(cond) qDebug("SOFT ASSERT: \"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
43 #define QTC_ASSERT(cond, action) if (cond) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)
44 #define QTC_CHECK(cond) if (cond) {} else { QTC_ASSERT_STRING(#cond); } do {} while (0)
45
46 namespace Debugger {
47 namespace Internal {
48
fromhex(uchar c)49 static uchar fromhex(uchar c)
50 {
51 if (c >= '0' && c <= '9')
52 return c - '0';
53 if (c >= 'a' && c <= 'z')
54 return 10 + c - 'a';
55 if (c >= 'A' && c <= 'Z')
56 return 10 + c - 'A';
57 return UCHAR_MAX;
58 }
59
60 // DebuggerOutputParser
61
DebuggerOutputParser(const QString & output)62 DebuggerOutputParser::DebuggerOutputParser(const QString &output)
63 : from(output.begin()), to(output.end())
64 {
65 }
66
skipCommas()67 void DebuggerOutputParser::skipCommas()
68 {
69 while (from < to && *from == ',')
70 ++from;
71 }
72
skipSpaces()73 void DebuggerOutputParser::skipSpaces()
74 {
75 while (from < to && isspace(from->unicode()))
76 ++from;
77 }
78
readString(const std::function<bool (char)> & isValidChar)79 QString DebuggerOutputParser::readString(const std::function<bool(char)> &isValidChar)
80 {
81 QString res;
82 while (from < to && isValidChar(from->unicode()))
83 res += *from++;
84 return res;
85 }
86
readInt()87 int DebuggerOutputParser::readInt()
88 {
89 int res = 0;
90 while (from < to && *from >= '0' && *from <= '9') {
91 res *= 10;
92 res += (*from++).unicode() - '0';
93 }
94 return res;
95 }
96
readChar()97 QChar DebuggerOutputParser::readChar()
98 {
99 return *from++;
100 }
101
isNameChar(char c)102 static bool isNameChar(char c)
103 {
104 return c != '=' && c != ':' && c != ']' && !isspace(c);
105 }
106
parseResultOrValue(DebuggerOutputParser & parser)107 void GdbMi::parseResultOrValue(DebuggerOutputParser &parser)
108 {
109 parser.skipSpaces();
110
111 if (parser.isAtEnd())
112 return;
113
114 //qDebug() << "parseResultOrValue: " << parser.buffer();
115 parseValue(parser);
116 parser.skipSpaces();
117 if (isValid()) {
118 //qDebug() << "no valid result in " << parser.buffer();
119 return;
120 }
121 if (parser.isAtEnd())
122 return;
123 if (parser.isCurrent('(')) {
124 parser.advance();
125 return;
126 }
127
128 m_name = parser.readString(isNameChar);
129
130 if (!parser.isAtEnd() && parser.isCurrent('=')) {
131 parser.advance();
132 parseValue(parser);
133 }
134 }
135
136 // Reads one \ooo entity.
parseOctalEscapedHelper(DebuggerOutputParser & parser,QByteArray & buffer)137 static bool parseOctalEscapedHelper(DebuggerOutputParser &parser, QByteArray &buffer)
138 {
139 if (parser.remainingChars() < 4)
140 return false;
141 if (!parser.isCurrent('\\'))
142 return false;
143
144 const char c1 = parser.lookAhead(1).unicode();
145 const char c2 = parser.lookAhead(2).unicode();
146 const char c3 = parser.lookAhead(3).unicode();
147 if (!isdigit(c1) || !isdigit(c2) || !isdigit(c3))
148 return false;
149
150 buffer += char((c1 - '0') * 64 + (c2 - '0') * 8 + (c3 - '0'));
151 parser.advance(4);
152 return true;
153 }
154
parseHexEscapedHelper(DebuggerOutputParser & parser,QByteArray & buffer)155 static bool parseHexEscapedHelper(DebuggerOutputParser &parser, QByteArray &buffer)
156 {
157 if (parser.remainingChars() < 4)
158 return false;
159 if (!parser.isCurrent('\\'))
160 return false;
161 if (parser.lookAhead(1) != 'x')
162 return false;
163
164 const char c1 = parser.lookAhead(2).unicode();
165 const char c2 = parser.lookAhead(3).unicode();
166 if (!isxdigit(c1) || !isxdigit(c2))
167 return false;
168
169 buffer += char(16 * fromhex(c1) + fromhex(c2));
170 parser.advance(4);
171 return true;
172 }
173
parseSimpleEscape(DebuggerOutputParser & parser,QString & result)174 static void parseSimpleEscape(DebuggerOutputParser &parser, QString &result)
175 {
176 if (parser.isAtEnd()) {
177 qDebug() << "MI Parse Error, unterminated backslash escape";
178 return;
179 }
180
181 const QChar c = parser.current();
182 parser.advance();
183 switch (c.unicode()) {
184 case 'a': result += '\a'; break;
185 case 'b': result += '\b'; break;
186 case 'f': result += '\f'; break;
187 case 'n': result += '\n'; break;
188 case 'r': result += '\r'; break;
189 case 't': result += '\t'; break;
190 case 'v': result += '\v'; break;
191 case '"': result += '"'; break;
192 case '\'': result += '\''; break;
193 case '\\': result += '\\'; break;
194 default:
195 qDebug() << "MI Parse Error, unrecognized backslash escape";
196 }
197 }
198
199 // Reads subsequent \123 or \x12 entities and converts to Utf8,
200 // *or* one escaped char, *or* one unescaped char.
parseCharOrEscape(DebuggerOutputParser & parser,QString & result)201 static void parseCharOrEscape(DebuggerOutputParser &parser, QString &result)
202 {
203 QByteArray buffer;
204 while (parseOctalEscapedHelper(parser, buffer))
205 ;
206 while (parseHexEscapedHelper(parser, buffer))
207 ;
208
209 if (!buffer.isEmpty()) {
210 result.append(QString::fromUtf8(buffer));
211 } else if (parser.isCurrent('\\')) {
212 parser.advance();
213 parseSimpleEscape(parser, result);
214 } else {
215 result += parser.readChar();
216 }
217 }
218
readCString()219 QString DebuggerOutputParser::readCString()
220 {
221 if (isAtEnd())
222 return QString();
223
224 if (*from != '"') {
225 qDebug() << "MI Parse Error, double quote expected";
226 ++from; // So we don't hang
227 return QString();
228 }
229
230 ++from; // Skip initial quote.
231 QString result;
232 result.reserve(to - from);
233 while (from < to) {
234 if (*from == '"') {
235 ++from;
236 return result;
237 }
238 parseCharOrEscape(*this, result);
239 }
240
241 qDebug() << "MI Parse Error, unfinished string";
242 return QString();
243 }
244
parseValue(DebuggerOutputParser & parser)245 void GdbMi::parseValue(DebuggerOutputParser &parser)
246 {
247 if (parser.isAtEnd())
248 return;
249
250 //qDebug() << "parseValue: " << parser;
251 switch (parser.current().unicode()) {
252 case '{':
253 parseTuple(parser);
254 break;
255 case '[':
256 parseList(parser);
257 break;
258 case '"':
259 m_type = Const;
260 m_data = parser.readCString();
261 break;
262 default:
263 break;
264 }
265 }
266
parseTuple(DebuggerOutputParser & parser)267 void GdbMi::parseTuple(DebuggerOutputParser &parser)
268 {
269 //qDebug() << "parseTuple: " << parser.buffer();
270 QTC_CHECK(parser.isCurrent('{'));
271 parser.advance();
272 parseTuple_helper(parser);
273 }
274
parseTuple_helper(DebuggerOutputParser & parser)275 void GdbMi::parseTuple_helper(DebuggerOutputParser &parser)
276 {
277 parser.skipCommas();
278 //qDebug() << "parseTuple_helper: " << parser.buffer();
279 QString buf = parser.buffer();
280 m_type = Tuple;
281 while (!parser.isAtEnd()) {
282 if (parser.isCurrent('}')) {
283 parser.advance();
284 break;
285 }
286 GdbMi child;
287 child.parseResultOrValue(parser);
288 //qDebug() << "\n=======\n" << qPrintable(child.toString()) << "\n========\n";
289 if (!child.isValid())
290 return;
291 m_children.push_back(child);
292 parser.skipCommas();
293 }
294 }
295
parseList(DebuggerOutputParser & parser)296 void GdbMi::parseList(DebuggerOutputParser &parser)
297 {
298 //qDebug() << "parseList: " << parser.buffer();
299 QTC_CHECK(parser.isCurrent('['));
300 parser.advance();
301 m_type = List;
302 parser.skipCommas();
303 while (true) {
304 QTC_ASSERT(!parser.isAtEnd(), break);
305 if (parser.isCurrent(']')) {
306 parser.advance();
307 break;
308 }
309 GdbMi child;
310 child.parseResultOrValue(parser);
311 if (!child.isValid())
312 break;
313 m_children.push_back(child);
314 parser.skipCommas();
315 }
316 }
317
ind(int indent)318 static QString ind(int indent)
319 {
320 return QString(2 * indent, QChar(' '));
321 }
322
dumpChildren(QString * str,bool multiline,int indent) const323 void GdbMi::dumpChildren(QString * str, bool multiline, int indent) const
324 {
325 bool first = true;
326 for (const GdbMi &child : *this) {
327 if (first) {
328 first = false;
329 } else {
330 *str += ',';
331 if (multiline)
332 *str += '\n';
333 }
334
335 if (multiline)
336 *str += ind(indent);
337 *str += child.toString(multiline, indent);
338 }
339 }
340
escapeCString(const QString & ba)341 QString GdbMi::escapeCString(const QString &ba)
342 {
343 QString ret;
344 ret.reserve(ba.length() * 2);
345 for (int i = 0; i < ba.length(); ++i) {
346 const ushort c = ba.at(i).unicode();
347 switch (c) {
348 case '\\': ret += "\\\\"; break;
349 case '\a': ret += "\\a"; break;
350 case '\b': ret += "\\b"; break;
351 case '\f': ret += "\\f"; break;
352 case '\n': ret += "\\n"; break;
353 case '\r': ret += "\\r"; break;
354 case '\t': ret += "\\t"; break;
355 case '\v': ret += "\\v"; break;
356 case '"': ret += "\\\""; break;
357 default:
358 if (c < 32 || c == 127) {
359 ret += '\\';
360 ret += QLatin1Char('0' + (c >> 6));
361 ret += QLatin1Char('0' + ((c >> 3) & 7));
362 ret += QLatin1Char('0' + (c & 7));
363 } else {
364 ret += c;
365 }
366 }
367 }
368 return ret;
369 }
370
toString(bool multiline,int indent) const371 QString GdbMi::toString(bool multiline, int indent) const
372 {
373 QString result;
374 switch (m_type) {
375 case Invalid:
376 if (multiline)
377 result += ind(indent) + "Invalid\n";
378 else
379 result += "Invalid";
380 break;
381 case Const:
382 if (!m_name.isEmpty())
383 result += m_name + '=';
384 result += '"' + escapeCString(m_data) + '"';
385 break;
386 case Tuple:
387 if (!m_name.isEmpty())
388 result += m_name + '=';
389 if (multiline) {
390 result += "{\n";
391 dumpChildren(&result, multiline, indent + 1);
392 result += '\n' + ind(indent) + '}';
393 } else {
394 result += '{';
395 dumpChildren(&result, multiline, indent + 1);
396 result += '}';
397 }
398 break;
399 case List:
400 if (!m_name.isEmpty())
401 result += m_name + '=';
402 if (multiline) {
403 result += "[\n";
404 dumpChildren(&result, multiline, indent + 1);
405 result += '\n' + ind(indent) + ']';
406 } else {
407 result += '[';
408 dumpChildren(&result, multiline, indent + 1);
409 result += ']';
410 }
411 break;
412 }
413 return result;
414 }
415
fromString(const QString & ba)416 void GdbMi::fromString(const QString &ba)
417 {
418 DebuggerOutputParser parser(ba);
419 parseResultOrValue(parser);
420 }
421
fromStringMultiple(const QString & ba)422 void GdbMi::fromStringMultiple(const QString &ba)
423 {
424 DebuggerOutputParser parser(ba);
425 parseTuple_helper(parser);
426 }
427
operator [](const char * name) const428 const GdbMi &GdbMi::operator[](const char *name) const
429 {
430 static GdbMi empty;
431 for (const GdbMi &child : *this)
432 if (child.m_name == QLatin1String(name))
433 return child;
434 return empty;
435 }
436
toAddress() const437 qulonglong GdbMi::toAddress() const
438 {
439 QString ba = m_data;
440 if (ba.endsWith('L'))
441 ba.chop(1);
442 if (ba.startsWith('*') || ba.startsWith('@'))
443 ba = ba.mid(1);
444 return ba.toULongLong(nullptr, 0);
445 }
446
toProcessHandle() const447 Utils::ProcessHandle GdbMi::toProcessHandle() const
448 {
449 return Utils::ProcessHandle(m_data.toULongLong());
450 }
451
452 //////////////////////////////////////////////////////////////////////////////////
453 //
454 // GdbResponse
455 //
456 //////////////////////////////////////////////////////////////////////////////////
457
stringFromResultClass(ResultClass resultClass)458 QString DebuggerResponse::stringFromResultClass(ResultClass resultClass)
459 {
460 switch (resultClass) {
461 case ResultDone: return QLatin1String("done");
462 case ResultRunning: return QLatin1String("running");
463 case ResultConnected: return QLatin1String("connected");
464 case ResultError: return QLatin1String("error");
465 case ResultExit: return QLatin1String("exit");
466 default: return QLatin1String("unknown");
467 }
468 }
469
toString() const470 QString DebuggerResponse::toString() const
471 {
472 QString result;
473 if (token != -1)
474 result = QString::number(token);
475 result += '^';
476 result += stringFromResultClass(resultClass);
477 if (data.isValid())
478 result += ',' + data.toString();
479 result += '\n';
480 return result;
481 }
482
483
484 //////////////////////////////////////////////////////////////////////////////////
485 //
486 // GdbResponse
487 //
488 //////////////////////////////////////////////////////////////////////////////////
489
490 // Tested in tests/auto/debugger/tst_gdb.cpp
491
492 //! Extract the GDB version number from the output of 'gdb --version'.
493 //! \param[out] gdbVersion GDB version "hash" with major*10000 + minor*100 + patch
494 //! e.g. version GDB 3.7.14 will set this to 30714
495 //! \param[out] gdbBuildVersion distribution dependent value
496 //! \note See the file tests/auto/debugger/tst_gdb.cpp for example conversions.
extractGdbVersion(const QString & msg,int * gdbVersion,int * gdbBuildVersion,bool * isMacGdb,bool * isQnxGdb)497 void extractGdbVersion(const QString &msg,
498 int *gdbVersion, int *gdbBuildVersion, bool *isMacGdb, bool *isQnxGdb)
499 {
500 const QChar dot('.');
501
502 const bool ignoreParenthesisContent = msg.contains("rubenvb")
503 || msg.contains("openSUSE")
504 || msg.contains("SUSE Linux Enterprise");
505
506 const QChar parOpen('(');
507 const QChar parClose(')');
508
509 QString cleaned;
510 QString build;
511 bool inClean = true;
512 bool inParenthesis = false;
513
514 int gdbMsgBegin = msg.indexOf("GNU gdb");
515 if (gdbMsgBegin == -1)
516 gdbMsgBegin = 0;
517
518 for (int i = gdbMsgBegin, gdbMsgSize = msg.size(); i < gdbMsgSize; ++i) {
519 QChar c = msg.at(i);
520 if (inClean && !cleaned.isEmpty() && c != dot && (c.isPunct() || c.isSpace()))
521 inClean = false;
522 if (ignoreParenthesisContent) {
523 if (!inParenthesis && c == parOpen)
524 inParenthesis = true;
525 if (inParenthesis && c == parClose)
526 inParenthesis = false;
527 if (inParenthesis)
528 continue;
529 }
530 if (inClean) {
531 if (c.isDigit())
532 cleaned.append(c);
533 else if (!cleaned.isEmpty() && !cleaned.endsWith(dot))
534 cleaned.append(dot);
535 } else {
536 if (c.isDigit())
537 build.append(c);
538 else if (!build.isEmpty() && !build.endsWith(dot))
539 build.append(dot);
540 }
541 }
542
543 *isMacGdb = msg.contains("Apple version");
544 *isQnxGdb = msg.contains("qnx");
545
546 *gdbVersion = 10000 * cleaned.section(dot, 0, 0).toInt()
547 + 100 * cleaned.section(dot, 1, 1).toInt()
548 + 1 * cleaned.section(dot, 2, 2).toInt();
549 if (cleaned.count(dot) >= 3)
550 *gdbBuildVersion = cleaned.section(dot, 3, 3).toInt();
551 else
552 *gdbBuildVersion = build.section(dot, 0, 0).toInt();
553
554 if (*isMacGdb)
555 *gdbBuildVersion = build.section(dot, 1, 1).toInt();
556 }
557
558 //////////////////////////////////////////////////////////////////////////////////
559 //
560 // Decoding
561 //
562 //////////////////////////////////////////////////////////////////////////////////
563
quoteUnprintableLatin1(const QString & ba)564 static QString quoteUnprintableLatin1(const QString &ba)
565 {
566 QString res;
567 char buf[10];
568 for (int i = 0, n = ba.size(); i != n; ++i) {
569 const unsigned char c = ba.at(i).unicode();
570 if (isprint(c)) {
571 res += ba.at(i);
572 } else {
573 qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
574 res += QLatin1String(buf);
575 }
576 }
577 return res;
578 }
579
dateFromData(int jd)580 static QDate dateFromData(int jd)
581 {
582 return jd ? QDate::fromJulianDay(jd) : QDate();
583 }
584
timeFromData(int ms)585 static QTime timeFromData(int ms)
586 {
587 return ms == -1 ? QTime() : QTime(0, 0, 0, 0).addMSecs(ms);
588 }
589
590 // Stolen and adapted from qdatetime.cpp
getDateTime(qint64 msecs,int status,QDate * date,QTime * time,int tiVersion)591 static void getDateTime(qint64 msecs, int status, QDate *date, QTime *time, int tiVersion)
592 {
593 enum {
594 SECS_PER_DAY = 86400,
595 MSECS_PER_DAY = 86400000,
596 SECS_PER_HOUR = 3600,
597 MSECS_PER_HOUR = 3600000,
598 SECS_PER_MIN = 60,
599 MSECS_PER_MIN = 60000,
600 TIME_T_MAX = 2145916799, // int maximum 2037-12-31T23:59:59 UTC
601 JULIAN_DAY_FOR_EPOCH = 2440588 // result of julianDayFromDate(1970, 1, 1)
602 };
603
604 // Status of date/time
605 enum StatusFlag {
606 NullDate = 0x01,
607 NullTime = 0x02,
608 ValidDate = 0x04,
609 ValidTime = 0x08,
610 ValidDateTime = 0x10,
611 TimeZoneCached = 0x20,
612 SetToStandardTime = 0x40,
613 SetToDaylightTime = 0x80
614 };
615
616 qint64 jd = JULIAN_DAY_FOR_EPOCH;
617 qint64 ds = 0;
618
619 if (qAbs(msecs) >= MSECS_PER_DAY) {
620 jd += (msecs / MSECS_PER_DAY);
621 msecs %= MSECS_PER_DAY;
622 }
623
624 if (msecs < 0) {
625 ds = MSECS_PER_DAY - msecs - 1;
626 jd -= ds / MSECS_PER_DAY;
627 ds = ds % MSECS_PER_DAY;
628 ds = MSECS_PER_DAY - ds - 1;
629 } else {
630 ds = msecs;
631 }
632
633 *date = ((status & NullDate) && tiVersion < 14) ? QDate() : QDate::fromJulianDay(jd);
634 *time = ((status & NullTime) && tiVersion < 14) ? QTime() : QTime::fromMSecsSinceStartOfDay(ds);
635 }
636
decodeData(const QString & ba,const QString & encoding)637 QString decodeData(const QString &ba, const QString &encoding)
638 {
639 if (encoding.isEmpty())
640 return quoteUnprintableLatin1(ba); // The common case.
641
642 if (encoding == "empty")
643 return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<empty>");
644 if (encoding == "minimumitemcount")
645 return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<at least %n items>", nullptr, ba.toInt());
646 if (encoding == "undefined")
647 return QLatin1String("Undefined");
648 if (encoding == "null")
649 return QLatin1String("Null");
650 if (encoding == "itemcount")
651 return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<%n items>", nullptr, ba.toInt());
652 if (encoding == "notaccessible")
653 return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<not accessible>");
654 if (encoding == "optimizedout")
655 return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<optimized out>");
656 if (encoding == "nullreference")
657 return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<null reference>");
658 if (encoding == "emptystructure")
659 return QLatin1String("{...}");
660 if (encoding == "uninitialized")
661 return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<uninitialized>");
662 if (encoding == "invalid")
663 return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<invalid>");
664 if (encoding == "notcallable")
665 return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<not callable>");
666 if (encoding == "outofscope")
667 return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<out of scope>");
668
669 DebuggerEncoding enc(encoding);
670 QString result;
671 switch (enc.type) {
672 case DebuggerEncoding::Unencoded: {
673 result = quoteUnprintableLatin1(ba);
674 break;
675 }
676 case DebuggerEncoding::HexEncodedLocal8Bit: {
677 const QByteArray decodedBa = QByteArray::fromHex(ba.toUtf8());
678 result = QString::fromLocal8Bit(decodedBa.data(), decodedBa.size());
679 break;
680 }
681 case DebuggerEncoding::HexEncodedLatin1: {
682 const QByteArray decodedBa = QByteArray::fromHex(ba.toUtf8());
683 result = QString::fromLatin1(decodedBa.data(), decodedBa.size());
684 break;
685 }
686 case DebuggerEncoding::HexEncodedUtf8: {
687 const QByteArray decodedBa = QByteArray::fromHex(ba.toUtf8());
688 result = QString::fromUtf8(decodedBa.data(), decodedBa.size());
689 break;
690 }
691 case DebuggerEncoding::HexEncodedUtf16: {
692 const QByteArray decodedBa = QByteArray::fromHex(ba.toUtf8());
693 result = QString::fromUtf16(reinterpret_cast<const ushort *>
694 (decodedBa.data()), decodedBa.size() / 2);
695 break;
696 }
697 case DebuggerEncoding::HexEncodedUcs4: {
698 const QByteArray decodedBa = QByteArray::fromHex(ba.toUtf8());
699 result = QString::fromUcs4(reinterpret_cast<const uint *>
700 (decodedBa.data()), decodedBa.size() / 4);
701 break;
702 }
703 case DebuggerEncoding::JulianDate: {
704 const QDate date = dateFromData(ba.toInt());
705 return date.isValid() ? date.toString(Qt::TextDate) : "(invalid)";
706 }
707 case DebuggerEncoding::MillisecondsSinceMidnight: {
708 const QTime time = timeFromData(ba.toInt());
709 return time.isValid() ? time.toString(Qt::TextDate) : "(invalid)";
710 }
711 case DebuggerEncoding::JulianDateAndMillisecondsSinceMidnight: {
712 const int p = ba.indexOf('/');
713 const QDate date = dateFromData(ba.left(p).toInt());
714 const QTime time = timeFromData(ba.mid(p + 1).toInt());
715 const QDateTime dateTime = QDateTime(date, time);
716 return dateTime.isValid() ? dateTime.toString(Qt::TextDate) : "(invalid)";
717 }
718 case DebuggerEncoding::HexEncodedUnsignedInteger:
719 case DebuggerEncoding::HexEncodedSignedInteger:
720 qDebug("not implemented"); // Only used in Arrays, see watchdata.cpp
721 return QString();
722 case DebuggerEncoding::HexEncodedFloat: {
723 QByteArray s = QByteArray::fromHex(ba.toUtf8());
724 if (s.size() < enc.size)
725 s.prepend(QByteArray(enc.size - s.size(), '\0'));
726 if (enc.size == 4) {
727 union { char c[4]; float f; } u = {{s[3], s[2], s[1], s[0]}};
728 return QString::number(u.f);
729 }
730 if (enc.size == 8) {
731 union { char c[8]; double d; } u = {{s[7], s[6], s[5], s[4], s[3], s[2], s[1], s[0]}};
732 return QString::number(u.d);
733 }
734 break;
735 }
736 case DebuggerEncoding::IPv6AddressAndHexScopeId: { // 16 hex-encoded bytes, "%" and the string-encoded scope
737 const int p = ba.indexOf('%');
738 QHostAddress ip6(p == -1 ? ba : ba.left(p));
739 if (ip6.isNull())
740 break;
741
742 const QByteArray scopeId = p == -1 ? QByteArray() : QByteArray::fromHex(ba.mid(p + 1).toUtf8());
743 if (!scopeId.isEmpty())
744 ip6.setScopeId(QString::fromUtf16(reinterpret_cast<const ushort *>(scopeId.constData()),
745 scopeId.length() / 2));
746 return ip6.toString();
747 }
748 case DebuggerEncoding::DateTimeInternal: { // DateTimeInternal: msecs, spec, offset, tz, status
749 int p0 = ba.indexOf('/');
750 int p1 = ba.indexOf('/', p0 + 1);
751 int p2 = ba.indexOf('/', p1 + 1);
752 int p3 = ba.indexOf('/', p2 + 1);
753 int p4 = ba.indexOf('/', p3 + 1);
754
755 qint64 msecs = ba.left(p0).toLongLong();
756 ++p0;
757 Qt::TimeSpec spec = Qt::TimeSpec(ba.mid(p0, p1 - p0).toInt());
758 ++p1;
759 qulonglong offset = ba.mid(p1, p2 - p1).toInt();
760 ++p2;
761 QByteArray timeZoneId = QByteArray::fromHex(ba.mid(p2, p3 - p2).toUtf8());
762 ++p3;
763 int status = ba.mid(p3, p4 - p3).toInt();
764 ++p4;
765 int tiVersion = ba.mid(p4).toInt();
766
767 QDate date;
768 QTime time;
769 getDateTime(msecs, status, &date, &time, tiVersion);
770
771 QDateTime dateTime;
772 if (spec == Qt::OffsetFromUTC) {
773 dateTime = QDateTime(date, time, spec, offset);
774 } else if (spec == Qt::TimeZone) {
775 if (!QTimeZone::isTimeZoneIdAvailable(timeZoneId))
776 return QLatin1String("<unavailable>");
777 dateTime = QDateTime(date, time, QTimeZone(timeZoneId));
778 } else {
779 dateTime = QDateTime(date, time, spec);
780 }
781 return dateTime.toString();
782 }
783 qDebug() << "ENCODING ERROR: " << enc.type;
784 return QCoreApplication::translate("Debugger", "<Encoding error>");
785 }
786
787 if (enc.quotes) {
788 const QChar doubleQuote('"');
789 result = doubleQuote + result + doubleQuote;
790 }
791 return result;
792 }
793
794 //////////////////////////////////////////////////////////////////////////////////
795 //
796 // DebuggerCommand
797 //
798 //////////////////////////////////////////////////////////////////////////////////
799
800 template<typename Value>
addToJsonObject(const QJsonValue & args,const char * name,const Value & value)801 QJsonValue addToJsonObject(const QJsonValue &args, const char *name, const Value &value)
802 {
803 QTC_ASSERT(args.isObject() || args.isNull(), return args);
804 QJsonObject obj = args.toObject();
805 obj.insert(QLatin1String(name), value);
806 return obj;
807 }
808
arg(const char * name,int value)809 void DebuggerCommand::arg(const char *name, int value)
810 {
811 args = addToJsonObject(args, name, value);
812 }
813
arg(const char * name,qlonglong value)814 void DebuggerCommand::arg(const char *name, qlonglong value)
815 {
816 args = addToJsonObject(args, name, value);
817 }
818
arg(const char * name,qulonglong value)819 void DebuggerCommand::arg(const char *name, qulonglong value)
820 {
821 // gdb and lldb will correctly cast the value back to unsigned if needed, so this is no problem.
822 args = addToJsonObject(args, name, qint64(value));
823 }
824
arg(const char * name,const QString & value)825 void DebuggerCommand::arg(const char *name, const QString &value)
826 {
827 args = addToJsonObject(args, name, value);
828 }
829
arg(const char * name,const char * value)830 void DebuggerCommand::arg(const char *name, const char *value)
831 {
832 args = addToJsonObject(args, name, value);
833 }
834
arg(const char * name,const QList<int> & list)835 void DebuggerCommand::arg(const char *name, const QList<int> &list)
836 {
837 QJsonArray numbers;
838 for (int item : list)
839 numbers.append(item);
840 args = addToJsonObject(args, name, numbers);
841 }
842
arg(const char * name,const QStringList & list)843 void DebuggerCommand::arg(const char *name, const QStringList &list)
844 {
845 QJsonArray arr;
846 for (const QString &item : list)
847 arr.append(toHex(item));
848 args = addToJsonObject(args, name, arr);
849 }
850
arg(const char * value)851 void DebuggerCommand::arg(const char *value)
852 {
853 QTC_ASSERT(args.isArray() || args.isNull(), return);
854 QJsonArray arr = args.toArray();
855 arr.append(value);
856 args = arr;
857 }
858
arg(const char * name,bool value)859 void DebuggerCommand::arg(const char *name, bool value)
860 {
861 args = addToJsonObject(args, name, value);
862 }
863
arg(const char * name,const QJsonValue & value)864 void DebuggerCommand::arg(const char *name, const QJsonValue &value)
865 {
866 args = addToJsonObject(args, name, value);
867 }
868
arg(const char * name,const Utils::FilePath & filePath)869 void DebuggerCommand::arg(const char *name, const Utils::FilePath &filePath)
870 {
871 args = addToJsonObject(args, name, filePath.toString());
872 }
873
translateJsonToPython(const QJsonValue & value)874 static QJsonValue translateJsonToPython(const QJsonValue &value)
875 {
876 // TODO: Verify that this covers all incompatibilities between python and json,
877 // e.g. number format and precision
878
879 switch (value.type()) {
880 // Undefined is not a problem as the JSON generator ignores that.
881 case QJsonValue::Null:
882 // Python doesn't understand "null"
883 return QJsonValue(0);
884 case QJsonValue::Bool:
885 // Python doesn't understand lowercase "true" or "false"
886 return QJsonValue(value.toBool() ? 1 : 0);
887 case QJsonValue::Object: {
888 QJsonObject object = value.toObject();
889 for (QJsonObject::iterator i = object.begin(); i != object.end(); ++i)
890 i.value() = translateJsonToPython(i.value());
891 return object;
892 }
893 case QJsonValue::Array: {
894 QJsonArray array = value.toArray();
895 for (QJsonArray::iterator i = array.begin(); i != array.end(); ++i)
896 *i = translateJsonToPython(*i);
897 return array;
898 }
899 default:
900 return value;
901 }
902 }
903
argsToPython() const904 QString DebuggerCommand::argsToPython() const
905 {
906 QJsonValue pythonCompatible(translateJsonToPython(args));
907 if (pythonCompatible.isArray())
908 return QString::fromUtf8(QJsonDocument(pythonCompatible.toArray()).toJson(QJsonDocument::Compact));
909 else
910 return QString::fromUtf8(QJsonDocument(pythonCompatible.toObject()).toJson(QJsonDocument::Compact));
911 }
912
argsToString() const913 QString DebuggerCommand::argsToString() const
914 {
915 return args.toString();
916 }
917
DebuggerEncoding(const QString & data)918 DebuggerEncoding::DebuggerEncoding(const QString &data)
919 {
920 const QStringList l = data.split(':');
921
922 const QString &t = l.at(0);
923 if (t == "latin1") {
924 type = HexEncodedLatin1;
925 size = 1;
926 quotes = true;
927 } else if (t == "local8bit") {
928 type = HexEncodedLocal8Bit;
929 size = 1;
930 quotes = true;
931 } else if (t == "utf8") {
932 type = HexEncodedUtf8;
933 size = 1;
934 quotes = true;
935 } else if (t == "utf16") {
936 type = HexEncodedUtf16;
937 size = 2;
938 quotes = true;
939 } else if (t == "ucs4") {
940 type = HexEncodedUcs4;
941 size = 4;
942 quotes = true;
943 } else if (t == "int") {
944 type = HexEncodedSignedInteger;
945 } else if (t == "uint") {
946 type = HexEncodedUnsignedInteger;
947 } else if (t == "float") {
948 type = HexEncodedFloat;
949 } else if (t == "juliandate") {
950 type = JulianDate;
951 } else if (t == "juliandateandmillisecondssincemidnight") {
952 type = JulianDateAndMillisecondsSinceMidnight;
953 } else if (t == "millisecondssincemidnight") {
954 type = MillisecondsSinceMidnight;
955 } else if (t == "ipv6addressandhexscopeid") {
956 type = IPv6AddressAndHexScopeId;
957 } else if (t == "datetimeinternal") {
958 type = DateTimeInternal;
959 } else if (!t.isEmpty()) {
960 qDebug() << "CANNOT DECODE ENCODING" << data;
961 }
962
963 if (l.size() >= 2)
964 size = l.at(1).toInt();
965
966 if (l.size() >= 3)
967 quotes = bool(l.at(2).toInt());
968 }
969
toString() const970 QString DebuggerEncoding::toString() const
971 {
972 return QString("%1:%2:%3").arg(type).arg(size).arg(quotes);
973 }
974
fromHex(const QString & str)975 QString fromHex(const QString &str)
976 {
977 return QString::fromUtf8(QByteArray::fromHex(str.toUtf8()));
978 }
979
toHex(const QString & str)980 QString toHex(const QString &str)
981 {
982 return QString::fromUtf8(str.toUtf8().toHex());
983 }
984
985 } // namespace Internal
986 } // namespace Debugger
987