dumper.py 152 KB
Newer Older
1 2
############################################################################
#
3 4
# Copyright (C) 2016 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
5 6 7 8 9 10 11
#
# 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
12 13 14
# a written agreement between you and The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
15
#
16 17 18 19 20 21 22
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 24 25 26
#
############################################################################

import os
Christian Stenger's avatar
Christian Stenger committed
27
import codecs
28
import copy
29
import collections
30
import struct
31 32
import sys
import base64
hjk's avatar
hjk committed
33
import re
34
import time
35
import inspect
36
import threading
hjk's avatar
hjk committed
37

38 39 40 41 42 43 44 45
try:
    # That's only used in native combined debugging right now, so
    # we do not need to hard fail in cases of partial python installation
    # that will never use this.
    import json
except:
    pass

46 47 48 49 50 51 52
if sys.version_info[0] >= 3:
    xrange = range
    toInteger = int
else:
    toInteger = long


53 54 55 56 57 58 59 60 61 62
# Debugger start modes. Keep in sync with DebuggerStartMode in debuggerconstants.h
NoStartMode, \
StartInternal, \
StartExternal,  \
AttachExternal,  \
AttachCrashedExternal,  \
AttachCore, \
AttachToRemoteServer, \
AttachToRemoteProcess, \
StartRemoteProcess, \
63
    = range(0, 9)
64 65


hjk's avatar
hjk committed
66
# Known special formats. Keep in sync with DisplayFormat in debuggerprotocol.h
67 68 69 70 71
AutomaticFormat, \
RawFormat, \
SimpleFormat, \
EnhancedFormat, \
SeparateFormat, \
hjk's avatar
hjk committed
72
Latin1StringFormat, \
73
SeparateLatin1StringFormat, \
hjk's avatar
hjk committed
74
Utf8StringFormat, \
75
SeparateUtf8StringFormat, \
hjk's avatar
hjk committed
76 77 78 79 80 81
Local8BitStringFormat, \
Utf16StringFormat, \
Ucs4StringFormat, \
Array10Format, \
Array100Format, \
Array1000Format, \
82
Array10000Format, \
83 84 85 86 87
ArrayPlotFormat, \
CompactMapFormat, \
DirectQListStorageFormat, \
IndirectQListStorageFormat, \
    = range(0, 20)
hjk's avatar
hjk committed
88

hjk's avatar
hjk committed
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
# Breakpoints. Keep synchronized with BreakpointType in breakpoint.h
UnknownType, \
BreakpointByFileAndLine, \
BreakpointByFunction, \
BreakpointByAddress, \
BreakpointAtThrow, \
BreakpointAtCatch, \
BreakpointAtMain, \
BreakpointAtFork, \
BreakpointAtExec, \
BreakpointAtSysCall, \
WatchpointAtAddress, \
WatchpointAtExpression, \
BreakpointOnQmlSignalEmit, \
BreakpointAtJavaScriptThrow, \
    = range(0, 14)
105

hjk's avatar
hjk committed
106

107
# Internal codes for types keep in sync with cdbextensions pytype.cpp
hjk's avatar
hjk committed
108 109 110 111 112 113 114 115 116 117 118 119 120
TypeCodeTypedef, \
TypeCodeStruct, \
TypeCodeVoid, \
TypeCodeIntegral, \
TypeCodeFloat, \
TypeCodeEnum, \
TypeCodePointer, \
TypeCodeArray, \
TypeCodeComplex, \
TypeCodeReference, \
TypeCodeFunction, \
TypeCodeMemberPointer, \
TypeCodeFortranString, \
121
TypeCodeUnresolvable, \
122 123
TypeCodeBitfield, \
    = range(0, 15)
hjk's avatar
hjk committed
124 125 126 127 128 129 130 131 132 133

def isIntegralTypeName(name):
    return name in ('int', 'unsigned int', 'signed int',
                    'short', 'unsigned short',
                    'long', 'unsigned long',
                    'long long', 'unsigned long long',
                    'char', 'signed char', 'unsigned char',
                    'bool')

def isFloatingPointTypeName(name):
hjk's avatar
hjk committed
134
    return name in ('float', 'double', 'long double')
hjk's avatar
hjk committed
135 136


