diff --git a/plugins/gdb/printers/qt.py b/plugins/gdb/printers/qt.py index bffd5811ff..9215f5d85e 100644 --- a/plugins/gdb/printers/qt.py +++ b/plugins/gdb/printers/qt.py @@ -1,733 +1,714 @@ # -*- coding: iso-8859-1 -*- # Pretty-printers for Qt 4 and Qt 5. # Copyright (C) 2009 Niko Sams # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import gdb import itertools import re import time from helper import * class QStringPrinter: def __init__(self, val): self.val = val def to_string(self): size = self.val['d']['size'] ret = "" # The QString object might be not yet initialized. In this case size is a bogus value # and the following 2 lines might throw memory access error. Hence the try/catch. try: isQt4 = has_field(self.val['d'], 'data') # Qt4 has d->data, Qt5 doesn't. if isQt4: dataAsCharPointer = self.val['d']['data'].cast(gdb.lookup_type("char").pointer()) else: dataAsCharPointer = (self.val['d'] + 1).cast(gdb.lookup_type("char").pointer()) ret = dataAsCharPointer.string(encoding = 'UTF-16', length = size * 2) except Exception: # swallow the exception and return empty string pass return ret def display_hint (self): return 'string' class QByteArrayPrinter: def __init__(self, val): self.val = val # Qt4 has 'data', Qt5 doesn't self.isQt4 = has_field(self.val['d'], 'data') class _iterator(Iterator): def __init__(self, data, size): self.data = data self.size = size self.count = 0 def __iter__(self): return self def __next__(self): if self.count >= self.size: raise StopIteration count = self.count self.count = self.count + 1 return ('[%d]' % count, self.data[count]) def stringData(self): if self.isQt4: return self.val['d']['data'] else: return self.val['d'].cast(gdb.lookup_type("char").const().pointer()) + self.val['d']['offset'] def children(self): return self._iterator(self.stringData(), self.val['d']['size']) def to_string(self): #todo: handle charset correctly return self.stringData() def display_hint (self): return 'string' class QListPrinter: "Print a QList" class _iterator(Iterator): def __init__(self, nodetype, d): self.nodetype = nodetype self.d = d self.count = 0 #from QTypeInfo::isLarge isLarge = self.nodetype.sizeof > gdb.lookup_type('void').pointer().sizeof isPointer = self.nodetype.code == gdb.TYPE_CODE_PTR #unfortunately we can't use QTypeInfo::isStatic as it's all inlined, so use #this list of types that use Q_DECLARE_TYPEINFO(T, Q_MOVABLE_TYPE) #(obviously it won't work for custom types) movableTypes = ['QRect', 'QRectF', 'QString', 'QMargins', 'QLocale', 'QChar', 'QDate', 'QTime', 'QDateTime', 'QVector', 'QRegExpr', 'QPoint', 'QPointF', 'QByteArray', 'QSize', 'QSizeF', 'QBitArray', 'QLine', 'QLineF', 'QModelIndex', 'QPersitentModelIndex', 'QVariant', 'QFileInfo', 'QUrl', 'QXmlStreamAttribute', 'QXmlStreamNamespaceDeclaration', 'QXmlStreamNotationDeclaration', 'QXmlStreamEntityDeclaration', 'QPair'] #this list of types that use Q_DECLARE_TYPEINFO(T, Q_PRIMITIVE_TYPE) (from qglobal.h) primitiveTypes = ['bool', 'char', 'signed char', 'unsigned char', 'short', 'unsigned short', 'int', 'unsigned int', 'long', 'unsigned long', 'long long', 'unsigned long long', 'float', 'double'] if movableTypes.count(self.nodetype.tag) or primitiveTypes.count(str(self.nodetype)): isStatic = False else: isStatic = not isPointer self.externalStorage = isLarge or isStatic #see QList::Node::t() def __iter__(self): return self def __next__(self): if self.count >= self.d['end'] - self.d['begin']: raise StopIteration count = self.count array = self.d['array'].address + self.d['begin'] + count if self.externalStorage: value = array.cast(gdb.lookup_type('QList<%s>::Node' % self.nodetype).pointer())['v'] else: value = array self.count = self.count + 1 return ('[%d]' % count, value.cast(self.nodetype.pointer()).dereference()) def __init__(self, val, container, itype): - self.val = val + self.d = val['d'] self.container = container + self.size = self.d['end'] - self.d['begin'] if itype == None: - self.itype = self.val.type.template_argument(0) + self.itype = val.type.template_argument(0) else: self.itype = gdb.lookup_type(itype) def children(self): - return self._iterator(self.itype, self.val['d']) + return self._iterator(self.itype, self.d) def to_string(self): - if self.val['d']['end'] == self.val['d']['begin']: - empty = "empty " - else: - empty = "" - - return "%s%s<%s>" % ( empty, self.container, self.itype ) + return "%s<%s> (size = %s)" % ( self.container, self.itype, self.size ) class QVectorPrinter: "Print a QVector" class _iterator(Iterator): def __init__(self, nodetype, data, size): self.nodetype = nodetype self.data = data self.size = size self.count = 0 def __iter__(self): return self def __next__(self): if self.count >= self.size: raise StopIteration count = self.count self.count = self.count + 1 return ('[%d]' % count, self.data[count]) def __init__(self, val, container): self.val = val self.container = container self.itype = self.val.type.template_argument(0) def children(self): isQt4 = has_field(self.val['d'], 'p') # Qt4 has 'p', Qt5 doesn't if isQt4: return self._iterator(self.itype, self.val['p']['array'], self.val['p']['size']) else: data = self.val['d'].cast(gdb.lookup_type("char").const().pointer()) + self.val['d']['offset'] return self._iterator(self.itype, data.cast(self.itype.pointer()), self.val['d']['size']) def to_string(self): - if self.val['d']['size'] == 0: - empty = "empty " - else: - empty = "" + size = self.val['d']['size'] - return "%s%s<%s>" % ( empty, self.container, self.itype ) + return "%s<%s> (size = %s)" % ( self.container, self.itype, size ) class QLinkedListPrinter: "Print a QLinkedList" class _iterator(Iterator): def __init__(self, nodetype, begin, size): self.nodetype = nodetype self.it = begin self.pos = 0 self.size = size def __iter__(self): return self def __next__(self): if self.pos >= self.size: raise StopIteration pos = self.pos val = self.it['t'] self.it = self.it['n'] self.pos = self.pos + 1 return ('[%d]' % pos, val) def __init__(self, val): self.val = val self.itype = self.val.type.template_argument(0) def children(self): return self._iterator(self.itype, self.val['e']['n'], self.val['d']['size']) def to_string(self): - if self.val['d']['size'] == 0: - empty = "empty " - else: - empty = "" + size = self.val['d']['size'] - return "%sQLinkedList<%s>" % ( empty, self.itype ) + return "QLinkedList<%s> (size = %s)" % ( self.itype, size ) class QMapPrinter: "Print a QMap" class _iteratorQt4(Iterator): def __init__(self, val): self.val = val self.ktype = self.val.type.template_argument(0) self.vtype = self.val.type.template_argument(1) self.data_node = self.val['e']['forward'][0] self.count = 0 def __iter__(self): return self def payload (self): if gdb.parse_and_eval: ret = int(gdb.parse_and_eval('QMap<%s, %s>::payload()' % (self.ktype, self.vtype))) if (ret): return ret; #if the inferior function call didn't work, let's try to calculate ourselves #we can't use QMapPayloadNode as it's inlined #as a workaround take the sum of sizeof(members) ret = self.ktype.sizeof ret += self.vtype.sizeof ret += gdb.lookup_type('void').pointer().sizeof #but because of data alignment the value can be higher #so guess it's aliged by sizeof(void*) #TODO: find a real solution for this problem ret += ret % gdb.lookup_type('void').pointer().sizeof #for some reason booleans are different if str(self.vtype) == 'bool': ret += 2 ret -= gdb.lookup_type('void').pointer().sizeof return ret def concrete (self, data_node): node_type = gdb.lookup_type('QMapNode<%s, %s>' % (self.ktype, self.vtype)).pointer() return (data_node.cast(gdb.lookup_type('char').pointer()) - self.payload()).cast(node_type) def __next__(self): if self.data_node == self.val['e']: raise StopIteration node = self.concrete(self.data_node).dereference() if self.count % 2 == 0: item = node['key'] else: item = node['value'] self.data_node = node['forward'][0] result = ('[%d]' % self.count, item) self.count = self.count + 1 return result class _iteratorQt5: def __init__(self, val): realtype = val.type.strip_typedefs() keytype = realtype.template_argument(0) valtype = realtype.template_argument(1) node_type = gdb.lookup_type('QMapData<' + keytype.name + ',' + valtype.name + '>::Node') self.node_p_type = node_type.pointer() self.root = val['d']['header'] self.current = None self.next_is_key = True self.i = -1 # we store the path here to avoid keeping re-fetching # values from the inferior (also, skips the pointer # arithmetic involved in using the parent pointer) self.path = [] def __iter__(self): return self def moveToNextNode(self): if self.current is None: # find the leftmost node if not self.root['left']: return False self.current = self.root while self.current['left']: self.path.append(self.current) self.current = self.current['left'] elif self.current['right']: self.path.append(self.current) self.current = self.current['right'] while self.current['left']: self.path.append(self.current) self.current = self.current['left'] else: last = self.current self.current = self.path.pop() while self.current['right'] == last: last = self.current self.current = self.path.pop() # if there are no more parents, we are at the root if len(self.path) == 0: return False return True def __next__(self): if self.next_is_key: if not self.moveToNextNode(): raise StopIteration self.current_typed = self.current.reinterpret_cast(self.node_p_type) self.next_is_key = False self.i += 1 return ('key' + str(self.i), self.current_typed['key']) else: self.next_is_key = True return ('value' + str(self.i), self.current_typed['value']) def next(self): return self.__next__() def __init__(self, val, container): self.val = val self.container = container def children(self): if self.val['d']['size'] == 0: return [] isQt4 = has_field(self.val, 'e') # Qt4 has 'e', Qt5 doesn't if isQt4: return self._iteratorQt4(self.val) else: return self._iteratorQt5(self.val) def to_string(self): - if self.val['d']['size'] == 0: - empty = "empty " - else: - empty = "" + size = self.val['d']['size'] - return "%s%s<%s, %s>" % ( empty, self.container, self.val.type.template_argument(0), self.val.type.template_argument(1) ) + return "%s<%s, %s> (size = %s)" % ( self.container, self.val.type.template_argument(0), self.val.type.template_argument(1), size ) def display_hint (self): return 'map' class QHashPrinter: "Print a QHash" class _iterator(Iterator): def __init__(self, val): self.val = val self.d = self.val['d'] self.ktype = self.val.type.template_argument(0) self.vtype = self.val.type.template_argument(1) self.end_node = self.d.cast(gdb.lookup_type('QHashData::Node').pointer()) self.data_node = self.firstNode() self.count = 0 def __iter__(self): return self def hashNode (self): "Casts the current QHashData::Node to a QHashNode and returns the result. See also QHash::concrete()" return self.data_node.cast(gdb.lookup_type('QHashNode<%s, %s>' % (self.ktype, self.vtype)).pointer()) def firstNode (self): "Get the first node, See QHashData::firstNode()." e = self.d.cast(gdb.lookup_type('QHashData::Node').pointer()) #print "QHashData::firstNode() e %s" % e bucketNum = 0 bucket = self.d['buckets'][bucketNum] #print "QHashData::firstNode() *bucket %s" % bucket n = self.d['numBuckets'] #print "QHashData::firstNode() n %s" % n while n: #print "QHashData::firstNode() in while, n %s" % n; if bucket != e: #print "QHashData::firstNode() in while, return *bucket %s" % bucket return bucket bucketNum += 1 bucket = self.d['buckets'][bucketNum] #print "QHashData::firstNode() in while, new bucket %s" % bucket n -= 1 #print "QHashData::firstNode() return e %s" % e return e def nextNode (self, node): "Get the nextNode after the current, see also QHashData::nextNode()." #print "******************************** nextNode" #print "nextNode: node %s" % node next = node['next'].cast(gdb.lookup_type('QHashData::Node').pointer()) e = next #print "nextNode: next %s" % next if next['next']: #print "nextNode: return next" return next #print "nextNode: node->h %s" % node['h'] #print "nextNode: numBuckets %s" % self.d['numBuckets'] start = (node['h'] % self.d['numBuckets']) + 1 bucketNum = start #print "nextNode: start %s" % start bucket = self.d['buckets'][start] #print "nextNode: bucket %s" % bucket n = self.d['numBuckets'] - start #print "nextNode: n %s" % n while n: #print "nextNode: in while; n %s" % n #print "nextNode: in while; e %s" % e #print "nextNode: in while; *bucket %s" % bucket if bucket != e: #print "nextNode: in while; return bucket %s" % bucket return bucket bucketNum += 1 bucket = self.d['buckets'][bucketNum] n -= 1 #print "nextNode: return e %s" % e return e def __next__(self): "GDB iteration, first call returns key, second value and then jumps to the next hash node." if self.data_node == self.end_node: raise StopIteration node = self.hashNode() if self.count % 2 == 0: item = node['key'] else: item = node['value'] self.data_node = self.nextNode(self.data_node) self.count = self.count + 1 return ('[%d]' % self.count, item) def __init__(self, val, container): self.val = val self.container = container def children(self): return self._iterator(self.val) def to_string(self): - if self.val['d']['size'] == 0: - empty = "empty " - else: - empty = "" + size = self.val['d']['size'] - return "%s%s<%s, %s>" % ( empty, self.container, self.val.type.template_argument(0), self.val.type.template_argument(1) ) + return "%s<%s, %s> (size = %s)" % ( self.container, self.val.type.template_argument(0), self.val.type.template_argument(1), size ) def display_hint (self): return 'map' class QDatePrinter: def __init__(self, val): self.val = val def to_string(self): julianDay = self.val['jd'] if julianDay == 0: return "invalid QDate" # Copied from Qt sources if julianDay >= 2299161: # Gregorian calendar starting from October 15, 1582 # This algorithm is from Henry F. Fliegel and Thomas C. Van Flandern ell = julianDay + 68569; n = (4 * ell) / 146097; ell = ell - (146097 * n + 3) / 4; i = (4000 * (ell + 1)) / 1461001; ell = ell - (1461 * i) / 4 + 31; j = (80 * ell) / 2447; d = ell - (2447 * j) / 80; ell = j / 11; m = j + 2 - (12 * ell); y = 100 * (n - 49) + i + ell; else: # Julian calendar until October 4, 1582 # Algorithm from Frequently Asked Questions about Calendars by Claus Toendering julianDay += 32082; dd = (4 * julianDay + 3) / 1461; ee = julianDay - (1461 * dd) / 4; mm = ((5 * ee) + 2) / 153; d = ee - (153 * mm + 2) / 5 + 1; m = mm + 3 - 12 * (mm / 10); y = dd - 4800 + (mm / 10); if y <= 0: --y; return "%d-%02d-%02d" % (y, m, d) class QTimePrinter: def __init__(self, val): self.val = val def to_string(self): ds = self.val['mds'] if ds == -1: return "invalid QTime" MSECS_PER_HOUR = 3600000 SECS_PER_MIN = 60 MSECS_PER_MIN = 60000 hour = ds / MSECS_PER_HOUR minute = (ds % MSECS_PER_HOUR) / MSECS_PER_MIN second = (ds / 1000)%SECS_PER_MIN msec = ds % 1000 return "%02d:%02d:%02d.%03d" % (hour, minute, second, msec) class QDateTimePrinter: def __init__(self, val): self.val = val def to_string(self): time_t = gdb.parse_and_eval("reinterpret_cast(%s)->toTime_t()" % self.val.address) return time.ctime(int(time_t)) class QUrlPrinter: def __init__(self, val): self.val = val def to_string(self): # first try to access the Qt 5 data try: int_type = gdb.lookup_type('int') string_type = gdb.lookup_type('QString') string_pointer = string_type.pointer() addr = self.val['d'].cast(gdb.lookup_type('char').pointer()) # skip QAtomicInt ref addr += int_type.sizeof # handle int port port = addr.cast(int_type.pointer()).dereference() addr += int_type.sizeof # handle QString scheme scheme = QStringPrinter(addr.cast(string_pointer).dereference()).to_string() addr += string_type.sizeof # handle QString username username = QStringPrinter(addr.cast(string_pointer).dereference()).to_string() addr += string_type.sizeof # skip QString password addr += string_type.sizeof # handle QString host host = QStringPrinter(addr.cast(string_pointer).dereference()).to_string() addr += string_type.sizeof # handle QString path path = QStringPrinter(addr.cast(string_pointer).dereference()).to_string() addr += string_type.sizeof # handle QString query query = QStringPrinter(addr.cast(string_pointer).dereference()).to_string() addr += string_type.sizeof # handle QString fragment fragment = QStringPrinter(addr.cast(string_pointer).dereference()).to_string() url = "" if len(scheme) > 0: # TODO: always adding // is apparently not compliant in all cases url += scheme + "://" if len(host) > 0: if len(username) > 0: url += username + "@" url += host if port != -1: url += ":" + str(port) url += path if len(query) > 0: url += "?" + query if len(fragment) > 0: url += "#" + fragment return url except: pass # then try to print directly, but that might lead to issues (see http://sourceware-org.1504.n7.nabble.com/help-Calling-malloc-from-a-Python-pretty-printer-td284031.html) try: return gdb.parse_and_eval("reinterpret_cast(%s)->toString((QUrl::FormattingOptions)QUrl::PrettyDecoded)" % self.val.address) except: pass # if everything fails, maybe we deal with Qt 4 code try: return self.val['d']['encodedOriginal'] except RuntimeError: #if no debug information is available for Qt, try guessing the correct address for encodedOriginal #problem with this is that if QUrlPrivate members get changed, this fails offset = gdb.lookup_type('int').sizeof offset += offset % gdb.lookup_type('void').pointer().sizeof #alignment offset += gdb.lookup_type('QString').sizeof * 6 offset += gdb.lookup_type('QByteArray').sizeof encodedOriginal = self.val['d'].cast(gdb.lookup_type('char').pointer()); encodedOriginal += offset encodedOriginal = encodedOriginal.cast(gdb.lookup_type('QByteArray').pointer()).dereference(); encodedOriginal = encodedOriginal['d']['data'].string() return encodedOriginal class QSetPrinter: "Print a QSet" def __init__(self, val): self.val = val class _iterator(Iterator): def __init__(self, hashIterator): self.hashIterator = hashIterator self.count = 0 def __iter__(self): return self def __next__(self): if self.hashIterator.data_node == self.hashIterator.end_node: raise StopIteration node = self.hashIterator.hashNode() item = node['key'] self.hashIterator.data_node = self.hashIterator.nextNode(self.hashIterator.data_node) self.count = self.count + 1 return ('[%d]' % (self.count-1), item) def children(self): hashPrinter = QHashPrinter(self.val['q_hash'], None) hashIterator = hashPrinter._iterator(self.val['q_hash']) return self._iterator(hashIterator) def to_string(self): - if self.val['q_hash']['d']['size'] == 0: - empty = "empty " - else: - empty = "" + size = self.val['q_hash']['d']['size'] - return "%sQSet<%s>" % ( empty , self.val.type.template_argument(0) ) + return "QSet<%s> (size = %s)" % ( self.val.type.template_argument(0), size ) class QCharPrinter: def __init__(self, val): self.val = val def to_string(self): return unichr(self.val['ucs']) def display_hint (self): return 'string' class QUuidPrinter: def __init__(self, val): self.val = val def to_string(self): return "QUuid({%x-%x-%x-%x%x-%x%x%x%x%x%x})" % (int(self.val['data1']), int(self.val['data2']), int(self.val['data3']), int(self.val['data4'][0]), int(self.val['data4'][1]), int(self.val['data4'][2]), int(self.val['data4'][3]), int(self.val['data4'][4]), int(self.val['data4'][5]), int(self.val['data4'][6]), int(self.val['data4'][7])) def display_hint (self): return 'string' pretty_printers_dict = {} def register_qt_printers (obj): if obj == None: obj = gdb obj.pretty_printers.append(FunctionLookup(gdb, pretty_printers_dict)) def build_dictionary (): pretty_printers_dict[re.compile('^QString$')] = lambda val: QStringPrinter(val) pretty_printers_dict[re.compile('^QByteArray$')] = lambda val: QByteArrayPrinter(val) pretty_printers_dict[re.compile('^QList<.*>$')] = lambda val: QListPrinter(val, 'QList', None) pretty_printers_dict[re.compile('^QStringList$')] = lambda val: QListPrinter(val, 'QStringList', 'QString') pretty_printers_dict[re.compile('^QQueue')] = lambda val: QListPrinter(val, 'QQueue', None) pretty_printers_dict[re.compile('^QVector<.*>$')] = lambda val: QVectorPrinter(val, 'QVector') pretty_printers_dict[re.compile('^QStack<.*>$')] = lambda val: QVectorPrinter(val, 'QStack') pretty_printers_dict[re.compile('^QLinkedList<.*>$')] = lambda val: QLinkedListPrinter(val) pretty_printers_dict[re.compile('^QMap<.*>$')] = lambda val: QMapPrinter(val, 'QMap') pretty_printers_dict[re.compile('^QMultiMap<.*>$')] = lambda val: QMapPrinter(val, 'QMultiMap') pretty_printers_dict[re.compile('^QHash<.*>$')] = lambda val: QHashPrinter(val, 'QHash') pretty_printers_dict[re.compile('^QMultiHash<.*>$')] = lambda val: QHashPrinter(val, 'QMultiHash') pretty_printers_dict[re.compile('^QDate$')] = lambda val: QDatePrinter(val) pretty_printers_dict[re.compile('^QTime$')] = lambda val: QTimePrinter(val) pretty_printers_dict[re.compile('^QDateTime$')] = lambda val: QDateTimePrinter(val) pretty_printers_dict[re.compile('^QUrl$')] = lambda val: QUrlPrinter(val) pretty_printers_dict[re.compile('^QSet<.*>$')] = lambda val: QSetPrinter(val) pretty_printers_dict[re.compile('^QChar$')] = lambda val: QCharPrinter(val) pretty_printers_dict[re.compile('^QUuid')] = lambda val: QUuidPrinter(val) build_dictionary () diff --git a/plugins/gdb/unittests/test_gdbprinters.cpp b/plugins/gdb/unittests/test_gdbprinters.cpp index 8f760d665b..9b66af112a 100644 --- a/plugins/gdb/unittests/test_gdbprinters.cpp +++ b/plugins/gdb/unittests/test_gdbprinters.cpp @@ -1,456 +1,469 @@ /* Copyright 2010 Niko Sams This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "test_gdbprinters.h" #include "tests/debuggers-tests-config.h" #include #include #include #include #include const QString BINARY_PATH(DEBUGGEE_BIN_DIR); class GdbProcess : private QProcess { public: explicit GdbProcess(const QString &program) : QProcess() { setProcessChannelMode(MergedChannels); // don't attempt to load .gdbinit in home (may cause unexpected results) QProcess::start(QStringLiteral("gdb"), (QStringList() << QStringLiteral("-nx") << (BINARY_PATH + '/' + program))); const bool started = waitForStarted(); if (!started) { qDebug() << "Failed to start 'gdb' executable:" << errorString(); Q_ASSERT(false); return; } QByteArray prompt = waitForPrompt(); QVERIFY(!prompt.contains("No such file or directory")); execute("set confirm off"); execute("set print pretty on"); execute("set disable-randomization off"); // see https://phabricator.kde.org/D2188 QList p; QDir printersDir = QFileInfo(__FILE__).dir(); printersDir.cdUp(); // go up to get to the main printers directory QVERIFY(printersDir.cd("printers")); p << "python" << "import sys" << "sys.path.insert(0, '"+printersDir.path().toLatin1()+"')" << "from qt import register_qt_printers" << "register_qt_printers (None)" << "from kde import register_kde_printers" << "register_kde_printers (None)" << "end"; foreach (const QByteArray &i, p) { write(i + "\n"); } waitForPrompt(); } ~GdbProcess() override { write("q\n"); waitForFinished(); } QByteArray waitForPrompt() { QByteArray output; while (!output.endsWith("(gdb) ")) { Q_ASSERT(state() == QProcess::Running); waitForReadyRead(); QByteArray l = readAll(); //qDebug() << l; output.append(l); } output.chop(7); //remove (gdb) prompt if (output.contains("Traceback") || output.contains("Exception")) { qDebug() << output; QTest::qFail("Unexpected Python Exception", __FILE__, __LINE__); } return output; } QByteArray execute(const QByteArray &cmd) { write(cmd + "\n"); auto out = waitForPrompt(); qDebug() << cmd << " = " << out; return out; } }; void QtPrintersTest::testQString() { GdbProcess gdb(QStringLiteral("debuggee_qstring")); gdb.execute("break qstring.cpp:5"); gdb.execute("run"); QVERIFY(gdb.execute("print s").contains("\"test string\"")); gdb.execute("next"); QVERIFY(gdb.execute("print s").contains("\"test stringx\"")); } void QtPrintersTest::testQByteArray() { GdbProcess gdb(QStringLiteral("debuggee_qbytearray")); gdb.execute("break qbytearray.cpp:5"); gdb.execute("run"); QByteArray out = gdb.execute("print ba"); QVERIFY(out.contains("\"test byte array\"")); QVERIFY(out.contains("[0] = 116 't'")); QVERIFY(out.contains("[4] = 32 ' '")); gdb.execute("next"); QVERIFY(gdb.execute("print ba").contains("\"test byte arrayx\"")); } void QtPrintersTest::testQListContainer_data() { QTest::addColumn("container"); QTest::newRow("QList") << "QList"; QTest::newRow("QQueue") << "QQueue"; QTest::newRow("QVector") << "QVector"; QTest::newRow("QStack") << "QStack"; QTest::newRow("QLinkedList") << "QLinkedList"; QTest::newRow("QSet") << "QSet"; } void QtPrintersTest::testQListContainer() { QFETCH(QString, container); GdbProcess gdb(QStringLiteral("debuggee_qlistcontainer")); gdb.execute("break main"); gdb.execute("run"); gdb.execute(QStringLiteral("break 'doStuff<%1>()'").arg(container).toLocal8Bit()); gdb.execute("cont"); { // gdb.execute("break qlistcontainer.cpp:34"); gdb.execute("cont"); QByteArray out = gdb.execute("print intList"); qWarning() << "FOO" << out; - QVERIFY(out.contains(QString("empty %1").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1 (size = 0)").arg(container).toLocal8Bit())); gdb.execute("next"); out = gdb.execute("print intList"); - QVERIFY(out.contains(QString("%1").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1 (size = 2)").arg(container).toLocal8Bit())); if (container != QLatin1String("QSet")) { QVERIFY(out.contains("[0] = 10")); QVERIFY(out.contains("[1] = 20")); QVERIFY(!out.contains("[2] = 30")); } else { // QSet order is undefined QVERIFY(out.contains("] = 10")); QVERIFY(out.contains("] = 20")); QVERIFY(!out.contains("] = 30")); } gdb.execute("next"); out = gdb.execute("print intList"); - QVERIFY(out.contains(QString("%1").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1 (size = 3)").arg(container).toLocal8Bit())); if (container != QLatin1String("QSet")) { QVERIFY(out.contains("[0] = 10")); QVERIFY(out.contains("[1] = 20")); QVERIFY(out.contains("[2] = 30")); } else { // QSet order is undefined QVERIFY(out.contains("] = 10")); QVERIFY(out.contains("] = 20")); QVERIFY(out.contains("] = 30")); } } { // gdb.execute("next"); QByteArray out = gdb.execute("print stringList"); - QVERIFY(out.contains(QString("empty %1").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1 (size = 0)").arg(container).toLocal8Bit())); gdb.execute("next"); out = gdb.execute("print stringList"); - QVERIFY(out.contains(QString("%1").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1 (size = 2)").arg(container).toLocal8Bit())); if (container != QLatin1String("QSet")) { QVERIFY(out.contains("[0] = \"a\"")); QVERIFY(out.contains("[1] = \"bc\"")); QVERIFY(!out.contains("[2] = \"d\"")); } else { // QSet order is undefined QVERIFY(out.contains("] = \"a\"")); QVERIFY(out.contains("] = \"bc\"")); QVERIFY(!out.contains("] = \"d\"")); } gdb.execute("next"); out = gdb.execute("print stringList"); - QVERIFY(out.contains(QString("%1").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1 (size = 3)").arg(container).toLocal8Bit())); if (container != QLatin1String("QSet")) { QVERIFY(out.contains("[0] = \"a\"")); QVERIFY(out.contains("[1] = \"bc\"")); QVERIFY(out.contains("[2] = \"d\"")); } else { // QSet order is undefined QVERIFY(out.contains("] = \"a\"")); QVERIFY(out.contains("] = \"bc\"")); QVERIFY(out.contains("] = \"d\"")); } } { // gdb.execute("next"); QByteArray out = gdb.execute("print structList"); - QVERIFY(out.contains(QString("empty %1").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1 (size = 0)").arg(container).toLocal8Bit())); gdb.execute("next"); out = gdb.execute("print structList"); - QVERIFY(out.contains(QString("%1").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1 (size = 1)").arg(container).toLocal8Bit())); QVERIFY(out.contains("[0] = {")); QVERIFY(out.contains("a = \"a\"")); QVERIFY(out.contains("b = \"b\"")); QVERIFY(out.contains("c = 100")); QVERIFY(out.contains("d = -200")); gdb.execute("next"); out = gdb.execute("print structList"); - QVERIFY(out.contains(QString("%1").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1 (size = 2)").arg(container).toLocal8Bit())); QVERIFY(out.contains("[1] = {")); } { // gdb.execute("next"); QByteArray out = gdb.execute("print pointerList"); - QVERIFY(out.contains(QString("empty %1").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1 (size = 0)").arg(container).toLocal8Bit())); gdb.execute("next"); out = gdb.execute("print pointerList"); - QVERIFY(out.contains(QString("%1").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1 (size = 2)").arg(container).toLocal8Bit())); QVERIFY(out.contains("[0] = 0x")); QVERIFY(out.contains("[1] = 0x")); QVERIFY(!out.contains("[2] = 0x")); gdb.execute("next"); out = gdb.execute("print pointerList"); + QVERIFY(out.contains(QString("%1 (size = 3)").arg(container).toLocal8Bit())); QVERIFY(out.contains("[0] = 0x")); QVERIFY(out.contains("[1] = 0x")); QVERIFY(out.contains("[2] = 0x")); gdb.execute("next"); } { // > gdb.execute("next"); QByteArray out = gdb.execute("print pairList"); - QVERIFY(out.contains(QString("empty %1>").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1> (size = 0)").arg(container).toLocal8Bit())); gdb.execute("next"); out = gdb.execute("print pairList"); - QVERIFY(out.contains(QString("%1>").arg(container).toLocal8Bit())); + QVERIFY(out.contains(QString("%1> (size = 2)").arg(container).toLocal8Bit())); if (container != QLatin1String("QSet")) { QVERIFY(out.contains("[0] = {\n first = 1, \n second = 2\n }")); QVERIFY(out.contains("[1] = {\n first = 2, \n second = 3\n }")); } else { // order is undefined in QSet QVERIFY(out.contains("] = {\n first = 1, \n second = 2\n }")); QVERIFY(out.contains("] = {\n first = 2, \n second = 3\n }")); } QVERIFY(!out.contains("[2] = ")); gdb.execute("next"); out = gdb.execute("print pairList"); + QVERIFY(out.contains(QString("%1> (size = 3)").arg(container).toLocal8Bit())); if (container != QLatin1String("QSet")) { QVERIFY(out.contains("[0] = {\n first = 1, \n second = 2\n }")); QVERIFY(out.contains("[1] = {\n first = 2, \n second = 3\n }")); QVERIFY(out.contains("[2] = {\n first = 4, \n second = 5\n }")); } else { // order is undefined in QSet QVERIFY(out.contains("] = {\n first = 1, \n second = 2\n }")); QVERIFY(out.contains("] = {\n first = 2, \n second = 3\n }")); QVERIFY(out.contains("] = {\n first = 4, \n second = 5\n }")); } } } void QtPrintersTest::testQMapInt() { GdbProcess gdb(QStringLiteral("debuggee_qmapint")); gdb.execute("break qmapint.cpp:7"); gdb.execute("run"); QByteArray out = gdb.execute("print m"); - QVERIFY(out.contains("QMap")); + QVERIFY(out.contains("QMap (size = 2)")); QVERIFY(out.contains("[10] = 100")); QVERIFY(out.contains("[20] = 200")); gdb.execute("next"); out = gdb.execute("print m"); + QVERIFY(out.contains("QMap (size = 3)")); QVERIFY(out.contains("[30] = 300")); } void QtPrintersTest::testQMapString() { GdbProcess gdb(QStringLiteral("debuggee_qmapstring")); gdb.execute("break qmapstring.cpp:8"); gdb.execute("run"); QByteArray out = gdb.execute("print m"); - QVERIFY(out.contains("QMap")); + QVERIFY(out.contains("QMap (size = 2)")); QVERIFY(out.contains("[\"10\"] = \"100\"")); QVERIFY(out.contains("[\"20\"] = \"200\"")); gdb.execute("next"); out = gdb.execute("print m"); + QVERIFY(out.contains("QMap (size = 3)")); QVERIFY(out.contains("[\"30\"] = \"300\"")); } void QtPrintersTest::testQMapStringBool() { GdbProcess gdb(QStringLiteral("debuggee_qmapstringbool")); gdb.execute("break qmapstringbool.cpp:8"); gdb.execute("run"); QByteArray out = gdb.execute("print m"); - QVERIFY(out.contains("QMap")); + QVERIFY(out.contains("QMap (size = 2)")); QVERIFY(out.contains("[\"10\"] = true")); QVERIFY(out.contains("[\"20\"] = false")); gdb.execute("next"); out = gdb.execute("print m"); + QVERIFY(out.contains("QMap (size = 3)")); QVERIFY(out.contains("[\"30\"] = true")); } void QtPrintersTest::testQDate() { GdbProcess gdb(QStringLiteral("debuggee_qdate")); gdb.execute("break qdate.cpp:6"); gdb.execute("run"); QByteArray out = gdb.execute("print d"); QVERIFY(out.contains("2010-01-20")); } void QtPrintersTest::testQTime() { GdbProcess gdb(QStringLiteral("debuggee_qtime")); gdb.execute("break qtime.cpp:6"); gdb.execute("run"); QByteArray out = gdb.execute("print t"); QVERIFY(out.contains("15:30:10.123")); } void QtPrintersTest::testQDateTime() { GdbProcess gdb(QStringLiteral("debuggee_qdatetime")); gdb.execute("break qdatetime.cpp:5"); gdb.execute("run"); QByteArray out = gdb.execute("print dt"); QVERIFY(out.contains("Wed Jan 20 15:31:13 2010")); } void QtPrintersTest::testQUrl() { GdbProcess gdb(QStringLiteral("debuggee_qurl")); gdb.execute("break qurl.cpp:5"); gdb.execute("run"); QByteArray out = gdb.execute("print u"); QVERIFY(out.contains("http://user@www.kdevelop.org:12345/foo?xyz=bar#asdf")); } void QtPrintersTest::testQHashInt() { GdbProcess gdb(QStringLiteral("debuggee_qhashint")); gdb.execute("break qhashint.cpp:7"); gdb.execute("run"); QByteArray out = gdb.execute("print h"); + QVERIFY(out.contains("QHash (size = 2)")); QVERIFY(out.contains("[10] = 100")); QVERIFY(out.contains("[20] = 200")); gdb.execute("next"); out = gdb.execute("print h"); + QVERIFY(out.contains("QHash (size = 3)")); QVERIFY(out.contains("[30] = 300")); } void QtPrintersTest::testQHashString() { GdbProcess gdb(QStringLiteral("debuggee_qhashstring")); gdb.execute("break qhashstring.cpp:8"); gdb.execute("run"); QByteArray out = gdb.execute("print h"); + QVERIFY(out.contains("QHash (size = 2)")); QVERIFY(out.contains("[\"10\"] = \"100\"")); QVERIFY(out.contains("[\"20\"] = \"200\"")); gdb.execute("next"); out = gdb.execute("print h"); + QVERIFY(out.contains("QHash (size = 3)")); QVERIFY(out.contains("[\"30\"] = \"300\"")); } void QtPrintersTest::testQSetInt() { GdbProcess gdb(QStringLiteral("debuggee_qsetint")); gdb.execute("break qsetint.cpp:7"); gdb.execute("run"); QByteArray out = gdb.execute("print s"); + QVERIFY(out.contains("QSet (size = 2)")); QVERIFY(out.contains("] = 10")); QVERIFY(out.contains("] = 20")); gdb.execute("next"); out = gdb.execute("print s"); + QVERIFY(out.contains("QSet (size = 3)")); QVERIFY(out.contains("] = 30")); } void QtPrintersTest::testQSetString() { GdbProcess gdb(QStringLiteral("debuggee_qsetstring")); gdb.execute("break qsetstring.cpp:8"); gdb.execute("run"); QByteArray out = gdb.execute("print s"); + QVERIFY(out.contains("QSet (size = 2)")); QVERIFY(out.contains("] = \"10\"")); QVERIFY(out.contains("] = \"20\"")); gdb.execute("next"); out = gdb.execute("print s"); + QVERIFY(out.contains("QSet (size = 3)")); QVERIFY(out.contains("] = \"30\"")); } void QtPrintersTest::testQChar() { GdbProcess gdb(QStringLiteral("debuggee_qchar")); gdb.execute("break qchar.cpp:5"); gdb.execute("run"); QVERIFY(gdb.execute("print c").contains("\"k\"")); } void QtPrintersTest::testQListPOD() { GdbProcess gdb(QStringLiteral("debuggee_qlistpod")); gdb.execute("break qlistpod.cpp:31"); gdb.execute("run"); QVERIFY(gdb.execute("print b").contains("false")); QVERIFY(gdb.execute("print c").contains("50")); QVERIFY(gdb.execute("print uc").contains("50")); QVERIFY(gdb.execute("print s").contains("50")); QVERIFY(gdb.execute("print us").contains("50")); QVERIFY(gdb.execute("print i").contains("50")); QVERIFY(gdb.execute("print ui").contains("50")); QVERIFY(gdb.execute("print l").contains("50")); QVERIFY(gdb.execute("print ul").contains("50")); QVERIFY(gdb.execute("print i64").contains("50")); QVERIFY(gdb.execute("print ui64").contains("50")); QVERIFY(gdb.execute("print f").contains("50")); QVERIFY(gdb.execute("print d").contains("50")); } void QtPrintersTest::testQUuid() { GdbProcess gdb(QStringLiteral("debuggee_quuid")); gdb.execute("break quuid.cpp:4"); gdb.execute("run"); QByteArray data = gdb.execute("print id"); QVERIFY(data.contains("{9ec3b70b-d105-42bf-b3b4-656e44d2e223}")); } void QtPrintersTest::testKTextEditorTypes() { GdbProcess gdb(QStringLiteral("debuggee_ktexteditortypes")); gdb.execute("break ktexteditortypes.cpp:9"); gdb.execute("run"); QByteArray data = gdb.execute("print cursor"); QCOMPARE(data, QByteArray("$1 = [1, 1]")); data = gdb.execute("print range"); QCOMPARE(data, QByteArray("$2 = [(1, 1) -> (2, 2)]")); } void QtPrintersTest::testKDevelopTypes() { GdbProcess gdb(QStringLiteral("debuggee_kdeveloptypes")); gdb.execute("break kdeveloptypes.cpp:12"); gdb.execute("run"); QVERIFY(gdb.execute("print path1").contains("(\"tmp\", \"foo\")")); QVERIFY(gdb.execute("print path2").contains("(\"http://www.test.com\", \"tmp\", \"asdf.txt\")")); } QTEST_MAIN(QtPrintersTest)