dumper.py 58.1 KB
Newer Older
1 2
############################################################################
#
3
# Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 5 6 7 8 9 10 11 12
# 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
Eike Ziller's avatar
Eike Ziller committed
13 14
# conditions see http://www.qt.io/licensing.  For further information
# use the contact form at http://www.qt.io/contact-us.
15 16 17
#
# GNU Lesser General Public License Usage
# Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
# General Public License version 2.1 or version 3 as published by the Free
# Software Foundation and appearing in the file LICENSE.LGPLv21 and
# LICENSE.LGPLv3 included in the packaging of this file.  Please review the
# following information to ensure the GNU Lesser General Public License
# requirements will be met: https://www.gnu.org/licenses/lgpl.html and
# http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 25 26 27 28 29 30 31
#
# 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
32
import struct
33 34
import sys
import base64
hjk's avatar
hjk committed
35
import re
36 37 38 39 40 41 42 43 44 45 46

if sys.version_info[0] >= 3:
    xrange = range
    toInteger = int
else:
    toInteger = long


verbosity = 0
verbosity = 1

47 48 49 50 51 52 53 54 55 56
# Debugger start modes. Keep in sync with DebuggerStartMode in debuggerconstants.h
NoStartMode, \
StartInternal, \
StartExternal,  \
AttachExternal,  \
AttachCrashedExternal,  \
AttachCore, \
AttachToRemoteServer, \
AttachToRemoteProcess, \
StartRemoteProcess, \
57
    = range(0, 9)
58 59


hjk's avatar
hjk committed
60 61 62 63 64 65 66 67 68 69
# Known special formats. Keep in sync with DisplayFormat in watchhandler.h
KnownDumperFormatBase, \
Latin1StringFormat, \
Utf8StringFormat, \
Local8BitStringFormat, \
Utf16StringFormat, \
Ucs4StringFormat, \
Array10Format, \
Array100Format, \
Array1000Format, \
70 71 72 73
Array10000Format, \
SeparateLatin1StringFormat, \
SeparateUtf8StringFormat \
    = range(100, 112)
hjk's avatar
hjk committed
74

75 76 77 78
def hasPlot():
    fileName = "/usr/bin/gnuplot"
    return os.path.isfile(fileName) and os.access(fileName, os.X_OK)

79 80 81 82 83 84 85 86 87
try:
    import subprocess
    def arrayForms():
        if hasPlot():
            return "Normal,Plot"
        return "Normal"
except:
    def arrayForms():
        return "Normal"
88 89


90 91 92 93 94 95
class ReportItem:
    """
    Helper structure to keep temporary "best" information about a value
    or a type scheduled to be reported. This might get overridden be
    subsequent better guesses during a putItem() run.
    """
hjk's avatar
hjk committed
96 97 98 99 100 101 102 103 104
    def __init__(self, value = None, encoding = None, priority = -100, elided = None):
        self.value = value
        self.priority = priority
        self.encoding = encoding
        self.elided = elided

    def __str__(self):
        return "Item(value: %s, encoding: %s, priority: %s, elided: %s)" \
            % (self.value, self.encoding, self.priority, self.elided)
105 106


hjk's avatar
hjk committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120
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

121 122 123
    def toBytes(self):
        """Retrieves "lazy" contents from memoryviews."""
        data = self.data
hjk's avatar
hjk committed
124 125 126 127 128 129

        major = sys.version_info[0]
        if major == 3 or (major == 2 and sys.version_info[1] >= 7):
            if isinstance(data, memoryview):
                data = data.tobytes()
        if major == 2 and isinstance(data, buffer):
130 131 132
            data = ''.join([c for c in data])
        return data

hjk's avatar
hjk committed
133 134 135 136
    def toString(self):
        data = self.toBytes()
        return data if sys.version_info[0] == 2 else data.decode("utf8")

137 138 139 140 141 142 143 144 145 146 147 148 149 150
    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]