137
def arrayForms():
138
    return [ArrayPlotFormat]
139 140

def mapForms():
141
    return [CompactMapFormat]
142 143


144 145
class ReportItem:
    """
146
    Helper structure to keep temporary 'best' information about a value
147 148 149
    or a type scheduled to be reported. This might get overridden be
    subsequent better guesses during a putItem() run.
    """
hjk's avatar
hjk committed
150 151 152 153 154 155 156
    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):
157
        return 'Item(value: %s, encoding: %s, priority: %s, elided: %s)' \
hjk's avatar
hjk committed
158
            % (self.value, self.encoding, self.priority, self.elided)
159 160


161
def warn(message):
162
    DumperBase.warn(message)
163

164
def xwarn(message):
165
    warn(message)
166 167 168
    import traceback
    traceback.print_stack()

169 170 171
def error(message):
    raise RuntimeError(message)

172
def showException(msg, exType, exValue, exTraceback):
173
    DumperBase.showException(msg, exType, exValue, exTraceback)
174 175 176 177 178 179 180 181 182 183 184

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
        if childType is None:
            self.childType = None
        else:
hjk's avatar
hjk committed
185
            self.childType = childType.name
186
            if not self.d.isCli:
187
                self.d.putField('childtype', self.childType)
188
            if childNumChild is not None:
189
                self.d.putField('childnumchild', childNumChild)
190
                self.childNumChild = childNumChild
191 192
        if addrBase is not None and addrStep is not None:
            self.d.put('addrbase="0x%x",addrstep="%d",' % (addrBase, addrStep))
193 194 195 196 197 198 199 200 201 202

    def __enter__(self):
        self.savedChildType = self.d.currentChildType
        self.savedChildNumChild = self.d.currentChildNumChild
        self.savedNumChild = self.d.currentNumChild
        self.savedMaxNumChild = self.d.currentMaxNumChild
        self.d.currentChildType = self.childType
        self.d.currentChildNumChild = self.childNumChild
        self.d.currentNumChild = self.numChild
        self.d.currentMaxNumChild = self.maxNumChild
203
        self.d.put(self.d.childrenPrefix)
204 205

    def __exit__(self, exType, exValue, exTraceBack):
206
        if exType is not None:
207
            if self.d.passExceptions:
208 209
                showException('CHILDREN', exType, exValue, exTraceBack)
            self.d.putSpecialValue('notaccessible')
210 211
            self.d.putNumChild(0)
        if self.d.currentMaxNumChild is not None:
212 213 214 215 216 217
            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
218 219
        if self.d.isCli:
            self.output += '\n' + '   ' * self.indent
220
        self.d.put(self.d.childrenSuffix)
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
        return True


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 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
245
        self.iname = '%s.%s' % (self.d.currentIName, component)
246 247 248
        self.name = None

class DumperBase:
249 250 251 252 253 254 255 256 257 258 259 260 261 262
    @staticmethod
    def warn(message):
        print('bridgemessage={msg="%s"},' % message.replace('"', '$').encode('latin1'))

    @staticmethod
    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

263 264 265 266
    def __init__(self):
        self.isCdb = False
        self.isGdb = False
        self.isLldb = False
267
        self.isCli = False
268

269 270
        # Later set, or not set:
        self.stringCutOff = 10000
271
        self.displayStringLimit = 100
272

273 274 275
        self.typesReported = {}
        self.typesToReport = {}
        self.qtNamespaceToReport = None
276
        self.qtCustomEventFunc = 0
277
        self.qtCustomEventPltFunc = 0
278
        self.qtPropertyFunc = 0
279
        self.passExceptions = False
280
        self.isTesting = False
281

282
        self.typeData = {}
283 284
        self.isBigEndian = False
        self.packCode = '<'
285

286
        self.resetCaches()
287
        self.resetStats()
288 289 290 291 292

        self.childrenPrefix = 'children=['
        self.childrenSuffix = '],'

        self.dumpermodules = [
293 294 295 296 297 298 299
            'qttypes',
            'stdtypes',
            'misctypes',
            'boosttypes',
            'opencvtypes',
            'creatortypes',
            'personaltypes',
300 301
        ]

