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

45
46
47
48
49
try:
    import binascii
except:
    pass

hjk's avatar
hjk committed
50
51
52
verbosity = 0
verbosity = 1

53
54
55
56
# Some "Enums"

# Encodings
Unencoded8Bit, \
57
58
59
60
61
62
63
64
65
66
Base64Encoded8BitWithQuotes, \
Base64Encoded16BitWithQuotes, \
Base64Encoded32BitWithQuotes, \
Base64Encoded16Bit, \
Base64Encoded8Bit, \
Hex2EncodedLatin1, \
Hex4EncodedLittleEndian, \
Hex8EncodedLittleEndian, \
Hex2EncodedUtf8, \
Hex8EncodedBigEndian, \
Arvid Ephraim Picciani's avatar
Arvid Ephraim Picciani committed
67
Hex4EncodedBigEndian, \
68
Hex4EncodedLittleEndianWithoutQuotes, \
69
70
71
72
73
Hex2EncodedLocal8Bit, \
JulianDate, \
MillisecondsSinceMidnight, \
JulianDateAndMillisecondsSinceMidnight \
    = range(17)
74
75

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


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

92
93
94
95
96
97
98
99
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)"

100
def dynamicTypeName(value):
101
102
103
104
105
106
107
108
109
110
111
112
113
114
    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
115
116
    return str(value.type)

117
118
def upcast(value):
    try:
119
120
121
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

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

139
140
typeCache = {}

141
142
143
144
145
146
147
class TypeInfo:
    def __init__(self, type):
        self.size = type.sizeof
        self.reported = False

typeInfoCache = {}

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

154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
    if typestring == "void":
        type = gdb.lookup_type(typestring)
        typeCache[typestring] = type
        return type

    if typestring.find("(anon") != -1:
        # gdb doesn't like
        # '(anonymous namespace)::AddAnalysisMessageSuppressionComment'
        typeCache[typestring] = None
        return None

    try:
        type = gdb.parse_and_eval("{%s}&main" % typestring).type
        typeCache[typestring] = type
        return type
    except:
        pass

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

hjk's avatar
hjk committed
176
177
    # This part should only trigger for
    # gdb 7.1 for types with namespace separators.
178

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

    try:
        #warn("LOOKING UP '%s'" % ts)
        type = gdb.lookup_type(ts)
    except RuntimeError, error:
        #warn("LOOKING UP '%s': %s" % (ts, error))
215
216
217
218
219
        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)"))
220
221
222
223
224
225
226
227
228
        # 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)
229
        pass
230
231
232

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

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

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

282

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

297

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

    def __enter__(self):
        self.savedOutput = self.d.output
hjk's avatar
hjk committed
314
        self.d.output = []
315
316
317
318
319
320

    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
321
322
            self.savedOutput.extend(self.d.output)
            self.d.output = self.savedOutput
323
324
325
        return False


326
327
328
329
330
class NoAddress:
    def __init__(self, d):
        self.d = d

    def __enter__(self):
hjk's avatar
hjk committed
331
332
        self.savedPrintsAddress = self.d.currentPrintsAddress
        self.d.currentPrintsAddress = False
333
334

    def __exit__(self, exType, exValue, exTraceBack):
hjk's avatar
hjk committed
335
        self.d.currentPrintsAddress = self.savedPrintsAddress
336
337


338

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

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

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

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

    def __exit__(self, exType, exValue, exTraceBack):
        if self.d.passExceptions and not exType is None:
            showException("CHILDREN", exType, exValue, exTraceBack)
hjk's avatar
hjk committed
458
459
460
        if not self.d.currentMaxNumChild is None:
            if self.d.currentMaxNumChild < self.d.currentNumChild:
                self.d.put('{name="<incomplete>",value="",type="",numchild="0"},')
461
462
        self.d.currentChildType = self.savedChildType
        self.d.currentChildNumChild = self.savedChildNumChild
hjk's avatar
hjk committed
463
464
465
        self.d.currentNumChild = self.savedNumChild
        self.d.currentMaxNumChild = self.savedMaxNumChild
        self.d.currentPrintsAddress = self.savedPrintsAddress
466
467
468
469
        self.d.put('],')
        return True


470
def value(expr):
471
    value = parseAndEvaluate(expr)
472
473
474
475
476
    try:
        return int(value)
    except:
        return str(value)

hjk's avatar
hjk committed
477
def isSimpleType(typeobj):
478
    code = typeobj.code