151

152 153
    def extractLong(self, offset = 0):
        return struct.unpack_from("l", self.data, offset)[0]
154

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
    # 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]

171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
#
# 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


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:
204
            self.childType = d.stripClassTag(str(childType))
205 206
            if not self.d.isCli:
                self.d.put('childtype="%s",' % self.childType)
207 208 209 210 211 212 213 214 215 216 217
            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
218
        self.printsAddress = not self.d.putAddressRange(addrBase, addrStep)
219 220 221 222 223 224 225 226 227 228 229 230

    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
231
        self.d.put(self.d.childrenPrefix)
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246

    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
247 248
        self.d.putNewline()
        self.d.put(self.d.childrenSuffix)
249 250
        return True

251
class PairedChildrenData:
252 253
    def __init__(self, d, pairType, keyType, valueType, useKeyAndValue):
        self.useKeyAndValue = useKeyAndValue
254
        self.pairType = pairType
255 256
        self.keyType = keyType
        self.valueType = valueType
257
        self.isCompact = d.isMapCompact(self.keyType, self.valueType)
258
        self.childType = valueType if self.isCompact else pairType
259
        ns = d.qtNamespace()
260 261 262 263 264
        keyTypeName = d.stripClassTag(str(self.keyType))
        self.keyIsQString = keyTypeName == ns + "QString"
        self.keyIsQByteArray = keyTypeName == ns + "QByteArray"
        self.keyIsStdString = keyTypeName == "std::string" \
            or keyTypeName.startswith("std::basic_string<char")
265

hjk's avatar
hjk committed
266
class PairedChildren(Children):
267 268
    def __init__(self, d, numChild, useKeyAndValue = False,
            pairType = None, keyType = None, valueType = None, maxNumChild = None):
269
        self.d = d
270 271 272 273
        if keyType is None:
            keyType = d.templateArgument(pairType, 0).unqualified()
        if valueType is None:
            valueType = d.templateArgument(pairType, 1)
274
        d.pairData = PairedChildrenData(d, pairType, keyType, valueType, useKeyAndValue)
275

276
        Children.__init__(self, d, numChild,
277 278 279
            d.pairData.childType,
            maxNumChild = maxNumChild,
            addrBase = None, addrStep = None)
280 281 282 283 284 285 286 287 288

    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

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329

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
330
        self.isCli = False
331

332 333 334
        # Later set, or not set:
        # cachedQtVersion
        self.stringCutOff = 10000
335
        self.displayStringLimit = 100
336 337 338 339 340 341 342 343 344 345 346 347 348

        # 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 = {}

349 350 351
        # Maps type names to static metaobjects. If a type is known
        # to not be QObject derived, it contains a 0 value.
        self.knownStaticMetaObjects = {}
hjk's avatar
hjk committed
352

353 354 355 356 357
        self.childrenPrefix = 'children=['
        self.childrenSuffix = '],'

    def putNewline(self):
        pass
358

