dumper.py 63 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
typeCache = {}

144
145
class TypeInfo:
    def __init__(self, type):
146
        self.type = type
147
148
149
        self.reported = False


150
def lookupType(typestring):
151
    typeInfo = typeCache.get(typestring)
hjk's avatar
hjk committed
152
    #warn("LOOKUP 1: %s -> %s" % (typestring, type))
153
154
    if not typeInfo is None:
        return typeInfo.type
155

156
157
    if typestring == "void":
        type = gdb.lookup_type(typestring)
158
        typeCache[typestring] = TypeInfo(type)
159
160
161
162
163
        return type

    if typestring.find("(anon") != -1:
        # gdb doesn't like
        # '(anonymous namespace)::AddAnalysisMessageSuppressionComment'
164
165
        #typeCache[typestring] = None
        typeCache[typestring] = TypeInfo(type)
166
167
168
169
        return None

    try:
        type = gdb.parse_and_eval("{%s}&main" % typestring).type
170
        typeCache[typestring] = TypeInfo(type)
171
172
173
174
        return type
    except:
        pass

hjk's avatar
hjk committed
175
176
177
    #warn(" RESULT FOR 7.2: '%s': %s" % (typestring, type))
    #typeCache[typestring] = type
    #return None
178

hjk's avatar
hjk committed
179
180
    # This part should only trigger for
    # gdb 7.1 for types with namespace separators.
181

182
183
    ts = typestring
    while True:
hjk's avatar
hjk committed
184
        #warn("TS: '%s'" % ts)
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
        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
208
209
        if not type is None:
            type = type.pointer()
210
            typeCache[typestring] = TypeInfo(type)
211
212
213
214
215
216
217
            return type

    try:
        #warn("LOOKING UP '%s'" % ts)
        type = gdb.lookup_type(ts)
    except RuntimeError, error:
        #warn("LOOKING UP '%s': %s" % (ts, error))
218
219
220
221
222
        if type is None:
            pos = typestring.find("<unnamed>")
            if pos != -1:
                # See http://sourceware.org/bugzilla/show_bug.cgi?id=13269
                return lookupType(typestring.replace("<unnamed>", "(anonymous namespace)"))
223
224
225
226
227
228
229
230
231
        # 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

    # This could still be None as gdb.lookup_type("char[3]") generates
    # "RuntimeError: No type named char[3]"
236
237
    return type

238
def cleanAddress(addr):
239
240
    if addr is None:
        return "<no address>"
241
242
    # We cannot use str(addr) as it yields rubbish for char pointers
    # that might trigger Unicode encoding errors.
243
    #return addr.cast(lookupType("void").pointer())
244
245
    # We do not use "hex(...)" as it (sometimes?) adds a "L" suffix.
    return "0x%x" % long(addr)
246

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
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):
277
278
279
280
281
282
    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<...>"
283
        return lookupType(extractTemplateArgument(type.strip_typedefs(), position))
284

285

hjk's avatar
hjk committed
286
287
288
289
# Workaround for gdb < 7.1
def numericTemplateArgument(type, position):
    try:
        return int(type.template_argument(position))
290
    except RuntimeError, error:
hjk's avatar
hjk committed
291
292
        # ": No type named 30."
        msg = str(error)
293
294
295
296
297
298
        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
299

300

301
302
303
304
305
306
307
308
309
310
311
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
312
    def __init__(self, d):
313
314
315
316
        self.d = d

    def __enter__(self):
        self.savedOutput = self.d.output
hjk's avatar
hjk committed
317
        self.d.output = []
318
319
320
321
322
323

    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
324
325
            self.savedOutput.extend(self.d.output)
            self.d.output = self.savedOutput
326
327
328
        return False


329
330
331
332
333
class NoAddress:
    def __init__(self, d):
        self.d = d

    def __enter__(self):
hjk's avatar
hjk committed
334
335
        self.savedPrintsAddress = self.d.currentPrintsAddress
        self.d.currentPrintsAddress = False
336
337

    def __exit__(self, exType, exValue, exTraceBack):
hjk's avatar
hjk committed
338
        self.d.currentPrintsAddress = self.savedPrintsAddress
339
340


341

342
class SubItem:
hjk's avatar
hjk committed
343
    def __init__(self, d, component):
344
        self.d = d
hjk's avatar
hjk committed
345
346
        self.iname = "%s.%s" % (d.currentIName, component)
        self.name = component
347
348
349

    def __enter__(self):
        self.d.put('{')
