dumper.py 44.6 KB
Newer Older
hjk's avatar
hjk committed
1
2
3
4
5
6
7

#Note: Keep name-type-value-numchild-extra order

#return

import sys
import gdb
8
import base64
9
import os
10
import __builtin__
11

12
13
14
15
16
17
if os.name == "nt":
    def printableChar(ucs):
        if ucs >= 32 and ucs <= 126:
            return ucs
        return '?'
else:
18
    import curses.ascii
19
20
    def printableChar(ucs):
        return select(curses.ascii.isprint(ucs), ucs, '?')
hjk's avatar
hjk committed
21

22
# only needed for gdb 7.0/7.0.1 that do not implement parse_and_eval
23
24
25
import os
import tempfile

hjk's avatar
hjk committed
26
27
28
verbosity = 0
verbosity = 1

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# Some "Enums"

# Encodings

Unencoded8Bit, \
    Base64Encoded8BitWithQuotes, \
    Base64Encoded16BitWithQuotes, \
    Base64Encoded32BitWithQuotes, \
    Base64Encoded16Bit, \
    Base64Encoded8Bit, \
    Hex2EncodedLatin1, \
    Hex4EncodedLittleEndian, \
    Hex8EncodedLittleEndian, \
    Hex2EncodedUtf8, \
    Hex8EncodedBigEndian, \
    Hex4EncodedBigEndian \
        = range(12)

# Display modes
StopDisplay, DisplayImage1, DisplayString, DisplayImage, DisplayProcess = range(5)


51
52
53
54
55
def select(condition, if_expr, else_expr):
    if condition:
        return if_expr
    return else_expr

56
57
58
59
60
def qmin(n, m):
    if n < m:
        return n
    return m

hjk's avatar
hjk committed
61
def isGoodGdb():
62
63
    #return gdb.VERSION.startswith("6.8.50.2009") \
    #   and gdb.VERSION != "6.8.50.20090630-cvs"
64
    return 'parse_and_eval' in __builtin__.dir(gdb)
hjk's avatar
hjk committed
65

66
def cleanAddress(addr):
67
68
    if addr is None:
        return "<no address>"
69
70
    # We cannot use str(addr) as it yields rubbish for char pointers
    # that might trigger Unicode encoding errors.
71
    return addr.cast(gdb.lookup_type("void").pointer())
72

hjk's avatar
hjk committed
73
74
75
76
# Workaround for gdb < 7.1
def numericTemplateArgument(type, position):
    try:
        return int(type.template_argument(position))
77
    except RuntimeError, error:
hjk's avatar
hjk committed
78
79
80
81
        # ": No type named 30."
        msg = str(error)
        return int(msg[14:-1])

82
def parseAndEvaluate(exp):
hjk's avatar
hjk committed
83
    if isGoodGdb():
84
85
86
87
        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")
88
89
90
91
    try:
        gdb.execute("print %s" % exp)
    except:
        gdb.execute("set logging off")
92
        gdb.execute("set logging redirect off")
93
        return None
94
    gdb.execute("set logging off")
95
    gdb.execute("set logging redirect off")
96
97
    return gdb.history(0)

98

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
def catchCliOutput(command):
    file = tempfile.mkstemp(prefix="gdbpy_")
    filename = file[1]
    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")
    gdb.execute(command)
    gdb.execute("set logging off")
    gdb.execute("set logging redirect off")
    file = open(filename, "r")
    lines = []
    for line in file:
        lines.append(line)
    file.close()
    try:  # files may still be locked by gdb on Windows
        os.remove(filename)
    except:
        pass
    return lines


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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"
146
    #6       breakpoint     keep y   0xb6cf18e5 <__cxa_throw+5>\n"
147
    lines = catchCliOutput("info break")
148
149
150
151

    lines.reverse()
    bp = Breakpoint()
    for line in lines:
152
153
        if len(line) == 0 or line.startswith(" "):
            continue
154
155
156
157
158
159
160
161
162
163
        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")
164
        posin = line.find(" in ", pos0x)
165
        poslt = line.find(" <", pos0x)
166
        posat = line.find(" at ", posin)
167
        poscol = line.find(":", posat)
168
169
170
171
172
173
        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)]
174
175
176
        # Take "no address" as indication that the bp is pending.
        #if line.find("<PENDING>") >= 0:
        #    bp.address.append("<PENDING>")
177
178
179
180
181
182
183
184
185
186
187
188
        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
189
190
191
192
        d.put('bkpt={number="%s",' % bp.number)
        d.put('type="breakpoint",')
        d.put('disp="keep",')
        d.put('enabled="y",')