479
480
481
482
483
    return code == BoolCode \
        or code == CharCode \
        or code == IntCode \
        or code == FloatCode \
        or code == EnumCode
hjk's avatar
hjk committed
484
485

def warn(message):
486
487
    if True or verbosity > 0:
        print "XXX: %s\n" % message.encode("latin1")
hjk's avatar
hjk committed
488
489
490
491
492
493
    pass

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

494
495
496
497
498
def checkSimpleRef(ref):
    count = ref["_q_value"]
    check(count > 0)
    check(count < 1000000)

499
def checkRef(ref):
500
501
502
503
504
505
506
507
508
509
    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)

510

hjk's avatar
hjk committed
511
#def couldBePointer(p, align):
512
#    type = lookupType("unsigned int")
hjk's avatar
hjk committed
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
#    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()

530
531
532
533
534
535
def isAccessible(p):
    try:
        long(p)
        return True
    except:
        return False
hjk's avatar
hjk committed
536
537

def isNull(p):
538
539
540
541
    # 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 ")
542
543
544
545
546
    #try:
    #    # Can fail with: "RuntimeError: Cannot access memory at address 0x5"
    #    return p.cast(lookupType("void").pointer()) == 0
    #except:
    #    return False
547
548
549
550
551
    try:
        # Can fail with: "RuntimeError: Cannot access memory at address 0x5"
        return long(p) == 0
    except:
        return False
hjk's avatar
hjk committed
552
553