hjk's avatar
hjk committed
350
351
352
353
        #if not self.name is None:
        if isinstance(self.name, str):
            self.d.put('name="%s",' % self.name)
        self.savedIName = self.d.currentIName
354
355
356
357
358
        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
359
        self.d.currentIName = self.iname
360
        self.d.currentValuePriority = -100
361
362
        self.d.currentValueEncoding = None
        self.d.currentType = ""
363
        self.d.currentTypePriority = -100
364
365
366
367

    def __exit__(self, exType, exValue, exTraceBack):
        #warn(" CURRENT VALUE: %s %s %s" % (self.d.currentValue,
        #    self.d.currentValueEncoding, self.d.currentValuePriority))
368
369
370
371
372
        if not exType is None:
            if self.d.passExceptions:
                showException("SUBITEM", exType, exValue, exTraceBack)
            self.d.putNumChild(0)
            self.d.putValue("<not accessible>")
373
        try:
374
            #warn("TYPE VALUE: %s" % self.d.currentValue)
hjk's avatar
hjk committed
375
376
377
378
379
380
381
382
383
384
            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)
385
                self.d.put('value="%s",' % self.d.currentValue)
386
387
        except:
            pass
388
        self.d.put('},')
hjk's avatar
hjk committed
389
        self.d.currentIName = self.savedIName
390
391
392
393
394
395
396
        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
397
398
399
400
401
402
403
404
405
406
407
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
408
409

class Children:
hjk's avatar
hjk committed
410
411
    def __init__(self, d, numChild = 1, childType = None, childNumChild = None,
            maxNumChild = None, addrBase = None, addrStep = None):
412
413
414
        self.d = d
        self.numChild = numChild
        self.childNumChild = childNumChild
hjk's avatar
hjk committed
415
416
417
418
        self.maxNumChild = maxNumChild
        self.addrBase = addrBase
        self.addrStep = addrStep
        self.printsAddress = True
419
420
421
        if childType is None:
            self.childType = None
        else:
hjk's avatar
hjk committed
422
423
            self.childType = stripClassTag(str(childType))
            self.d.put('childtype="%s",' % self.childType)
424
425
426
427
428
429
430
431
432
433
            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
434
435
436
437
438
439
440
        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)
441
442
443
444
445
        #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
446
447
448
449
450
451
452
453
        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
454
455
456
        self.d.put("children=[")

    def __exit__(self, exType, exValue, exTraceBack):
457
458
459
460
461
        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
462
463
464
        if not self.d.currentMaxNumChild is None:
            if self.d.currentMaxNumChild < self.d.currentNumChild:
                self.d.put('{name="<incomplete>",value="",type="",numchild="0"},')
465
466
        self.d.currentChildType = self.savedChildType
        self.d.currentChildNumChild = self.savedChildNumChild
hjk's avatar
hjk committed
467
468
469
        self.d.currentNumChild = self.savedNumChild
        self.d.currentMaxNumChild = self.savedMaxNumChild
        self.d.currentPrintsAddress = self.savedPrintsAddress
470
471
472
473
        self.d.put('],')
        return True


474
def value(expr):
475
    value = parseAndEvaluate(expr)
476
477
478
479
480
    try:
        return int(value)
    except:
        return str(value)

hjk's avatar
hjk committed
481
def isSimpleType(typeobj):
482
    code = typeobj.code
483
484
485
486
487
    return code == BoolCode \
        or code == CharCode \
        or code == IntCode \
        or code == FloatCode \
        or code == EnumCode
hjk's avatar
hjk committed
488

489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
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
509
def warn(message):
510
511
    if True or verbosity > 0:
        print "XXX: %s\n" % message.encode("latin1")
hjk's avatar
hjk committed
512
513
514
515
516
517
    pass

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

518
519
520
521
522
def checkSimpleRef(ref):
    count = ref["_q_value"]
    check(count > 0)
    check(count < 1000000)

523
def checkRef(ref):
524
525
526
527
528
529
530
531
532
533
    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)

534

hjk's avatar
hjk committed
535
#def couldBePointer(p, align):
536
#    type = lookupType("unsigned int")
hjk's avatar
hjk committed
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
#    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()

554
555
556
557
558
559
def isAccessible(p):
    try:
        long(p)
        return True
    except:
        return False
hjk's avatar
hjk committed
560
561

def isNull(p):
562
563
564
565
    # 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 ")
566
567
568
569
570
    #try:
    #    # Can fail with: "RuntimeError: Cannot access memory at address 0x5"
    #    return p.cast(lookupType("void").pointer()) == 0
    #except:
    #    return False