193
        for address in bp.address:
hjk's avatar
hjk committed
194
            d.put('addr="%s",' % address)
195
        if not bp.function is None:
hjk's avatar
hjk committed
196
            d.put('func="%s",' % bp.function)
197
        if not bp.filename is None:
hjk's avatar
hjk committed
198
            d.put('file="%s",' % bp.filename)
199
        if not bp.fullname is None:
hjk's avatar
hjk committed
200
            d.put('fullname="%s",' % bp.fullname)
201
        if not bp.linenumber is None:
hjk's avatar
hjk committed
202
            d.put('line="%s",' % bp.linenumber)
203
        if not bp.condition is None:
hjk's avatar
hjk committed
204
            d.put('cond="%s",' % bp.condition)
205
        if not bp.fullname is None:
hjk's avatar
hjk committed
206
            d.put('fullname="%s",' % bt.fullname)
207
        if not bp.times is None:
hjk's avatar
hjk committed
208
            d.put('times="1",' % bp.times)
209
        #d.put('original-location="-"')
hjk's avatar
hjk committed
210
        d.put('},')
211
212
213
        bp = Breakpoint()


214
215
216
217
218
219
220
221
222
223
224
# 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


225
def listOfLocals(varList):
226
227
228
229
    try:
        frame = gdb.selected_frame()
        #warn("FRAME %s: " % frame)
    except RuntimeError:
230
        warn("FRAME NOT ACCESSIBLE")
hjk's avatar
hjk committed
231
        return []
232

233
    # gdb-6.8-symbianelf fails here
234
    hasBlock = 'block' in __builtin__.dir(frame)
235

236
    items = []
237
238
239
240
241
242
243
244
245
    if hasBlock and isGoodGdb():
        warn("IS GOOD: %s " % varList)
        try:
            block = frame.block()
            #warn("BLOCK: %s " % block)
        except:
            warn("BLOCK NOT ACCESSIBLE")
            return items

246
247
248
249
250
251
        while True:
            if block is None:
                warn("UNEXPECTED 'None' BLOCK")
                break
            for symbol in block:
                name = symbol.print_name
252

253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
                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))
                item = Item(0, "local", name, name)
                try:
                    item.value = frame.read_var(name)  # this is a gdb value
                except RuntimeError:
                    # happens for  void foo() { std::string s; std::wstring w; }
                    #warn("  FRAME READ VAR ERROR: %s (%s): " % (symbol, name))
                    continue
                #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
276
        # Assuming gdb 7.0 release or 6.8-symbianelf.
277
278
        file = tempfile.mkstemp(prefix="gdbpy_")
        filename = file[1]
279
280
        #warn("VARLIST: %s " % varList)
        #warn("VARLIST: %s " % len(varList))
hjk's avatar
hjk committed
281
282
        gdb.execute("set logging off")
        gdb.execute("set logging redirect off")
283
284
285
        gdb.execute("set logging file %s" % filename)
        gdb.execute("set logging redirect on")
        gdb.execute("set logging on")
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
        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
303
304
        gdb.execute("set logging off")
        gdb.execute("set logging redirect off")
305

306
307
308
309
        file = open(filename, "r")
        for line in file:
            if len(line) == 0 or line.startswith(" "):
                continue
310
            # The function parameters
311
312
313
            pos = line.find(" = ")
            if pos < 0:
                continue
314
315
            varList.append(line[0:pos])
        file.close()
316
317
318
319
        try:  # files may still be locked by gdb on Windows
            os.remove(filename)
        except:
            pass
320
321
322
        #warn("VARLIST: %s " % varList)
        for name in varList:
            #warn("NAME %s " % name)
323
324
325
326
            item = Item(0, "local", name, name)
            try:
                item.value = frame.read_var(name)  # this is a gdb value
            except RuntimeError:
327
328
                pass
                #continue
329
330
331
332
333
            items.append(item)

    return items


334
def value(expr):
335
    value = parseAndEvaluate(expr)
336
337
338
339
340
    try:
        return int(value)
    except:
        return str(value)

341

hjk's avatar
hjk committed
342
def isSimpleType(typeobj):
343
344
    if typeobj.code == gdb.TYPE_CODE_PTR:
        return False
hjk's avatar
hjk committed
345
346
347
348
349
350
351
352
    type = str(typeobj)
    return type == "bool" \
        or type == "char" \
        or type == "double" \
        or type == "float" \
        or type == "int" \
        or type == "long" or type.startswith("long ") \
        or type == "short" or type.startswith("short ") \
353
        or type == "signed" or type.startswith("signed ") \
hjk's avatar
hjk committed
354
355
        or type == "unsigned" or type.startswith("unsigned ")

