dumper.py 63.7 KB
Newer Older
hjk's avatar
hjk committed
1
import sys
2
import base64
3
import __builtin__
4
import os
hjk's avatar
hjk committed
5
import tempfile
6

7
8
9
10
# Fails on Windows.
try:
    import curses.ascii
    def printableChar(ucs):
11
12
13
        if curses.ascii.isprint(ucs):
            return ucs
        return '?'
14
except:
15
16
17
18
    def printableChar(ucs):
        if ucs >= 32 and ucs <= 126:
            return ucs
        return '?'
hjk's avatar
hjk committed
19

20
# Fails on SimulatorQt.
21
tempFileCounter = 0
22
try:
23
24
25
    # Test if 2.6 is used (Windows), trigger exception and default
    # to 2nd version.
    tempfile.NamedTemporaryFile(prefix="py_",delete=True)
26
    def createTempFile():
27
        file = tempfile.NamedTemporaryFile(prefix="py_",delete=False)
28
        file.close()
29
        return file.name, file
30

31
32
except:
    def createTempFile():
33
34
        global tempFileCounter
        tempFileCounter += 1
35
        fileName = "%s/py_tmp_%d_%d" \
hjk's avatar
hjk committed
36
            % (tempfile.gettempdir(), os.getpid(), tempFileCounter)
37
        return fileName, None
38

39
40
41
42
def removeTempFile(name, file):
    try:
        os.remove(name)
    except:
43
        pass
44

hjk's avatar
hjk committed
45
46
47
verbosity = 0
verbosity = 1

48
49
50
51
# Some "Enums"

# Encodings
Unencoded8Bit, \
52
53
54
55
56
57
58
59
60
61
Base64Encoded8BitWithQuotes, \
Base64Encoded16BitWithQuotes, \
Base64Encoded32BitWithQuotes, \
Base64Encoded16Bit, \
Base64Encoded8Bit, \
Hex2EncodedLatin1, \
Hex4EncodedLittleEndian, \
Hex8EncodedLittleEndian, \
Hex2EncodedUtf8, \
Hex8EncodedBigEndian, \
Arvid Ephraim Picciani's avatar
Arvid Ephraim Picciani committed
62
Hex4EncodedBigEndian, \
63
Hex4EncodedLittleEndianWithoutQuotes, \
64
65
66
Hex2EncodedLocal8Bit, \
JulianDate, \
MillisecondsSinceMidnight, \
67
68
69
70
71
72
73
74
JulianDateAndMillisecondsSinceMidnight, \
Hex2EncodedInt1, \
Hex2EncodedInt2, \
Hex2EncodedInt4, \
Hex2EncodedInt8, \
Hex2EncodedFloat4, \
Hex2EncodedFloat8 \
    = range(23)
75
76

# Display modes
77
78
79
80
81
82
StopDisplay, \
DisplayImage1, \
DisplayString, \
DisplayImage, \
DisplayProcess \
    = range(5)
83
84


85
86
qqStringCutOff = 1000

87
def hasInferiorThreadList():
88
    #return False
89
    try:
90
        a = gdb.inferiors()[0].threads()
91
92
93
94
        return True
    except:
        return False

95
96
97
98
99
100
101
102
def hasVTable(type):
    fields = type.fields()
    if len(fields) == 0:
        return False
    if fields[0].is_base_class:
        return hasVTable(fields[0].type)
    return str(fields[0].type) ==  "int (**)(void)"

103
def dynamicTypeName(value):
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    if hasVTable(value.type):
        #vtbl = str(parseAndEvaluate("{int(*)(int)}%s" % long(value.address)))
        try:
            # Fails on 7.1 due to the missing to_string.
            vtbl = gdb.execute("info symbol {int*}%s" % long(value.address),
                to_string = True)
            pos1 = vtbl.find("vtable ")
            if pos1 != -1:
                pos1 += 11
                pos2 = vtbl.find(" +", pos1)
                if pos2 != -1:
                    return vtbl[pos1 : pos2]
        except:
            pass
118
119
    return str(value.type)

120
def downcast(value):
121
    try:
122
123
124
125
126
127
128
129
130
        return value.cast(value.dynamic_type)
    except:
        pass
    #try:
    #    return value.cast(lookupType(dynamicTypeName(value)))
    #except:
    #    pass
    return value

131
def expensiveDowncast(value):
132
133
    try:
        return value.cast(value.dynamic_type)
134
    except:
135
136
137
138
139
140
        pass
    try:
        return value.cast(lookupType(dynamicTypeName(value)))
    except:
        pass
    return value
141

142
143
144
typeCache = {}

def lookupType(typestring):
145
146
147
    global typeCache
    global typesToReport
    type = typeCache.get(typestring)
hjk's avatar
hjk committed
148
    #warn("LOOKUP 1: %s -> %s" % (typestring, type))
149
150
    if not type is None:
        return type
151

152
153
    if typestring == "void":
        type = gdb.lookup_type(typestring)
154
155
        typeCache[typestring] = type
        typesToReport[typestring] = type
