dumper.py 64.4 KB
Newer Older
1
from __future__ import with_statement
hjk's avatar
hjk committed
2
3
4

import sys
import gdb
5
import base64
6
import __builtin__
7
import os
8

9
10
11
12
13
14
# Fails on Windows.
try:
    import curses.ascii
    def printableChar(ucs):
        return select(curses.ascii.isprint(ucs), ucs, '?')
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
23
try:
    import tempfile
24
25
26
#   Test if 2.6 is used (Windows), trigger exception and default
#   to 2nd version.
    tempfile.NamedTemporaryFile(prefix="gdbpy_",delete=True)
27
    def createTempFile():
28
        file = tempfile.NamedTemporaryFile(prefix="gdbpy_",delete=False)
29
        file.close()
30
        return file.name, file
31

32
33
except:
    def createTempFile():
34
35
36
37
        global tempFileCounter
        tempFileCounter += 1
        fileName = "%s/gdbpy_tmp_%d_%d" % (tempfile.gettempdir(), os.getpid(), tempFileCounter)
        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
57
# Some "Enums"

# Encodings

Unencoded8Bit, \
58
59
60
61
62
63
64
65
66
67
Base64Encoded8BitWithQuotes, \
Base64Encoded16BitWithQuotes, \
Base64Encoded32BitWithQuotes, \
Base64Encoded16Bit, \
Base64Encoded8Bit, \
Hex2EncodedLatin1, \
Hex4EncodedLittleEndian, \
Hex8EncodedLittleEndian, \
Hex2EncodedUtf8, \
Hex8EncodedBigEndian, \
Arvid Ephraim Picciani's avatar
Arvid Ephraim Picciani committed
68
69
70
Hex4EncodedBigEndian, \
Hex4EncodedLittleEndianWithoutQuotes \
    = range(13)
71
72

# Display modes
73
74
75
76
77
78
StopDisplay, \
DisplayImage1, \
DisplayString, \
DisplayImage, \
DisplayProcess \
    = range(5)
79
80


81
82
83
84
85
def select(condition, if_expr, else_expr):
    if condition:
        return if_expr
    return else_expr

86
87
88
89
90
def qmin(n, m):
    if n < m:
        return n
    return m

hjk's avatar
hjk committed
91
def isGoodGdb():
92
93
    #return gdb.VERSION.startswith("6.8.50.2009") \
    #   and gdb.VERSION != "6.8.50.20090630-cvs"
94
    return 'parse_and_eval' in __builtin__.dir(gdb)
hjk's avatar
hjk committed
95

96
97
98
99
100
101
102
def hasInferiorThreadList():
    try:
        a= gdb.inferiors()[0].threads()
        return True
    except:
        return False

103
104
105
106
typeCache = {}

def lookupType(typestring):
    type = typeCache.get(typestring)
hjk's avatar
hjk committed
107
    #warn("LOOKUP 1: %s -> %s" % (typestring, type))
108
    if type is None:
hjk's avatar
hjk committed
109
110
111
112
113
114
115
116
117
118
119
        ts = typestring
        while True:
            #WARN("ts: '%s'" % ts)
            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:]
120
121
            elif ts.startswith("enum "):
                ts = ts[5:]
hjk's avatar
hjk committed
122
123
124
125
126
127
128
129
130
            elif ts.endswith("const"):
                ts = ts[-5:]
            elif ts.endswith("volatile"):
                ts = ts[-8:]
            else:
                break
        try:
            #warn("LOOKING UP '%s'" % ts)
            type = gdb.lookup_type(ts)
131
        except RuntimeError, error:
132
            #warn("LOOKING UP '%s': %s" % (ts, error))
133
            # See http://sourceware.org/bugzilla/show_bug.cgi?id=11912
134
            exp = "(class '%s'*)0" % ts
135
136
137
138
139
            try:
                type = parseAndEvaluate(exp).type.target()
            except:
                # Can throw "RuntimeError: No type named class Foo."
                pass
140
        except:
hjk's avatar
hjk committed
141
142
143
144
145
            #warn("LOOKING UP '%s' FAILED" % ts)
            pass
        #warn("  RESULT: '%s'" % type)
        #if not type is None:
        #    warn("  FIELDS: '%s'" % type.fields())
146
        typeCache[typestring] = type
hjk's avatar
hjk committed
147
148
149
150
151
    if type is None and typestring.endswith('*'):
        type = lookupType(typestring[0:-1])
        if not type is None:
            type = type.pointer()
            typeCache[typestring] = type
152
153
154
155
    if type is None:
        # could be gdb.lookup_type("char[3]") generating
        # "RuntimeError: No type named char[3]"
        pass
156
157
    return type

158
def cleanAddress(addr):
159
160
    if addr is None:
        return "<no address>"
161
162
    # We cannot use str(addr) as it yields rubbish for char pointers
    # that might trigger Unicode encoding errors.
163
    return addr.cast(lookupType("void").pointer())
164

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
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):
195
196
197
198
199
200
    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<...>"
201
        return lookupType(extractTemplateArgument(type.strip_typedefs(), position))
202

hjk's avatar
hjk committed
203
204
205
206
# Workaround for gdb < 7.1
def numericTemplateArgument(type, position):
    try:
        return int(type.template_argument(position))
207
    except RuntimeError, error:
hjk's avatar
hjk committed
208
209
210
211
        # ": No type named 30."
        msg = str(error)
        return int(msg[14:-1])

212
def parseAndEvaluate(exp):
hjk's avatar
hjk committed
213
    if isGoodGdb():
214
215
216
217
        return gdb.parse_and_eval(exp)
    # Work around non-existing gdb.parse_and_eval as in released 7.0
    gdb.execute("set logging redirect on")
    gdb.execute("set logging on")
218
219
220
221
    try:
        gdb.execute("print %s" % exp)
    except:
        gdb.execute("set logging off")
222
        gdb.execute("set logging redirect off")
223
        return None
224
    gdb.execute("set logging off")
225
    gdb.execute("set logging redirect off")
226
227
    return gdb.history(0)

228

229
def catchCliOutput(command):
230
231
232
233
    try:
        return gdb.execute(command, to_string=True)
    except:
        pass
234
    filename, file = createTempFile()
235
236
237
238
239
    gdb.execute("set logging off")
    gdb.execute("set logging redirect off")
    gdb.execute("set logging file %s" % filename)
    gdb.execute("set logging redirect on")
    gdb.execute("set logging on")
240
241
242
243
244
245
246
247
    msg = ""
    try:
        gdb.execute(command)
    except RuntimeError, error:
        # For the first phase of core file loading this yield
        # "No symbol table is loaded.  Use the \"file\" command."
        msg = str(error)
    except:
hjk's avatar
hjk committed
248
        msg = "Unknown error"
249
250
    gdb.execute("set logging off")
    gdb.execute("set logging redirect off")
251
    if len(msg):
hjk's avatar
hjk committed
252
253
254
        # Having that might confuse result handlers in the gdbengine.
        #warn("CLI ERROR: %s " % msg)
        return "CLI ERROR: %s " % msg
255
    temp = open(filename, "r")
256
    lines = []
257
    for line in temp:
258
        lines.append(line)
259
260
    temp.close()
    removeTempFile(filename, file)
261
262
263
    return lines


264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
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:
    def __init__(self, d, pre = "", post = ""):
        self.d = d
        self.pre = pre
        self.post = post

    def __enter__(self):
        self.d.put(self.pre)
        self.savedOutput = self.d.output
        self.d.output = ""

    def __exit__(self, exType, exValue, exTraceBack):
        self.d.put(self.post)
        if self.d.passExceptions and not exType is None:
            showException("OUTPUTSAFER", exType, exValue, exTraceBack)
            self.d.output = self.savedOutput
        else:
            self.d.output = self.savedOutput + self.d.output
        return False


class SubItem:
    def __init__(self, d):
        self.d = d

    def __enter__(self):
        self.d.put('{')
        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
