dumper.py 69.7 KB
Newer Older
1
2
############################################################################
#
Eike Ziller's avatar
Eike Ziller committed
3
4
# Copyright (C) 2015 The Qt Company Ltd.
# Contact: http://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
Eike Ziller's avatar
Eike Ziller committed
12
13
# a written agreement between you and The Qt Company.  For licensing terms and
# conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
# 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
#
Eike Ziller's avatar
Eike Ziller committed
25
26
# In addition, as a special exception, The Qt Company gives you certain additional
# rights.  These rights are described in The Qt Company LGPL Exception
27
28
29
30
31
# 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
import time
hjk's avatar
hjk committed
37
38
import importlib

39
40
41
42
43
44
45
46
47
48
if sys.version_info[0] >= 3:
    xrange = range
    toInteger = int
else:
    toInteger = long


verbosity = 0
verbosity = 1

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


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

hjk's avatar
hjk committed
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# 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)
101

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# Encodings. Keep that synchronized with DebuggerEncoding in debuggerprotocol.h
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, \
IPv6AddressAndHexScopeId, \
Hex2EncodedUtf8WithoutQuotes, \
132
133
134
135
136
137
138
139
140
141
142
143
DateTimeInternal, \
SpecialEmptyValue, \
SpecialUninitializedValue, \
SpecialInvalidValue, \
SpecialNotAccessibleValue, \
SpecialItemCountValue, \
SpecialMinimumItemCountValue, \
SpecialNotCallableValue, \
SpecialNullReferenceValue, \
SpecialOptimizedOutValue, \
SpecialEmptyStructureValue, \
    = range(40)
144
145
146
147
148
149
150
151
152
153

# Display modes. Keep that synchronized with DebuggerDisplay in watchutils.h
StopDisplay, \
DisplayImageData, \
DisplayUtf16String, \
DisplayImageFile, \
DisplayLatin1String, \
DisplayUtf8String, \
DisplayPlotData \
    = range(7)
154
155
156


def arrayForms():
157
    return [ArrayPlotFormat]
158
159

def mapForms():
160
    return [CompactMapFormat]
161
162


163
164
165
166
167
168
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
169
170
171
172
173
174
175
176
177
    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)
178
179


hjk's avatar
hjk committed
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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

194
195
196
    def toBytes(self):
        """Retrieves "lazy" contents from memoryviews."""
        data = self.data
hjk's avatar
hjk committed
197
198
199
200
201
202

        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):
203
204
205
            data = ''.join([c for c in data])
        return data

hjk's avatar
hjk committed
206
207
208
209
    def toString(self):
        data = self.toBytes()
        return data if sys.version_info[0] == 2 else data.decode("utf8")

210
211
212
213
214
215
216
217
218
219
220
221
222
223
    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]
224

225
226
    def extractLong(self, offset = 0):
        return struct.unpack_from("l", self.data, offset)[0]
227

228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
    # 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]

244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
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:
271
            self.childType = d.stripClassTag(str(childType))
272
273
            if not self.d.isCli:
                self.d.put('childtype="%s",' % self.childType)
274
275
276
277
278
279
280
281
282
283
284
            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
285
        self.printsAddress = not self.d.putAddressRange(addrBase, addrStep)
286
287
288
289
290
291
292
293
294
295
296
297

    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
298
        self.d.put(self.d.childrenPrefix)
299
300
301
302
303
304

    def __exit__(self, exType, exValue, exTraceBack):
        if not exType is None:
            if self.d.passExceptions:
                showException("CHILDREN", exType, exValue, exTraceBack)
            self.d.putNumChild(0)
305
            self.d.putSpecialValue(SpecialNotAccessibleValue)
306
307
308
309
310
311
312
313
        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
314
315
        self.d.putNewline()
        self.d.put(self.d.childrenSuffix)
316
317
        return True

318
class PairedChildrenData:
319
320
    def __init__(self, d, pairType, keyType, valueType, useKeyAndValue):
        self.useKeyAndValue = useKeyAndValue
321
        self.pairType = pairType
322
323
        self.keyType = keyType
        self.valueType = valueType
324
        self.isCompact = d.isMapCompact(self.keyType, self.valueType)