356

hjk's avatar
hjk committed
357
358
359
360
361
362
363
364
365
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):
366
367
    if True or verbosity > 0:
        print "XXX: %s\n" % message.encode("latin1")
hjk's avatar
hjk committed
368
369
370
371
372
373
    pass

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

374
375
376
377
378
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
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
#def couldBePointer(p, align):
#    type = gdb.lookup_type("unsigned int")
#    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):
400
401
402
403
    # 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 ")
404
    return p.cast(gdb.lookup_type("void").pointer()) == 0
hjk's avatar
hjk committed
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430

movableTypes = set([
    "QBrush", "QBitArray", "QByteArray",
    "QCustomTypeInfo", "QChar",
    "QDate", "QDateTime",
    "QFileInfo", "QFixed", "QFixedPoint", "QFixedSize",
    "QHashDummyValue",
    "QIcon", "QImage",
    "QLine", "QLineF", "QLatin1Char", "QLocal",
    "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:]
    return type

def checkPointerRange(p, n):
431
    for i in xrange(n):
hjk's avatar
hjk committed
432
433
434
435
436
437
        checkPointer(p)
        ++p

def call(value, func):
    #warn("CALL: %s -> %s" % (value, func))
    type = stripClassTag(str(value.type))
438
    if type.find(":") >= 0:
hjk's avatar
hjk committed
439
440
441
        type = "'" + type + "'"
    exp = "((%s*)%s)->%s" % (type, value.address, func)
    #warn("CALL: %s" % exp)
442
    result = parseAndEvaluate(exp)
hjk's avatar
hjk committed
443
444
445
    #warn("  -> %s" % result)
    return result

446
447
def qtNamespace():
    try:
448
        type = str(parseAndEvaluate("&QString::null").type.target().unqualified())
449
450
451
        return type[0:len(type) - len("QString::null")]
    except RuntimeError:
        return ""
452
453
454
    except AttributeError:
        # Happens for none-Qt applications
        return ""
hjk's avatar
hjk committed
455

456
457
458
459
460
461
462
def findFirstZero(p, max):
    for i in xrange(max):
        if p.dereference() == 0:
            return i
        p = p + 1
    return -1

463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488

def extractCharArray(p, maxsize):
    t = gdb.lookup_type("unsigned char").pointer()
    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
    return extractCharArray(data, 100)

489
def encodeCharArray(p, maxsize, size = -1):
490
491
    t = gdb.lookup_type("unsigned char").pointer()
    p = p.cast(t)
492
493
494
495
    if size == -1:
        i = findFirstZero(p, maxsize)
    else:
        i = size
496
    limit = select(i < 0, maxsize, i)
497
    s = ""
498
    for i in xrange(limit):
499
500
        s += "%02x" % int(p.dereference())
        p += 1
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
    if i == maxsize:
        s += "2e2e2e"
    return s

def encodeChar2Array(p, maxsize):
    t = gdb.lookup_type("unsigned short").pointer()
    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):
    t = gdb.lookup_type("unsigned int").pointer()
    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"
529
530
    return s

531
532
533
534
535
536
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)
537
    checkRef(d_ptr["ref"])
538
539
540
    if size > 0:
        checkAccess(data, 4)
        checkAccess(data + size) == 0
541
    return encodeCharArray(data, size)
542
543
544
545
546
547
548
549
550
551

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)
        checkAccess(data + size * 2) == 0
552
    checkRef(d_ptr["ref"])
553
554
    p = gdb.Value(d_ptr["data"])
    s = ""
555
    for i in xrange(size):
556
557
558
559
560
561
        val = int(p.dereference())
        s += "%02x" % (val % 256)
        s += "%02x" % (val / 256)
        p += 1
    return s

hjk's avatar
hjk committed
562
563
564
565
566
567
568
#######################################################################
#
# Item
#
#######################################################################

class Item:
569
    def __init__(self, value, parentiname, iname, name = None):
hjk's avatar
hjk committed
570
        self.value = value
571
572
573
574
        if iname is None:
            self.iname = parentiname
        else:
            self.iname = "%s.%s" % (parentiname, iname)
hjk's avatar
hjk committed
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
        self.name = name


#######################################################################
#
# FrameCommand
#
#######################################################################

class FrameCommand(gdb.Command):
    """Do fancy stuff. Usage bb --verbose expandedINames"""

    def __init__(self):
        super(FrameCommand, self).__init__("bb", gdb.COMMAND_OBSCURE)

590
591
592
593
594
    def invoke(self, args, from_tty):
        options = []
        varList = []
        typeformats = {}
        formats = {}