359 360 361 362 363 364 365 366 367 368 369
    def stripClassTag(self, 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

370 371 372 373 374
    def stripForFormat(self, typeName):
        if typeName in self.cachedFormats:
            return self.cachedFormats[typeName]
        stripped = ""
        inArray = 0
375
        for c in self.stripClassTag(typeName):
376 377 378 379 380 381 382 383 384 385 386 387 388 389
            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

390
    # Hex decoding operating on str, return str.
hjk's avatar
hjk committed
391 392 393 394 395
    def hexdecode(self, s):
        if sys.version_info[0] == 2:
            return s.decode("hex")
        return bytes.fromhex(s).decode("utf8")

396
    # Hex decoding operating on str or bytes, return str.
hjk's avatar
hjk committed
397 398 399
    def hexencode(self, s):
        if sys.version_info[0] == 2:
            return s.encode("hex")
400 401 402 403 404
        if isinstance(s, str):
            s = s.encode("utf8")
        return base64.b16encode(s).decode("utf8")

    #def toBlob(self, value):
405
    #    """Abstract"""
hjk's avatar
hjk committed
406

407 408 409
    def is32bit(self):
        return self.ptrSize() == 4

hjk's avatar
hjk committed
410 411 412
    def is64bit(self):
        return self.ptrSize() == 8

413 414 415 416
    def isQt3Support(self):
        # assume no Qt 3 support by default
        return False

417
    # Clamps size to limit.
418
    def computeLimit(self, size, limit):
hjk's avatar
hjk committed
419 420
        if limit == 0:
            limit = self.displayStringLimit
421 422 423
        if limit is None or size <= limit:
            return 0, size
        return size, limit
424

425 426 427 428 429 430 431 432 433 434 435
    def vectorDataHelper(self, addr):
        if self.qtVersion() >= 0x050000:
            size = self.extractInt(addr + 4)
            alloc = self.extractInt(addr + 8) & 0x7ffffff
            data = addr + self.extractPointer(addr + 8 + self.ptrSize())
        else:
            alloc = self.extractInt(addr + 4)
            size = self.extractInt(addr + 8)
            data = addr + 16
        return data, size, alloc

436 437 438 439 440 441 442 443 444
    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
445
            data = addr + self.extractPointer(addr + 8 + self.ptrSize())
446 447 448 449
            if self.ptrSize() == 4:
                data = data & 0xffffffff
            else:
                data = data & 0xffffffffffffffff
450
        elif self.qtVersion() >= 0x040000:
451 452 453 454 455 456 457
            # Data:
            # - QBasicAtomicInt ref;
            # - int alloc, size;
            # - [padding]
            # - char *data;
            alloc = self.extractInt(addr + 4)
            size = self.extractInt(addr + 8)
458
            data = self.extractPointer(addr + 8 + self.ptrSize())
459 460 461 462 463 464 465 466 467
        else:
            # Data:
            # - QShared count;
            # - QChar *unicode
            # - char *ascii
            # - uint len: 30
            size = self.extractInt(addr + 3 * self.ptrSize()) & 0x3ffffff
            alloc = size  # pretend.
            data = self.extractPointer(addr + self.ptrSize())
468 469 470
        return data, size, alloc

    # addr is the begin of a QByteArrayData structure
471
    def encodeStringHelper(self, addr, limit):
472 473 474
        # Should not happen, but we get it with LLDB as result
        # of inferior calls
        if addr == 0:
475
            return 0, ""
476 477 478
        data, size, alloc = self.byteArrayDataHelper(addr)
        if alloc != 0:
            self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
479 480
        elided, shown = self.computeLimit(size, limit)
        return elided, self.readMemory(data, 2 * shown)
481

482
    def encodeByteArrayHelper(self, addr, limit):
483 484 485
        data, size, alloc = self.byteArrayDataHelper(addr)
        if alloc != 0:
            self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
486 487
        elided, shown = self.computeLimit(size, limit)
        return elided, self.readMemory(data, shown)
488

hjk's avatar
hjk committed
489
    def readMemory(self, addr, size):
490 491
        data = self.extractBlob(addr, size).toBytes()
        return self.hexencode(data)
hjk's avatar
hjk committed
492

493
    def encodeByteArray(self, value, limit = 0):
494 495
        elided, data = self.encodeByteArrayHelper(self.extractPointer(value), limit)
        return data
496 497

    def byteArrayData(self, value):
498
        return self.byteArrayDataHelper(self.extractPointer(value))
499

hjk's avatar
hjk committed
500
    def putByteArrayValueByAddress(self, addr):
501 502
        elided, data = self.encodeByteArrayHelper(addr, self.displayStringLimit)
        self.putValue(data, Hex2EncodedLatin1, elided=elided)
hjk's avatar
hjk committed
503

hjk's avatar
hjk committed
504 505 506
    def putByteArrayValue(self, value):
        elided, data = self.encodeByteArrayHelper(self.extractPointer(value), self.displayStringLimit)
        self.putValue(data, Hex2EncodedLatin1, elided=elided)
507

508
    def encodeString(self, value, limit = 0):
509 510
        elided, data = self.encodeStringHelper(self.extractPointer(value), limit)
        return data
511 512

    def stringData(self, value):
513
        return self.byteArrayDataHelper(self.extractPointer(value))
514

515 516 517 518 519 520 521 522 523
    def encodeStdString(self, value, limit = 0):
        data = value["_M_dataplus"]["_M_p"]
        sizePtr = data.cast(self.sizetType().pointer())
        size = int(sizePtr[-3])
        alloc = int(sizePtr[-2])
        self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
        elided, shown = self.computeLimit(size, limit)
        return self.readMemory(data, shown)

524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
    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()

hjk's avatar
hjk committed
552 553 554 555
    def putStringValueByAddress(self, addr):
        elided, data = self.encodeStringHelper(addr, self.displayStringLimit)
        self.putValue(data, Hex4EncodedLittleEndian, elided=elided)

556
    def putStringValue(self, value):
557 558
        elided, data = self.encodeStringHelper(self.extractPointer(value), self.displayStringLimit)
        self.putValue(data, Hex4EncodedLittleEndian, elided=elided)
559

560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
    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)

