1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
37 #include <QtCore/QCoreApplication>
38 #include <QtCore/QDebug>
39 #include <QtCore/QDate>
40 #include <QtCore/QDateTime>
41 #include <QtCore/QTime>
43 #define logMessage(s) do { qDebug() << "TRKCLIENT: " << s; } while (0)
47 Library::Library() : codeseg(0), dataseg(0), pid(0)
51 Library::Library(const TrkResult &result) : codeseg(0), dataseg(0), pid(0)
53 if (result.data.size() < 20) {
54 qWarning("Invalid trk creation notification received.");
58 const char *data = result.data.constData();
59 pid = extractInt(data + 2);
60 codeseg = extractInt(data + 10);
61 dataseg = extractInt(data + 14);
62 const uint len = extractShort(data + 18);
63 name = result.data.mid(20, len);
66 TrkAppVersion::TrkAppVersion()
71 void TrkAppVersion::reset()
73 trkMajor = trkMinor= protocolMajor = protocolMinor = 0;
88 extended1TypeSize = 0;
89 extended2TypeSize = 0;
97 trkAppVersion.reset();
100 static QString formatCpu(int major, int minor)
102 //: CPU description of an S60 device
103 //: %1 major verison, %2 minor version
104 //: %3 real name of major verison, %4 real name of minor version
105 const QString str = QCoreApplication::translate("trk::Session", "CPU: v%1.%2%3%4");
118 return str.arg(major).arg(minor).arg(majorStr).arg(minorStr);
121 QString formatTrkVersion(const TrkAppVersion &version)
123 QString str = QCoreApplication::translate("trk::Session",
124 "App TRK: v%1.%2 TRK protocol: v%3.%4");
125 str = str.arg(version.trkMajor).arg(version.trkMinor);
126 return str.arg(version.protocolMajor).arg(version.protocolMinor);
129 QString Session::deviceDescription(unsigned verbose) const
135 //: description of an S60 device
136 //: %1 CPU description, %2 endianness
137 //: %3 default type size (if any), %4 float size (if any)
139 QString msg = QCoreApplication::translate("trk::Session", "%1, %2%3%4, %5");
140 QString endianness = bigEndian
141 ? QCoreApplication::translate("trk::Session", "big endian")
142 : QCoreApplication::translate("trk::Session", "little endian");
143 msg = msg.arg(formatCpu(cpuMajor, cpuMinor)).arg(endianness);
144 QString defaultTypeSizeStr;
145 QString fpTypeSizeStr;
146 if (verbose && defaultTypeSize)
147 //: will be inserted into s60description
148 defaultTypeSizeStr = QCoreApplication::translate("trk::Session", ", type size: %1").arg(defaultTypeSize);
149 if (verbose && fpTypeSize)
150 //: will be inserted into s60description
151 fpTypeSizeStr = QCoreApplication::translate("trk::Session", ", float size: %1").arg(fpTypeSize);
152 msg = msg.arg(defaultTypeSizeStr).arg(fpTypeSizeStr);
153 return msg.arg(formatTrkVersion(trkAppVersion));
156 QByteArray Session::gdbLibraryList() const
158 const int count = libraries.size();
159 QByteArray response = "l<library-list>";
160 for (int i = 0; i != count; ++i) {
161 const trk::Library &lib = libraries.at(i);
162 response += "<library name=\"";
163 response += lib.name;
165 response += "<section address=\"0x";
166 response += trk::hexNumber(lib.codeseg);
168 response += "<section address=\"0x";
169 response += trk::hexNumber(lib.dataseg);
171 response += "<section address=\"0x";
172 response += trk::hexNumber(lib.dataseg);
174 response += "</library>";
176 response += "</library-list>";
180 QByteArray Session::gdbQsDllInfo(int start, int count) const
182 // Happens with gdb 6.4.50.20060226-cvs / CodeSourcery.
183 // Never made it into FSF gdb that got qXfer:libraries:read instead.
184 // http://sourceware.org/ml/gdb/2007-05/msg00038.html
185 // Name=hexname,TextSeg=textaddr[,DataSeg=dataaddr]
186 const int libraryCount = libraries.size();
187 const int end = count < 0 ? libraryCount : qMin(libraryCount, start + count);
188 QByteArray response(1, end == libraryCount ? 'l' : 'm');
189 for (int i = start; i < end; ++i) {
192 const Library &lib = libraries.at(i);
194 response += lib.name.toHex();
195 response += ",TextSeg=";
196 response += hexNumber(lib.codeseg);
197 response += ",DataSeg=";
198 response += hexNumber(lib.dataseg);
203 QString Session::toString() const
206 QTextStream str(&rc);
207 str << "Session: " << deviceDescription(false) << '\n'
208 << "pid: " << pid << "main thread: " << mainTid
209 << " current thread: " << tid << ' ';
210 str.setIntegerBase(16);
211 str << " code: 0x" << codeseg << " data: 0x" << dataseg << '\n';
212 if (const int libCount = libraries.size()) {
213 str << "Libraries:\n";
214 for (int i = 0; i < libCount; i++)
215 str << " #" << i << ' ' << libraries.at(i).name
216 << " code: 0x" << libraries.at(i).codeseg
217 << " data: 0x" << libraries.at(i).dataseg << '\n';
219 if (const int moduleCount = modules.size()) {
221 for (int i = 0; i < moduleCount; i++)
222 str << " #" << i << ' ' << modules.at(i) << '\n';
224 str.setIntegerBase(10);
225 if (!addressToBP.isEmpty()) {
226 typedef QHash<uint, uint>::const_iterator BP_ConstIterator;
227 str << "Breakpoints:\n";
228 const BP_ConstIterator cend = addressToBP.constEnd();
229 for (BP_ConstIterator it = addressToBP.constBegin(); it != cend; ++it) {
230 str.setIntegerBase(16);
231 str << " 0x" << it.key();
232 str.setIntegerBase(10);
233 str << ' ' << it.value() << '\n';
242 QByteArray decode7d(const QByteArray &ba)
245 res.reserve(ba.size());
246 for (int i = 0; i < ba.size(); ++i) {
247 byte c = byte(ba.at(i));
250 c = 0x20 ^ byte(ba.at(i));
257 QByteArray encode7d(const QByteArray &ba)
260 res.reserve(ba.size() + 2);
261 for (int i = 0; i < ba.size(); ++i) {
262 byte c = byte(ba.at(i));
263 if (c == 0x7e || c == 0x7d) {
265 res.append(0x20 ^ c);
273 // FIXME: Use the QByteArray based version below?
274 static inline QString stringFromByte(byte c)
276 return QString::fromLatin1("%1").arg(c, 2, 16, QChar('0'));
279 SYMBIANUTILS_EXPORT QString stringFromArray(const QByteArray &ba, int maxLen)
283 const int size = maxLen == -1 ? ba.size() : qMin(ba.size(), maxLen);
284 for (int i = 0; i < size; ++i) {
285 const int c = byte(ba.at(i));
286 str += QString::fromAscii("%1 ").arg(c, 2, 16, QChar('0'));
287 ascii += QChar(c).isPrint() ? QChar(c) : QChar('.');
289 if (size != ba.size()) {
290 str += QLatin1String("...");
291 ascii += QLatin1String("...");
293 return str + QLatin1String(" ") + ascii;
296 SYMBIANUTILS_EXPORT QByteArray hexNumber(uint n, int digits)
298 QByteArray ba = QByteArray::number(n, 16);
299 if (digits == 0 || ba.size() == digits)
301 return QByteArray(digits - ba.size(), '0') + ba;
304 SYMBIANUTILS_EXPORT QByteArray hexxNumber(uint n, int digits)
306 return "0x" + hexNumber(n, digits);
309 TrkResult::TrkResult() :
316 void TrkResult::clear()
319 isDebugOutput = false;
324 QString TrkResult::toString() const
326 QString res = stringFromByte(code);
327 res += QLatin1String(" [");
328 res += stringFromByte(token);
329 res += QLatin1Char(']');
330 res += QLatin1Char(' ');
331 res += stringFromArray(data);
335 QByteArray frameMessage(byte command, byte token, const QByteArray &data, bool serialFrame)
337 byte s = command + token;
338 for (int i = 0; i != data.size(); ++i)
340 byte checksum = 255 - (s & 0xff);
342 //logMessage("check: " << s << checksum << x;
345 response.reserve(data.size() + 3);
346 response.append(char(command));
347 response.append(char(token));
348 response.append(data);
349 response.append(char(checksum));
351 QByteArray encodedData = encode7d(response);
354 ba.reserve(encodedData.size() + 6);
356 ba.append(char(0x01));
357 ba.append(char(0x90));
358 const ushort encodedSize = encodedData.size() + 2; // 2 x 0x7e
359 appendShort(&ba, encodedSize, BigEndian);
361 ba.append(char(0x7e));
362 ba.append(encodedData);
363 ba.append(char(0x7e));
368 /* returns 0 if array doesn't represent a result,
369 otherwise returns the length of the result data */
370 ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame, ushort& mux)
373 // Serial protocol with length info
374 if (buffer.length() < 4)
376 mux = extractShort(buffer.data());
377 const ushort len = extractShort(buffer.data() + 2);
378 return (buffer.size() >= len + 4) ? len : ushort(0);
380 // Frameless protocol without length info
381 const char delimiter = char(0x7e);
382 const int firstDelimiterPos = buffer.indexOf(delimiter);
383 // Regular message delimited by 0x7e..0x7e
384 if (firstDelimiterPos == 0) {
386 const int endPos = buffer.indexOf(delimiter, firstDelimiterPos + 1);
387 return endPos != -1 ? endPos + 1 - firstDelimiterPos : 0;
389 // Some ASCII log message up to first delimiter or all
390 return firstDelimiterPos != -1 ? firstDelimiterPos : buffer.size();
393 bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, bool &linkEstablishmentMode, QByteArray *rawData)
398 ushort len = isValidTrkResult(*buffer, serialFrame, result->multiplex);
399 // handle receiving application output, which is not a regular command
400 const int delimiterPos = serialFrame ? 4 : 0;
401 if (linkEstablishmentMode) {
402 //when "hot connecting" a device, we can receive partial frames.
403 //this code resyncs by discarding data until a TRK frame is found
404 while (buffer->length() > delimiterPos
405 && result->multiplex != MuxTextTrace
406 && !(result->multiplex == MuxTrk && buffer->at(delimiterPos) == 0x7e)) {
408 len = isValidTrkResult(*buffer, serialFrame, result->multiplex);
413 if (buffer->at(delimiterPos) != 0x7e) {
414 result->isDebugOutput = true;
415 result->data = buffer->mid(delimiterPos, len);
416 buffer->remove(0, delimiterPos + len);
419 // FIXME: what happens if the length contains 0xfe?
420 // Assume for now that it passes unencoded!
421 const QByteArray data = decode7d(buffer->mid(delimiterPos + 1, len - 2));
424 buffer->remove(0, delimiterPos + len);
427 for (int i = 0; i < data.size(); ++i) // 3 = 2 * 0xfe + sum
428 sum += byte(data.at(i));
430 logMessage("*** CHECKSUM ERROR: " << byte(sum));
432 result->code = data.at(0);
433 result->token = data.at(1);
434 result->data = data.mid(2, data.size() - 3);
435 //logMessage(" REST BUF: " << stringFromArray(*buffer));
436 //logMessage(" CURR DATA: " << stringFromArray(data));
437 //QByteArray prefix = "READ BUF: ";
438 //logMessage((prefix + "HEADER: " + stringFromArray(header).toLatin1()).data());
439 linkEstablishmentMode = false; //have received a good TRK packet, therefore in sync
443 SYMBIANUTILS_EXPORT ushort extractShort(const char *data)
445 return byte(data[0]) * 256 + byte(data[1]);
448 SYMBIANUTILS_EXPORT uint extractInt(const char *data)
450 uint res = byte(data[0]);
451 res *= 256; res += byte(data[1]);
452 res *= 256; res += byte(data[2]);
453 res *= 256; res += byte(data[3]);
457 SYMBIANUTILS_EXPORT quint64 extractInt64(const char *data)
459 quint64 res = byte(data[0]);
460 res <<= 8; res += byte(data[1]);
461 res <<= 8; res += byte(data[2]);
462 res <<= 8; res += byte(data[3]);
463 res <<= 8; res += byte(data[4]);
464 res <<= 8; res += byte(data[5]);
465 res <<= 8; res += byte(data[6]);
466 res <<= 8; res += byte(data[7]);
470 SYMBIANUTILS_EXPORT QString quoteUnprintableLatin1(const QByteArray &ba)
474 for (int i = 0, n = ba.size(); i != n; ++i) {
475 const byte c = ba.at(i);
479 qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
486 SYMBIANUTILS_EXPORT void appendShort(QByteArray *ba, ushort s, Endianness endian)
488 if (endian == BigEndian) {
497 SYMBIANUTILS_EXPORT void appendInt(QByteArray *ba, uint i, Endianness endian)
499 const uchar b3 = i % 256; i /= 256;
500 const uchar b2 = i % 256; i /= 256;
501 const uchar b1 = i % 256; i /= 256;
503 ba->reserve(ba->size() + 4);
504 if (endian == BigEndian) {
517 void appendString(QByteArray *ba, const QByteArray &str, Endianness endian, bool appendNullTerminator)
519 const int fullSize = str.size() + (appendNullTerminator ? 1 : 0);
520 appendShort(ba, fullSize, endian); // count the terminating \0
522 if (appendNullTerminator)
526 void appendDateTime(QByteArray *ba, QDateTime dateTime, Endianness endian)
528 // convert the QDateTime to UTC and append its representation to QByteArray
529 // format is the same as in FAT file system
530 dateTime = dateTime.toUTC();
531 const QTime utcTime = dateTime.time();
532 const QDate utcDate = dateTime.date();
533 uint fatDateTime = (utcTime.hour() << 11 | utcTime.minute() << 5 | utcTime.second()/2) << 16;
534 fatDateTime |= (utcDate.year()-1980) << 9 | utcDate.month() << 5 | utcDate.day();
535 appendInt(ba, fatDateTime, endian);
538 QByteArray errorMessage(byte code)
541 case 0x00: return "No error";
542 case 0x01: return "Generic error in CWDS message";
543 case 0x02: return "Unexpected packet size in send msg";
544 case 0x03: return "Internal error occurred in CWDS";
545 case 0x04: return "Escape followed by frame flag";
546 case 0x05: return "Bad FCS in packet";
547 case 0x06: return "Packet too long";
548 case 0x07: return "Sequence ID not expected (gap in sequence)";
550 case 0x10: return "Command not supported";
551 case 0x11: return "Command param out of range";
552 case 0x12: return "An option was not supported";
553 case 0x13: return "Read/write to invalid memory";
554 case 0x14: return "Read/write invalid registers";
555 case 0x15: return "Exception occurred in CWDS";
556 case 0x16: return "Targeted system or thread is running";
557 case 0x17: return "Breakpoint resources (HW or SW) exhausted";
558 case 0x18: return "Requested breakpoint conflicts with existing one";
560 case 0x20: return "General OS-related error";
561 case 0x21: return "Request specified invalid process";
562 case 0x22: return "Request specified invalid thread";
564 return "Unknown error";
567 uint swapEndian(uint in)
569 return (in>>24) | ((in<<8) & 0x00FF0000) | ((in>>8) & 0x0000FF00) | (in<<24);
572 int TrkResult::errorCode() const
574 // NAK means always error, else data sized 1 with a non-null element
575 const bool isNAK = code == 0xff;
576 if (data.size() != 1 && !isNAK)
578 if (const int errorCode = data.at(0))
580 return isNAK ? 0xff : 0;
583 QString TrkResult::errorString() const
585 // NAK means always error, else data sized 1 with a non-null element
589 return "Unknown error packet";
590 return errorMessage(data.at(0));