595
        watchers = ""
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
        for arg in args.split(' '):
            pos = arg.find(":") + 1
            if arg.startswith("options:"):
                options = arg[pos:].split(",")
            elif arg.startswith("vars:"):
                vars = arg[pos:].split(",")
            elif arg.startswith("expanded:"):
                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)
                        typeformats[type] = int(f[pos+1:])
            elif arg.startswith("formats:"):
                for f in arg[pos:].split(","):
                    pos = f.find("=")
                    if pos != -1:
                        formats[f[0:pos]] = int(f[pos+1:])
            elif arg.startswith("watchers:"):
                watchers = base64.b16decode(arg[pos:], True)
617

618
        useFancy = "fancy" in options
619
620

        #warn("VARIABLES: %s" % varList)
hjk's avatar
hjk committed
621
622
623
624
        #warn("EXPANDED INAMES: %s" % expandedINames)
        module = sys.modules[__name__]
        self.dumpers = {}

625
626
627
        if False:
            dumpers = ""
            typeformats = ""
hjk's avatar
hjk committed
628
            for key, value in module.__dict__.items():
629
                if key.startswith("qdump__"):
630
631
                    dumpers += '"' + key[7:] + '",'
            output = "dumpers=[%s]," % dumpers
hjk's avatar
hjk committed
632
            #output += "qtversion=[%d,%d,%d]"
633
            #output += "qtversion=[4,6,0],"
hjk's avatar
hjk committed
634
635
636
637
638
639
640
            output += "namespace=\"%s\"," % qtNamespace()
            output += "dumperversion=\"2.0\","
            output += "sizes=[],"
            output += "expressions=[]"
            output += "]"
            print output
            return
641

642

hjk's avatar
hjk committed
643
644
        if useFancy:
            for key, value in module.__dict__.items():
645
646
                if key.startswith("qdump__"):
                    self.dumpers[key[7:]] = value
hjk's avatar
hjk committed
647
648
649

        d = Dumper()
        d.dumpers = self.dumpers
650
651
        d.typeformats = typeformats
        d.formats = formats
652
653
654
        d.useFancy = useFancy
        d.passExceptions = "passexceptions" in options
        d.autoDerefPointers = "autoderef" in options
655
        d.ns = qtNamespace()
656
        d.expandedINames = expandedINames
hjk's avatar
hjk committed
657
658
        #warn(" NAMESPACE IS: '%s'" % d.ns)

659
660
661
        #
        # Locals
        #
662
        for item in listOfLocals(varList):
663
            #warn("ITEM NAME %s: " % item.name)
664
665
666
            try:
                #warn("ITEM VALUE %s: " % item.value)
                # Throw on funny stuff, catch below.
hjk's avatar
hjk committed
667
668
669
670
                # Unfortunately, this fails also with a "Unicode encoding error"
                # in testArray().
                #dummy = str(item.value)
                pass
671
672
673
674
675
676
677
678
679
680
681
            except:
                # Locals with failing memory access.
                d.beginHash()
                d.put('iname="%s",' % item.iname)
                d.put('name="%s",' % item.name)
                d.put('addr="<not accessible>",')
                d.put('value="<not accessible>",')
                d.put('type="%s",' % item.value.type)
                d.put('numchild="0"');
                d.endHash()
                continue
682
683
684
685

            type = item.value.type
            if type.code == gdb.TYPE_CODE_PTR \
                    and item.name == "argv" and str(type) == "char **":
686
                # Special handling for char** argv.
687
688
                n = 0
                p = item.value
689
690
691
692
693
                # p is 0 for "optimized out" cases.
                if not isNull(p):
                    while not isNull(p.dereference()) and n <= 100:
                        p += 1
                        n += 1
694
695
696
697
698
699
700
701

                d.beginHash()
                d.put('iname="%s",' % item.iname)
                d.putName(item.name)
                d.putItemCount(select(n <= 100, n, "> 100"))
                d.putType(type)
                d.putNumChild(n)
                if d.isExpanded(item):
702
                    p = item.value
703
                    d.beginChildren(n)
704
                    for i in xrange(n):
705
706
                        value = p.dereference()
                        d.putItem(Item(value, item.iname, i, None))
707
                        p += 1
708
709
710
711
712
713
                    if n > 100:
                        d.putEllipsis()
                    d.endChildren()
                d.endHash()

            else:
714
                # A "normal" local variable or parameter.
715
                try:
716
717
718
719
720
721
                   addr = cleanAddress(item.value.address)
                   d.beginHash()
                   d.put('iname="%s",' % item.iname)
                   d.put('addr="%s",' % addr)
                   d.safePutItemHelper(item)
                   d.endHash()