302 303 304 305 306 307 308 309 310 311 312
        # These values are never used, but the variables need to have
        # some value base for the swapping logic in Children.__enter__()
        # and Children.__exit__().
        self.currentIName = None
        self.currentValue = None
        self.currentType = None
        self.currentNumChild = None
        self.currentMaxNumChild = None
        self.currentPrintsAddress = True
        self.currentChildType = None
        self.currentChildNumChild = None
David Schulz's avatar
David Schulz committed
313
        self.registerKnownTypes()
314 315 316 317 318 319 320 321 322 323 324 325 326

    def setVariableFetchingOptions(self, args):
        self.resultVarName = args.get('resultvarname', '')
        self.expandedINames = set(args.get('expanded', []))
        self.stringCutOff = int(args.get('stringcutoff', 10000))
        self.displayStringLimit = int(args.get('displaystringlimit', 100))
        self.typeformats = args.get('typeformats', {})
        self.formats = args.get('formats', {})
        self.watchers = args.get('watchers', {})
        self.useDynamicType = int(args.get('dyntype', '0'))
        self.useFancy = int(args.get('fancy', '0'))
        self.forceQtNamespace = int(args.get('forcens', '0'))
        self.passExceptions = int(args.get('passexceptions', '0'))
327
        self.isTesting = int(args.get('testing', '0'))
328
        self.showQObjectNames = int(args.get('qobjectnames', '1'))
329 330 331
        self.nativeMixed = int(args.get('nativemixed', '0'))
        self.autoDerefPointers = int(args.get('autoderef', '0'))
        self.partialVariable = args.get('partialvar', '')
332 333
        self.uninitialized = args.get('uninitialized', [])
        self.uninitialized = list(map(lambda x: self.hexdecode(x), self.uninitialized))
334 335 336 337 338 339
        self.partialUpdate = int(args.get('partial', '0'))
        self.fallbackQtVersion = 0x50200
        #warn('NAMESPACE: "%s"' % self.qtNamespace())
        #warn('EXPANDED INAMES: %s' % self.expandedINames)
        #warn('WATCHERS: %s' % self.watchers)

340
    def resetCaches(self):
341
        # This is a cache mapping from 'type name' to 'display alternatives'.
342
        self.qqFormats = { 'QVariant (QVariantMap)' : mapForms() }
343 344

        # This is a cache of all known dumpers.
345 346
        self.qqDumpers = {}    # Direct type match
        self.qqDumpersEx = {}  # Using regexp
347 348 349 350 351 352 353

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

354 355 356
        # 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
357

358
        self.counts = {}
359
        self.structPatternCache = {}
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
        self.pretimings = {}
        self.timings = []

    def resetStats(self):
        # Timing collection
        self.pretimings = {}
        self.timings = []
        pass

    def dumpStats(self):
        msg = [self.counts, self.timings]
        self.resetStats()
        return msg

    def bump(self, key):
        if key in self.counts:
            self.counts[key] += 1
        else:
            self.counts[key] = 1

    def preping(self, key):
381
        return
382 383 384
        self.pretimings[key] = time.time()

    def ping(self, key):
385
        return
hjk's avatar
hjk committed
386
        elapsed = int(1000 * (time.time() - self.pretimings[key]))
387
        self.timings.append([key, elapsed])
388

hjk's avatar
hjk committed
389 390 391 392 393
    def childRange(self):
        if self.currentMaxNumChild is None:
            return xrange(0, self.currentNumChild)
        return xrange(min(self.currentMaxNumChild, self.currentNumChild))

394 395
    def enterSubItem(self, item):
        if not item.iname:
396
            item.iname = '%s.%s' % (self.currentIName, item.name)
397
        if not self.isCli:
398 399
            self.put('{')
            if isinstance(item.name, str):
400
                self.putField('name', item.name)
401
        else:
402 403 404 405 406 407 408 409 410 411 412 413
            self.indent += 1
            self.output += '\n' + '   ' * self.indent
            if isinstance(item.name, str):
                self.output += item.name + ' = '
        item.savedIName = self.currentIName
        item.savedValue = self.currentValue
        item.savedType = self.currentType
        self.currentIName = item.iname
        self.currentValue = ReportItem();
        self.currentType = ReportItem();

    def exitSubItem(self, item, exType, exValue, exTraceBack):
