config/gdb: add qt printers
https://github.com/mixxxdj/mixxx/wiki/Debugging-Cpp-Code
This commit is contained in:
parent
a4b7d602ed
commit
ba44dd2888
|
@ -0,0 +1,714 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
# Pretty-printers for Qt 4 and Qt 5.
|
||||
|
||||
# Copyright (C) 2009 Niko Sams <niko.sams@gmail.com>
|
||||
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<T>::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<int, int>']
|
||||
#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.d = val['d']
|
||||
self.container = container
|
||||
self.size = self.d['end'] - self.d['begin']
|
||||
if itype == None:
|
||||
self.itype = val.type.template_argument(0)
|
||||
else:
|
||||
self.itype = gdb.lookup_type(itype)
|
||||
|
||||
def children(self):
|
||||
return self._iterator(self.itype, self.d)
|
||||
|
||||
def to_string(self):
|
||||
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):
|
||||
size = self.val['d']['size']
|
||||
|
||||
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):
|
||||
size = self.val['d']['size']
|
||||
|
||||
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):
|
||||
size = self.val['d']['size']
|
||||
|
||||
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):
|
||||
size = self.val['d']['size']
|
||||
|
||||
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<const QDateTime*>(%s)->toSecsSinceEpoch()" % 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<const QUrl*>(%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):
|
||||
size = self.val['q_hash']['d']['size']
|
||||
|
||||
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 ()
|
|
@ -0,0 +1,11 @@
|
|||
#set auto-load local-gdbinit on
|
||||
#add-auto-load-safe-path /
|
||||
|
||||
python
|
||||
import sys, os.path
|
||||
sys.path.insert(0, '/home/janek/.config/gdb')
|
||||
import qt5printers
|
||||
qt5printers.register_printers(gdb.current_objfile())
|
||||
end
|
||||
|
||||
set print pretty on
|
|
@ -0,0 +1,59 @@
|
|||
# qt5printers
|
||||
The `core.py`, `typeinfo.py` and `__init__.py` files are taken from patchset 2
|
||||
at https://codereview.qt-project.org/87052 and provide a GDB pretty printer for
|
||||
Qt5. These are authored by Alex Merry from the KDE project.
|
||||
|
||||
## Usage
|
||||
Copy the three Python files to `~/.gdb/qt5printers/` and add this to your
|
||||
`~/.gdbinit` (or execute it from an existing `gdb` session):
|
||||
|
||||
|
||||
python
|
||||
import sys, os.path
|
||||
sys.path.insert(0, os.path.expanduser('~/.gdb'))
|
||||
import qt5printers
|
||||
qt5printers.register_printers(gdb.current_objfile())
|
||||
end
|
||||
|
||||
Now verify it with your favorite program. Below you can find a quick test
|
||||
program.
|
||||
|
||||
### Test program
|
||||
Here is a test program (save it as `test.cpp`):
|
||||
|
||||
#include <QTextStream>
|
||||
void test(const QByteArray & ba) { }
|
||||
int main(void) {
|
||||
test(QByteArray("abc"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
Compile it with:
|
||||
|
||||
g++ test.cpp $(pkg-config --cflags --libs Qt5Core) -g
|
||||
|
||||
If everything goes well you should see the expanded data:
|
||||
|
||||
$ gdb -q -ex break\ test -ex r ./a.out
|
||||
...
|
||||
Breakpoint 1, test (ba="abc" = {...}) at test.cpp:4
|
||||
4 test(QByteArray("abc"));
|
||||
|
||||
## Background
|
||||
The Qt4 pretty printers from KDevelop[0] are not fully compatible with Qt5. For
|
||||
instance, the latest version (from December 2014) does not properly handle
|
||||
QByteArray. While these qt5printers are compatible with Qt5, it conflicts with
|
||||
Qt4 (for example, QByteArray changed in Qt 5 from Qt 4 in this commit[1]).
|
||||
|
||||
See also:
|
||||
|
||||
- https://bugs.kde.org/show_bug.cgi?id=331044
|
||||
- https://techbase.kde.org/Development/Tutorials/Debugging/Debugging_with_GDB
|
||||
|
||||
|
||||
## License
|
||||
For the applicable licenses, see the headers of the files and refer to the Qt5
|
||||
sources at https://code.qt.io/cgit/qt/qtbase.git/tree/.
|
||||
|
||||
[0]: https://projects.kde.org/projects/extragear/kdevelop/kdevelop/repository/revisions/master/show/debuggers/gdb/printers
|
||||
[1]: https://code.qt.io/cgit/qt/qtbase.git/commit/src/corelib/tools/qbytearray.h?id=ad35a41739c8e1fb6db62ed37b764448b2e59ece
|
|
@ -0,0 +1,54 @@
|
|||
#############################################################################
|
||||
##
|
||||
## Copyright (C) 2014 Alex Merry <alex.merry@kde.org>
|
||||
## Contact: http://www.qt-project.org/legal
|
||||
##
|
||||
## This file is part of the GDB pretty printers for the Qt Toolkit.
|
||||
##
|
||||
## $QT_BEGIN_LICENSE:LGPL$
|
||||
## 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.
|
||||
##
|
||||
## GNU General Public License Usage
|
||||
## Alternatively, this file may be used under the terms of the GNU
|
||||
## General Public License version 3.0 as published by the Free Software
|
||||
## Foundation and appearing in the file LICENSE.GPL included in the
|
||||
## packaging of this file. Please review the following information to
|
||||
## ensure the GNU General Public License version 3.0 requirements will be
|
||||
## met: http://www.gnu.org/copyleft/gpl.html.
|
||||
##
|
||||
##
|
||||
## $QT_END_LICENSE$
|
||||
##
|
||||
#############################################################################
|
||||
|
||||
import gdb.printing
|
||||
from . import core
|
||||
|
||||
"""Qt5 Pretty Printers for GDB.
|
||||
|
||||
The printers are split into submodules, one for each Qt library. Each
|
||||
submodule has a "printer" attribute that contains a pretty-printer for
|
||||
that library.
|
||||
"""
|
||||
|
||||
def register_printers(obj):
|
||||
"""Registers all known Qt5 pretty-printers."""
|
||||
gdb.printing.register_pretty_printer(obj, core.printer)
|
|
@ -0,0 +1,953 @@
|
|||
#############################################################################
|
||||
##
|
||||
## Copyright (C) 2014 Alex Merry <alex.merry@kde.org>
|
||||
## Contact: http://www.qt-project.org/legal
|
||||
##
|
||||
## This file is part of the GDB pretty printers for the Qt Toolkit.
|
||||
##
|
||||
## $QT_BEGIN_LICENSE:LGPL$
|
||||
## 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.
|
||||
##
|
||||
## GNU General Public License Usage
|
||||
## Alternatively, this file may be used under the terms of the GNU
|
||||
## General Public License version 3.0 as published by the Free Software
|
||||
## Foundation and appearing in the file LICENSE.GPL included in the
|
||||
## packaging of this file. Please review the following information to
|
||||
## ensure the GNU General Public License version 3.0 requirements will be
|
||||
## met: http://www.gnu.org/copyleft/gpl.html.
|
||||
##
|
||||
##
|
||||
## $QT_END_LICENSE$
|
||||
##
|
||||
#############################################################################
|
||||
|
||||
import gdb.printing
|
||||
import itertools
|
||||
from . import typeinfo
|
||||
try:
|
||||
import urlparse
|
||||
except ImportError:
|
||||
# Python 3
|
||||
import urllib.parse as urlparse
|
||||
|
||||
"""Qt5Core pretty printer for GDB."""
|
||||
|
||||
# NB: no QPair printer: the default should be fine
|
||||
|
||||
def _format_jd(jd):
|
||||
"""Format a Julian Day in YYYY-MM-DD format."""
|
||||
# maths from http://www.tondering.dk/claus/cal/julperiod.php
|
||||
a = jd + 32044
|
||||
b = (4 * a + 3) // 146097
|
||||
c = a - ( (146097 * b) // 4 )
|
||||
d = (4 * c + 3) // 1461
|
||||
e = c - ( (1461 * d) // 4 )
|
||||
m = (5 * e + 2) // 153
|
||||
day = e - ( (153 * m + 2) // 5 ) + 1
|
||||
month = m + 3 - 12 * ( m // 10 )
|
||||
year = 100 * b + d - 4800 + ( m // 10 )
|
||||
return '{:0=4}-{:0=2}-{:0=2}'.format(year, month, day)
|
||||
|
||||
def _jd_is_valid(jd):
|
||||
"""Return whether QDate would consider a given Julian Day valid."""
|
||||
return jd >= -784350574879 and jd <= 784354017364
|
||||
|
||||
def _format_time_ms(msecs):
|
||||
"""Format a number of milliseconds since midnight in HH:MM:SS.ssss format."""
|
||||
secs = msecs // 1000
|
||||
mins = secs // 60
|
||||
hours = mins // 60
|
||||
return '{:0=2}:{:0=2}:{:0=2}.{:0=3}'.format(
|
||||
hours % 24, mins % 60, secs % 60, msecs % 1000)
|
||||
|
||||
def _ms_is_valid(msecs):
|
||||
"""Return whether QTime would consider a ms since midnight valid."""
|
||||
return msecs >= 0 and msecs <= 86400000
|
||||
|
||||
class ArrayIter:
|
||||
"""Iterates over a fixed-size array."""
|
||||
def __init__(self, array, size):
|
||||
self.array = array
|
||||
self.i = -1
|
||||
self.size = size
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.i + 1 >= self.size:
|
||||
raise StopIteration
|
||||
self.i += 1
|
||||
return ('[%d]' % self.i, self.array[self.i])
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
|
||||
class StructReader:
|
||||
"""Reads entries from a struct."""
|
||||
def __init__(self, data):
|
||||
self.data = data.reinterpret_cast(gdb.lookup_type('char').pointer())
|
||||
self.ptr_t = gdb.lookup_type('void').pointer()
|
||||
|
||||
def next_aligned_val(self, typ):
|
||||
ptr_val = int(str(self.data.reinterpret_cast(self.ptr_t)), 16)
|
||||
misalignment = ptr_val % self.ptr_t.sizeof
|
||||
if misalignment > 0:
|
||||
self.data += self.ptr_t.sizeof - misalignment
|
||||
val = self.data.reinterpret_cast(typ.pointer())
|
||||
self.data += typ.sizeof
|
||||
return val.referenced_value()
|
||||
|
||||
def next_val(self, typ):
|
||||
val = self.data.reinterpret_cast(typ.pointer())
|
||||
self.data += typ.sizeof
|
||||
return val.referenced_value()
|
||||
|
||||
class QBitArrayPrinter:
|
||||
"""Print a Qt5 QBitArray"""
|
||||
|
||||
class Iter:
|
||||
def __init__(self, data, size):
|
||||
self.data = data
|
||||
self.i = -1
|
||||
self.size = size
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.i + 1 >= self.size:
|
||||
raise StopIteration
|
||||
self.i += 1
|
||||
if self.data[1 + (self.i >> 3)] & (1 << (self.i&7)):
|
||||
return (str(self.i), 1)
|
||||
else:
|
||||
return (str(self.i), 0)
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def children(self):
|
||||
d = self.val['d']['d']
|
||||
data = d.reinterpret_cast(gdb.lookup_type('char').pointer()) + d['offset']
|
||||
size = (int(d['size']) << 3) - int(data[0])
|
||||
|
||||
return self.Iter(data, size)
|
||||
|
||||
def to_string(self):
|
||||
d = self.val['d']['d']
|
||||
data = d.reinterpret_cast(gdb.lookup_type('char').pointer()) + d['offset']
|
||||
size = (int(d['size']) << 3) - int(data[0])
|
||||
if size == 0:
|
||||
return '<empty>'
|
||||
return None
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
class QByteArrayPrinter:
|
||||
"""Print a Qt5 QByteArray"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def children(self):
|
||||
d = self.val['d']
|
||||
data = d.reinterpret_cast(gdb.lookup_type('char').pointer()) + d['offset']
|
||||
return ArrayIter(data, d['size'])
|
||||
|
||||
def to_string(self):
|
||||
d = self.val['d']
|
||||
data = d.reinterpret_cast(gdb.lookup_type('char').pointer()) + d['offset']
|
||||
return data.string('', 'replace', d['size'])
|
||||
|
||||
def display_hint(self):
|
||||
return 'string'
|
||||
|
||||
class QCharPrinter:
|
||||
"""Print a Qt5 QChar"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
ucs = self.val['ucs']
|
||||
data = ucs.address.reinterpret_cast(gdb.lookup_type('char').pointer())
|
||||
unicode_str = data.string('utf-16', 'replace', 2)
|
||||
uch = unicode_str[0]
|
||||
if uch == unichr(0x27):
|
||||
return "'\\''"
|
||||
# this actually gives us Python escapes, but they should all be
|
||||
# valid C escapes as well
|
||||
return "'" + uch.encode('unicode_escape') + "'"
|
||||
|
||||
def display_hint(self):
|
||||
# this is not recognized by gdb, hence the manual escaping and quoting
|
||||
# we do above
|
||||
return 'char'
|
||||
|
||||
class QDatePrinter:
|
||||
"""Print a Qt5 QDate"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
jd = int(self.val['jd'])
|
||||
if not _jd_is_valid(jd):
|
||||
return '<invalid>'
|
||||
return _format_jd(jd)
|
||||
|
||||
def display_hint(self):
|
||||
return 'date'
|
||||
|
||||
class QDateTimePrinter:
|
||||
"""Print a Qt5 QDateTime"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
_unix_epoch_jd = 2440588
|
||||
_ms_per_day = 86400000
|
||||
|
||||
# status field
|
||||
_validDate = 0x04
|
||||
_validTime = 0x08
|
||||
_validDateTime = 0x10
|
||||
_timeZoneCached = 0x20
|
||||
|
||||
# time spec
|
||||
_localTime = 0
|
||||
_UTC = 1
|
||||
_offsetFromUTC = 2
|
||||
_timeZone = 3
|
||||
|
||||
def to_string(self):
|
||||
d = self.val['d']['d']
|
||||
if not d:
|
||||
return '<invalid>'
|
||||
|
||||
try:
|
||||
qshareddata_t = gdb.lookup_type('QSharedData')
|
||||
except gdb.error:
|
||||
try:
|
||||
# well, it only has a QAtomicInt in it
|
||||
qshareddata_t = gdb.lookup_type('QAtomicInt')
|
||||
except gdb.error:
|
||||
# let's hope it's the same size as an int
|
||||
qshareddata_t = gdb.lookup_type('int')
|
||||
try:
|
||||
timespec_t = gdb.lookup_type('Qt::TimeSpec')
|
||||
except gdb.error:
|
||||
# probably an int
|
||||
timespec_t = gdb.lookup_type('int')
|
||||
|
||||
reader = StructReader(d)
|
||||
reader.next_val(qshareddata_t)
|
||||
m_msecs = reader.next_aligned_val(gdb.lookup_type('qint64'))
|
||||
spec = int(reader.next_val(timespec_t))
|
||||
m_offsetFromUtc = reader.next_val(gdb.lookup_type('int'))
|
||||
m_timeZone = reader.next_val(gdb.lookup_type('QTimeZone'))
|
||||
status = int(reader.next_val(gdb.lookup_type('int')))
|
||||
|
||||
if spec == self._timeZone:
|
||||
timeZoneStr = QTimeZonePrinter(m_timeZone).to_string()
|
||||
if timeZoneStr == '':
|
||||
return '<invalid>'
|
||||
|
||||
if spec == self._localTime or (spec == self._timeZone and
|
||||
not status & self._timeZoneCached):
|
||||
# Because QDateTime delays timezone calculations as far as
|
||||
# possible, the ValidDateTime flag may not be set even if
|
||||
# it is a valid DateTime.
|
||||
if not status & self._validDate or not status & self._validTime:
|
||||
return '<invalid>'
|
||||
elif not (status & self._validDateTime):
|
||||
return '<invalid>'
|
||||
|
||||
# actually fetch:
|
||||
m_msecs = int(m_msecs)
|
||||
|
||||
jd = self._unix_epoch_jd # UNIX epoch
|
||||
jd += m_msecs // self._ms_per_day
|
||||
msecs = m_msecs % self._ms_per_day
|
||||
if msecs < 0:
|
||||
# need to adjust back to the previous day
|
||||
jd -= 1
|
||||
msecs += self._ms_per_day
|
||||
|
||||
result = _format_jd(jd) + ' ' + _format_time_ms(msecs)
|
||||
|
||||
if spec == self._localTime:
|
||||
result += ' (Local)'
|
||||
elif spec == self._UTC:
|
||||
result += ' (UTC)'
|
||||
elif spec == self._offsetFromUTC:
|
||||
offset = int(m_offsetFromUtc)
|
||||
if offset == 0:
|
||||
diffstr = ''
|
||||
else:
|
||||
hours = abs(offset // 3600)
|
||||
mins = abs((offset % 3600) // 60)
|
||||
secs = abs(offset % 60)
|
||||
sign = '+' if offset > 0 else '-'
|
||||
diffstr = '{:}{:0=2d}:{:0=2d}'.format(sign, hours, mins)
|
||||
if secs > 0:
|
||||
diffstr += ':{:0=2d}'.format(secs)
|
||||
result += ' (UTC{:})'.format(diffstr)
|
||||
elif spec == self._timeZone:
|
||||
result += ' ({:})'.format(timeZoneStr)
|
||||
|
||||
return result
|
||||
|
||||
def display_hint(self):
|
||||
return 'datetime'
|
||||
|
||||
class QHashPrinter:
|
||||
"""Print a Qt5 QHash"""
|
||||
|
||||
class Iter:
|
||||
def __init__(self, d, e):
|
||||
self.buckets_left = d['numBuckets']
|
||||
self.node_type = e.type
|
||||
# set us up at the end of a "dummy bucket"
|
||||
self.current_bucket = d['buckets'] - 1
|
||||
self.current_node = None
|
||||
self.i = -1
|
||||
self.waiting_for_value = False
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.waiting_for_value:
|
||||
self.waiting_for_value = False
|
||||
node = self.current_node.reinterpret_cast(self.node_type)
|
||||
return ('value' + str(self.i), node['value'])
|
||||
|
||||
if self.current_node:
|
||||
self.current_node = self.current_node['next']
|
||||
|
||||
# the dummy node that terminates a bucket is distinguishable
|
||||
# by not having its 'next' value set
|
||||
if not self.current_node or not self.current_node['next']:
|
||||
while self.buckets_left:
|
||||
self.current_bucket += 1
|
||||
self.buckets_left -= 1
|
||||
self.current_node = self.current_bucket.referenced_value()
|
||||
if self.current_node['next']:
|
||||
break
|
||||
else:
|
||||
raise StopIteration
|
||||
|
||||
self.i += 1
|
||||
self.waiting_for_value = True
|
||||
node = self.current_node.reinterpret_cast(self.node_type)
|
||||
return ('key' + str(self.i), node['key'])
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def children(self):
|
||||
d = self.val['d']
|
||||
|
||||
if d['size'] == 0:
|
||||
return []
|
||||
|
||||
return self.Iter(d, self.val['e'])
|
||||
|
||||
def to_string(self):
|
||||
# if we return an empty list from children, gdb doesn't print anything
|
||||
if self.val['d']['size'] == 0:
|
||||
return '<empty>'
|
||||
return None
|
||||
|
||||
def display_hint(self):
|
||||
return 'map'
|
||||
|
||||
class QJsonObjectPrinter:
|
||||
"""Print a Qt5 QJsonObject"""
|
||||
|
||||
def __init__(self, val):
|
||||
# delegate everything to map
|
||||
self.printer = QMapPrinter(gdb.parse_and_eval('((QJsonObject*){:})->toVariantMap()'.format(int(val.address))))
|
||||
|
||||
def children(self):
|
||||
return self.printer.children()
|
||||
|
||||
def to_string(self):
|
||||
return self.printer.to_string()
|
||||
|
||||
def display_hint(self):
|
||||
return 'map'
|
||||
|
||||
class QJsonArrayPrinter:
|
||||
"""Print a Qt5 QJsonArray"""
|
||||
|
||||
def __init__(self, val):
|
||||
# delegate everything to list
|
||||
self.printer = QListPrinter(gdb.parse_and_eval('((QJsonArray*){:})->toVariantList()'.format(int(val.address))))
|
||||
|
||||
def children(self):
|
||||
return self.printer.children()
|
||||
|
||||
def to_string(self):
|
||||
return self.printer.to_string()
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
class QLatin1StringPrinter:
|
||||
"""Print a Qt5 QLatin1String"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return self.val['m_data'].string('', 'replace', self.val['m_size'])
|
||||
|
||||
def display_hint(self):
|
||||
return 'string'
|
||||
|
||||
class QLinkedListPrinter:
|
||||
"""Print a Qt5 QLinkedList"""
|
||||
|
||||
class Iter:
|
||||
def __init__(self, tail, size):
|
||||
self.current = tail
|
||||
self.i = -1
|
||||
self.size = size
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.i + 1 >= self.size:
|
||||
raise StopIteration
|
||||
self.i += 1
|
||||
self.current = self.current['n']
|
||||
return (str(self.i), self.current['t'])
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def children(self):
|
||||
size = int(self.val['d']['size'])
|
||||
|
||||
if size == 0:
|
||||
return []
|
||||
|
||||
return self.Iter(self.val['e'], size)
|
||||
|
||||
def to_string(self):
|
||||
# if we return an empty list from children, gdb doesn't print anything
|
||||
if self.val['d']['size'] == 0:
|
||||
return '<empty>'
|
||||
return None
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
class QListPrinter:
|
||||
"""Print a Qt5 QList"""
|
||||
|
||||
class Iter:
|
||||
def __init__(self, array, begin, end, typ):
|
||||
self.array = array
|
||||
self.end = end
|
||||
self.begin = begin
|
||||
self.offset = 0
|
||||
if typ.name == 'QStringList':
|
||||
self.el_type = gdb.lookup_type('QString')
|
||||
else:
|
||||
self.el_type = typ.template_argument(0)
|
||||
|
||||
if ((self.el_type.sizeof > gdb.lookup_type('void').pointer().sizeof)
|
||||
or typeinfo.type_is_known_static(self.el_type)):
|
||||
self.is_pointer = True
|
||||
elif (typeinfo.type_is_known_movable(self.el_type) or
|
||||
typeinfo.type_is_known_primitive(self.el_type)):
|
||||
self.is_pointer = False
|
||||
else:
|
||||
raise ValueError("Could not determine whether QList stores " +
|
||||
self.el_type.name + " directly or as a pointer: to fix " +
|
||||
"this, add it to one of the variables in the "+
|
||||
"qt5printers.typeinfo module")
|
||||
self.node_type = gdb.lookup_type(typ.name + '::Node').pointer()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self.begin + self.offset >= self.end:
|
||||
raise StopIteration
|
||||
node = self.array[self.begin + self.offset].reinterpret_cast(self.node_type)
|
||||
if self.is_pointer:
|
||||
p = node['v']
|
||||
else:
|
||||
p = node
|
||||
self.offset += 1
|
||||
value = p.address.cast(self.el_type.pointer()).dereference()
|
||||
return (str(self.offset), value)
|
||||
|
||||
def next(self):
|
||||
return self.__next__()
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def children(self):
|
||||
d = self.val['d']
|
||||
begin = int(d['begin'])
|
||||
end = int(d['end'])
|
||||
|
||||
if begin == end:
|
||||
return []
|
||||
|
||||
return self.Iter(d['array'], begin, end, self.val.type.strip_typedefs())
|
||||
|
||||
def to_string(self):
|
||||
# if we return an empty list from children, gdb doesn't print anything
|
||||
if self.val['d']['begin'] == self.val['d']['end']:
|
||||
return '<empty>'
|
||||
return None
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
class QMapPrinter:
|
||||
"""Print a Qt5 QMap"""
|
||||
|
||||
class Iter:
|
||||
def __init__(self, root, node_p_type):
|
||||
self.root = root
|
||||
self.current = None
|
||||
self.node_p_type = node_p_type
|
||||
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):
|
||||
self.val = val
|
||||
|
||||
def children(self):
|
||||
d = self.val['d']
|
||||
size = int(d['size'])
|
||||
|
||||
if size == 0:
|
||||
return []
|
||||
|
||||
realtype = self.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')
|
||||
|
||||
return self.Iter(d['header'], node_type.pointer())
|
||||
|
||||
def to_string(self):
|
||||
# if we return an empty list from children, gdb doesn't print anything
|
||||
if self.val['d']['size'] == 0:
|
||||
return '<empty>'
|
||||
return None
|
||||
|
||||
def display_hint(self):
|
||||
return 'map'
|
||||
|
||||
class QSetPrinter:
|
||||
"""Print a Qt5 QSet"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def children(self):
|
||||
hashPrinter = QHashPrinter(self.val['q_hash'])
|
||||
# the keys of the hash are the elements of the set, so select
|
||||
# every other item (starting with the first)
|
||||
return itertools.islice(hashPrinter.children(), 0, None, 2)
|
||||
|
||||
def to_string(self):
|
||||
# if we return an empty list from children, gdb doesn't print anything
|
||||
if self.val['q_hash']['d']['size'] == 0:
|
||||
return '<empty>'
|
||||
return None
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
class QStringPrinter:
|
||||
"""Print a Qt5 QString"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
d = self.val['d']
|
||||
data = d.reinterpret_cast(gdb.lookup_type('char').pointer()) + d['offset']
|
||||
data_len = d['size'] * gdb.lookup_type('unsigned short').sizeof
|
||||
return data.string('utf-16', 'replace', data_len)
|
||||
|
||||
def display_hint(self):
|
||||
return 'string'
|
||||
|
||||
class QTimePrinter:
|
||||
"""Print a Qt5 QTime"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
msecs = int(self.val['mds'])
|
||||
if not _ms_is_valid(msecs):
|
||||
return '<invalid>'
|
||||
return _format_time_ms(msecs)
|
||||
|
||||
def display_hint(self):
|
||||
return 'time'
|
||||
|
||||
class QTimeZonePrinter:
|
||||
"""Print a Qt5 QTimeZone"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
d = self.val['d']['d']
|
||||
if not d:
|
||||
return ''
|
||||
|
||||
try:
|
||||
# Accessing the private data is error-prone,
|
||||
# so try just calling the id() method.
|
||||
# This should be reasonably safe, as all it will
|
||||
# do is create a QByteArray that references the
|
||||
# same internal data as the stored one. However,
|
||||
# it will only work with an attached process.
|
||||
m_id = gdb.parse_and_eval('((QTimeZone*){:})->id()'.format(self.val.address))
|
||||
except:
|
||||
ptr_size = gdb.lookup_type('void').pointer().sizeof
|
||||
try:
|
||||
qshareddata_t = gdb.lookup_type('QSharedData')
|
||||
except gdb.error:
|
||||
try:
|
||||
# well, it only has a QAtomicInt in it
|
||||
qshareddata_t = gdb.lookup_type('QAtomicInt')
|
||||
except gdb.error:
|
||||
# let's hope it's the same size as an int
|
||||
qshareddata_t = gdb.lookup_type('int')
|
||||
|
||||
reader = StructReader(d)
|
||||
reader.next_val(gdb.lookup_type('void').pointer()) # vtable
|
||||
reader.next_val(qshareddata_t)
|
||||
m_id = reader.next_aligned_val(gdb.lookup_type('QByteArray'))
|
||||
|
||||
return QByteArrayPrinter(m_id).to_string()
|
||||
|
||||
def display_hint(self):
|
||||
return 'string'
|
||||
|
||||
class QVariantPrinter:
|
||||
"""Print a Qt5 QVariant"""
|
||||
|
||||
_varmap = {
|
||||
'char': 'c',
|
||||
'uchar': 'uc',
|
||||
'short': 's',
|
||||
'signed char': 'sc',
|
||||
'ushort': 'us',
|
||||
'int': 'i',
|
||||
'uint': 'u',
|
||||
'long': 'l',
|
||||
'ulong': 'ul',
|
||||
'bool': 'b',
|
||||
'double': 'd',
|
||||
'float': 'f',
|
||||
'qreal': 'real',
|
||||
'qlonglong': 'll',
|
||||
'qulonglong': 'ull',
|
||||
'QObject*': 'o',
|
||||
'void*': 'ptr'
|
||||
}
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
d = self.val['d']
|
||||
typ = int(d['type'])
|
||||
if typ == typeinfo.meta_type_unknown:
|
||||
return '<invalid type>'
|
||||
|
||||
data = d['data']
|
||||
|
||||
if typ in typeinfo.meta_type_names:
|
||||
typename = typeinfo.meta_type_names[typ]
|
||||
if typename in self._varmap:
|
||||
field = self._varmap[typename]
|
||||
return data[field]
|
||||
|
||||
try:
|
||||
if typename.endswith('*'):
|
||||
gdb_type = gdb.lookup_type(typename[0:-1]).pointer()
|
||||
else:
|
||||
gdb_type = gdb.lookup_type(typename)
|
||||
except gdb.error:
|
||||
# couldn't find any type information
|
||||
return data
|
||||
|
||||
if gdb_type.sizeof > data.type.sizeof:
|
||||
is_pointer = True
|
||||
elif (typeinfo.type_is_known_movable(gdb_type) or
|
||||
typeinfo.type_is_known_primitive(gdb_type)):
|
||||
is_pointer = False
|
||||
elif gdb_type.tag == 'enum':
|
||||
is_pointer = False
|
||||
else:
|
||||
# couldn't figure out how the type is stored
|
||||
return data['o'].cast(gdb_type)
|
||||
|
||||
if is_pointer:
|
||||
value = data['shared']['ptr'].reinterpret_cast(gdb_type.pointer())
|
||||
else:
|
||||
void_star = gdb.lookup_type('void').pointer()
|
||||
data_void = data['c'].address.reinterpret_cast(void_star)
|
||||
value = data_void.reinterpret_cast(gdb_type.pointer())
|
||||
|
||||
return value.referenced_value()
|
||||
else:
|
||||
# custom type?
|
||||
return data
|
||||
|
||||
class QVarLengthArrayPrinter:
|
||||
"""Print a Qt5 QVarLengthArray"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def children(self):
|
||||
size = int(self.val['s'])
|
||||
|
||||
if size == 0:
|
||||
return []
|
||||
|
||||
return ArrayIter(self.val['ptr'], size)
|
||||
|
||||
def to_string(self):
|
||||
# if we return an empty list from children, gdb doesn't print anything
|
||||
if self.val['s'] == 0:
|
||||
return '<empty>'
|
||||
return None
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
class QVectorPrinter:
|
||||
"""Print a Qt5 QVector"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def children(self):
|
||||
d = self.val['d']
|
||||
el_type = self.val.type.template_argument(0)
|
||||
data_len = int(d['size'])
|
||||
|
||||
if data_len == 0:
|
||||
return []
|
||||
|
||||
data_char = d.reinterpret_cast(gdb.lookup_type('char').pointer()) + d['offset']
|
||||
data = data_char.reinterpret_cast(el_type.pointer())
|
||||
|
||||
return ArrayIter(data, data_len)
|
||||
|
||||
def to_string(self):
|
||||
# if we return an empty list from children, gdb doesn't print anything
|
||||
if self.val['d']['size'] == 0:
|
||||
return '<empty>'
|
||||
return None
|
||||
|
||||
def display_hint(self):
|
||||
return 'array'
|
||||
|
||||
class QUrlPrinter:
|
||||
"""Print a Qt5 QUrl"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
d = self.val['d']
|
||||
if not d:
|
||||
return '<empty>'
|
||||
|
||||
int_t = gdb.lookup_type('int')
|
||||
try:
|
||||
atomicint_t = gdb.lookup_type('QAtomicInt')
|
||||
except gdb.error:
|
||||
# let's hope it's the same size as an int
|
||||
atomicint_t = int_t
|
||||
qstring_t = gdb.lookup_type('QString')
|
||||
uchar_t = gdb.lookup_type('uchar')
|
||||
|
||||
reader = StructReader(d)
|
||||
|
||||
# These fields (including order) are unstable, and
|
||||
# may change between even patch-level Qt releases
|
||||
reader.next_val(atomicint_t)
|
||||
port = int(reader.next_val(int_t))
|
||||
scheme = reader.next_val(qstring_t)
|
||||
userName = reader.next_val(qstring_t)
|
||||
password = reader.next_val(qstring_t)
|
||||
host = reader.next_val(qstring_t)
|
||||
path = reader.next_val(qstring_t)
|
||||
query = reader.next_val(qstring_t)
|
||||
fragment = reader.next_val(qstring_t)
|
||||
reader.next_val(gdb.lookup_type('void').pointer())
|
||||
sections = int(reader.next_val(uchar_t))
|
||||
flags = int(reader.next_val(uchar_t))
|
||||
|
||||
# isLocalFile and no query and no fragment
|
||||
if flags & 0x01 and not (sections & 0x40) and not (sections & 0x80):
|
||||
# local file
|
||||
return path
|
||||
|
||||
def qs_to_s(qstring):
|
||||
return QStringPrinter(qstring).to_string()
|
||||
|
||||
# QUrl::toString() is way more complicated than what we do here,
|
||||
# but this is good enough for debugging
|
||||
result = ''
|
||||
if sections & 0x01:
|
||||
result += qs_to_s(scheme) + ':'
|
||||
if sections & (0x02 | 0x04 | 0x08 | 0x10) or flags & 0x01:
|
||||
result += '//'
|
||||
if sections & 0x02 or sections & 0x04:
|
||||
result += qs_to_s(userName)
|
||||
if sections & 0x04:
|
||||
# this may appear in backtraces that will be sent to other
|
||||
# people
|
||||
result += ':<omitted>'
|
||||
result += '@'
|
||||
if sections & 0x08:
|
||||
result += qs_to_s(host)
|
||||
if port != -1:
|
||||
result += ':' + str(port)
|
||||
result += qs_to_s(path)
|
||||
if sections & 0x40:
|
||||
result += '?' + qs_to_s(query)
|
||||
if sections & 0x80:
|
||||
result += '#' + qs_to_s(fragment)
|
||||
return result
|
||||
|
||||
def display_hint(self):
|
||||
return 'string'
|
||||
|
||||
|
||||
def build_pretty_printer():
|
||||
"""Builds the pretty printer for Qt5Core."""
|
||||
pp = gdb.printing.RegexpCollectionPrettyPrinter("Qt5Core")
|
||||
pp.add_printer('QBitArray', '^QBitArray$', QBitArrayPrinter)
|
||||
pp.add_printer('QByteArray', '^QByteArray$', QByteArrayPrinter)
|
||||
pp.add_printer('QChar', '^QChar$', QCharPrinter)
|
||||
pp.add_printer('QDate', '^QDate$', QDatePrinter)
|
||||
pp.add_printer('QDateTime', '^QDateTime$', QDateTimePrinter)
|
||||
pp.add_printer('QJsonArray', '^QJsonArray', QJsonArrayPrinter)
|
||||
pp.add_printer('QJsonObject', '^QJsonObject$', QJsonObjectPrinter)
|
||||
pp.add_printer('QLatin1String', '^QLatin1String$', QLatin1StringPrinter)
|
||||
pp.add_printer('QLinkedList', '^QLinkedList<.*>$', QLinkedListPrinter)
|
||||
pp.add_printer('QList', '^QList<.*>$', QListPrinter)
|
||||
pp.add_printer('QMap', '^QMap<.*>$', QMapPrinter)
|
||||
pp.add_printer('QHash', '^QHash<.*>$', QHashPrinter)
|
||||
pp.add_printer('QQueue', '^QQueue<.*>$', QListPrinter)
|
||||
pp.add_printer('QSet', '^QSet<.*>$', QSetPrinter)
|
||||
pp.add_printer('QStack', '^QStack<.*>$', QVectorPrinter)
|
||||
pp.add_printer('QString', '^QString$', QStringPrinter)
|
||||
pp.add_printer('QStringList', '^QStringList$', QListPrinter)
|
||||
pp.add_printer('QTime', '^QTime$', QTimePrinter)
|
||||
pp.add_printer('QTimeZone', '^QTimeZone$', QTimeZonePrinter)
|
||||
pp.add_printer('QVariant', '^QVariant$', QVariantPrinter)
|
||||
pp.add_printer('QVariantList', '^QVariantList$', QListPrinter)
|
||||
pp.add_printer('QVariantMap', '^QVariantMap$', QMapPrinter)
|
||||
pp.add_printer('QVector', '^QVector<.*>$', QVectorPrinter)
|
||||
pp.add_printer('QVarLengthArray', '^QVarLengthArray<.*>$', QVarLengthArrayPrinter)
|
||||
pp.add_printer('QUrl', '^QUrl$', QUrlPrinter)
|
||||
return pp
|
||||
|
||||
printer = build_pretty_printer()
|
||||
"""The pretty printer for Qt5Core.
|
||||
|
||||
This can be registered using gdb.printing.register_pretty_printer().
|
||||
"""
|
|
@ -0,0 +1,375 @@
|
|||
#############################################################################
|
||||
##
|
||||
## Copyright (C) 2014 Alex Merry <alex.merry@kde.org>
|
||||
## Contact: http://www.qt-project.org/legal
|
||||
##
|
||||
## This file is part of the GDB pretty printers for the Qt Toolkit.
|
||||
##
|
||||
## $QT_BEGIN_LICENSE:LGPL$
|
||||
## 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.
|
||||
##
|
||||
## GNU General Public License Usage
|
||||
## Alternatively, this file may be used under the terms of the GNU
|
||||
## General Public License version 3.0 as published by the Free Software
|
||||
## Foundation and appearing in the file LICENSE.GPL included in the
|
||||
## packaging of this file. Please review the following information to
|
||||
## ensure the GNU General Public License version 3.0 requirements will be
|
||||
## met: http://www.gnu.org/copyleft/gpl.html.
|
||||
##
|
||||
##
|
||||
## $QT_END_LICENSE$
|
||||
##
|
||||
#############################################################################
|
||||
|
||||
import gdb.printing
|
||||
|
||||
"""Qt5 Type Information
|
||||
|
||||
Since the QTypeInfo information is not necessarily available at debug time, this
|
||||
module contains useful type information about standard and Qt types (such as
|
||||
whether a type is movable) that is necessary for the operation of the printers.
|
||||
This information allows the QList printer, for example, to determine how the
|
||||
elements are stored in the list.
|
||||
"""
|
||||
|
||||
primitive_types = set([
|
||||
'HB_FixedPoint',
|
||||
'HB_GlyphAttributes',
|
||||
'QCharAttributes',
|
||||
'QFlag',
|
||||
'QIncompatibleFlag',
|
||||
'QRegExpAnchorAlternation',
|
||||
'QRegExpAtom',
|
||||
'QRegExpCharClassRange',
|
||||
'QStaticPlugin',
|
||||
'QStringRef',
|
||||
'QTzType',
|
||||
'QUuid'
|
||||
])
|
||||
"""Primitive (non-template) types.
|
||||
|
||||
This does not need to include compiler-primitive types (like int).
|
||||
|
||||
If you use the Q_DECLARE_TYPEINFO macro with Q_PRIMITIVE_TYPE flag, you
|
||||
should add the type to this set. This is particularly important for
|
||||
types that are the same size as a pointer or smaller.
|
||||
"""
|
||||
|
||||
primitive_tpl_types = set(['QFlags'])
|
||||
"""Primitive template types.
|
||||
|
||||
If you use the Q_DECLARE_TYPEINFO_BODY macro with Q_PRIMITIVE_TYPE flag
|
||||
on a type with template parameters, you should add the type to this
|
||||
set. This is particularly important for types that are the same size as
|
||||
a pointer or smaller.
|
||||
|
||||
Entries should just be the base typename, without any template
|
||||
parameters (eg: "QFlags", rather than "QFlags<T>").
|
||||
"""
|
||||
|
||||
movable_types = set([
|
||||
'QBasicTimer',
|
||||
'QBitArray',
|
||||
'QByteArray',
|
||||
'QChar',
|
||||
'QCharRef',
|
||||
'QCustomTypeInfo',
|
||||
'QDate',
|
||||
'QDateTime',
|
||||
'QFileInfo',
|
||||
'QEasingCurve',
|
||||
'QFileSystemWatcherPathKey',
|
||||
'QHashDummyValue',
|
||||
'QItemSelectionRange',
|
||||
'QLatin1String',
|
||||
'QLine',
|
||||
'QLineF',
|
||||
'QLocale',
|
||||
'QLoggingRule',
|
||||
'QMargins',
|
||||
'QMarginsF',
|
||||
'QMetaClassInfo',
|
||||
'QMetaEnum',
|
||||
'QMetaMethod',
|
||||
'QMimeMagicRule',
|
||||
'QModelIndex',
|
||||
'QPersistentModelIndex',
|
||||
'QObjectPrivate::Connection',
|
||||
'QObjectPrivate::Sender',
|
||||
'QPoint',
|
||||
'QPointF',
|
||||
'QPostEvent',
|
||||
'QProcEnvKey',
|
||||
'QProcEnvValue',
|
||||
'QRect',
|
||||
'QRectF',
|
||||
'QRegExp',
|
||||
'QRegExpAutomatonState',
|
||||
'QRegExpCharClass',
|
||||
'QResourceRoot',
|
||||
'QSize',
|
||||
'QSizeF',
|
||||
'QString',
|
||||
'QStringList',
|
||||
'QTime',
|
||||
'QTimeZone::OffsetData',
|
||||
'QUrl',
|
||||
'QVariant',
|
||||
'QXmlStreamAttribute',
|
||||
'QXmlStreamEntityDeclaration',
|
||||
'QXmlStreamNamespaceDeclaration',
|
||||
'QXmlStreamNotationDeclaration'
|
||||
])
|
||||
"""Movable (non-template) types.
|
||||
|
||||
If you use the Q_DECLARE_TYPEINFO macro with Q_MOVABLE_TYPE flag, you
|
||||
should add the type to this set. This is particularly important for
|
||||
types that are the same size as a pointer or smaller.
|
||||
"""
|
||||
|
||||
movable_tpl_types = set([
|
||||
'QExplicitlySharedDataPointer',
|
||||
'QLinkedList',
|
||||
'QList',
|
||||
'QPointer',
|
||||
'QQueue',
|
||||
'QSet',
|
||||
'QSharedDataPointer',
|
||||
'QSharedPointer',
|
||||
'QStack',
|
||||
'QVector',
|
||||
'QWeakPointer'
|
||||
])
|
||||
"""Movable template types.
|
||||
|
||||
If you use the Q_DECLARE_TYPEINFO_BODY macro with Q_MOVABLE_TYPE flag
|
||||
on a type with template parameters, you should add the type to this
|
||||
set. This is particularly important for types that are the same size as
|
||||
a pointer or smaller.
|
||||
|
||||
Entries should just be the base typename, without any template
|
||||
parameters (eg: "QFlags", rather than "QFlags<T>").
|
||||
"""
|
||||
|
||||
static_types = set()
|
||||
"""Static (non-template) types.
|
||||
|
||||
If you define a custom type that is neither primitive nor movable, you
|
||||
can add the type to this set to indicate this. This is particularly
|
||||
important for types that are the same size as a pointer or smaller.
|
||||
"""
|
||||
|
||||
static_tpl_types = set()
|
||||
"""Static template types.
|
||||
|
||||
If you define a custom type with template parameters that is neither
|
||||
primitive nor movable, you can add the type to this set to indicate
|
||||
this. This is particularly important for types that are the same size as
|
||||
a pointer or smaller.
|
||||
|
||||
Entries should just be the base typename, without any template
|
||||
parameters (eg: "QFlags", rather than "QFlags<T>").
|
||||
"""
|
||||
|
||||
def type_is_known_primitive(typ):
|
||||
"""Returns True if the given gdb type is known to be primitive."""
|
||||
if typ.code == gdb.TYPE_CODE_PTR or typ.code == gdb.TYPE_CODE_INT or typ.code == gdb.TYPE_CODE_FLT or typ.code == gdb.TYPE_CODE_CHAR or typ.code == gdb.TYPE_CODE_BOOL:
|
||||
return True
|
||||
pos = typ.name.find('<')
|
||||
if pos > 0:
|
||||
return typ.name[0:pos] in primitive_tpl_types
|
||||
else:
|
||||
return typ.name in primitive_types
|
||||
|
||||
def type_is_known_movable(typ):
|
||||
"""Returns True if the given gdb type is known to be movable."""
|
||||
if not typ.name:
|
||||
return False
|
||||
pos = typ.name.find('<')
|
||||
if pos > 0:
|
||||
return typ.name[0:pos] in movable_tpl_types
|
||||
else:
|
||||
return typ.name in movable_types
|
||||
|
||||
def type_is_known_static(typ):
|
||||
"""Returns True if the given gdb type is known to be neither primitive nor movable."""
|
||||
if not typ.name:
|
||||
return False
|
||||
pos = typ.name.find('<')
|
||||
if pos > 0:
|
||||
return typ.name[0:pos] in static_tpl_types
|
||||
else:
|
||||
return typ.name in static_types
|
||||
|
||||
meta_type_unknown = 0
|
||||
"""The unknown/invalid meta type ID."""
|
||||
meta_type_user = 1024
|
||||
"""The starting value for custom type IDs."""
|
||||
meta_type_ids = {
|
||||
'bool': 1,
|
||||
'int': 2,
|
||||
'uint': 3,
|
||||
'qlonglong': 4,
|
||||
'qulonglong': 5,
|
||||
'double': 6,
|
||||
'QChar': 7,
|
||||
'QVariantMap': 8,
|
||||
'QVariantList': 9,
|
||||
'QString': 10,
|
||||
'QStringList': 11,
|
||||
'QByteArray': 12,
|
||||
'QBitArray': 13,
|
||||
'QDate': 14,
|
||||
'QTime': 15,
|
||||
'QDateTime': 16,
|
||||
'QUrl': 17,
|
||||
'QLocale': 18,
|
||||
'QRect': 19,
|
||||
'QRectF': 20,
|
||||
'QSize': 21,
|
||||
'QSizeF': 22,
|
||||
'QLine': 23,
|
||||
'QLineF': 24,
|
||||
'QPoint': 25,
|
||||
'QPointF': 26,
|
||||
'QRegExp': 27,
|
||||
'QVariantHash': 28,
|
||||
'QEasingCurve': 29,
|
||||
'QUuid': 30,
|
||||
'void*': 31,
|
||||
'long': 32,
|
||||
'short': 33,
|
||||
'char': 34,
|
||||
'ulong': 35,
|
||||
'ushort': 36,
|
||||
'uchar': 37,
|
||||
'float': 38,
|
||||
'QObject*': 39,
|
||||
'signed char': 40,
|
||||
'QVariant': 41,
|
||||
'QModelIndex': 42,
|
||||
'void': 43,
|
||||
'QRegularExpression': 44,
|
||||
'QJsonValue': 45,
|
||||
'QJsonObject': 46,
|
||||
'QJsonArray': 47,
|
||||
'QJsonDocument': 48,
|
||||
'QFont': 64,
|
||||
'QPixmap': 65,
|
||||
'QBrush': 66,
|
||||
'QColor': 67,
|
||||
'QPalette': 68,
|
||||
'QIcon': 69,
|
||||
'QImage': 70,
|
||||
'QPolygon': 71,
|
||||
'QRegion': 72,
|
||||
'QBitmap': 73,
|
||||
'QCursor': 74,
|
||||
'QKeySequence': 75,
|
||||
'QPen': 76,
|
||||
'QTextLength': 77,
|
||||
'QTextFormat': 78,
|
||||
'QMatrix': 79,
|
||||
'QTransform': 80,
|
||||
'QMatrix4x4': 81,
|
||||
'QVector2D': 82,
|
||||
'QVector3D': 83,
|
||||
'QVector4D': 84,
|
||||
'QQuaternion': 85,
|
||||
'QPolygonF': 86,
|
||||
'QSizePolicy': 121
|
||||
}
|
||||
"""Map from type names to meta type IDs."""
|
||||
meta_type_names = {
|
||||
1: 'bool',
|
||||
2: 'int',
|
||||
3: 'uint',
|
||||
4: 'qlonglong',
|
||||
5: 'qulonglong',
|
||||
6: 'double',
|
||||
7: 'QChar',
|
||||
8: 'QVariantMap',
|
||||
9: 'QVariantList',
|
||||
10: 'QString',
|
||||
11: 'QStringList',
|
||||
12: 'QByteArray',
|
||||
13: 'QBitArray',
|
||||
14: 'QDate',
|
||||
15: 'QTime',
|
||||
16: 'QDateTime',
|
||||
17: 'QUrl',
|
||||
18: 'QLocale',
|
||||
19: 'QRect',
|
||||
20: 'QRectF',
|
||||
21: 'QSize',
|
||||
22: 'QSizeF',
|
||||
23: 'QLine',
|
||||
24: 'QLineF',
|
||||
25: 'QPoint',
|
||||
26: 'QPointF',
|
||||
27: 'QRegExp',
|
||||
28: 'QVariantHash',
|
||||
29: 'QEasingCurve',
|
||||
30: 'QUuid',
|
||||
31: 'void*',
|
||||
32: 'long',
|
||||
33: 'short',
|
||||
34: 'char',
|
||||
35: 'ulong',
|
||||
36: 'ushort',
|
||||
37: 'uchar',
|
||||
38: 'float',
|
||||
39: 'QObject*',
|
||||
40: 'signed char',
|
||||
41: 'QVariant',
|
||||
42: 'QModelIndex',
|
||||
43: 'void',
|
||||
44: 'QRegularExpression',
|
||||
45: 'QJsonValue',
|
||||
46: 'QJsonObject',
|
||||
47: 'QJsonArray',
|
||||
48: 'QJsonDocument',
|
||||
64: 'QFont',
|
||||
65: 'QPixmap',
|
||||
66: 'QBrush',
|
||||
67: 'QColor',
|
||||
68: 'QPalette',
|
||||
69: 'QIcon',
|
||||
70: 'QImage',
|
||||
71: 'QPolygon',
|
||||
72: 'QRegion',
|
||||
73: 'QBitmap',
|
||||
74: 'QCursor',
|
||||
75: 'QKeySequence',
|
||||
76: 'QPen',
|
||||
77: 'QTextLength',
|
||||
78: 'QTextFormat',
|
||||
79: 'QMatrix',
|
||||
80: 'QTransform',
|
||||
81: 'QMatrix4x4',
|
||||
82: 'QVector2D',
|
||||
83: 'QVector3D',
|
||||
84: 'QVector4D',
|
||||
85: 'QQuaternion',
|
||||
86: 'QPolygonF',
|
||||
121: 'QSizePolicy'
|
||||
}
|
||||
"""Map from meta type IDs to type names."""
|
Loading…
Reference in New Issue