722
                except AttributeError:
723
724
                    # Thrown by cleanAddress with message "'NoneType' object
                    # has no attribute 'cast'" for optimized-out values.
725
726
727
728
729
730
731
                    d.beginHash()
                    d.put('iname="%s",' % item.iname)
                    d.put('name="%s",' % item.name)
                    d.put('addr="<optimized out>",')
                    d.put('value="<optimized out>",')
                    d.put('type="%s"' % item.value.type)
                    d.endHash()
hjk's avatar
hjk committed
732

733
734
735
736
        d.pushOutput()
        locals = d.safeoutput


737
738
739
        #
        # Watchers
        #
740
        d.safeoutput = ""
741
        if len(watchers) > 0:
742
            for watcher in watchers.split("##"):
743
744
                (exp, iname) = watcher.split("#")
                self.handleWatch(d, exp, iname)
745
746
747
        d.pushOutput()
        watchers = d.safeoutput

hjk's avatar
hjk committed
748
749
750
751
        sep = ""
        if len(locals) and len(watchers):
            sep = ","

752
753
754
        #
        # Breakpoints
        #
755
756
757
758
759
760
761
762
        #breakpoints = ""
        #d.safeoutput = ""
        #listOfBreakpoints(d)
        #d.pushOutput()
        #breakpoints = d.safeoutput

        #print('data=[' + locals + sep + watchers + '],bkpts=[' + breakpoints + ']\n')
        print('data=[' + locals + sep + watchers + ']\n')
763

764

765
766
    def handleWatch(self, d, exp, iname):
        #warn("HANDLING WATCH %s, INAME: '%s'" % (exp, iname))
767
        if exp.startswith("["):
768
            warn("EVAL: EXP: %s" % exp)
769
            d.beginHash()
770
            d.put('iname="%s",' % iname)
771
            d.put('name="%s",' % exp)
772
            d.put('exp="%s",' % exp)
773
774
            try:
                list = eval(exp)
775
                #warn("EVAL: LIST: %s" % list)
776
777
778
779
780
781
782
                d.put('value=" "')
                d.put('type=" "')
                d.put('numchild="%d"' % len(list))
                # This is a list of expressions to evaluate
                d.beginChildren(len(list))
                itemNumber = 0
                for item in list:
783
                    self.handleWatch(d, item, "%s.%d" % (iname, itemNumber))
784
785
786
787
788
789
790
791
792
793
794
795
796
                    itemNumber += 1
                d.endChildren()
            except:
                warn("EVAL: ERROR CAUGHT")
                d.put('value="<syntax error>"')
                d.put('type=" "')
                d.put('numchild="0"')
                d.beginChildren(0)
                d.endChildren()
            d.endHash()
            return

        d.beginHash()
797
        d.put('iname="%s",' % iname)
798
        d.put('name="%s",' % exp)
799
        d.put('exp="%s",' % exp)
800
801
        handled = False
        if exp == "<Edit>":
hjk's avatar
hjk committed
802
            d.put('value=" ",type=" ",numchild="0",')
803
        else:
804
            try:
805
                value = parseAndEvaluate(exp)
806
                item = Item(value, iname, None, None)
807
                d.putItemHelper(item)
808
            except RuntimeError:
hjk's avatar
hjk committed
809
                d.put('value="<invalid>",type="<unknown>",numchild="0",')
810
        d.endHash()
811

hjk's avatar
hjk committed
812
813
814
815

FrameCommand()


816
817
818
819
820
821
822
823
824
825
826
827
828
829
#######################################################################
#
# Step Command
#
#######################################################################


class SalCommand(gdb.Command):
    """Do fancy stuff."""

    def __init__(self):
        super(SalCommand, self).__init__("sal", gdb.COMMAND_OBSCURE)

    def invoke(self, arg, from_tty):
830
831
        (cmd, addr) = arg.split(",")
        lines = catchCliOutput("info line *" + addr)
832
833
834
835
836
837
838
839
840
841
        fromAddr = "0x0"
        toAddr = "0x0"
        for line in lines:
            pos0from = line.find(" starts at address") + 19
            pos1from = line.find(" ", pos0from)
            pos0to = line.find(" ends at", pos1from) + 9
            pos1to = line.find(" ", pos0to)
            if pos1to > 0:
                fromAddr = line[pos0from : pos1from]
                toAddr = line[pos0to : pos1to]
842
        gdb.execute("maint packet sal%s,%s,%s" % (cmd,fromAddr, toAddr))
843
844

SalCommand()
hjk's avatar
hjk committed
845