414
        #warn('CURRENT VALUE: %s: %s %s' %
415 416 417
        # (self.currentIName, self.currentValue, self.currentType))
        if not exType is None:
            if self.passExceptions:
418 419
                showException('SUBITEM', exType, exValue, exTraceBack)
            self.putSpecialValue('notaccessible')
420
            self.putNumChild(0)
421
        if not self.isCli:
422 423 424 425
            try:
                if self.currentType.value:
                    typeName = self.currentType.value
                    if len(typeName) > 0 and typeName != self.currentChildType:
426
                        self.putField('type', typeName)
427 428 429 430 431 432 433 434 435 436 437
                if self.currentValue.value is None:
                    self.put('value="",encoding="notaccessible",numchild="0",')
                else:
                    if not self.currentValue.encoding is None:
                        self.put('valueencoded="%s",' % self.currentValue.encoding)
                    if self.currentValue.elided:
                        self.put('valueelided="%s",' % self.currentValue.elided)
                    self.put('value="%s",' % self.currentValue.value)
            except:
                pass
            self.put('},')
438
        else:
439 440 441
            self.indent -= 1
            try:
                if self.currentType.value:
hjk's avatar
hjk committed
442
                    typeName = self.currentType.value
443 444 445 446 447 448
                    self.put('<%s> = {' % typeName)

                if  self.currentValue.value is None:
                    self.put('<not accessible>')
                else:
                    value = self.currentValue.value
449
                    if self.currentValue.encoding == 'latin1':
450
                        value = self.hexdecode(value)
451
                    elif self.currentValue.encoding == 'utf8':
452
                        value = self.hexdecode(value)
453
                    elif self.currentValue.encoding == 'utf16':
Christian Stenger's avatar
Christian Stenger committed
454
                        b = bytes(bytearray.fromhex(value))
455 456 457 458 459 460 461 462 463 464 465 466 467
                        value = codecs.decode(b, 'utf-16')
                    self.put('"%s"' % value)
                    if self.currentValue.elided:
                        self.put('...')

                if self.currentType.value:
                    self.put('}')
            except:
                pass
        self.currentIName = item.savedIName
        self.currentValue = item.savedValue
        self.currentType = item.savedType
        return True
468 469

    def stripForFormat(self, typeName):
470
        if not isinstance(typeName, str):
471
            error('Expected string in stripForFormat(), got %s' % type(typeName))
472 473
        if typeName in self.cachedFormats:
            return self.cachedFormats[typeName]
474
        stripped = ''
475
        inArray = 0
hjk's avatar
hjk committed
476
        for c in typeName:
477 478 479 480 481 482 483 484 485 486 487 488 489 490
            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

491 492 493
    def templateArgument(self, typeobj, position):
        return typeobj.templateArgument(position)

494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
    def intType(self):
        result = self.lookupType('int')
        self.intType = lambda: result
        return result

    def charType(self):
        result = self.lookupType('char')
        self.intType = lambda: result
        return result

    def ptrSize(self):
        result = self.lookupType('void*').size()
        self.ptrSize = lambda: result
        return result

509 510 511 512
    def lookupType(self, typeName):
        nativeType = self.lookupNativeType(typeName)
        return None if nativeType is None else self.fromNativeType(nativeType)

David Schulz's avatar
David Schulz committed
513
    def registerKnownTypes(self):
514 515 516 517 518 519 520 521
        typeId = 'unsigned short'
        tdata = self.TypeData(self)
        tdata.name = typeId
        tdata.typeId = typeId
        tdata.lbitsize = 16
        tdata.code = TypeCodeIntegral
        self.registerType(typeId, tdata)

David Schulz's avatar
David Schulz committed
522 523 524 525 526 527
        typeId = 'QChar'
        tdata = self.TypeData(self)
        tdata.name = typeId
        tdata.typeId = typeId
        tdata.lbitsize = 16
        tdata.code = TypeCodeStruct
528
        tdata.lfields = [self.Field(dumper=self, name='ucs', type='unsigned short', bitsize=16, bitpos=0)]
David Schulz's avatar
David Schulz committed
529 530 531 532
        tdata.lalignment = 2
        tdata.templateArguments = []
        self.registerType(typeId, tdata)