156
157
158
159
        return type

    try:
        type = gdb.parse_and_eval("{%s}&main" % typestring).type
160
161
162
163
        if not type is None:
            typeCache[typestring] = type
            typesToReport[typestring] = type
            return type
164
165
166
    except:
        pass

167
168
169
170
171
172
173
174
175
176
177
178
179
    # See http://sourceware.org/bugzilla/show_bug.cgi?id=13269
    # gcc produces "{anonymous}", gdb "(anonymous namespace)"
    # "<unnamed>" has been seen too. The only thing gdb
    # understands when reading things back is "(anonymous namespace)"
    if typestring.find("{anonymous}") != -1:
        ts = typestring
        ts = ts.replace("{anonymous}", "(anonymous namespace)")
        type = lookupType(ts)
        if not type is None:
            typeCache[typestring] = type
            typesToReport[typestring] = type
            return type

hjk's avatar
hjk committed
180
    #warn(" RESULT FOR 7.2: '%s': %s" % (typestring, type))
181

hjk's avatar
hjk committed
182
183
    # This part should only trigger for
    # gdb 7.1 for types with namespace separators.
184
    # And anonymous namespaces.
185

186
187
    ts = typestring
    while True:
hjk's avatar
hjk committed
188
        #warn("TS: '%s'" % ts)
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
        if ts.startswith("class "):
            ts = ts[6:]
        elif ts.startswith("struct "):
            ts = ts[7:]
        elif ts.startswith("const "):
            ts = ts[6:]
        elif ts.startswith("volatile "):
            ts = ts[9:]
        elif ts.startswith("enum "):
            ts = ts[5:]
        elif ts.endswith(" const"):
            ts = ts[:-6]
        elif ts.endswith(" volatile"):
            ts = ts[:-9]
        elif ts.endswith("*const"):
            ts = ts[:-5]
        elif ts.endswith("*volatile"):
            ts = ts[:-8]
        else:
            break

    if ts.endswith('*'):
        type = lookupType(ts[0:-1])
hjk's avatar
hjk committed
212
213
        if not type is None:
            type = type.pointer()
214
215
            typeCache[typestring] = type
            typesToReport[typestring] = type
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
            return type

    try:
        #warn("LOOKING UP '%s'" % ts)
        type = gdb.lookup_type(ts)
    except RuntimeError, error:
        #warn("LOOKING UP '%s': %s" % (ts, error))
        # See http://sourceware.org/bugzilla/show_bug.cgi?id=11912
        exp = "(class '%s'*)0" % ts
        try:
            type = parseAndEvaluate(exp).type.target()
        except:
            # Can throw "RuntimeError: No type named class Foo."
            pass
    except:
        #warn("LOOKING UP '%s' FAILED" % ts)
232
        pass
233

234
235
236
237
238
    if not type is None:
        typeCache[typestring] = type
        typesToReport[typestring] = type
        return type

239
240
    # This could still be None as gdb.lookup_type("char[3]") generates
    # "RuntimeError: No type named char[3]"
241
242
    typeCache[typestring] = type
    typesToReport[typestring] = type
243
244
    return type

245
def cleanAddress(addr):
246
247
    if addr is None:
        return "<no address>"
248
249
    # We cannot use str(addr) as it yields rubbish for char pointers
    # that might trigger Unicode encoding errors.
250
    #return addr.cast(lookupType("void").pointer())
251
252
    # We do not use "hex(...)" as it (sometimes?) adds a "L" suffix.
    return "0x%x" % long(addr)