846

hjk's avatar
hjk committed
847
848
849
850
851
852
#######################################################################
#
# The Dumper Class
#
#######################################################################

853

hjk's avatar
hjk committed
854
855
856
857
858
859
class Dumper:
    def __init__(self):
        self.output = ""
        self.safeoutput = ""
        self.childTypes = [""]
        self.childNumChilds = [-1]
860
861
        self.maxNumChilds = [-1]
        self.numChilds = [-1]
hjk's avatar
hjk committed
862
863
864
865
866

    def put(self, value):
        self.output += value

    def putField(self, name, value):
hjk's avatar
hjk committed
867
        self.put('%s="%s",' % (name, value))
hjk's avatar
hjk committed
868
869
870
871
872

    def beginHash(self):
        self.put('{')

    def endHash(self):
hjk's avatar
hjk committed
873
        self.put('},')
hjk's avatar
hjk committed
874
875

    def beginItem(self, name):
876
        self.put('%s="' % name)
hjk's avatar
hjk committed
877
878

    def endItem(self):
hjk's avatar
hjk committed
879
        self.put('",')
hjk's avatar
hjk committed
880

881
    def beginChildren(self, numChild_ = 1, childType_ = None, childNumChild_ = None):
hjk's avatar
hjk committed
882
883
        childType = ""
        childNumChild = -1
884
885
886
887
888
889
        if type(numChild_) is list:
            numChild = numChild_[0]
            maxNumChild = numChild_[1]
        else:
            numChild = numChild_
            maxNumChild = numChild_
890
        if numChild == 0:
891
892
893
            childType_ = None
        if not childType_ is None:
            childType = stripClassTag(str(childType_))
894
            self.put('childtype="%s",' % childType)
895
            if isSimpleType(childType_) or isStringType(self, childType_):
896
                self.put('childnumchild="0",')
hjk's avatar
hjk committed
897
                childNumChild = 0
898
            elif childType_.code == gdb.TYPE_CODE_PTR:
899
                self.put('childnumchild="1",')
hjk's avatar
hjk committed
900
                childNumChild = 1
901
902
903
        if not childNumChild_ is None:
            self.put('childnumchild="%s",' % childNumChild_)
            childNumChild = childNumChild_
hjk's avatar
hjk committed
904
905
        self.childTypes.append(childType)
        self.childNumChilds.append(childNumChild)
906
907
        self.numChilds.append(numChild)
        self.maxNumChilds.append(maxNumChild)
hjk's avatar
hjk committed
908
909
910
911
912
        #warn("BEGIN: %s" % self.childTypes)
        self.put("children=[")

    def endChildren(self):
        #warn("END: %s" % self.childTypes)
913
914
915
916
        numChild = self.numChilds.pop()
        maxNumChild = self.maxNumChilds.pop()
        if maxNumChild < numChild:
            self.putEllipsis();
hjk's avatar
hjk committed
917
918
        self.childTypes.pop()
        self.childNumChilds.pop()
hjk's avatar
hjk committed
919
        self.put('],')
hjk's avatar
hjk committed
920

921
922
923
    def childRange(self):
        return xrange(qmin(self.maxNumChilds[-1], self.numChilds[-1]))

hjk's avatar
hjk committed
924
925
    # convenience
    def putItemCount(self, count):
hjk's avatar
hjk committed
926
        self.put('value="<%s items>",' % count)
hjk's avatar
hjk committed
927
928

    def putEllipsis(self):
hjk's avatar
hjk committed
929
        self.put('{name="<incomplete>",value="",type="",numchild="0"},')
hjk's avatar
hjk committed
930
931
932
933
934
935

    def putType(self, type):
        #warn("TYPES: '%s' '%s'" % (type, self.childTypes))
        #warn("  EQUAL 2: %s " % (str(type) == self.childTypes[-1]))
        type = stripClassTag(str(type))
        if len(type) > 0 and type != self.childTypes[-1]:
hjk's avatar
hjk committed
936
            self.put('type="%s",' % type) # str(type.unqualified()) ?
hjk's avatar
hjk committed
937

938
939
940
    def putAddress(self, addr):
        self.put('addr="%s",' % cleanAddress(addr))

hjk's avatar
hjk committed
941
942
    def putNumChild(self, numchild):
        #warn("NUM CHILD: '%s' '%s'" % (numchild, self.childNumChilds[-1]))
943
        if numchild != self.childNumChilds[-1]:
hjk's avatar
hjk committed
944
            self.put('numchild="%s",' % numchild)
hjk's avatar
hjk committed
945