533 534 535
    def nativeDynamicType(self, address, baseType):
        return baseType # Override in backends.

536
    def listTemplateParameters(self, typename):
hjk's avatar
hjk committed
537 538 539
        return self.listTemplateParametersManually(typename)

    def listTemplateParametersManually(self, typename):
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
        targs = []
        if not typename.endswith('>'):
            return targs

        def push(inner):
            # Handle local struct definitions like QList<main(int, char**)::SomeStruct>
            inner = inner.strip()[::-1]
            p = inner.find(')::')
            if p > -1:
                inner = inner[p+3:].strip()
            if inner.startswith('const '):
                inner = inner[6:].strip()
            if inner.endswith(' const'):
                inner = inner[:-6].strip()
            #warn("FOUND: %s" % inner)
            targs.append(inner)

        #warn("SPLITTING %s" % typename)
        level = 0
        inner = ''
        for c in typename[::-1]: # Reversed...
            #warn("C: %s" % c)
            if c == '>':
                if level > 0:
                    inner += c
                level += 1
            elif c == '<':
                level -= 1
                if level > 0:
                    inner += c
                else:
                    push(inner)
                    inner = ''
573
                    break
574 575 576 577 578 579 580 581 582 583 584 585 586
            elif c == ',':
                #warn('c: %s level: %s' % (c, level))
                if level == 1:
                    push(inner)
                    inner = ''
                else:
                    inner += c
            else:
                inner += c

        #warn("TARGS: %s %s" % (typename, targs))
        res = []
        for item in targs[::-1]:
587 588
            if len(item) == 0:
                continue
589
            c = ord(item[0])
590
            if c in (45, 46) or (c >= 48 and c < 58): # '-', '.' or digit.
591 592 593
                if item.find('.') > -1:
                    res.append(float(item))
                else:
hjk's avatar
hjk committed
594 595 596 597
                    if item.endswith('l'):
                        item = item[:-1]
                    if item.endswith('u'):
                        item = item[:-1]
598 599 600 601 602 603 604 605 606
                    val = toInteger(item)
                    if val > 0x80000000:
                        val -= 0x100000000
                    res.append(val)
            else:
                res.append(self.Type(self, item))
        #warn("RES: %s %s" % (typename, [(None if t is None else t.name) for t in res]))
        return res

607
    # Hex decoding operating on str, return str.
hjk's avatar
hjk committed
608 609
    def hexdecode(self, s):
        if sys.version_info[0] == 2:
610 611
            return s.decode('hex')
        return bytes.fromhex(s).decode('utf8')
hjk's avatar
hjk committed
612

hjk's avatar
hjk committed
613
    # Hex encoding operating on str or bytes, return str.
hjk's avatar
hjk committed
614
    def hexencode(self, s):
615 616
        if s is None:
            s = ''
hjk's avatar
hjk committed
617
        if sys.version_info[0] == 2:
618
            if isinstance(s, buffer):
619 620
                return bytes(s).encode('hex')
            return s.encode('hex')
621
        if isinstance(s, str):
622 623
            s = s.encode('utf8')
        return base64.b16encode(s).decode('utf8')
624

625 626 627 628
    def isQt3Support(self):
        # assume no Qt 3 support by default
        return False

629
    # Clamps size to limit.
630
    def computeLimit(self, size, limit):
hjk's avatar
hjk committed
631 632
        if limit == 0:
            limit = self.displayStringLimit
633 634 635
        if limit is None or size <= limit:
            return 0, size
        return size, limit
636

637 638
    def vectorDataHelper(self, addr):
        if self.qtVersion() >= 0x050000:
639
            if self.ptrSize() == 4:
640
                (ref, size, alloc, offset) = self.split('IIIp', addr)
641
            else:
642
                (ref, size, alloc, pad, offset) = self.split('IIIIp', addr)
643 644
            alloc = alloc & 0x7ffffff
            data = addr + offset
645
        else:
646
            (ref, alloc, size) = self.split('III', addr)
647
            data = addr + 16
648
        self.check(0 <= size and size <= alloc and alloc <= 1000 * 1000 * 1000)
649 650
        return data, size, alloc