571
572
573
574
575
    try:
        # Can fail with: "RuntimeError: Cannot access memory at address 0x5"
        return long(p) == 0
    except:
        return False
hjk's avatar
hjk committed
576
577

movableTypes = set([
hjk's avatar
hjk committed
578
579
580
581
582
583
    "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
584
    "QXmlStreamAttribute", "QXmlStreamNamespaceDeclaration",
hjk's avatar
hjk committed
585
586
587
588
589
590
591
592
593
594
595
596
597
    "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
598
599

def checkPointerRange(p, n):
600
    for i in xrange(n):
hjk's avatar
hjk committed
601
602
603
        checkPointer(p)
        ++p

hjk's avatar
hjk committed
604
605
606
607
608
609
610
611
612
613
614
615
616
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
617
    type = stripClassTag(str(value.type))
618
    if type.find(":") >= 0:
hjk's avatar
hjk committed
619
        type = "'" + type + "'"
620
    # 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
hjk's avatar
hjk committed
621
    exp = "((class %s*)%s)->%s(%s)" % (type, value.address, func, arg)
hjk's avatar
hjk committed
622
    #warn("CALL: %s" % exp)
623
624
625
626
627
    result = None
    try:
        result = parseAndEvaluate(exp)
    except:
        pass
hjk's avatar
hjk committed
628
629
630
    #warn("  -> %s" % result)
    return result

hjk's avatar
hjk committed
631
632
633
def call(value, func, *args):
    return call2(value, func, args)

634
635
636
637
def makeValue(type, init):
    type = stripClassTag(type)
    if type.find(":") >= 0:
        type = "'" + type + "'"
hjk's avatar
hjk committed
638
    # Avoid malloc symbol clash with QVector.
639
    gdb.execute("set $d = (%s*)calloc(sizeof(%s), 1)" % (type, type))
640
641
642
643
644
645
646
    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

647
648
649
650
651
652
653
654
655
656
657
658
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


659
660
661
662
663
664
665
666
667
668
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

669
670
qqNs = None

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

691
692
def findFirstZero(p, maximum):
    for i in xrange(maximum):
693
694
695
        if p.dereference() == 0:
            return i
        p = p + 1
696
    return maximum + 1
697

698
699
700
701
702
703
704
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
705
    return s
706

707
708
def encodeCharArray(p):
    return encodeCArray(p, "unsigned char", "2e2e2e")
709

710
711
def encodeChar2Array(p):
    return encodeCArray(p, "unsigned short", "2e002e002e00")
712

713
714
def encodeChar4Array(p):
    return encodeCArray(p, "unsigned int", "2e0000002e0000002e000000")
715

716
def qByteArrayData(value):
717
718
719
720
721
    private = value['d']
    checkRef(private['ref'])
    try:
        # Qt 5. Will fail on Qt 4 due to the missing 'offset' member.
        offset = private['offset']
722
        charPointerType = lookupType('char *')
723
724
725
726
727
        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'])
728

729
def encodeByteArray(value):
730
    data, size, alloc = qByteArrayData(value)
731
732
733
734
735
736
737
    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
738

739
def qStringData(value):
740
741
742
743
744
    private = value['d']
    checkRef(private['ref'])
    try:
        # Qt 5. Will fail on Qt 4 due to the missing 'offset' member.
        offset = private['offset']
745
        ushortPointerType = lookupType('ushort *')
746
747
748
749
750
        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'])
751

752
def encodeString(value):
753
    data, size, alloc = qStringData(value)
754
755
    if alloc != 0:
        check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
756
    limit = min(size, qqStringCutOff)
757
    s = readRawMemory(data, 2 * limit)
758
759
    if limit < size:
        s += "2e002e002e00"
760
761
    return s

hjk's avatar
hjk committed
762
763
def stripTypedefs(type):
    type = type.unqualified()
hjk's avatar
hjk committed
764
    while type.code == TypedefCode:
765
766
767
        type = type.strip_typedefs().unqualified()
    return type

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

hjk's avatar
hjk committed
791
792
#######################################################################
#
793
# LocalItem
hjk's avatar
hjk committed
794
795
796
#
#######################################################################

797
# Contains iname, name, and value.
hjk's avatar
hjk committed
798
class LocalItem:
799
    pass
hjk's avatar
hjk committed
800

801
802
803
804
805
806
#######################################################################
#
# SetupCommand
#
#######################################################################

807
# This is a cache mapping from 'type name' to 'display alternatives'.
808
qqFormats = {}
809
810

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

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

816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
# 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

839
def bbsetup(args):
hjk's avatar
hjk committed
840
    typeCache = {}
841
842
843
844
845
    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
846
            qqFormats[name] = qqFormats.get(name, "")
847
848
849
850
851
852
853
854
        elif key.startswith("qform__"):
            name = key[7:]
            formats = ""
            try:
                formats = value()
            except:
                pass
            qqFormats[name] = formats
855
856
857
858
859
860
        elif key.startswith("qedit__"):
            name = key[7:]
            try:
                qqEditable[name] = value
            except:
                pass
861
    result = "dumpers=["
862
    #qqNs = qtNamespace() # This is too early
863
    for key, value in qqFormats.items():
864
865
866
867
        if qqEditable.has_key(key):
            result += '{type="%s",formats="%s",editable="true"},' % (key, value)
        else:
            result += '{type="%s",formats="%s"},' % (key, value)
868
869
    result += ']'
    #result += ',namespace="%s"' % qqNs
870
    result += ',hasInferiorThreadList="%s"' % int(hasInferiorThreadList())
871
872
    return result

873
874
registerCommand("bbsetup", bbsetup)

875

876
877
878
879
880
881
882
#######################################################################
#
# Edit Command
#
#######################################################################

def bbedit(args):
883
884
    (type, expr, value) = args.split(",")
    type = base64.b16decode(type, True)
885
    ns = qtNamespace()
886
887
    if type.startswith(ns):
        type = type[len(ns):]
888
    type = type.replace("::", "__")
889
890
891
892
893
894
    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))
