summaryrefslogtreecommitdiffstats
path: root/tools/runonphone/symbianutils
diff options
context:
space:
mode:
Diffstat (limited to 'tools/runonphone/symbianutils')
-rw-r--r--tools/runonphone/symbianutils/json.cpp490
-rw-r--r--tools/runonphone/symbianutils/json.h149
-rw-r--r--tools/runonphone/symbianutils/launcher.cpp440
-rw-r--r--tools/runonphone/symbianutils/launcher.h35
-rw-r--r--tools/runonphone/symbianutils/symbiandevicemanager.cpp194
-rw-r--r--tools/runonphone/symbianutils/symbiandevicemanager.h47
-rw-r--r--tools/runonphone/symbianutils/symbianutils.pri12
-rw-r--r--tools/runonphone/symbianutils/tcftrkdevice.cpp929
-rw-r--r--tools/runonphone/symbianutils/tcftrkdevice.h295
-rw-r--r--tools/runonphone/symbianutils/tcftrkmessage.cpp562
-rw-r--r--tools/runonphone/symbianutils/tcftrkmessage.h296
-rw-r--r--tools/runonphone/symbianutils/trkdevice.cpp116
-rw-r--r--tools/runonphone/symbianutils/trkdevice.h11
-rw-r--r--tools/runonphone/symbianutils/trkutils.cpp163
-rw-r--r--tools/runonphone/symbianutils/trkutils.h92
-rw-r--r--tools/runonphone/symbianutils/trkutils_p.h2
16 files changed, 3674 insertions, 159 deletions
diff --git a/tools/runonphone/symbianutils/json.cpp b/tools/runonphone/symbianutils/json.cpp
new file mode 100644
index 0000000..2d58824
--- /dev/null
+++ b/tools/runonphone/symbianutils/json.cpp
@@ -0,0 +1,490 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "json.h"
+
+#ifdef TODO_USE_CREATOR
+#include <utils/qtcassert.h>
+#endif // TODO_USE_CREATOR
+
+#include <QtCore/QByteArray>
+#include <QtCore/QTextStream>
+#include <QtCore/QDebug>
+#include <QtCore/QStringList>
+
+#include <ctype.h>
+
+//#define DEBUG_JASON
+#ifdef DEBUG_JASON
+#define JDEBUG(s) qDebug() << s
+#else
+#define JDEBUG(s)
+#endif
+
+namespace tcftrk {
+
+static void skipSpaces(const char *&from, const char *to)
+{
+ while (from != to && isspace(*from))
+ ++from;
+}
+
+QTextStream &operator<<(QTextStream &os, const JsonValue &mi)
+{
+ return os << mi.toString();
+}
+
+void JsonValue::parsePair(const char *&from, const char *to)
+{
+ skipSpaces(from, to);
+ JDEBUG("parsePair: " << QByteArray(from, to - from));
+ m_name = parseCString(from, to);
+ skipSpaces(from, to);
+ while (from < to && *from != ':') {
+ JDEBUG("not a colon" << *from);
+ ++from;
+ }
+ ++from;
+ parseValue(from, to);
+ skipSpaces(from, to);
+}
+
+QByteArray JsonValue::parseNumber(const char *&from, const char *to)
+{
+ QByteArray result;
+ if (from < to && *from == '-') // Leading '-'.
+ result.append(*from++);
+ while (from < to && *from >= '0' && *from <= '9')
+ result.append(*from++);
+ return result;
+}
+
+QByteArray JsonValue::parseCString(const char *&from, const char *to)
+{
+ QByteArray result;
+ JDEBUG("parseCString: " << QByteArray(from, to - from));
+ if (*from != '"') {
+ qDebug() << "JSON Parse Error, double quote expected";
+ ++from; // So we don't hang
+ return QByteArray();
+ }
+ const char *ptr = from;
+ ++ptr;
+ while (ptr < to) {
+ if (*ptr == '"') {
+ ++ptr;
+ result = QByteArray(from + 1, ptr - from - 2);
+ break;
+ }
+ if (*ptr == '\\') {
+ ++ptr;
+ if (ptr == to) {
+ qDebug() << "JSON Parse Error, unterminated backslash escape";
+ from = ptr; // So we don't hang
+ return QByteArray();
+ }
+ }
+ ++ptr;
+ }
+ from = ptr;
+
+ 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;
+ 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() << "JSON 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());
+ }
+
+ JDEBUG("parseCString, got " << result);
+ return result;
+}
+
+
+
+void JsonValue::parseValue(const char *&from, const char *to)
+{
+ JDEBUG("parseValue: " << QByteArray(from, to - from));
+ switch (*from) {
+ case '{':
+ parseObject(from, to);
+ break;
+ case 't':
+ if (to - from >= 4 && qstrncmp(from, "true", 4) == 0) {
+ m_data = QByteArray(from, 4);
+ from += m_data.size();
+ m_type = Boolean;
+ }
+ break;
+ case 'f':
+ if (to - from >= 5 && qstrncmp(from, "false", 5) == 0) {
+ m_data = QByteArray(from, 5);
+ from += m_data.size();
+ m_type = Boolean;
+ }
+ break;
+ case 'n':
+ if (to - from >= 4 && qstrncmp(from, "null", 4) == 0) {
+ m_data = QByteArray(from, 4);
+ from += m_data.size();
+ m_type = NullObject;
+ }
+ break;
+ case '[':
+ parseArray(from, to);
+ break;
+ case '"':
+ m_type = String;
+ m_data = parseCString(from, to);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '-':
+ m_type = Number;
+ m_data = parseNumber(from, to);
+ default:
+ break;
+ }
+}
+
+void JsonValue::parseObject(const char *&from, const char *to)
+{
+ JDEBUG("parseObject: " << QByteArray(from, to - from));
+#ifdef TODO_USE_CREATOR
+ QTC_ASSERT(*from == '{', /**/);
+#endif
+ ++from;
+ m_type = Object;
+ while (from < to) {
+ if (*from == '}') {
+ ++from;
+ break;
+ }
+ JsonValue child;
+ child.parsePair(from, to);
+ if (!child.isValid())
+ return;
+ m_children += child;
+ if (*from == ',')
+ ++from;
+ }
+}
+
+void JsonValue::parseArray(const char *&from, const char *to)
+{
+ JDEBUG("parseArray: " << QByteArray(from, to - from));
+#ifdef TODO_USE_CREATOR
+ QTC_ASSERT(*from == '[', /**/);
+#endif
+ ++from;
+ m_type = Array;
+ while (from < to) {
+ if (*from == ']') {
+ ++from;
+ break;
+ }
+ JsonValue child;
+ child.parseValue(from, to);
+ if (child.isValid())
+ m_children += child;
+ if (*from == ',')
+ ++from;
+ }
+}
+
+void JsonValue::setStreamOutput(const QByteArray &name, const QByteArray &content)
+{
+ if (content.isEmpty())
+ return;
+ JsonValue child;
+ child.m_type = String;
+ child.m_name = name;
+ child.m_data = content;
+ m_children += child;
+ if (m_type == Invalid)
+ m_type = Object;
+}
+
+static QByteArray ind(int indent)
+{
+ return QByteArray(2 * indent, ' ');
+}
+
+void JsonValue::dumpChildren(QByteArray * str, bool multiline, int indent) const
+{
+ for (int i = 0; i < m_children.size(); ++i) {
+ if (i != 0) {
+ *str += ',';
+ if (multiline)
+ *str += '\n';
+ }
+ if (multiline)
+ *str += ind(indent);
+ *str += m_children.at(i).toString(multiline, indent);
+ }
+}
+
+class MyString : public QString {
+public:
+ ushort at(int i) const { return constData()[i].unicode(); }
+};
+
+template<class ST, typename CT>
+inline ST escapeCStringTpl(const ST &ba)
+{
+ ST ret;
+ ret.reserve(ba.length() * 2);
+ for (int i = 0; i < ba.length(); ++i) {
+ CT c = ba.at(i);
+ switch (c) {
+ 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;
+ default:
+ if (c < 32 || c == 127) {
+ ret += '\\';
+ ret += '0' + (c >> 6);
+ ret += '0' + ((c >> 3) & 7);
+ ret += '0' + (c & 7);
+ } else {
+ ret += c;
+ }
+ }
+ }
+ return ret;
+}
+
+QString JsonValue::escapeCString(const QString &ba)
+{
+ return escapeCStringTpl<MyString, ushort>(static_cast<const MyString &>(ba));
+}
+
+QByteArray JsonValue::escapeCString(const QByteArray &ba)
+{
+ return escapeCStringTpl<QByteArray, uchar>(ba);
+}
+
+QByteArray JsonValue::toString(bool multiline, int indent) const
+{
+ QByteArray result;
+ switch (m_type) {
+ case Invalid:
+ if (multiline)
+ result += ind(indent) + "Invalid\n";
+ else
+ result += "Invalid";
+ break;
+ case String:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ result += '"' + escapeCString(m_data) + '"';
+ break;
+ case Number:
+ if (!m_name.isEmpty())
+ result += '"' + m_name + "\":";
+ result += m_data;
+ break;
+ case Boolean:
+ case NullObject:
+ if (!m_name.isEmpty())
+ result += '"' + m_name + "\":";
+ result += m_data;
+ break;
+ case Object:
+ if (!m_name.isEmpty())
+ result += m_name + '=';
+ if (multiline) {
+ result += "{\n";
+ dumpChildren(&result, multiline, indent + 1);
+ result += '\n' + ind(indent) + "}";
+ } else {
+ result += "{";
+ dumpChildren(&result, multiline, indent + 1);
+ result += "}";
+ }
+ break;
+ case Array:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ if (multiline) {
+ result += "[\n";
+ dumpChildren(&result, multiline, indent + 1);
+ result += '\n' + ind(indent) + "]";
+ } else {
+ result += "[";
+ dumpChildren(&result, multiline, indent + 1);
+ result += "]";
+ }
+ break;
+ }
+ return result;
+}
+
+void JsonValue::fromString(const QByteArray &ba)
+{
+ const char *from = ba.constBegin();
+ const char *to = ba.constEnd();
+ parseValue(from, to);
+}
+
+JsonValue JsonValue::findChild(const char *name) const
+{
+ for (int i = 0; i < m_children.size(); ++i)
+ if (m_children.at(i).m_name == name)
+ return m_children.at(i);
+ return JsonValue();
+}
+
+void JsonInputStream::appendCString(const char *s)
+{
+ m_target.append('"');
+ for (const char *p = s; *p; p++) {
+ if (*p == '"' || *p == '\\')
+ m_target.append('\\');
+ m_target.append(*p);
+ }
+ m_target.append('"');
+}
+
+void JsonInputStream::appendString(const QString &in)
+{
+ if (in.isEmpty()) {
+ m_target.append("\"\"");
+ return;
+ }
+
+ const QChar doubleQuote('"');
+ const QChar backSlash('\\');
+ QString rc;
+ const int inSize = in.size();
+ rc.reserve(in.size() + 5);
+ rc.append(doubleQuote);
+ for (int i = 0; i < inSize; i++) {
+ const QChar c = in.at(i);
+ if (c == doubleQuote || c == backSlash)
+ rc.append(backSlash);
+ rc.append(c);
+ }
+ rc.append(doubleQuote);
+ m_target.append(rc.toUtf8());
+ return;
+}
+
+JsonInputStream &JsonInputStream::operator<<(const QStringList &in)
+{
+ m_target.append('[');
+ const int count = in.size();
+ for (int i = 0 ; i < count; i++) {
+ if (i)
+ m_target.append(',');
+ appendString(in.at(i));
+ }
+ m_target.append(']');
+ return *this;
+}
+
+JsonInputStream &JsonInputStream::operator<<(const QVector<QByteArray> &ba)
+{
+ m_target.append('[');
+ const int count = ba.size();
+ for (int i = 0 ; i < count; i++) {
+ if (i)
+ m_target.append(',');
+ appendCString(ba.at(i).constData());
+ }
+ m_target.append(']');
+ return *this;
+}
+
+JsonInputStream &JsonInputStream::operator<<(bool b)
+{
+ m_target.append(b ? "true" : "false");
+ return *this;
+}
+
+} // namespace tcftrk
+
diff --git a/tools/runonphone/symbianutils/json.h b/tools/runonphone/symbianutils/json.h
new file mode 100644
index 0000000..ffcb7ab
--- /dev/null
+++ b/tools/runonphone/symbianutils/json.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMBIANUTILS_JSON_H
+#define SYMBIANUTILS_JSON_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+namespace tcftrk {
+
+class SYMBIANUTILS_EXPORT JsonValue
+{
+public:
+ JsonValue() : m_type(Invalid) {}
+ explicit JsonValue(const QByteArray &str) { fromString(str); }
+
+ QByteArray m_name;
+ QByteArray m_data;
+ QList<JsonValue> m_children;
+
+ enum Type {
+ Invalid,
+ String,
+ Number,
+ Boolean,
+ Object,
+ NullObject,
+ Array,
+ };
+
+ Type m_type;
+
+ inline Type type() const { return m_type; }
+ inline QByteArray name() const { return m_name; }
+ inline bool hasName(const char *name) const { return m_name == name; }
+
+ inline bool isValid() const { return m_type != Invalid; }
+ inline bool isNumber() const { return m_type == Number; }
+ inline bool isString() const { return m_type == String; }
+ inline bool isObject() const { return m_type == Object; }
+ inline bool isArray() const { return m_type == Array; }
+
+
+ inline QByteArray data() const { return m_data; }
+ inline const QList<JsonValue> &children() const { return m_children; }
+ inline int childCount() const { return m_children.size(); }
+
+ const JsonValue &childAt(int index) const { return m_children[index]; }
+ JsonValue &childAt(int index) { return m_children[index]; }
+ JsonValue findChild(const char *name) const;
+
+ QByteArray toString(bool multiline = false, int indent = 0) const;
+ void fromString(const QByteArray &str);
+ void setStreamOutput(const QByteArray &name, const QByteArray &content);
+
+private:
+ static QByteArray parseCString(const char *&from, const char *to);
+ static QByteArray parseNumber(const char *&from, const char *to);
+ static QByteArray escapeCString(const QByteArray &ba);
+ static QString escapeCString(const QString &ba);
+ void parsePair(const char *&from, const char *to);
+ void parseValue(const char *&from, const char *to);
+ void parseObject(const char *&from, const char *to);
+ void parseArray(const char *&from, const char *to);
+
+ void dumpChildren(QByteArray *str, bool multiline, int indent) const;
+};
+
+/* Thin wrapper around QByteArray for formatting JSON input. Use as in:
+ * JsonInputStream(byteArray) << '{' << "bla" << ':' << "blup" << '}';
+ * Note that strings get double quotes and JSON-escaping, characters should be
+ * used for the array/hash delimiters.
+ * */
+class SYMBIANUTILS_EXPORT JsonInputStream {
+public:
+ explicit JsonInputStream(QByteArray &a) : m_target(a) {}
+
+ JsonInputStream &operator<<(char c) { m_target.append(c); return *this; }
+ JsonInputStream &operator<<(const char *c) { appendCString(c); return *this; }
+ JsonInputStream &operator<<(const QByteArray &a) { appendCString(a.constData()); return *this; }
+ JsonInputStream &operator<<(const QString &c) { appendString(c); return *this; }
+
+ // Format as array
+ JsonInputStream &operator<<(const QStringList &c);
+
+ // Format as array
+ JsonInputStream &operator<<(const QVector<QByteArray> &ba);
+
+ JsonInputStream &operator<<(bool b);
+
+ JsonInputStream &operator<<(int i)
+ { m_target.append(QByteArray::number(i)); return *this; }
+ JsonInputStream &operator<<(unsigned i)
+ { m_target.append(QByteArray::number(i)); return *this; }
+ JsonInputStream &operator<<(quint64 i)
+ { m_target.append(QByteArray::number(i)); return *this; }
+
+private:
+ void appendString(const QString &);
+ void appendCString(const char *c);
+
+ QByteArray &m_target;
+};
+
+} // namespace tcftrk
+
+#endif // SYMBIANUTILS_JSON_H
diff --git a/tools/runonphone/symbianutils/launcher.cpp b/tools/runonphone/symbianutils/launcher.cpp
index 408829b..ee87480 100644
--- a/tools/runonphone/symbianutils/launcher.cpp
+++ b/tools/runonphone/symbianutils/launcher.cpp
@@ -44,6 +44,7 @@
#include "trkutils_p.h"
#include "trkdevice.h"
#include "bluetoothlistener.h"
+#include "symbiandevicemanager.h"
#include <QtCore/QTimer>
#include <QtCore/QDateTime>
@@ -53,15 +54,45 @@
#include <QtCore/QFile>
#include <QtCore/QScopedPointer>
+#include <cstdio>
+
namespace trk {
+struct CrashReportState {
+ CrashReportState();
+ void clear();
+
+ typedef uint Thread;
+ typedef QList<Thread> Threads;
+ Threads threads;
+
+ QList<uint> registers;
+ QByteArray stack;
+ uint sp;
+ uint fetchingStackPID;
+ uint fetchingStackTID;
+};
+
+CrashReportState::CrashReportState()
+{
+ clear();
+}
+
+void CrashReportState::clear()
+{
+ threads.clear();
+ stack.clear();
+ sp = fetchingStackPID = fetchingStackTID = 0;
+}
+
struct LauncherPrivate {
struct CopyState {
QString sourceFileName;
QString destinationFileName;
uint copyFileHandle;
QScopedPointer<QByteArray> data;
- int position;
+ qint64 position;
+ QScopedPointer<QFile> localFile;
};
explicit LauncherPrivate(const TrkDevicePtr &d);
@@ -75,12 +106,14 @@ struct LauncherPrivate {
Session m_session; // global-ish data (process id, target information)
CopyState m_copyState;
+ CopyState m_downloadState;
QString m_fileName;
QStringList m_commandLineArgs;
QString m_installFileName;
int m_verbose;
Launcher::Actions m_startupActions;
bool m_closeDevice;
+ CrashReportState m_crashReportState;
};
LauncherPrivate::LauncherPrivate(const TrkDevicePtr &d) :
@@ -100,12 +133,15 @@ Launcher::Launcher(Actions startupActions,
d(new LauncherPrivate(dev))
{
d->m_startupActions = startupActions;
- connect(d->m_device.data(), SIGNAL(messageReceived(trk::TrkResult)), this, SLOT(handleResult(trk::TrkResult)));
- connect(this, SIGNAL(finished()), d->m_device.data(), SLOT(close()));
+ connect(d->m_device.data(), SIGNAL(messageReceived(trk::TrkResult)), this, SLOT(handleResult(trk::TrkResult)));
}
Launcher::~Launcher()
{
+ // Destroyed before protocol was through: Close
+ if (d->m_closeDevice && d->m_device->isOpen())
+ d->m_device->close();
+ emit destroyed(d->m_device->port());
logMessage("Shutting down.\n");
delete d;
}
@@ -154,6 +190,12 @@ void Launcher::setCopyFileName(const QString &srcName, const QString &dstName)
d->m_copyState.destinationFileName = dstName;
}
+void Launcher::setDownloadFileName(const QString &srcName, const QString &dstName)
+{
+ d->m_downloadState.sourceFileName = srcName;
+ d->m_downloadState.destinationFileName = dstName;
+}
+
void Launcher::setInstallFileName(const QString &name)
{
d->m_installFileName = name;
@@ -188,11 +230,28 @@ void Launcher::setCloseDevice(bool c)
bool Launcher::startServer(QString *errorMessage)
{
errorMessage->clear();
+ d->m_crashReportState.clear();
if (d->m_verbose) {
- const QString msg = QString::fromLatin1("Port=%1 Executable=%2 Arguments=%3 Package=%4 Remote Package=%5 Install file=%6")
- .arg(trkServerName(), d->m_fileName,
- d->m_commandLineArgs.join(QString(QLatin1Char(' '))),
- d->m_copyState.sourceFileName, d->m_copyState.destinationFileName, d->m_installFileName);
+ QString msg;
+ QTextStream str(&msg);
+ str.setIntegerBase(16);
+ str << "Actions=0x" << d->m_startupActions;
+ str.setIntegerBase(10);
+ str << " Port=" << trkServerName();
+ if (!d->m_fileName.isEmpty())
+ str << " Executable=" << d->m_fileName;
+ if (!d->m_commandLineArgs.isEmpty())
+ str << " Arguments= " << d->m_commandLineArgs.join(QString(QLatin1Char(' ')));
+ if (!d->m_copyState.sourceFileName.isEmpty())
+ str << " Package/Source=" << d->m_copyState.sourceFileName;
+ if (!d->m_copyState.destinationFileName.isEmpty())
+ str << " Remote Package/Destination=" << d->m_copyState.destinationFileName;
+ if (!d->m_downloadState.sourceFileName.isEmpty())
+ str << " Source=" << d->m_downloadState.sourceFileName;
+ if (!d->m_downloadState.destinationFileName.isEmpty())
+ str << " Destination=" << d->m_downloadState.destinationFileName;
+ if (!d->m_installFileName.isEmpty())
+ str << " Install file=" << d->m_installFileName;
logMessage(msg);
}
if (d->m_startupActions & ActionCopy) {
@@ -214,11 +273,6 @@ bool Launcher::startServer(QString *errorMessage)
}
if (!d->m_device->isOpen() && !d->m_device->open(errorMessage))
return false;
- if (d->m_closeDevice) {
- connect(this, SIGNAL(finished()), d->m_device.data(), SLOT(close()));
- } else {
- disconnect(this, SIGNAL(finished()), d->m_device.data(), 0);
- }
setState(Connecting);
// Set up the temporary 'waiting' state if we do not get immediate connection
QTimer::singleShot(1000, this, SLOT(slotWaitingForTrk()));
@@ -252,6 +306,8 @@ void Launcher::handleConnect(const TrkResult &result)
installRemotePackageSilently();
else if (d->m_startupActions & ActionRun)
startInferiorIfNeeded();
+ else if (d->m_startupActions & ActionDownload)
+ copyFileFromRemote();
}
void Launcher::setVerbose(int v)
@@ -266,6 +322,13 @@ void Launcher::logMessage(const QString &msg)
qDebug() << "LAUNCHER: " << qPrintable(msg);
}
+void Launcher::handleFinished()
+{
+ if (d->m_closeDevice)
+ d->m_device->close();
+ emit finished();
+}
+
void Launcher::terminate()
{
switch (state()) {
@@ -287,7 +350,7 @@ void Launcher::terminate()
case Connecting:
case WaitingForTrk:
setState(Disconnected);
- emit finished();
+ handleFinished();
break;
}
}
@@ -331,8 +394,22 @@ void Launcher::handleResult(const TrkResult &result)
QByteArray prefix = "READ BUF: ";
QByteArray str = result.toString().toUtf8();
if (result.isDebugOutput) { // handle application output
- logMessage("APPLICATION OUTPUT: " + result.data);
- emit applicationOutputReceived(result.data);
+ QString msg;
+ if (result.multiplex == MuxTextTrace) {
+ if (result.data.length() > 8) {
+ quint64 timestamp = extractInt64(result.data) & 0x0FFFFFFFFFFFFFFFULL;
+ quint64 secs = timestamp / 1000000000;
+ quint64 ns = timestamp % 1000000000;
+ msg = QString("[%1.%2] %3").arg(secs).arg(ns).arg(QString(result.data.mid(8)));
+ logMessage("TEXT TRACE: " + msg);
+ }
+ } else {
+ logMessage("APPLICATION OUTPUT: " + stringFromArray(result.data));
+ msg = result.data;
+ }
+ msg.replace("\r\n", "\n");
+ if(!msg.endsWith('\n')) msg.append('\n');
+ emit applicationOutputReceived(msg);
return;
}
switch (result.code) {
@@ -368,49 +445,54 @@ void Launcher::handleResult(const TrkResult &result)
// target->host OS notification
case TrkNotifyCreated: { // Notify Created
- /*
- const char *data = result.data.data();
- byte error = result.data.at(0);
- byte type = result.data.at(1); // type: 1 byte; for dll item, this value is 2.
- uint pid = extractInt(data + 2); // ProcessID: 4 bytes;
- uint tid = extractInt(data + 6); //threadID: 4 bytes
- uint codeseg = extractInt(data + 10); //code address: 4 bytes; code base address for the library
- uint dataseg = extractInt(data + 14); //data address: 4 bytes; data base address for the library
- uint len = extractShort(data + 18); //length: 2 bytes; length of the library name string to follow
- QByteArray name = result.data.mid(20, len); // name: library name
-
- logMessage(prefix + "NOTE: LIBRARY LOAD: " + str);
- logMessage(prefix + "TOKEN: " + result.token);
- logMessage(prefix + "ERROR: " + int(error));
- logMessage(prefix + "TYPE: " + int(type));
- logMessage(prefix + "PID: " + pid);
- logMessage(prefix + "TID: " + tid);
- logMessage(prefix + "CODE: " + codeseg);
- logMessage(prefix + "DATA: " + dataseg);
- logMessage(prefix + "LEN: " + len);
- logMessage(prefix + "NAME: " + name);
- */
if (result.data.size() < 10)
break;
+ const char *data = result.data.constData();
+ const byte error = result.data.at(0);
+ Q_UNUSED(error)
+ const byte type = result.data.at(1); // type: 1 byte; for dll item, this value is 2.
+ const uint tid = extractInt(data + 6); //threadID: 4 bytes
+ Q_UNUSED(tid)
+ if (type == kDSOSDLLItem && result.data.size() >=20) {
+ const Library lib = Library(result);
+ d->m_session.libraries.push_back(lib);
+ emit libraryLoaded(lib);
+ }
QByteArray ba;
ba.append(result.data.mid(2, 8));
d->m_device->sendTrkMessage(TrkContinue, TrkCallback(), ba, "CONTINUE");
- //d->m_device->sendTrkAck(result.token)
break;
}
case TrkNotifyDeleted: { // NotifyDeleted
const ushort itemType = (unsigned char)result.data.at(1);
- const ushort len = result.data.size() > 12 ? extractShort(result.data.data() + 10) : ushort(0);
+ const uint pid = result.data.size() >= 6 ? extractShort(result.data.constData() + 2) : 0;
+ const uint tid = result.data.size() >= 10 ? extractShort(result.data.constData() + 6) : 0;
+ Q_UNUSED(tid)
+ const ushort len = result.data.size() > 12 ? extractShort(result.data.constData() + 10) : ushort(0);
const QString name = len ? QString::fromAscii(result.data.mid(12, len)) : QString();
logMessage(QString::fromLatin1("%1 %2 UNLOAD: %3").
arg(QString::fromAscii(prefix)).arg(itemType ? QLatin1String("LIB") : QLatin1String("PROCESS")).
arg(name));
d->m_device->sendTrkAck(result.token);
- if (itemType == 0 // process
+ if (itemType == kDSOSProcessItem // process
&& result.data.size() >= 10
&& d->m_session.pid == extractInt(result.data.data() + 6)) {
- disconnectTrk();
+ if (d->m_startupActions & ActionDownload)
+ copyFileFromRemote();
+ else
+ disconnectTrk();
+ }
+ else if (itemType == kDSOSDLLItem && len) {
+ // Remove libraries of process.
+ for (QList<Library>::iterator it = d->m_session.libraries.begin(); it != d->m_session.libraries.end(); ) {
+ if ((*it).pid == pid && (*it).name == name) {
+ emit libraryUnloaded(*it);
+ it = d->m_session.libraries.erase(it);
+ } else {
+ ++it;
+ }
+ }
}
break;
}
@@ -446,7 +528,7 @@ void Launcher::handleTrkVersion(const TrkResult &result)
if (result.errorCode() || result.data.size() < 5) {
if (d->m_startupActions == ActionPingOnly) {
setState(Disconnected);
- emit finished();
+ handleFinished();
}
return;
}
@@ -455,31 +537,109 @@ void Launcher::handleTrkVersion(const TrkResult &result)
d->m_session.trkAppVersion.protocolMajor = result.data.at(3);
d->m_session.trkAppVersion.protocolMinor = result.data.at(4);
setState(DeviceDescriptionReceived);
+ const QString msg = deviceDescription();
+ emit deviceDescriptionReceived(trkServerName(), msg);
// Ping mode: Log & Terminate
if (d->m_startupActions == ActionPingOnly) {
- qWarning("%s", qPrintable(deviceDescription()));
+ qWarning("%s", qPrintable(msg));
setState(Disconnected);
- emit finished();
+ handleFinished();
}
}
+static inline QString msgCannotOpenRemoteFile(const QString &fileName, const QString &message)
+{
+ return Launcher::tr("Cannot open remote file '%1': %2").arg(fileName, message);
+}
+
+static inline QString msgCannotOpenLocalFile(const QString &fileName, const QString &message)
+{
+ return Launcher::tr("Cannot open '%1': %2").arg(fileName, message);
+}
+
void Launcher::handleFileCreation(const TrkResult &result)
{
if (result.errorCode() || result.data.size() < 6) {
- emit canNotCreateFile(d->m_copyState.destinationFileName, result.errorString());
+ const QString msg = msgCannotOpenRemoteFile(d->m_copyState.destinationFileName, result.errorString());
+ logMessage(msg);
+ emit canNotCreateFile(d->m_copyState.destinationFileName, msg);
disconnectTrk();
return;
}
const char *data = result.data.data();
d->m_copyState.copyFileHandle = extractInt(data + 2);
- QFile file(d->m_copyState.sourceFileName);
- file.open(QIODevice::ReadOnly);
- d->m_copyState.data.reset(new QByteArray(file.readAll()));
+ const QString localFileName = d->m_copyState.sourceFileName;
+ QFile file(localFileName);
d->m_copyState.position = 0;
+ if (!file.open(QIODevice::ReadOnly)) {
+ const QString msg = msgCannotOpenLocalFile(localFileName, file.errorString());
+ logMessage(msg);
+ emit canNotOpenLocalFile(localFileName, msg);
+ closeRemoteFile(true);
+ disconnectTrk();
+ return;
+ }
+ d->m_copyState.data.reset(new QByteArray(file.readAll()));
file.close();
continueCopying();
}
+void Launcher::handleFileOpened(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 6) {
+ const QString msg = msgCannotOpenRemoteFile(d->m_downloadState.sourceFileName, result.errorString());
+ logMessage(msg);
+ emit canNotOpenFile(d->m_downloadState.sourceFileName, msg);
+ disconnectTrk();
+ return;
+ }
+ d->m_downloadState.position = 0;
+ const QString localFileName = d->m_downloadState.destinationFileName;
+ bool opened = false;
+ if (localFileName == QLatin1String("-")) {
+ d->m_downloadState.localFile.reset(new QFile);
+ opened = d->m_downloadState.localFile->open(stdout, QFile::WriteOnly);
+ } else {
+ d->m_downloadState.localFile.reset(new QFile(localFileName));
+ opened = d->m_downloadState.localFile->open(QFile::WriteOnly | QFile::Truncate);
+ }
+ if (!opened) {
+ const QString msg = msgCannotOpenLocalFile(localFileName, d->m_downloadState.localFile->errorString());
+ logMessage(msg);
+ emit canNotOpenLocalFile(localFileName, msg);
+ closeRemoteFile(true);
+ disconnectTrk();
+ }
+ continueReading();
+}
+
+void Launcher::continueReading()
+{
+ QByteArray ba;
+ appendInt(&ba, d->m_downloadState.copyFileHandle, TargetByteOrder);
+ appendShort(&ba, 2048, TargetByteOrder);
+ d->m_device->sendTrkMessage(TrkReadFile, TrkCallback(this, &Launcher::handleRead), ba);
+}
+
+void Launcher::handleRead(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 4) {
+ d->m_downloadState.localFile->close();
+ closeRemoteFile(true);
+ disconnectTrk();
+ } else {
+ int length = extractShort(result.data.data() + 2);
+ //TRK doesn't tell us the file length, so we need to keep reading until it returns 0 length
+ if (length > 0) {
+ d->m_downloadState.localFile->write(result.data.data() + 4, length);
+ continueReading();
+ } else {
+ closeRemoteFile(true);
+ disconnectTrk();
+ }
+ }
+}
+
void Launcher::handleCopy(const TrkResult &result)
{
if (result.errorCode() || result.data.size() < 4) {
@@ -493,13 +653,14 @@ void Launcher::handleCopy(const TrkResult &result)
void Launcher::continueCopying(uint lastCopiedBlockSize)
{
- int size = d->m_copyState.data->length();
+ qint64 size = d->m_copyState.data->length();
d->m_copyState.position += lastCopiedBlockSize;
if (size == 0)
emit copyProgress(100);
else {
- int percent = qMin((d->m_copyState.position*100)/size, 100);
- emit copyProgress(percent);
+ const qint64 hundred = 100;
+ const qint64 percent = qMin( (d->m_copyState.position * hundred) / size, hundred);
+ emit copyProgress(static_cast<int>(percent));
}
if (d->m_copyState.position < size) {
QByteArray ba;
@@ -532,6 +693,8 @@ void Launcher::handleFileCopied(const TrkResult &result)
installRemotePackageSilently();
else if (d->m_startupActions & ActionRun)
startInferiorIfNeeded();
+ else if (d->m_startupActions & ActionDownload)
+ copyFileFromRemote();
else
disconnectTrk();
}
@@ -576,6 +739,15 @@ void Launcher::handleCreateProcess(const TrkResult &result)
logMessage(msg);
}
emit applicationRunning(d->m_session.pid);
+ //create a "library" entry for the executable which launched the process
+ Library lib;
+ lib.pid = d->m_session.pid;
+ lib.codeseg = d->m_session.codeseg;
+ lib.dataseg = d->m_session.dataseg;
+ lib.name = d->m_fileName.toUtf8();
+ d->m_session.libraries << lib;
+ emit libraryLoaded(lib);
+
QByteArray ba;
appendInt(&ba, d->m_session.pid);
appendInt(&ba, d->m_session.tid);
@@ -586,7 +758,7 @@ void Launcher::handleWaitForFinished(const TrkResult &result)
{
logMessage(" FINISHED: " + stringFromArray(result.data));
setState(Disconnected);
- emit finished();
+ handleFinished();
}
void Launcher::handleSupportMask(const TrkResult &result)
@@ -595,17 +767,18 @@ void Launcher::handleSupportMask(const TrkResult &result)
return;
const char *data = result.data.data() + 1;
- QString str = QLatin1String("SUPPORTED: ");
- for (int i = 0; i < 32; ++i) {
- //str.append(" [" + formatByte(data[i]) + "]: ");
- for (int j = 0; j < 8; ++j) {
- if (data[i] & (1 << j)) {
- str.append(QString::number(i * 8 + j, 16));
- str.append(QLatin1Char(' '));
+ if (d->m_verbose > 1) {
+ QString str = QLatin1String("SUPPORTED: ");
+ for (int i = 0; i < 32; ++i) {
+ for (int j = 0; j < 8; ++j) {
+ if (data[i] & (1 << j)) {
+ str.append(QString::number(i * 8 + j, 16));
+ str.append(QLatin1Char(' '));
+ }
}
}
+ logMessage(str);
}
- logMessage(str);
}
void Launcher::cleanUp()
@@ -669,11 +842,20 @@ void Launcher::copyFileToRemote()
{
emit copyingStarted();
QByteArray ba;
- ba.append(char(10));
+ ba.append(char(10)); //kDSFileOpenWrite | kDSFileOpenBinary
appendString(&ba, d->m_copyState.destinationFileName.toLocal8Bit(), TargetByteOrder, false);
d->m_device->sendTrkMessage(TrkOpenFile, TrkCallback(this, &Launcher::handleFileCreation), ba);
}
+void Launcher::copyFileFromRemote()
+{
+ emit copyingStarted();
+ QByteArray ba;
+ ba.append(char(9)); //kDSFileOpenRead | kDSFileOpenBinary
+ appendString(&ba, d->m_downloadState.sourceFileName.toLocal8Bit(), TargetByteOrder, false);
+ d->m_device->sendTrkMessage(TrkOpenFile, TrkCallback(this, &Launcher::handleFileOpened), ba);
+}
+
void Launcher::installRemotePackageSilently()
{
emit installingStarted();
@@ -694,6 +876,8 @@ void Launcher::handleInstallPackageFinished(const TrkResult &result)
}
if (d->m_startupActions & ActionRun) {
startInferiorIfNeeded();
+ } else if (d->m_startupActions & ActionDownload) {
+ copyFileFromRemote();
} else {
disconnectTrk();
}
@@ -704,18 +888,38 @@ QByteArray Launcher::startProcessMessage(const QString &executable,
{
// It's not started yet
QByteArray ba;
- appendShort(&ba, 0, TargetByteOrder); // create new process
+ appendShort(&ba, 0, TargetByteOrder); // create new process (kDSOSProcessItem)
ba.append(char(0)); // options - currently unused
- if(arguments.isEmpty()) {
- appendString(&ba, executable.toLocal8Bit(), TargetByteOrder);
- return ba;
- }
- // Append full command line as one string (leading length information).
- QByteArray commandLineBa;
- commandLineBa.append(executable.toLocal8Bit());
- commandLineBa.append('\0');
- commandLineBa.append(arguments.join(QString(QLatin1Char(' '))).toLocal8Bit());
- appendString(&ba, commandLineBa, TargetByteOrder);
+ // One string consisting of binary terminated by '\0' and arguments terminated by '\0'
+ QByteArray commandLineBa = executable.toLocal8Bit();
+ commandLineBa.append(char(0));
+ if (!arguments.isEmpty())
+ commandLineBa.append(arguments.join(QString(QLatin1Char(' '))).toLocal8Bit());
+ appendString(&ba, commandLineBa, TargetByteOrder, true);
+ return ba;
+}
+
+QByteArray Launcher::readMemoryMessage(uint pid, uint tid, uint from, uint len)
+{
+ QByteArray ba;
+ ba.reserve(11);
+ ba.append(char(0x8)); // Options, FIXME: why?
+ appendShort(&ba, len);
+ appendInt(&ba, from);
+ appendInt(&ba, pid);
+ appendInt(&ba, tid);
+ return ba;
+}
+
+QByteArray Launcher::readRegistersMessage(uint pid, uint tid)
+{
+ QByteArray ba;
+ ba.reserve(15);
+ ba.append(char(0)); // Register set, only 0 supported
+ appendShort(&ba, 0); //R0
+ appendShort(&ba, 16); // last register CPSR
+ appendInt(&ba, pid);
+ appendInt(&ba, tid);
return ba;
}
@@ -737,4 +941,96 @@ void Launcher::resumeProcess(uint pid, uint tid)
appendInt(&ba, tid, BigEndian);
d->m_device->sendTrkMessage(TrkContinue, TrkCallback(), ba, "CONTINUE");
}
+
+// Acquire a device from SymbianDeviceManager, return 0 if not available.
+Launcher *Launcher::acquireFromDeviceManager(const QString &serverName,
+ QObject *parent,
+ QString *errorMessage)
+{
+ SymbianUtils::SymbianDeviceManager *sdm = SymbianUtils::SymbianDeviceManager::instance();
+ const QSharedPointer<trk::TrkDevice> device = sdm->acquireDevice(serverName);
+ if (device.isNull()) {
+ *errorMessage = tr("Unable to acquire a device for port '%1'. It appears to be in use.").arg(serverName);
+ return 0;
+ }
+ // Wire release signal.
+ Launcher *rc = new Launcher(trk::Launcher::ActionPingOnly, device, parent);
+ connect(rc, SIGNAL(deviceDescriptionReceived(QString,QString)),
+ sdm, SLOT(setAdditionalInformation(QString,QString)));
+ connect(rc, SIGNAL(destroyed(QString)), sdm, SLOT(releaseDevice(QString)));
+ return rc;
+}
+
+// Preliminary release of device, disconnecting the signal.
+void Launcher::releaseToDeviceManager(Launcher *launcher)
+{
+ SymbianUtils::SymbianDeviceManager *sdm = SymbianUtils::SymbianDeviceManager::instance();
+ // Disentangle launcher and its device, remove connection from destroyed
+ launcher->setCloseDevice(false);
+ TrkDevice *device = launcher->trkDevice().data();
+ launcher->disconnect(device);
+ device->disconnect(launcher);
+ launcher->disconnect(sdm);
+ sdm->releaseDevice(launcher->trkServerName());
+}
+
+void Launcher::getRegistersAndCallStack(uint pid, uint tid)
+{
+ d->m_device->sendTrkMessage(TrkReadRegisters,
+ TrkCallback(this, &Launcher::handleReadRegisters),
+ Launcher::readRegistersMessage(pid, tid));
+ d->m_crashReportState.fetchingStackPID = pid;
+ d->m_crashReportState.fetchingStackTID = tid;
+}
+
+void Launcher::handleReadRegisters(const TrkResult &result)
+{
+ if(result.errorCode() || result.data.size() < (17*4)) {
+ terminate();
+ return;
+ }
+ const char* data = result.data.constData() + 1;
+ d->m_crashReportState.registers.clear();
+ d->m_crashReportState.stack.clear();
+ for (int i=0;i<17;i++) {
+ uint r = extractInt(data);
+ data += 4;
+ d->m_crashReportState.registers.append(r);
+ }
+ d->m_crashReportState.sp = d->m_crashReportState.registers.at(13);
+
+ const ushort len = 1024 - (d->m_crashReportState.sp % 1024); //read to 1k boundary first
+ const QByteArray ba = Launcher::readMemoryMessage(d->m_crashReportState.fetchingStackPID,
+ d->m_crashReportState.fetchingStackTID,
+ d->m_crashReportState.sp,
+ len);
+ d->m_device->sendTrkMessage(TrkReadMemory, TrkCallback(this, &Launcher::handleReadStack), ba);
+ d->m_crashReportState.sp += len;
+}
+
+void Launcher::handleReadStack(const TrkResult &result)
+{
+ if (result.errorCode()) {
+ //error implies memory fault when reaching end of stack
+ emit registersAndCallStackReadComplete(d->m_crashReportState.registers, d->m_crashReportState.stack);
+ return;
+ }
+
+ const uint len = extractShort(result.data.constData() + 1);
+ d->m_crashReportState.stack.append(result.data.mid(3, len));
+
+ if (d->m_crashReportState.sp - d->m_crashReportState.registers.at(13) > 0x10000) {
+ //read enough stack, stop here
+ emit registersAndCallStackReadComplete(d->m_crashReportState.registers, d->m_crashReportState.stack);
+ return;
+ }
+ //read 1k more
+ const QByteArray ba = Launcher::readMemoryMessage(d->m_crashReportState.fetchingStackPID,
+ d->m_crashReportState.fetchingStackTID,
+ d->m_crashReportState.sp,
+ 1024);
+ d->m_device->sendTrkMessage(TrkReadMemory, TrkCallback(this, &Launcher::handleReadStack), ba);
+ d->m_crashReportState.sp += 1024;
+}
+
} // namespace trk
diff --git a/tools/runonphone/symbianutils/launcher.h b/tools/runonphone/symbianutils/launcher.h
index 2b23fd8..230a122 100644
--- a/tools/runonphone/symbianutils/launcher.h
+++ b/tools/runonphone/symbianutils/launcher.h
@@ -43,6 +43,7 @@
#define LAUNCHER_H
#include "trkdevice.h"
+#include "trkutils.h"
#include <QtCore/QObject>
#include <QtCore/QVariant>
@@ -69,6 +70,7 @@ public:
ActionInstall = 0x2,
ActionCopyInstall = ActionCopy | ActionInstall,
ActionRun = 0x4,
+ ActionDownload = 0x8,
ActionCopyRun = ActionCopy | ActionRun,
ActionInstallRun = ActionInstall | ActionRun,
ActionCopyInstallRun = ActionCopy | ActionInstall | ActionRun
@@ -94,10 +96,11 @@ public:
QString trkServerName() const;
void setFileName(const QString &name);
void setCopyFileName(const QString &srcName, const QString &dstName);
+ void setDownloadFileName(const QString &srcName, const QString &dstName);
void setInstallFileName(const QString &name);
void setCommandLineArgs(const QStringList &args);
bool startServer(QString *errorMessage);
- void setVerbose(int v);
+ void setVerbose(int v);
void setSerialFrame(bool b);
bool serialFrame() const;
// Close device or leave it open
@@ -109,8 +112,20 @@ public:
// becomes valid after successful execution of ActionPingOnly
QString deviceDescription(unsigned verbose = 0u) const;
+ // Acquire a device from SymbianDeviceManager, return 0 if not available.
+ // The device will be released on destruction.
+ static Launcher *acquireFromDeviceManager(const QString &serverName,
+ QObject *parent,
+ QString *errorMessage);
+ // Preliminary release of device, disconnecting the signal.
+ static void releaseToDeviceManager(Launcher *l);
+
+ // Create Trk message to start a process.
static QByteArray startProcessMessage(const QString &executable,
const QStringList &arguments);
+ // Create Trk message to read memory
+ static QByteArray readMemoryMessage(uint pid, uint tid, uint from, uint len);
+ static QByteArray readRegistersMessage(uint pid, uint tid);
// Parse a TrkNotifyStopped message
static bool parseNotifyStopped(const QByteArray &a,
uint *pid, uint *tid, uint *address,
@@ -119,9 +134,12 @@ public:
static QString msgStopped(uint pid, uint tid, uint address, const QString &why);
signals:
+ void deviceDescriptionReceived(const QString &port, const QString &description);
void copyingStarted();
void canNotConnect(const QString &errorMessage);
void canNotCreateFile(const QString &filename, const QString &errorMessage);
+ void canNotOpenFile(const QString &filename, const QString &errorMessage);
+ void canNotOpenLocalFile(const QString &filename, const QString &errorMessage);
void canNotWriteFile(const QString &filename, const QString &errorMessage);
void canNotCloseFile(const QString &filename, const QString &errorMessage);
void installingStarted();
@@ -135,10 +153,18 @@ signals:
void copyProgress(int percent);
void stateChanged(int);
void processStopped(uint pc, uint pid, uint tid, const QString& reason);
+ void processResumed(uint pid, uint tid);
+ void libraryLoaded(const trk::Library &lib);
+ void libraryUnloaded(const trk::Library &lib);
+ void registersAndCallStackReadComplete(const QList<uint>& registers, const QByteArray& stack);
+ // Emitted by the destructor, for releasing devices of SymbianDeviceManager by name
+ void destroyed(const QString &serverName);
public slots:
void terminate();
void resumeProcess(uint pid, uint tid);
+ //can be used to obtain traceback after a breakpoint / exception
+ void getRegistersAndCallStack(uint pid, uint tid);
private slots:
void handleResult(const trk::TrkResult &data);
@@ -152,8 +178,11 @@ private:
void handleRemoteProcessKilled(const TrkResult &result);
void handleConnect(const TrkResult &result);
void handleFileCreation(const TrkResult &result);
+ void handleFileOpened(const TrkResult &result);
void handleCopy(const TrkResult &result);
+ void handleRead(const TrkResult &result);
void continueCopying(uint lastCopiedBlockSize = 0);
+ void continueReading();
void closeRemoteFile(bool failed = false);
void handleFileCopied(const TrkResult &result);
void handleInstallPackageFinished(const TrkResult &result);
@@ -163,10 +192,14 @@ private:
void handleStop(const TrkResult &result);
void handleSupportMask(const TrkResult &result);
void handleTrkVersion(const TrkResult &result);
+ void handleReadRegisters(const TrkResult &result);
+ void handleReadStack(const TrkResult &result);
void copyFileToRemote();
+ void copyFileFromRemote();
void installRemotePackageSilently();
void startInferiorIfNeeded();
+ void handleFinished();
void logMessage(const QString &msg);
void setState(State s);
diff --git a/tools/runonphone/symbianutils/symbiandevicemanager.cpp b/tools/runonphone/symbianutils/symbiandevicemanager.cpp
index f663816..8877ea1 100644
--- a/tools/runonphone/symbianutils/symbiandevicemanager.cpp
+++ b/tools/runonphone/symbianutils/symbiandevicemanager.cpp
@@ -40,6 +40,7 @@
****************************************************************************/
#include "symbiandevicemanager.h"
+#include "trkdevice.h"
#include <QtCore/QSettings>
#include <QtCore/QStringList>
@@ -48,6 +49,7 @@
#include <QtCore/QTextStream>
#include <QtCore/QSharedData>
#include <QtCore/QScopedPointer>
+#include <QtCore/QSignalMapper>
namespace SymbianUtils {
@@ -61,19 +63,51 @@ const char *SymbianDeviceManager::linuxBlueToothDeviceRootC = "/dev/rfcomm";
// ------------- SymbianDevice
class SymbianDeviceData : public QSharedData {
public:
- SymbianDeviceData() : type(SerialPortCommunication) {}
+ SymbianDeviceData();
+ ~SymbianDeviceData();
+
+ inline bool isOpen() const { return !device.isNull() && device->isOpen(); }
+ void forcedClose();
QString portName;
QString friendlyName;
QString deviceDesc;
QString manufacturer;
+ QString additionalInformation;
+
DeviceCommunicationType type;
+ QSharedPointer<trk::TrkDevice> device;
+ bool deviceAcquired;
};
+SymbianDeviceData::SymbianDeviceData() :
+ type(SerialPortCommunication),
+ deviceAcquired(false)
+{
+}
+
+SymbianDeviceData::~SymbianDeviceData()
+{
+ forcedClose();
+}
+
+void SymbianDeviceData::forcedClose()
+{
+ // Close the device when unplugging. Should devices be in 'acquired' state,
+ // their owners should hit on write failures.
+ // Apart from the <shared item> destructor, also called by the devicemanager
+ // to ensure it also happens if other shared instances are still around.
+ if (isOpen()) {
+ if (deviceAcquired)
+ qWarning("Device on '%s' unplugged while an operation is in progress.",
+ qPrintable(portName));
+ device->close();
+ }
+}
+
SymbianDevice::SymbianDevice(SymbianDeviceData *data) :
m_data(data)
{
-
}
SymbianDevice::SymbianDevice() :
@@ -96,6 +130,11 @@ SymbianDevice::~SymbianDevice()
{
}
+void SymbianDevice::forcedClose()
+{
+ m_data->forcedClose();
+}
+
QString SymbianDevice::portName() const
{
return m_data->portName;
@@ -106,6 +145,51 @@ QString SymbianDevice::friendlyName() const
return m_data->friendlyName;
}
+QString SymbianDevice::additionalInformation() const
+{
+ return m_data->additionalInformation;
+}
+
+void SymbianDevice::setAdditionalInformation(const QString &a)
+{
+ m_data->additionalInformation = a;
+}
+
+SymbianDevice::TrkDevicePtr SymbianDevice::acquireDevice()
+{
+ if (debug)
+ qDebug() << "SymbianDevice::acquireDevice" << m_data->portName
+ << "acquired: " << m_data->deviceAcquired << " open: " << isOpen();
+ if (isNull() || m_data->deviceAcquired)
+ return TrkDevicePtr();
+ if (m_data->device.isNull()) {
+ m_data->device = TrkDevicePtr(new trk::TrkDevice);
+ m_data->device->setPort(m_data->portName);
+ m_data->device->setSerialFrame(m_data->type == SerialPortCommunication);
+ }
+ m_data->deviceAcquired = true;
+ return m_data->device;
+}
+
+void SymbianDevice::releaseDevice(TrkDevicePtr *ptr /* = 0 */)
+{
+ if (debug)
+ qDebug() << "SymbianDevice::releaseDevice" << m_data->portName
+ << " open: " << isOpen();
+ if (m_data->deviceAcquired) {
+ if (m_data->device->isOpen())
+ m_data->device->clearWriteQueue();
+ // Release if a valid pointer was passed in.
+ if (ptr && !ptr->isNull()) {
+ ptr->data()->disconnect();
+ *ptr = TrkDevicePtr();
+ }
+ m_data->deviceAcquired = false;
+ } else {
+ qWarning("Internal error: Attempt to release device that is not acquired.");
+ }
+}
+
QString SymbianDevice::deviceDesc() const
{
return m_data->deviceDesc;
@@ -123,7 +207,12 @@ DeviceCommunicationType SymbianDevice::type() const
bool SymbianDevice::isNull() const
{
- return !m_data->portName.isEmpty();
+ return m_data->portName.isEmpty();
+}
+
+bool SymbianDevice::isOpen() const
+{
+ return m_data->isOpen();
}
QString SymbianDevice::toString() const
@@ -158,7 +247,7 @@ int SymbianDevice::compare(const SymbianDevice &rhs) const
return 0;
}
-QDebug operator<<(QDebug d, const SymbianDevice &cd)
+SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDevice &cd)
{
d.nospace() << cd.toString();
return d;
@@ -166,10 +255,11 @@ QDebug operator<<(QDebug d, const SymbianDevice &cd)
// ------------- SymbianDeviceManagerPrivate
struct SymbianDeviceManagerPrivate {
- SymbianDeviceManagerPrivate() : m_initialized(false) {}
+ SymbianDeviceManagerPrivate() : m_initialized(false), m_destroyReleaseMapper(0) {}
bool m_initialized;
SymbianDeviceManager::SymbianDeviceList m_devices;
+ QSignalMapper *m_destroyReleaseMapper;
};
SymbianDeviceManager::SymbianDeviceManager(QObject *parent) :
@@ -185,8 +275,7 @@ SymbianDeviceManager::~SymbianDeviceManager()
SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::devices() const
{
- if (!d->m_initialized)
- const_cast<SymbianDeviceManager*>(this)->update(false);
+ ensureInitialized();
return d->m_devices;
}
@@ -194,6 +283,7 @@ QString SymbianDeviceManager::toString() const
{
QString rc;
QTextStream str(&rc);
+ str << d->m_devices.size() << " devices:\n";
const int count = d->m_devices.size();
for (int i = 0; i < count; i++) {
str << '#' << i << ' ';
@@ -203,13 +293,37 @@ QString SymbianDeviceManager::toString() const
return rc;
}
+int SymbianDeviceManager::findByPortName(const QString &p) const
+{
+ ensureInitialized();
+ const int count = d->m_devices.size();
+ for (int i = 0; i < count; i++)
+ if (d->m_devices.at(i).portName() == p)
+ return i;
+ return -1;
+}
+
QString SymbianDeviceManager::friendlyNameForPort(const QString &port) const
{
- foreach (const SymbianDevice &device, d->m_devices) {
- if (device.portName() == port)
- return device.friendlyName();
- }
- return QString();
+ const int idx = findByPortName(port);
+ return idx == -1 ? QString() : d->m_devices.at(idx).friendlyName();
+}
+
+SymbianDeviceManager::TrkDevicePtr
+ SymbianDeviceManager::acquireDevice(const QString &port)
+{
+ ensureInitialized();
+ const int idx = findByPortName(port);
+ if (idx == -1) {
+ qWarning("Attempt to acquire device '%s' that does not exist.", qPrintable(port));
+ if (debug)
+ qDebug() << *this;
+ return TrkDevicePtr();
+ }
+ const TrkDevicePtr rc = d->m_devices[idx].acquireDevice();
+ if (debug)
+ qDebug() << "SymbianDeviceManager::acquireDevice" << port << " returns " << !rc.isNull();
+ return rc;
}
void SymbianDeviceManager::update()
@@ -217,12 +331,38 @@ void SymbianDeviceManager::update()
update(true);
}
+void SymbianDeviceManager::releaseDevice(const QString &port)
+{
+ const int idx = findByPortName(port);
+ if (debug)
+ qDebug() << "SymbianDeviceManager::releaseDevice" << port << idx << sender();
+ if (idx != -1) {
+ d->m_devices[idx].releaseDevice();
+ } else {
+ qWarning("Attempt to release non-existing device %s.", qPrintable(port));
+ }
+}
+
+void SymbianDeviceManager::setAdditionalInformation(const QString &port, const QString &ai)
+{
+ const int idx = findByPortName(port);
+ if (idx != -1)
+ d->m_devices[idx].setAdditionalInformation(ai);
+}
+
+void SymbianDeviceManager::ensureInitialized() const
+{
+ if (!d->m_initialized) // Flag is set in update()
+ const_cast<SymbianDeviceManager*>(this)->update(false);
+}
+
void SymbianDeviceManager::update(bool emitSignals)
{
+ static int n = 0;
typedef SymbianDeviceList::iterator SymbianDeviceListIterator;
if (debug)
- qDebug(">SerialDeviceLister::update(%d)\n%s", int(emitSignals),
+ qDebug(">SerialDeviceLister::update(#%d, signals=%d)\n%s", n++, int(emitSignals),
qPrintable(toString()));
d->m_initialized = true;
@@ -230,8 +370,11 @@ void SymbianDeviceManager::update(bool emitSignals)
SymbianDeviceList newDevices = serialPorts() + blueToothDevices();
if (newDevices.size() > 1)
qStableSort(newDevices.begin(), newDevices.end());
- if (d->m_devices == newDevices) // Happy, nothing changed.
+ if (d->m_devices == newDevices) { // Happy, nothing changed.
+ if (debug)
+ qDebug("<SerialDeviceLister::update: unchanged\n");
return;
+ }
// Merge the lists and emit the respective added/removed signals, assuming
// no one can plug a different device on the same port at the speed of lightning
if (!d->m_devices.isEmpty()) {
@@ -240,7 +383,8 @@ void SymbianDeviceManager::update(bool emitSignals)
if (newDevices.contains(*oldIt)) {
++oldIt;
} else {
- const SymbianDevice toBeDeleted = *oldIt;
+ SymbianDevice toBeDeleted = *oldIt;
+ toBeDeleted.forcedClose();
oldIt = d->m_devices.erase(oldIt);
if (emitSignals)
emit deviceRemoved(toBeDeleted);
@@ -301,16 +445,30 @@ SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::blueToothDevices()
// Bluetooth devices are created on connection. List the existing ones
// or at least the first one.
const QString prefix = QLatin1String(linuxBlueToothDeviceRootC);
- const QString friendlyFormat = QLatin1String("Bluetooth device (%1)");
+ const QString blueToothfriendlyFormat = QLatin1String("Bluetooth device (%1)");
for (int d = 0; d < 4; d++) {
QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
device->type = BlueToothCommunication;
device->portName = prefix + QString::number(d);
if (d == 0 || QFileInfo(device->portName).exists()) {
- device->friendlyName = friendlyFormat.arg(device->portName);
+ device->friendlyName = blueToothfriendlyFormat.arg(device->portName);
rc.push_back(SymbianDevice(device.take()));
}
}
+ // New kernel versions support /dev/ttyUSB0, /dev/ttyUSB1. Trk responds
+ // on the latter (usually), try first.
+ static const char *usbTtyDevices[] = { "/dev/ttyUSB1", "/dev/ttyUSB0" };
+ const int usbTtyCount = sizeof(usbTtyDevices)/sizeof(const char *);
+ for (int d = 0; d < usbTtyCount; d++) {
+ const QString ttyUSBDevice = QLatin1String(usbTtyDevices[d]);
+ if (QFileInfo(ttyUSBDevice).exists()) {
+ SymbianDeviceData *device = new SymbianDeviceData;
+ device->type = SerialPortCommunication;
+ device->portName = ttyUSBDevice;
+ device->friendlyName = QString::fromLatin1("USB/Serial device (%1)").arg(device->portName);
+ rc.push_back(SymbianDevice(device));
+ }
+ }
#endif
return rc;
}
@@ -322,7 +480,7 @@ SymbianDeviceManager *SymbianDeviceManager::instance()
return symbianDeviceManager();
}
-QDebug operator<<(QDebug d, const SymbianDeviceManager &sdm)
+SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDeviceManager &sdm)
{
d.nospace() << sdm.toString();
return d;
diff --git a/tools/runonphone/symbianutils/symbiandevicemanager.h b/tools/runonphone/symbianutils/symbiandevicemanager.h
index dcf131a..180173d 100644
--- a/tools/runonphone/symbianutils/symbiandevicemanager.h
+++ b/tools/runonphone/symbianutils/symbiandevicemanager.h
@@ -46,12 +46,17 @@
#include <QtCore/QObject>
#include <QtCore/QExplicitlySharedDataPointer>
+#include <QtCore/QSharedPointer>
QT_BEGIN_NAMESPACE
class QDebug;
class QTextStream;
QT_END_NAMESPACE
+namespace trk {
+ class TrkDevice;
+}
+
namespace SymbianUtils {
struct SymbianDeviceManagerPrivate;
@@ -62,11 +67,16 @@ enum DeviceCommunicationType {
BlueToothCommunication = 1
};
-// SymbianDevice, explicitly shared.
+// SymbianDevice: Explicitly shared device data and a TrkDevice
+// instance that can be acquired (exclusively) for use.
+// A device removal from the manager will result in the
+// device being closed.
class SYMBIANUTILS_EXPORT SymbianDevice {
explicit SymbianDevice(SymbianDeviceData *data);
friend class SymbianDeviceManager;
public:
+ typedef QSharedPointer<trk::TrkDevice> TrkDevicePtr;
+
SymbianDevice();
SymbianDevice(const SymbianDevice &rhs);
SymbianDevice &operator=(const SymbianDevice &rhs);
@@ -77,6 +87,17 @@ public:
bool isNull() const;
QString portName() const;
QString friendlyName() const;
+ QString additionalInformation() const;
+ void setAdditionalInformation(const QString &);
+
+ // Acquire: Mark the device as 'out' and return a shared pointer
+ // unless it is already in use by another owner. The result should not
+ // be passed on further.
+ TrkDevicePtr acquireDevice();
+ // Give back a device and mark it as 'free'.
+ void releaseDevice(TrkDevicePtr *ptr = 0);
+
+ bool isOpen() const;
// Windows only.
QString deviceDesc() const;
@@ -86,10 +107,12 @@ public:
QString toString() const;
private:
+ void forcedClose();
+
QExplicitlySharedDataPointer<SymbianDeviceData> m_data;
};
-QDebug operator<<(QDebug d, const SymbianDevice &);
+SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDevice &);
inline bool operator==(const SymbianDevice &d1, const SymbianDevice &d2)
{ return d1.compare(d2) == 0; }
@@ -100,13 +123,15 @@ inline bool operator<(const SymbianDevice &d1, const SymbianDevice &d2)
/* SymbianDeviceManager: Singleton that maintains a list of Symbian devices.
* and emits change signals.
- * On Windows, the update slot must be connected to a signal
- * emitted from an event handler listening for WM_DEVICECHANGE. */
+ * On Windows, the update slot must be connected to a [delayed] signal
+ * emitted from an event handler listening for WM_DEVICECHANGE.
+ * Device removal will result in the device being closed. */
class SYMBIANUTILS_EXPORT SymbianDeviceManager : public QObject
{
Q_OBJECT
public:
typedef QList<SymbianDevice> SymbianDeviceList;
+ typedef QSharedPointer<trk::TrkDevice> TrkDevicePtr;
static const char *linuxBlueToothDeviceRootC;
@@ -120,17 +145,25 @@ public:
SymbianDeviceList devices() const;
QString toString() const;
+ // Acquire a device for use. See releaseDevice().
+ TrkDevicePtr acquireDevice(const QString &port);
+
+ int findByPortName(const QString &p) const;
QString friendlyNameForPort(const QString &port) const;
public slots:
void update();
+ // Relase a device, make it available for further use.
+ void releaseDevice(const QString &port);
+ void setAdditionalInformation(const QString &port, const QString &ai);
signals:
- void deviceRemoved(const SymbianDevice &d);
- void deviceAdded(const SymbianDevice &d);
+ void deviceRemoved(const SymbianUtils::SymbianDevice &d);
+ void deviceAdded(const SymbianUtils::SymbianDevice &d);
void updated();
private:
+ void ensureInitialized() const;
void update(bool emitSignals);
SymbianDeviceList serialPorts() const;
SymbianDeviceList blueToothDevices() const;
@@ -138,7 +171,7 @@ private:
SymbianDeviceManagerPrivate *d;
};
-QDebug operator<<(QDebug d, const SymbianDeviceManager &);
+SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDeviceManager &);
} // namespace SymbianUtils
diff --git a/tools/runonphone/symbianutils/symbianutils.pri b/tools/runonphone/symbianutils/symbianutils.pri
index 6309517..f07e494 100644
--- a/tools/runonphone/symbianutils/symbianutils.pri
+++ b/tools/runonphone/symbianutils/symbianutils.pri
@@ -1,5 +1,7 @@
INCLUDEPATH *= $$PWD
+QT += network
+
# Input
HEADERS += $$PWD/symbianutils_global.h \
$$PWD/callback.h \
@@ -9,14 +11,20 @@ HEADERS += $$PWD/symbianutils_global.h \
$$PWD/launcher.h \
$$PWD/bluetoothlistener.h \
$$PWD/communicationstarter.h \
- $$PWD/symbiandevicemanager.h
+ $$PWD/symbiandevicemanager.h \
+ $$PWD/tcftrkdevice.h \
+ $$PWD/tcftrkmessage.h \
+ $$PWD/json.h
SOURCES += $$PWD/trkutils.cpp \
$$PWD/trkdevice.cpp \
$$PWD/launcher.cpp \
$$PWD/bluetoothlistener.cpp \
$$PWD/communicationstarter.cpp \
- $$PWD/symbiandevicemanager.cpp
+ $$PWD/symbiandevicemanager.cpp \
+ $$PWD/tcftrkdevice.cpp \
+ $$PWD/tcftrkmessage.cpp \
+ $$PWD/json.cpp
# Tests/trklauncher is a console application
contains(QT, gui) {
diff --git a/tools/runonphone/symbianutils/tcftrkdevice.cpp b/tools/runonphone/symbianutils/tcftrkdevice.cpp
new file mode 100644
index 0000000..50e3937
--- /dev/null
+++ b/tools/runonphone/symbianutils/tcftrkdevice.cpp
@@ -0,0 +1,929 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "tcftrkdevice.h"
+#include "json.h"
+
+#include <QtNetwork/QAbstractSocket>
+#include <QtCore/QDebug>
+#include <QtCore/QVector>
+#include <QtCore/QQueue>
+#include <QtCore/QTextStream>
+#include <QtCore/QDateTime>
+#include <QtCore/QFileInfo>
+
+enum { debug = 0 };
+
+static const char messageTerminatorC[] = "\003\001";
+
+namespace tcftrk {
+// ------------- TcfTrkCommandError
+
+TcfTrkCommandError::TcfTrkCommandError() : timeMS(0), code(0), alternativeCode(0)
+{
+}
+
+void TcfTrkCommandError::clear()
+{
+ timeMS = 0;
+ code = alternativeCode = 0;
+ format.clear();
+ alternativeOrganization.clear();
+}
+
+void TcfTrkCommandError::write(QTextStream &str) const
+{
+ if (timeMS) {
+ const QDateTime time(QDate(1970, 1, 1));
+ str << time.addMSecs(timeMS).toString(Qt::ISODate) << ": Error code: " << code
+ << " '" << format << '\'';
+ if (!alternativeOrganization.isEmpty())
+ str << " ('" << alternativeOrganization << "', code: " << alternativeCode << ')';
+ } else{
+ str << "<No error>";
+ }
+}
+
+QString TcfTrkCommandError::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ write(str);
+ return rc;
+}
+
+/* {"Time":1277459762255,"Code":1,"AltCode":-6,"AltOrg":"POSIX","Format":"Unknown error: -6"} */
+bool TcfTrkCommandError::parse(const QVector<JsonValue> &values)
+{
+ // Parse an arbitrary hash (that could as well be a command response)
+ // and check for error elements.
+ unsigned errorKeyCount = 0;
+ clear();
+ do {
+ if (values.isEmpty() || values.front().type() != JsonValue::Object)
+ break;
+ foreach (const JsonValue &c, values.front().children()) {
+ if (c.name() == "Time") {
+ timeMS = c.data().toULongLong();
+ errorKeyCount++;
+ } else if (c.name() == "Code") {
+ code = c.data().toInt();
+ errorKeyCount++;
+ } else if (c.name() == "Format") {
+ format = c.data();
+ errorKeyCount++;
+ } else if (c.name() == "AltCode") {
+ alternativeCode = c.data().toInt();
+ errorKeyCount++;
+ } else if (c.name() == "AltOrg") {
+ alternativeOrganization = c.data();
+ errorKeyCount++;
+ }
+ }
+ } while (false);
+ const bool errorFound = errorKeyCount >= 2u; // Should be at least 'Time', 'Code'.
+ if (!errorFound)
+ clear();
+ if (debug) {
+ qDebug() << "TcfTrkCommandError::parse: Found error: " << errorFound;
+ if (!values.isEmpty())
+ qDebug() << values.front().toString();
+ }
+ return errorFound;
+}
+
+// ------------ TcfTrkCommandResult
+
+TcfTrkCommandResult::TcfTrkCommandResult(Type t) :
+ type(t), service(LocatorService)
+{
+}
+
+TcfTrkCommandResult::TcfTrkCommandResult(char typeChar, Services s,
+ const QByteArray &r,
+ const QVector<JsonValue> &v,
+ const QVariant &ck) :
+ type(FailReply), service(s), request(r), values(v), cookie(ck)
+{
+ switch (typeChar) {
+ case 'N':
+ type = FailReply;
+ break;
+ case 'P':
+ type = ProgressReply;
+ break;
+ case 'R':
+ type = commandError.parse(values) ? CommandErrorReply : SuccessReply;
+ break;
+ default:
+ qWarning("Unknown TCF reply type '%c'", typeChar);
+ }
+}
+
+QString TcfTrkCommandResult::errorString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+
+ switch (type) {
+ case SuccessReply:
+ case ProgressReply:
+ str << "<No error>";
+ return rc;
+ case FailReply:
+ str << "NAK";
+ case CommandErrorReply:
+ commandError.write(str);
+ break;
+ }
+ // Append the failed command for reference
+ str << " (Command was: '";
+ QByteArray printableRequest = request;
+ printableRequest.replace('\0', '|');
+ str << printableRequest << "')";
+ return rc;
+}
+
+QString TcfTrkCommandResult::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << "Command answer ";
+ switch (type) {
+ case SuccessReply:
+ str << "[success]";
+ break;
+ case CommandErrorReply:
+ str << "[command error]";
+ break;
+ case FailReply:
+ str << "[fail (NAK)]";
+ break;
+ case ProgressReply:
+ str << "[progress]";
+ break;
+ }
+ str << ", " << values.size() << " values(s) to request: '";
+ QByteArray printableRequest = request;
+ printableRequest.replace('\0', '|');
+ str << printableRequest << "' ";
+ if (cookie.isValid())
+ str << " cookie: " << cookie.toString();
+ str << '\n';
+ for (int i = 0, count = values.size(); i < count; i++)
+ str << '#' << i << ' ' << values.at(i).toString() << '\n';
+ if (type == CommandErrorReply)
+ str << "Error: " << errorString();
+ return rc;
+}
+
+struct TcfTrkSendQueueEntry
+{
+ typedef TcfTrkDevice::MessageType MessageType;
+
+ explicit TcfTrkSendQueueEntry(MessageType mt,
+ int tok,
+ Services s,
+ const QByteArray &d,
+ const TcfTrkCallback &cb= TcfTrkCallback(),
+ const QVariant &ck = QVariant()) :
+ messageType(mt), service(s), data(d), token(tok), cookie(ck), callback(cb) {}
+
+ MessageType messageType;
+ Services service;
+ QByteArray data;
+ int token;
+ QVariant cookie;
+ TcfTrkCallback callback;
+};
+
+struct TcfTrkDevicePrivate {
+ typedef TcfTrkDevice::IODevicePtr IODevicePtr;
+ typedef QHash<int, TcfTrkSendQueueEntry> TokenWrittenMessageMap;
+
+ TcfTrkDevicePrivate();
+
+ const QByteArray m_messageTerminator;
+
+ IODevicePtr m_device;
+ unsigned m_verbose;
+ QByteArray m_readBuffer;
+ int m_token;
+ QQueue<TcfTrkSendQueueEntry> m_sendQueue;
+ TokenWrittenMessageMap m_writtenMessages;
+ QVector<QByteArray> m_registerNames;
+};
+
+TcfTrkDevicePrivate::TcfTrkDevicePrivate() :
+ m_messageTerminator(messageTerminatorC),
+ m_verbose(0), m_token(0)
+{
+}
+
+TcfTrkDevice::TcfTrkDevice(QObject *parent) :
+ QObject(parent), d(new TcfTrkDevicePrivate)
+{
+}
+
+TcfTrkDevice::~TcfTrkDevice()
+{
+ delete d;
+}
+
+QVector<QByteArray> TcfTrkDevice::registerNames() const
+{
+ return d->m_registerNames;
+}
+
+void TcfTrkDevice::setRegisterNames(const QVector<QByteArray>& n)
+{
+ d->m_registerNames = n;
+ if (d->m_verbose) {
+ QString msg;
+ QTextStream str(&msg);
+ const int count = n.size();
+ str << "Registers (" << count << "): ";
+ for (int i = 0; i < count; i++)
+ str << '#' << i << '=' << n.at(i) << ' ';
+ emitLogMessage(msg);
+ }
+}
+
+TcfTrkDevice::IODevicePtr TcfTrkDevice::device() const
+{
+ return d->m_device;
+}
+
+TcfTrkDevice::IODevicePtr TcfTrkDevice::takeDevice()
+{
+ const IODevicePtr old = d->m_device;
+ if (!old.isNull()) {
+ old.data()->disconnect(this);
+ d->m_device = IODevicePtr();
+ }
+ d->m_readBuffer.clear();
+ d->m_token = 0;
+ d->m_sendQueue.clear();
+ return old;
+}
+
+void TcfTrkDevice::setDevice(const IODevicePtr &dp)
+{
+ if (dp.data() == d->m_device.data())
+ return;
+ if (dp.isNull()) {
+ emitLogMessage(QLatin1String("Internal error: Attempt to set NULL device."));
+ return;
+ }
+ takeDevice();
+ d->m_device = dp;
+ connect(dp.data(), SIGNAL(readyRead()), this, SLOT(slotDeviceReadyRead()));
+ if (QAbstractSocket *s = qobject_cast<QAbstractSocket *>(dp.data())) {
+ connect(s, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotDeviceError()));
+ connect(s, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(slotDeviceSocketStateChanged()));
+ }
+}
+
+void TcfTrkDevice::slotDeviceError()
+{
+ const QString message = d->m_device->errorString();
+ emitLogMessage(message);
+ emit error(message);
+}
+
+void TcfTrkDevice::slotDeviceSocketStateChanged()
+{
+ if (const QAbstractSocket *s = qobject_cast<const QAbstractSocket *>(d->m_device.data())) {
+ const QAbstractSocket::SocketState st = s->state();
+ switch (st) {
+ case QAbstractSocket::UnconnectedState:
+ emitLogMessage(QLatin1String("Unconnected"));
+ break;
+ case QAbstractSocket::HostLookupState:
+ emitLogMessage(QLatin1String("HostLookupState"));
+ break;
+ case QAbstractSocket::ConnectingState:
+ emitLogMessage(QLatin1String("Connecting"));
+ break;
+ case QAbstractSocket::ConnectedState:
+ emitLogMessage(QLatin1String("Connected"));
+ break;
+ case QAbstractSocket::ClosingState:
+ emitLogMessage(QLatin1String("Closing"));
+ break;
+ default:
+ emitLogMessage(QString::fromLatin1("State %1").arg(st));
+ break;
+ }
+ }
+}
+
+static inline QString debugMessage(QByteArray message, const char *prefix = 0)
+{
+ message.replace('\0', '|');
+ const QString messageS = QString::fromLatin1(message);
+ return prefix ?
+ (QLatin1String(prefix) + messageS) : messageS;
+}
+
+void TcfTrkDevice::slotDeviceReadyRead()
+{
+ d->m_readBuffer += d->m_device->readAll();
+ // Take complete message off front of readbuffer.
+ do {
+ const int messageEndPos = d->m_readBuffer.indexOf(d->m_messageTerminator);
+ if (messageEndPos == -1)
+ break;
+ const QByteArray message = d->m_readBuffer.left(messageEndPos);
+ if (debug)
+ qDebug("Read:\n%s", qPrintable(formatData(message)));
+ if (const int errorCode = parseMessage(message)) {
+ emitLogMessage(QString::fromLatin1("Parse error %1 for: %2").arg(errorCode).arg(debugMessage(message)));
+ }
+ d->m_readBuffer.remove(0, messageEndPos + d->m_messageTerminator.size());
+ } while (!d->m_readBuffer.isEmpty());
+ checkSendQueue(); // Send off further message
+}
+
+// Split \0-terminated message into tokens, skipping the initial type character
+static inline QVector<QByteArray> splitMessage(const QByteArray &message)
+{
+ QVector<QByteArray> tokens;
+ tokens.reserve(7);
+ const int messageSize = message.size();
+ for (int pos = 2; pos < messageSize; ) {
+ const int nextPos = message.indexOf('\0', pos);
+ if (nextPos == -1)
+ break;
+ tokens.push_back(message.mid(pos, nextPos - pos));
+ pos = nextPos + 1;
+ }
+ return tokens;
+}
+
+int TcfTrkDevice::parseMessage(const QByteArray &message)
+{
+ if (d->m_verbose)
+ emitLogMessage(debugMessage(message, "TCF ->"));
+ // Special JSON parse error message or protocol format error.
+ // The port is usually closed after receiving it.
+ // "\3\2{"Time":1276096098255,"Code":3,"Format": "Protocol format error"}"
+ if (message.startsWith("\003\002")) {
+ QByteArray text = message.mid(2);
+ const QString errorMessage = QString::fromLatin1("Parse error received: %1").arg(QString::fromAscii(text));
+ emit error(errorMessage);
+ return 0;
+ }
+ if (message.size() < 4 || message.at(1) != '\0')
+ return 1;
+ // Split into tokens
+ const char type = message.at(0);
+ const QVector<QByteArray> tokens = splitMessage(message);
+ switch (type) {
+ case 'E':
+ return parseTcfEvent(tokens);
+ case 'R': // Command replies
+ case 'N':
+ case 'P':
+ return parseTcfCommandReply(type, tokens);
+ default:
+ emitLogMessage(QString::fromLatin1("Unhandled message type: %1").arg(debugMessage(message)));
+ return 756;
+ }
+ return 0;
+}
+
+int TcfTrkDevice::parseTcfCommandReply(char type, const QVector<QByteArray> &tokens)
+{
+ typedef TcfTrkDevicePrivate::TokenWrittenMessageMap::iterator TokenWrittenMessageMapIterator;
+ // Find the corresponding entry in the written messages hash.
+ const int tokenCount = tokens.size();
+ if (tokenCount < 1)
+ return 234;
+ bool tokenOk;
+ const int token = tokens.at(0).toInt(&tokenOk);
+ if (!tokenOk)
+ return 235;
+ const TokenWrittenMessageMapIterator it = d->m_writtenMessages.find(token);
+ if (it == d->m_writtenMessages.end()) {
+ qWarning("TcfTrkDevice: Internal error: token %d not found for '%s'",
+ token, qPrintable(joinByteArrays(tokens)));
+ return 236;
+ }
+ // No callback: remove entry from map, happy
+ if (!it.value().callback) {
+ d->m_writtenMessages.erase(it);
+ return 0;
+ }
+ // Parse values into JSON
+ QVector<JsonValue> values;
+ values.reserve(tokenCount);
+ for (int i = 1; i < tokenCount; i++) {
+ if (!tokens.at(i).isEmpty()) { // Strange: Empty tokens occur.
+ const JsonValue value(tokens.at(i));
+ if (value.isValid()) {
+ values.push_back(value);
+ } else {
+ qWarning("JSON parse error for reply to command token %d: #%d '%s'",
+ token, i, tokens.at(i).constData());
+ d->m_writtenMessages.erase(it);
+ return -1;
+ }
+ }
+ }
+
+ // Construct result and invoke callback, remove entry from map.
+ TcfTrkCallback callback = it.value().callback;
+ TcfTrkCommandResult result(type, it.value().service, it.value().data,
+ values, it.value().cookie);
+ d->m_writtenMessages.erase(it);
+ callback(result);
+ return 0;
+}
+
+static const char locatorAnswerC[] = "E\0Locator\0Hello\0[\"Locator\"]";
+
+int TcfTrkDevice::parseTcfEvent(const QVector<QByteArray> &tokens)
+{
+ // Event: Ignore the periodical heartbeat event, answer 'Hello',
+ // emit signal for the rest
+ if (tokens.size() < 3)
+ return 433;
+ const Services service = serviceFromName(tokens.at(0).constData());
+ if (service == LocatorService && tokens.at(1) == "peerHeartBeat")
+ return 0;
+ QVector<JsonValue> values;
+ for (int i = 2; i < tokens.size(); i++) {
+ const JsonValue value(tokens.at(i));
+ if (!value.isValid())
+ return 434;
+ values.push_back(value);
+ }
+ // Parse known events, emit signals
+ QScopedPointer<TcfTrkEvent> knownEvent(TcfTrkEvent::parseEvent(service, tokens.at(1), values));
+ if (!knownEvent.isNull()) {
+ // Answer hello event.
+ if (knownEvent->type() == TcfTrkEvent::LocatorHello)
+ writeMessage(QByteArray(locatorAnswerC, sizeof(locatorAnswerC)));
+ emit tcfEvent(*knownEvent);
+ }
+ emit genericTcfEvent(service, tokens.at(1), values);
+
+ if (debug || d->m_verbose) {
+ QString msg;
+ QTextStream str(&msg);
+ if (knownEvent.isNull()) {
+ str << "Event: " << tokens.at(0) << ' ' << tokens.at(1) << '\n';
+ foreach(const JsonValue &val, values)
+ str << " " << val.toString() << '\n';
+ } else {
+ str << knownEvent->toString();
+ }
+ emitLogMessage(msg);
+ }
+
+ return 0;
+}
+
+unsigned TcfTrkDevice::verbose() const
+{
+ return d->m_verbose;
+}
+
+void TcfTrkDevice::setVerbose(unsigned v)
+{
+ d->m_verbose = v;
+}
+
+void TcfTrkDevice::emitLogMessage(const QString &m)
+{
+ if (debug)
+ qWarning("%s", qPrintable(m));
+ emit logMessage(m);
+}
+
+bool TcfTrkDevice::checkOpen()
+{
+ if (d->m_device.isNull()) {
+ emitLogMessage(QLatin1String("Internal error: No device set on TcfTrkDevice."));
+ return false;
+ }
+ if (!d->m_device->isOpen()) {
+ emitLogMessage(QLatin1String("Internal error: Device not open in TcfTrkDevice."));
+ return false;
+ }
+ return true;
+}
+
+void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+ const char *commandParameters, int commandParametersLength,
+ const TcfTrkCallback &callBack,
+ const QVariant &cookie)
+
+{
+ if (!checkOpen())
+ return;
+ // Format the message
+ const int token = d->m_token++;
+ QByteArray data;
+ data.reserve(30 + commandParametersLength);
+ data.append('C');
+ data.append('\0');
+ data.append(QByteArray::number(token));
+ data.append('\0');
+ data.append(serviceName(service));
+ data.append('\0');
+ data.append(command);
+ data.append('\0');
+ if (commandParametersLength)
+ data.append(commandParameters, commandParametersLength);
+ const TcfTrkSendQueueEntry entry(mt, token, service, data, callBack, cookie);
+ d->m_sendQueue.enqueue(entry);
+ checkSendQueue();
+}
+
+void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+ const QByteArray &commandParameters,
+ const TcfTrkCallback &callBack,
+ const QVariant &cookie)
+{
+ sendTcfTrkMessage(mt, service, command, commandParameters.constData(), commandParameters.size(),
+ callBack, cookie);
+}
+
+// Enclose in message frame and write.
+void TcfTrkDevice::writeMessage(QByteArray data)
+{
+ if (!checkOpen())
+ return;
+
+ if (d->m_verbose)
+ emitLogMessage(debugMessage(data, "TCF <-"));
+
+ // Ensure \0-termination which easily gets lost in QByteArray CT.
+ if (!data.endsWith('\0'))
+ data.append('\0');
+ data += d->m_messageTerminator;
+
+ if (debug > 1)
+ qDebug("Writing:\n%s", qPrintable(formatData(data)));
+
+ d->m_device->write(data);
+ if (QAbstractSocket *as = qobject_cast<QAbstractSocket *>(d->m_device.data()))
+ as->flush();
+}
+
+void TcfTrkDevice::checkSendQueue()
+{
+ // Fire off messages or invoke noops until a message with reply is found
+ // and an entry to writtenMessages is made.
+ while (d->m_writtenMessages.empty()) {
+ if (d->m_sendQueue.isEmpty())
+ break;
+ TcfTrkSendQueueEntry entry = d->m_sendQueue.dequeue();
+ switch (entry.messageType) {
+ case MessageWithReply:
+ d->m_writtenMessages.insert(entry.token, entry);
+ writeMessage(entry.data);
+ break;
+ case MessageWithoutReply:
+ writeMessage(entry.data);
+ break;
+ case NoopMessage: // Invoke the noop-callback for synchronization
+ if (entry.callback) {
+ TcfTrkCommandResult noopResult(TcfTrkCommandResult::SuccessReply);
+ noopResult.cookie = entry.cookie;
+ entry.callback(noopResult);
+ }
+ break;
+ }
+ }
+}
+
+// Fix slashes
+static inline QString fixFileName(QString in)
+{
+ in.replace(QLatin1Char('/'), QLatin1Char('\\'));
+ return in;
+}
+
+// Start a process (consisting of a non-reply setSettings and start).
+void TcfTrkDevice::sendProcessStartCommand(const TcfTrkCallback &callBack,
+ const QString &binaryIn,
+ unsigned uid,
+ QStringList arguments,
+ QString workingDirectory,
+ bool debugControl,
+ const QStringList &additionalLibraries,
+ const QVariant &cookie)
+{
+ // Obtain the bin directory, expand by c:/sys/bin if missing
+ const QChar backSlash('\\');
+ int slashPos = binaryIn.lastIndexOf(QLatin1Char('/'));
+ if (slashPos == -1)
+ slashPos = binaryIn.lastIndexOf(backSlash);
+ const QString sysBin = QLatin1String("c:/sys/bin");
+ const QString binaryFileName = slashPos == -1 ? binaryIn : binaryIn.mid(slashPos + 1);
+ const QString binaryDirectory = slashPos == -1 ? sysBin : binaryIn.left(slashPos);
+ const QString binary = fixFileName(binaryDirectory + QLatin1Char('/') + binaryFileName);
+
+ // Fixup: Does argv[0] convention exist on Symbian?
+ arguments.push_front(binary);
+ if (workingDirectory.isEmpty())
+ workingDirectory = sysBin;
+
+ // Format settings with empty dummy parameter
+ QByteArray setData;
+ JsonInputStream setStr(setData);
+ setStr << "" << '\0'
+ << '[' << "exeToLaunch" << ',' << "addExecutables" << ',' << "addLibraries" << ']'
+ << '\0' << '['
+ << binary << ','
+ << '{' << binaryFileName << ':' << QString::number(uid, 16) << '}' << ','
+ << additionalLibraries
+ << ']';
+ sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData);
+
+ QByteArray startData;
+ JsonInputStream startStr(startData);
+ startStr << fixFileName(workingDirectory)
+ << '\0' << binary << '\0' << arguments << '\0'
+ << QStringList() << '\0' // Env is an array ["PATH=value"] (non-standard)
+ << debugControl;
+ sendTcfTrkMessage(MessageWithReply, ProcessesService, "start", startData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendProcessTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendTcfTrkMessage(MessageWithReply, ProcessesService, "terminate", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendTcfTrkMessage(MessageWithReply, RunControlService, "terminate", data, callBack, cookie);
+}
+
+// Non-standard: Remove executable from settings
+void TcfTrkDevice::sendSettingsRemoveExecutableCommand(const QString &binaryIn,
+ unsigned uid,
+ const QStringList &additionalLibraries,
+ const QVariant &cookie)
+{
+ QByteArray setData;
+ JsonInputStream setStr(setData);
+ setStr << "" << '\0'
+ << '[' << "removedExecutables" << ',' << "removedLibraries" << ']'
+ << '\0' << '['
+ << '{' << QFileInfo(binaryIn).fileName() << ':' << QString::number(uid, 16) << '}' << ','
+ << additionalLibraries
+ << ']';
+ sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData, TcfTrkCallback(), cookie);
+}
+
+void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ RunControlResumeMode mode,
+ unsigned count,
+ quint64 rangeStart,
+ quint64 rangeEnd,
+ const QVariant &cookie)
+{
+ QByteArray resumeData;
+ JsonInputStream str(resumeData);
+ str << id << '\0' << int(mode) << '\0' << count;
+ switch (mode) {
+ case RM_STEP_OVER_RANGE:
+ case RM_STEP_INTO_RANGE:
+ case RM_REVERSE_STEP_OVER_RANGE:
+ case RM_REVERSE_STEP_INTO_RANGE:
+ str << '\0' << '{' << "RANGE_START" << ':' << rangeStart
+ << ',' << "RANGE_END" << ':' << rangeEnd << '}';
+ break;
+ default:
+ break;
+ }
+ sendTcfTrkMessage(MessageWithReply, RunControlService, "resume", resumeData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlSuspendCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendTcfTrkMessage(MessageWithReply, RunControlService, "suspend", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ sendRunControlResumeCommand(callBack, id, RM_RESUME, 1, 0, 0, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsAddCommand(const TcfTrkCallback &callBack,
+ const Breakpoint &bp,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << bp;
+ sendTcfTrkMessage(MessageWithReply, BreakpointsService, "add", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ sendBreakpointsRemoveCommand(callBack, QVector<QByteArray>(1, id), cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << ids;
+ sendTcfTrkMessage(MessageWithReply, BreakpointsService, "remove", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ bool enable,
+ const QVariant &cookie)
+{
+ sendBreakpointsEnableCommand(callBack, QVector<QByteArray>(1, id), enable, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &ids,
+ bool enable,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << ids;
+ sendTcfTrkMessage(MessageWithReply, BreakpointsService,
+ enable ? "enable" : "disable",
+ data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendMemorySetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, const QByteArray& data,
+ const QVariant &cookie)
+{
+ QByteArray getData;
+ JsonInputStream str(getData);
+ // start/word size/mode. Mode should ideally be 1 (continue on error?)
+ str << contextId << '\0' << start << '\0' << 1 << '\0' << data.size() << '\0' << 1
+ << '\0' << data.toBase64();
+ sendTcfTrkMessage(MessageWithReply, MemoryService, "set", getData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendMemoryGetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, quint64 size,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ // start/word size/mode. Mode should ideally be 1 (continue on error?)
+ str << contextId << '\0' << start << '\0' << 1 << '\0' << size << '\0' << 1;
+ sendTcfTrkMessage(MessageWithReply, MemoryService, "get", data, callBack, cookie);
+}
+
+QByteArray TcfTrkDevice::parseMemoryGet(const TcfTrkCommandResult &r)
+{
+ if (r.type != TcfTrkCommandResult::SuccessReply || r.values.size() < 1)
+ return QByteArray();
+ const JsonValue &memoryV = r.values.front();
+
+ if (memoryV.type() != JsonValue::String || memoryV.data().size() < 2
+ || !memoryV.data().endsWith('='))
+ return QByteArray();
+ // Catch errors reported as hash:
+ // R.4."TlVMTA==".{"Time":1276786871255,"Code":1,"AltCode":-38,"AltOrg":"POSIX","Format":"BadDescriptor"}
+ // Not sure what to make of it.
+ if (r.values.size() >= 2 && r.values.at(1).type() == JsonValue::Object)
+ qWarning("Error retrieving memory: %s", r.values.at(1).toString(false).constData());
+ // decode
+ const QByteArray memory = QByteArray::fromBase64(memoryV.data());
+ if (memory.isEmpty())
+ qWarning("Base64 decoding of %s failed.", memoryV.data().constData());
+ if (debug)
+ qDebug("TcfTrkDevice::parseMemoryGet: received %d bytes", memory.size());
+ return memory;
+}
+
+void TcfTrkDevice::sendRegistersGetMCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie)
+{
+ // TODO: use "Registers" (which uses base64-encoded values)
+ QByteArray data;
+ JsonInputStream str(data);
+ str << contextId << '\0' << ids;
+ sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "get", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned start, unsigned count)
+{
+ const unsigned end = start + count;
+ if (end > (unsigned)d->m_registerNames.size()) {
+ qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", end, d->m_registerNames.size());
+ return;
+ }
+
+ QVector<QByteArray> ids;
+ ids.reserve(count);
+ for (unsigned i = start; i < end; i++)
+ ids.push_back(d->m_registerNames.at(i));
+ sendRegistersGetMCommand(callBack, contextId, ids, QVariant(start));
+}
+
+// Set register
+void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QByteArray &id,
+ unsigned value,
+ const QVariant &cookie)
+{
+ // TODO: use "Registers" (which uses base64-encoded values)
+ QByteArray data;
+ JsonInputStream str(data);
+ str << contextId << '\0' << QVector<QByteArray>(1, id)
+ << '\0' << QVector<QByteArray>(1, QByteArray::number(value, 16));
+ sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "set", data, callBack, cookie);
+}
+
+// Set register
+void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned registerNumber,
+ unsigned value,
+ const QVariant &cookie)
+{
+ if (registerNumber >= (unsigned)d->m_registerNames.size()) {
+ qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", registerNumber, d->m_registerNames.size());
+ return;
+ }
+ sendRegistersSetCommand(callBack, contextId,
+ d->m_registerNames[registerNumber],
+ value, cookie);
+}
+
+} // namespace tcftrk
diff --git a/tools/runonphone/symbianutils/tcftrkdevice.h b/tools/runonphone/symbianutils/tcftrkdevice.h
new file mode 100644
index 0000000..f56a86e
--- /dev/null
+++ b/tools/runonphone/symbianutils/tcftrkdevice.h
@@ -0,0 +1,295 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TCFTRKENGINE_H
+#define TCFTRKENGINE_H
+
+#include "symbianutils_global.h"
+#include "tcftrkmessage.h"
+#include "callback.h"
+#include "json.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QVector>
+#include <QtCore/QVariant>
+#include <QtCore/QStringList>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace tcftrk {
+
+struct TcfTrkDevicePrivate;
+struct Breakpoint;
+
+/* Command error handling in TCF:
+ * 1) 'Severe' errors (JSON format, parameter format): Trk emits a
+ * nonstandard message (\3\2 error parameters) and closes the connection.
+ * 2) Protocol errors: 'N' without error message is returned.
+ * 3) Errors in command execution: 'R' with a TCF error hash is returned
+ * (see TcfTrkCommandError). */
+
+/* Error code return in 'R' reply to command
+ * (see top of 'Services' documentation). */
+struct SYMBIANUTILS_EXPORT TcfTrkCommandError {
+ TcfTrkCommandError();
+ void clear();
+ operator bool() const { return timeMS != 0; }
+ QString toString() const;
+ void write(QTextStream &str) const;
+ bool parse(const QVector<JsonValue> &values);
+
+ quint64 timeMS; // Since 1.1.1970
+ int code;
+ QByteArray format; // message
+ // 'Alternative' meaning, like altOrg="POSIX"/altCode=<some errno>
+ QByteArray alternativeOrganization;
+ int alternativeCode;
+};
+
+/* Answer to a Tcf command passed to the callback. */
+struct SYMBIANUTILS_EXPORT TcfTrkCommandResult {
+ enum Type {
+ SuccessReply, // 'R' and no error -> all happy.
+ CommandErrorReply, // 'R' with TcfTrkCommandError received
+ ProgressReply, // 'P', progress indicator
+ FailReply // 'N' Protocol NAK, severe error
+ };
+
+ explicit TcfTrkCommandResult(Type t = SuccessReply);
+ explicit TcfTrkCommandResult(char typeChar, Services service,
+ const QByteArray &request,
+ const QVector<JsonValue> &values,
+ const QVariant &cookie);
+
+ QString toString() const;
+ QString errorString() const;
+ operator bool() const { return type == SuccessReply || type == ProgressReply; }
+
+ Type type;
+ Services service;
+ QByteArray request;
+ TcfTrkCommandError commandError;
+ QVector<JsonValue> values;
+ QVariant cookie;
+};
+
+typedef trk::Callback<const TcfTrkCommandResult &> TcfTrkCallback;
+
+/* TcfTrkDevice: TCF communication helper using an asynchronous QIODevice
+ * implementing the TCF protocol according to:
+http://dev.eclipse.org/svnroot/dsdp/org.eclipse.tm.tcf/trunk/docs/TCF%20Specification.html
+http://dev.eclipse.org/svnroot/dsdp/org.eclipse.tm.tcf/trunk/docs/TCF%20Services.html
+ * Commands can be sent along with callbacks that are passed a
+ * TcfTrkCommandResult and an opaque QVariant cookie. In addition, events are emitted.
+*/
+
+class SYMBIANUTILS_EXPORT TcfTrkDevice : public QObject
+{
+ Q_PROPERTY(unsigned verbose READ verbose WRITE setVerbose)
+ Q_OBJECT
+public:
+ enum MessageType { MessageWithReply,
+ MessageWithoutReply, /* Non-standard: "Settings:set" command does not reply */
+ NoopMessage };
+
+ typedef QSharedPointer<QIODevice> IODevicePtr;
+
+ explicit TcfTrkDevice(QObject *parent = 0);
+ virtual ~TcfTrkDevice();
+
+ unsigned verbose() const;
+
+ // Mapping of register names for indices
+ QVector<QByteArray> registerNames() const;
+ void setRegisterNames(const QVector<QByteArray>& n);
+
+ IODevicePtr device() const;
+ IODevicePtr takeDevice();
+ void setDevice(const IODevicePtr &dp);
+
+ void sendTcfTrkMessage(MessageType mt, Services service,
+ const char *command,
+ const char *commandParameters, int commandParametersLength,
+ const TcfTrkCallback &callBack = TcfTrkCallback(),
+ const QVariant &cookie = QVariant());
+
+ void sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+ const QByteArray &commandParameters,
+ const TcfTrkCallback &callBack = TcfTrkCallback(),
+ const QVariant &cookie = QVariant());
+
+ // Convenience messages: Start a process
+ void sendProcessStartCommand(const TcfTrkCallback &callBack,
+ const QString &binary,
+ unsigned uid,
+ QStringList arguments = QStringList(),
+ QString workingDirectory = QString(),
+ bool debugControl = true,
+ const QStringList &additionalLibraries = QStringList(),
+ const QVariant &cookie = QVariant());
+
+ // Preferred over Processes:Terminate by TCF TRK.
+ void sendRunControlTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendProcessTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ // Non-standard: Remove executable from settings.
+ // Probably needs to be called after stopping. This command has no response.
+ void sendSettingsRemoveExecutableCommand(const QString &binaryIn,
+ unsigned uid,
+ const QStringList &additionalLibraries = QStringList(),
+ const QVariant &cookie = QVariant());
+
+ void sendRunControlSuspendCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ // Resume / Step (see RunControlResumeMode).
+ void sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ RunControlResumeMode mode,
+ unsigned count /* = 1, currently ignored. */,
+ quint64 rangeStart, quint64 rangeEnd,
+ const QVariant &cookie = QVariant());
+
+ // Convenience to resume a suspended process
+ void sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsAddCommand(const TcfTrkCallback &callBack,
+ const Breakpoint &b,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ bool enable,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &id,
+ bool enable,
+ const QVariant &cookie = QVariant());
+
+
+ void sendMemoryGetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, quint64 size,
+ const QVariant &cookie = QVariant());
+
+ void sendMemorySetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, const QByteArray& data,
+ const QVariant &cookie = QVariant());
+
+ // Reply is an array of hexvalues
+ void sendRegistersGetMCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie = QVariant());
+
+ // Convenience to get a range of register "R0" .. "R<n>".
+ // Cookie will be an int containing "start".
+ void sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned start, unsigned count);
+
+ // Set register
+ void sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QByteArray &ids,
+ unsigned value,
+ const QVariant &cookie = QVariant());
+ // Set register
+ void sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned registerNumber,
+ unsigned value,
+ const QVariant &cookie = QVariant());
+
+ static QByteArray parseMemoryGet(const TcfTrkCommandResult &r);
+
+signals:
+ void genericTcfEvent(int service, const QByteArray &name, const QVector<tcftrk::JsonValue> &value);
+ void tcfEvent(const tcftrk::TcfTrkEvent &knownEvent);
+
+ void logMessage(const QString &);
+ void error(const QString &);
+
+public slots:
+ void setVerbose(unsigned v);
+
+private slots:
+ void slotDeviceError();
+ void slotDeviceSocketStateChanged();
+ void slotDeviceReadyRead();
+
+private:
+ bool checkOpen();
+ void checkSendQueue();
+ void writeMessage(QByteArray data);
+ void emitLogMessage(const QString &);
+ int parseMessage(const QByteArray &);
+ int parseTcfCommandReply(char type, const QVector<QByteArray> &tokens);
+ int parseTcfEvent(const QVector<QByteArray> &tokens);
+
+ TcfTrkDevicePrivate *d;
+};
+
+} // namespace tcftrk
+
+#endif // TCFTRKENGINE_H
diff --git a/tools/runonphone/symbianutils/tcftrkmessage.cpp b/tools/runonphone/symbianutils/tcftrkmessage.cpp
new file mode 100644
index 0000000..06035ab
--- /dev/null
+++ b/tools/runonphone/symbianutils/tcftrkmessage.cpp
@@ -0,0 +1,562 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "tcftrkmessage.h"
+#include "json.h"
+
+#include <QtCore/QString>
+#include <QtCore/QTextStream>
+
+// Names matching the enum
+static const char *serviceNamesC[] =
+{ "Locator", "RunControl", "Processes", "Memory", "Settings", "Breakpoints",
+ "Registers", "SimpleRegisters",
+ "UnknownService"};
+
+namespace tcftrk {
+
+SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep)
+{
+ QString rc;
+ const int count = a.size();
+ for (int i = 0; i < count; i++) {
+ if (i)
+ rc += QLatin1Char(sep);
+ rc += QString::fromUtf8(a.at(i));
+ }
+ return rc;
+}
+
+static inline bool jsonToBool(const JsonValue& js)
+{
+ return js.type() == JsonValue::Boolean && js.data() == "true";
+}
+
+SYMBIANUTILS_EXPORT const char *serviceName(Services s)
+{
+ return serviceNamesC[s];
+}
+
+SYMBIANUTILS_EXPORT Services serviceFromName(const char *n)
+{
+ const int count = sizeof(serviceNamesC)/sizeof(char *);
+ for (int i = 0; i < count; i++)
+ if (!qstrcmp(serviceNamesC[i], n))
+ return static_cast<Services>(i);
+ return UnknownService;
+}
+
+SYMBIANUTILS_EXPORT QString formatData(const QByteArray &a)
+{
+ const int columns = 16;
+ QString rc;
+ QTextStream str(&rc);
+ str.setIntegerBase(16);
+ str.setPadChar(QLatin1Char('0'));
+ const unsigned char *start = reinterpret_cast<const unsigned char *>(a.constData());
+ const unsigned char *end = start + a.size();
+ for (const unsigned char *p = start; p < end ; ) {
+ str << "0x";
+ str.setFieldWidth(4);
+ str << (p - start);
+ str.setFieldWidth(0);
+ str << ' ';
+ QString asc;
+ int c = 0;
+ for ( ; c < columns && p < end; c++, p++) {
+ const unsigned u = *p;
+ str.setFieldWidth(2);
+ str << u;
+ str.setFieldWidth(0);
+ str << ' ';
+ switch (u) {
+ case '\n':
+ asc += QLatin1String("\\n");
+ break;
+ case '\r':
+ asc += QLatin1String("\\r");
+ break;
+ case '\t':
+ asc += QLatin1String("\\t");
+ break;
+ default:
+ if (u >= 32 && u < 128) {
+ asc += QLatin1Char(' ');
+ asc += QLatin1Char(u);
+ } else {
+ asc += QLatin1String(" .");
+ }
+ break;
+ }
+ }
+ if (const int remainder = columns - c)
+ str << QString(3 * remainder, QLatin1Char(' '));
+ str << ' ' << asc << '\n';
+ }
+ return rc;
+}
+
+// ----------- RunControlContext
+RunControlContext::RunControlContext() :
+ flags(0), resumeFlags(0)
+{
+}
+
+void RunControlContext::clear()
+{
+ flags =0;
+ resumeFlags = 0;
+ id.clear();
+ osid.clear();
+ parentId.clear();
+}
+
+RunControlContext::Type RunControlContext::typeFromTcfId(const QByteArray &id)
+{
+ // "p12" or "p12.t34"?
+ return id.contains(".t") ? Thread : Process;
+}
+
+unsigned RunControlContext::processId() const
+{
+ return processIdFromTcdfId(id);
+}
+
+unsigned RunControlContext::threadId() const
+{
+ return threadIdFromTcdfId(id);
+}
+
+unsigned RunControlContext::processIdFromTcdfId(const QByteArray &id)
+{
+ // Cut out process id from "p12" or "p12.t34"?
+ if (!id.startsWith('p'))
+ return 0;
+ const int dotPos = id.indexOf('.');
+ const int pLen = dotPos == -1 ? id.size() : dotPos;
+ return id.mid(1, pLen - 1).toUInt();
+}
+
+unsigned RunControlContext::threadIdFromTcdfId(const QByteArray &id)
+{
+ const int tPos = id.indexOf(".t");
+ return tPos != -1 ? id.mid(tPos + 2).toUInt() : uint(0);
+}
+
+QByteArray RunControlContext::tcfId(unsigned processId, unsigned threadId /* = 0 */)
+{
+ QByteArray rc("p");
+ rc += QByteArray::number(processId);
+ if (threadId) {
+ rc += ".t";
+ rc += QByteArray::number(threadId);
+ }
+ return rc;
+}
+
+RunControlContext::Type RunControlContext::type() const
+{
+ return RunControlContext::typeFromTcfId(id);
+}
+
+bool RunControlContext::parse(const JsonValue &val)
+{
+ clear();
+ if (val.type() != JsonValue::Object)
+ return false;
+ foreach(const JsonValue &c, val.children()) {
+ if (c.name() == "ID") {
+ id = c.data();
+ } else if (c.name() == "OSID") {
+ osid = c.data();
+ } else if (c.name() == "ParentID") {
+ parentId = c.data();
+ } else if (c.name() == "IsContainer") {
+ if (jsonToBool(c))
+ flags |= Container;
+ } else if (c.name() == "CanTerminate") {
+ if (jsonToBool(c))
+ flags |= CanTerminate;
+ } else if (c.name() == "CanResume") {
+ resumeFlags = c.data().toUInt();
+ } else if (c.name() == "HasState") {
+ if (jsonToBool(c))
+ flags |= HasState;
+ } else if (c.name() == "CanSuspend") {
+ if (jsonToBool(c))
+ flags |= CanSuspend;
+ }
+ }
+ return true;
+}
+
+QString RunControlContext::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ format(str);
+ return rc;
+}
+
+void RunControlContext::format(QTextStream &str) const
+{
+ str << " id='" << id << "' osid='" << osid
+ << "' parentId='" << parentId <<"' ";
+ if (flags & Container)
+ str << "[container] ";
+ if (flags & HasState)
+ str << "[has state] ";
+ if (flags & CanSuspend)
+ str << "[can suspend] ";
+ if (flags & CanSuspend)
+ str << "[can terminate] ";
+ str.setIntegerBase(16);
+ str << " resume_flags: 0x" << resumeFlags;
+ str.setIntegerBase(10);
+}
+
+// ------ ModuleLoadEventInfo
+ModuleLoadEventInfo::ModuleLoadEventInfo() :
+ loaded(false), codeAddress(0), dataAddress(0), requireResume(false)
+{
+}
+
+void ModuleLoadEventInfo::clear()
+{
+ loaded = requireResume = false;
+ codeAddress = dataAddress =0;
+}
+
+bool ModuleLoadEventInfo::parse(const JsonValue &val)
+{
+ clear();
+ if (val.type() != JsonValue::Object)
+ return false;
+ foreach(const JsonValue &c, val.children()) {
+ if (c.name() == "Name") {
+ name = c.data();
+ } else if (c.name() == "File") {
+ file = c.data();
+ } else if (c.name() == "CodeAddress") {
+ codeAddress = c.data().toULongLong();
+ } else if (c.name() == "DataAddress") {
+ dataAddress = c.data().toULongLong();
+ } else if (c.name() == "Loaded") {
+ loaded = jsonToBool(c);
+ } else if (c.name() == "RequireResume") {
+ requireResume =jsonToBool(c);
+ }
+ }
+ return true;
+}
+void ModuleLoadEventInfo::format(QTextStream &str) const
+{
+ str << "name='" << name << "' file='" << file << "' " <<
+ (loaded ? "[loaded] " : "[not loaded] ");
+ if (requireResume)
+ str << "[requires resume] ";
+ str.setIntegerBase(16);
+ str << " code: 0x" << codeAddress << " data: 0x" << dataAddress;
+ str.setIntegerBase(10);
+}
+
+// ---------------------- Breakpoint
+
+// Types matching enum
+static const char *breakPointTypesC[] = {"Software", "Hardware", "Auto"};
+
+Breakpoint::Breakpoint(quint64 loc) :
+ type(Auto), enabled(true), ignoreCount(0), location(loc), size(1), thumb(true)
+{
+ if (loc)
+ id = idFromLocation(location);
+}
+
+void Breakpoint::setContextId(unsigned processId, unsigned threadId)
+{
+ contextIds = QVector<QByteArray>(1, RunControlContext::tcfId(processId, threadId));
+}
+
+QByteArray Breakpoint::idFromLocation(quint64 loc)
+{
+ return QByteArray("BP_0x") + QByteArray::number(loc, 16);
+}
+
+QString Breakpoint::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str.setIntegerBase(16);
+ str << "Breakpoint '" << id << "' " << breakPointTypesC[type] << " for contexts '"
+ << joinByteArrays(contextIds, ',') << "' at 0x" << location;
+ str.setIntegerBase(10);
+ str << " size " << size;
+ if (enabled)
+ str << " [enabled]";
+ if (thumb)
+ str << " [thumb]";
+ if (ignoreCount)
+ str << " IgnoreCount " << ignoreCount;
+ return rc;
+}
+
+JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b)
+{
+ if (b.contextIds.isEmpty())
+ qWarning("tcftrk::Breakpoint: No context ids specified");
+
+ str << '{' << "ID" << ':' << QString::fromUtf8(b.id) << ','
+ << "BreakpointType" << ':' << breakPointTypesC[b.type] << ','
+ << "Enabled" << ':' << b.enabled << ','
+ << "IgnoreCount" << ':' << b.ignoreCount << ','
+ << "ContextIds" << ':' << b.contextIds << ','
+ << "Location" << ':' << QString::number(b.location) << ','
+ << "Size" << ':' << b.size << ','
+ << "THUMB_BREAKPOINT" << ':' << b.thumb
+ << '}';
+ return str;
+}
+
+// --- Events
+TcfTrkEvent::TcfTrkEvent(Type type) : m_type(type)
+{
+}
+
+TcfTrkEvent::~TcfTrkEvent()
+{
+}
+
+TcfTrkEvent::Type TcfTrkEvent::type() const
+{
+ return m_type;
+}
+
+QString TcfTrkEvent::toString() const
+{
+ return QString();
+}
+
+static const char sharedLibrarySuspendReasonC[] = "Shared Library";
+
+TcfTrkEvent *TcfTrkEvent::parseEvent(Services s, const QByteArray &nameBA, const QVector<JsonValue> &values)
+{
+ switch (s) {
+ case LocatorService:
+ if (nameBA == "Hello" && values.size() == 1 && values.front().type() == JsonValue::Array) {
+ QStringList services;
+ foreach (const JsonValue &jv, values.front().children())
+ services.push_back(QString::fromUtf8(jv.data()));
+ return new TcfTrkLocatorHelloEvent(services);
+ }
+ break;
+ case RunControlService:
+ if (values.empty())
+ return 0;
+ // "id/PC/Reason/Data"
+ if (nameBA == "contextSuspended" && values.size() == 4) {
+ const QByteArray idBA = values.at(0).data();
+ const quint64 pc = values.at(1).data().toULongLong();
+ const QByteArray reasonBA = values.at(2).data();
+ // Module load: Special
+ if (reasonBA == sharedLibrarySuspendReasonC) {
+ ModuleLoadEventInfo info;
+ if (!info.parse(values.at(3)))
+ return 0;
+ return new TcfTrkRunControlModuleLoadContextSuspendedEvent(idBA, reasonBA, pc, info);
+ }
+ return new TcfTrkRunControlContextSuspendedEvent(idBA, reasonBA, pc);
+ } // "contextSuspended"
+ if (nameBA == "contextAdded")
+ return TcfTrkRunControlContextAddedEvent::parseEvent(values);
+ if (nameBA == "contextRemoved" && values.front().type() == JsonValue::Array) {
+ QVector<QByteArray> ids;
+ foreach(const JsonValue &c, values.front().children())
+ ids.push_back(c.data());
+ return new TcfTrkRunControlContextRemovedEvent(ids);
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+// -------------- TcfTrkServiceHelloEvent
+TcfTrkLocatorHelloEvent::TcfTrkLocatorHelloEvent(const QStringList &s) :
+ TcfTrkEvent(LocatorHello),
+ m_services(s)
+{
+}
+
+QString TcfTrkLocatorHelloEvent::toString() const
+{
+ return QLatin1String("ServiceHello: ") + m_services.join(QLatin1String(", "));
+}
+
+// -------------- TcfTrkIdEvent
+TcfTrkIdEvent::TcfTrkIdEvent(Type t, const QByteArray &id) :
+ TcfTrkEvent(t), m_id(id)
+{
+}
+
+// ---------- TcfTrkIdsEvent
+TcfTrkIdsEvent::TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids) :
+ TcfTrkEvent(t), m_ids(ids)
+{
+}
+
+QString TcfTrkIdsEvent::joinedIdString(const char sep) const
+{
+ return joinByteArrays(m_ids, sep);
+}
+
+// ---------------- TcfTrkRunControlContextAddedEvent
+TcfTrkRunControlContextAddedEvent::TcfTrkRunControlContextAddedEvent(const RunControlContexts &c) :
+ TcfTrkEvent(RunControlContextAdded), m_contexts(c)
+{
+}
+
+TcfTrkRunControlContextAddedEvent
+ *TcfTrkRunControlContextAddedEvent::parseEvent(const QVector<JsonValue> &values)
+{
+ // Parse array of contexts
+ if (values.size() < 1 || values.front().type() != JsonValue::Array)
+ return 0;
+
+ RunControlContexts contexts;
+ foreach (const JsonValue &v, values.front().children()) {
+ RunControlContext context;
+ if (context.parse(v))
+ contexts.push_back(context);
+ }
+ return new TcfTrkRunControlContextAddedEvent(contexts);
+}
+
+QString TcfTrkRunControlContextAddedEvent::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << "RunControl: " << m_contexts.size() << " context(s) "
+ << (type() == RunControlContextAdded ? "added" : "removed")
+ << '\n';
+ foreach (const RunControlContext &c, m_contexts) {
+ c.format(str);
+ str << '\n';
+ }
+ return rc;
+}
+
+// --------------- TcfTrkRunControlContextRemovedEvent
+TcfTrkRunControlContextRemovedEvent::TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &ids) :
+ TcfTrkIdsEvent(RunControlContextRemoved, ids)
+{
+}
+
+QString TcfTrkRunControlContextRemovedEvent::toString() const
+{
+ return QLatin1String("RunControl: Removed contexts '") + joinedIdString() + ("'.");
+}
+
+// --------------- TcfTrkRunControlContextSuspendedEvent
+TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc) :
+ TcfTrkIdEvent(RunControlSuspended, id), m_pc(pc), m_reason(reason)
+{
+}
+
+TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(Type t,
+ const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc) :
+ TcfTrkIdEvent(t, id), m_pc(pc), m_reason(reason)
+{
+}
+
+void TcfTrkRunControlContextSuspendedEvent::format(QTextStream &str) const
+{
+ str.setIntegerBase(16);
+ str << "RunControl: '" << idString() << "' suspended at 0x"
+ << m_pc << ": '" << m_reason << "'.";
+ str.setIntegerBase(10);
+}
+
+QString TcfTrkRunControlContextSuspendedEvent::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ format(str);
+ return rc;
+}
+
+TcfTrkRunControlContextSuspendedEvent::Reason TcfTrkRunControlContextSuspendedEvent::reason() const
+{
+ if (m_reason == sharedLibrarySuspendReasonC)
+ return ModuleLoad;
+ if (m_reason == "Breakpoint")
+ return BreakPoint;
+ // 'Data abort exception'/'Thread has panicked' ... unfortunately somewhat unspecific.
+ if (m_reason.contains("exception") || m_reason.contains("panick"))
+ return Crash;
+ return Other;
+}
+
+TcfTrkRunControlModuleLoadContextSuspendedEvent::TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc,
+ const ModuleLoadEventInfo &mi) :
+ TcfTrkRunControlContextSuspendedEvent(RunControlModuleLoadSuspended, id, reason, pc),
+ m_mi(mi)
+{
+}
+
+QString TcfTrkRunControlModuleLoadContextSuspendedEvent::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ TcfTrkRunControlContextSuspendedEvent::format(str);
+ str << ' ';
+ m_mi.format(str);
+ return rc;
+}
+
+
+} // namespace tcftrk
diff --git a/tools/runonphone/symbianutils/tcftrkmessage.h b/tools/runonphone/symbianutils/tcftrkmessage.h
new file mode 100644
index 0000000..929a9e6
--- /dev/null
+++ b/tools/runonphone/symbianutils/tcftrkmessage.h
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TRCFTRKMESSAGE_H
+#define TRCFTRKMESSAGE_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+QT_BEGIN_NAMESPACE
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace tcftrk {
+
+class JsonValue;
+class JsonInputStream;
+
+enum Services {
+ LocatorService,
+ RunControlService,
+ ProcessesService,
+ MemoryService,
+ SettingsService, // non-standard, trk specific
+ BreakpointsService,
+ RegistersService,
+ SimpleRegistersService, // non-standard, trk specific
+ UnknownService
+}; // Note: Check string array 'serviceNamesC' of same size when modifying this.
+
+// Modes of RunControl/'Resume' (see EDF documentation).
+// As of 24.6.2010, RM_RESUME, RM_STEP_OVER, RM_STEP_INTO,
+// RM_STEP_OVER_RANGE, RM_STEP_INTO_RANGE are supported with
+// RANG_START/RANGE_END parameters.
+enum RunControlResumeMode {
+ RM_RESUME = 0,
+ RM_STEP_OVER = 1, RM_STEP_INTO = 2,
+ RM_STEP_OVER_LINE = 3, RM_STEP_INTO_LINE = 4,
+ RM_STEP_OUT = 5, RM_REVERSE_RESUME = 6,
+ RM_REVERSE_STEP_OVER = 7, RM_REVERSE_STEP_INTO = 8,
+ RM_REVERSE_STEP_OVER_LINE = 9, RM_REVERSE_STEP_INTO_LINE = 10,
+ RM_REVERSE_STEP_OUT = 11, RM_STEP_OVER_RANGE = 12,
+ RM_STEP_INTO_RANGE = 13, RM_REVERSE_STEP_OVER_RANGE = 14,
+ RM_REVERSE_STEP_INTO_RANGE = 15
+};
+
+SYMBIANUTILS_EXPORT const char *serviceName(Services s);
+SYMBIANUTILS_EXPORT Services serviceFromName(const char *);
+
+// Debug helpers
+SYMBIANUTILS_EXPORT QString formatData(const QByteArray &a);
+SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep = ',');
+
+// Context used in 'RunControl contextAdded' events and in reply
+// to 'Processes start'. Could be thread or process.
+struct SYMBIANUTILS_EXPORT RunControlContext {
+ enum Flags {
+ Container = 0x1, HasState = 0x2, CanSuspend = 0x4,
+ CanTerminate = 0x8
+ };
+ enum Type { Process, Thread };
+
+ RunControlContext();
+ Type type() const;
+ unsigned processId() const;
+ unsigned threadId() const;
+
+ void clear();
+ bool parse(const JsonValue &v);
+ void format(QTextStream &str) const;
+ QString toString() const;
+
+ // Helper for converting the TCF ids ("p12" or "p12.t34")
+ static Type typeFromTcfId(const QByteArray &id);
+ static unsigned processIdFromTcdfId(const QByteArray &id);
+ static unsigned threadIdFromTcdfId(const QByteArray &id);
+ static QByteArray tcfId(unsigned processId, unsigned threadId = 0);
+
+ unsigned flags;
+ unsigned resumeFlags;
+ QByteArray id; // "p434.t699"
+ QByteArray osid; // Non-standard: Process or thread id
+ QByteArray parentId; // Parent process id of a thread.
+};
+
+// Module load information occurring with 'RunControl contextSuspended' events
+struct SYMBIANUTILS_EXPORT ModuleLoadEventInfo {
+ ModuleLoadEventInfo();
+ void clear();
+ bool parse(const JsonValue &v);
+ void format(QTextStream &str) const;
+
+ QByteArray name;
+ QByteArray file;
+ bool loaded;
+ quint64 codeAddress;
+ quint64 dataAddress;
+ bool requireResume;
+};
+
+// Breakpoint as supported by TcfTrk source June 2010
+// TODO: Add watchpoints,etc once they are implemented
+struct SYMBIANUTILS_EXPORT Breakpoint {
+ enum Type { Software, Hardware, Auto };
+
+ explicit Breakpoint(quint64 loc = 0);
+ void setContextId(unsigned processId, unsigned threadId = 0);
+ QString toString() const;
+
+ static QByteArray idFromLocation(quint64 loc); // Automagically determine from location
+
+ Type type;
+ bool enabled;
+ int ignoreCount;
+ QVector<QByteArray> contextIds; // Process or thread ids.
+ QByteArray id; // Id of the breakpoint;
+ quint64 location;
+ unsigned size;
+ bool thumb;
+};
+
+SYMBIANUTILS_EXPORT JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b);
+
+// Event hierarchy
+class SYMBIANUTILS_EXPORT TcfTrkEvent {
+ Q_DISABLE_COPY(TcfTrkEvent)
+public:
+ enum Type { None,
+ LocatorHello,
+ RunControlContextAdded,
+ RunControlContextRemoved,
+ RunControlSuspended,
+ RunControlBreakpointSuspended,
+ RunControlModuleLoadSuspended,
+ RunControlResumed
+ };
+
+ virtual ~TcfTrkEvent();
+
+ Type type() const;
+ virtual QString toString() const;
+
+ static TcfTrkEvent *parseEvent(Services s, const QByteArray &name, const QVector<JsonValue> &val);
+
+protected:
+ explicit TcfTrkEvent(Type type = None);
+
+private:
+ const Type m_type;
+};
+
+// ServiceHello
+class SYMBIANUTILS_EXPORT TcfTrkLocatorHelloEvent : public TcfTrkEvent {
+public:
+ explicit TcfTrkLocatorHelloEvent(const QStringList &);
+
+ const QStringList &services() { return m_services; }
+ virtual QString toString() const;
+
+private:
+ QStringList m_services;
+};
+
+// Base for events that just have one id as parameter
+// (simple suspend)
+class SYMBIANUTILS_EXPORT TcfTrkIdEvent : public TcfTrkEvent {
+protected:
+ explicit TcfTrkIdEvent(Type t, const QByteArray &id);
+public:
+ QByteArray id() const { return m_id; }
+ QString idString() const { return QString::fromUtf8(m_id); }
+
+private:
+ const QByteArray m_id;
+};
+
+// Base for events that just have some ids as parameter
+// (context removed)
+class SYMBIANUTILS_EXPORT TcfTrkIdsEvent : public TcfTrkEvent {
+protected:
+ explicit TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids);
+
+public:
+ QVector<QByteArray> ids() const { return m_ids; }
+ QString joinedIdString(const char sep = ',') const;
+
+private:
+ const QVector<QByteArray> m_ids;
+};
+
+// RunControlContextAdded
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextAddedEvent : public TcfTrkEvent {
+public:
+ typedef QVector<RunControlContext> RunControlContexts;
+
+ explicit TcfTrkRunControlContextAddedEvent(const RunControlContexts &c);
+
+ const RunControlContexts &contexts() const { return m_contexts; }
+ virtual QString toString() const;
+
+ static TcfTrkRunControlContextAddedEvent *parseEvent(const QVector<JsonValue> &val);
+
+private:
+ const RunControlContexts m_contexts;
+};
+
+// RunControlContextRemoved
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextRemovedEvent : public TcfTrkIdsEvent {
+public:
+ explicit TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &id);
+ virtual QString toString() const;
+};
+
+// Simple RunControlContextSuspended (process/thread)
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextSuspendedEvent : public TcfTrkIdEvent {
+public:
+ enum Reason { BreakPoint, ModuleLoad, Crash, Other } ;
+
+ explicit TcfTrkRunControlContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc = 0);
+ virtual QString toString() const;
+
+ quint64 pc() const { return m_pc; }
+ QByteArray reasonID() const { return m_reason; }
+ Reason reason() const;
+
+protected:
+ explicit TcfTrkRunControlContextSuspendedEvent(Type t,
+ const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc = 0);
+ void format(QTextStream &str) const;
+
+private:
+ const quint64 m_pc;
+ const QByteArray m_reason;
+};
+
+// RunControlContextSuspended due to module load
+class SYMBIANUTILS_EXPORT TcfTrkRunControlModuleLoadContextSuspendedEvent : public TcfTrkRunControlContextSuspendedEvent {
+public:
+ explicit TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc,
+ const ModuleLoadEventInfo &mi);
+
+ virtual QString toString() const;
+ const ModuleLoadEventInfo &info() const { return m_mi; }
+
+private:
+ const ModuleLoadEventInfo m_mi;
+};
+
+} // namespace tcftrk
+#endif // TRCFTRKMESSAGE_H
diff --git a/tools/runonphone/symbianutils/trkdevice.cpp b/tools/runonphone/symbianutils/trkdevice.cpp
index b327ab3..9039184 100644
--- a/tools/runonphone/symbianutils/trkdevice.cpp
+++ b/tools/runonphone/symbianutils/trkdevice.cpp
@@ -52,6 +52,7 @@
#include <QtCore/QMutex>
#include <QtCore/QWaitCondition>
#include <QtCore/QSharedPointer>
+#include <QtCore/QScopedPointer>
#include <QtCore/QMetaType>
#ifdef Q_OS_WIN
@@ -102,6 +103,11 @@ QString winErrorMessage(unsigned long error)
enum { verboseTrk = 0 };
+static inline QString msgAccessingClosedDevice(const QString &msg)
+{
+ return QString::fromLatin1("Error: Attempt to access device '%1', which is closed.").arg(msg);
+}
+
namespace trk {
///////////////////////////////////////////////////////////////////////
@@ -155,10 +161,11 @@ namespace trk {
///////////////////////////////////////////////////////////////////////
class TrkWriteQueue
-{
+{
Q_DISABLE_COPY(TrkWriteQueue)
public:
explicit TrkWriteQueue();
+ void clear();
// Enqueue messages.
void queueTrkMessage(byte code, TrkCallback callback,
@@ -208,13 +215,24 @@ TrkWriteQueue::TrkWriteQueue() :
{
}
+void TrkWriteQueue::clear()
+{
+ m_trkWriteToken = 0;
+ m_trkWriteBusy = false;
+ m_trkWriteQueue.clear();
+ const int discarded = m_writtenTrkMessages.size();
+ m_writtenTrkMessages.clear();
+ if (verboseTrk)
+ qDebug() << "TrkWriteQueue::clear: discarded " << discarded;
+}
+
byte TrkWriteQueue::nextTrkWriteToken()
{
++m_trkWriteToken;
if (m_trkWriteToken == 0)
++m_trkWriteToken;
if (verboseTrk)
- qDebug() << "Write token: " << m_trkWriteToken;
+ qDebug() << "nextTrkWriteToken:" << m_trkWriteToken;
return m_trkWriteToken;
}
@@ -349,7 +367,7 @@ class WriterThread : public QThread
{
Q_OBJECT
Q_DISABLE_COPY(WriterThread)
-public:
+public:
explicit WriterThread(const QSharedPointer<DeviceContext> &context);
// Enqueue messages.
@@ -357,6 +375,8 @@ public:
const QByteArray &data, const QVariant &cookie);
void queueTrkInitialPing();
+ void clearWriteQueue();
+
// Call this from the device read notification with the results.
void slotHandleResult(const TrkResult &result);
@@ -374,7 +394,7 @@ public slots:
private slots:
void invokeNoopMessage(const trk::TrkMessage &);
-private:
+private:
bool write(const QByteArray &data, QString *errorMessage);
inline int writePendingMessage();
@@ -462,6 +482,7 @@ void WriterThread::terminate()
m_waitCondition.wakeAll();
wait();
m_terminate = false;
+ m_queue.clear();
}
#ifdef Q_OS_WIN
@@ -561,6 +582,13 @@ void WriterThread::queueTrkMessage(byte code, TrkCallback callback,
tryWrite();
}
+void WriterThread::clearWriteQueue()
+{
+ m_dataMutex.lock();
+ m_queue.clear();
+ m_dataMutex.unlock();
+}
+
void WriterThread::queueTrkInitialPing()
{
m_dataMutex.lock();
@@ -592,6 +620,8 @@ class ReaderThreadBase : public QThread
Q_DISABLE_COPY(ReaderThreadBase)
public:
+ int bytesPending() const { return m_trkReadBuffer.size(); }
+
signals:
void messageReceived(const trk::TrkResult &result, const QByteArray &rawData);
@@ -606,10 +636,11 @@ private:
void readMessages();
QByteArray m_trkReadBuffer;
+ bool linkEstablishmentMode;
};
ReaderThreadBase::ReaderThreadBase(const QSharedPointer<DeviceContext> &context) :
- m_context(context)
+ m_context(context), linkEstablishmentMode(true)
{
static const int trkResultMetaId = qRegisterMetaType<trk::TrkResult>();
Q_UNUSED(trkResultMetaId)
@@ -632,7 +663,7 @@ void ReaderThreadBase::readMessages()
{
TrkResult r;
QByteArray rawData;
- while (extractResult(&m_trkReadBuffer, m_context->serialFrame, &r, &rawData)) {
+ while (extractResult(&m_trkReadBuffer, m_context->serialFrame, &r, linkEstablishmentMode, &rawData)) {
emit messageReceived(r, rawData);
}
}
@@ -692,7 +723,7 @@ int WinReaderThread::tryRead()
if (!ClearCommError(m_context->device, NULL, &comStat)){
emit error(QString::fromLatin1("ClearCommError failed: %1").arg(winErrorMessage(GetLastError())));
return -7;
- }
+ }
const DWORD bytesToRead = qMax(DWORD(1), qMin(comStat.cbInQue, DWORD(BufSize)));
// Trigger read
DWORD bytesRead = 0;
@@ -708,7 +739,7 @@ int WinReaderThread::tryRead()
if (readError != ERROR_IO_PENDING) {
emit error(QString::fromLatin1("Read error: %1").arg(winErrorMessage(readError)));
return -1;
- }
+ }
// Wait for either termination or data
const DWORD wr = WaitForMultipleObjects(HandleCount, m_handles, false, INFINITE);
if (wr == WAIT_FAILED) {
@@ -783,7 +814,7 @@ private:
int m_terminatePipeFileDescriptors[2];
};
-UnixReaderThread::UnixReaderThread(const QSharedPointer<DeviceContext> &context) :
+UnixReaderThread::UnixReaderThread(const QSharedPointer<DeviceContext> &context) :
ReaderThreadBase(context)
{
m_terminatePipeFileDescriptors[0] = m_terminatePipeFileDescriptors[1] = -1;
@@ -877,8 +908,8 @@ struct TrkDevicePrivate
TrkDevicePrivate();
QSharedPointer<DeviceContext> deviceContext;
- QSharedPointer<WriterThread> writerThread;
- QSharedPointer<ReaderThread> readerThread;
+ QScopedPointer<WriterThread> writerThread;
+ QScopedPointer<ReaderThread> readerThread;
QByteArray trkReadBuffer;
int verbose;
@@ -917,14 +948,14 @@ TrkDevice::~TrkDevice()
bool TrkDevice::open(QString *errorMessage)
{
- if (d->verbose)
+ if (d->verbose || verboseTrk)
qDebug() << "Opening" << port() << "is open: " << isOpen() << " serialFrame=" << serialFrame();
+ if (isOpen())
+ return true;
if (d->port.isEmpty()) {
*errorMessage = QLatin1String("Internal error: No port set on TrkDevice");
return false;
}
-
- close();
#ifdef Q_OS_WIN
const QString fullPort = QLatin1String("\\\\.\\") + d->port;
d->deviceContext->device = CreateFile(reinterpret_cast<const WCHAR*>(fullPort.utf16()),
@@ -975,7 +1006,7 @@ bool TrkDevice::open(QString *errorMessage)
return false;
}
#endif
- d->readerThread = QSharedPointer<ReaderThread>(new ReaderThread(d->deviceContext));
+ d->readerThread.reset(new ReaderThread(d->deviceContext));
connect(d->readerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)),
Qt::QueuedConnection);
connect(d->readerThread.data(), SIGNAL(messageReceived(trk::TrkResult,QByteArray)),
@@ -983,18 +1014,22 @@ bool TrkDevice::open(QString *errorMessage)
Qt::QueuedConnection);
d->readerThread->start();
- d->writerThread = QSharedPointer<WriterThread>(new WriterThread(d->deviceContext));
+ d->writerThread.reset(new WriterThread(d->deviceContext));
connect(d->writerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)),
- Qt::QueuedConnection);
- d->writerThread->start();
+ Qt::QueuedConnection);
+ d->writerThread->start();
- if (d->verbose)
- qDebug() << "Opened" << d->port;
+ if (d->verbose || verboseTrk)
+ qDebug() << "Opened" << d->port << d->readerThread.data() << d->writerThread.data();
return true;
}
void TrkDevice::close()
{
+ if (verboseTrk)
+ qDebug() << "close" << d->port << " is open: " << isOpen()
+ << " read pending " << (d->readerThread.isNull() ? 0 : d->readerThread->bytesPending())
+ << sender();
if (!isOpen())
return;
if (d->readerThread)
@@ -1010,6 +1045,7 @@ void TrkDevice::close()
#else
d->deviceContext->file.close();
#endif
+
if (d->verbose)
emitLogMessage("Close");
}
@@ -1030,6 +1066,8 @@ QString TrkDevice::port() const
void TrkDevice::setPort(const QString &p)
{
+ if (verboseTrk)
+ qDebug() << "setPort" << p;
d->port = p;
}
@@ -1045,6 +1083,8 @@ bool TrkDevice::serialFrame() const
void TrkDevice::setSerialFrame(bool f)
{
+ if (verboseTrk)
+ qDebug() << "setSerialFrame" << f;
d->deviceContext->serialFrame = f;
}
@@ -1060,12 +1100,14 @@ void TrkDevice::setVerbose(int b)
void TrkDevice::slotMessageReceived(const trk::TrkResult &result, const QByteArray &rawData)
{
- d->writerThread->slotHandleResult(result);
- if (d->verbose > 1)
- qDebug() << "Received: " << result.toString();
- emit messageReceived(result);
- if (!rawData.isEmpty())
- emit rawDataReceived(rawData);
+ if (isOpen()) { // Might receive bytes after closing due to queued connections.
+ d->writerThread->slotHandleResult(result);
+ if (d->verbose > 1)
+ qDebug() << "Received: " << result.toString();
+ emit messageReceived(result);
+ if (!rawData.isEmpty())
+ emit rawDataReceived(rawData);
+ }
}
void TrkDevice::emitError(const QString &s)
@@ -1075,15 +1117,27 @@ void TrkDevice::emitError(const QString &s)
emit error(s);
}
+void TrkDevice::clearWriteQueue()
+{
+ if (isOpen())
+ d->writerThread->clearWriteQueue();
+}
+
void TrkDevice::sendTrkMessage(byte code, TrkCallback callback,
const QByteArray &data, const QVariant &cookie)
{
+ if (!isOpen()) {
+ emitError(msgAccessingClosedDevice(d->port));
+ return;
+ }
if (!d->writerThread.isNull()) {
if (d->verbose > 1) {
- QByteArray msg = "Sending: ";
+ QByteArray msg = "Sending: 0x";
msg += QByteArray::number(code, 16);
msg += ": ";
msg += stringFromArray(data).toLatin1();
+ if (cookie.isValid())
+ msg += " Cookie: " + cookie.toString().toLatin1();
qDebug("%s", msg.data());
}
d->writerThread->queueTrkMessage(code, callback, data, cookie);
@@ -1092,12 +1146,20 @@ void TrkDevice::sendTrkMessage(byte code, TrkCallback callback,
void TrkDevice::sendTrkInitialPing()
{
+ if (!isOpen()) {
+ emitError(msgAccessingClosedDevice(d->port));
+ return;
+ }
if (!d->writerThread.isNull())
d->writerThread->queueTrkInitialPing();
}
bool TrkDevice::sendTrkAck(byte token)
{
+ if (!isOpen()) {
+ emitError(msgAccessingClosedDevice(d->port));
+ return false;
+ }
if (d->writerThread.isNull())
return false;
// The acknowledgement must not be queued!
diff --git a/tools/runonphone/symbianutils/trkdevice.h b/tools/runonphone/symbianutils/trkdevice.h
index 78012fd..1021a7d 100644
--- a/tools/runonphone/symbianutils/trkdevice.h
+++ b/tools/runonphone/symbianutils/trkdevice.h
@@ -64,12 +64,14 @@ struct TrkDevicePrivate;
* Trk communications. Provides synchronous write and asynchronous
* read operation.
* The serialFrames property specifies whether packets are encapsulated in
- * "0x90 <length>" frames, which is currently the case for serial ports.
+ * "0x90 <length>" frames, which is currently the case for serial ports.
* Contains a write message queue allowing
* for queueing messages with a notification callback. If the message receives
* an ACK, the callback is invoked.
- * The special message TRK_WRITE_QUEUE_NOOP_CODE code can be used for synchronisation.
- * The respective message will not be sent, the callback is just invoked. */
+ * The special message TRK_WRITE_QUEUE_NOOP_CODE code can be used for synchronization.
+ * The respective message will not be sent, the callback is just invoked.
+ * Note that calling open/close in quick succession can cause crashes
+ * due to the use of queused signals. */
enum { TRK_WRITE_QUEUE_NOOP_CODE = 0x7f };
@@ -111,6 +113,9 @@ public:
// Send an Ack synchronously, bypassing the queue
bool sendTrkAck(unsigned char token);
+public slots:
+ void clearWriteQueue();
+
signals:
void messageReceived(const trk::TrkResult &result);
// Emitted with the contents of messages enclosed in 07e, not for log output
diff --git a/tools/runonphone/symbianutils/trkutils.cpp b/tools/runonphone/symbianutils/trkutils.cpp
index 9b43c96..d89da20 100644
--- a/tools/runonphone/symbianutils/trkutils.cpp
+++ b/tools/runonphone/symbianutils/trkutils.cpp
@@ -52,6 +52,25 @@
namespace trk {
+Library::Library() : codeseg(0), dataseg(0), pid(0)
+{
+}
+
+Library::Library(const TrkResult &result) : codeseg(0), dataseg(0), pid(0)
+{
+ if (result.data.size() < 20) {
+ qWarning("Invalid trk creation notification received.");
+ return;
+ }
+
+ const char *data = result.data.constData();
+ pid = extractInt(data + 2);
+ codeseg = extractInt(data + 10);
+ dataseg = extractInt(data + 14);
+ const uint len = extractShort(data + 18);
+ name = result.data.mid(20, len);
+}
+
TrkAppVersion::TrkAppVersion()
{
reset();
@@ -77,11 +96,11 @@ void Session::reset()
extended1TypeSize = 0;
extended2TypeSize = 0;
pid = 0;
+ mainTid = 0;
tid = 0;
codeseg = 0;
dataseg = 0;
- currentThread = 0;
libraries.clear();
trkAppVersion.reset();
}
@@ -143,6 +162,90 @@ QString Session::deviceDescription(unsigned verbose) const
return msg.arg(formatTrkVersion(trkAppVersion));
}
+QByteArray Session::gdbLibraryList() const
+{
+ const int count = libraries.size();
+ QByteArray response = "l<library-list>";
+ for (int i = 0; i != count; ++i) {
+ const trk::Library &lib = libraries.at(i);
+ response += "<library name=\"";
+ response += lib.name;
+ response += "\">";
+ response += "<section address=\"0x";
+ response += trk::hexNumber(lib.codeseg);
+ response += "\"/>";
+ response += "<section address=\"0x";
+ response += trk::hexNumber(lib.dataseg);
+ response += "\"/>";
+ response += "<section address=\"0x";
+ response += trk::hexNumber(lib.dataseg);
+ response += "\"/>";
+ response += "</library>";
+ }
+ response += "</library-list>";
+ return response;
+}
+
+QByteArray Session::gdbQsDllInfo(int start, int count) const
+{
+ // Happens with gdb 6.4.50.20060226-cvs / CodeSourcery.
+ // Never made it into FSF gdb that got qXfer:libraries:read instead.
+ // http://sourceware.org/ml/gdb/2007-05/msg00038.html
+ // Name=hexname,TextSeg=textaddr[,DataSeg=dataaddr]
+ const int libraryCount = libraries.size();
+ const int end = count < 0 ? libraryCount : qMin(libraryCount, start + count);
+ QByteArray response(1, end == libraryCount ? 'l' : 'm');
+ for (int i = start; i < end; ++i) {
+ if (i != start)
+ response += ';';
+ const Library &lib = libraries.at(i);
+ response += "Name=";
+ response += lib.name.toHex();
+ response += ",TextSeg=";
+ response += hexNumber(lib.codeseg);
+ response += ",DataSeg=";
+ response += hexNumber(lib.dataseg);
+ }
+ return response;
+}
+
+QString Session::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << "Session: " << deviceDescription(false) << '\n'
+ << "pid: " << pid << "main thread: " << mainTid
+ << " current thread: " << tid << ' ';
+ str.setIntegerBase(16);
+ str << " code: 0x" << codeseg << " data: 0x" << dataseg << '\n';
+ if (const int libCount = libraries.size()) {
+ str << "Libraries:\n";
+ for (int i = 0; i < libCount; i++)
+ str << " #" << i << ' ' << libraries.at(i).name
+ << " code: 0x" << libraries.at(i).codeseg
+ << " data: 0x" << libraries.at(i).dataseg << '\n';
+ }
+ if (const int moduleCount = modules.size()) {
+ str << "Modules:\n";
+ for (int i = 0; i < moduleCount; i++)
+ str << " #" << i << ' ' << modules.at(i) << '\n';
+ }
+ str.setIntegerBase(10);
+ if (!addressToBP.isEmpty()) {
+ typedef QHash<uint, uint>::const_iterator BP_ConstIterator;
+ str << "Breakpoints:\n";
+ const BP_ConstIterator cend = addressToBP.constEnd();
+ for (BP_ConstIterator it = addressToBP.constBegin(); it != cend; ++it) {
+ str.setIntegerBase(16);
+ str << " 0x" << it.key();
+ str.setIntegerBase(10);
+ str << ' ' << it.value() << '\n';
+ }
+ }
+
+ return rc;
+}
+
// --------------
QByteArray decode7d(const QByteArray &ba)
@@ -188,18 +291,15 @@ SYMBIANUTILS_EXPORT QString stringFromArray(const QByteArray &ba, int maxLen)
QString ascii;
const int size = maxLen == -1 ? ba.size() : qMin(ba.size(), maxLen);
for (int i = 0; i < size; ++i) {
- //if (i == 5 || i == ba.size() - 2)
- // str += " ";
- int c = byte(ba.at(i));
- str += QString("%1 ").arg(c, 2, 16, QChar('0'));
- if (i >= 8 && i < ba.size() - 2)
- ascii += QChar(c).isPrint() ? QChar(c) : QChar('.');
+ const int c = byte(ba.at(i));
+ str += QString::fromAscii("%1 ").arg(c, 2, 16, QChar('0'));
+ ascii += QChar(c).isPrint() ? QChar(c) : QChar('.');
}
if (size != ba.size()) {
- str += "...";
- ascii += "...";
+ str += QLatin1String("...");
+ ascii += QLatin1String("...");
}
- return str + " " + ascii;
+ return str + QLatin1String(" ") + ascii;
}
SYMBIANUTILS_EXPORT QByteArray hexNumber(uint n, int digits)
@@ -276,14 +376,13 @@ QByteArray frameMessage(byte command, byte token, const QByteArray &data, bool s
/* returns 0 if array doesn't represent a result,
otherwise returns the length of the result data */
-ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame)
+ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame, ushort& mux)
{
if (serialFrame) {
// Serial protocol with length info
if (buffer.length() < 4)
return 0;
- if (buffer.at(0) != 0x01 || byte(buffer.at(1)) != 0x90)
- return 0;
+ mux = extractShort(buffer.data());
const ushort len = extractShort(buffer.data() + 2);
return (buffer.size() >= len + 4) ? len : ushort(0);
}
@@ -292,6 +391,7 @@ ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame)
const int firstDelimiterPos = buffer.indexOf(delimiter);
// Regular message delimited by 0x7e..0x7e
if (firstDelimiterPos == 0) {
+ mux = MuxTrk;
const int endPos = buffer.indexOf(delimiter, firstDelimiterPos + 1);
return endPos != -1 ? endPos + 1 - firstDelimiterPos : 0;
}
@@ -299,21 +399,30 @@ ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame)
return firstDelimiterPos != -1 ? firstDelimiterPos : buffer.size();
}
-bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, QByteArray *rawData)
+bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, bool &linkEstablishmentMode, QByteArray *rawData)
{
result->clear();
if(rawData)
rawData->clear();
- const ushort len = isValidTrkResult(*buffer, serialFrame);
- if (!len)
- return false;
+ ushort len = isValidTrkResult(*buffer, serialFrame, result->multiplex);
// handle receiving application output, which is not a regular command
const int delimiterPos = serialFrame ? 4 : 0;
+ if (linkEstablishmentMode) {
+ //when "hot connecting" a device, we can receive partial frames.
+ //this code resyncs by discarding data until a TRK frame is found
+ while (buffer->length() > delimiterPos
+ && result->multiplex != MuxTextTrace
+ && !(result->multiplex == MuxTrk && buffer->at(delimiterPos) == 0x7e)) {
+ buffer->remove(0,1);
+ len = isValidTrkResult(*buffer, serialFrame, result->multiplex);
+ }
+ }
+ if (!len)
+ return false;
if (buffer->at(delimiterPos) != 0x7e) {
result->isDebugOutput = true;
result->data = buffer->mid(delimiterPos, len);
- result->data.replace("\r\n", "\n");
- *buffer->remove(0, delimiterPos + len);
+ buffer->remove(0, delimiterPos + len);
return true;
}
// FIXME: what happens if the length contains 0xfe?
@@ -321,7 +430,7 @@ bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, QByt
const QByteArray data = decode7d(buffer->mid(delimiterPos + 1, len - 2));
if(rawData)
*rawData = data;
- *buffer->remove(0, delimiterPos + len);
+ buffer->remove(0, delimiterPos + len);
byte sum = 0;
for (int i = 0; i < data.size(); ++i) // 3 = 2 * 0xfe + sum
@@ -336,6 +445,7 @@ bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, QByt
//logMessage(" CURR DATA: " << stringFromArray(data));
//QByteArray prefix = "READ BUF: ";
//logMessage((prefix + "HEADER: " + stringFromArray(header).toLatin1()).data());
+ linkEstablishmentMode = false; //have received a good TRK packet, therefore in sync
return true;
}
@@ -353,6 +463,19 @@ SYMBIANUTILS_EXPORT uint extractInt(const char *data)
return res;
}
+SYMBIANUTILS_EXPORT quint64 extractInt64(const char *data)
+{
+ quint64 res = byte(data[0]);
+ res <<= 8; res += byte(data[1]);
+ res <<= 8; res += byte(data[2]);
+ res <<= 8; res += byte(data[3]);
+ res <<= 8; res += byte(data[4]);
+ res <<= 8; res += byte(data[5]);
+ res <<= 8; res += byte(data[6]);
+ res <<= 8; res += byte(data[7]);
+ return res;
+}
+
SYMBIANUTILS_EXPORT QString quoteUnprintableLatin1(const QByteArray &ba)
{
QString res;
diff --git a/tools/runonphone/symbianutils/trkutils.h b/tools/runonphone/symbianutils/trkutils.h
index 3a485c7..d365f0d 100644
--- a/tools/runonphone/symbianutils/trkutils.h
+++ b/tools/runonphone/symbianutils/trkutils.h
@@ -43,6 +43,7 @@
#define DEBUGGER_TRK_UTILS
#include "symbianutils_global.h"
+
#include <QtCore/QByteArray>
#include <QtCore/QHash>
#include <QtCore/QStringList>
@@ -55,40 +56,110 @@ QT_END_NAMESPACE
namespace trk {
typedef unsigned char byte;
+struct TrkResult;
enum Command {
+ //meta commands
TrkPing = 0x00,
TrkConnect = 0x01,
TrkDisconnect = 0x02,
+ TrkReset = 0x03,
TrkVersions = 0x04,
TrkSupported = 0x05,
TrkCpuType = 0x06,
+ TrkConfigTransport = 0x07,
+ TrkVersions2 = 0x08,
TrkHostVersions = 0x09,
+
+ //state commands
+ TrkReadMemory = 0x10,
+ TrkWriteMemory = 0x11,
+ TrkReadRegisters = 0x12,
+ TrkWriteRegisters = 0x13,
+ TrkFillMemory = 0x14,
+ TrkCopyMemory = 0x15,
+ TrkFlushCache = 0x16,
+
+ //execution commands
TrkContinue = 0x18,
+ TrkStep = 0x19,
+ TrkStop = 0x1a,
+ TrkSetBreak = 0x1b,
+ TrkClearBreak = 0x1c,
+ TrkDownload = 0x1d,
+ TrkModifyBreakThread = 0x1e,
+
+ //host -> target IO management
+ TrkNotifyFileInput = 0x20,
+ TrkBlockFileIo = 0x21,
+
+ //host -> target os commands
TrkCreateItem = 0x40,
TrkDeleteItem = 0x41,
+ TrkReadInfo = 0x42,
+ TrkWriteInfo = 0x43,
TrkWriteFile = 0x48,
+ TrkReadFile = 0x49,
TrkOpenFile = 0x4a,
TrkCloseFile = 0x4b,
+ TrkPositionFile = 0x4c,
TrkInstallFile = 0x4d,
TrkInstallFile2 = 0x4e,
+ TrkPhoneSwVersion = 0x4f,
+ TrkPhoneName = 0x50,
+ TrkVersions3 = 0x51,
+
+ //replies
TrkNotifyAck = 0x80,
TrkNotifyNak = 0xff,
+
+ //target -> host notification
TrkNotifyStopped = 0x90,
TrkNotifyException = 0x91,
TrkNotifyInternalError = 0x92,
+ TrkNotifyStopped2 = 0x94,
+
+ //target -> host OS notification
TrkNotifyCreated = 0xa0,
TrkNotifyDeleted = 0xa1,
TrkNotifyProcessorStarted = 0xa2,
TrkNotifyProcessorStandBy = 0xa6,
- TrkNotifyProcessorReset = 0xa7
+ TrkNotifyProcessorReset = 0xa7,
+
+ //target -> host support commands (these are defined but not implemented in TRK)
+ TrkDSWriteFile = 0xd0,
+ TrkDSReadFile = 0xd1,
+ TrkDSOpenFile = 0xd2,
+ TrkDSCloseFile = 0xd3,
+ TrkDSPositionFile = 0xd4
+};
+
+enum DSOSItemTypes {
+ kDSOSProcessItem = 0x0000,
+ kDSOSThreadItem = 0x0001,
+ kDSOSDLLItem = 0x0002,
+ kDSOSAppItem = 0x0003,
+ kDSOSMemBlockItem = 0x0004,
+ kDSOSProcAttachItem = 0x0005,
+ kDSOSThreadAttachItem = 0x0006,
+ kDSOSProcAttach2Item = 0x0007,
+ kDSOSProcRunItem = 0x0008,
+ /* 0x0009 - 0x00ff reserved for general expansion */
+ /* 0x0100 - 0xffff available for target-specific use */
+};
+
+enum SerialMultiplexor {
+ MuxRaw = 0,
+ MuxTextTrace = 0x0102,
+ MuxTrk = 0x0190
};
inline byte extractByte(const char *data) { return *data; }
SYMBIANUTILS_EXPORT ushort extractShort(const char *data);
SYMBIANUTILS_EXPORT uint extractInt(const char *data);
+SYMBIANUTILS_EXPORT quint64 extractInt64(const char *data);
SYMBIANUTILS_EXPORT QString quoteUnprintableLatin1(const QByteArray &ba);
@@ -108,17 +179,20 @@ SYMBIANUTILS_EXPORT void appendString(QByteArray *ba, const QByteArray &str, End
struct SYMBIANUTILS_EXPORT Library
{
- Library() {}
+ Library();
+ explicit Library(const TrkResult &r);
QByteArray name;
uint codeseg;
uint dataseg;
+ //library addresses are valid for a given process (depending on memory model, they might be loaded at the same address in all processes or not)
+ uint pid;
};
struct SYMBIANUTILS_EXPORT TrkAppVersion
{
TrkAppVersion();
- void reset();
+ void reset();
int trkMajor;
int trkMinor;
@@ -131,6 +205,11 @@ struct SYMBIANUTILS_EXPORT Session
Session();
void reset();
QString deviceDescription(unsigned verbose) const;
+ QString toString() const;
+ // Answer to qXfer::libraries
+ QByteArray gdbLibraryList() const;
+ // Answer to qsDllInfo, can be sent chunk-wise.
+ QByteArray gdbQsDllInfo(int start = 0, int count = -1) const;
// Trk feedback
byte cpuMajor;
@@ -142,6 +221,7 @@ struct SYMBIANUTILS_EXPORT Session
byte extended2TypeSize;
TrkAppVersion trkAppVersion;
uint pid;
+ uint mainTid;
uint tid;
uint codeseg;
uint dataseg;
@@ -150,12 +230,7 @@ struct SYMBIANUTILS_EXPORT Session
typedef QList<Library> Libraries;
Libraries libraries;
- typedef uint Thread;
- typedef QList<Thread> Threads;
- Threads threads;
-
// Gdb request
- uint currentThread;
QStringList modules;
};
@@ -168,6 +243,7 @@ struct SYMBIANUTILS_EXPORT TrkResult
int errorCode() const;
QString errorString() const;
+ ushort multiplex;
byte code;
byte token;
QByteArray data;
diff --git a/tools/runonphone/symbianutils/trkutils_p.h b/tools/runonphone/symbianutils/trkutils_p.h
index 12b0109..05df83a 100644
--- a/tools/runonphone/symbianutils/trkutils_p.h
+++ b/tools/runonphone/symbianutils/trkutils_p.h
@@ -55,7 +55,7 @@ void appendDateTime(QByteArray *ba, QDateTime dateTime, Endianness = TargetByteO
// returns a QByteArray containing optionally
// the serial frame [0x01 0x90 <len>] and 0x7e encoded7d(ba) 0x7e
QByteArray frameMessage(byte command, byte token, const QByteArray &data, bool serialFrame);
-bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *r, QByteArray *rawData = 0);
+bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *r, bool& linkEstablishmentMode, QByteArray *rawData = 0);
} // namespace trk