2013-09-11 21:35:39 +02:00
|
|
|
############################################################################
|
|
|
|
#
|
2014-01-07 13:27:11 +01:00
|
|
|
# Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
2013-09-11 21:35:39 +02:00
|
|
|
# Contact: http://www.qt-project.org/legal
|
|
|
|
#
|
|
|
|
# This file is part of Qt Creator.
|
|
|
|
#
|
|
|
|
# Commercial License Usage
|
|
|
|
# Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
# accordance with the commercial license agreement provided with the
|
|
|
|
# Software or, alternatively, in accordance with the terms contained in
|
|
|
|
# a written agreement between you and Digia. For licensing terms and
|
|
|
|
# conditions see http://qt.digia.com/licensing. For further information
|
|
|
|
# use the contact form at http://qt.digia.com/contact-us.
|
|
|
|
#
|
|
|
|
# GNU Lesser General Public License Usage
|
|
|
|
# Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
# General Public License version 2.1 as published by the Free Software
|
|
|
|
# Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
# packaging of this file. Please review the following information to
|
|
|
|
# ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
# will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
#
|
|
|
|
# In addition, as a special exception, Digia gives you certain additional
|
|
|
|
# rights. These rights are described in the Digia Qt LGPL Exception
|
|
|
|
# version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
#
|
|
|
|
############################################################################
|
|
|
|
|
|
|
|
import os
|
2014-01-24 15:13:20 +01:00
|
|
|
import struct
|
2013-09-11 21:35:39 +02:00
|
|
|
import sys
|
|
|
|
import base64
|
|
|
|
|
|
|
|
if sys.version_info[0] >= 3:
|
|
|
|
xrange = range
|
|
|
|
toInteger = int
|
|
|
|
else:
|
|
|
|
toInteger = long
|
|
|
|
|
|
|
|
|
|
|
|
verbosity = 0
|
|
|
|
verbosity = 1
|
|
|
|
|
|
|
|
def hasPlot():
|
|
|
|
fileName = "/usr/bin/gnuplot"
|
|
|
|
return os.path.isfile(fileName) and os.access(fileName, os.X_OK)
|
|
|
|
|
2013-09-24 14:39:41 +02:00
|
|
|
try:
|
|
|
|
import subprocess
|
|
|
|
def arrayForms():
|
|
|
|
if hasPlot():
|
|
|
|
return "Normal,Plot"
|
|
|
|
return "Normal"
|
|
|
|
except:
|
|
|
|
def arrayForms():
|
|
|
|
return "Normal"
|
2013-09-11 21:35:39 +02:00
|
|
|
|
|
|
|
|
2014-01-23 15:30:51 +01:00
|
|
|
class Blob(object):
|
|
|
|
"""
|
|
|
|
Helper structure to keep a blob of bytes, possibly
|
|
|
|
in the inferior.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, data, isComplete = True):
|
|
|
|
self.data = data
|
|
|
|
self.size = len(data)
|
|
|
|
self.isComplete = isComplete
|
|
|
|
|
|
|
|
def size(self):
|
|
|
|
return self.size
|
|
|
|
|
2014-01-24 15:13:20 +01:00
|
|
|
def toBytes(self):
|
|
|
|
"""Retrieves "lazy" contents from memoryviews."""
|
|
|
|
data = self.data
|
|
|
|
if isinstance(data, memoryview):
|
|
|
|
data = data.tobytes()
|
|
|
|
if sys.version_info[0] == 2 and isinstance(data, buffer):
|
|
|
|
data = ''.join([c for c in data])
|
|
|
|
return data
|
|
|
|
|
2014-01-24 17:41:07 +01:00
|
|
|
def toString(self):
|
|
|
|
data = self.toBytes()
|
|
|
|
return data if sys.version_info[0] == 2 else data.decode("utf8")
|
|
|
|
|
2014-01-24 15:13:20 +01:00
|
|
|
def extractByte(self, offset = 0):
|
|
|
|
return struct.unpack_from("b", self.data, offset)[0]
|
|
|
|
|
|
|
|
def extractShort(self, offset = 0):
|
|
|
|
return struct.unpack_from("h", self.data, offset)[0]
|
|
|
|
|
|
|
|
def extractUShort(self, offset = 0):
|
|
|
|
return struct.unpack_from("H", self.data, offset)[0]
|
|
|
|
|
|
|
|
def extractInt(self, offset = 0):
|
|
|
|
return struct.unpack_from("i", self.data, offset)[0]
|
|
|
|
|
|
|
|
def extractUInt(self, offset = 0):
|
|
|
|
return struct.unpack_from("I", self.data, offset)[0]
|
2013-09-11 21:35:39 +02:00
|
|
|
|
2014-01-24 15:13:20 +01:00
|
|
|
def extractLong(self, offset = 0):
|
|
|
|
return struct.unpack_from("l", self.data, offset)[0]
|
2013-09-11 21:35:39 +02:00
|
|
|
|
2014-01-24 15:13:20 +01:00
|
|
|
# FIXME: Note these should take target architecture into account.
|
|
|
|
def extractULong(self, offset = 0):
|
|
|
|
return struct.unpack_from("L", self.data, offset)[0]
|
|
|
|
|
|
|
|
def extractInt64(self, offset = 0):
|
|
|
|
return struct.unpack_from("q", self.data, offset)[0]
|
|
|
|
|
|
|
|
def extractUInt64(self, offset = 0):
|
|
|
|
return struct.unpack_from("Q", self.data, offset)[0]
|
|
|
|
|
|
|
|
def extractDouble(self, offset = 0):
|
|
|
|
return struct.unpack_from("d", self.data, offset)[0]
|
|
|
|
|
|
|
|
def extractFloat(self, offset = 0):
|
|
|
|
return struct.unpack_from("f", self.data, offset)[0]
|
|
|
|
|
2013-09-11 21:35:39 +02:00
|
|
|
#
|
|
|
|
# Gnuplot based display for array-like structures.
|
|
|
|
#
|
|
|
|
gnuplotPipe = {}
|
|
|
|
gnuplotPid = {}
|
|
|
|
|
|
|
|
def warn(message):
|
|
|
|
print("XXX: %s\n" % message.encode("latin1"))
|
|
|
|
|
|
|
|
|
|
|
|
def showException(msg, exType, exValue, exTraceback):
|
|
|
|
warn("**** CAUGHT EXCEPTION: %s ****" % msg)
|
|
|
|
try:
|
|
|
|
import traceback
|
|
|
|
for line in traceback.format_exception(exType, exValue, exTraceback):
|
|
|
|
warn("%s" % line)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def stripClassTag(typeName):
|
|
|
|
if typeName.startswith("class "):
|
|
|
|
return typeName[6:]
|
|
|
|
if typeName.startswith("struct "):
|
|
|
|
return typeName[7:]
|
|
|
|
if typeName.startswith("const "):
|
|
|
|
return typeName[6:]
|
|
|
|
if typeName.startswith("volatile "):
|
|
|
|
return typeName[9:]
|
|
|
|
return typeName
|
|
|
|
|
|
|
|
|
|
|
|
class Children:
|
|
|
|
def __init__(self, d, numChild = 1, childType = None, childNumChild = None,
|
|
|
|
maxNumChild = None, addrBase = None, addrStep = None):
|
|
|
|
self.d = d
|
|
|
|
self.numChild = numChild
|
|
|
|
self.childNumChild = childNumChild
|
|
|
|
self.maxNumChild = maxNumChild
|
|
|
|
self.addrBase = addrBase
|
|
|
|
self.addrStep = addrStep
|
|
|
|
self.printsAddress = True
|
|
|
|
if childType is None:
|
|
|
|
self.childType = None
|
|
|
|
else:
|
|
|
|
self.childType = stripClassTag(str(childType))
|
|
|
|
self.d.put('childtype="%s",' % self.childType)
|
|
|
|
if childNumChild is None:
|
|
|
|
pass
|
|
|
|
#if self.d.isSimpleType(childType):
|
|
|
|
# self.d.put('childnumchild="0",')
|
|
|
|
# self.childNumChild = 0
|
|
|
|
#elif childType.code == PointerCode:
|
|
|
|
# self.d.put('childnumchild="1",')
|
|
|
|
# self.childNumChild = 1
|
|
|
|
else:
|
|
|
|
self.d.put('childnumchild="%s",' % childNumChild)
|
|
|
|
self.childNumChild = childNumChild
|
|
|
|
try:
|
|
|
|
if not addrBase is None and not addrStep is None:
|
2013-10-23 12:29:38 +02:00
|
|
|
self.d.put('addrbase="0x%x",' % toInteger(addrBase))
|
|
|
|
self.d.put('addrstep="0x%x",' % toInteger(addrStep))
|
2013-09-11 21:35:39 +02:00
|
|
|
self.printsAddress = False
|
|
|
|
except:
|
|
|
|
warn("ADDRBASE: %s" % addrBase)
|
2013-10-23 12:29:38 +02:00
|
|
|
warn("ADDRSTEP: %s" % addrStep)
|
2013-09-11 21:35:39 +02:00
|
|
|
#warn("CHILDREN: %s %s %s" % (numChild, childType, childNumChild))
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
self.savedChildType = self.d.currentChildType
|
|
|
|
self.savedChildNumChild = self.d.currentChildNumChild
|
|
|
|
self.savedNumChild = self.d.currentNumChild
|
|
|
|
self.savedMaxNumChild = self.d.currentMaxNumChild
|
|
|
|
self.savedPrintsAddress = self.d.currentPrintsAddress
|
|
|
|
self.d.currentChildType = self.childType
|
|
|
|
self.d.currentChildNumChild = self.childNumChild
|
|
|
|
self.d.currentNumChild = self.numChild
|
|
|
|
self.d.currentMaxNumChild = self.maxNumChild
|
|
|
|
self.d.currentPrintsAddress = self.printsAddress
|
|
|
|
self.d.put("children=[")
|
|
|
|
|
|
|
|
def __exit__(self, exType, exValue, exTraceBack):
|
|
|
|
if not exType is None:
|
|
|
|
if self.d.passExceptions:
|
|
|
|
showException("CHILDREN", exType, exValue, exTraceBack)
|
|
|
|
self.d.putNumChild(0)
|
|
|
|
self.d.putValue("<not accessible>")
|
|
|
|
if not self.d.currentMaxNumChild is None:
|
|
|
|
if self.d.currentMaxNumChild < self.d.currentNumChild:
|
|
|
|
self.d.put('{name="<incomplete>",value="",type="",numchild="0"},')
|
|
|
|
self.d.currentChildType = self.savedChildType
|
|
|
|
self.d.currentChildNumChild = self.savedChildNumChild
|
|
|
|
self.d.currentNumChild = self.savedNumChild
|
|
|
|
self.d.currentMaxNumChild = self.savedMaxNumChild
|
|
|
|
self.d.currentPrintsAddress = self.savedPrintsAddress
|
|
|
|
self.d.put('],')
|
|
|
|
return True
|
|
|
|
|
2014-01-13 18:23:22 +01:00
|
|
|
class PairedChildrenData:
|
2014-01-15 15:45:48 +01:00
|
|
|
def __init__(self, d, pairType, keyType, valueType, useKeyAndValue):
|
|
|
|
self.useKeyAndValue = useKeyAndValue
|
2014-01-13 18:23:22 +01:00
|
|
|
self.pairType = pairType
|
2014-01-15 14:56:27 +01:00
|
|
|
self.keyType = keyType
|
|
|
|
self.valueType = valueType
|
2014-01-13 18:23:22 +01:00
|
|
|
self.isCompact = d.isMapCompact(self.keyType, self.valueType)
|
2014-01-15 14:56:27 +01:00
|
|
|
self.childType = valueType if self.isCompact else pairType
|
2014-01-13 18:23:22 +01:00
|
|
|
ns = d.qtNamespace()
|
|
|
|
self.keyIsQString = str(self.keyType) == ns + "QString"
|
|
|
|
self.keyIsQByteArray = str(self.keyType) == ns + "QByteArray"
|
|
|
|
|
2014-01-14 18:24:55 +01:00
|
|
|
class PairedChildren(Children):
|
2014-01-15 15:45:48 +01:00
|
|
|
def __init__(self, d, numChild, useKeyAndValue = False,
|
|
|
|
pairType = None, keyType = None, valueType = None, maxNumChild = None):
|
2014-01-13 18:23:22 +01:00
|
|
|
self.d = d
|
2014-01-15 14:56:27 +01:00
|
|
|
if keyType is None:
|
|
|
|
keyType = d.templateArgument(pairType, 0).unqualified()
|
|
|
|
if valueType is None:
|
|
|
|
valueType = d.templateArgument(pairType, 1)
|
2014-01-15 15:45:48 +01:00
|
|
|
d.pairData = PairedChildrenData(d, pairType, keyType, valueType, useKeyAndValue)
|
2014-01-15 14:56:27 +01:00
|
|
|
|
2014-01-13 18:23:22 +01:00
|
|
|
Children.__init__(self, d, numChild,
|
2014-01-15 14:56:27 +01:00
|
|
|
d.pairData.childType,
|
|
|
|
maxNumChild = maxNumChild,
|
|
|
|
addrBase = None, addrStep = None)
|
2014-01-13 18:23:22 +01:00
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
self.savedPairData = self.d.pairData if hasattr(self.d, "pairData") else None
|
|
|
|
Children.__enter__(self)
|
|
|
|
|
|
|
|
def __exit__(self, exType, exValue, exTraceBack):
|
|
|
|
Children.__exit__(self, exType, exValue, exTraceBack)
|
|
|
|
self.d.pairData = self.savedPairData if self.savedPairData else None
|
|
|
|
|
2013-09-11 21:35:39 +02:00
|
|
|
|
|
|
|
class SubItem:
|
|
|
|
def __init__(self, d, component):
|
|
|
|
self.d = d
|
|
|
|
self.name = component
|
|
|
|
self.iname = None
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
self.d.enterSubItem(self)
|
|
|
|
|
|
|
|
def __exit__(self, exType, exValue, exTraceBack):
|
|
|
|
return self.d.exitSubItem(self, exType, exValue, exTraceBack)
|
|
|
|
|
|
|
|
class NoAddress:
|
|
|
|
def __init__(self, d):
|
|
|
|
self.d = d
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
self.savedPrintsAddress = self.d.currentPrintsAddress
|
|
|
|
self.d.currentPrintsAddress = False
|
|
|
|
|
|
|
|
def __exit__(self, exType, exValue, exTraceBack):
|
|
|
|
self.d.currentPrintsAddress = self.savedPrintsAddress
|
|
|
|
|
|
|
|
class TopLevelItem(SubItem):
|
|
|
|
def __init__(self, d, iname):
|
|
|
|
self.d = d
|
|
|
|
self.iname = iname
|
|
|
|
self.name = None
|
|
|
|
|
|
|
|
class UnnamedSubItem(SubItem):
|
|
|
|
def __init__(self, d, component):
|
|
|
|
self.d = d
|
|
|
|
self.iname = "%s.%s" % (self.d.currentIName, component)
|
|
|
|
self.name = None
|
|
|
|
|
|
|
|
class DumperBase:
|
|
|
|
def __init__(self):
|
|
|
|
self.isCdb = False
|
|
|
|
self.isGdb = False
|
|
|
|
self.isLldb = False
|
|
|
|
|
2013-10-30 15:07:54 +01:00
|
|
|
# Later set, or not set:
|
|
|
|
# cachedQtVersion
|
|
|
|
self.stringCutOff = 10000
|
|
|
|
|
|
|
|
# This is a cache mapping from 'type name' to 'display alternatives'.
|
|
|
|
self.qqFormats = {}
|
|
|
|
|
|
|
|
# This is a cache of all known dumpers.
|
|
|
|
self.qqDumpers = {}
|
|
|
|
|
|
|
|
# This is a cache of all dumpers that support writing.
|
|
|
|
self.qqEditable = {}
|
|
|
|
|
|
|
|
# This keeps canonical forms of the typenames, without array indices etc.
|
|
|
|
self.cachedFormats = {}
|
|
|
|
|
2014-01-28 23:23:01 +01:00
|
|
|
# Maps type names to static metaobjects. If a type is known
|
|
|
|
# to not be QObject derived, it contains a 0 value.
|
|
|
|
self.knownStaticMetaObjects = {}
|
2014-01-20 15:03:27 +01:00
|
|
|
|
2013-10-30 15:07:54 +01:00
|
|
|
|
|
|
|
def stripForFormat(self, typeName):
|
|
|
|
if typeName in self.cachedFormats:
|
|
|
|
return self.cachedFormats[typeName]
|
|
|
|
stripped = ""
|
|
|
|
inArray = 0
|
|
|
|
for c in stripClassTag(typeName):
|
|
|
|
if c == '<':
|
|
|
|
break
|
|
|
|
if c == ' ':
|
|
|
|
continue
|
|
|
|
if c == '[':
|
|
|
|
inArray += 1
|
|
|
|
elif c == ']':
|
|
|
|
inArray -= 1
|
|
|
|
if inArray and ord(c) >= 48 and ord(c) <= 57:
|
|
|
|
continue
|
|
|
|
stripped += c
|
|
|
|
self.cachedFormats[typeName] = stripped
|
|
|
|
return stripped
|
|
|
|
|
2014-01-24 15:13:20 +01:00
|
|
|
# Hex decoding operating on str, return str.
|
2014-01-23 15:30:51 +01:00
|
|
|
def hexdecode(self, s):
|
|
|
|
if sys.version_info[0] == 2:
|
|
|
|
return s.decode("hex")
|
|
|
|
return bytes.fromhex(s).decode("utf8")
|
|
|
|
|
2014-01-24 15:13:20 +01:00
|
|
|
# Hex decoding operating on str or bytes, return str.
|
2014-01-23 15:30:51 +01:00
|
|
|
def hexencode(self, s):
|
|
|
|
if sys.version_info[0] == 2:
|
|
|
|
return s.encode("hex")
|
2014-01-24 15:13:20 +01:00
|
|
|
if isinstance(s, str):
|
|
|
|
s = s.encode("utf8")
|
|
|
|
return base64.b16encode(s).decode("utf8")
|
|
|
|
|
|
|
|
#def toBlob(self, value):
|
2014-01-24 17:02:23 +01:00
|
|
|
# """Abstract"""
|
2014-01-23 15:30:51 +01:00
|
|
|
|
2013-11-11 09:54:54 +01:00
|
|
|
def isArmArchitecture(self):
|
|
|
|
return False
|
|
|
|
|
|
|
|
def isQnxTarget(self):
|
|
|
|
return False
|
2013-10-30 15:07:54 +01:00
|
|
|
|
2013-10-27 10:47:46 +01:00
|
|
|
def is32bit(self):
|
|
|
|
return self.ptrSize() == 4
|
|
|
|
|
2014-03-17 17:48:46 +01:00
|
|
|
def is64bit(self):
|
|
|
|
return self.ptrSize() == 8
|
|
|
|
|
2013-11-11 09:54:54 +01:00
|
|
|
def isQt3Support(self):
|
|
|
|
# assume no Qt 3 support by default
|
|
|
|
return False
|
|
|
|
|
2013-09-11 21:35:39 +02:00
|
|
|
def computeLimit(self, size, limit):
|
|
|
|
if limit is None:
|
|
|
|
return size
|
|
|
|
if limit == 0:
|
2013-10-30 15:07:54 +01:00
|
|
|
return min(size, self.stringCutOff)
|
2013-09-11 21:35:39 +02:00
|
|
|
return min(size, limit)
|
|
|
|
|
|
|
|
def byteArrayDataHelper(self, addr):
|
|
|
|
if self.qtVersion() >= 0x050000:
|
|
|
|
# QTypedArray:
|
|
|
|
# - QtPrivate::RefCount ref
|
|
|
|
# - int size
|
|
|
|
# - uint alloc : 31, capacityReserved : 1
|
|
|
|
# - qptrdiff offset
|
|
|
|
size = self.extractInt(addr + 4)
|
|
|
|
alloc = self.extractInt(addr + 8) & 0x7ffffff
|
2014-01-28 13:20:05 +01:00
|
|
|
data = addr + self.extractPointer(addr + 8 + self.ptrSize())
|
2013-09-26 23:38:17 +02:00
|
|
|
if self.ptrSize() == 4:
|
|
|
|
data = data & 0xffffffff
|
|
|
|
else:
|
|
|
|
data = data & 0xffffffffffffffff
|
2013-09-11 21:35:39 +02:00
|
|
|
else:
|
|
|
|
# Data:
|
|
|
|
# - QBasicAtomicInt ref;
|
|
|
|
# - int alloc, size;
|
|
|
|
# - [padding]
|
|
|
|
# - char *data;
|
|
|
|
alloc = self.extractInt(addr + 4)
|
|
|
|
size = self.extractInt(addr + 8)
|
2014-01-28 13:20:05 +01:00
|
|
|
data = self.extractPointer(addr + 8 + self.ptrSize())
|
2013-09-11 21:35:39 +02:00
|
|
|
return data, size, alloc
|
|
|
|
|
|
|
|
# addr is the begin of a QByteArrayData structure
|
|
|
|
def encodeStringHelper(self, addr, limit = 0):
|
|
|
|
# Should not happen, but we get it with LLDB as result
|
|
|
|
# of inferior calls
|
|
|
|
if addr == 0:
|
|
|
|
return ""
|
|
|
|
data, size, alloc = self.byteArrayDataHelper(addr)
|
|
|
|
if alloc != 0:
|
|
|
|
self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
|
|
|
|
limit = self.computeLimit(size, limit)
|
|
|
|
s = self.readMemory(data, 2 * limit)
|
|
|
|
if limit < size:
|
|
|
|
s += "2e002e002e00"
|
|
|
|
return s
|
|
|
|
|
|
|
|
def encodeByteArrayHelper(self, addr, limit = None):
|
|
|
|
data, size, alloc = self.byteArrayDataHelper(addr)
|
|
|
|
if alloc != 0:
|
|
|
|
self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
|
|
|
|
limit = self.computeLimit(size, limit)
|
|
|
|
s = self.readMemory(data, limit)
|
|
|
|
if limit < size:
|
|
|
|
s += "2e2e2e"
|
|
|
|
return s
|
|
|
|
|
2014-01-23 15:30:51 +01:00
|
|
|
def readMemory(self, addr, size):
|
2014-01-24 15:13:20 +01:00
|
|
|
data = self.extractBlob(addr, size).toBytes()
|
|
|
|
return self.hexencode(data)
|
2014-01-23 15:30:51 +01:00
|
|
|
|
2013-09-11 21:35:39 +02:00
|
|
|
def encodeByteArray(self, value):
|
2014-01-28 13:20:05 +01:00
|
|
|
return self.encodeByteArrayHelper(self.extractPointer(value))
|
2013-09-11 21:35:39 +02:00
|
|
|
|
|
|
|
def byteArrayData(self, value):
|
2014-01-28 13:20:05 +01:00
|
|
|
return self.byteArrayDataHelper(self.extractPointer(value))
|
2013-09-11 21:35:39 +02:00
|
|
|
|
|
|
|
def putByteArrayValue(self, value):
|
|
|
|
return self.putValue(self.encodeByteArray(value), Hex2EncodedLatin1)
|
|
|
|
|
2013-11-30 23:41:05 +01:00
|
|
|
def putByteArrayValueByAddress(self, addr):
|
2014-01-28 13:20:05 +01:00
|
|
|
self.putValue(self.encodeByteArrayHelper(self.extractPointer(addr)),
|
2013-11-30 23:41:05 +01:00
|
|
|
Hex2EncodedLatin1)
|
|
|
|
|
2013-09-11 21:35:39 +02:00
|
|
|
def putStringValueByAddress(self, addr):
|
2014-01-28 13:20:05 +01:00
|
|
|
self.putValue(self.encodeStringHelper(self.extractPointer(addr)),
|
2013-09-11 21:35:39 +02:00
|
|
|
Hex4EncodedLittleEndian)
|
|
|
|
|
|
|
|
def encodeString(self, value):
|
2014-01-28 13:20:05 +01:00
|
|
|
return self.encodeStringHelper(self.extractPointer(value))
|
2013-09-11 21:35:39 +02:00
|
|
|
|
|
|
|
def stringData(self, value):
|
2014-01-28 13:20:05 +01:00
|
|
|
return self.byteArrayDataHelper(self.extractPointer(value))
|
2013-09-11 21:35:39 +02:00
|
|
|
|
2013-10-28 11:35:45 +01:00
|
|
|
def extractTemplateArgument(self, typename, position):
|
|
|
|
level = 0
|
|
|
|
skipSpace = False
|
|
|
|
inner = ''
|
|
|
|
for c in typename[typename.find('<') + 1 : -1]:
|
|
|
|
if c == '<':
|
|
|
|
inner += c
|
|
|
|
level += 1
|
|
|
|
elif c == '>':
|
|
|
|
level -= 1
|
|
|
|
inner += c
|
|
|
|
elif c == ',':
|
|
|
|
if level == 0:
|
|
|
|
if position == 0:
|
|
|
|
return inner.strip()
|
|
|
|
position -= 1
|
|
|
|
inner = ''
|
|
|
|
else:
|
|
|
|
inner += c
|
|
|
|
skipSpace = True
|
|
|
|
else:
|
|
|
|
if skipSpace and c == ' ':
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
inner += c
|
|
|
|
skipSpace = False
|
|
|
|
return inner.strip()
|
|
|
|
|
2013-09-11 21:35:39 +02:00
|
|
|
def putStringValue(self, value):
|
|
|
|
return self.putValue(self.encodeString(value), Hex4EncodedLittleEndian)
|
|
|
|
|
2013-11-01 12:49:14 +01:00
|
|
|
def putAddressItem(self, name, value, type = ""):
|
|
|
|
with SubItem(self, name):
|
|
|
|
self.putValue("0x%x" % value)
|
|
|
|
self.putType(type)
|
|
|
|
self.putNumChild(0)
|
|
|
|
|
|
|
|
def putIntItem(self, name, value):
|
|
|
|
with SubItem(self, name):
|
|
|
|
self.putValue(value)
|
|
|
|
self.putType("int")
|
|
|
|
self.putNumChild(0)
|
|
|
|
|
|
|
|
def putBoolItem(self, name, value):
|
|
|
|
with SubItem(self, name):
|
|
|
|
self.putValue(value)
|
|
|
|
self.putType("bool")
|
|
|
|
self.putNumChild(0)
|
|
|
|
|
|
|
|
def putGenericItem(self, name, type, value, encoding = None):
|
|
|
|
with SubItem(self, name):
|
|
|
|
self.putValue(value, encoding)
|
|
|
|
self.putType(type)
|
|
|
|
self.putNumChild(0)
|
|
|
|
|
|
|
|
|
2014-01-13 15:32:01 +01:00
|
|
|
def putMapName(self, value, index = -1):
|
2013-10-30 15:07:54 +01:00
|
|
|
ns = self.qtNamespace()
|
|
|
|
if str(value.type) == ns + "QString":
|
2013-09-11 21:35:39 +02:00
|
|
|
self.put('key="%s",' % self.encodeString(value))
|
|
|
|
self.put('keyencoded="%s",' % Hex4EncodedLittleEndian)
|
2013-10-30 15:07:54 +01:00
|
|
|
elif str(value.type) == ns + "QByteArray":
|
2013-09-11 21:35:39 +02:00
|
|
|
self.put('key="%s",' % self.encodeByteArray(value))
|
|
|
|
self.put('keyencoded="%s",' % Hex2EncodedLatin1)
|
|
|
|
else:
|
2014-01-13 15:32:01 +01:00
|
|
|
val = str(value.GetValue()) if self.isLldb else str(value)
|
|
|
|
if index == -1:
|
|
|
|
self.put('name="%s",' % val)
|
2013-09-11 21:35:39 +02:00
|
|
|
else:
|
2014-01-13 15:32:01 +01:00
|
|
|
self.put('key="[%d] %s",' % (index, val))
|
2013-09-11 21:35:39 +02:00
|
|
|
|
2014-01-13 18:23:22 +01:00
|
|
|
def putPair(self, pair, index = -1):
|
2014-01-15 15:45:48 +01:00
|
|
|
if self.pairData.useKeyAndValue:
|
2014-01-15 14:56:27 +01:00
|
|
|
key = pair["key"]
|
|
|
|
value = pair["value"]
|
2014-01-15 15:45:48 +01:00
|
|
|
else:
|
|
|
|
key = pair["first"]
|
|
|
|
value = pair["second"]
|
2014-01-13 18:23:22 +01:00
|
|
|
if self.pairData.isCompact:
|
|
|
|
if self.pairData.keyIsQString:
|
|
|
|
self.put('key="%s",' % self.encodeString(key))
|
|
|
|
self.put('keyencoded="%s",' % Hex4EncodedLittleEndian)
|
|
|
|
elif self.pairData.keyIsQByteArray:
|
|
|
|
self.put('key="%s",' % self.encodeByteArray(key))
|
|
|
|
self.put('keyencoded="%s",' % Hex2EncodedLatin1)
|
|
|
|
else:
|
|
|
|
name = str(key.GetValue()) if self.isLldb else str(key)
|
|
|
|
if index == -1:
|
|
|
|
self.put('name="%s",' % name)
|
|
|
|
else:
|
|
|
|
self.put('key="[%d] %s",' % (index, name))
|
|
|
|
self.putItem(value)
|
|
|
|
else:
|
|
|
|
self.putEmptyValue()
|
2014-01-15 14:56:27 +01:00
|
|
|
self.putNumChild(2)
|
|
|
|
self.putField("iname", self.currentIName)
|
2014-01-13 18:23:22 +01:00
|
|
|
if self.isExpanded():
|
2014-01-15 14:56:27 +01:00
|
|
|
with Children(self):
|
2014-01-15 15:45:48 +01:00
|
|
|
if self.pairData.useKeyAndValue:
|
|
|
|
self.putSubItem("key", key)
|
|
|
|
self.putSubItem("value", value)
|
|
|
|
else:
|
|
|
|
self.putSubItem("first", key)
|
|
|
|
self.putSubItem("second", value)
|
2014-01-13 18:23:22 +01:00
|
|
|
|
2013-09-11 21:35:39 +02:00
|
|
|
def isMapCompact(self, keyType, valueType):
|
|
|
|
format = self.currentItemFormat()
|
|
|
|
if format == 2:
|
|
|
|
return True # Compact.
|
|
|
|
return self.isSimpleType(keyType) and self.isSimpleType(valueType)
|
|
|
|
|
|
|
|
|
|
|
|
def check(self, exp):
|
|
|
|
if not exp:
|
|
|
|
raise RuntimeError("Check failed")
|
|
|
|
|
|
|
|
def checkRef(self, ref):
|
|
|
|
try:
|
|
|
|
count = int(ref["atomic"]["_q_value"]) # Qt 5.
|
|
|
|
minimum = -1
|
|
|
|
except:
|
|
|
|
count = int(ref["_q_value"]) # Qt 4.
|
|
|
|
minimum = 0
|
|
|
|
# Assume there aren't a million references to any object.
|
|
|
|
self.check(count >= minimum)
|
|
|
|
self.check(count < 1000000)
|
|
|
|
|
2013-10-16 18:39:01 +02:00
|
|
|
def findFirstZero(self, p, maximum):
|
|
|
|
for i in xrange(maximum):
|
|
|
|
if int(p.dereference()) == 0:
|
|
|
|
return i
|
|
|
|
p = p + 1
|
|
|
|
return maximum + 1
|
|
|
|
|
|
|
|
def encodeCArray(self, p, innerType, suffix):
|
|
|
|
t = self.lookupType(innerType)
|
|
|
|
p = p.cast(t.pointer())
|
2013-10-30 15:07:54 +01:00
|
|
|
limit = self.findFirstZero(p, self.stringCutOff)
|
2013-10-16 18:39:01 +02:00
|
|
|
s = self.readMemory(p, limit * t.sizeof)
|
2013-10-30 15:07:54 +01:00
|
|
|
if limit > self.stringCutOff:
|
2013-10-16 18:39:01 +02:00
|
|
|
s += suffix
|
|
|
|
return s
|
|
|
|
|
|
|
|
def encodeCharArray(self, p):
|
|
|
|
return self.encodeCArray(p, "unsigned char", "2e2e2e")
|
|
|
|
|
|
|
|
def encodeChar2Array(self, p):
|
|
|
|
return self.encodeCArray(p, "unsigned short", "2e002e002e00")
|
|
|
|
|
|
|
|
def encodeChar4Array(self, p):
|
|
|
|
return self.encodeCArray(p, "unsigned int", "2e0000002e0000002e000000")
|
|
|
|
|
2013-11-01 12:49:14 +01:00
|
|
|
def putItemCount(self, count, maximum = 1000000000):
|
|
|
|
# This needs to override the default value, so don't use 'put' directly.
|
|
|
|
if count > maximum:
|
|
|
|
self.putValue('<>%s items>' % maximum)
|
|
|
|
else:
|
|
|
|
self.putValue('<%s items>' % count)
|
|
|
|
|
2013-11-07 13:04:09 +01:00
|
|
|
def putField(self, name, value):
|
|
|
|
self.put('%s="%s",' % (name, value))
|
|
|
|
|
|
|
|
def putType(self, type, priority = 0):
|
|
|
|
# Higher priority values override lower ones.
|
|
|
|
if priority >= self.currentTypePriority:
|
|
|
|
self.currentType = str(type)
|
|
|
|
self.currentTypePriority = priority
|
|
|
|
|
|
|
|
def putValue(self, value, encoding = None, priority = 0):
|
|
|
|
# Higher priority values override lower ones.
|
|
|
|
if priority >= self.currentValuePriority:
|
|
|
|
self.currentValue = value
|
|
|
|
self.currentValuePriority = priority
|
|
|
|
self.currentValueEncoding = encoding
|
|
|
|
|
|
|
|
def putEmptyValue(self, priority = -10):
|
|
|
|
if priority >= self.currentValuePriority:
|
|
|
|
self.currentValue = ""
|
|
|
|
self.currentValuePriority = priority
|
|
|
|
self.currentValueEncoding = None
|
|
|
|
|
|
|
|
def putName(self, name):
|
|
|
|
self.put('name="%s",' % name)
|
|
|
|
|
2013-11-01 12:49:14 +01:00
|
|
|
def putNoType(self):
|
|
|
|
# FIXME: replace with something that does not need special handling
|
|
|
|
# in SubItem.__exit__().
|
|
|
|
self.putBetterType(" ")
|
|
|
|
|
|
|
|
def putInaccessible(self):
|
|
|
|
#self.putBetterType(" ")
|
|
|
|
self.putNumChild(0)
|
|
|
|
self.currentValue = None
|
|
|
|
|
2013-11-07 13:04:09 +01:00
|
|
|
def putNamedSubItem(self, component, value, name):
|
|
|
|
with SubItem(self, component):
|
|
|
|
self.putName(name)
|
|
|
|
self.putItem(value)
|
|
|
|
|
|
|
|
def isExpanded(self):
|
|
|
|
#warn("IS EXPANDED: %s in %s: %s" % (self.currentIName,
|
|
|
|
# self.expandedINames, self.currentIName in self.expandedINames))
|
|
|
|
return self.currentIName in self.expandedINames
|
|
|
|
|
|
|
|
def putPlainChildren(self, value):
|
|
|
|
self.putEmptyValue(-99)
|
|
|
|
self.putNumChild(1)
|
|
|
|
if self.currentIName in self.expandedINames:
|
|
|
|
with Children(self):
|
|
|
|
self.putFields(value)
|
|
|
|
|
2013-11-07 12:26:14 +01:00
|
|
|
def putCStyleArray(self, value):
|
|
|
|
type = value.type.unqualified()
|
|
|
|
targetType = value[0].type
|
|
|
|
#self.putAddress(value.address)
|
|
|
|
self.putType(type)
|
|
|
|
self.putNumChild(1)
|
|
|
|
format = self.currentItemFormat()
|
|
|
|
isDefault = format == None and str(targetType.unqualified()) == "char"
|
|
|
|
if isDefault or format == 0 or format == 1 or format == 2:
|
|
|
|
blob = self.readMemory(self.addressOf(value), type.sizeof)
|
|
|
|
|
|
|
|
if isDefault:
|
|
|
|
# Use Latin1 as default for char [].
|
|
|
|
self.putValue(blob, Hex2EncodedLatin1)
|
|
|
|
elif format == 0:
|
|
|
|
# Explicitly requested Latin1 formatting.
|
|
|
|
self.putValue(blob, Hex2EncodedLatin1)
|
|
|
|
elif format == 1:
|
|
|
|
# Explicitly requested UTF-8 formatting.
|
|
|
|
self.putValue(blob, Hex2EncodedUtf8)
|
|
|
|
elif format == 2:
|
|
|
|
# Explicitly requested Local 8-bit formatting.
|
|
|
|
self.putValue(blob, Hex2EncodedLocal8Bit)
|
|
|
|
else:
|
2014-01-25 15:32:10 +01:00
|
|
|
try:
|
|
|
|
self.putValue("@0x%x" % self.pointerValue(value.cast(targetType.pointer())))
|
|
|
|
except:
|
|
|
|
self.putEmptyValue()
|
2013-11-07 12:26:14 +01:00
|
|
|
|
|
|
|
if self.currentIName in self.expandedINames:
|
2014-01-25 22:24:39 +01:00
|
|
|
try:
|
|
|
|
# May fail on artificial items like xmm register data.
|
|
|
|
p = self.addressOf(value)
|
|
|
|
ts = targetType.sizeof
|
|
|
|
if not self.tryPutArrayContents(targetType, p, int(type.sizeof / ts)):
|
|
|
|
with Children(self, childType=targetType,
|
|
|
|
addrBase=p, addrStep=ts):
|
|
|
|
self.putFields(value)
|
|
|
|
except:
|
|
|
|
with Children(self, childType=targetType):
|
2013-11-07 12:26:14 +01:00
|
|
|
self.putFields(value)
|
|
|
|
|
2014-01-21 18:13:02 +01:00
|
|
|
def cleanAddress(self, addr):
|
|
|
|
if addr is None:
|
|
|
|
return "<no address>"
|
|
|
|
# We cannot use str(addr) as it yields rubbish for char pointers
|
|
|
|
# that might trigger Unicode encoding errors.
|
|
|
|
#return addr.cast(lookupType("void").pointer())
|
|
|
|
# We do not use "hex(...)" as it (sometimes?) adds a "L" suffix.
|
|
|
|
try:
|
|
|
|
return "0x%x" % toInteger(addr)
|
|
|
|
except:
|
|
|
|
warn("CANNOT CONVERT TYPE: %s" % type(addr))
|
|
|
|
return str(addr)
|
|
|
|
|
2014-03-06 17:30:11 +01:00
|
|
|
def tryPutArrayContents(self, typeobj, base, n):
|
|
|
|
enc = self.simpleEncoding(typeobj)
|
|
|
|
if not enc:
|
|
|
|
return False
|
|
|
|
size = n * typeobj.sizeof;
|
|
|
|
self.put('childtype="%s",' % typeobj)
|
|
|
|
self.put('addrbase="0x%x",' % toInteger(base))
|
|
|
|
self.put('addrstep="0x%x",' % toInteger(typeobj.sizeof))
|
|
|
|
self.put('arrayencoding="%s",' % enc)
|
|
|
|
self.put('arraydata="')
|
|
|
|
self.put(self.readMemory(base, size))
|
|
|
|
self.put('",')
|
|
|
|
return True
|
2014-01-21 18:13:02 +01:00
|
|
|
|
2013-11-06 17:57:12 +01:00
|
|
|
def putFormattedPointer(self, value):
|
|
|
|
#warn("POINTER: %s" % value)
|
|
|
|
if self.isNull(value):
|
|
|
|
#warn("NULL POINTER")
|
|
|
|
self.putType(value.type)
|
|
|
|
self.putValue("0x0")
|
|
|
|
self.putNumChild(0)
|
2014-01-25 15:32:10 +01:00
|
|
|
return
|
2013-11-06 17:57:12 +01:00
|
|
|
|
|
|
|
typeName = str(value.type)
|
|
|
|
innerType = value.type.target().unqualified()
|
|
|
|
innerTypeName = str(innerType)
|
|
|
|
|
|
|
|
try:
|
|
|
|
value.dereference()
|
|
|
|
except:
|
|
|
|
# Failure to dereference a pointer should at least
|
|
|
|
# show the value of a pointer.
|
2014-01-21 18:13:02 +01:00
|
|
|
self.putValue(self.cleanAddress(value))
|
2013-11-06 17:57:12 +01:00
|
|
|
self.putType(typeName)
|
|
|
|
self.putNumChild(0)
|
2014-01-25 15:32:10 +01:00
|
|
|
return
|
2013-11-06 17:57:12 +01:00
|
|
|
|
|
|
|
format = self.currentItemFormat(value.type)
|
|
|
|
|
|
|
|
if innerTypeName == "void":
|
|
|
|
#warn("VOID POINTER: %s" % format)
|
|
|
|
self.putType(typeName)
|
|
|
|
self.putValue(str(value))
|
|
|
|
self.putNumChild(0)
|
2014-01-25 15:32:10 +01:00
|
|
|
return
|
2013-11-06 17:57:12 +01:00
|
|
|
|
|
|
|
if format == None and innerTypeName == "char":
|
|
|
|
# Use Latin1 as default for char *.
|
|
|
|
self.putType(typeName)
|
|
|
|
self.putValue(self.encodeCharArray(value), Hex2EncodedLatin1)
|
|
|
|
self.putNumChild(0)
|
2014-01-25 15:32:10 +01:00
|
|
|
return
|
2013-11-06 17:57:12 +01:00
|
|
|
|
|
|
|
if format == 0:
|
|
|
|
# Explicitly requested bald pointer.
|
|
|
|
self.putType(typeName)
|
2014-01-23 15:30:51 +01:00
|
|
|
self.putValue(self.hexencode(str(value)), Hex2EncodedUtf8WithoutQuotes)
|
2013-11-06 17:57:12 +01:00
|
|
|
self.putNumChild(1)
|
|
|
|
if self.currentIName in self.expandedINames:
|
|
|
|
with Children(self):
|
|
|
|
with SubItem(self, '*'):
|
|
|
|
self.putItem(value.dereference())
|
2014-01-25 15:32:10 +01:00
|
|
|
return
|
2013-11-06 17:57:12 +01:00
|
|
|
|
|
|
|
if format == 1:
|
|
|
|
# Explicitly requested Latin1 formatting.
|
|
|
|
self.putType(typeName)
|
|
|
|
self.putValue(self.encodeCharArray(value), Hex2EncodedLatin1)
|
|
|
|
self.putNumChild(0)
|
2014-01-25 15:32:10 +01:00
|
|
|
return
|
2013-11-06 17:57:12 +01:00
|
|
|
|
|
|
|
if format == 2:
|
|
|
|
# Explicitly requested UTF-8 formatting.
|
|
|
|
self.putType(typeName)
|
|
|
|
self.putValue(self.encodeCharArray(value), Hex2EncodedUtf8)
|
|
|
|
self.putNumChild(0)
|
2014-01-25 15:32:10 +01:00
|
|
|
return
|
2013-11-06 17:57:12 +01:00
|
|
|
|
|
|
|
if format == 3:
|
|
|
|
# Explicitly requested local 8 bit formatting.
|
|
|
|
self.putType(typeName)
|
|
|
|
self.putValue(self.encodeCharArray(value), Hex2EncodedLocal8Bit)
|
|
|
|
self.putNumChild(0)
|
2014-01-25 15:32:10 +01:00
|
|
|
return
|
2013-11-06 17:57:12 +01:00
|
|
|
|
|
|
|
if format == 4:
|
|
|
|
# Explicitly requested UTF-16 formatting.
|
|
|
|
self.putType(typeName)
|
|
|
|
self.putValue(self.encodeChar2Array(value), Hex4EncodedLittleEndian)
|
|
|
|
self.putNumChild(0)
|
2014-01-25 15:32:10 +01:00
|
|
|
return
|
2013-11-06 17:57:12 +01:00
|
|
|
|
|
|
|
if format == 5:
|
|
|
|
# Explicitly requested UCS-4 formatting.
|
|
|
|
self.putType(typeName)
|
|
|
|
self.putValue(self.encodeChar4Array(value), Hex8EncodedLittleEndian)
|
|
|
|
self.putNumChild(0)
|
2014-01-25 15:32:10 +01:00
|
|
|
return
|
2013-11-06 17:57:12 +01:00
|
|
|
|
2013-11-27 15:22:55 +01:00
|
|
|
if not format is None and format >= 6 and format <= 9:
|
2013-11-22 01:21:57 +01:00
|
|
|
# Explicitly requested formatting as array of n items.
|
|
|
|
n = (10, 100, 1000, 10000)[format - 6]
|
2013-11-06 17:57:12 +01:00
|
|
|
self.putType(typeName)
|
2013-11-22 01:21:57 +01:00
|
|
|
self.putItemCount(n)
|
|
|
|
self.putNumChild(n)
|
|
|
|
self.putArrayData(innerType, value, n)
|
2014-01-25 15:32:10 +01:00
|
|
|
return
|
2013-11-06 17:57:12 +01:00
|
|
|
|
|
|
|
if self.isFunctionType(innerType):
|
|
|
|
# A function pointer.
|
|
|
|
val = str(value)
|
|
|
|
pos = val.find(" = ") # LLDB only, but...
|
|
|
|
if pos > 0:
|
|
|
|
val = val[pos + 3:]
|
|
|
|
self.putValue(val)
|
|
|
|
self.putType(innerTypeName)
|
|
|
|
self.putNumChild(0)
|
|
|
|
return
|
|
|
|
|
|
|
|
#warn("AUTODEREF: %s" % self.autoDerefPointers)
|
|
|
|
#warn("INAME: %s" % self.currentIName)
|
|
|
|
if self.autoDerefPointers or self.currentIName.endswith('.this'):
|
|
|
|
## Generic pointer type with format None
|
|
|
|
#warn("GENERIC AUTODEREF POINTER: %s AT %s TO %s"
|
|
|
|
# % (type, value.address, innerTypeName))
|
|
|
|
# Never dereference char types.
|
|
|
|
if innerTypeName != "char" \
|
|
|
|
and innerTypeName != "signed char" \
|
|
|
|
and innerTypeName != "unsigned char" \
|
|
|
|
and innerTypeName != "wchar_t":
|
|
|
|
self.putType(innerTypeName)
|
|
|
|
savedCurrentChildType = self.currentChildType
|
|
|
|
self.currentChildType = stripClassTag(innerTypeName)
|
|
|
|
self.putItem(value.dereference())
|
|
|
|
self.currentChildType = savedCurrentChildType
|
|
|
|
#self.putPointerValue(value)
|
2014-01-25 15:32:10 +01:00
|
|
|
if not value.address is None:
|
|
|
|
self.put('origaddr="0x%x",' % toInteger(value.address))
|
|
|
|
return
|
2013-11-06 17:57:12 +01:00
|
|
|
|
|
|
|
#warn("GENERIC PLAIN POINTER: %s" % value.type)
|
2014-01-22 12:10:49 +01:00
|
|
|
#warn("ADDR PLAIN POINTER: 0x%x" % value.address)
|
2013-11-06 17:57:12 +01:00
|
|
|
self.putType(typeName)
|
|
|
|
self.putValue("0x%x" % self.pointerValue(value))
|
|
|
|
self.putNumChild(1)
|
|
|
|
if self.currentIName in self.expandedINames:
|
|
|
|
with Children(self):
|
|
|
|
with SubItem(self, "*"):
|
|
|
|
self.putItem(value.dereference())
|
|
|
|
|
2013-09-11 21:35:39 +02:00
|
|
|
def putQObjectNameValue(self, value):
|
|
|
|
try:
|
|
|
|
intSize = self.intSize()
|
|
|
|
ptrSize = self.ptrSize()
|
|
|
|
# dd = value["d_ptr"]["d"] is just behind the vtable.
|
2014-01-28 13:20:05 +01:00
|
|
|
dd = self.extractPointer(value, offset=ptrSize)
|
2013-09-11 21:35:39 +02:00
|
|
|
|
|
|
|
if self.qtVersion() < 0x050000:
|
|
|
|
# Size of QObjectData: 5 pointer + 2 int
|
|
|
|
# - vtable
|
|
|
|
# - QObject *q_ptr;
|
|
|
|
# - QObject *parent;
|
|
|
|
# - QObjectList children;
|
|
|
|
# - uint isWidget : 1; etc..
|
|
|
|
# - int postedEvents;
|
|
|
|
# - QMetaObject *metaObject;
|
|
|
|
|
|
|
|
# Offset of objectName in QObjectPrivate: 5 pointer + 2 int
|
|
|
|
# - [QObjectData base]
|
|
|
|
# - QString objectName
|
2014-01-30 16:15:22 +01:00
|
|
|
objectName = self.extractPointer(dd + 5 * ptrSize + 2 * intSize)
|
2013-09-11 21:35:39 +02:00
|
|
|
|
|
|
|
else:
|
|
|
|
# Size of QObjectData: 5 pointer + 2 int
|
|
|
|
# - vtable
|
|
|
|
# - QObject *q_ptr;
|
|
|
|
# - QObject *parent;
|
|
|
|
# - QObjectList children;
|
|
|
|
# - uint isWidget : 1; etc...
|
|
|
|
# - int postedEvents;
|
|
|
|
# - QDynamicMetaObjectData *metaObject;
|
2014-01-30 16:15:22 +01:00
|
|
|
extra = self.extractPointer(dd + 5 * ptrSize + 2 * intSize)
|
2013-09-11 21:35:39 +02:00
|
|
|
if extra == 0:
|
|
|
|
return False
|
|
|
|
|
|
|
|
# Offset of objectName in ExtraData: 6 pointer
|
|
|
|
# - QVector<QObjectUserData *> userData; only #ifndef QT_NO_USERDATA
|
|
|
|
# - QList<QByteArray> propertyNames;
|
|
|
|
# - QList<QVariant> propertyValues;
|
|
|
|
# - QVector<int> runningTimers;
|
|
|
|
# - QList<QPointer<QObject> > eventFilters;
|
|
|
|
# - QString objectName
|
2014-01-30 16:15:22 +01:00
|
|
|
objectName = self.extractPointer(extra + 5 * ptrSize)
|
2013-09-11 21:35:39 +02:00
|
|
|
|
|
|
|
data, size, alloc = self.byteArrayDataHelper(objectName)
|
|
|
|
|
|
|
|
if size == 0:
|
|
|
|
return False
|
|
|
|
|
2014-01-20 15:03:27 +01:00
|
|
|
raw = self.readMemory(data, 2 * size)
|
|
|
|
self.putValue(raw, Hex4EncodedLittleEndian, 1)
|
2013-09-11 21:35:39 +02:00
|
|
|
return True
|
|
|
|
|
|
|
|
except:
|
2014-01-24 17:02:23 +01:00
|
|
|
#warn("NO QOBJECT: %s" % value.type)
|
2013-09-11 21:35:39 +02:00
|
|
|
pass
|
|
|
|
|
2013-10-23 16:28:02 +02:00
|
|
|
|
2014-03-12 13:20:21 +01:00
|
|
|
def extractStaticMetaObjectHelper(self, typeName):
|
|
|
|
"""
|
|
|
|
Checks whether type has a Q_OBJECT macro.
|
|
|
|
Returns the staticMetaObject, or 0.
|
|
|
|
"""
|
|
|
|
# No templates for now.
|
|
|
|
if typeName.find('<') >= 0:
|
|
|
|
return 0
|
|
|
|
|
|
|
|
staticMetaObjectName = typeName + "::staticMetaObject"
|
|
|
|
result = self.findSymbol(staticMetaObjectName)
|
|
|
|
|
|
|
|
# We need to distinguish Q_OBJECT from Q_GADGET:
|
|
|
|
# a Q_OBJECT SMO has a non-null superdata (unless it's QObject itself),
|
|
|
|
# a Q_GADGET SMO has a null superdata (hopefully)
|
|
|
|
if result and typeName != self.qtNamespace() + "QObject":
|
|
|
|
if not self.extractPointer(result):
|
|
|
|
# This looks like a Q_GADGET
|
|
|
|
result = 0
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
def extractStaticMetaObject(self, typeobj):
|
|
|
|
"""
|
|
|
|
Checks recursively whether a type derives from QObject.
|
|
|
|
"""
|
|
|
|
typeName = str(typeobj)
|
|
|
|
result = self.knownStaticMetaObjects.get(typeName, None)
|
|
|
|
if result is not None: # Is 0 or the static metaobject.
|
|
|
|
return result
|
|
|
|
|
|
|
|
result = self.extractStaticMetaObjectHelper(typeName)
|
|
|
|
if not result:
|
|
|
|
base = self.directBaseClass(typeobj, 0)
|
|
|
|
if base:
|
|
|
|
result = self.extractStaticMetaObject(base)
|
|
|
|
|
|
|
|
self.knownStaticMetaObjects[typeName] = result
|
|
|
|
return result
|
|
|
|
|
2014-01-20 15:03:27 +01:00
|
|
|
def staticQObjectPropertyNames(self, metaobject):
|
|
|
|
properties = []
|
|
|
|
dd = metaobject["d"]
|
2014-01-28 13:20:05 +01:00
|
|
|
data = self.extractPointer(dd["data"])
|
|
|
|
sd = self.extractPointer(dd["stringdata"])
|
2014-01-20 15:03:27 +01:00
|
|
|
|
2014-01-29 13:40:09 +01:00
|
|
|
metaObjectVersion = self.extractInt(data)
|
2014-01-20 15:03:27 +01:00
|
|
|
propertyCount = self.extractInt(data + 24)
|
|
|
|
propertyData = self.extractInt(data + 28)
|
|
|
|
|
2014-01-29 13:40:09 +01:00
|
|
|
if metaObjectVersion >= 7: # Qt 5.
|
|
|
|
byteArrayDataType = self.lookupType(self.qtNamespace() + "QByteArrayData")
|
|
|
|
byteArrayDataSize = byteArrayDataType.sizeof
|
|
|
|
for i in range(propertyCount):
|
|
|
|
x = data + (propertyData + 3 * i) * 4
|
|
|
|
literal = sd + self.extractInt(x) * byteArrayDataSize
|
|
|
|
ldata, lsize, lalloc = self.byteArrayDataHelper(literal)
|
|
|
|
properties.append(self.extractBlob(ldata, lsize).toString())
|
|
|
|
else: # Qt 4.
|
|
|
|
for i in range(propertyCount):
|
|
|
|
x = data + (propertyData + 3 * i) * 4
|
|
|
|
ldata = sd + self.extractInt(x)
|
|
|
|
properties.append(self.extractCString(ldata).decode("utf8"))
|
|
|
|
|
2014-01-20 15:03:27 +01:00
|
|
|
return properties
|
|
|
|
|
2014-01-29 13:40:09 +01:00
|
|
|
def extractCString(self, addr):
|
|
|
|
result = bytearray()
|
|
|
|
while True:
|
|
|
|
d = self.extractByte(addr)
|
|
|
|
if d == 0:
|
|
|
|
break
|
|
|
|
result.append(d)
|
|
|
|
addr += 1
|
|
|
|
return result
|
|
|
|
|
2014-02-07 23:00:26 +01:00
|
|
|
def generateQListChildren(self, value):
|
|
|
|
dptr = self.childAt(value, 0)["d"]
|
|
|
|
private = dptr.dereference()
|
|
|
|
begin = int(private["begin"])
|
|
|
|
end = int(private["end"])
|
|
|
|
array = private["array"]
|
|
|
|
size = end - begin
|
|
|
|
innerType = self.templateArgument(value.type, 0)
|
|
|
|
innerSize = innerType.sizeof
|
|
|
|
stepSize = dptr.type.sizeof
|
|
|
|
addr = self.addressOf(array) + begin * stepSize
|
|
|
|
isInternal = innerSize <= stepSize and self.isMovableType(innerType)
|
|
|
|
if isInternal:
|
|
|
|
for i in range(size):
|
|
|
|
yield self.createValue(addr + i * stepSize, innerType)
|
|
|
|
else:
|
|
|
|
p = self.createPointerValue(addr, innerType.pointer())
|
|
|
|
for i in range(size):
|
|
|
|
yield p.dereference().dereference()
|
|
|
|
p += 1
|
|
|
|
|
2014-01-20 15:03:27 +01:00
|
|
|
|
|
|
|
# This is called is when a QObject derived class is expanded
|
2014-01-24 17:02:23 +01:00
|
|
|
def putQObjectGuts(self, qobject, smo):
|
2014-02-07 23:00:26 +01:00
|
|
|
intSize = self.intSize()
|
|
|
|
ptrSize = self.ptrSize()
|
|
|
|
# dd = value["d_ptr"]["d"] is just behind the vtable.
|
|
|
|
dd = self.extractPointer(qobject, offset=ptrSize)
|
|
|
|
|
|
|
|
extraData = self.extractPointer(dd + 5 * ptrSize + 2 * intSize)
|
|
|
|
#with SubItem(self, "[extradata]"):
|
|
|
|
# self.putValue("0x%x" % toInteger(extraData))
|
|
|
|
|
2014-01-20 15:03:27 +01:00
|
|
|
with SubItem(self, "[properties]"):
|
2014-02-07 23:00:26 +01:00
|
|
|
propertyCount = 0
|
2014-01-24 17:41:07 +01:00
|
|
|
if self.isExpanded():
|
2014-02-07 23:00:26 +01:00
|
|
|
propertyNames = self.staticQObjectPropertyNames(smo)
|
|
|
|
propertyCount = len(propertyNames) # Doesn't include dynamic properties.
|
2014-01-24 17:41:07 +01:00
|
|
|
with Children(self):
|
2014-02-07 23:00:26 +01:00
|
|
|
# Static properties.
|
2014-01-24 17:41:07 +01:00
|
|
|
for i in range(propertyCount):
|
|
|
|
name = propertyNames[i]
|
|
|
|
self.putCallItem(name, qobject, "property", '"' + name + '"')
|
2014-01-20 15:03:27 +01:00
|
|
|
|
2014-02-07 23:00:26 +01:00
|
|
|
# Dynamic properties.
|
2014-02-28 14:09:44 +01:00
|
|
|
if extraData:
|
2014-02-07 23:00:26 +01:00
|
|
|
propertyNames = extraData + ptrSize
|
|
|
|
propertyValues = extraData + 2 * ptrSize
|
|
|
|
|
|
|
|
ns = self.qtNamespace()
|
|
|
|
|
|
|
|
typ = self.lookupType(ns + "QList<" + ns + "QByteArray>")
|
|
|
|
names = self.createValue(propertyNames, typ)
|
|
|
|
|
|
|
|
typ = self.lookupType(ns + "QList<" + ns + "QVariant>")
|
|
|
|
values = self.createValue(propertyValues, typ)
|
|
|
|
|
|
|
|
for (k, v) in zip(self.generateQListChildren(names),
|
|
|
|
self.generateQListChildren(values)) :
|
|
|
|
with SubItem(self, propertyCount):
|
|
|
|
self.put('key="%s",' % self.encodeByteArray(k))
|
|
|
|
self.put('keyencoded="%s",' % Hex2EncodedLatin1)
|
|
|
|
self.putItem(v)
|
|
|
|
propertyCount += 1
|
|
|
|
|
|
|
|
self.putValue('<%s items>' % propertyCount if propertyCount else '<>0 items>')
|
|
|
|
self.putNumChild(1)
|
|
|
|
|
2014-01-20 15:03:27 +01:00
|
|
|
|
2013-10-23 16:28:02 +02:00
|
|
|
def isKnownMovableType(self, type):
|
|
|
|
if type in (
|
|
|
|
"QBrush", "QBitArray", "QByteArray", "QCustomTypeInfo", "QChar", "QDate",
|
|
|
|
"QDateTime", "QFileInfo", "QFixed", "QFixedPoint", "QFixedSize",
|
|
|
|
"QHashDummyValue", "QIcon", "QImage", "QLine", "QLineF", "QLatin1Char",
|
|
|
|
"QLocale", "QMatrix", "QModelIndex", "QPoint", "QPointF", "QPen",
|
|
|
|
"QPersistentModelIndex", "QResourceRoot", "QRect", "QRectF", "QRegExp",
|
|
|
|
"QSize", "QSizeF", "QString", "QTime", "QTextBlock", "QUrl", "QVariant",
|
|
|
|
"QXmlStreamAttribute", "QXmlStreamNamespaceDeclaration",
|
|
|
|
"QXmlStreamNotationDeclaration", "QXmlStreamEntityDeclaration"
|
|
|
|
):
|
|
|
|
return True
|
|
|
|
|
|
|
|
return type == "QStringList" and self.qtVersion() >= 0x050000
|
|
|
|
|
2013-10-30 15:07:54 +01:00
|
|
|
def currentItemFormat(self, type = None):
|
|
|
|
format = self.formats.get(self.currentIName)
|
|
|
|
if format is None:
|
|
|
|
if type is None:
|
|
|
|
type = self.currentType
|
|
|
|
needle = self.stripForFormat(str(type))
|
|
|
|
format = self.typeformats.get(needle)
|
|
|
|
return format
|
2013-10-23 16:28:02 +02:00
|
|
|
|
2014-01-04 00:39:23 +01:00
|
|
|
def putPlotData(self, type, base, n, plotFormat = 2):
|
|
|
|
if self.isExpanded():
|
|
|
|
self.putArrayData(type, base, n)
|
|
|
|
if not hasPlot():
|
|
|
|
return
|
|
|
|
if not self.isSimpleType(type):
|
|
|
|
#self.putValue(self.currentValue + " (not plottable)")
|
|
|
|
self.putValue(self.currentValue)
|
|
|
|
self.putField("plottable", "0")
|
|
|
|
return
|
|
|
|
global gnuplotPipe
|
|
|
|
global gnuplotPid
|
|
|
|
format = self.currentItemFormat()
|
|
|
|
iname = self.currentIName
|
|
|
|
if format != plotFormat:
|
|
|
|
if iname in gnuplotPipe:
|
|
|
|
os.kill(gnuplotPid[iname], 9)
|
|
|
|
del gnuplotPid[iname]
|
|
|
|
gnuplotPipe[iname].terminate()
|
|
|
|
del gnuplotPipe[iname]
|
|
|
|
return
|
|
|
|
base = self.createPointerValue(base, type)
|
|
|
|
if not iname in gnuplotPipe:
|
|
|
|
gnuplotPipe[iname] = subprocess.Popen(["gnuplot"],
|
|
|
|
stdin=subprocess.PIPE)
|
|
|
|
gnuplotPid[iname] = gnuplotPipe[iname].pid
|
|
|
|
f = gnuplotPipe[iname].stdin;
|
|
|
|
# On Ubuntu install gnuplot-x11
|
|
|
|
f.write("set term wxt noraise\n")
|
|
|
|
f.write("set title 'Data fields'\n")
|
|
|
|
f.write("set xlabel 'Index'\n")
|
|
|
|
f.write("set ylabel 'Value'\n")
|
|
|
|
f.write("set grid\n")
|
|
|
|
f.write("set style data lines;\n")
|
|
|
|
f.write("plot '-' title '%s'\n" % iname)
|
|
|
|
for i in range(0, n):
|
|
|
|
f.write(" %s\n" % base.dereference())
|
|
|
|
base += 1
|
|
|
|
f.write("e\n")
|
|
|
|
|
2014-02-04 14:48:40 +01:00
|
|
|
def putSpecialArgv(self, value):
|
|
|
|
"""
|
|
|
|
Special handling for char** argv.
|
|
|
|
"""
|
|
|
|
n = 0
|
|
|
|
p = value
|
|
|
|
# p is 0 for "optimized out" cases. Or contains rubbish.
|
|
|
|
try:
|
|
|
|
if not self.isNull(p):
|
|
|
|
while not self.isNull(p.dereference()) and n <= 100:
|
|
|
|
p += 1
|
|
|
|
n += 1
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
with TopLevelItem(self, 'local.argv'):
|
|
|
|
self.put('iname="local.argv",name="argv",')
|
|
|
|
self.putItemCount(n, 100)
|
|
|
|
self.putType('char **')
|
|
|
|
self.putNumChild(n)
|
|
|
|
if self.currentIName in self.expandedINames:
|
|
|
|
p = value
|
|
|
|
with Children(self, n):
|
|
|
|
for i in xrange(n):
|
|
|
|
self.putSubItem(i, p.dereference())
|
|
|
|
p += 1
|
|
|
|
|
2014-01-28 13:20:05 +01:00
|
|
|
def extractPointer(self, thing, offset = 0):
|
|
|
|
if isinstance(thing, int):
|
|
|
|
bytes = self.extractBlob(thing, self.ptrSize()).toBytes()
|
|
|
|
elif sys.version_info[0] == 2 and isinstance(thing, long):
|
|
|
|
bytes = self.extractBlob(thing, self.ptrSize()).toBytes()
|
|
|
|
elif isinstance(thing, Blob):
|
|
|
|
bytes = blob.toBytes()
|
|
|
|
else:
|
|
|
|
# Assume it's a (backend specific) Value.
|
|
|
|
bytes = self.toBlob(thing).toBytes()
|
|
|
|
code = "I" if self.ptrSize() == 4 else "Q"
|
|
|
|
return struct.unpack_from(code, bytes, offset)[0]
|
2013-09-11 21:35:39 +02:00
|
|
|
|
|
|
|
# Some "Enums"
|
|
|
|
|
2013-09-26 16:29:13 +02:00
|
|
|
# Encodings. Keep that synchronized with DebuggerEncoding in debuggerprotocol.h
|
2013-09-11 21:35:39 +02:00
|
|
|
Unencoded8Bit, \
|
|
|
|
Base64Encoded8BitWithQuotes, \
|
|
|
|
Base64Encoded16BitWithQuotes, \
|
|
|
|
Base64Encoded32BitWithQuotes, \
|
|
|
|
Base64Encoded16Bit, \
|
|
|
|
Base64Encoded8Bit, \
|
|
|
|
Hex2EncodedLatin1, \
|
|
|
|
Hex4EncodedLittleEndian, \
|
|
|
|
Hex8EncodedLittleEndian, \
|
|
|
|
Hex2EncodedUtf8, \
|
|
|
|
Hex8EncodedBigEndian, \
|
|
|
|
Hex4EncodedBigEndian, \
|
|
|
|
Hex4EncodedLittleEndianWithoutQuotes, \
|
|
|
|
Hex2EncodedLocal8Bit, \
|
|
|
|
JulianDate, \
|
|
|
|
MillisecondsSinceMidnight, \
|
|
|
|
JulianDateAndMillisecondsSinceMidnight, \
|
|
|
|
Hex2EncodedInt1, \
|
|
|
|
Hex2EncodedInt2, \
|
|
|
|
Hex2EncodedInt4, \
|
|
|
|
Hex2EncodedInt8, \
|
|
|
|
Hex2EncodedUInt1, \
|
|
|
|
Hex2EncodedUInt2, \
|
|
|
|
Hex2EncodedUInt4, \
|
|
|
|
Hex2EncodedUInt8, \
|
|
|
|
Hex2EncodedFloat4, \
|
|
|
|
Hex2EncodedFloat8, \
|
2013-09-26 16:29:13 +02:00
|
|
|
IPv6AddressAndHexScopeId, \
|
2013-10-22 13:51:35 +02:00
|
|
|
Hex2EncodedUtf8WithoutQuotes, \
|
2013-11-30 23:46:25 +01:00
|
|
|
DateTimeInternal \
|
2013-10-22 13:51:35 +02:00
|
|
|
= range(30)
|
2013-09-11 21:35:39 +02:00
|
|
|
|
|
|
|
# Display modes. Keep that synchronized with DebuggerDisplay in watchutils.h
|
|
|
|
StopDisplay, \
|
|
|
|
DisplayImageData, \
|
|
|
|
DisplayUtf16String, \
|
|
|
|
DisplayImageFile, \
|
|
|
|
DisplayProcess, \
|
|
|
|
DisplayLatin1String, \
|
|
|
|
DisplayUtf8String \
|
|
|
|
= range(7)
|
|
|
|
|
|
|
|
|
|
|
|
def mapForms():
|
|
|
|
return "Normal,Compact"
|
|
|
|
|
|
|
|
def arrayForms():
|
|
|
|
if hasPlot():
|
|
|
|
return "Normal,Plot"
|
|
|
|
return "Normal"
|
|
|
|
|