946
947
948
949
950
951
    def putValue(self, value, encoding = None):
        if not encoding is None:
            self.putField("valueencoded", encoding)
        self.putField("value", value)

    def putStringValue(self, value):
952
        if value is None:
hjk's avatar
hjk committed
953
            self.put('value="<not available>",')
954
955
        else:
            str = encodeString(value)
956
            self.put('valueencoded="%d",value="%s",' % (Hex4EncodedLittleEndian, str))
957

958
959
960
961
962
963
964
965
    def putDisplay(self, format, value = None, cmd = None):
        self.put('editformat="%s",' % format)
        if cmd is None:
            if not value is None:
                self.put('editvalue="%s",' % value)
        else:
            self.put('editvalue="%s|%s",' % (cmd, value))

966
967
    def putByteArrayValue(self, value):
        str = encodeByteArray(value)
968
        self.put('valueencoded="%d",value="%s",' % (Hex2EncodedLatin1, str))
969

970
    def putName(self, name):
hjk's avatar
hjk committed
971
        self.put('name="%s",' % name)
972

hjk's avatar
hjk committed
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
    def isExpanded(self, item):
        #warn("IS EXPANDED: %s in %s" % (item.iname, self.expandedINames))
        if item.iname is None:
            raise "Illegal iname 'None'"
        if item.iname.startswith("None"):
            raise "Illegal iname '%s'" % item.iname
        #warn("   --> %s" % (item.iname in self.expandedINames))
        return item.iname in self.expandedINames

    def isExpandedIName(self, iname):
        return iname in self.expandedINames

    def unputField(self, name):
        pos = self.output.rfind(",")
        if self.output[pos + 1:].startswith(name):
            self.output = self.output[0:pos]

    def stripNamespaceFromType(self, typeobj):
991
992
993
994
995
        # This breaks for dumpers type names containing '__star'.
        # But this should not happen as identifiers containing two
        # subsequent underscores are reserved for the implemention.
        if typeobj.code == gdb.TYPE_CODE_PTR:
            return self.stripNamespaceFromType(typeobj.target()) + "__star"
hjk's avatar
hjk committed
996
997
998
999
        type = stripClassTag(str(typeobj))
        if len(self.ns) > 0 and type.startswith(self.ns):
            type = type[len(self.ns):]
        pos = type.find("<")
1000
1001
1002
1003
1004
        # FIXME: make it recognize  foo<A>::bar<B>::iterator?
        while pos != -1:
            pos1 = type.rfind(">", pos)
            type = type[0:pos] + type[pos1+1:]
            pos = type.find("<")
hjk's avatar
hjk committed
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
        return type

    def isMovableType(self, type):
        if type.code == gdb.TYPE_CODE_PTR:
            return True
        if isSimpleType(type):
            return True
        return self.stripNamespaceFromType(type) in movableTypes

    def putIntItem(self, name, value):
        self.beginHash()
1016
1017
        self.putName(name)
        self.putValue(value)
hjk's avatar
hjk committed
1018
1019
1020
1021
1022
1023
        self.putType("int")
        self.putNumChild(0)
        self.endHash()

    def putBoolItem(self, name, value):
        self.beginHash()
1024
1025
        self.putName(name)
        self.putValue(value)
hjk's avatar
hjk committed
1026
1027
1028
1029
1030
1031
1032
1033
1034
        self.putType("bool")
        self.putNumChild(0)
        self.endHash()

    def pushOutput(self):
        #warn("PUSH OUTPUT: %s " % self.output)
        self.safeoutput += self.output
        self.output = ""

1035
    def dumpInnerValueHelper(self, item):
hjk's avatar
hjk committed
1036
        if isSimpleType(item.value.type):
1037
            self.safePutItemHelper(item)
hjk's avatar
hjk committed
1038

1039
1040
1041
1042
1043
1044
    def itemFormat(self, item):
        format = self.formats.get(item.iname)
        if format is None:
            format = self.typeformats.get(stripClassTag(str(item.value.type)))
        return format

1045
1046
1047
1048
1049
    def safePutItem(self, item):
        self.beginHash()
        self.safePutItemHelper(item)
        self.endHash()

hjk's avatar
hjk committed
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
    def safePutItemHelper(self, item):
        self.pushOutput()
        # This is only used at the top level to ensure continuation
        # after failures due to uninitialized or corrupted data.
        if self.passExceptions:
            # for debugging reasons propagate errors.
            self.putItemHelper(item)

        else:
            try:
                self.putItemHelper(item)

1062
            except RuntimeError:
hjk's avatar
hjk committed
1063
1064
1065
1066
                self.output = ""
                # FIXME: Only catch debugger related exceptions
                #exType, exValue, exTraceback = sys.exc_info()
                #tb = traceback.format_exception(exType, exValue, exTraceback)
