dumper.py 42.8 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

hjk's avatar
hjk committed
363
364
365
    def is64bit(self):
        return self.ptrSize() == 8

366
367
368
369
    def isQt3Support(self):
        # assume no Qt 3 support by default
        return False

370
371
372
373
    def computeLimit(self, size, limit):
        if limit is None:
            return size
        if limit == 0:
374
            return min(size, self.stringCutOff)
375
376
377
378
379
380
381
382
383
384
385
        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
386
            data = addr + self.extractPointer(addr + 8 + self.ptrSize())
387
388
389
390
            if self.ptrSize() == 4:
                data = data & 0xffffffff
            else:
                data = data & 0xffffffffffffffff
391
392
393
394
395
396
397
398
        else:
            # Data:
            # - QBasicAtomicInt ref;
            # - int alloc, size;
            # - [padding]
            # - char *data;
            alloc = self.extractInt(addr + 4)
            size = self.extractInt(addr + 8)
399
            data = self.extractPointer(addr + 8 + self.ptrSize())
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
        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
427
    def readMemory(self, addr, size):
428
429
        data = self.extractBlob(addr, size).toBytes()
        return self.hexencode(data)
hjk's avatar
hjk committed
430

431
    def encodeByteArray(self, value):
432
        return self.encodeByteArrayHelper(self.extractPointer(value))
433
434

    def byteArrayData(self, value):
435
        return self.byteArrayDataHelper(self.extractPointer(value))
436
437
438
439

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

hjk's avatar
hjk committed
440
    def putByteArrayValueByAddress(self, addr):
441
        self.putValue(self.encodeByteArrayHelper(self.extractPointer(addr)),
hjk's avatar
hjk committed
442
443
            Hex2EncodedLatin1)

444
    def putStringValueByAddress(self, addr):
445
        self.putValue(self.encodeStringHelper(self.extractPointer(addr)),
446
447
448
            Hex4EncodedLittleEndian)

    def encodeString(self, value):
449
        return self.encodeStringHelper(self.extractPointer(value))
450
451

    def stringData(self, value):
452
        return self.byteArrayDataHelper(self.extractPointer(value))
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
479
480
481
    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()

482
483
484
    def putStringValue(self, value):
        return self.putValue(self.encodeString(value), Hex4EncodedLittleEndian)

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


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

525
    def putPair(self, pair, index = -1):
526
        if self.pairData.useKeyAndValue:
527
528
            key = pair["key"]
            value = pair["value"]
529
530
531
        else:
            key = pair["first"]
            value = pair["second"]
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
        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()
548
549
            self.putNumChild(2)
            self.putField("iname", self.currentIName)
550
            if self.isExpanded():
551
                with Children(self):
552
553
554
555
556
557
                    if self.pairData.useKeyAndValue:
                        self.putSubItem("key", key)
                        self.putSubItem("value", value)
                    else:
                        self.putSubItem("first", key)
                        self.putSubItem("second", value)
558

559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
    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
581
582
583
584
585
586
587
588
589
590
    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())
591
        limit = self.findFirstZero(p, self.stringCutOff)
hjk's avatar
hjk committed
592
        s = self.readMemory(p, limit * t.sizeof)
593
        if limit > self.stringCutOff:
hjk's avatar
hjk committed
594
595
596
597
598
599
600
601
602
603
604
605
            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")

606
607
608
609
610
611
612
    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)

613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
    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)

638
639
640
641
642
643
644
645
646
647
    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

648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
    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)

665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
    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
689
690
691
692
            try:
                self.putValue("@0x%x" % self.pointerValue(value.cast(targetType.pointer())))
            except:
                self.putEmptyValue()
693
694

        if self.currentIName in self.expandedINames:
hjk's avatar
hjk committed
695
696
697
698
699
700
701
702
703
704
            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):
705
706
                    self.putFields(value)

707
708
709
710
711
712
713
714
715
716
717
718
719
    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)

720
721
722
723
724
725
726
727
728
729
730
731
732
    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
733

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

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

        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
764
            return
hjk's avatar
hjk committed
765
766
767
768
769
770

        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
771
            return
hjk's avatar
hjk committed
772
773
774
775

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

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

        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
796
            return
hjk's avatar
hjk committed
797
798
799
800
801
802

        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
803
            return
hjk's avatar
hjk committed
804
805
806
807
808
809

        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
810
            return
hjk's avatar
hjk committed
811
812
813
814
815
816

        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