306
        self.d.currentValue = "<not accessible>"
307
        self.d.currentValuePriority = -100
308
309
        self.d.currentValueEncoding = None
        self.d.currentType = ""
310
        self.d.currentTypePriority = -100
311
312
313
314
315
316

    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)
317
        try:
318
            #warn("TYPE VALUE: %s" % self.d.currentValue)
319
320
321
322
323
            type = stripClassTag(str(self.d.currentType))
            #warn("TYPE: '%s'  DEFAULT: '%s'" % (type, self.d.currentChildType))
            if len(type) > 0 and type != self.d.currentChildType:
                self.d.put('type="%s",' % type) # str(type.unqualified()) ?
            if not self.d.currentValueEncoding is None:
324
                self.d.put('valueencoded="%d",' % self.d.currentValueEncoding)
325
            if not self.d.currentValue is None:
326
                self.d.put('value="%s",' % self.d.currentValue)
327
328
        except:
            pass
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
        self.d.put('},')
        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


class Children:
    def __init__(self, d, numChild = 1, childType = None, childNumChild = None):
        self.d = d
        self.numChild = numChild
        self.childType = childType
        self.childNumChild = childNumChild
        #warn("CHILDREN: %s %s %s" % (numChild, childType, childNumChild))

    def __enter__(self):
        childType = ""
        childNumChild = -1
        if type(self.numChild) is list:
            numChild = self.numChild[0]
            maxNumChild = self.numChild[1]
        else:
            numChild = self.numChild
            maxNumChild = self.numChild
        if numChild == 0:
            self.childType = None
        if not self.childType is None:
            childType = stripClassTag(str(self.childType))
            self.d.put('childtype="%s",' % childType)
            if isSimpleType(self.childType) or isStringType(self.d, self.childType):
                self.d.put('childnumchild="0",')
                childNumChild = 0
            elif self.childType.code == gdb.TYPE_CODE_PTR:
                self.d.put('childnumchild="1",')
                childNumChild = 1
        if not self.childNumChild is None:
            self.d.put('childnumchild="%s",' % self.childNumChild)
            childNumChild = self.childNumChild
        self.savedChildType = self.d.currentChildType
        self.savedChildNumChild = self.d.currentChildNumChild
        self.savedNumChilds = self.d.currentNumChilds
        self.savedMaxNumChilds = self.d.currentNumChilds
        self.d.currentChildType = childType
        self.d.currentChildNumChild = childNumChild
        self.d.currentNumChilds = numChild
        self.d.currentMaxNumChilds = maxNumChild
        self.d.put("children=[")

    def __exit__(self, exType, exValue, exTraceBack):
        if self.d.passExceptions and not exType is None:
            showException("CHILDREN", exType, exValue, exTraceBack)
        if self.d.currentMaxNumChilds < self.d.currentNumChilds:
hjk's avatar
hjk committed
383
            self.d.putEllipsis()
384
385
386
387
388
389
390
391
        self.d.currentChildType = self.savedChildType
        self.d.currentChildNumChild = self.savedChildNumChild
        self.d.currentNumChilds = self.savedNumChilds
        self.d.currentMaxNumChilds = self.savedMaxNumChilds
        self.d.put('],')
        return True


392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
class Breakpoint:
    def __init__(self):
        self.number = None
        self.filename = None
        self.linenumber = None
        self.address = []
        self.function = None
        self.fullname = None
        self.condition = None
        self.times = None

def listOfBreakpoints(d):
    # [bkpt={number="1",type="breakpoint",disp="keep",enabled="y",
    #addr="0x0804da6d",func="testHidden()",file="../app.cpp",
    #fullname="...",line="1292",times="1",original-location="\"app.cpp\":1292"},
    # Num     Type           Disp Enb Address    What\n"
    #1       breakpoint     keep y   0x0804da6d in testHidden() at app.cpp:1292
    #\tbreakpoint already hit 1 time
    #2       breakpoint     keep y   0x080564d3 in espace::..doit(int) at ../app.cpp:1210\n"
    #3       breakpoint     keep y   <PENDING>  \"plugin.cpp\":38\n"
    #4       breakpoint     keep y   <MULTIPLE> \n"
    #4.1                         y     0x08056673 in Foo at ../app.cpp:126\n"
    #4.2                         y     0x0805678b in Foo at ../app.cpp:126\n"
    #5       hw watchpoint  keep y              &main\n"