movableTypes = set([
hjk's avatar
hjk committed
554
555
556
557
558
559
    "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
560
    "QXmlStreamAttribute", "QXmlStreamNamespaceDeclaration",
hjk's avatar
hjk committed
561
562
563
564
565
566
567
568
569
570
571
572
573
    "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
574
575

def checkPointerRange(p, n):
576
    for i in xrange(n):
hjk's avatar
hjk committed
577
578
579
        checkPointer(p)
        ++p

hjk's avatar
hjk committed
580
581
582
583
584
585
586
587
588
589
590
591
592
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
593
    type = stripClassTag(str(value.type))
594
    if type.find(":") >= 0:
hjk's avatar
hjk committed
595
        type = "'" + type + "'"
596
    # 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
hjk's avatar
hjk committed
597
    exp = "((class %s*)%s)->%s(%s)" % (type, value.address, func, arg)
hjk's avatar
hjk committed
598
    #warn("CALL: %s" % exp)
599
600
601
602
603
    result = None
    try:
        result = parseAndEvaluate(exp)
    except:
        pass
hjk's avatar
hjk committed
604
605
606
    #warn("  -> %s" % result)
    return result

hjk's avatar
hjk committed
607
608
609
def call(value, func, *args):
    return call2(value, func, args)

610
611
612
613
def makeValue(type, init):
    type = stripClassTag(type)
    if type.find(":") >= 0:
        type = "'" + type + "'"
hjk's avatar
hjk committed
614
    # Avoid malloc symbol clash with QVector.
615
    gdb.execute("set $d = (%s*)calloc(sizeof(%s), 1)" % (type, type))
616
617
618
619
620
621
622
    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

623
624
625
626
627
628
629
630
631
632
633
634
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


635
636
637
638
639
640
641
642
643
644
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

645
646
qqNs = None

647
def qtNamespace():
648
    # FIXME: This only works when call from inside a Qt function frame.
649
    global qqNs
650
651
    if not qqNs is None:
        return qqNs
652
    try:
653
        str = catchCliOutput("ptype QString::Null")[0]
hjk's avatar
hjk committed
654
        # The result looks like:
655
656
657
658
659
        # "type = const struct myns::QString::Null {"
        # "    <no data fields>"
        # "}"
        pos1 = str.find("struct") + 7
        pos2 = str.find("QString::Null")
660
661
662
663
        if pos1 > -1 and pos2 > -1:
            qqNs = str[pos1:pos2]
            return qqNs
        return ""
664
    except:
665
        return ""
hjk's avatar
hjk committed
666

667
668
def findFirstZero(p, maximum):
    for i in xrange(maximum):
669
670
671
        if p.dereference() == 0:
            return i
        p = p + 1
672
    return maximum + 1
673

674
def extractCharArray(p, maxsize):
675
    p = p.cast(lookupType("unsigned char").pointer())
676
    s = ""
677
678
679
680
681
682
    i = 0
    while i < maxsize:
        c = int(p.dereference())
        if c == 0:
            return s
        s += "%c" % c
683
        p += 1
684
        i += 1
685
686
687
    if p.dereference() != 0:
        s += "..."
    return s
688
689
690
691
692
693
694
695
696
697
698

def extractByteArray(value):
    d_ptr = value['d'].dereference()
    data = d_ptr['data']
    size = d_ptr['size']
    alloc = d_ptr['alloc']
    check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
    checkRef(d_ptr["ref"])
    if size > 0:
        checkAccess(data, 4)
        checkAccess(data + size) == 0
699
    return extractCharArray(data, min(100, size))
700

701
def encodeCharArray(p, maxsize, limit = -1):
702
    t = lookupType("unsigned char").pointer()
703
    p = p.cast(t)
704
705
    if limit == -1:
        limit = findFirstZero(p, maxsize)
706
    s = ""
707
708
709
710
711
712
713
714
    try:
        # gdb.Inferior is new in gdb 7.2
        inferior = gdb.inferiors()[0]
        s = binascii.hexlify(inferior.read_memory(p, limit))
    except:
        for i in xrange(limit):
            s += "%02x" % int(p.dereference())
            p += 1
715
    if limit > maxsize:
716
717
718
719
        s += "2e2e2e"
    return s

def encodeChar2Array(p, maxsize):
720
    t = lookupType("unsigned short").pointer()
721
    p = p.cast(t)
722
    limit = findFirstZero(p, maxsize)
723
724
725
726
727
728
729
730
731
    s = ""
    for i in xrange(limit):
        s += "%04x" % int(p.dereference())
        p += 1
    if i == maxsize:
        s += "2e002e002e00"
    return s

def encodeChar4Array(p, maxsize):
732
    t = lookupType("unsigned int").pointer()
733
    p = p.cast(t)
734
    limit = findFirstZero(p, maxsize)
735
736
737
738
    s = ""
    for i in xrange(limit):
        s += "%08x" % int(p.dereference())
        p += 1
739
    if i > maxsize:
740
        s += "2e0000002e0000002e000000"
741
742
    return s

743
def qByteArrayData(value):
744
745
746
747
748
    private = value['d']
    checkRef(private['ref'])
    try:
        # Qt 5. Will fail on Qt 4 due to the missing 'offset' member.
        offset = private['offset']
749
        charPointerType = lookupType('char *')
750
751
752
753
754
        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'])
755

756
def encodeByteArray(value):
757
    data, size, alloc = qByteArrayData(value)
758
759
760
761
    check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
    if size > 0:
        checkAccess(data, 4)
        checkAccess(data + size) == 0
762
    return encodeCharArray(data, 100, size)
763

764
def qQStringData(value):
765
766
767
768
769
    private = value['d']
    checkRef(private['ref'])
    try:
        # Qt 5. Will fail on Qt 4 due to the missing 'offset' member.
        offset = private['offset']
770
        ushortPointerType = lookupType('ushort *')
771
772
773
774
775
        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'])
776

777
def encodeString(value):
778
779
    data, size, alloc = qQStringData(value)

780
781
    if alloc != 0:
        check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
782
783
    if size > 0:
        checkAccess(data, 4)
hjk's avatar
hjk committed
784
        checkAccess(data + size) == 0
785
    s = ""
786
    limit = min(size, 1000)
787
788
789
    try:
        # gdb.Inferior is new in gdb 7.2
        inferior = gdb.inferiors()[0]
790
        s = binascii.hexlify(inferior.read_memory(data, 2 * limit))
791
    except:
792
        p = data
793
        for i in xrange(limit):
794
795
796
797
            val = int(p.dereference())
            s += "%02x" % (val % 256)
            s += "%02x" % (val / 256)
            p += 1
798
799
    if limit < size:
        s += "2e002e002e00"
800
801
    return s

hjk's avatar
hjk committed
802
803
def stripTypedefs(type):
    type = type.unqualified()
hjk's avatar
hjk committed
804
    while type.code == TypedefCode:
805
806
807
        type = type.strip_typedefs().unqualified()
    return type

hjk's avatar
hjk committed
808
809
def extractFields(type):
    # Insufficient, see http://sourceware.org/bugzilla/show_bug.cgi?id=10953:
hjk's avatar
hjk committed
810
    #fields = type.fields()
hjk's avatar
hjk committed
811
    # Insufficient, see http://sourceware.org/bugzilla/show_bug.cgi?id=11777:
hjk's avatar
hjk committed
812
    #fields = defsype).fields()
hjk's avatar
hjk committed
813
    # This seems to work.
hjk's avatar
hjk committed
814
    #warn("TYPE 0: %s" % type)
hjk's avatar
hjk committed
815
    type = stripTypedefs(type)
816
817
818
    fields = type.fields()
    if len(fields):
        return fields
hjk's avatar
hjk committed
819
    #warn("TYPE 1: %s" % type)
820
    # This fails for arrays. See comment in lookupType.
821
822
823
    type0 = lookupType(str(type))
    if not type0 is None:
        type = type0
hjk's avatar
hjk committed
824
    if type.code == FunctionCode:
825
        return []
hjk's avatar
hjk committed
826
827
    #warn("TYPE 2: %s" % type)
    fields = type.fields()
hjk's avatar
hjk committed
828
    #warn("FIELDS: %s" % fields)
hjk's avatar
hjk committed
829
830
    return fields

hjk's avatar
hjk committed
831
832
#######################################################################
#
833
# LocalItem
hjk's avatar
hjk committed
834
835
836
#
#######################################################################

837
# Contains iname, name, and value.
hjk's avatar
hjk committed
838
class LocalItem:
839
    pass
hjk's avatar
hjk committed
840

841
842
843
844
845
846
#######################################################################
#
# SetupCommand
#
#######################################################################

847
# This is a cache mapping from 'type name' to 'display alternatives'.
848
qqFormats = {}
849
850

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

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

856
857
858
859
860
# This is a cache of the namespace of the currently used Qt version.
# FIXME: This is not available on 'bbsetup' time, only at 'bb' time.

# This is a cache of typenames->bool saying whether we are QObject
# derived.
hjk's avatar
hjk committed
861
qqQObjectCache = {}
862

863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
# 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

886
def bbsetup(args):
hjk's avatar
hjk committed
887
888
    typeInfoCache = {}
    typeCache = {}
889
890
891
892
893
    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
894
            qqFormats[name] = qqFormats.get(name, "")
895
896
897
898
899
900
901
902
        elif key.startswith("qform__"):
            name = key[7:]
            formats = ""
            try:
                formats = value()
            except:
                pass
            qqFormats[name] = formats
903
904
905
906
907
908
        elif key.startswith("qedit__"):
            name = key[7:]
            try:
                qqEditable[name] = value
            except:
                pass
909
    result = "dumpers=["
910
    #qqNs = qtNamespace() # This is too early
911
    for key, value in qqFormats.items():
912
913
914
915
        if qqEditable.has_key(key):
            result += '{type="%s",formats="%s",editable="true"},' % (key, value)
        else:
            result += '{type="%s",formats="%s"},' % (key, value)
916
917
    result += ']'
    #result += ',namespace="%s"' % qqNs
918
    result += ',hasInferiorThreadList="%s"' % int(hasInferiorThreadList())
919
920
    return result

921
922
registerCommand("bbsetup", bbsetup)

923

924
925
926
927
928
929
930
#######################################################################
#
# Edit Command
#
#######################################################################

def bbedit(args):
931
932
    (type, expr, value) = args.split(",")
    type = base64.b16decode(type, True)
933
    ns = qtNamespace()
934
935
    if type.startswith(ns):
        type = type[len(ns):]
936
    type = type.replace("::", "__")
937
938
939
940
941
942
    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))
