dumper.py 41.4 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
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Contact: http://www.qt-project.org/legal
#
# This file is part of Qt Creator.
#
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and Digia.  For licensing terms and
# conditions see http://qt.digia.com/licensing.  For further information
# use the contact form at http://qt.digia.com/contact-us.
#
# GNU Lesser General Public License Usage
# Alternatively, this file may be used under the terms of the GNU Lesser
# General Public License version 2.1 as published by the Free Software
# Foundation and appearing in the file LICENSE.LGPL included in the
# packaging of this file.  Please review the following information to
# ensure the GNU Lesser General Public License version 2.1 requirements
# will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
#
# In addition, as a special exception, Digia gives you certain additional
# rights.  These rights are described in the Digia Qt LGPL Exception
# version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
#
############################################################################

import os
31
import struct
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import sys
import base64

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


verbosity = 0
verbosity = 1

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

49
50
51
52
53
54
55
56
57
try:
    import subprocess
    def arrayForms():
        if hasPlot():
            return "Normal,Plot"
        return "Normal"
except:
    def arrayForms():
        return "Normal"
58
59


hjk's avatar
hjk committed
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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

74
75
76
77
78
79
80
81
82
    def toBytes(self):
        """Retrieves "lazy" contents from memoryviews."""
        data = self.data
        if isinstance(data, memoryview):
            data = data.tobytes()
        if sys.version_info[0] == 2 and isinstance(data, buffer):
            data = ''.join([c for c in data])
        return data

hjk's avatar
hjk committed
83
84
85
86
    def toString(self):
        data = self.toBytes()
        return data if sys.version_info[0] == 2 else data.decode("utf8")

87
88
89
90
91
92
93
94
95
96
97
98
99
100
    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]
101

102
103
    def extractLong(self, offset = 0):
        return struct.unpack_from("l", self.data, offset)[0]
104

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
    # 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]

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#
# Gnuplot based display for array-like structures.
#
gnuplotPipe = {}
gnuplotPid = {}

def warn(message):
    print("XXX: %s\n" % message.encode("latin1"))


def showException(msg, exType, exValue, exTraceback):
    warn("**** CAUGHT EXCEPTION: %s ****" % msg)
    try:
        import traceback
        for line in traceback.format_exception(exType, exValue, exTraceback):
            warn("%s" % line)
    except:
        pass


def stripClassTag(typeName):
    if typeName.startswith("class "):
        return typeName[6:]
    if typeName.startswith("struct "):
        return typeName[7:]
    if typeName.startswith("const "):
        return typeName[6:]
    if typeName.startswith("volatile "):
        return typeName[9:]
    return typeName


class Children:
    def __init__(self, d, numChild = 1, childType = None, childNumChild = None,
            maxNumChild = None, addrBase = None, addrStep = None):
        self.d = d
        self.numChild = numChild
        self.childNumChild = childNumChild
        self.maxNumChild = maxNumChild
        self.addrBase = addrBase
        self.addrStep = addrStep
        self.printsAddress = True
        if childType is None:
            self.childType = None
        else:
            self.childType = stripClassTag(str(childType))
            self.d.put('childtype="%s",' % self.childType)
            if childNumChild is None:
                pass
                #if self.d.isSimpleType(childType):
                #    self.d.put('childnumchild="0",')
                #    self.childNumChild = 0
                #elif childType.code == PointerCode:
                #    self.d.put('childnumchild="1",')
                #    self.childNumChild = 1
            else:
                self.d.put('childnumchild="%s",' % childNumChild)
                self.childNumChild = childNumChild
        try:
            if not addrBase is None and not addrStep is None:
181
182
                self.d.put('addrbase="0x%x",' % toInteger(addrBase))
                self.d.put('addrstep="0x%x",' % toInteger(addrStep))
183
184
185
                self.printsAddress = False
        except:
            warn("ADDRBASE: %s" % addrBase)