651 652 653 654 655 656 657
    def byteArrayDataHelper(self, addr):
        if self.qtVersion() >= 0x050000:
            # QTypedArray:
            # - QtPrivate::RefCount ref
            # - int size
            # - uint alloc : 31, capacityReserved : 1
            # - qptrdiff offset
658
            (ref, size, alloc, offset) = self.split('IIpp', addr)
659 660
            alloc = alloc & 0x7ffffff
            data = addr + offset
661 662 663 664
            if self.ptrSize() == 4:
                data = data & 0xffffffff
            else:
                data = data & 0xffffffffffffffff
665
        elif self.qtVersion() >= 0x040000:
666 667 668 669 670
            # Data:
            # - QBasicAtomicInt ref;
            # - int alloc, size;
            # - [padding]
            # - char *data;
671
            if self.ptrSize() == 4:
672
                (ref, alloc, size, data) = self.split('IIIp', addr)
673
            else:
674
                (ref, alloc, size, pad, data) = self.split('IIIIp', addr)
675 676 677 678 679 680
        else:
            # Data:
            # - QShared count;
            # - QChar *unicode
            # - char *ascii
            # - uint len: 30
681
            (dummy, dummy, dummy, size) = self.split('IIIp', addr)
682 683 684
            size = self.extractInt(addr + 3 * self.ptrSize()) & 0x3ffffff
            alloc = size  # pretend.
            data = self.extractPointer(addr + self.ptrSize())
685 686 687
        return data, size, alloc

    # addr is the begin of a QByteArrayData structure
688
    def encodeStringHelper(self, addr, limit):
689 690 691
        # Should not happen, but we get it with LLDB as result
        # of inferior calls
        if addr == 0:
692
            return 0, ''
693 694 695
        data, size, alloc = self.byteArrayDataHelper(addr)
        if alloc != 0:
            self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
696 697
        elided, shown = self.computeLimit(size, limit)
        return elided, self.readMemory(data, 2 * shown)
698

699
    def encodeByteArrayHelper(self, addr, limit):
700 701 702
        data, size, alloc = self.byteArrayDataHelper(addr)
        if alloc != 0:
            self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
703 704
        elided, shown = self.computeLimit(size, limit)
        return elided, self.readMemory(data, shown)
705

706
    def putCharArrayHelper(self, data, size, charType,
707 708
                           displayFormat = AutomaticFormat,
                           makeExpandable = True):
709
        charSize = charType.size()
710 711 712 713
        bytelen = size * charSize
        elided, shown = self.computeLimit(bytelen, self.displayStringLimit)
        mem = self.readMemory(data, shown)
        if charSize == 1:
714
            if displayFormat in (Latin1StringFormat, SeparateLatin1StringFormat):
715
                encodingType = 'latin1'
716
            else:
717 718
                encodingType = 'utf8'
            #childType = 'char'
719
        elif charSize == 2:
720 721
            encodingType = 'utf16'
            #childType = 'short'
722
        else:
723 724
            encodingType = 'ucs4'
            #childType = 'int'
725 726 727

        self.putValue(mem, encodingType, elided=elided)

728
        if displayFormat in (SeparateLatin1StringFormat, SeparateUtf8StringFormat, SeparateFormat):
729
            elided, shown = self.computeLimit(bytelen, 100000)
730
            self.putDisplay(encodingType + ':separate', self.readMemory(data, shown))
731

732 733 734 735 736
        if makeExpandable:
            self.putNumChild(size)
            if self.isExpanded():
                with Children(self):
                    for i in range(size):
737
                        self.putSubItem(size, self.createValue(data + i * charSize, charType))
738

hjk's avatar
hjk committed
739
    def readMemory(self, addr, size):
740
        return self.hexencode(bytes(self.readRawMemory(addr, size)))
hjk's avatar
hjk committed
741

742
    def encodeByteArray(self, value, limit = 0):
743 744
        elided, data = self.encodeByteArrayHelper(self.extractPointer(value), limit)
        return data
745 746

    def byteArrayData(self, value):
747
        return self.byteArrayDataHelper(self.extractPointer(value))
748

hjk's avatar
hjk committed
749 750
    def putByteArrayValue(self, value):
        elided, data = self.encodeByteArrayHelper(self.extractPointer(value), self.displayStringLimit)