895
896
    if qqEditable.has_key(type):
        qqEditable[type](expr, value)
897
898
    else:
        gdb.execute("set (%s)=%s" % (expr, value))
899

900
registerCommand("bbedit", bbedit)
901
902


hjk's avatar
hjk committed
903
904
#######################################################################
#
905
# Frame Command
hjk's avatar
hjk committed
906
907
908
#
#######################################################################

909
def bb(args):
hjk's avatar
hjk committed
910
    output = 'data=[' + "".join(Dumper(args).output) + '],typeinfo=['
911
    for typeName, typeInfo in typeCache.iteritems():
912
913
        if not typeInfo.reported:
            output += '{name="' + base64.b64encode(typeName)
914
            output += '",size="' + str(typeInfo.type.sizeof) + '"},'
915
916
917
            typeInfo.reported = True
    output += ']';
    return output
hjk's avatar
hjk committed
918

919
920
921
922
923
924
925
926
927
928
929
930
931
932

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)

933
934
registerCommand("bb", bb)
registerCommand("p1", p1)
935
936
registerCommand("p2", p2)

hjk's avatar
hjk committed
937
938
939
940
941
942
943

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

944

hjk's avatar
hjk committed
945
class Dumper:
946
    def __init__(self, args):
hjk's avatar
hjk committed
947
        self.output = []
hjk's avatar
hjk committed
948
949
        self.currentIName = ""
        self.currentPrintsAddress = True
950
951
        self.currentChildType = ""
        self.currentChildNumChild = -1
hjk's avatar
hjk committed
952
953
        self.currentMaxNumChild = -1
        self.currentNumChild = -1
954
955
956
957
958
        self.currentValue = None
        self.currentValuePriority = -100
        self.currentValueEncoding = None
        self.currentType = None
        self.currentTypePriority = -100
959
960
961
962
963
964
965
966
        self.typeformats = {}
        self.formats = {}
        self.expandedINames = ""

        options = []
        varList = []
        watchers = ""

967
        resultVarName = ""
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
        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)

993
        self.useDynamicType = "dyntype" in options
994
995
996
        self.useFancy = "fancy" in options
        self.passExceptions = "pe" in options
        self.autoDerefPointers = "autoderef" in options
997
        self.partialUpdate = "partial" in options
998
999
        self.tooltipOnly = "tooltiponly" in options
        self.noLocals = "nolocals" in options
1000
        self.ns = qtNamespace()
1001
1002
1003
1004

        #warn("NAMESPACE: '%s'" % self.ns)
        #warn("VARIABLES: %s" % varList)
        #warn("EXPANDED INAMES: %s" % self.expandedINames)
1005
        #warn("WATCHERS: %s" % watchers)
1006
1007
        #warn("PARTIAL: %s" % self.partialUpdate)
        #warn("NO LOCALS: %s" % self.noLocals)