186
            warn("ADDRSTEP: %s" % addrStep)
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
        #warn("CHILDREN: %s %s %s" % (numChild, childType, childNumChild))

    def __enter__(self):
        self.savedChildType = self.d.currentChildType
        self.savedChildNumChild = self.d.currentChildNumChild
        self.savedNumChild = self.d.currentNumChild
        self.savedMaxNumChild = self.d.currentMaxNumChild
        self.savedPrintsAddress = self.d.currentPrintsAddress
        self.d.currentChildType = self.childType
        self.d.currentChildNumChild = self.childNumChild
        self.d.currentNumChild = self.numChild
        self.d.currentMaxNumChild = self.maxNumChild
        self.d.currentPrintsAddress = self.printsAddress
        self.d.put("children=[")

    def __exit__(self, exType, exValue, exTraceBack):
        if not exType is None:
            if self.d.passExceptions:
                showException("CHILDREN", exType, exValue, exTraceBack)
            self.d.putNumChild(0)
            self.d.putValue("<not accessible>")
        if not self.d.currentMaxNumChild is None:
            if self.d.currentMaxNumChild < self.d.currentNumChild:
                self.d.put('{name="<incomplete>",value="",type="",numchild="0"},')
        self.d.currentChildType = self.savedChildType
        self.d.currentChildNumChild = self.savedChildNumChild
        self.d.currentNumChild = self.savedNumChild
        self.d.currentMaxNumChild = self.savedMaxNumChild
        self.d.currentPrintsAddress = self.savedPrintsAddress
        self.d.put('],')
        return True

219
class PairedChildrenData:
220
221
    def __init__(self, d, pairType, keyType, valueType, useKeyAndValue):
        self.useKeyAndValue = useKeyAndValue
222
        self.pairType = pairType
223
224
        self.keyType = keyType
        self.valueType = valueType
225
        self.isCompact = d.isMapCompact(self.keyType, self.valueType)
226
        self.childType = valueType if self.isCompact else pairType
227
228
229
230
        ns = d.qtNamespace()
        self.keyIsQString = str(self.keyType) == ns + "QString"
        self.keyIsQByteArray = str(self.keyType) == ns + "QByteArray"

hjk's avatar
hjk committed
231
class PairedChildren(Children):
232
233
    def __init__(self, d, numChild, useKeyAndValue = False,
            pairType = None, keyType = None, valueType = None, maxNumChild = None):
234
        self.d = d
235
236
237
238
        if keyType is None:
            keyType = d.templateArgument(pairType, 0).unqualified()
        if valueType is None:
            valueType = d.templateArgument(pairType, 1)
239
        d.pairData = PairedChildrenData(d, pairType, keyType, valueType, useKeyAndValue)
240

241
        Children.__init__(self, d, numChild,
242
243
244
            d.pairData.childType,
            maxNumChild = maxNumChild,
            addrBase = None, addrStep = None)
245
246
247
248
249
250
251
252
253

    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

254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295

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

296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
        # Later set, or not set:
        # cachedQtVersion
        self.stringCutOff = 10000

        # This is a cache mapping from 'type name' to 'display alternatives'.
        self.qqFormats = {}

        # This is a cache of all known dumpers.
        self.qqDumpers = {}

        # This is a cache of all dumpers that support writing.
        self.qqEditable = {}

        # This keeps canonical forms of the typenames, without array indices etc.
        self.cachedFormats = {}

312
313
314
        # 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
315

316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336

    def stripForFormat(self, typeName):
        if typeName in self.cachedFormats:
            return self.cachedFormats[typeName]
        stripped = ""
        inArray = 0
        for c in stripClassTag(typeName):
            if c == '<':
                break
            if c == ' ':
                continue
            if c == '[':
                inArray += 1
            elif c == ']':
                inArray -= 1
            if inArray and ord(c) >= 48 and ord(c) <= 57:
                continue
            stripped +=  c
        self.cachedFormats[typeName] = stripped
        return stripped

337
    # Hex decoding operating on str, return str.
hjk's avatar
hjk committed
338
339
340
341
342
    def hexdecode(self, s):
        if sys.version_info[0] == 2:
            return s.decode("hex")
        return bytes.fromhex(s).decode("utf8")

343
    # Hex decoding operating on str or bytes, return str.
hjk's avatar
hjk committed
344
345
346
    def hexencode(self, s):
        if sys.version_info[0] == 2:
            return s.encode("hex")
347
348
349
350
351
        if isinstance(s, str):
            s = s.encode("utf8")
        return base64.b16encode(s).decode("utf8")

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

354
355
356
357
358
    def isArmArchitecture(self):
        return False

    def isQnxTarget(self):
        return False
359

360
361
362
    def is32bit(self):
        return self.ptrSize() == 4