751
        self.putValue(data, 'latin1', elided=elided)
752

753
    def encodeString(self, value, limit = 0):
754 755
        elided, data = self.encodeStringHelper(self.extractPointer(value), limit)
        return data
756

757 758 759 760 761 762
    def encodedUtf16ToUtf8(self, s):
        return ''.join([chr(int(s[i:i+2], 16)) for i in range(0, len(s), 4)])

    def encodeStringUtf8(self, value, limit = 0):
        return self.encodedUtf16ToUtf8(self.encodeString(value, limit))

763
    def stringData(self, value):
764
        return self.byteArrayDataHelper(self.extractPointer(value))
765

766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
    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
792 793 794 795 796 797
        # Handle local struct definitions like QList<main(int, char**)::SomeStruct>
        inner = inner.strip()
        p = inner.find(')::')
        if p > -1:
            inner = inner[p+3:]
        return inner
798

799
    def putStringValue(self, value):
800 801 802
        addr = self.extractPointer(value)
        elided, data = self.encodeStringHelper(addr, self.displayStringLimit)
        self.putValue(data, 'utf16', elided=elided)
803

804 805
    def putPtrItem(self, name, value):
        with SubItem(self, name):
806 807
            self.putValue('0x%x' % value)
            self.putType('void*')
808 809
            self.putNumChild(0)

810 811
    def putIntItem(self, name, value):
        with SubItem(self, name):
812 813 814 815
            if isinstance(value, self.Value):
                self.putValue(value.display())
            else:
                self.putValue(value)
816
            self.putType('int')
817 818 819 820 821
            self.putNumChild(0)

    def putBoolItem(self, name, value):
        with SubItem(self, name):
            self.putValue(value)
822
            self.putType('bool')
823 824
            self.putNumChild(0)

825
    def putPairItem(self, index, pair, keyName='first', valueName='second'):
826
        with SubItem(self, index):
827
            self.putPairContents(index, pair, keyName, valueName)
hjk's avatar
hjk committed
828 829 830 831 832 833 834 835 836 837 838 839

    def putPairContents(self, index, pair, kname, vname):
        with Children(self):
            first, second = pair if isinstance(pair, tuple) else pair.members(False)
            key = self.putSubItem(kname, first)
            value = self.putSubItem(vname, second)
        if index is not None:
            self.putField('keyprefix', '[%s] ' % index)
        self.putField('key', key.value)
        if key.encoding is not None:
            self.putField('keyencoded', key.encoding)
        self.putValue(value.value, value.encoding)
840

841 842 843 844 845 846 847
    def putEnumValue(self, value, vals):
        ival = value.integer()
        nice = vals.get(ival, None)
        display = ('%d' % ival) if nice is None else ('%s (%d)' % (nice, ival))
        self.putValue(display)
        self.putNumChild(0)

848 849 850 851
    def putCallItem(self, name, rettype, value, func, *args):
        with SubItem(self, name):
            try:
                result = self.callHelper(rettype, value, func, args)
852
            except Exception as error:
853 854
                if self.passExceptions:
                    raise error
855 856
                children = [('error', error)]
                self.putSpecialValue("notcallable", children=children)
857
            else:
858 859
                self.putItem(result)

860 861
    def call(self, rettype, value, func, *args):
        return self.callHelper(rettype, value, func, args)
862

863 864 865
    def putAddress(self, address):
        if address is not None and not self.isCli:
            self.put('address="0x%x",' % address)
866

867 868 869 870 871 872 873
    def putPlainChildren(self, value, dumpBase = True):
        self.putEmptyValue(-99)
        self.putNumChild(1)
        if self.isExpanded():
            with Children(self):
                self.putFields(value, dumpBase)

874 875 876 877 878 879 880 881
    def putNamedChildren(self, values, names):
        self.putEmptyValue(-99)
        self.putNumChild(1)
        if self.isExpanded():
            with Children(self):
                for n, v in zip(names, values):
                    self.putSubItem(n, v)

hjk's avatar
hjk committed
882 883 884 885 886 887
    def prettySymbolByAddress(self, address):
        return '0x%x' % address

    def putSymbolValue(self, address):
        self.putValue(self.prettySymbolByAddress(address))