416
    #6       breakpoint     keep y   0xb6cf18e5 <__cxa_throw+5>\n"
417
    lines = catchCliOutput("info break")
418
419
420
421

    lines.reverse()
    bp = Breakpoint()
    for line in lines:
422
423
        if len(line) == 0 or line.startswith(" "):
            continue
424
425
426
427
428
429
430
431
432
433
        if line[0] < '0' or line[0] > '9':
            continue
        if line.startswith("\tstop only if "):
            bp.condition = line[14:]
            continue
        if line.startswith("\tbreakpoint already hit "):
            bp.times = line[24:]
            continue
        number = line[0:5]
        pos0x = line.find(" 0x")
434
        posin = line.find(" in ", pos0x)
435
        poslt = line.find(" <", pos0x)
436
        posat = line.find(" at ", posin)
437
        poscol = line.find(":", posat)
438
439
440
441
442
443
        if pos0x != -1:
            if pos0x < posin:
                bp.address.append(line[pos0x + 1 : posin])
            elif pos0x < poslt:
                bp.address.append(line[pos0x + 1 : poslt])
                bp.function = line[poslt + 2 : line.find('>', poslt)]
444
445
446
        # Take "no address" as indication that the bp is pending.
        #if line.find("<PENDING>") >= 0:
        #    bp.address.append("<PENDING>")
447
448
449
450
451
452
453
454
455
456
457
458
        if posin < posat and posin != -1:
            bp.function = line[posin + 4 : posat]
        if posat < poscol and poscol != -1:
            bp.filename = line[posat + 4 : poscol]
        if poscol != -1:
            bp.linenumber = line[poscol + 1 : -1]

        if '.' in number: # Part of multiple breakpoint.
            continue

        # A breakpoint of its own
        bp.number = int(number)
hjk's avatar
hjk committed
459
460
461
462
        d.put('bkpt={number="%s",' % bp.number)
        d.put('type="breakpoint",')
        d.put('disp="keep",')
        d.put('enabled="y",')
463
        for address in bp.address:
hjk's avatar
hjk committed
464
            d.put('addr="%s",' % address)
465
        if not bp.function is None:
hjk's avatar
hjk committed
466
            d.put('func="%s",' % bp.function)
467
        if not bp.filename is None:
hjk's avatar
hjk committed
468
            d.put('file="%s",' % bp.filename)
469
        if not bp.fullname is None:
hjk's avatar
hjk committed
470
            d.put('fullname="%s",' % bp.fullname)
471
        if not bp.linenumber is None:
hjk's avatar
hjk committed
472
            d.put('line="%s",' % bp.linenumber)
473
        if not bp.condition is None:
hjk's avatar
hjk committed
474
            d.put('cond="%s",' % bp.condition)
475
        if not bp.fullname is None:
hjk's avatar
hjk committed
476
            d.put('fullname="%s",' % bt.fullname)
477
        if not bp.times is None:
hjk's avatar
hjk committed
478
            d.put('times="1",' % bp.times)
479
        #d.put('original-location="-"')
hjk's avatar
hjk committed
480
        d.put('},')
481
482
483
        bp = Breakpoint()


484
485
486
487
488
489
490
491
492
493
494
# Creates a list of field names of an anon union or struct
def listOfFields(type):
    fields = []
    for field in type.fields():
        if len(field.name) > 0:
            fields += field.name
        else:
            fields += listOfFields(field.type)
    return fields


495
def listOfLocals(varList):
496
497
498
    try:
        frame = gdb.selected_frame()
        #warn("FRAME %s: " % frame)