1008
1009
1010
1011
1012
        module = sys.modules[__name__]

        #
        # Locals
        #
1013
        locals = []
1014
        fullUpdateNeeded = True
1015
        if self.partialUpdate and len(varList) == 1 and not self.tooltipOnly:
1016
1017
1018
1019
1020
1021
1022
1023
            #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
1024
1025
1026
                item = LocalItem()
                item.iname = "local." + name
                item.name = name
1027
1028
1029
1030
1031
1032
1033
1034
                item.value = frame.read_var(name)
                locals = [item]
                #warn("PARTIAL LOCALS: %s" % locals)
                fullUpdateNeeded = False
            except:
                pass
            varList = []

1035
        if fullUpdateNeeded and not self.tooltipOnly and not self.noLocals:
1036
            locals = listOfLocals(varList)
1037

1038
1039
1040
1041
        if "autotest" in options:
            for item in listOfLocals([]):
                self.expandedINames.add(item.iname)
                self.expandedINames.discard("")
1042
                #warn("EXPANDED: %s" % self.expandedINames)
1043

1044
1045
1046
        # Take care of the return value of the last function call.
        if len(resultVarName) > 0:
            try:
hjk's avatar
hjk committed
1047
1048
1049
1050
1051
                item = LocalItem()
                item.name = resultVarName
                item.iname = "return." + resultVarName
                item.value = parseAndEvaluate(resultVarName)
                locals.append(item)
1052
1053
1054
1055
1056
            except:
                # Don't bother. It's only supplementary information anyway.
                pass

        for item in locals:
1057
            value = downcast(item.value)
hjk's avatar
hjk committed
1058
            with OutputSafer(self):
hjk's avatar
hjk committed
1059
1060
1061
1062
1063
                self.anonNumber = -1

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

1064
                # Special handling for char** argv.
1065
                if type.code == PointerCode \
hjk's avatar
hjk committed
1066
1067
1068
1069
1070
1071
1072
1073
                        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:
1074
                                p += 1
hjk's avatar
hjk committed
1075
1076
1077
                                n += 1
                    except:
                        pass
1078

hjk's avatar
hjk committed
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
                    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):
1095
1096
                        self.put('iname="%s",' % item.iname)
                        self.put('name="%s",' % item.name)
hjk's avatar
hjk committed
1097
                        self.putItem(value)
1098
1099
1100
1101

        #
        # Watchers
        #
hjk's avatar
hjk committed
1102
        with OutputSafer(self):
1103
            if len(watchers) > 0:
hjk's avatar
hjk committed
1104
                self.put(",")
1105
1106
                for watcher in watchers.split("##"):
                    (exp, iname) = watcher.split("#")
1107
                    self.handleWatch(exp, iname)
1108

1109
        #print('data=[' + locals + sep + watchers + ']\n')
1110
1111
1112
1113


    def handleWatch(self, exp, iname):
        exp = str(exp)
1114
        escapedExp = base64.b64encode(exp);
1115
1116
1117
        #warn("HANDLING WATCH %s, INAME: '%s'" % (exp, iname))
        if exp.startswith("[") and exp.endswith("]"):
            #warn("EVAL: EXP: %s" % exp)
hjk's avatar
hjk committed
1118
            with TopLevelItem(self, iname):
1119
                self.put('iname="%s",' % iname)
1120
                self.put('wname="%s",' % escapedExp)
1121
1122
1123
                try:
                    list = eval(exp)
                    self.putValue("")
1124
                    self.putNoType()
1125
1126
1127
1128
1129
                    self.putNumChild(len(list))
                    # This is a list of expressions to evaluate
                    with Children(self, len(list)):
                        itemNumber = 0
                        for item in list:
1130
                            self.handleWatch(item, "%s.%d" % (iname, itemNumber))
1131
1132
1133
1134
                            itemNumber += 1
                except RuntimeError, error:
                    warn("EVAL: ERROR CAUGHT %s" % error)
                    self.putValue("<syntax error>")
1135
                    self.putNoType()
1136
                    self.putNumChild(0)
hjk's avatar
hjk committed
1137
                    self.put("children=[],")
1138
1139
            return

hjk's avatar
hjk committed
1140
        with TopLevelItem(self, iname):
1141
            self.put('iname="%s",' % iname)
1142
            self.put('wname="%s",' % escapedExp)
1143
            if len(exp) == 0: # The <Edit> case
1144
                self.putValue(" ")
1145
                self.putNoType()