325
        self.childType = valueType if self.isCompact else pairType
326
        ns = d.qtNamespace()
327
328
329
330
331
        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")
332

hjk's avatar
hjk committed
333
class PairedChildren(Children):
334
335
    def __init__(self, d, numChild, useKeyAndValue = False,
            pairType = None, keyType = None, valueType = None, maxNumChild = None):
336
        self.d = d
337
338
339
340
        if keyType is None:
            keyType = d.templateArgument(pairType, 0).unqualified()
        if valueType is None:
            valueType = d.templateArgument(pairType, 1)
341
        d.pairData = PairedChildrenData(d, pairType, keyType, valueType, useKeyAndValue)
342

343
        Children.__init__(self, d, numChild,
344
345
346
            d.pairData.childType,
            maxNumChild = maxNumChild,
            addrBase = None, addrStep = None)
347
348
349
350
351
352
353
354
355

    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

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396

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
397
        self.isCli = False
398

399
400
        # Later set, or not set:
        self.stringCutOff = 10000
401
        self.displayStringLimit = 100
402

403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
        self.resetCaches()

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

        self.dumpermodules = [
            "qttypes",
            "stdtypes",
            "misctypes",
            "boosttypes",
            "creatortypes",
            "personaltypes",
        ]


    def resetCaches(self):
419
        # This is a cache mapping from 'type name' to 'display alternatives'.
420
        self.qqFormats = { "QVariant (QVariantMap)" : mapForms() }
421
422
423
424
425
426
427
428
429
430

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

431
432
433
        # 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
434

435

436
437
    def putNewline(self):
        pass
438

439
440
441
442
443
444
445
446
447
448
449
    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

450
451
452
453
454
    def stripForFormat(self, typeName):
        if typeName in self.cachedFormats:
            return self.cachedFormats[typeName]
        stripped = ""
        inArray = 0
455
        for c in self.stripClassTag(typeName):
456
457
458
459
460
461
462
463
464
465
466
467
468
469
            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

470
    # Hex decoding operating on str, return str.
hjk's avatar
hjk committed
471
472
473
474
475
    def hexdecode(self, s):
        if sys.version_info[0] == 2:
            return s.decode("hex")
        return bytes.fromhex(s).decode("utf8")

hjk's avatar
hjk committed
476
    # Hex encoding operating on str or bytes, return str.
hjk's avatar
hjk committed
477
478
479
    def hexencode(self, s):
        if sys.version_info[0] == 2:
            return s.encode("hex")
480
481
482
483
484
        if isinstance(s, str):
            s = s.encode("utf8")
        return base64.b16encode(s).decode("utf8")

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

487
488
489
    def is32bit(self):
        return self.ptrSize() == 4

hjk's avatar
hjk committed
490
491
492
    def is64bit(self):
        return self.ptrSize() == 8

493
494
495
496
    def isQt3Support(self):
        # assume no Qt 3 support by default
        return False

497
498
499
    def lookupQtType(self, typeName):
        return self.lookupType(self.qtNamespace() + typeName)

500
    # Clamps size to limit.
501
    def computeLimit(self, size, limit):
hjk's avatar
hjk committed
502
503
        if limit == 0:
            limit = self.displayStringLimit
504
505
506
        if limit is None or size <= limit:
            return 0, size
        return size, limit
507

508
509
510
511
512
513
514
515
516
517
518
    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

519
520
521
522
523
524
525
526
527
    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
528
            data = addr + self.extractPointer(addr + 8 + self.ptrSize())
529
530
531
532
            if self.ptrSize() == 4:
                data = data & 0xffffffff
            else:
                data = data & 0xffffffffffffffff
533
        elif self.qtVersion() >= 0x040000:
534
535
536
537
538
539
540
            # Data:
            # - QBasicAtomicInt ref;
            # - int alloc, size;
            # - [padding]
            # - char *data;
            alloc = self.extractInt(addr + 4)
            size = self.extractInt(addr + 8)
541
            data = self.extractPointer(addr + 8 + self.ptrSize())
542
543
544
545
546
547
548
549
550
        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())
551
552
553
        return data, size, alloc

    # addr is the begin of a QByteArrayData structure