817
            return
hjk's avatar
hjk committed
818

819
        if not format is None and format >= 6 and format <= 9:
820
821
            # Explicitly requested formatting as array of n items.
            n = (10, 100, 1000, 10000)[format - 6]
hjk's avatar
hjk committed
822
            self.putType(typeName)
823
824
825
            self.putItemCount(n)
            self.putNumChild(n)
            self.putArrayData(innerType, value, n)
hjk's avatar
hjk committed
826
            return
hjk's avatar
hjk committed
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
853
854
855

        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
856
857
858
                if not value.address is None:
                    self.put('origaddr="0x%x",' % toInteger(value.address))
                return
hjk's avatar
hjk committed
859
860

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

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

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

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

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

            if size == 0:
                return False

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

        except:
924
            #warn("NO QOBJECT: %s" % value.type)
925
926
            pass

927

928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
    def extractStaticMetaObjectHelper(self, typeName):
        """
        Checks whether type has a Q_OBJECT macro.
        Returns the staticMetaObject, or 0.
        """
        # No templates for now.
        if typeName.find('<') >= 0:
            return 0

        staticMetaObjectName = typeName + "::staticMetaObject"
        result = self.findSymbol(staticMetaObjectName)

        # We need to distinguish Q_OBJECT from Q_GADGET:
        # a Q_OBJECT SMO has a non-null superdata (unless it's QObject itself),
        # a Q_GADGET SMO has a null superdata (hopefully)
        if result and typeName != self.qtNamespace() + "QObject":
            if not self.extractPointer(result):
                # This looks like a Q_GADGET
                result = 0

        return result

    def extractStaticMetaObject(self, typeobj):
        """
        Checks recursively whether a type derives from QObject.
        """
        typeName = str(typeobj)
        result = self.knownStaticMetaObjects.get(typeName, None)
        if result is not None: # Is 0 or the static metaobject.
            return result

        result = self.extractStaticMetaObjectHelper(typeName)
        if not result:
            base = self.directBaseClass(typeobj, 0)
            if base:
                result = self.extractStaticMetaObject(base)

        self.knownStaticMetaObjects[typeName] = result
        return result

hjk's avatar
hjk committed
968
969
970
    def staticQObjectPropertyNames(self, metaobject):
        properties = []
        dd = metaobject["d"]
971
972
        data = self.extractPointer(dd["data"])
        sd = self.extractPointer(dd["stringdata"])
hjk's avatar
hjk committed
973

974
        metaObjectVersion = self.extractInt(data)
hjk's avatar
hjk committed
975
976
977
        propertyCount = self.extractInt(data + 24)
        propertyData = self.extractInt(data + 28)

978
979
980
981
982
983
984
985
986
987
988
989
990
991
        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
992
993
        return properties

994
995
996
997
998
999
1000
1001
1002
1003
    def extractCString(self, addr):
        result = bytearray()
        while True:
            d = self.extractByte(addr)
            if d == 0:
                break
            result.append(d)
            addr += 1
        return result

1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
    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
1025
1026

    # This is called is when a QObject derived class is expanded
1027
    def putQObjectGuts(self, qobject, smo):
1028
1029
1030
1031
1032
1033
1034
1035
1036
        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
1037
        with SubItem(self, "[properties]"):
1038
            propertyCount = 0
hjk's avatar
hjk committed
1039
            if self.isExpanded():
1040
1041
                propertyNames = self.staticQObjectPropertyNames(smo)
                propertyCount = len(propertyNames) # Doesn't include dynamic properties.
hjk's avatar
hjk committed
1042
                with Children(self):
1043
                    # Static properties.
hjk's avatar
hjk committed
1044
1045
1046
                    for i in range(propertyCount):
                        name = propertyNames[i]
                        self.putCallItem(name, qobject, "property", '"' + name + '"')
hjk's avatar
hjk committed
1047

1048
                    # Dynamic properties.
1049
                    if extraData:
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
                        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
1072

1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
    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

1088
1089
1090
1091
1092
1093
1094
1095
    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
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
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
    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")

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

1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
    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]
1176
1177
1178

# Some "Enums"

1179
# Encodings. Keep that synchronized with DebuggerEncoding in debuggerprotocol.h
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
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, \
1207
IPv6AddressAndHexScopeId, \
1208
Hex2EncodedUtf8WithoutQuotes, \
1209
DateTimeInternal \
1210
    = range(30)
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230

# 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"