584 585 586 587 588 589 590 591 592 593 594 595
    def putCallItem(self, name, value, func, *args):
        try:
            result = self.callHelper(value, func, args)
            with SubItem(self, name):
                self.putItem(result)
        except:
            with SubItem(self, name):
                self.putValue("<not callable>")
                self.putNumChild(0)

    def call(self, value, func, *args):
        return self.callHelper(value, func, args)
596

597 598 599 600 601 602 603
    def putAddressRange(self, base, step):
        try:
            if not addrBase is None and not step is None:
                self.put('addrbase="0x%x",' % toInteger(base))
                self.put('addrstep="0x%x",' % toInteger(step))
                return True
        except:
hjk's avatar
hjk committed
604 605
            #warn("ADDRBASE: %s" % base)
            #warn("ADDRSTEP: %s" % step)
606
            pass
607 608 609
        return False

        #warn("CHILDREN: %s %s %s" % (numChild, childType, childNumChild))
610
    def putMapName(self, value, index = None):
611
        ns = self.qtNamespace()
612 613
        typeName = self.stripClassTag(str(value.type))
        if typeName == ns + "QString":
614 615
            self.put('key="%s",' % self.encodeString(value))
            self.put('keyencoded="%s",' % Hex4EncodedLittleEndian)
616
        elif typeName == ns + "QByteArray":
617 618
            self.put('key="%s",' % self.encodeByteArray(value))
            self.put('keyencoded="%s",' % Hex2EncodedLatin1)
619 620 621
        elif typeName == "std::string":
            self.put('key="%s",' % self.encodeStdString(value))
            self.put('keyencoded="%s",' % Hex2EncodedLatin1)
622
        else:
623
            val = str(value.GetValue()) if self.isLldb else str(value)
624 625
            if index is None:
                key = '%s' % val
626
            else:
627
                key = '[%s] %s' % (index, val)
628
            self.put('key="%s",' % self.hexencode(key))
629
            self.put('keyencoded="%s",' % Hex2EncodedUtf8WithoutQuotes)
630

631
    def putPair(self, pair, index = None):
632
        if self.pairData.useKeyAndValue:
633 634
            key = pair["key"]
            value = pair["value"]
635 636 637
        else:
            key = pair["first"]
            value = pair["second"]
638 639 640 641 642 643 644
        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)
645 646 647
            elif self.pairData.keyIsStdString:
                self.put('key="%s",' % self.encodeStdString(key))
                self.put('keyencoded="%s",' % Hex2EncodedLatin1)