499
500
501
502
503
    except RuntimeError, error:
        warn("FRAME NOT ACCESSIBLE: %s" % error)
        return []
    except:
        warn("FRAME NOT ACCESSIBLE FOR UNKNOWN REASONS")
hjk's avatar
hjk committed
504
        return []
505

506
    # gdb-6.8-symbianelf fails here
507
    hasBlock = 'block' in __builtin__.dir(frame)
508

509
    items = []
hjk's avatar
hjk committed
510
511
    #warn("HAS BLOCK: %s" % hasBlock)
    #warn("IS GOOD GDB: %s" % isGoodGdb())
512
    if hasBlock and isGoodGdb():
513
        #warn("IS GOOD: %s " % varList)
514
515
516
        try:
            block = frame.block()
            #warn("BLOCK: %s " % block)
517
518
519
        except RuntimeError, error:
            warn("FRAME NOT ACCESSIBLE: %s" % error)
            return items
520
        except:
521
            warn("BLOCK NOT ACCESSIBLE FOR UNKNOWN REASONS")
522
523
            return items

524
        shadowed = {}
525
526
527
528
529
530
        while True:
            if block is None:
                warn("UNEXPECTED 'None' BLOCK")
                break
            for symbol in block:
                name = symbol.print_name
531

532
533
534
535
536
537
538
                if name == "__in_chrg":
                    continue

                # "NotImplementedError: Symbol type not yet supported in
                # Python scripts."
                #warn("SYMBOL %s: " % symbol.value)
                #warn("SYMBOL %s  (%s): " % (symbol, name))
539
540
541
542
543
544
545
546
547
                if name in shadowed:
                    level = shadowed[name]
                    name1 = "%s <shadowed %s>" % (name, level)
                    shadowed[name] = level + 1
                else:
                    name1 = name
                    shadowed[name] = 1
                #warn("SYMBOL %s  (%s, %s)): " % (symbol, name, symbol.name))
                item = Item(0, "local", name1, name1)
548
                try:
549
                    item.value = frame.read_var(name, block)  # this is a gdb value
hjk's avatar
hjk committed
550
                except:
551
552
553
554
555
556
557
558
559
                    try:
                        item.value = frame.read_var(name)  # this is a gdb value
                    except:
                        # RuntimeError: happens for
                        #     void foo() { std::string s; std::wstring w; }
                        # ValueError: happens for (as of 2010/11/4)
                        #     a local struct as found e.g. in
                        #     gcc sources in gcc.c, int execute()
                        continue
560
561
562
563
564
565
566
567
568
                #warn("ITEM %s: " % item.value)
                items.append(item)
            # The outermost block in a function has the function member
            # FIXME: check whether this is guaranteed.
            if not block.function is None:
                break

            block = block.superblock
    else:
hjk's avatar
hjk committed
569
        # Assuming gdb 7.0 release or 6.8-symbianelf.
570
        filename, file = createTempFile()
571
        #warn("VARLIST: %s " % varList)
572
        #warn("FILENAME: %s " % filename)
hjk's avatar
hjk committed
573
574
        gdb.execute("set logging off")
        gdb.execute("set logging redirect off")
575
576
577
        gdb.execute("set logging file %s" % filename)
        gdb.execute("set logging redirect on")
        gdb.execute("set logging on")
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
        try:
            gdb.execute("info args")
            # We cannot use "info locals" as at least 6.8-symbianelf
            # aborts as soon as we hit unreadable memory.
            # gdb.execute("interpreter mi '-stack-list-locals 0'")
            # results in &"Recursive internal problem.\n", so we have
            # the frontend pass us the list of locals.

            # There are two cases, either varList is empty, so we have
            # to fetch the list here, or it is not empty with the
            # first entry being a dummy.
            if len(varList) == 0:
                gdb.execute("info locals")
            else:
                varList = varList[1:]
        except:
            pass
595
596
        gdb.execute("set logging off")
        gdb.execute("set logging redirect off")
597