554
    def encodeStringHelper(self, addr, limit):
555
556
557
        # Should not happen, but we get it with LLDB as result
        # of inferior calls
        if addr == 0:
558
            return 0, ""
559
560
561
        data, size, alloc = self.byteArrayDataHelper(addr)
        if alloc != 0:
            self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
562
563
        elided, shown = self.computeLimit(size, limit)
        return elided, self.readMemory(data, 2 * shown)
564

565
    def encodeByteArrayHelper(self, addr, limit):
566
567
568
        data, size, alloc = self.byteArrayDataHelper(addr)
        if alloc != 0:
            self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
569
570
        elided, shown = self.computeLimit(size, limit)
        return elided, self.readMemory(data, shown)
571

572
    def putStdStringHelper(self, data, size, charSize, displayFormat = AutomaticFormat):
573
574
575
576
        bytelen = size * charSize
        elided, shown = self.computeLimit(bytelen, self.displayStringLimit)
        mem = self.readMemory(data, shown)
        if charSize == 1:
577
578
            if displayFormat == Latin1StringFormat \
                    or displayFormat == SeparateLatin1StringFormat:
579
580
581
                encodingType = Hex2EncodedLatin1
            else:
                encodingType = Hex2EncodedUtf8
582
583
584
585
586
587
588
589
590
591
592
            displayType = DisplayLatin1String
        elif charSize == 2:
            encodingType = Hex4EncodedLittleEndian
            displayType = DisplayUtf16String
        else:
            encodingType = Hex8EncodedLittleEndian
            displayType = DisplayUtf16String

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

593
        if displayFormat == SeparateLatin1StringFormat \
594
                or displayFormat == SeparateUtf8StringFormat:
595
596
597
598
            self.putField("editformat", displayType)
            elided, shown = self.computeLimit(bytelen, 100000)
            self.putField("editvalue", self.readMemory(data, shown))

hjk's avatar
hjk committed
599
    def readMemory(self, addr, size):
600
601
        data = self.extractBlob(addr, size).toBytes()
        return self.hexencode(data)
hjk's avatar
hjk committed
602

603
    def encodeByteArray(self, value, limit = 0):
604
605
        elided, data = self.encodeByteArrayHelper(self.extractPointer(value), limit)
        return data
606
607

    def byteArrayData(self, value):
608
        return self.byteArrayDataHelper(self.extractPointer(value))
609

hjk's avatar
hjk committed
610
611
612
    def putByteArrayValue(self, value):
        elided, data = self.encodeByteArrayHelper(self.extractPointer(value), self.displayStringLimit)
        self.putValue(data, Hex2EncodedLatin1, elided=elided)
613

614
    def encodeString(self, value, limit = 0):
615
616
        elided, data = self.encodeStringHelper(self.extractPointer(value), limit)
        return data
617

618
619
620
621
622
623
    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))

624
    def stringData(self, value):
625
        return self.byteArrayDataHelper(self.extractPointer(value))
626

627
628
629
630
631
632
633
634
635
    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)

636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
    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
664
665
666
667
    def putStringValueByAddress(self, addr):
        elided, data = self.encodeStringHelper(addr, self.displayStringLimit)
        self.putValue(data, Hex4EncodedLittleEndian, elided=elided)

668
    def putStringValue(self, value):
hjk's avatar
hjk committed
669
670
671
        elided, data = self.encodeStringHelper(
            self.extractPointer(value),
            self.displayStringLimit)
672
        self.putValue(data, Hex4EncodedLittleEndian, elided=elided)
673

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

698
699
700
701
702
703
704
    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):
705
                self.putSpecialValue(SpecialNotCallableValue);
706
707
708
709
                self.putNumChild(0)

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

711
712
713
714
715
716
717
    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
718
719
            #warn("ADDRBASE: %s" % base)
            #warn("ADDRSTEP: %s" % step)
720
            pass
721
722
723
        return False

        #warn("CHILDREN: %s %s %s" % (numChild, childType, childNumChild))
724
    def putMapName(self, value, index = None):
725
        ns = self.qtNamespace()
726
727
        typeName = self.stripClassTag(str(value.type))
        if typeName == ns + "QString":