363
364
365
366
    def isQt3Support(self):
        # assume no Qt 3 support by default
        return False

367
368
369
370
    def computeLimit(self, size, limit):
        if limit is None:
            return size
        if limit == 0:
371
            return min(size, self.stringCutOff)
372
373
374
375
376
377
378
379
380
381
382
        return min(size, limit)

    def byteArrayDataHelper(self, addr):
        if self.qtVersion() >= 0x050000:
            # QTypedArray:
            # - QtPrivate::RefCount ref
            # - int size
            # - uint alloc : 31, capacityReserved : 1
            # - qptrdiff offset
            size = self.extractInt(addr + 4)
            alloc = self.extractInt(addr + 8) & 0x7ffffff
383
            data = addr + self.extractPointer(addr + 8 + self.ptrSize())
384
385
386
387
            if self.ptrSize() == 4:
                data = data & 0xffffffff
            else:
                data = data & 0xffffffffffffffff
388
389
390
391
392
393
394
395
        else:
            # Data:
            # - QBasicAtomicInt ref;
            # - int alloc, size;
            # - [padding]
            # - char *data;
            alloc = self.extractInt(addr + 4)
            size = self.extractInt(addr + 8)
396
            data = self.extractPointer(addr + 8 + self.ptrSize())
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
        return data, size, alloc

    # addr is the begin of a QByteArrayData structure
    def encodeStringHelper(self, addr, limit = 0):
        # Should not happen, but we get it with LLDB as result
        # of inferior calls
        if addr == 0:
            return ""
        data, size, alloc = self.byteArrayDataHelper(addr)
        if alloc != 0:
            self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
        limit = self.computeLimit(size, limit)
        s = self.readMemory(data, 2 * limit)
        if limit < size:
            s += "2e002e002e00"
        return s

    def encodeByteArrayHelper(self, addr, limit = None):
        data, size, alloc = self.byteArrayDataHelper(addr)
        if alloc != 0:
            self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
        limit = self.computeLimit(size, limit)
        s = self.readMemory(data, limit)
        if limit < size:
            s += "2e2e2e"
        return s

hjk's avatar
hjk committed
424
    def readMemory(self, addr, size):
425
426
        data = self.extractBlob(addr, size).toBytes()
        return self.hexencode(data)
hjk's avatar
hjk committed
427

428
    def encodeByteArray(self, value):
429
        return self.encodeByteArrayHelper(self.extractPointer(value))
430
431

    def byteArrayData(self, value):
432
        return self.byteArrayDataHelper(self.extractPointer(value))
433
434
435
436

    def putByteArrayValue(self, value):
        return self.putValue(self.encodeByteArray(value), Hex2EncodedLatin1)

hjk's avatar
hjk committed
437
    def putByteArrayValueByAddress(self, addr):
438
        self.putValue(self.encodeByteArrayHelper(self.extractPointer(addr)),
hjk's avatar
hjk committed
439
440
            Hex2EncodedLatin1)

441
    def putStringValueByAddress(self, addr):
442
        self.putValue(self.encodeStringHelper(self.extractPointer(addr)),
443
444
445
            Hex4EncodedLittleEndian)

    def encodeString(self, value):
446
        return self.encodeStringHelper(self.extractPointer(value))
447
448

    def stringData(self, value):
449
        return self.byteArrayDataHelper(self.extractPointer(value))
450

451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
    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()

479
480
481
    def putStringValue(self, value):
        return self.putValue(self.encodeString(value), Hex4EncodedLittleEndian)

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
    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)


507
    def putMapName(self, value, index = -1):
508
509
        ns = self.qtNamespace()
        if str(value.type) == ns + "QString":
510
511
            self.put('key="%s",' % self.encodeString(value))
            self.put('keyencoded="%s",' % Hex4EncodedLittleEndian)
512
        elif str(value.type) == ns + "QByteArray":
513
514
515
            self.put('key="%s",' % self.encodeByteArray(value))
            self.put('keyencoded="%s",' % Hex2EncodedLatin1)
        else:
516
517
518
            val = str(value.GetValue()) if self.isLldb else str(value)
            if index == -1:
                self.put('name="%s",' % val)
519
            else:
520
                self.put('key="[%d] %s",' % (index, val))
521

522
    def putPair(self, pair, index = -1):