943
944
    if qqEditable.has_key(type):
        qqEditable[type](expr, value)
945
946
    else:
        gdb.execute("set (%s)=%s" % (expr, value))
947

948
registerCommand("bbedit", bbedit)
949
950


hjk's avatar
hjk committed
951
952
#######################################################################
#
953
# Frame Command
hjk's avatar
hjk committed
954
955
956
#
#######################################################################

957
def bb(args):
hjk's avatar
hjk committed
958
    output = 'data=[' + "".join(Dumper(args).output) + '],typeinfo=['
959
960
961
962
963
964
965
    for typeName, typeInfo in typeInfoCache.iteritems():
        if not typeInfo.reported:
            output += '{name="' + base64.b64encode(typeName)
            output += '",size="' + str(typeInfo.size) + '"},'
            typeInfo.reported = True
    output += ']';
    return output
hjk's avatar
hjk committed
966

967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
registerCommand("bb", bb)

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

registerCommand("p1", p1)

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

registerCommand("p2", p2)

hjk's avatar
hjk committed
985
986
987
988
989
990
991

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

992

hjk's avatar
hjk committed
993
class Dumper:
994
    def __init__(self, args):
hjk's avatar
hjk committed
995
        self.output = []
hjk's avatar
hjk committed
996
997
        self.currentIName = ""
        self.currentPrintsAddress = True