1067
                #warn("Exception: %s" % ex.message)
hjk's avatar
hjk committed
1068
1069
1070
1071
1072
                # DeprecationWarning: BaseException.message
                # has been deprecated
                #warn("Exception.")
                #for line in tb:
                #    warn("%s" % line)
1073
1074
1075
1076
                self.putName(item.name)
                self.putValue("<invalid>")
                self.putType(str(item.value.type))
                self.putNumChild(0)
hjk's avatar
hjk committed
1077
1078
1079
1080
1081
1082
1083
                #if self.isExpanded(item):
                self.beginChildren()
                self.endChildren()
        self.pushOutput()

    def putItem(self, item):
        self.beginHash()
hjk's avatar
hjk committed
1084
        self.safePutItemHelper(item)
hjk's avatar
hjk committed
1085
1086
1087
        self.endHash()

    def putCallItem(self, name, item, func):
1088
1089
1090
1091
1092
        try:
            result = call(item.value, func)
            self.safePutItem(Item(result, item.iname, name, name))
        except:
            self.safePutItem(Item(None, item.iname))
hjk's avatar
hjk committed
1093

1094
    def putItemHelper(self, item):
hjk's avatar
hjk committed
1095
1096
        name = getattr(item, "name", None)
        if not name is None:
1097
            self.putName(name)
hjk's avatar
hjk committed
1098

1099
1100
1101
1102
1103
1104
1105
1106
        if item.value is None:
            # Happens for non-available watchers in gdb versions that
            # need to use gdb.execute instead of gdb.parse_and_eval
            self.putValue("<not available>")
            self.putType("<unknown>")
            self.putNumChild(0)
            return

hjk's avatar
hjk committed
1107
        # FIXME: Gui shows references stripped?
1108
        #warn(" ");
hjk's avatar
hjk committed
1109
        #warn("REAL INAME: %s " % item.iname)
1110
        #warn("REAL NAME: %s " % name)
hjk's avatar
hjk committed
1111
        #warn("REAL TYPE: %s " % item.value.type)
1112
        #warn("REAL VALUE: %s " % item.value)
1113
1114
        #try:
        #    warn("REAL VALUE: %s " % item.value)
1115
1116
        #except:
        #    #UnicodeEncodeError:
1117
        #    warn("REAL VALUE: <unprintable>")
hjk's avatar
hjk committed
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130

        value = item.value
        type = value.type

        if type.code == gdb.TYPE_CODE_REF:
            type = type.target()
            value = value.cast(type)

        if type.code == gdb.TYPE_CODE_TYPEDEF:
            type = type.target()

        strippedType = self.stripNamespaceFromType(
            type.strip_typedefs().unqualified()).replace("::", "__")
1131

hjk's avatar
hjk committed
1132
1133
1134
1135
        #warn(" STRIPPED: %s" % strippedType)
        #warn(" DUMPERS: %s" % self.dumpers)
        #warn(" DUMPERS: %s" % (strippedType in self.dumpers))

1136
        if isSimpleType(type.unqualified()):
1137
1138
            #warn("IS SIMPLE: %s " % type)
            self.putType(item.value.type)
1139
1140
            self.putValue(value)
            self.putNumChild(0)
hjk's avatar
hjk committed
1141
1142

        elif strippedType in self.dumpers:
1143
1144
            #warn("IS DUMPABLE: %s " % type)
            self.putType(item.value.type)
hjk's avatar
hjk committed
1145
            self.dumpers[strippedType](self, item)
1146
            #warn(" RESULT: %s " % self.output)
hjk's avatar
hjk committed
1147
1148

        elif type.code == gdb.TYPE_CODE_ENUM:
1149
            #warn("GENERIC ENUM: %s" % value)
1150
            self.putType(item.value.type)
1151
            self.putValue(value)
hjk's avatar
hjk committed
1152
            self.putNumChild(0)
1153

hjk's avatar
hjk committed
1154
1155

        elif type.code == gdb.TYPE_CODE_PTR:
1156
1157
            isHandled = False

1158
            format = self.itemFormat(item)
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170

            if not format is None:
                self.putAddress(value.address)
                self.putType(item.value.type)
                self.putNumChild(0)
                isHandled = True

            if format == 0:
                # Bald pointer.
                self.putValue(str(cleanAddress(value.address)))
            elif format == 1 or format == 2:
                # Latin1 or UTF-8
1171
                f = select(format == 1, Hex2EncodedLatin1, Hex2EncodedUtf8)
1172
1173
1174