888 889 890 891 892 893 894 895 896 897 898 899
    def putVTableChildren(self, item, itemCount):
        p = item.pointer()
        for i in xrange(itemCount):
            deref = self.extractPointer(p)
            if deref == 0:
                itemCount = i
                break
            with SubItem(self, i):
                self.putItem(self.createPointerValue(deref, 'void'))
                p += self.ptrSize()
        return itemCount

900
    def putFields(self, value, dumpBase = True):
901
        baseIndex = 0
902
        for item in value.members(True):
903 904 905 906 907 908 909
            if item.name is not None:
                if item.name.startswith('_vptr.') or item.name.startswith('__vfptr'):
                    with SubItem(self, '[vptr]'):
                        # int (**)(void)
                        self.putType(' ')
                        self.putField('sortgroup', 20)
                        self.putValue(item.name)
910
                        n = 100
911 912
                        if self.isExpanded():
                            with Children(self):
913
                                n = self.putVTableChildren(item, n)
914 915
                        self.putNumChild(n)
                    continue
916

917
            if item.isBaseClass and dumpBase:
918
                baseIndex += 1
919 920
                # We cannot use nativeField.name as part of the iname as
                # it might contain spaces and other strange characters.
921
                with UnnamedSubItem(self, "@%d" % baseIndex):
922
                    self.putField('iname', self.currentIName)
923
                    self.putField('name', '[%s]' % item.name)
924
                    self.putField('sortgroup', 1000 - baseIndex)
925 926
                    self.putAddress(item.address())
                    self.putItem(item)
927 928
                continue

929 930
            with SubItem(self, item.name):
                self.putItem(item)
931 932


933
    def putMembersItem(self, value, sortorder = 10):
934 935
        with SubItem(self, '[members]'):
            self.putField('sortgroup', sortorder)
936 937
            self.putPlainChildren(value)

938 939 940
    def put(self, stuff):
        self.output += stuff

941 942
    def check(self, exp):
        if not exp:
943
            error('Check failed: %s' % exp)
944 945 946

    def checkRef(self, ref):
        # Assume there aren't a million references to any object.
947 948 949 950 951
        self.check(ref >= -1)
        self.check(ref < 1000000)

    def checkIntType(self, thing):
        if not self.isInt(thing):
952
            error('Expected an integral value, got %s' % type(thing))
953 954 955 956 957

    def readToFirstZero(self, base, tsize, maximum):
        self.checkIntType(base)
        self.checkIntType(tsize)
        self.checkIntType(maximum)
958

959
        code = self.packCode + (None, 'b', 'H', None, 'I')[tsize]
960
        #blob = self.readRawMemory(base, 1)
961 962 963 964 965 966 967
        blob = bytes()
        while maximum > 1:
            try:
                blob = self.readRawMemory(base, maximum)
                break
            except:
                maximum = int(maximum / 2)
968
                warn('REDUCING READING MAXIMUM TO %s' % maximum)
969

970
        #warn('BASE: 0x%x TSIZE: %s MAX: %s' % (base, tsize, maximum))
971
        for i in xrange(0, maximum, tsize):
972 973 974 975
            t = struct.unpack_from(code, blob, i)[0]
            if t == 0:
                return 0, i, self.hexencode(blob[:i])

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

979 980 981
    def encodeCArray(self, p, tsize, limit):
        elided, shown, blob = self.readToFirstZero(p, tsize, limit)
        return elided, blob
hjk's avatar
hjk committed
982

983 984 985
    def putItemCount(self, count, maximum = 1000000000):
        # This needs to override the default value, so don't use 'put' directly.
        if count > maximum:
986
            self.putSpecialValue('minimumitemcount', maximum)
987
        else:
988
            self.putSpecialValue('itemcount', count)
989
        self.putNumChild(count)
990

991
    def resultToMi(self, value):
992 993 994
        if type(value) is bool:
            return '"%d"' % int(value)
        if type(value) is dict:
995
            return '{' + ','.join(['%s=%s' % (k, self.resultToMi(v))
996
                                for (k, v) in list(value.items())]) + '}'
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
        if type(value) is list:
            return '[' + ','.join([self.resultToMi(k)
                                for k in list(value.items())]) + ']'