diff options
author | hjk <[email protected]> | 2013-01-24 10:38:40 +0100 |
---|---|---|
committer | hjk <[email protected]> | 2013-01-24 11:05:36 +0100 |
commit | e52a35a239ceab5381e289482fca546e26b06366 (patch) | |
tree | accf80b123d5ab430ba1746a0855b9af3bcb9983 /src/plugins/debugger/debuggerprotocol.cpp | |
parent | 09cd8bf4e69b6c24f6c0fa725f6a0cac64e21d6c (diff) |
Debugger: rename gdb/gdbmi.{h,cpp} into debuggerprotocol.{h,cpp}
The scope is a bit broader nowadays.
Change-Id: I9578da94f06df199be5668c1751fd7bfb37f1c5b
Reviewed-by: Friedemann Kleint <[email protected]>
Diffstat (limited to 'src/plugins/debugger/debuggerprotocol.cpp')
-rw-r--r-- | src/plugins/debugger/debuggerprotocol.cpp | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/src/plugins/debugger/debuggerprotocol.cpp b/src/plugins/debugger/debuggerprotocol.cpp new file mode 100644 index 00000000000..a194ff90df1 --- /dev/null +++ b/src/plugins/debugger/debuggerprotocol.cpp @@ -0,0 +1,433 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "debuggerprotocol.h" + +#include <utils/qtcassert.h> + +#include <QByteArray> +#include <QDebug> +#include <QRegExp> +#include <QTextStream> + +#include <ctype.h> + +namespace Debugger { +namespace Internal { + +void skipCommas(const char *&from, const char *to) +{ + while (*from == ',' && from != to) + ++from; +} + +QTextStream &operator<<(QTextStream &os, const GdbMi &mi) +{ + return os << mi.toString(); +} + +void GdbMi::parseResultOrValue(const char *&from, const char *to) +{ + while (from != to && isspace(*from)) + ++from; + + //qDebug() << "parseResultOrValue: " << QByteArray(from, to - from); + parseValue(from, to); + if (isValid()) { + //qDebug() << "no valid result in " << QByteArray(from, to - from); + return; + } + if (from == to || *from == '(') + return; + const char *ptr = from; + while (ptr < to && *ptr != '=') { + //qDebug() << "adding" << QChar(*ptr) << "to name"; + ++ptr; + } + m_name = QByteArray(from, ptr - from); + from = ptr; + if (from < to && *from == '=') { + ++from; + parseValue(from, to); + } +} + +QByteArray GdbMi::parseCString(const char *&from, const char *to) +{ + QByteArray result; + //qDebug() << "parseCString: " << QByteArray(from, to - from); + if (*from != '"') { + qDebug() << "MI 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() << "MI 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() << "MI Parse Error, unrecognized backslash escape"; + return QByteArray(); + } + *dst++ = prod; + } + } + while (src != end) { + char c = *src++; + if (c == '\\') + break; + *dst++ = c; + } + } while (src != end); + *dst = 0; + result.truncate(dst - result.data()); + } + + return result; +} + +void GdbMi::parseValue(const char *&from, const char *to) +{ + //qDebug() << "parseValue: " << QByteArray(from, to - from); + switch (*from) { + case '{': + parseTuple(from, to); + break; + case '[': + parseList(from, to); + break; + case '"': + m_type = Const; + m_data = parseCString(from, to); + break; + default: + break; + } +} + + +void GdbMi::parseTuple(const char *&from, const char *to) +{ + //qDebug() << "parseTuple: " << QByteArray(from, to - from); + QTC_CHECK(*from == '{'); + ++from; + parseTuple_helper(from, to); +} + +void GdbMi::parseTuple_helper(const char *&from, const char *to) +{ + skipCommas(from, to); + //qDebug() << "parseTuple_helper: " << QByteArray(from, to - from); + m_type = Tuple; + while (from < to) { + if (*from == '}') { + ++from; + break; + } + GdbMi child; + child.parseResultOrValue(from, to); + //qDebug() << "\n=======\n" << qPrintable(child.toString()) << "\n========\n"; + if (!child.isValid()) + return; + m_children += child; + skipCommas(from, to); + } +} + +void GdbMi::parseList(const char *&from, const char *to) +{ + //qDebug() << "parseList: " << QByteArray(from, to - from); + QTC_CHECK(*from == '['); + ++from; + m_type = List; + skipCommas(from, to); + while (from < to) { + if (*from == ']') { + ++from; + break; + } + GdbMi child; + child.parseResultOrValue(from, to); + if (child.isValid()) + m_children += child; + skipCommas(from, to); + } +} + +static QByteArray ind(int indent) +{ + return QByteArray(2 * indent, ' '); +} + +void GdbMi::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); + } +} + +QByteArray GdbMi::escapeCString(const QByteArray &ba) +{ + QByteArray ret; + ret.reserve(ba.length() * 2); + for (int i = 0; i < ba.length(); ++i) { + const uchar 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; +} + +QByteArray GdbMi::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 Const: + if (!m_name.isEmpty()) + result += m_name + '='; + result += '"' + escapeCString(m_data) + '"'; + break; + case Tuple: + 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 List: + 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 GdbMi::fromString(const QByteArray &ba) +{ + const char *from = ba.constBegin(); + const char *to = ba.constEnd(); + parseResultOrValue(from, to); +} + +void GdbMi::fromStringMultiple(const QByteArray &ba) +{ + const char *from = ba.constBegin(); + const char *to = ba.constEnd(); + parseTuple_helper(from, to); +} + +GdbMi GdbMi::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 GdbMi(); +} + +qulonglong GdbMi::toAddress() const +{ + QByteArray ba = m_data; + if (ba.endsWith('L')) + ba.chop(1); + if (ba.startsWith('*') || ba.startsWith('@')) + ba = ba.mid(1); + return ba.toULongLong(0, 0); +} + +////////////////////////////////////////////////////////////////////////////////// +// +// GdbResponse +// +////////////////////////////////////////////////////////////////////////////////// + +QByteArray GdbResponse::stringFromResultClass(GdbResultClass resultClass) +{ + switch (resultClass) { + case GdbResultDone: return "done"; + case GdbResultRunning: return "running"; + case GdbResultConnected: return "connected"; + case GdbResultError: return "error"; + case GdbResultExit: return "exit"; + default: return "unknown"; + } +} + +QByteArray GdbResponse::toString() const +{ + QByteArray result; + if (token != -1) + result = QByteArray::number(token); + result += '^'; + result += stringFromResultClass(resultClass); + if (data.isValid()) + result += ',' + data.toString(); + result += '\n'; + return result; +} + + +////////////////////////////////////////////////////////////////////////////////// +// +// GdbResponse +// +////////////////////////////////////////////////////////////////////////////////// + +void extractGdbVersion(const QString &msg, + int *gdbVersion, int *gdbBuildVersion, bool *isMacGdb, bool *isQnxGdb) +{ + const QChar dot(QLatin1Char('.')); + + QString cleaned; + QString build; + bool inClean = true; + foreach (QChar c, msg) { + if (inClean && !cleaned.isEmpty() && c != dot && (c.isPunct() || c.isSpace())) + inClean = false; + if (inClean) { + if (c.isDigit()) + cleaned.append(c); + else if (!cleaned.isEmpty() && !cleaned.endsWith(dot)) + cleaned.append(dot); + } else { + if (c.isDigit()) + build.append(c); + else if (!build.isEmpty() && !build.endsWith(dot)) + build.append(dot); + } + } + + *isMacGdb = msg.contains(QLatin1String("Apple version")); + *isQnxGdb = msg.contains(QLatin1String("qnx")); + + *gdbVersion = 10000 * cleaned.section(dot, 0, 0).toInt() + + 100 * cleaned.section(dot, 1, 1).toInt() + + 1 * cleaned.section(dot, 2, 2).toInt(); + if (cleaned.count(dot) >= 3) + *gdbBuildVersion = cleaned.section(dot, 3, 3).toInt(); + else + *gdbBuildVersion = build.section(dot, 0, 0).toInt(); + + if (*isMacGdb) + *gdbBuildVersion = build.section(dot, 1, 1).toInt(); +} + +} // namespace Internal +} // namespace Debugger |