523
        if self.pairData.useKeyAndValue:
524
525
            key = pair["key"]
            value = pair["value"]
526
527
528
        else:
            key = pair["first"]
            value = pair["second"]
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
        if self.pairData.isCompact:
            if self.pairData.keyIsQString:
                self.put('key="%s",' % self.encodeString(key))
                self.put('keyencoded="%s",' % Hex4EncodedLittleEndian)
            elif self.pairData.keyIsQByteArray:
                self.put('key="%s",' % self.encodeByteArray(key))
                self.put('keyencoded="%s",' % Hex2EncodedLatin1)
            else:
                name = str(key.GetValue()) if self.isLldb else str(key)
                if index == -1:
                    self.put('name="%s",' % name)
                else:
                    self.put('key="[%d] %s",' % (index, name))
            self.putItem(value)
        else:
            self.putEmptyValue()
545
546
            self.putNumChild(2)
            self.putField("iname", self.currentIName)
547
            if self.isExpanded():
548
                with Children(self):
549
550
551
552
553
554
                    if self.pairData.useKeyAndValue:
                        self.putSubItem("key", key)
                        self.putSubItem("value", value)
                    else:
                        self.putSubItem("first", key)
                        self.putSubItem("second", value)
555

556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
    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)

hjk's avatar
hjk committed
578
579
580
581
582
583
584
585
586
587
    def findFirstZero(self, p, maximum):
        for i in xrange(maximum):
            if int(p.dereference()) == 0:
                return i
            p = p + 1
        return maximum + 1

    def encodeCArray(self, p, innerType, suffix):
        t = self.lookupType(innerType)
        p = p.cast(t.pointer())
588
        limit = self.findFirstZero(p, self.stringCutOff)
hjk's avatar
hjk committed
589
        s = self.readMemory(p, limit * t.sizeof)
590
        if limit > self.stringCutOff:
hjk's avatar
hjk committed
591
592
593
594
595
596
597
598
599
600
601
602
            s += suffix
        return s

    def encodeCharArray(self, p):
        return self.encodeCArray(p, "unsigned char", "2e2e2e")

    def encodeChar2Array(self, p):
        return self.encodeCArray(p, "unsigned short", "2e002e002e00")

    def encodeChar4Array(self, p):
        return self.encodeCArray(p, "unsigned int", "2e0000002e0000002e000000")

603
604
605
606
607
608
609
    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)

610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
    def putField(self, name, value):
        self.put('%s="%s",' % (name, value))

    def putType(self, type, priority = 0):
        # Higher priority values override lower ones.
        if priority >= self.currentTypePriority:
            self.currentType = str(type)
            self.currentTypePriority = priority

    def putValue(self, value, encoding = None, priority = 0):
        # Higher priority values override lower ones.
        if priority >= self.currentValuePriority:
            self.currentValue = value
            self.currentValuePriority = priority
            self.currentValueEncoding = encoding

    def putEmptyValue(self, priority = -10):
        if priority >= self.currentValuePriority:
            self.currentValue = ""
            self.currentValuePriority = priority
            self.currentValueEncoding = None

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

635
636
637
638
639
640
641
642
643
644
    def putNoType(self):
        # FIXME: replace with something that does not need special handling
        # in SubItem.__exit__().
        self.putBetterType(" ")

    def putInaccessible(self):
        #self.putBetterType(" ")
        self.putNumChild(0)
        self.currentValue = None

645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
    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)

662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
    def putCStyleArray(self, value):
        type = value.type.unqualified()
        targetType = value[0].type
        #self.putAddress(value.address)
        self.putType(type)
        self.putNumChild(1)
        format = self.currentItemFormat()
        isDefault = format == None and str(targetType.unqualified()) == "char"
        if isDefault or format == 0 or format == 1 or format == 2:
            blob = self.readMemory(self.addressOf(value), type.sizeof)

        if isDefault:
            # Use Latin1 as default for char [].
            self.putValue(blob, Hex2EncodedLatin1)
        elif format == 0:
            # Explicitly requested Latin1 formatting.
            self.putValue(blob, Hex2EncodedLatin1)
        elif format == 1:
            # Explicitly requested UTF-8 formatting.
            self.putValue(blob, Hex2EncodedUtf8)
        elif format == 2:
            # Explicitly requested Local 8-bit formatting.
            self.putValue(blob, Hex2EncodedLocal8Bit)
        else:
hjk's avatar
hjk committed
686
687
688
689
            try:
                self.putValue("@0x%x" % self.pointerValue(value.cast(targetType.pointer())))
            except:
                self.putEmptyValue()
690
691

        if self.currentIName in self.expandedINames:
hjk's avatar
hjk committed
692
693
694
695
696
697
698
699
700
701
            try:
                # May fail on artificial items like xmm register data.
                p = self.addressOf(value)
                ts = targetType.sizeof
                if not self.tryPutArrayContents(targetType, p, int(type.sizeof / ts)):
                    with Children(self, childType=targetType,
                            addrBase=p, addrStep=ts):
                        self.putFields(value)
            except:
                with Children(self, childType=targetType):
702
703
                    self.putFields(value)

704
705
706
707
708
709
710
711
712
713
714
715
716
    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)

717
718
719
720
721
722
723
724
725
726
727
728
729
    def tryPutArrayContents(self, typeobj, base, n):
        enc = self.simpleEncoding(typeobj)
        if not enc:
            return False
        size = n * typeobj.sizeof;
        self.put('childtype="%s",' % typeobj)
        self.put('addrbase="0x%x",' % toInteger(base))
        self.put('addrstep="0x%x",' % toInteger(typeobj.sizeof))
        self.put('arrayencoding="%s",' % enc)
        self.put('arraydata="')
        self.put(self.readMemory(base, size))
        self.put('",')
        return True
730

hjk's avatar
hjk committed
731
732
733
734
735
736
737
    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
738
            return
hjk's avatar
hjk committed
739
740
741
742
743
744
745
746
747
748

        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.
749
            self.putValue(self.cleanAddress(value))
hjk's avatar
hjk committed
750
751
            self.putType(typeName)
            self.putNumChild(0)
hjk's avatar
hjk committed
752
            return
hjk's avatar
hjk committed
753
754
755
756
757
758
759
760

        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
761
            return
hjk's avatar
hjk committed
762
763
764
765
766
767

        if format == None and innerTypeName == "char":
            # Use Latin1 as default for char *.
            self.putType(typeName)
            self.putValue(self.encodeCharArray(value), Hex2EncodedLatin1)
            self.putNumChild(0)
hjk's avatar
hjk committed
768
            return
hjk's avatar
hjk committed
769
770
771
772

        if format == 0:
            # Explicitly requested bald pointer.
            self.putType(typeName)
hjk's avatar
hjk committed
773
            self.putValue(self.hexencode(str(value)), Hex2EncodedUtf8WithoutQuotes)
hjk's avatar
hjk committed
774
775
776
777
778
            self.putNumChild(1)
            if self.currentIName in self.expandedINames:
                with Children(self):
                    with SubItem(self, '*'):
                        self.putItem(value.dereference())
hjk's avatar
hjk committed
779
            return
hjk's avatar
hjk committed
780
781
782
783
784
785

        if format == 1:
            # Explicitly requested Latin1 formatting.
            self.putType(typeName)
            self.putValue(self.encodeCharArray(value), Hex2EncodedLatin1)
            self.putNumChild(0)
hjk's avatar
hjk committed
786
            return
hjk's avatar
hjk committed
787
788
789
790
791
792

        if format == 2:
            # Explicitly requested UTF-8 formatting.
            self.putType(typeName)
            self.putValue(self.encodeCharArray(value), Hex2EncodedUtf8)
            self.putNumChild(0)
hjk's avatar
hjk committed
793
            return
hjk's avatar
hjk committed
794
795
796
797
798
799

        if format == 3:
            # Explicitly requested local 8 bit formatting.
            self.putType(typeName)
            self.putValue(self.encodeCharArray(value), Hex2EncodedLocal8Bit)
            self.putNumChild(0)
hjk's avatar
hjk committed
800
            return
hjk's avatar
hjk committed
801
802
803
804
805
806

        if format == 4:
            # Explicitly requested UTF-16 formatting.
            self.putType(typeName)
            self.putValue(self.encodeChar2Array(value), Hex4EncodedLittleEndian)
            self.putNumChild(0)
hjk's avatar
hjk committed
807
            return