648 649 650 651 652
            else:
                name = str(key.GetValue()) if self.isLldb else str(key)
                if index == -1:
                    self.put('name="%s",' % name)
                else:
653
                    self.put('key="[%s] %s",' % (index, name))
654 655 656
            self.putItem(value)
        else:
            self.putEmptyValue()
657 658
            self.putNumChild(2)
            self.putField("iname", self.currentIName)
659
            if self.isExpanded():
660
                with Children(self):
661 662 663 664 665 666
                    if self.pairData.useKeyAndValue:
                        self.putSubItem("key", key)
                        self.putSubItem("value", value)
                    else:
                        self.putSubItem("first", key)
                        self.putSubItem("second", value)
667

668 669 670 671 672 673 674
    def putPlainChildren(self, value, dumpBase = True):
        self.putEmptyValue(-99)
        self.putNumChild(1)
        if self.isExpanded():
            with Children(self):
                self.putFields(value, dumpBase)

675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
    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)

697 698 699 700 701 702 703 704 705
    def readToFirstZero(self, p, tsize, maximum):
        code = (None, "b", "H", None, "I")[tsize]
        base = toInteger(p)
        blob = self.extractBlob(base, maximum).toBytes()
        for i in xrange(0, maximum / tsize):
            t = struct.unpack_from(code, blob, i)[0]
            if t == 0:
                return 0, i, self.hexencode(blob[:i])

706
        # Real end is unknown.
707
        return -1, maximum, self.hexencode(blob[:maximum])
hjk's avatar
hjk committed
708

709 710 711
    def encodeCArray(self, p, tsize, limit):
        elided, shown, blob = self.readToFirstZero(p, tsize, limit)
        return elided, blob
hjk's avatar
hjk committed
712

713 714 715 716 717 718
    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)
719
        self.putNumChild(count)
720

721 722 723 724 725
    def putField(self, name, value):
        self.put('%s="%s",' % (name, value))

    def putType(self, type, priority = 0):
        # Higher priority values override lower ones.
726 727 728
        if priority >= self.currentType.priority:
            self.currentType.value = str(type)
            self.currentType.priority = priority
729

730
    def putValue(self, value, encoding = None, priority = 0, elided = None):
731
        # Higher priority values override lower ones.
732 733 734
        # elided = 0 indicates all data is available in value,
        # otherwise it's the true length.
        if priority >= self.currentValue.priority:
hjk's avatar
hjk committed
735
            self.currentValue = ReportItem(value, encoding, priority, elided)
736 737

    def putEmptyValue(self, priority = -10):
738
        if priority >= self.currentValue.priority:
hjk's avatar
hjk committed
739
            self.currentValue = ReportItem("", None, priority, None)
740 741 742 743

    def putName(self, name):
        self.put('name="%s",' % name)

744
    def putBetterType(self, type):
hjk's avatar
hjk committed
745 746 747 748
        if isinstance(type, ReportItem):
            self.currentType.value = str(type.value)
        else:
            self.currentType.value = str(type)
749 750
        self.currentType.priority += 1

751 752 753 754 755 756 757 758
    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)
759
        self.currentValue.value = None
760

761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
    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)

778 779
    def putCStyleArray(self, value):
        type = value.type.unqualified()
780
        innerType = value[0].type
781 782 783 784
        #self.putAddress(value.address)
        self.putType(type)
        self.putNumChild(1)
        format = self.currentItemFormat()
hjk's avatar
hjk committed
785 786 787
        isDefault1 = format == None and str(innerType.unqualified()) == "char"
        isDefault2 = format == None and str(innerType.unqualified()) == "wchar_t"
        if isDefault1 or isDefault2 or format == 0 or format == 1 or format == 2:
788 789
            blob = self.readMemory(self.addressOf(value), type.sizeof)