998
999
        self.currentChildType = ""
        self.currentChildNumChild = -1
hjk's avatar
hjk committed
1000
1001
        self.currentMaxNumChild = -1
        self.currentNumChild = -1
1002
1003
1004
1005
1006
        self.currentValue = None
        self.currentValuePriority = -100
        self.currentValueEncoding = None
        self.currentType = None
        self.currentTypePriority = -100
1007
1008
1009
1010
1011
1012
1013
1014
        self.typeformats = {}
        self.formats = {}
        self.expandedINames = ""

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

1015
        resultVarName = ""
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
        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)

1041
        self.useDynamicType = "dyntype" in options
1042
1043
1044
        self.useFancy = "fancy" in options
        self.passExceptions = "pe" in options
        self.autoDerefPointers = "autoderef" in options
1045
        self.partialUpdate = "partial" in options
1046
1047
        self.tooltipOnly = "tooltiponly" in options
        self.noLocals = "nolocals" in options
1048
        self.ns = qtNamespace()
1049
1050
1051
1052

        #warn("NAMESPACE: '%s'" % self.ns)
        #warn("VARIABLES: %s" % varList)
        #warn("EXPANDED INAMES: %s" % self.expandedINames)
1053
        #warn("WATCHERS: %s" % watchers)
1054
1055
        #warn("PARTIAL: %s" % self.partialUpdate)
        #warn("NO LOCALS: %s" % self.noLocals)
1056
1057
1058
1059
1060
        module = sys.modules[__name__]

        #
        # Locals
        #
1061
        locals = []
1062
        fullUpdateNeeded = True
1063
        if self.partialUpdate and len(varList) == 1 and not self.tooltipOnly:
1064
1065
1066
1067
1068
1069
1070
1071
            #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
1072
1073
1074
                item = LocalItem()
                item.iname = "local." + name
                item.name = name
1075
1076
1077
1078
1079
1080
1081
1082
                item.value = frame.read_var(name)
                locals = [item]
                #warn("PARTIAL LOCALS: %s" % locals)
                fullUpdateNeeded = False
            except:
                pass
            varList = []

1083
        if fullUpdateNeeded and not self.tooltipOnly and not self.noLocals:
1084
            locals = listOfLocals(varList)
1085

1086
1087
1088
1089
1090
1091
        if "autotest" in options:
            for item in listOfLocals([]):
                self.expandedINames.add(item.iname)
                self.expandedINames.discard("")
                warn("EXPANDED: %s" % self.expandedINames)

1092
1093
1094
        # Take care of the return value of the last function call.
        if len(resultVarName) > 0:
            try:
hjk's avatar
hjk committed
1095
1096
1097
1098
1099
                item = LocalItem()
                item.name = resultVarName
                item.iname = "return." + resultVarName
                item.value = parseAndEvaluate(resultVarName)
                locals.append(item)
1100
1101
1102
1103
1104
            except:
                # Don't bother. It's only supplementary information anyway.
                pass

        for item in locals:
hjk's avatar
hjk committed
1105
            value = upcast(item.value)
hjk's avatar
hjk committed
1106
            with OutputSafer(self):
hjk's avatar
hjk committed
1107
1108
1109
1110
1111
                self.anonNumber = -1

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

1112
                # Special handling for char** argv.
1113
                if type.code == PointerCode \
hjk's avatar
hjk committed
1114
1115
1116
1117
1118
1119
1120
1121
                        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:
1122
                                p += 1
hjk's avatar
hjk committed
1123
1124
1125
                                n += 1
                    except:
                        pass
1126

hjk's avatar
hjk committed
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
                    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):
1143
1144
                        self.put('iname="%s",' % item.iname)
                        self.put('name="%s",' % item.name)
hjk's avatar
hjk committed
1145
                        self.putItem(value)
1146
1147
1148
1149

        #
        # Watchers
        #
hjk's avatar
hjk committed
1150
        with OutputSafer(self):
1151
            if len(watchers) > 0:
hjk's avatar
hjk committed
1152
                self.put(",")
1153
1154
                for watcher in watchers.split("##"):
                    (exp, iname) = watcher.split("#")