hjk's avatar
hjk committed
808
809
810
811
812
813

        if format == 5:
            # Explicitly requested UCS-4 formatting.
            self.putType(typeName)
            self.putValue(self.encodeChar4Array(value), Hex8EncodedLittleEndian)
            self.putNumChild(0)
hjk's avatar
hjk committed
814
            return
hjk's avatar
hjk committed
815

816
        if not format is None and format >= 6 and format <= 9:
817
818
            # Explicitly requested formatting as array of n items.
            n = (10, 100, 1000, 10000)[format - 6]
hjk's avatar
hjk committed
819
            self.putType(typeName)
820
821
822
            self.putItemCount(n)
            self.putNumChild(n)
            self.putArrayData(innerType, value, n)
hjk's avatar
hjk committed
823
            return
hjk's avatar
hjk committed
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852

        if self.isFunctionType(innerType):
            # A function pointer.
            val = str(value)
            pos = val.find(" = ") # LLDB only, but...
            if pos > 0:
                val = val[pos + 3:]
            self.putValue(val)
            self.putType(innerTypeName)
            self.putNumChild(0)
            return

        #warn("AUTODEREF: %s" % self.autoDerefPointers)
        #warn("INAME: %s" % self.currentIName)
        if self.autoDerefPointers or self.currentIName.endswith('.this'):
            ## Generic pointer type with format None
            #warn("GENERIC AUTODEREF POINTER: %s AT %s TO %s"
            #    % (type, value.address, innerTypeName))
            # Never dereference char types.
            if innerTypeName != "char" \
                    and innerTypeName != "signed char" \
                    and innerTypeName != "unsigned char"  \
                    and innerTypeName != "wchar_t":
                self.putType(innerTypeName)
                savedCurrentChildType = self.currentChildType
                self.currentChildType = stripClassTag(innerTypeName)
                self.putItem(value.dereference())
                self.currentChildType = savedCurrentChildType
                #self.putPointerValue(value)
hjk's avatar
hjk committed
853
854
855
                if not value.address is None:
                    self.put('origaddr="0x%x",' % toInteger(value.address))
                return
hjk's avatar
hjk committed
856
857

        #warn("GENERIC PLAIN POINTER: %s" % value.type)
858
        #warn("ADDR PLAIN POINTER: 0x%x" % value.address)
hjk's avatar
hjk committed
859
860
861
862
863
864
865
866
        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())

867
868
869
870
871
    def putQObjectNameValue(self, value):
        try:
            intSize = self.intSize()
            ptrSize = self.ptrSize()
            # dd = value["d_ptr"]["d"] is just behind the vtable.
872
            dd = self.extractPointer(value, offset=ptrSize)
873
874
875
876
877
878
879
880
881
882
883
884
885
886

            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
887
                objectName = self.extractPointer(dd + 5 * ptrSize + 2 * intSize)
888
889
890
891
892
893
894
895
896
897

            else:
                # Size of QObjectData: 5 pointer + 2 int
                #   - vtable
                #   - QObject *q_ptr;
                #   - QObject *parent;
                #   - QObjectList children;
                #   - uint isWidget : 1; etc...
                #   - int postedEvents;
                #   - QDynamicMetaObjectData *metaObject;
898
                extra = self.extractPointer(dd + 5 * ptrSize + 2 * intSize)
899
900
901
902
903
904
905
906
907
908
                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
909
                objectName = self.extractPointer(extra + 5 * ptrSize)
910
911
912
913
914
915

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

            if size == 0:
                return False

hjk's avatar
hjk committed
916
917
            raw = self.readMemory(data, 2 * size)
            self.putValue(raw, Hex4EncodedLittleEndian, 1)
918
919
920
            return True

        except:
921
            #warn("NO QOBJECT: %s" % value.type)
922
923
            pass

924

hjk's avatar
hjk committed
925
926
927
    def staticQObjectPropertyNames(self, metaobject):
        properties = []
        dd = metaobject["d"]
928
929
        data = self.extractPointer(dd["data"])
        sd = self.extractPointer(dd["stringdata"])
hjk's avatar
hjk committed
930

931
        metaObjectVersion = self.extractInt(data)
hjk's avatar
hjk committed
932
933
934
        propertyCount = self.extractInt(data + 24)
        propertyData = self.extractInt(data + 28)