hjk's avatar
hjk committed
790
        if isDefault1:
791 792
            # Use Latin1 as default for char [].
            self.putValue(blob, Hex2EncodedLatin1)
hjk's avatar
hjk committed
793 794 795 796 797
        elif isDefault2:
            if type.sizeof == 2:
                self.putValue(blob, Hex4EncodedLittleEndian)
            else:
                self.putValue(blob, Hex8EncodedLittleEndian)
798 799 800 801 802 803 804 805 806 807
        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:
hjk's avatar
hjk committed
808
            try:
809
                self.putValue("@0x%x" % self.pointerValue(value.cast(innerType.pointer())))
hjk's avatar
hjk committed
810 811
            except:
                self.putEmptyValue()
812 813

        if self.currentIName in self.expandedINames:
hjk's avatar
hjk committed
814 815 816
            try:
                # May fail on artificial items like xmm register data.
                p = self.addressOf(value)
817 818 819
                ts = innerType.sizeof
                if not self.tryPutArrayContents(p, int(type.sizeof / ts), innerType):
                    with Children(self, childType=innerType,
hjk's avatar
hjk committed
820 821 822
                            addrBase=p, addrStep=ts):
                        self.putFields(value)
            except:
823
                with Children(self, childType=innerType):
824 825
                    self.putFields(value)

826 827 828 829 830 831 832 833 834 835 836 837 838
    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)

839 840
    def tryPutArrayContents(self, base, n, innerType):
        enc = self.simpleEncoding(innerType)
841 842
        if not enc:
            return False
843 844
        size = n * innerType.sizeof;
        self.put('childtype="%s",' % innerType)
845
        self.put('addrbase="0x%x",' % toInteger(base))
846
        self.put('addrstep="0x%x",' % toInteger(innerType.sizeof))
847 848 849 850 851
        self.put('arrayencoding="%s",' % enc)
        self.put('arraydata="')
        self.put(self.readMemory(base, size))
        self.put('",')
        return True
852

853 854
    def putSimpleCharArray(self, base, size = None):
        if size is None:
855
            elided, shown, data = self.readToFirstZero(base, 1, self.displayStringLimit)
856 857
        else:
            elided, shown = self.computeLimit(int(size), self.displayStringLimit)
858
            data = self.readMemory(p, shown)
859 860
        self.putValue(data, Hex2EncodedLatin1, elided=elided)

861 862 863 864 865 866 867 868
    def putDisplay(self, format, value = None, cmd = None):
        self.put('editformat="%s",' % format)
        if cmd is None:
            if not value is None:
                self.put('editvalue="%s",' % value)
        else:
            self.put('editvalue="%s|%s",' % (cmd, value))

hjk's avatar
hjk committed
869 870 871 872 873 874 875
    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)
hjk's avatar
hjk committed
876
            return
hjk's avatar
hjk committed
877 878 879 880 881 882 883 884 885 886

        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.
887
            self.putValue(self.cleanAddress(value))
hjk's avatar
hjk committed
888 889
            self.putType(typeName)
            self.putNumChild(0)
hjk's avatar
hjk committed
890
            return
hjk's avatar
hjk committed
891 892 893 894 895 896 897 898

        format = self.currentItemFormat(value.type)

        if innerTypeName == "void":
            #warn("VOID POINTER: %s" % format)
            self.putType(typeName)
            self.putValue(str(value))
            self.putNumChild(0)
hjk's avatar
hjk committed
899
            return
hjk's avatar
hjk committed
900 901 902 903

        if format == None and innerTypeName == "char":
            # Use Latin1 as default for char *.
            self.putType(typeName)
904
            (elided, data) = self.encodeCArray(value, 1, self.displayStringLimit)
905
            self.putValue(data, Hex2EncodedLatin1, elided=elided)
hjk's avatar
hjk committed
906
            self.putNumChild(0)
hjk's avatar
hjk committed
907
            return