728
729
            self.put('key="%s",' % self.encodeString(value))
            self.put('keyencoded="%s",' % Hex4EncodedLittleEndian)
730
        elif typeName == ns + "QByteArray":
731
732
            self.put('key="%s",' % self.encodeByteArray(value))
            self.put('keyencoded="%s",' % Hex2EncodedLatin1)
733
734
735
        elif typeName == "std::string":
            self.put('key="%s",' % self.encodeStdString(value))
            self.put('keyencoded="%s",' % Hex2EncodedLatin1)
736
        else:
737
            val = str(value.GetValue()) if self.isLldb else str(value)
738
739
            if index is None:
                key = '%s' % val
740
            else:
741
                key = '[%s] %s' % (index, val)
742
            self.put('key="%s",' % self.hexencode(key))
743
            self.put('keyencoded="%s",' % Hex2EncodedUtf8WithoutQuotes)
744

745
    def putPair(self, pair, index = None):
746
        if self.pairData.useKeyAndValue:
747
748
            key = pair["key"]
            value = pair["value"]
749
750
751
        else:
            key = pair["first"]
            value = pair["second"]
752
753
754
755
756
757
758
        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)
759
760
761
            elif self.pairData.keyIsStdString:
                self.put('key="%s",' % self.encodeStdString(key))
                self.put('keyencoded="%s",' % Hex2EncodedLatin1)
762
763
764
765
766
            else:
                name = str(key.GetValue()) if self.isLldb else str(key)
                if index == -1:
                    self.put('name="%s",' % name)
                else:
767
                    self.put('key="[%s] %s",' % (index, name))
768
769
770
            self.putItem(value)
        else:
            self.putEmptyValue()
771
772
            self.putNumChild(2)
            self.putField("iname", self.currentIName)
773
            if self.isExpanded():
774
                with Children(self):
775
776
777
778
779
780
                    if self.pairData.useKeyAndValue:
                        self.putSubItem("key", key)
                        self.putSubItem("value", value)
                    else:
                        self.putSubItem("first", key)
                        self.putSubItem("second", value)
781

782
783
784
785
786
787
788
    def putPlainChildren(self, value, dumpBase = True):
        self.putEmptyValue(-99)
        self.putNumChild(1)
        if self.isExpanded():
            with Children(self):
                self.putFields(value, dumpBase)

789
    def isMapCompact(self, keyType, valueType):
790
791
        if self.currentItemFormat() == CompactMapFormat:
            return True
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
        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)

810
811
812
813
    def readToFirstZero(self, p, tsize, maximum):
        code = (None, "b", "H", None, "I")[tsize]
        base = toInteger(p)
        blob = self.extractBlob(base, maximum).toBytes()
814
        for i in xrange(0, maximum, tsize):
815
816
817
818
            t = struct.unpack_from(code, blob, i)[0]
            if t == 0:
                return 0, i, self.hexencode(blob[:i])

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

822
823
824
    def encodeCArray(self, p, tsize, limit):
        elided, shown, blob = self.readToFirstZero(p, tsize, limit)
        return elided, blob
hjk's avatar
hjk committed
825

826
827
828
    def putItemCount(self, count, maximum = 1000000000):
        # This needs to override the default value, so don't use 'put' directly.
        if count > maximum:
829
            self.putSpecialValue(SpecialMinimumItemCountValue, maximum)
830
        else:
831
            self.putSpecialValue(SpecialItemCountValue, count)
832
        self.putNumChild(count)
833

834
835
836
837
838
    def putField(self, name, value):
        self.put('%s="%s",' % (name, value))

    def putType(self, type, priority = 0):
        # Higher priority values override lower ones.
839
840
841
        if priority >= self.currentType.priority:
            self.currentType.value = str(type)
            self.currentType.priority = priority
842

843
    def putValue(self, value, encoding = None, priority = 0, elided = None):
844
        # Higher priority values override lower ones.
845
846
847
        # 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
848
            self.currentValue = ReportItem(value, encoding, priority, elided)
849

850
851
852
    def putSpecialValue(self, encoding, value = ""):
        self.putValue(value, encoding)

853
    def putEmptyValue(self, priority = -10):
854
        if priority >= self.currentValue.priority:
hjk's avatar
hjk committed
855
            self.currentValue = ReportItem("", None, priority, None)
856
857
858
859

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

860
    def putBetterType(self, type):
hjk's avatar
hjk committed
861
862
863
864
        if isinstance(type, ReportItem):
            self.currentType.value = str(type.value)
        else:
            self.currentType.value = str(type)
865
866
        self.currentType.priority += 1

867
868
869
870
871
872
873
874
    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)
875
        self.currentValue.value = None
876

877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
    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)

894
    def putCStyleArray(self, value):
895
        arrayType = value.type.unqualified()
896
        innerType = value[0].type
897
        innerTypeName = str(innerType.unqualified())
898
        ts = innerType.sizeof
899

900
901
902
903
        try:
            self.putValue("@0x%x" % self.addressOf(value), priority = -1)
        except:
            self.putEmptyValue()
904
905
906
907
908
909
910
        self.putType(arrayType)

        try:
            p = self.addressOf(value)
        except:
            p = None

911
        displayFormat = self.currentItemFormat()
912
913
914
915
916
917
918
919
        arrayByteSize = arrayType.sizeof
        if arrayByteSize == 0:
            # This should not happen. But it does, see QTCREATORBUG-14755.
            # GDB/GCC produce sizeof == 0 for QProcess arr[3]
            s = str(value.type)
            arrayByteSize = int(s[s.find('[')+1:s.find(']')]) * ts;

        n = int(arrayByteSize / ts)
920
        if displayFormat != RawFormat:
921
922
            if innerTypeName == "char":
                # Use Latin1 as default for char [].
923
                blob = self.readMemory(self.addressOf(value), arrayByteSize)
924
925
                self.putValue(blob, Hex2EncodedLatin1)
            elif innerTypeName == "wchar_t":
926
                blob = self.readMemory(self.addressOf(value), arrayByteSize)
927
928
929
930
                if innerType.sizeof == 2:
                    self.putValue(blob, Hex4EncodedLittleEndian)
                else:
                    self.putValue(blob, Hex8EncodedLittleEndian)
931
            elif p:
932
933
                self.tryPutSimpleFormattedPointer(p, arrayType, innerTypeName,
                    displayFormat, arrayByteSize)
934
        self.putNumChild(n)
935

hjk's avatar
hjk committed
936
        if self.isExpanded():
937
938
939
            with Children(self):
                for i in range(n):
                    self.putSubItem(i, value[i])
940

941
        self.putPlotDataHelper(p, n, innerType)
hjk's avatar
hjk committed
942

943
944
945
946
947
948
949
950
951
952
953
    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))
954
955
956
957
958
959
960
961
            try:
                warn("ADDR: %s" % addr)
            except:
                pass
            try:
                warn("TYPE: %s" % addr.type)
            except:
                pass
962
963
            return str(addr)

964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
    def tryPutPrettyItem(self, typeName, value):
        if self.useFancy and self.currentItemFormat() != RawFormat:
            self.putType(typeName)

            nsStrippedType = self.stripNamespaceFromType(typeName)\
                .replace("::", "__")

            # The following block is only needed for D.
            if nsStrippedType.startswith("_A"):
                # DMD v2.058 encodes string[] as _Array_uns long long.
                # With spaces.
                if nsStrippedType.startswith("_Array_"):
                    qdump_Array(self, value)
                    return True
                if nsStrippedType.startswith("_AArray_"):
                    qdump_AArray(self, value)
                    return True

            dumper = self.qqDumpers.get(nsStrippedType)
            if not dumper is None:
                dumper(self, value)
                return True

        return False

989
990
    def putSimpleCharArray(self, base, size = None):
        if size is None:
991
            elided, shown, data = self.readToFirstZero(base, 1, self.displayStringLimit)
992
993
        else:
            elided, shown = self.computeLimit(int(size), self.displayStringLimit)
994
            data = self.readMemory(base, shown)
995
996
        self.putValue(data, Hex2EncodedLatin1, elided=elided)

997
    def putDisplay(self, editFormat, value):
998
        self.put('editformat="%s",' % editFormat)
999
        self.put('editvalue="%s",' % value)
1000

For faster browsing, not all history is shown. View entire blame