598
599
600
601
602
603
604
605
606
607
608
609
610
        try:
            temp = open(filename, "r")
            for line in temp:
                if len(line) == 0 or line.startswith(" "):
                    continue
                # The function parameters
                pos = line.find(" = ")
                if pos < 0:
                    continue
                varList.append(line[0:pos])
            temp.close()
        except:
            pass
611
        removeTempFile(filename, file)
612
613
614
        #warn("VARLIST: %s " % varList)
        for name in varList:
            #warn("NAME %s " % name)
615
616
617
618
            item = Item(0, "local", name, name)
            try:
                item.value = frame.read_var(name)  # this is a gdb value
            except RuntimeError:
619
620
                pass
                #continue
621
622
623
624
625
            except:
                # Something breaking the list, like intermediate gdb warnings
                # like 'Warning: can't find linker symbol for virtual table for
                # `std::less<char const*>' value\n\nwarning:  found
                # `myns::QHashData::shared_null' instead [...]
626
                # that break subsequent parsing. Chicken out and take the
627
628
                # next "usable" line.
                continue
629
630
631
632
633
            items.append(item)

    return items


634
def value(expr):
635
    value = parseAndEvaluate(expr)
636
637
638
639
640
    try:
        return int(value)
    except:
        return str(value)

hjk's avatar
hjk committed
641
def isSimpleType(typeobj):
642
643
644
645
646
    code = typeobj.code
    return code == gdb.TYPE_CODE_BOOL \
        or code == gdb.TYPE_CODE_CHAR \
        or code == gdb.TYPE_CODE_INT \
        or code == gdb.TYPE_CODE_FLT \
647
        or code == gdb.TYPE_CODE_ENUM
648

hjk's avatar
hjk committed
649
650
651
652
653
654
655
656
657
def isStringType(d, typeobj):
    type = str(typeobj)
    return type == d.ns + "QString" \
        or type == d.ns + "QByteArray" \
        or type == "std::string" \
        or type == "std::wstring" \
        or type == "wstring"

def warn(message):
658
659
    if True or verbosity > 0:
        print "XXX: %s\n" % message.encode("latin1")
hjk's avatar
hjk committed
660
661
662
663
664
665
    pass

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

666
667
668
669
670
def checkRef(ref):
    count = ref["_q_value"]
    check(count > 0)
    check(count < 1000000) # assume there aren't a million references to any object

hjk's avatar
hjk committed
671
#def couldBePointer(p, align):
672
#    type = lookupType("unsigned int")
hjk's avatar
hjk committed
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
#    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()


def isNull(p):
692
693
694
695
    # 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 ")
696
697
698
699
700
701
    #try:
    #    # Can fail with: "RuntimeError: Cannot access memory at address 0x5"
    #    return p.cast(lookupType("void").pointer()) == 0
    #except:
    #    return False
    return long(p) == 0
hjk's avatar
hjk committed
702
703
704
705
706
707
708
709

movableTypes = set([
    "QBrush", "QBitArray", "QByteArray",
    "QCustomTypeInfo", "QChar",
    "QDate", "QDateTime",
    "QFileInfo", "QFixed", "QFixedPoint", "QFixedSize",
    "QHashDummyValue",
    "QIcon", "QImage",
hjk's avatar
hjk committed
710
    "QLine", "QLineF", "QLatin1Char", "QLocale",
hjk's avatar
hjk committed
711
712
713
714
715
716
717
718
719
720
721
722
723
724
    "QMatrix", "QModelIndex",
    "QPoint", "QPointF", "QPen", "QPersistentModelIndex",
    "QResourceRoot", "QRect", "QRectF", "QRegExp",
    "QSize", "QSizeF", "QString",
    "QTime", "QTextBlock",
    "QUrl",
    "QVariant",
    "QXmlStreamAttribute", "QXmlStreamNamespaceDeclaration",
    "QXmlStreamNotationDeclaration", "QXmlStreamEntityDeclaration"])


def stripClassTag(type):
    if type.startswith("class "):
        return type[6:]
hjk's avatar
hjk committed
725
    if type.startswith("struct "):
726
        return type[7:]
hjk's avatar
hjk committed
727
    if type.startswith("const "):