hjk's avatar
hjk committed
908 909 910 911

        if format == 0:
            # Explicitly requested bald pointer.
            self.putType(typeName)
hjk's avatar
hjk committed
912
            self.putValue(self.hexencode(str(value)), Hex2EncodedUtf8WithoutQuotes)
hjk's avatar
hjk committed
913 914 915 916 917
            self.putNumChild(1)
            if self.currentIName in self.expandedINames:
                with Children(self):
                    with SubItem(self, '*'):
                        self.putItem(value.dereference())
hjk's avatar
hjk committed
918
            return
hjk's avatar
hjk committed
919

920
        if format == Latin1StringFormat or format == SeparateLatin1StringFormat:
hjk's avatar
hjk committed
921
            # Explicitly requested Latin1 formatting.
922
            limit = self.displayStringLimit if format == Latin1StringFormat else 1000000
hjk's avatar
hjk committed
923
            self.putType(typeName)
924
            (elided, data) = self.encodeCArray(value, 1, limit)
925
            self.putValue(data, Hex2EncodedLatin1, elided=elided)
hjk's avatar
hjk committed
926
            self.putNumChild(0)
927
            self.putDisplay((StopDisplay if format == Latin1StringFormat else DisplayLatin1String), data)
hjk's avatar
hjk committed
928
            return
hjk's avatar
hjk committed
929

930
        if format == Utf8StringFormat or format == SeparateUtf8StringFormat:
hjk's avatar
hjk committed
931
            # Explicitly requested UTF-8 formatting.
932
            limit = self.displayStringLimit if format == Utf8StringFormat else 1000000
hjk's avatar
hjk committed
933
            self.putType(typeName)
934
            (elided, data) = self.encodeCArray(value, 1, limit)
935
            self.putValue(data, Hex2EncodedUtf8, elided=elided)
hjk's avatar
hjk committed
936
            self.putNumChild(0)
937
            self.putDisplay((StopDisplay if format == Utf8StringFormat else DisplayUtf8String), data)
hjk's avatar
hjk committed
938
            return
hjk's avatar
hjk committed
939

hjk's avatar
hjk committed
940
        if format == Local8BitStringFormat:
hjk's avatar
hjk committed
941 942
            # Explicitly requested local 8 bit formatting.
            self.putType(typeName)
943
            (elided, data) = self.encodeCArray(value, 1, self.displayStringLimit)
944
            self.putValue(data, Hex2EncodedLocal8Bit, elided=elided)
hjk's avatar
hjk committed
945
            self.putNumChild(0)
hjk's avatar
hjk committed
946
            return
hjk's avatar
hjk committed
947

hjk's avatar
hjk committed
948
        if format == Utf16StringFormat:
hjk's avatar
hjk committed
949 950
            # Explicitly requested UTF-16 formatting.
            self.putType(typeName)
951
            (elided, data) = self.encodeCArray(value, 2, self.displayStringLimit)
952
            self.putValue(data, Hex4EncodedLittleEndian, elided=elided)
hjk's avatar
hjk committed
953
            self.putNumChild(0)
hjk's avatar
hjk committed
954
            return
hjk's avatar
hjk committed
955

hjk's avatar
hjk committed
956
        if format == Ucs4StringFormat:
hjk's avatar
hjk committed
957 958
            # Explicitly requested UCS-4 formatting.
            self.putType(typeName)
959
            (elided, data) = self.encodeCArray(value, 4, self.displayStringLimit)
960
            self.putValue(data, Hex8EncodedLittleEndian, elided=elided)
hjk's avatar
hjk committed
961
            self.putNumChild(0)
hjk's avatar
hjk committed
962
            return
hjk's avatar
hjk committed
963

hjk's avatar
hjk committed
964 965
        if not format is None \
            and format >= Array10Format and format <= Array1000Format:
966
            # Explicitly requested formatting as array of n items.