935
936
937
938
939
940
941
942
943
944
945
946
947
948
        if metaObjectVersion >= 7: # Qt 5.
            byteArrayDataType = self.lookupType(self.qtNamespace() + "QByteArrayData")
            byteArrayDataSize = byteArrayDataType.sizeof
            for i in range(propertyCount):
                x = data + (propertyData + 3 * i) * 4
                literal = sd + self.extractInt(x) * byteArrayDataSize
                ldata, lsize, lalloc = self.byteArrayDataHelper(literal)
                properties.append(self.extractBlob(ldata, lsize).toString())
        else: # Qt 4.
            for i in range(propertyCount):
                x = data + (propertyData + 3 * i) * 4
                ldata = sd + self.extractInt(x)
                properties.append(self.extractCString(ldata).decode("utf8"))

hjk's avatar
hjk committed
949
950
        return properties

951
952
953
954
955
956
957
958
959
960
    def extractCString(self, addr):
        result = bytearray()
        while True:
            d = self.extractByte(addr)
            if d == 0:
                break
            result.append(d)
            addr += 1
        return result

961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
    def generateQListChildren(self, value):
        dptr = self.childAt(value, 0)["d"]
        private = dptr.dereference()
        begin = int(private["begin"])
        end = int(private["end"])
        array = private["array"]
        size = end - begin
        innerType = self.templateArgument(value.type, 0)
        innerSize = innerType.sizeof
        stepSize = dptr.type.sizeof
        addr = self.addressOf(array) + begin * stepSize
        isInternal = innerSize <= stepSize and self.isMovableType(innerType)
        if isInternal:
            for i in range(size):
                yield self.createValue(addr + i * stepSize, innerType)
        else:
            p = self.createPointerValue(addr, innerType.pointer())
            for i in range(size):
                yield p.dereference().dereference()
                p += 1

hjk's avatar
hjk committed
982
983

    # This is called is when a QObject derived class is expanded
984
    def putQObjectGuts(self, qobject, smo):
985
986
987
988
989
990
991
992
993
        intSize = self.intSize()
        ptrSize = self.ptrSize()
        # dd = value["d_ptr"]["d"] is just behind the vtable.
        dd = self.extractPointer(qobject, offset=ptrSize)

        extraData = self.extractPointer(dd + 5 * ptrSize + 2 * intSize)
        #with SubItem(self, "[extradata]"):
        #    self.putValue("0x%x" % toInteger(extraData))

hjk's avatar
hjk committed
994
        with SubItem(self, "[properties]"):
995
            propertyCount = 0
hjk's avatar
hjk committed
996
            if self.isExpanded():
997
998
                propertyNames = self.staticQObjectPropertyNames(smo)
                propertyCount = len(propertyNames) # Doesn't include dynamic properties.
hjk's avatar
hjk committed
999
                with Children(self):
1000
                    # Static properties.
hjk's avatar
hjk committed
1001
1002
1003
                    for i in range(propertyCount):
                        name = propertyNames[i]
                        self.putCallItem(name, qobject, "property", '"' + name + '"')
hjk's avatar
hjk committed
1004

1005
                    # Dynamic properties.
1006
                    if extraData:
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
                        propertyNames = extraData + ptrSize
                        propertyValues = extraData + 2 * ptrSize

                        ns = self.qtNamespace()

                        typ = self.lookupType(ns + "QList<" + ns + "QByteArray>")
                        names = self.createValue(propertyNames, typ)

                        typ = self.lookupType(ns + "QList<" + ns + "QVariant>")
                        values = self.createValue(propertyValues, typ)

                        for (k, v) in zip(self.generateQListChildren(names),
                                self.generateQListChildren(values)) :
                            with SubItem(self, propertyCount):
                                self.put('key="%s",' % self.encodeByteArray(k))
                                self.put('keyencoded="%s",' % Hex2EncodedLatin1)
                                self.putItem(v)
                                propertyCount += 1

            self.putValue('<%s items>' % propertyCount if propertyCount else '<>0 items>')
            self.putNumChild(1)

hjk's avatar
hjk committed
1029