hjk's avatar
hjk committed
728
        return type[6:]
hjk's avatar
hjk committed
729
    if type.startswith("volatile "):
hjk's avatar
hjk committed
730
        return type[9:]
hjk's avatar
hjk committed
731
732
733
    return type

def checkPointerRange(p, n):
734
    for i in xrange(n):
hjk's avatar
hjk committed
735
736
737
        checkPointer(p)
        ++p

hjk's avatar
hjk committed
738
739
740
741
742
743
744
745
746
747
748
749
750
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
751
    type = stripClassTag(str(value.type))
752
    if type.find(":") >= 0:
hjk's avatar
hjk committed
753
        type = "'" + type + "'"
754
    # 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
hjk's avatar
hjk committed
755
    exp = "((class %s*)%s)->%s(%s)" % (type, value.address, func, arg)
hjk's avatar
hjk committed
756
    #warn("CALL: %s" % exp)
757
758
759
760
761
    result = None
    try:
        result = parseAndEvaluate(exp)
    except:
        pass
hjk's avatar
hjk committed
762
763
764
    #warn("  -> %s" % result)
    return result

hjk's avatar
hjk committed
765
766
767
def call(value, func, *args):
    return call2(value, func, args)

768
769
770
771
def makeValue(type, init):
    type = stripClassTag(type)
    if type.find(":") >= 0:
        type = "'" + type + "'"
772
773
    # Avoid malloc symbol clash with QVector
    gdb.execute("set $d = (%s*)calloc(sizeof(%s), 1)" % (type, type))
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
    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

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

791
792
def qtNamespace():
    try:
793
        str = catchCliOutput("ptype QString::Null")[0]
hjk's avatar
hjk committed
794
        # The result looks like:
795
796
797
798
799
800
801
        # "type = const struct myns::QString::Null {"
        # "    <no data fields>"
        # "}"
        pos1 = str.find("struct") + 7
        pos2 = str.find("QString::Null")
        return str[pos1:pos2]
    except:
802
        return ""
hjk's avatar
hjk committed
803

804
805
806
807
808
809
810
def findFirstZero(p, max):
    for i in xrange(max):
        if p.dereference() == 0:
            return i
        p = p + 1
    return -1

811
def extractCharArray(p, maxsize):
812
    t = lookupType("unsigned char").pointer()
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
    p = p.cast(t)
    i = findFirstZero(p, maxsize)
    limit = select(i < 0, maxsize, i)
    s = ""
    for i in xrange(limit):
        s += "%c" % int(p.dereference())
        p += 1
    if i == maxsize:
        s += "..."
    return s

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
834
    return extractCharArray(data, 1000, size)
835

836
def encodeCharArray(p, maxsize, size = -1):
837
    t = lookupType("unsigned char").pointer()
838
    p = p.cast(t)
839
    if size == -1:
840
841
842
843
844
845
        size = findFirstZero(p, maxsize)
    if size == -1:
        size = maxsize
    limit = size
    if size > maxsize:
        limit = maxsize
846
    s = ""
847
848
849
850
851
852
853
854
855
    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
    if maxsize < size:
856
857
858
859
        s += "2e2e2e"
    return s

def encodeChar2Array(p, maxsize):
860
    t = lookupType("unsigned short").pointer()
861
862
863
864
865
866
867
868
869
870
871
872
    p = p.cast(t)
    i = findFirstZero(p, maxsize)
    limit = select(i < 0, maxsize, i)
    s = ""
    for i in xrange(limit):
        s += "%04x" % int(p.dereference())
        p += 1
    if i == maxsize:
        s += "2e002e002e00"
    return s

def encodeChar4Array(p, maxsize):
873
    t = lookupType("unsigned int").pointer()
874
875
876
877
878
879
880
881
882
    p = p.cast(t)
    i = findFirstZero(p, maxsize)
    limit = select(i < 0, maxsize, i)
    s = ""
    for i in xrange(limit):
        s += "%08x" % int(p.dereference())
        p += 1
    if i == maxsize:
        s += "2e0000002e0000002e000000"