hjk's avatar
hjk committed
967
            n = (10, 100, 1000, 10000)[format - Array10Format]
hjk's avatar
hjk committed
968
            self.putType(typeName)
969
            self.putItemCount(n)
970
            self.putArrayData(value, n, innerType)
hjk's avatar
hjk committed
971
            return
hjk's avatar
hjk committed
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996

        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
997
                self.currentChildType = self.stripClassTag(innerTypeName)
hjk's avatar
hjk committed
998 999 1000
                self.putItem(value.dereference())
                self.currentChildType = savedCurrentChildType
                #self.putPointerValue(value)
1001
                self.putOriginalAddress(value)
hjk's avatar
hjk committed
1002
                return
hjk's avatar
hjk committed
1003 1004

        #warn("GENERIC PLAIN POINTER: %s" % value.type)
1005
        #warn("ADDR PLAIN POINTER: 0x%x" % value.address)
hjk's avatar
hjk committed
1006 1007 1008 1009 1010 1011 1012 1013
        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())

1014 1015 1016 1017
    def putOriginalAddress(self, value):
        if not value.address is None:
            self.put('origaddr="0x%x",' % toInteger(value.address))

1018 1019 1020 1021 1022
    def putQObjectNameValue(self, value):
        try:
            intSize = self.intSize()
            ptrSize = self.ptrSize()
            # dd = value["d_ptr"]["d"] is just behind the vtable.
1023
            dd = self.extractPointer(value, offset=ptrSize)
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037

            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
1038
                objectName = self.extractPointer(dd + 5 * ptrSize + 2 * intSize)
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048

            else:
                # Size of QObjectData: 5 pointer + 2 int
                #   - vtable
                #   - QObject *q_ptr;
                #   - QObject *parent;
                #   - QObjectList children;
                #   - uint isWidget : 1; etc...
                #   - int postedEvents;
                #   - QDynamicMetaObjectData *metaObject;
1049
                extra = self.extractPointer(dd + 5 * ptrSize + 2 * intSize)
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
                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
1060
                objectName = self.extractPointer(extra + 5 * ptrSize)
1061 1062 1063

            data, size, alloc = self.byteArrayDataHelper(objectName)

1064 1065 1066
            # Object names are short, and GDB can crash on to big chunks.
            # Since this here is a convenience feature only, limit it.
            if size <= 0 or size > 80:
1067 1068
                return False

hjk's avatar
hjk committed
1069 1070
            raw = self.readMemory(data, 2 * size)
            self.putValue(raw, Hex4EncodedLittleEndian, 1)
1071 1072 1073
            return True

        except:
1074
        #    warn("NO QOBJECT: %s" % value.type)
1075 1076
            pass

1077

1078
    def extractStaticMetaObjectHelper(self, typeobj):
1079 1080 1081 1082
        """
        Checks whether type has a Q_OBJECT macro.
        Returns the staticMetaObject, or 0.
        """
1083 1084

        if self.isSimpleType(typeobj):
1085 1086
            return 0

1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097
        typeName = str(typeobj)
        isQObjectProper = typeName == self.qtNamespace() + "QObject"

        if not isQObjectProper:
            if self.directBaseClass(typeobj, 0) is None:
                return 0

            # No templates for now.
            if typeName.find('<') >= 0:
                return 0

1098
        result = self.findStaticMetaObject(typeName)
1099 1100 1101 1102

        # 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)
1103 1104 1105
        if result and not isQObjectProper:
            superdata = self.extractPointer(result)
            if toInteger(superdata) == 0:
1106
                # This looks like a Q_GADGET
1107
                return 0
1108 1109 1110 1111 1112 1113 1114

        return result

    def extractStaticMetaObject(self, typeobj):
        """
        Checks recursively whether a type derives from QObject.
        """
1115 1116 1117
        if not self.useFancy:
            return 0

1118 1119 1120 1121 1122
        typeName = str(