253

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
def extractTemplateArgument(type, position):
    level = 0
    skipSpace = False
    inner = ""
    type = str(type)
    for c in type[type.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
                position -= 1
                inner = ""
            else:
                inner += c
                skipSpace = True
        else:
            if skipSpace and c == ' ':
                pass
            else:
                inner += c
                skipSpace = False
    return inner

def templateArgument(type, position):
284
285
286
287
288
289
    try:
        # This fails on stock 7.2 with
        # "RuntimeError: No type named myns::QObject.\n"
        return type.template_argument(position)
    except:
        # That's something like "myns::QList<...>"
290
        return lookupType(extractTemplateArgument(type.strip_typedefs(), position))
291

292

hjk's avatar
hjk committed
293
294
295
296
# Workaround for gdb < 7.1
def numericTemplateArgument(type, position):
    try:
        return int(type.template_argument(position))
297
    except RuntimeError, error:
hjk's avatar
hjk committed
298
299
        # ": No type named 30."
        msg = str(error)
300
301
302
303
304
305
        msg = msg[14:-1]
        # gdb at least until 7.4 produces for std::array<int, 4u>
        # for template_argument(1): RuntimeError: No type named 4u.
        if msg[-1] == 'u':
           msg = msg[0:-1]
        return int(msg)
hjk's avatar
hjk committed
306

307

308
309
310
311
312
313
314
315
316
317
318
def showException(msg, exType, exValue, exTraceback):
    warn("**** CAUGHT EXCEPTION: %s ****" % msg)
    try:
        import traceback
        for line in traceback.format_exception(exType, exValue, exTraceback):
            warn("%s" % line)
    except:
        pass


class OutputSafer:
hjk's avatar
hjk committed
319
    def __init__(self, d):
320
321
322
323
        self.d = d

    def __enter__(self):
        self.savedOutput = self.d.output
hjk's avatar
hjk committed
324
        self.d.output = []
325
326
327
328
329
330

    def __exit__(self, exType, exValue, exTraceBack):
        if self.d.passExceptions and not exType is None:
            showException("OUTPUTSAFER", exType, exValue, exTraceBack)
            self.d.output = self.savedOutput
        else:
hjk's avatar
hjk committed
331
332
            self.savedOutput.extend(self.d.output)
            self.d.output = self.savedOutput
333
334
335
        return False


336
337
338
339
340
class NoAddress:
    def __init__(self, d):
        self.d = d

    def __enter__(self):
hjk's avatar
hjk committed
341
342
        self.savedPrintsAddress = self.d.currentPrintsAddress
        self.d.currentPrintsAddress = False
343
344

    def __exit__(self, exType, exValue, exTraceBack):
hjk's avatar
hjk committed
345
        self.d.currentPrintsAddress = self.savedPrintsAddress
346
347


348

349
class SubItem:
hjk's avatar
hjk committed
350
    def __init__(self, d, component):
351
        self.d = d
hjk's avatar
hjk committed
352
353
        self.iname = "%s.%s" % (d.currentIName, component)
        self.name = component
354
355
356

    def __enter__(self):
        self.d.put('{')
hjk's avatar
hjk committed
357
358
359
360
        #if not self.name is None:
        if isinstance(self.name, str):
            self.d.put('name="%s",' % self.name)
        self.savedIName = self.d.currentIName
361
362
363
364
365
        self.savedValue = self.d.currentValue
        self.savedValuePriority = self.d.currentValuePriority
        self.savedValueEncoding = self.d.currentValueEncoding
        self.savedType = self.d.currentType
        self.savedTypePriority = self.d.currentTypePriority
hjk's avatar
hjk committed
366
        self.d.currentIName = self.iname
367
        self.d.currentValuePriority = -100
368
369
        self.d.currentValueEncoding = None
        self.d.currentType = ""
370
        self.d.currentTypePriority = -100
371
372
373
374

    def __exit__(self, exType, exValue, exTraceBack):
        #warn(" CURRENT VALUE: %s %s %s" % (self.d.currentValue,
        #    self.d.currentValueEncoding, self.d.currentValuePriority))
375
376
377
378
379
        if not exType is None:
            if self.d.passExceptions:
                showException("SUBITEM", exType, exValue, exTraceBack)
            self.d.putNumChild(0)
            self.d.putValue("<not accessible>")
380
        try:
381
            #warn("TYPE VALUE: %s" % self.d.currentValue)
hjk's avatar
hjk committed
382
383
384
385
386
387
388
389
390
391
            typeName = stripClassTag(self.d.currentType)
            #warn("TYPE: '%s'  DEFAULT: '%s' % (typeName, self.d.currentChildType))

            if len(typeName) > 0 and typeName != self.d.currentChildType:
                self.d.put('type="%s",' % typeName) # str(type.unqualified()) ?
            if  self.d.currentValue is None:
                self.d.put('value="<not accessible>",numchild="0",')
            else:
                if not self.d.currentValueEncoding is None:
                    self.d.put('valueencoded="%d",' % self.d.currentValueEncoding)
392
                self.d.put('value="%s",' % self.d.currentValue)
393
394
        except:
            pass
395
        self.d.put('},')
hjk's avatar
hjk committed
396
        self.d.currentIName = self.savedIName
397
398
399
400
401
402
403
        self.d.currentValue = self.savedValue
        self.d.currentValuePriority = self.savedValuePriority
        self.d.currentValueEncoding = self.savedValueEncoding
        self.d.currentType = self.savedType
        self.d.currentTypePriority = self.savedTypePriority
        return True

hjk's avatar
hjk committed
404
405
406
407
408
409
410
411
412
413
414
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
415
416

class Children:
hjk's avatar
hjk committed
417
418
    def __init__(self, d, numChild = 1, childType = None, childNumChild = None,
            maxNumChild = None, addrBase = None, addrStep = None):
419
420
421
        self.d = d
        self.numChild = numChild
        self.childNumChild = childNumChild
hjk's avatar
hjk committed
422
423
424
425
        self.maxNumChild = maxNumChild
        self.addrBase = addrBase
        self.addrStep = addrStep
        self.printsAddress = True
426
427
428
        if childType is None:
            self.childType = None
        else:
hjk's avatar
hjk committed
429
430
            self.childType = stripClassTag(str(childType))
            self.d.put('childtype="%s",' % self.childType)
431
432
433
434
435
436
437
438
439
440
            if childNumChild is None:
                if 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
hjk's avatar
hjk committed
441
442
443
444
445
446
447
        try:
            if not addrBase is None and not addrStep is None:
                self.d.put('addrbase="0x%x",' % long(addrBase))
                self.d.put('addrstep="0x%x",' % long(addrStep))
                self.printsAddress = False
        except:
            warn("ADDRBASE: %s" % addrBase)
448
449
450
451
452
        #warn("CHILDREN: %s %s %s" % (numChild, childType, childNumChild))

    def __enter__(self):
        self.savedChildType = self.d.currentChildType
        self.savedChildNumChild = self.d.currentChildNumChild
hjk's avatar
hjk committed
453
454
455
456
457
458
459
460
        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
461
462
463
        self.d.put("children=[")

    def __exit__(self, exType, exValue, exTraceBack):
464
465
466
467
468
        if not exType is None:
            if self.d.passExceptions:
                showException("CHILDREN", exType, exValue, exTraceBack)
            self.d.putNumChild(0)
            self.d.putValue("<not accessible>")
hjk's avatar
hjk committed
469
470
471
        if not self.d.currentMaxNumChild is None:
            if self.d.currentMaxNumChild < self.d.currentNumChild:
                self.d.put('{name="<incomplete>",value="",type="",numchild="0"},')
472
473
        self.d.currentChildType = self.savedChildType
        self.d.currentChildNumChild = self.savedChildNumChild
hjk's avatar
hjk committed
474
475
476
        self.d.currentNumChild = self.savedNumChild
        self.d.currentMaxNumChild = self.savedMaxNumChild
        self.d.currentPrintsAddress = self.savedPrintsAddress
477
478
479
480
        self.d.put('],')
        return True


481
def value(expr):
482
    value = parseAndEvaluate(expr)
483
484
485
486
487
    try:
        return int(value)
    except:
        return str(value)

hjk's avatar
hjk committed
488
def isSimpleType(typeobj):
489
    code = typeobj.code
490
491
492
493
494
    return code == BoolCode \
        or code == CharCode \
        or code == IntCode \
        or code == FloatCode \
        or code == EnumCode
hjk's avatar
hjk committed
495

496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
def simpleEncoding(typeobj):
    code = typeobj.code
    if code == BoolCode or code == CharCode:
        return Hex2EncodedInt1
    if code == IntCode:
        if typeobj.sizeof == 1:
            return Hex2EncodedInt1
        if typeobj.sizeof == 2:
            return Hex2EncodedInt2
        if typeobj.sizeof == 4:
            return Hex2EncodedInt4
        if typeobj.sizeof == 8:
            return Hex2EncodedInt8
    if code == FloatCode:
        if typeobj.sizeof == 4:
            return Hex2EncodedFloat4
        if typeobj.sizeof == 8:
            return Hex2EncodedFloat8
    return None

hjk's avatar
hjk committed
516
def warn(message):
517
518
    if True or verbosity > 0:
        print "XXX: %s\n" % message.encode("latin1")
hjk's avatar
hjk committed
519
520
521
522
523
524
    pass

def check(exp):
    if not exp:
        raise RuntimeError("Check failed")

525
526
527
528
529
def checkSimpleRef(ref):
    count = ref["_q_value"]
    check(count > 0)
    check(count < 1000000)

530
def checkRef(ref):
531
532
533
534
535
536
537
538
539
540
    try:
        count = ref["atomic"]["_q_value"] # Qt 5.
        minimum = -1
    except:
        count = ref["_q_value"] # Qt 4.
        minimum = 0
    # Assume there aren't a million references to any object.
    check(count >= minimum)
    check(count < 1000000)

541

hjk's avatar
hjk committed
542
#def couldBePointer(p, align):
543
#    type = lookupType("unsigned int")
hjk's avatar
hjk committed
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
#    ptr = gdb.Value(p).cast(type)
#    d = int(str(ptr))
#    warn("CHECKING : %s %d " % (p, ((d & 3) == 0 and (d > 1000 or d == 0))))
#    return (d & (align - 1)) and (d > 1000 or d == 0)


def checkAccess(p, align = 1):
    return p.dereference()

def checkContents(p, expected, align = 1):
    if int(p.dereference()) != expected:
        raise RuntimeError("Contents check failed")

def checkPointer(p, align = 1):
    if not isNull(p):
        p.dereference()

561
562
563
564
565
566
def isAccessible(p):
    try:
        long(p)
        return True
    except:
        return False
hjk's avatar
hjk committed
567
568

def isNull(p):
569
570
571
572
    # The following can cause evaluation to abort with "UnicodeEncodeError"
    # for invalid char *, as their "contents" is being examined
    #s = str(p)
    #return s == "0x0" or s.startswith("0x0 ")
573
574
575
576
577
    #try:
    #    # Can fail with: "RuntimeError: Cannot access memory at address 0x5"
    #    return p.cast(lookupType("void").pointer()) == 0
    #except:
    #    return False
578
579
580
581
582
    try:
        # Can fail with: "RuntimeError: Cannot access memory at address 0x5"
        return long(p) == 0
    except:
        return False
hjk's avatar
hjk committed
583
584

movableTypes = set([
hjk's avatar
hjk committed
585
586
587
588
589
590
    "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",
hjk's avatar
hjk committed
591
    "QXmlStreamAttribute", "QXmlStreamNamespaceDeclaration",
hjk's avatar
hjk committed
592
593
594
595
596
597
598
599
600
601
602
603
604
    "QXmlStreamNotationDeclaration", "QXmlStreamEntityDeclaration"
])

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
hjk's avatar
hjk committed
605
606

def checkPointerRange(p, n):
607
    for i in xrange(n):
hjk's avatar
hjk committed
608
609
610
        checkPointer(p)
        ++p

hjk's avatar
hjk committed
611
612
613
614
615
616
617
618
619
620
621
622
623
def call2(value, func, args):
    # args is a tuple.
    arg = ""
    for i in range(len(args)):
        if i:
            arg += ','
        a = args[i]
        if (':' in a) and not ("'" in a):
            arg = "'%s'" % a
        else:
            arg += a

    #warn("CALL: %s -> %s(%s)" % (value, func, arg))
hjk's avatar
hjk committed
624
    type = stripClassTag(str(value.type))
625
    if type.find(":") >= 0:
hjk's avatar
hjk committed
626
        type = "'" + type + "'"
627
    # 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
hjk's avatar
hjk committed
628
    exp = "((class %s*)%s)->%s(%s)" % (type, value.address, func, arg)
hjk's avatar
hjk committed
629
    #warn("CALL: %s" % exp)
630
631
632
633
634
    result = None
    try:
        result = parseAndEvaluate(exp)
    except:
        pass
hjk's avatar
hjk committed
635
636
637
    #warn("  -> %s" % result)
    return result

hjk's avatar
hjk committed
638
639
640
def call(value, func, *args):
    return call2(value, func, args)

641
642
643
644
def makeValue(type, init):
    type = stripClassTag(type)
    if type.find(":") >= 0:
        type = "'" + type + "'"
hjk's avatar
hjk committed
645
    # Avoid malloc symbol clash with QVector.
646
    gdb.execute("set $d = (%s*)calloc(sizeof(%s), 1)" % (type, type))
647
648
649
650
651
652
653
    gdb.execute("set *$d = {%s}" % init)
    value = parseAndEvaluate("$d").dereference()
    #warn("  TYPE: %s" % value.type)
    #warn("  ADDR: %s" % value.address)
    #warn("  VALUE: %s" % value)
    return value

654
655
656
657
658
659
660
661
662
663
664
665
def makeStdString(init):
    # Works only for small allocators, but they are usually empty.
    gdb.execute("set $d=(std::string*)calloc(sizeof(std::string), 2)");
    gdb.execute("call($d->basic_string(\"" + init +
        "\",*(std::allocator<char>*)(1+$d)))")
    value = parseAndEvaluate("$d").dereference()
    #warn("  TYPE: %s" % value.type)
    #warn("  ADDR: %s" % value.address)
    #warn("  VALUE: %s" % value)
    return value


666
667
668
669
670
671
672
673
674
675
def makeExpression(value):
    type = stripClassTag(str(value.type))
    if type.find(":") >= 0:
        type = "'" + type + "'"
    #warn("  TYPE: %s" % type)
    #exp = "(*(%s*)(&%s))" % (type, value.address)
    exp = "(*(%s*)(%s))" % (type, value.address)
    #warn("  EXP: %s" % exp)
    return exp

676
677
qqNs = None

678
def qtNamespace():
679
    # FIXME: This only works when call from inside a Qt function frame.
680
    global qqNs
681
682
    if not qqNs is None:
        return qqNs
683
    try:
684
        str = catchCliOutput("ptype QString::Null")[0]
hjk's avatar
hjk committed
685
        # The result looks like:
686
687
688
689
690
        # "type = const struct myns::QString::Null {"
        # "    <no data fields>"
        # "}"
        pos1 = str.find("struct") + 7
        pos2 = str.find("QString::Null")
691
692
693
694
        if pos1 > -1 and pos2 > -1:
            qqNs = str[pos1:pos2]
            return qqNs
        return ""
695
    except:
696
        return ""
hjk's avatar
hjk committed
697

698
699
def findFirstZero(p, maximum):
    for i in xrange(maximum):
700
701
702
        if p.dereference() == 0:
            return i
        p = p + 1
703
    return maximum + 1
704

705
706
707
708
709
710
711
def encodeCArray(p, innerType, suffix):
    t = lookupType(innerType)
    p = p.cast(t.pointer())
    limit = findFirstZero(p, qqStringCutOff)
    s = readRawMemory(p, limit * t.sizeof)
    if limit > qqStringCutOff:
        s += suffix
712
    return s
713

714
715
def encodeCharArray(p):
    return encodeCArray(p, "unsigned char", "2e2e2e")
716

717
718
def encodeChar2Array(p):
    return encodeCArray(p, "unsigned short", "2e002e002e00")
719

720
721
def encodeChar4Array(p):
    return encodeCArray(p, "unsigned int", "2e0000002e0000002e000000")
722

723
def qByteArrayData(value):
724
725
726
727
728
    private = value['d']
    checkRef(private['ref'])
    try:
        # Qt 5. Will fail on Qt 4 due to the missing 'offset' member.
        offset = private['offset']
729
        charPointerType = lookupType('char *')
730
731
732
733
734
        data = private.cast(charPointerType) + private['offset']
        return data, int(private['size']), int(private['alloc'])
    except:
        # Qt 4:
        return private['data'], int(private['size']), int(private['alloc'])
735

736
def encodeByteArray(value):
737
    data, size, alloc = qByteArrayData(value)
738
739
740
741
742
743
744
    if alloc != 0:
        check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
    limit = min(size, qqStringCutOff)
    s = readRawMemory(data, limit)
    if limit < size:
        s += "2e2e2e"
    return s
745

746
def qStringData(value):
747
748
749
750
751
    private = value['d']
    checkRef(private['ref'])
    try:
        # Qt 5. Will fail on Qt 4 due to the missing 'offset' member.
        offset = private['offset']
752
        ushortPointerType = lookupType('ushort *')
753
754
755
756
757
        data = private.cast(ushortPointerType) + offset / 2
        return data, int(private['size']), int(private['alloc'])
    except:
        # Qt 4.
        return private['data'], int(private['size']), int(private['alloc'])
758

759
def encodeString(value):
760
    data, size, alloc = qStringData(value)
761
762
    if alloc != 0:
        check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
763
    limit = min(size, qqStringCutOff)
764
    s = readRawMemory(data, 2 * limit)
765
766
    if limit < size:
        s += "2e002e002e00"
767
768
    return s

hjk's avatar
hjk committed
769
770
def stripTypedefs(type):
    type = type.unqualified()
hjk's avatar
hjk committed
771
    while type.code == TypedefCode:
772
773
774
        type = type.strip_typedefs().unqualified()
    return type

hjk's avatar
hjk committed
775
776
def extractFields(type):
    # Insufficient, see http://sourceware.org/bugzilla/show_bug.cgi?id=10953:
hjk's avatar
hjk committed
777
    #fields = type.fields()
hjk's avatar
hjk committed
778
    # Insufficient, see http://sourceware.org/bugzilla/show_bug.cgi?id=11777:
hjk's avatar
hjk committed
779
    #fields = defsype).fields()
hjk's avatar
hjk committed
780
    # This seems to work.
hjk's avatar
hjk committed
781
    #warn("TYPE 0: %s" % type)
hjk's avatar
hjk committed
782
    type = stripTypedefs(type)
783
784
785
    fields = type.fields()
    if len(fields):
        return fields
hjk's avatar
hjk committed
786
    #warn("TYPE 1: %s" % type)
787
    # This fails for arrays. See comment in lookupType.
788
789
790
    type0 = lookupType(str(type))
    if not type0 is None:
        type = type0
hjk's avatar
hjk committed
791
    if type.code == FunctionCode:
792
        return []
hjk's avatar
hjk committed
793
794
    #warn("TYPE 2: %s" % type)
    fields = type.fields()
hjk's avatar
hjk committed
795
    #warn("FIELDS: %s" % fields)
hjk's avatar
hjk committed
796
797
    return fields

hjk's avatar
hjk committed
798
799
#######################################################################
#
800
# LocalItem
hjk's avatar
hjk committed
801
802
803
#
#######################################################################

804
# Contains iname, name, and value.
hjk's avatar
hjk committed
805
class LocalItem:
806
    pass
hjk's avatar
hjk committed
807

808
809
810
811
812
813
#######################################################################
#
# SetupCommand
#
#######################################################################

814
# This is a cache mapping from 'type name' to 'display alternatives'.
815
qqFormats = {}
816
817

# This is a cache of all known dumpers.
hjk's avatar
hjk committed
818
qqDumpers = {}
819

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

823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
# This keeps canonical forms of the typenames, without array indices etc.
qqStripForFormat = {}

def stripForFormat(typeName):
    if typeName in qqStripForFormat:
        return qqStripForFormat[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
    qqStripForFormat[typeName] = stripped
    return stripped

846
def bbsetup(args):
hjk's avatar
hjk committed
847
    typeCache = {}
848
849
850
851
852
    module = sys.modules[__name__]
    for key, value in module.__dict__.items():
        if key.startswith("qdump__"):
            name = key[7:]
            qqDumpers[name] = value
hjk's avatar
hjk committed
853
            qqFormats[name] = qqFormats.get(name, "")
854
855
856
857
858
859
860
861
        elif key.startswith("qform__"):
            name = key[7:]
            formats = ""
            try:
                formats = value()
            except:
                pass
            qqFormats[name] = formats
862
863
864
865
866
867
        elif key.startswith("qedit__"):
            name = key[7:]
            try:
                qqEditable[name] = value
            except:
                pass
868
    result = "dumpers=["
869
    #qqNs = qtNamespace() # This is too early
870
    for key, value in qqFormats.items():
871
872
873
874
        if qqEditable.has_key(key):
            result += '{type="%s",formats="%s",editable="true"},' % (key, value)
        else:
            result += '{type="%s",formats="%s"},' % (key, value)
875
876
    result += ']'
    #result += ',namespace="%s"' % qqNs
877
    result += ',hasInferiorThreadList="%s"' % int(hasInferiorThreadList())
878
879
    return result

880
881
registerCommand("bbsetup", bbsetup)

882

883
884
885
886
887
888
889
#######################################################################
#
# Edit Command
#
#######################################################################

def bbedit(args):
890
891
    (type, expr, value) = args.split(",")
    type = base64.b16decode(type, True)
892
    ns = qtNamespace()
893
894
    if type.startswith(ns):
        type = type[len(ns):]
895
    type = type.replace("::", "__")
896
897
898
899
900
901
    pos = type.find('<')
    if pos != -1:
        type = type[0:pos]
    expr = base64.b16decode(expr, True)
    value = base64.b16decode(value, True)
    #warn("EDIT: %s %s %s %s: " % (pos, type, expr, value))
902
903
    if qqEditable.has_key(type):
        qqEditable[type](expr, value)
904
905
    else:
        gdb.execute("set (%s)=%s" % (expr, value))
906

907
registerCommand("bbedit", bbedit)
908
909


hjk's avatar
hjk committed
910
911
#######################################################################
#
912
# Frame Command
hjk's avatar
hjk committed
913
914
915
#
#######################################################################

916
917
typesToReport = {}

918
def bb(args):
919
920
921
922
    global typesToReport
    output = Dumper(args).output
    output.append('],typeinfo=[')
    for name, type in typesToReport.iteritems():
923
924
925
926
        # Happens e.g. for '(anonymous namespace)::InsertDefOperation'
        if not type is None:
            output.append('{name="%s",size="%s"}'
                % (base64.b64encode(name), type.sizeof))
927
    output.append(']')
928
    typesToReport = {}
929
    return "".join(output)
hjk's avatar
hjk committed
930

931
932
933
934
935
936
937
938
939
940
941
942
943
944

def p1(args):
    import cProfile
    cProfile.run('bb("%s")' % args, "/tmp/bbprof")
    import pstats
    pstats.Stats('/tmp/bbprof').sort_stats('time').print_stats()
    return ""


def p2(args):
    import timeit
    return timeit.repeat('bb("%s")' % args,
        'from __main__ import bb', number=10)

945
946
registerCommand("bb", bb)
registerCommand("p1", p1)
947
948
registerCommand("p2", p2)

hjk's avatar
hjk committed
949
950
951
952
953
954
955

#######################################################################
#
# The Dumper Class
#
#######################################################################

956

hjk's avatar
hjk committed
957
class Dumper:
958
    def __init__(self, args):
hjk's avatar
hjk committed
959
        self.output = []
hjk's avatar
hjk committed
960
961
        self.currentIName = ""
        self.currentPrintsAddress = True
962
963
        self.currentChildType = ""
        self.currentChildNumChild = -1
hjk's avatar
hjk committed
964
965
        self.currentMaxNumChild = -1
        self.currentNumChild = -1
966
967
968
969
970
        self.currentValue = None
        self.currentValuePriority = -100
        self.currentValueEncoding = None
        self.currentType = None
        self.currentTypePriority = -100
971
972
973
974
        self.typeformats = {}
        self.formats = {}
        self.expandedINames = ""

975
976
        self.output.append('data=[')

977
978
979
980
        options = []
        varList = []
        watchers = ""

981
        resultVarName = ""
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
        for arg in args.split(' '):
            pos = arg.find(":") + 1
            if arg.startswith("options:"):
                options = arg[pos:].split(",")
            elif arg.startswith("vars:"):
                if len(arg[pos:]) > 0:
                    varList = arg[pos:].split(",")
            elif arg.startswith("resultvarname:"):
                resultVarName = arg[pos:]
            elif arg.startswith("expanded:"):
                self.expandedINames = set(arg[pos:].split(","))
            elif arg.startswith("typeformats:"):
                for f in arg[pos:].split(","):
                    pos = f.find("=")
                    if pos != -1:
                        type = base64.b16decode(f[0:pos], True)
                        self.typeformats[type] = int(f[pos+1:])
            elif arg.startswith("formats:"):
                for f in arg[pos:].split(","):
                    pos = f.find("=")
                    if pos != -1:
                        self.formats[f[0:pos]] = int(f[pos+1:])
            elif arg.startswith("watchers:"):
                watchers = base64.b16decode(arg[pos:], True)

1007
        self.useDynamicType = "dyntype" in options
1008
1009
1010
        self.useFancy = "fancy" in options
        self.passExceptions = "pe" in options
        self.autoDerefPointers = "autoderef" in options
1011
        self.partialUpdate = "partial" in options
1012
1013
        self.tooltipOnly = "tooltiponly" in options
        self.noLocals = "nolocals" in options
1014
        self.ns = qtNamespace()
1015
1016
1017
1018

        #warn("NAMESPACE: '%s'" % self.ns)
        #warn("VARIABLES: %s" % varList)
        #warn("EXPANDED INAMES: %s" % self.expandedINames)
1019
        #warn("WATCHERS: %s" % watchers)
1020
1021
        #warn("PARTIAL: %s" % self.partialUpdate)
        #warn("NO LOCALS: %s" % self.noLocals)
1022
1023
1024
1025
1026
        module = sys.modules[__name__]

        #
        # Locals
        #
1027
        locals = []
1028
        fullUpdateNeeded = True
1029
        if self.partialUpdate and len(varList) == 1 and not self.tooltipOnly:
1030
1031
1032
1033
1034
1035
1036
1037
            #warn("PARTIAL: %s" % varList)
            parts = varList[0].split('.')
            #warn("PARTIAL PARTS: %s" % parts)
            name = parts[1]
            #warn("PARTIAL VAR: %s" % name)
            #fullUpdateNeeded = False
            try:
                frame = gdb.selected_frame()
hjk's avatar
hjk committed
1038
1039
1040
                item = LocalItem()
                item.iname = "local." + name
                item.name = name
1041
1042
1043
1044
1045
1046
1047
1048
                item.value = frame.read_var(name)
                locals = [item]
                #warn("PARTIAL LOCALS: %s" % locals)
                fullUpdateNeeded = False
            except:
                pass
            varList = []

1049
        if fullUpdateNeeded and not self.tooltipOnly and not self.noLocals:
1050
            locals = listOfLocals(varList)
1051

1052
1053
1054
1055
        if "autotest" in options:
            for item in listOfLocals([]):
                self.expandedINames.add(item.iname)
                self.expandedINames.discard("")
1056
                #warn("EXPANDED: %s" % self.expandedINames)
1057

1058
1059
1060
        # Take care of the return value of the last function call.
        if len(resultVarName) > 0:
            try:
hjk's avatar
hjk committed
1061
1062
1063
1064
1065
                item = LocalItem()
                item.name = resultVarName
                item.iname = "return." + resultVarName
                item.value = parseAndEvaluate(resultVarName)
                locals.append(item)
1066
1067
1068
1069
1070
            except:
                # Don't bother. It's only supplementary information anyway.
                pass

        for item in locals:
1071
            value = downcast(item.value) if self.useDynamicType else item.value
hjk's avatar
hjk committed
1072
            with OutputSafer(self):
hjk's avatar
hjk committed
1073
1074
1075
1076
1077
                self.anonNumber = -1

                type = value.type.unqualified()
                typeName = str(type)

1078
                # Special handling for char** argv.
1079
                if type.code == PointerCode \
hjk's avatar
hjk committed
1080
1081
1082
1083
1084
1085
1086
1087
                        and item.iname == "local.argv" \
                        and typeName == "char **":
                    n = 0
                    p = value
                    # p is 0 for "optimized out" cases. Or contains rubbish.
                    try:
                        if not isNull(p):
                            while not isNull(p.dereference()) and n <= 100:
1088
                                p += 1
hjk's avatar
hjk committed
1089
1090
1091
                                n += 1
                    except:
                        pass
1092

hjk's avatar
hjk committed
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
                    with TopLevelItem(self, item.iname):
                        self.put('iname="local.argv",name="argv",')
                        self.putItemCount(n, 100)
                        self.putType(typeName)
                        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
                    continue

                else:
                    # A "normal" local variable or parameter.
                    with TopLevelItem(self, item.iname):
1109
1110
                        self.put('iname="%s",' % item.iname)
                        self.put('name="%s",' % item.name)
hjk's avatar
hjk committed
1111
                        self.putItem(value)
1112
1113
1114
1115

        #
        # Watchers
        #
hjk's avatar
hjk committed
1116
        with OutputSafer(self):
1117
            if len(watchers) > 0:
hjk's avatar
hjk committed
1118
                self.put(",")
1119
1120
                for watcher in watchers.split("##"):
                    (exp, iname) = watcher.split("#")
1121
                    self.handleWatch(exp, iname)
1122

1123
        #print('data=[' + locals + sep + watchers + ']\n')
1124
1125
1126
1127


    def handleWatch(self, exp, iname):
        exp = str(exp)
1128
        escapedExp = base64.b64encode(exp);
1129
1130
1131
        #warn("HANDLING WATCH %s, INAME: '%s'" % (exp, iname))
        if exp.startswith("[") and exp.endswith("]"):
            #warn("EVAL: EXP: %s" % exp)
hjk's avatar
hjk committed
1132
            with TopLevelItem(self, iname):
1133
                self.put('iname="%s",' % iname)
1134
                self.put('wname="%s",' % escapedExp)
1135
1136
1137
                try:
                    list = eval(exp)
                    self.putValue("")
1138
                    self.putNoType()
1139
1140
1141
1142
1143
                    self.putNumChild(len(list))
                    # This is a list of expressions to evaluate
                    with Children(self, len(list)):
                        itemNumber = 0
                        for item in list:
1144
                            self.handleWatch(item, "%s.%d" % (iname, itemNumber))
1145
1146
1147
1148
                            itemNumber += 1
                except RuntimeError, error:
                    warn("EVAL: ERROR CAUGHT %s" % error)
                    self.putValue("<syntax error>")