1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
    def isKnownMovableType(self, type):
        if type in (
                "QBrush", "QBitArray", "QByteArray", "QCustomTypeInfo", "QChar", "QDate",
                "QDateTime", "QFileInfo", "QFixed", "QFixedPoint", "QFixedSize",
                "QHashDummyValue", "QIcon", "QImage", "QLine", "QLineF", "QLatin1Char",
                "QLocale", "QMatrix", "QModelIndex", "QPoint", "QPointF", "QPen",
                "QPersistentModelIndex", "QResourceRoot", "QRect", "QRectF", "QRegExp",
                "QSize", "QSizeF", "QString", "QTime", "QTextBlock", "QUrl", "QVariant",
                "QXmlStreamAttribute", "QXmlStreamNamespaceDeclaration",
                "QXmlStreamNotationDeclaration", "QXmlStreamEntityDeclaration"
                ):
            return True

        return type == "QStringList" and self.qtVersion() >= 0x050000

1045
1046
1047
1048
1049
1050
1051
1052
    def currentItemFormat(self, type = None):
        format = self.formats.get(self.currentIName)
        if format is None:
            if type is None:
                type = self.currentType
            needle = self.stripForFormat(str(type))
            format = self.typeformats.get(needle)
        return format
1053

1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
    def putPlotData(self, type, base, n, plotFormat = 2):
        if self.isExpanded():
            self.putArrayData(type, base, n)
        if not hasPlot():
            return
        if not self.isSimpleType(type):
            #self.putValue(self.currentValue + " (not plottable)")
            self.putValue(self.currentValue)
            self.putField("plottable", "0")
            return
        global gnuplotPipe
        global gnuplotPid
        format = self.currentItemFormat()
        iname = self.currentIName
        if format != plotFormat:
            if iname in gnuplotPipe:
                os.kill(gnuplotPid[iname], 9)
                del gnuplotPid[iname]
                gnuplotPipe[iname].terminate()
                del gnuplotPipe[iname]
            return
        base = self.createPointerValue(base, type)
        if not iname in gnuplotPipe:
            gnuplotPipe[iname] = subprocess.Popen(["gnuplot"],
                    stdin=subprocess.PIPE)
            gnuplotPid[iname] = gnuplotPipe[iname].pid
        f = gnuplotPipe[iname].stdin;
        # On Ubuntu install gnuplot-x11
        f.write("set term wxt noraise\n")
        f.write("set title 'Data fields'\n")
        f.write("set xlabel 'Index'\n")
        f.write("set ylabel 'Value'\n")
        f.write("set grid\n")
        f.write("set style data lines;\n")
        f.write("plot  '-' title '%s'\n" % iname)
        for i in range(0, n):
            f.write(" %s\n" % base.dereference())
            base += 1
        f.write("e\n")

1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
    def putSpecialArgv(self, value):
        """
        Special handling for char** argv.
        """
        n = 0
        p = value
        # p is 0 for "optimized out" cases. Or contains rubbish.
        try:
            if not self.isNull(p):
                while not self.isNull(p.dereference()) and n <= 100:
                    p += 1
                    n += 1
        except:
            pass

        with TopLevelItem(self, 'local.argv'):
            self.put('iname="local.argv",name="argv",')
            self.putItemCount(n, 100)
            self.putType('char **')
            self.putNumChild(n)
            if self.currentIName in self.expandedINames:
                p = value
                with Children(self, n):
                    for i in xrange(n):
                        self.putSubItem(i, p.dereference())
                        p += 1

1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
    def extractPointer(self, thing, offset = 0):
        if isinstance(thing, int):
            bytes = self.extractBlob(thing, self.ptrSize()).toBytes()
        elif sys.version_info[0] == 2 and isinstance(thing, long):
            bytes = self.extractBlob(thing, self.ptrSize()).toBytes()
        elif isinstance(thing, Blob):
            bytes = blob.toBytes()
        else:
            # Assume it's a (backend specific) Value.
            bytes = self.toBlob(thing).toBytes()
        code = "I" if self.ptrSize() == 4 else "Q"
        return struct.unpack_from(code, bytes, offset)[0]
1133
1134
1135

# Some "Enums"

1136
# Encodings. Keep that synchronized with DebuggerEncoding in debuggerprotocol.h
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
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, \
1164
IPv6AddressAndHexScopeId, \
1165
Hex2EncodedUtf8WithoutQuotes, \
1166
DateTimeInternal \
1167
    = range(30)
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187

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


def mapForms():
    return "Normal,Compact"

def arrayForms():
    if hasPlot():
        return "Normal,Plot"
    return "Normal"