883
884
    return s

885
886
887
888
889
890
def encodeByteArray(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)
891
    checkRef(d_ptr["ref"])
892
893
894
    if size > 0:
        checkAccess(data, 4)
        checkAccess(data + size) == 0
895
    return encodeCharArray(data, 1000, size)
896
897
898
899
900
901
902
903
904

def encodeString(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)
    if size > 0:
        checkAccess(data, 4)
hjk's avatar
hjk committed
905
        checkAccess(data + size) == 0
906
    checkRef(d_ptr["ref"])
907
908
    p = gdb.Value(d_ptr["data"])
    s = ""
909
910
911
912
913
914
915
916
917
918
    try:
        # gdb.Inferior is new in gdb 7.2
        inferior = gdb.inferiors()[0]
        s = binascii.hexlify(inferior.read_memory(p, 2 * int(size)))
    except:
        for i in xrange(size):
            val = int(p.dereference())
            s += "%02x" % (val % 256)
            s += "%02x" % (val / 256)
            p += 1
919
920
    return s

hjk's avatar
hjk committed
921
922
def stripTypedefs(type):
    type = type.unqualified()
923
924
925
926
    while type.code == gdb.TYPE_CODE_TYPEDEF:
        type = type.strip_typedefs().unqualified()
    return type

hjk's avatar
hjk committed
927
928
929
930
931
932
def extractFields(type):
    # Insufficient, see http://sourceware.org/bugzilla/show_bug.cgi?id=10953:
    #fields = value.type.fields()
    # Insufficient, see http://sourceware.org/bugzilla/show_bug.cgi?id=11777:
    #fields = stripTypedefs(value.type).fields()
    # This seems to work.
hjk's avatar
hjk committed
933
    #warn("TYPE 0: %s" % type)
hjk's avatar
hjk committed
934
    type = stripTypedefs(type)
935
936
937
    fields = type.fields()
    if len(fields):
        return fields
hjk's avatar
hjk committed
938
    #warn("TYPE 1: %s" % type)
939
    # This fails for arrays. See comment in lookupType.
940
941
942
    type0 = lookupType(str(type))
    if not type0 is None:
        type = type0
943
944
    if type.code == gdb.TYPE_CODE_FUNC:
        return []
hjk's avatar
hjk committed
945
946
    #warn("TYPE 2: %s" % type)
    fields = type.fields()
hjk's avatar
hjk committed
947
    #warn("FIELDS: %s" % fields)
hjk's avatar
hjk committed
948
949
    return fields

hjk's avatar
hjk committed
950
951
952
953
954
955
956
#######################################################################
#
# Item
#
#######################################################################

class Item:
957
    def __init__(self, value, parentiname, iname = None, name = None):
hjk's avatar
hjk committed
958
        self.value = value
959
960
961
962
        if iname is None:
            self.iname = parentiname
        else:
            self.iname = "%s.%s" % (parentiname, iname)
hjk's avatar
hjk committed
963
964
965
        self.name = name


966
967
968
969
970
971
#######################################################################
#
# SetupCommand
#
#######################################################################

972
# This is a cache mapping from 'type name' to 'display alternatives'.
973
qqFormats = {}
974
975

# This is a cache of all known dumpers.
hjk's avatar
hjk committed
976
qqDumpers = {}
977
978
979

# 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.
980
qqNs = ""
981
982
983

# This is a cache of typenames->bool saying whether we are QObject
# derived.
hjk's avatar
hjk committed
984
qqQObjectCache = {}
985
986
987
988
989
990
991
992
993


class SetupCommand(gdb.Command):
    """Setup Creator Pretty Printing"""

    def __init__(self):
        super(SetupCommand, self).__init__("bbsetup", gdb.COMMAND_OBSCURE)

    def invoke(self, args, from_tty):
994
        print bbsetup()
995
996
997

SetupCommand()

998
999
1000
def bbsetup():
    module = sys.modules[__name__]
    for key, value in module.__dict__.items():
For faster browsing, not all history is shown. View entire blame