1############################################################################
2#
3# Copyright (C) 2016 The Qt Company Ltd.
4# Contact: https://www.qt.io/licensing/
5#
6# This file is part of Qt Creator.
7#
8# Commercial License Usage
9# Licensees holding valid commercial Qt licenses may use this file in
10# accordance with the commercial license agreement provided with the
11# Software or, alternatively, in accordance with the terms contained in
12# a written agreement between you and The Qt Company. For licensing terms
13# and conditions see https://www.qt.io/terms-conditions. For further
14# information use the contact form at https://www.qt.io/contact-us.
15#
16# GNU General Public License Usage
17# Alternatively, this file may be used under the terms of the GNU
18# General Public License version 3 as published by the Free Software
19# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20# included in the packaging of this file. Please review the following
21# information to ensure the GNU General Public License requirements will
22# be met: https://www.gnu.org/licenses/gpl-3.0.html.
23#
24############################################################################
25
26try:
27    import __builtin__
28except:
29    import builtins
30
31import gdb
32import os
33import os.path
34import re
35import sys
36import struct
37import tempfile
38
39from dumper import DumperBase, Children, toInteger, TopLevelItem
40from utils import TypeCode
41from gdbtracepoint import *
42
43
44#######################################################################
45#
46# Infrastructure
47#
48#######################################################################
49
50
51def safePrint(output):
52    try:
53        print(output)
54    except:
55        out = ''
56        for c in output:
57            cc = ord(c)
58            if cc > 127:
59                out += '\\\\%d' % cc
60            elif cc < 0:
61                out += '\\\\%d' % (cc + 256)
62            else:
63                out += c
64        print(out)
65
66
67def registerCommand(name, func):
68
69    class Command(gdb.Command):
70        def __init__(self):
71            super(Command, self).__init__(name, gdb.COMMAND_OBSCURE)
72
73        def invoke(self, args, from_tty):
74            safePrint(func(args))
75
76    Command()
77
78
79#######################################################################
80#
81# Convenience
82#
83#######################################################################
84
85# For CLI dumper use, see README.txt
86class PPCommand(gdb.Command):
87    def __init__(self):
88        super(PPCommand, self).__init__('pp', gdb.COMMAND_OBSCURE)
89
90    def invoke(self, args, from_tty):
91        print(theCliDumper.fetchVariable(args))
92
93
94PPCommand()
95
96# Just convenience for 'python print gdb.parse_and_eval(...)'
97
98
99class PPPCommand(gdb.Command):
100    def __init__(self):
101        super(PPPCommand, self).__init__('ppp', gdb.COMMAND_OBSCURE)
102
103    def invoke(self, args, from_tty):
104        print(gdb.parse_and_eval(args))
105
106
107PPPCommand()
108
109
110def scanStack(p, n):
111    p = int(p)
112    r = []
113    for i in range(n):
114        f = gdb.parse_and_eval('{void*}%s' % p)
115        m = gdb.execute('info symbol %s' % f, to_string=True)
116        if not m.startswith('No symbol matches'):
117            r.append(m)
118        p += f.type.sizeof
119    return r
120
121
122class ScanStackCommand(gdb.Command):
123    def __init__(self):
124        super(ScanStackCommand, self).__init__('scanStack', gdb.COMMAND_OBSCURE)
125
126    def invoke(self, args, from_tty):
127        if len(args) == 0:
128            args = 20
129        safePrint(scanStack(gdb.parse_and_eval('$sp'), int(args)))
130
131
132ScanStackCommand()
133
134
135#######################################################################
136#
137# Import plain gdb pretty printers
138#
139#######################################################################
140
141class PlainDumper():
142    def __init__(self, printer):
143        self.printer = printer
144        self.typeCache = {}
145
146    def __call__(self, d, value):
147        if value.nativeValue is None:
148            # warn('PlainDumper(gdb): value.nativeValue is missing (%s)'%value)
149            nativeType        = theDumper.lookupNativeType(value.type.name)
150            nativeTypePointer = nativeType.pointer()
151            nativePointer     = gdb.Value(value.laddress)
152            value.nativeValue = nativePointer.cast(nativeTypePointer).dereference()
153        try:
154            printer = self.printer.gen_printer(value.nativeValue)
155        except:
156            printer = self.printer.invoke(value.nativeValue)
157        d.putType(value.nativeValue.type.name)
158        val = printer.to_string()
159        if isinstance(val, str):
160            # encode and avoid extra quotes ('"') at beginning and end
161            d.putValue(d.hexencode(val), 'utf8:1:0')
162        elif sys.version_info[0] <= 2 and isinstance(val, unicode):
163            d.putValue(val)
164        elif val is not None:  # Assuming LazyString
165            d.putCharArrayValue(val.address, val.length,
166                                val.type.target().sizeof)
167
168        lister = getattr(printer, 'children', None)
169        if lister is None:
170            d.putNumChild(0)
171        else:
172            if d.isExpanded():
173                children = lister()
174                with Children(d):
175                    i = 0
176                    for (name, child) in children:
177                        d.putSubItem(name, d.fromNativeValue(child))
178                        i += 1
179                        if i > 1000:
180                            break
181            d.putNumChild(1)
182
183
184def importPlainDumpers(args):
185    if args == 'off':
186        theDumper.usePlainDumpers = False
187        try:
188            gdb.execute('disable pretty-printer .* .*')
189        except:
190            # Might occur in non-ASCII directories
191            DumperBase.warn('COULD NOT DISABLE PRETTY PRINTERS')
192    else:
193        theDumper.usePlainDumpers = True
194        theDumper.importPlainDumpers()
195
196
197registerCommand('importPlainDumpers', importPlainDumpers)
198
199
200class OutputSaver():
201    def __init__(self, d):
202        self.d = d
203
204    def __enter__(self):
205        self.savedOutput = self.d.output
206        self.d.output = ''
207
208    def __exit__(self, exType, exValue, exTraceBack):
209        if self.d.passExceptions and exType is not None:
210            self.d.showException('OUTPUTSAVER', exType, exValue, exTraceBack)
211            self.d.output = self.savedOutput
212        else:
213            self.savedOutput += self.d.output
214            self.d.output = self.savedOutput
215        return False
216
217
218#######################################################################
219#
220# The Dumper Class
221#
222#######################################################################
223
224
225class Dumper(DumperBase):
226
227    def __init__(self):
228        DumperBase.__init__(self)
229
230        # whether to load plain dumpers for objfiles
231        self.usePlainDumpers = False
232
233        # These values will be kept between calls to 'fetchVariables'.
234        self.isGdb = True
235        self.typeCache = {}
236        self.interpreterBreakpointResolvers = []
237
238    def prepare(self, args):
239        self.output = ''
240        self.setVariableFetchingOptions(args)
241
242    def fromFrameValue(self, nativeValue):
243        #DumperBase.warn('FROM FRAME VALUE: %s' % nativeValue.address)
244        val = nativeValue
245        if self.useDynamicType:
246            try:
247                val = nativeValue.cast(nativeValue.dynamic_type)
248            except:
249                pass
250        return self.fromNativeValue(val)
251
252    def nativeValueType(self, nativeValue):
253        return self.fromNativeType(nativeValue.type)
254
255    def fromNativeValue(self, nativeValue):
256        #DumperBase.warn('FROM NATIVE VALUE: %s' % nativeValue)
257        self.check(isinstance(nativeValue, gdb.Value))
258        nativeType = nativeValue.type
259        code = nativeType.code
260        if code == gdb.TYPE_CODE_REF:
261            targetType = self.fromNativeType(nativeType.target().unqualified())
262            val = self.createReferenceValue(toInteger(nativeValue.address), targetType)
263            val.nativeValue = nativeValue
264            #DumperBase.warn('CREATED REF: %s' % val)
265            return val
266        if code == gdb.TYPE_CODE_PTR:
267            try:
268                nativeTargetValue = nativeValue.dereference()
269            except:
270                nativeTargetValue = None
271            targetType = self.fromNativeType(nativeType.target().unqualified())
272            val = self.createPointerValue(toInteger(nativeValue), targetType)
273            # The nativeValue is needed in case of multiple inheritance, see
274            # QTCREATORBUG-17823. Using it triggers nativeValueDereferencePointer()
275            # later which
276            # is surprisingly expensive.
277            val.nativeValue = nativeValue
278            #DumperBase.warn('CREATED PTR 1: %s' % val)
279            if nativeValue.address is not None:
280                val.laddress = toInteger(nativeValue.address)
281            #DumperBase.warn('CREATED PTR 2: %s' % val)
282            return val
283        if code == gdb.TYPE_CODE_TYPEDEF:
284            targetType = nativeType.strip_typedefs().unqualified()
285            #DumperBase.warn('TARGET TYPE: %s' % targetType)
286            if targetType.code == gdb.TYPE_CODE_ARRAY:
287                val = self.Value(self)
288            else:
289                try:
290                    # Cast may fail for arrays, for typedefs to __uint128_t with
291                    # gdb.error: That operation is not available on integers
292                    # of more than 8 bytes.
293                    # See test for Bug5799, QTCREATORBUG-18450.
294                    val = self.fromNativeValue(nativeValue.cast(targetType))
295                except:
296                    val = self.Value(self)
297            #DumperBase.warn('CREATED TYPEDEF: %s' % val)
298        else:
299            val = self.Value(self)
300
301        val.nativeValue = nativeValue
302        if nativeValue.address is not None:
303            val.laddress = toInteger(nativeValue.address)
304        else:
305            size = nativeType.sizeof
306            chars = self.lookupNativeType('unsigned char')
307            y = nativeValue.cast(chars.array(0, int(nativeType.sizeof - 1)))
308            buf = bytearray(struct.pack('x' * size))
309            for i in range(size):
310                try:
311                    buf[i] = int(y[i])
312                except:
313                    pass
314            val.ldata = bytes(buf)
315
316        val._type = self.fromNativeType(nativeType)
317        val.lIsInScope = not nativeValue.is_optimized_out
318        code = nativeType.code
319        if code == gdb.TYPE_CODE_ENUM:
320            val.ldisplay = str(nativeValue)
321            intval = int(nativeValue)
322            if val.ldisplay != intval:
323                val.ldisplay += ' (%s)' % intval
324        elif code == gdb.TYPE_CODE_COMPLEX:
325            val.ldisplay = str(nativeValue)
326        elif code in [gdb.TYPE_CODE_BOOL, gdb.TYPE_CODE_INT]:
327            try:
328                # extract int presentation from native value and remember it
329                val.lvalue = int(nativeValue)
330            except:
331                # GDB only support converting integers of max. 64 bits to Python int as of now
332                pass
333        #elif code == gdb.TYPE_CODE_ARRAY:
334        #    val.type.ltarget = nativeValue[0].type.unqualified()
335        return val
336
337    def ptrSize(self):
338        result = gdb.lookup_type('void').pointer().sizeof
339        self.ptrSize = lambda: result
340        return result
341
342    def fromNativeType(self, nativeType):
343        self.check(isinstance(nativeType, gdb.Type))
344        code = nativeType.code
345        #DumperBase.warn('FROM NATIVE TYPE: %s' % nativeType)
346        nativeType = nativeType.unqualified()
347
348        if code == gdb.TYPE_CODE_PTR:
349            #DumperBase.warn('PTR')
350            targetType = self.fromNativeType(nativeType.target().unqualified())
351            return self.createPointerType(targetType)
352
353        if code == gdb.TYPE_CODE_REF:
354            #DumperBase.warn('REF')
355            targetType = self.fromNativeType(nativeType.target().unqualified())
356            return self.createReferenceType(targetType)
357
358        if hasattr(gdb, "TYPE_CODE_RVALUE_REF"):
359            if code == gdb.TYPE_CODE_RVALUE_REF:
360                #DumperBase.warn('RVALUEREF')
361                targetType = self.fromNativeType(nativeType.target())
362                return self.createRValueReferenceType(targetType)
363
364        if code == gdb.TYPE_CODE_ARRAY:
365            #DumperBase.warn('ARRAY')
366            nativeTargetType = nativeType.target().unqualified()
367            targetType = self.fromNativeType(nativeTargetType)
368            if nativeType.sizeof == 0:
369                # QTCREATORBUG-23998, note that nativeType.name == None here,
370                # whereas str(nativeType) returns sth like 'QObject [5]'
371                count = self.arrayItemCountFromTypeName(str(nativeType), 1)
372            else:
373                count = nativeType.sizeof // nativeTargetType.sizeof
374            return self.createArrayType(targetType, count)
375
376        if code == gdb.TYPE_CODE_TYPEDEF:
377            #DumperBase.warn('TYPEDEF')
378            nativeTargetType = nativeType.unqualified()
379            while nativeTargetType.code == gdb.TYPE_CODE_TYPEDEF:
380                nativeTargetType = nativeTargetType.strip_typedefs().unqualified()
381            targetType = self.fromNativeType(nativeTargetType)
382            return self.createTypedefedType(targetType, str(nativeType),
383                                            self.nativeTypeId(nativeType))
384
385        if code == gdb.TYPE_CODE_ERROR:
386            self.warn('Type error: %s' % nativeType)
387            return self.Type(self, '')
388
389        typeId = self.nativeTypeId(nativeType)
390        res = self.typeData.get(typeId, None)
391        if res is None:
392            tdata = self.TypeData(self)
393            tdata.name = str(nativeType)
394            tdata.typeId = typeId
395            tdata.lbitsize = nativeType.sizeof * 8
396            tdata.code = {
397                #gdb.TYPE_CODE_TYPEDEF : TypeCode.Typedef, # Handled above.
398                gdb.TYPE_CODE_METHOD: TypeCode.Function,
399                gdb.TYPE_CODE_VOID: TypeCode.Void,
400                gdb.TYPE_CODE_FUNC: TypeCode.Function,
401                gdb.TYPE_CODE_METHODPTR: TypeCode.Function,
402                gdb.TYPE_CODE_MEMBERPTR: TypeCode.Function,
403                #gdb.TYPE_CODE_PTR : TypeCode.Pointer,  # Handled above.
404                #gdb.TYPE_CODE_REF : TypeCode.Reference,  # Handled above.
405                gdb.TYPE_CODE_BOOL: TypeCode.Integral,
406                gdb.TYPE_CODE_CHAR: TypeCode.Integral,
407                gdb.TYPE_CODE_INT: TypeCode.Integral,
408                gdb.TYPE_CODE_FLT: TypeCode.Float,
409                gdb.TYPE_CODE_ENUM: TypeCode.Enum,
410                #gdb.TYPE_CODE_ARRAY : TypeCode.Array,
411                gdb.TYPE_CODE_STRUCT: TypeCode.Struct,
412                gdb.TYPE_CODE_UNION: TypeCode.Struct,
413                gdb.TYPE_CODE_COMPLEX: TypeCode.Complex,
414                gdb.TYPE_CODE_STRING: TypeCode.FortranString,
415            }[code]
416            if tdata.code == TypeCode.Enum:
417                tdata.enumDisplay = lambda intval, addr, form: \
418                    self.nativeTypeEnumDisplay(nativeType, intval, form)
419            if tdata.code == TypeCode.Struct:
420                tdata.lalignment = lambda: \
421                    self.nativeStructAlignment(nativeType)
422                tdata.lfields = lambda value: \
423                    self.listMembers(value, nativeType)
424            tdata.templateArguments = self.listTemplateParameters(nativeType)
425            self.registerType(typeId, tdata)  # Fix up fields and template args
426        #    warn('CREATE TYPE: %s' % typeId)
427        #else:
428        #    warn('REUSE TYPE: %s' % typeId)
429        return self.Type(self, typeId)
430
431    def listTemplateParameters(self, nativeType):
432        targs = []
433        pos = 0
434        while True:
435            try:
436                targ = nativeType.template_argument(pos)
437            except:
438                break
439            if isinstance(targ, gdb.Type):
440                targs.append(self.fromNativeType(targ.unqualified()))
441            elif isinstance(targ, gdb.Value):
442                targs.append(self.fromNativeValue(targ).value())
443            else:
444                raise RuntimeError('UNKNOWN TEMPLATE PARAMETER')
445            pos += 1
446        targs2 = self.listTemplateParametersManually(str(nativeType))
447        return targs if len(targs) >= len(targs2) else targs2
448
449    def nativeTypeEnumDisplay(self, nativeType, intval, form):
450        try:
451            enumerators = []
452            for field in nativeType.fields():
453                # If we found an exact match, return it immediately
454                if field.enumval == intval:
455                    return field.name + ' (' + (form % intval) + ')'
456                enumerators.append((field.name, field.enumval))
457
458            # No match was found, try to return as flags
459            enumerators.sort(key=lambda x: x[1])
460            flags = []
461            v = intval
462            found = False
463            for (name, value) in enumerators:
464                if v & value != 0:
465                    flags.append(name)
466                    v = v & ~value
467                    found = True
468            if not found or v != 0:
469                # Leftover value
470                flags.append('unknown: %d' % v)
471            return '(' + " | ".join(flags) + ') (' + (form % intval) + ')'
472        except:
473            pass
474        return form % intval
475
476    def nativeTypeId(self, nativeType):
477        if nativeType and (nativeType.code == gdb.TYPE_CODE_TYPEDEF):
478            return '%s{%s}' % (nativeType, nativeType.strip_typedefs())
479        name = str(nativeType)
480        if len(name) == 0:
481            c = '0'
482        elif name == 'union {...}':
483            c = 'u'
484        elif name.endswith('{...}'):
485            c = 's'
486        else:
487            return name
488        typeId = c + ''.join(['{%s:%s}' % (f.name, self.nativeTypeId(f.type))
489                              for f in nativeType.fields()])
490        return typeId
491
492    def nativeStructAlignment(self, nativeType):
493        #DumperBase.warn('NATIVE ALIGN FOR %s' % nativeType.name)
494        def handleItem(nativeFieldType, align):
495            a = self.fromNativeType(nativeFieldType).alignment()
496            return a if a > align else align
497        align = 1
498        for f in nativeType.fields():
499            align = handleItem(f.type, align)
500        return align
501
502        #except:
503        #    # Happens in the BoostList dumper for a 'const bool'
504        #    # item named 'constant_time_size'. There isn't anything we can do
505        #    # in this case.
506        #   pass
507
508        #yield value.extractField(field)
509
510    def memberFromNativeFieldAndValue(self, nativeField, nativeValue, fieldName, value):
511        nativeMember = self.nativeMemberFromField(nativeValue, nativeField)
512        if nativeMember is None:
513            val = self.Value(self)
514            val.name = fieldName
515            val._type = self.fromNativeType(nativeField.type)
516            val.lIsInScope = False
517            return val
518        val = self.fromNativeValue(nativeMember)
519        nativeFieldType = nativeField.type.unqualified()
520        if nativeField.bitsize:
521            val.lvalue = int(nativeMember)
522            val.laddress = None
523            fieldType = self.fromNativeType(nativeFieldType)
524            val._type = self.createBitfieldType(fieldType, nativeField.bitsize)
525        val.isBaseClass = nativeField.is_base_class
526        val.name = fieldName
527        return val
528
529    def nativeMemberFromField(self, nativeValue, nativeField):
530        if nativeField.is_base_class:
531            return nativeValue.cast(nativeField.type)
532        try:
533            return nativeValue[nativeField]
534        except:
535            pass
536        try:
537            return nativeValue[nativeField.name]
538        except:
539            pass
540        return None
541
542    def listMembers(self, value, nativeType):
543        nativeValue = value.nativeValue
544
545        anonNumber = 0
546
547        #DumperBase.warn('LISTING FIELDS FOR %s' % nativeType)
548        for nativeField in nativeType.fields():
549            fieldName = nativeField.name
550            # Something without a name.
551            # Anonymous union? We need a dummy name to distinguish
552            # multiple anonymous unions in the struct.
553            # Since GDB commit b5b08fb4 anonymous structs get also reported
554            # with a 'None' name.
555            if fieldName is None or len(fieldName) == 0:
556                # Something without a name.
557                # Anonymous union? We need a dummy name to distinguish
558                # multiple anonymous unions in the struct.
559                anonNumber += 1
560                fieldName = '#%s' % anonNumber
561            #DumperBase.warn('FIELD: %s' % fieldName)
562            # hasattr(nativeField, 'bitpos') == False indicates a static field,
563            # but if we have access to a nativeValue .fromNativeField will
564            # also succeed. We essentially skip only static members from
565            # artificial values, like array members constructed from address.
566            if hasattr(nativeField, 'bitpos') or nativeValue is not None:
567                yield self.fromNativeField(nativeField, nativeValue, fieldName)
568
569    def fromNativeField(self, nativeField, nativeValue, fieldName):
570        nativeFieldType = nativeField.type.unqualified()
571        #DumperBase.warn('  TYPE: %s' % nativeFieldType)
572        #DumperBase.warn('  TYPEID: %s' % self.nativeTypeId(nativeFieldType))
573
574        if hasattr(nativeField, 'bitpos'):
575            bitpos = nativeField.bitpos
576        else:
577            bitpos = 0
578
579        if hasattr(nativeField, 'bitsize') and nativeField.bitsize != 0:
580            bitsize = nativeField.bitsize
581        else:
582            bitsize = 8 * nativeFieldType.sizeof
583
584        fieldType = self.fromNativeType(nativeFieldType)
585        if bitsize != nativeFieldType.sizeof * 8:
586            fieldType = self.createBitfieldType(fieldType, bitsize)
587        else:
588            fieldType = fieldType
589
590        if nativeValue is None:
591            extractor = None
592        else:
593            extractor = lambda value, \
594                capturedNativeField = nativeField, \
595                capturedNativeValue = nativeValue, \
596                capturedFieldName = fieldName: \
597                self.memberFromNativeFieldAndValue(capturedNativeField,
598                                                   capturedNativeValue,
599                                                   capturedFieldName,
600                                                   value)
601
602        #DumperBase.warn("FOUND NATIVE FIELD: %s bitpos: %s" % (fieldName, bitpos))
603        return self.Field(dumper=self, name=fieldName, isBase=nativeField.is_base_class,
604                          bitsize=bitsize, bitpos=bitpos, type=fieldType,
605                          extractor=extractor)
606
607    def listLocals(self, partialVar):
608        frame = gdb.selected_frame()
609
610        try:
611            block = frame.block()
612            #DumperBase.warn('BLOCK: %s ' % block)
613        except RuntimeError as error:
614            #DumperBase.warn('BLOCK IN FRAME NOT ACCESSIBLE: %s' % error)
615            return []
616        except:
617            self.warn('BLOCK NOT ACCESSIBLE FOR UNKNOWN REASONS')
618            return []
619
620        items = []
621        shadowed = {}
622        while True:
623            if block is None:
624                self.warn("UNEXPECTED 'None' BLOCK")
625                break
626            for symbol in block:
627
628                # Filter out labels etc.
629                if symbol.is_variable or symbol.is_argument:
630                    name = symbol.print_name
631
632                    if name in ('__in_chrg', '__PRETTY_FUNCTION__'):
633                        continue
634
635                    if partialVar is not None and partialVar != name:
636                        continue
637
638                    # 'NotImplementedError: Symbol type not yet supported in
639                    # Python scripts.'
640                    #DumperBase.warn('SYMBOL %s  (%s, %s)): ' % (symbol, name, symbol.name))
641                    if self.passExceptions and not self.isTesting:
642                        nativeValue = frame.read_var(name, block)
643                        value = self.fromFrameValue(nativeValue)
644                        value.name = name
645                        #DumperBase.warn('READ 0: %s' % value.stringify())
646                        items.append(value)
647                        continue
648
649                    try:
650                        # Same as above, but for production.
651                        nativeValue = frame.read_var(name, block)
652                        value = self.fromFrameValue(nativeValue)
653                        value.name = name
654                        #DumperBase.warn('READ 1: %s' % value.stringify())
655                        items.append(value)
656                        continue
657                    except:
658                        pass
659
660                    try:
661                        #DumperBase.warn('READ 2: %s' % item.value)
662                        value = self.fromFrameValue(frame.read_var(name))
663                        value.name = name
664                        items.append(value)
665                        continue
666                    except:
667                        # RuntimeError: happens for
668                        #     void foo() { std::string s; std::wstring w; }
669                        # ValueError: happens for (as of 2010/11/4)
670                        #     a local struct as found e.g. in
671                        #     gcc sources in gcc.c, int execute()
672                        pass
673
674                    try:
675                        #DumperBase.warn('READ 3: %s %s' % (name, item.value))
676                        #DumperBase.warn('ITEM 3: %s' % item.value)
677                        value = self.fromFrameValue(gdb.parse_and_eval(name))
678                        value.name = name
679                        items.append(value)
680                    except:
681                        # Can happen in inlined code (see last line of
682                        # RowPainter::paintChars(): 'RuntimeError:
683                        # No symbol '__val' in current context.\n'
684                        pass
685
686            # The outermost block in a function has the function member
687            # FIXME: check whether this is guaranteed.
688            if block.function is not None:
689                break
690
691            block = block.superblock
692
693        return items
694
695    def reportToken(self, args):
696        pass
697
698    # Hack to avoid QDate* dumper timeouts with GDB 7.4 on 32 bit
699    # due to misaligned %ebx in SSE calls (qstring.cpp:findChar)
700    # This seems to be fixed in 7.9 (or earlier)
701    def canCallLocale(self):
702        return self.ptrSize() == 8
703
704    def fetchVariables(self, args):
705        self.resetStats()
706        self.prepare(args)
707
708        self.isBigEndian = gdb.execute('show endian', to_string=True).find('big endian') > 0
709        self.packCode = '>' if self.isBigEndian else '<'
710
711        (ok, res) = self.tryFetchInterpreterVariables(args)
712        if ok:
713            safePrint(res)
714            return
715
716        self.output += 'data=['
717
718        partialVar = args.get('partialvar', '')
719        isPartial = len(partialVar) > 0
720        partialName = partialVar.split('.')[1].split('@')[0] if isPartial else None
721
722        variables = self.listLocals(partialName)
723        #DumperBase.warn('VARIABLES: %s' % variables)
724
725        # Take care of the return value of the last function call.
726        if len(self.resultVarName) > 0:
727            try:
728                value = self.parseAndEvaluate(self.resultVarName)
729                value.name = self.resultVarName
730                value.iname = 'return.' + self.resultVarName
731                variables.append(value)
732            except:
733                # Don't bother. It's only supplementary information anyway.
734                pass
735
736        self.handleLocals(variables)
737        self.handleWatches(args)
738
739        self.output += '],typeinfo=['
740        for name in self.typesToReport.keys():
741            typeobj = self.typesToReport[name]
742            # Happens e.g. for '(anonymous namespace)::InsertDefOperation'
743            #if not typeobj is None:
744            #    self.output.append('{name="%s",size="%s"}'
745            #        % (self.hexencode(name), typeobj.sizeof))
746        self.output += ']'
747        self.typesToReport = {}
748
749        if self.forceQtNamespace:
750            self.qtNamespaceToReport = self.qtNamespace()
751
752        if self.qtNamespaceToReport:
753            self.output += ',qtnamespace="%s"' % self.qtNamespaceToReport
754            self.qtNamespaceToReport = None
755
756        self.output += ',partial="%d"' % isPartial
757        self.output += ',counts=%s' % self.counts
758        self.output += ',timings=%s' % self.timings
759        self.reportResult(self.output)
760
761    def parseAndEvaluate(self, exp):
762        val = self.nativeParseAndEvaluate(exp)
763        return None if val is None else self.fromNativeValue(val)
764
765    def nativeParseAndEvaluate(self, exp):
766        #DumperBase.warn('EVALUATE "%s"' % exp)
767        try:
768            val = gdb.parse_and_eval(exp)
769            return val
770        except RuntimeError as error:
771            if self.passExceptions:
772                self.warn("Cannot evaluate '%s': %s" % (exp, error))
773            return None
774
775    def callHelper(self, rettype, value, function, args):
776        if self.isWindowsTarget():
777            raise Exception("gdb crashes when calling functions on Windows")
778        # args is a tuple.
779        arg = ''
780        for i in range(len(args)):
781            if i:
782                arg += ','
783            a = args[i]
784            if (':' in a) and not ("'" in a):
785                arg = "'%s'" % a
786            else:
787                arg += a
788
789        #DumperBase.warn('CALL: %s -> %s(%s)' % (value, function, arg))
790        typeName = value.type.name
791        if typeName.find(':') >= 0:
792            typeName = "'" + typeName + "'"
793        # 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
794        #exp = '((class %s*)%s)->%s(%s)' % (typeName, value.laddress, function, arg)
795        addr = value.address()
796        if addr is None:
797            addr = self.pokeValue(value)
798        #DumperBase.warn('PTR: %s -> %s(%s)' % (value, function, addr))
799        exp = '((%s*)0x%x)->%s(%s)' % (typeName, addr, function, arg)
800        #DumperBase.warn('CALL: %s' % exp)
801        result = gdb.parse_and_eval(exp)
802        #DumperBase.warn('  -> %s' % result)
803        res = self.fromNativeValue(result)
804        if value.address() is None:
805            self.releaseValue(addr)
806        return res
807
808    def makeExpression(self, value):
809        typename = '::' + value.type.name
810        #DumperBase.warn('  TYPE: %s' % typename)
811        exp = '(*(%s*)(0x%x))' % (typename, value.address())
812        #DumperBase.warn('  EXP: %s' % exp)
813        return exp
814
815    def makeStdString(init):
816        # Works only for small allocators, but they are usually empty.
817        gdb.execute('set $d=(std::string*)calloc(sizeof(std::string), 2)')
818        gdb.execute('call($d->basic_string("' + init +
819                    '",*(std::allocator<char>*)(1+$d)))')
820        value = gdb.parse_and_eval('$d').dereference()
821        return value
822
823    def pokeValue(self, value):
824        # Allocates inferior memory and copies the contents of value.
825        # Returns a pointer to the copy.
826        # Avoid malloc symbol clash with QVector
827        size = value.type.size()
828        data = value.data()
829        h = self.hexencode(data)
830        #DumperBase.warn('DATA: %s' % h)
831        string = ''.join('\\x' + h[2 * i:2 * i + 2] for i in range(size))
832        exp = '(%s*)memcpy(calloc(%d, 1), "%s", %d)' \
833            % (value.type.name, size, string, size)
834        #DumperBase.warn('EXP: %s' % exp)
835        res = gdb.parse_and_eval(exp)
836        #DumperBase.warn('RES: %s' % res)
837        return toInteger(res)
838
839    def releaseValue(self, address):
840        gdb.parse_and_eval('free(0x%x)' % address)
841
842    def setValue(self, address, typename, value):
843        cmd = 'set {%s}%s=%s' % (typename, address, value)
844        gdb.execute(cmd)
845
846    def setValues(self, address, typename, values):
847        cmd = 'set {%s[%s]}%s={%s}' \
848            % (typename, len(values), address, ','.join(map(str, values)))
849        gdb.execute(cmd)
850
851    def selectedInferior(self):
852        try:
853            # gdb.Inferior is new in gdb 7.2
854            self.cachedInferior = gdb.selected_inferior()
855        except:
856            # Pre gdb 7.4. Right now we don't have more than one inferior anyway.
857            self.cachedInferior = gdb.inferiors()[0]
858
859        # Memoize result.
860        self.selectedInferior = lambda: self.cachedInferior
861        return self.cachedInferior
862
863    def readRawMemory(self, address, size):
864        #DumperBase.warn('READ: %s FROM 0x%x' % (size, address))
865        if address == 0 or size == 0:
866            return bytes()
867        res = self.selectedInferior().read_memory(address, size)
868        return res
869
870    def findStaticMetaObject(self, type):
871        symbolName = type.name + '::staticMetaObject'
872        symbol = gdb.lookup_global_symbol(symbolName, gdb.SYMBOL_VAR_DOMAIN)
873        if not symbol:
874            return 0
875        try:
876            # Older GDB ~7.4 don't have gdb.Symbol.value()
877            return toInteger(symbol.value().address)
878        except:
879            pass
880
881        address = gdb.parse_and_eval("&'%s'" % symbolName)
882        return toInteger(address)
883
884    def isArmArchitecture(self):
885        return 'arm' in gdb.TARGET_CONFIG.lower()
886
887    def isQnxTarget(self):
888        return 'qnx' in gdb.TARGET_CONFIG.lower()
889
890    def isWindowsTarget(self):
891        # We get i686-w64-mingw32
892        return 'mingw' in gdb.TARGET_CONFIG.lower()
893
894    def isMsvcTarget(self):
895        return False
896
897    def prettySymbolByAddress(self, address):
898        try:
899            return str(gdb.parse_and_eval('(void(*))0x%x' % address))
900        except:
901            return '0x%x' % address
902
903    def qtVersionString(self):
904        try:
905            return str(gdb.lookup_symbol('qVersion')[0].value()())
906        except:
907            pass
908        try:
909            ns = self.qtNamespace()
910            return str(gdb.parse_and_eval("((const char*(*)())'%sqVersion')()" % ns))
911        except:
912            pass
913        return None
914
915    def qtVersion(self):
916        try:
917            # Only available with Qt 5.3+
918            qtversion = int(str(gdb.parse_and_eval('((void**)&qtHookData)[2]')), 16)
919            self.qtVersion = lambda: qtversion
920            return qtversion
921        except:
922            pass
923
924        try:
925            version = self.qtVersionString()
926            (major, minor, patch) = version[version.find('"') + 1:version.rfind('"')].split('.')
927            qtversion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch)
928            self.qtVersion = lambda: qtversion
929            return qtversion
930        except:
931            # Use fallback until we have a better answer.
932            return self.fallbackQtVersion
933
934    def createSpecialBreakpoints(self, args):
935        self.specialBreakpoints = []
936
937        def newSpecial(spec):
938            # GDB < 8.1 does not have the 'qualified' parameter here,
939            # GDB >= 8.1 applies some generous pattern matching, hitting
940            # e.g. also Foo::abort() when asking for '::abort'
941            class Pre81SpecialBreakpoint(gdb.Breakpoint):
942                def __init__(self, spec):
943                    super(Pre81SpecialBreakpoint, self).__init__(spec,
944                                                                 gdb.BP_BREAKPOINT, internal=True)
945                    self.spec = spec
946
947                def stop(self):
948                    print("Breakpoint on '%s' hit." % self.spec)
949                    return True
950
951            class SpecialBreakpoint(gdb.Breakpoint):
952                def __init__(self, spec):
953                    super(SpecialBreakpoint, self).__init__(spec,
954                                                            gdb.BP_BREAKPOINT,
955                                                            internal=True,
956                                                            qualified=True)
957                    self.spec = spec
958
959                def stop(self):
960                    print("Breakpoint on '%s' hit." % self.spec)
961                    return True
962
963            try:
964                return SpecialBreakpoint(spec)
965            except:
966                return Pre81SpecialBreakpoint(spec)
967
968        # FIXME: ns is accessed too early. gdb.Breakpoint() has no
969        # 'rbreak' replacement, and breakpoints created with
970        # 'gdb.execute('rbreak...') cannot be made invisible.
971        # So let's ignore the existing of namespaced builds for this
972        # fringe feature here for now.
973        ns = self.qtNamespace()
974        if args.get('breakonabort', 0):
975            self.specialBreakpoints.append(newSpecial('abort'))
976
977        if args.get('breakonwarning', 0):
978            self.specialBreakpoints.append(newSpecial(ns + 'qWarning'))
979            self.specialBreakpoints.append(newSpecial(ns + 'QMessageLogger::warning'))
980
981        if args.get('breakonfatal', 0):
982            self.specialBreakpoints.append(newSpecial(ns + 'qFatal'))
983            self.specialBreakpoints.append(newSpecial(ns + 'QMessageLogger::fatal'))
984
985    #def threadname(self, maximalStackDepth, objectPrivateType):
986    #    e = gdb.selected_frame()
987    #    out = ''
988    #    ns = self.qtNamespace()
989    #    while True:
990    #        maximalStackDepth -= 1
991    #        if maximalStackDepth < 0:
992    #            break
993    #        e = e.older()
994    #        if e == None or e.name() == None:
995    #            break
996    #        if e.name() in (ns + 'QThreadPrivate::start', '_ZN14QThreadPrivate5startEPv@4'):
997    #            try:
998    #                thrptr = e.read_var('thr').dereference()
999    #                d_ptr = thrptr['d_ptr']['d'].cast(objectPrivateType).dereference()
1000    #                try:
1001    #                    objectName = d_ptr['objectName']
1002    #                except: # Qt 5
1003    #                    p = d_ptr['extraData']
1004    #                    if not self.isNull(p):
1005    #                        objectName = p.dereference()['objectName']
1006    #                if not objectName is None:
1007    #                    (data, size, alloc) = self.stringData(objectName)
1008    #                    if size > 0:
1009    #                         s = self.readMemory(data, 2 * size)
1010    #
1011    #                thread = gdb.selected_thread()
1012    #                inner = '{valueencoded="uf16:2:0",id="'
1013    #                inner += str(thread.num) + '",value="'
1014    #                inner += s
1015    #                #inner += self.encodeString(objectName)
1016    #                inner += '"},'
1017    #
1018    #                out += inner
1019    #            except:
1020    #                pass
1021    #    return out
1022
1023    def threadnames(self, maximalStackDepth):
1024        # FIXME: This needs a proper implementation for MinGW, and only there.
1025        # Linux, Mac and QNX mirror the objectName() to the underlying threads,
1026        # so we get the names already as part of the -thread-info output.
1027        return '[]'
1028        #out = '['
1029        #oldthread = gdb.selected_thread()
1030        #if oldthread:
1031        #    try:
1032        #        objectPrivateType = gdb.lookup_type(ns + 'QObjectPrivate').pointer()
1033        #        inferior = self.selectedInferior()
1034        #        for thread in inferior.threads():
1035        #            thread.switch()
1036        #            out += self.threadname(maximalStackDepth, objectPrivateType)
1037        #    except:
1038        #        pass
1039        #    oldthread.switch()
1040        #return out + ']'
1041
1042    def importPlainDumper(self, printer):
1043        name = printer.name.replace('::', '__')
1044        self.qqDumpers[name] = PlainDumper(printer)
1045        self.qqFormats[name] = ''
1046
1047    def importPlainDumpersForObj(self, obj):
1048        for printers in obj.pretty_printers + gdb.pretty_printers:
1049            for printer in printers.subprinters:
1050                self.importPlainDumper(printer)
1051
1052    def importPlainDumpers(self):
1053        for obj in gdb.objfiles():
1054            self.importPlainDumpersForObj(obj)
1055
1056    def qtNamespace(self):
1057        # This function is replaced by handleQtCoreLoaded()
1058        return ''
1059
1060    def findSymbol(self, symbolName):
1061        try:
1062            return toInteger(gdb.parse_and_eval("(size_t)&'%s'" % symbolName))
1063        except:
1064            return 0
1065
1066    def handleNewObjectFile(self, objfile):
1067        name = objfile.filename
1068        if self.isWindowsTarget():
1069            qtCoreMatch = re.match(r'.*Qt[56]?Core[^/.]*d?\.dll', name)
1070        else:
1071            qtCoreMatch = re.match(r'.*/libQt[56]?Core[^/.]*\.so', name)
1072
1073        if qtCoreMatch is not None:
1074            self.addDebugLibs(objfile)
1075            self.handleQtCoreLoaded(objfile)
1076
1077        if self.usePlainDumpers:
1078            self.importPlainDumpersForObj(objfile)
1079
1080    def addDebugLibs(self, objfile):
1081        # The directory where separate debug symbols are searched for
1082        # is "/usr/lib/debug".
1083        try:
1084            cooked = gdb.execute('show debug-file-directory', to_string=True)
1085            clean = cooked.split('"')[1]
1086            newdir = '/'.join(objfile.filename.split('/')[:-1])
1087            gdb.execute('set debug-file-directory %s:%s' % (clean, newdir))
1088        except:
1089            pass
1090
1091    def handleQtCoreLoaded(self, objfile):
1092        fd, tmppath = tempfile.mkstemp()
1093        os.close(fd)
1094        try:
1095            cmd = 'maint print msymbols -objfile "%s" -- %s' % (objfile.filename, tmppath)
1096            symbols = gdb.execute(cmd, to_string=True)
1097        except:
1098            try:
1099                # command syntax depends on gdb version - below is gdb < 8
1100                cmd = 'maint print msymbols %s "%s"' % (tmppath, objfile.filename)
1101                symbols = gdb.execute(cmd, to_string=True)
1102            except:
1103                pass
1104        ns = ''
1105        with open(tmppath) as f:
1106            for line in f:
1107                if line.find('msgHandlerGrabbed ') >= 0:
1108                    # [11] b 0x7ffff683c000 _ZN4MynsL17msgHandlerGrabbedE
1109                    # section .tbss Myns::msgHandlerGrabbed  qlogging.cpp
1110                    ns = re.split(r'_ZN?(\d*)(\w*)L17msgHandlerGrabbedE? ', line)[2]
1111                    if len(ns):
1112                        ns += '::'
1113                    break
1114                if line.find('currentThreadData ') >= 0:
1115                    # [ 0] b 0x7ffff67d3000 _ZN2UUL17currentThreadDataE
1116                    # section .tbss  UU::currentThreadData qthread_unix.cpp\\n
1117                    ns = re.split(r'_ZN?(\d*)(\w*)L17currentThreadDataE? ', line)[2]
1118                    if len(ns):
1119                        ns += '::'
1120                    break
1121        os.remove(tmppath)
1122
1123        lenns = len(ns)
1124        strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else ''
1125
1126        if lenns:
1127            # This might be wrong, but we can't do better: We found
1128            # a libQt5Core and could not extract a namespace.
1129            # The best guess is that there isn't any.
1130            self.qtNamespaceToReport = ns
1131            self.qtNamespace = lambda: ns
1132
1133            sym = '_ZN%s7QObject11customEventEPNS_6QEventE' % strns
1134        else:
1135            sym = '_ZN7QObject11customEventEP6QEvent'
1136        self.qtCustomEventFunc = self.findSymbol(sym)
1137
1138        sym += '@plt'
1139        self.qtCustomEventPltFunc = self.findSymbol(sym)
1140
1141        sym = '_ZNK%s7QObject8propertyEPKc' % strns
1142        if not self.isWindowsTarget(): # prevent calling the property function on windows
1143            self.qtPropertyFunc = self.findSymbol(sym)
1144
1145    def assignValue(self, args):
1146        typeName = self.hexdecode(args['type'])
1147        expr = self.hexdecode(args['expr'])
1148        value = self.hexdecode(args['value'])
1149        simpleType = int(args['simpleType'])
1150        ns = self.qtNamespace()
1151        if typeName.startswith(ns):
1152            typeName = typeName[len(ns):]
1153        typeName = typeName.replace('::', '__')
1154        pos = typeName.find('<')
1155        if pos != -1:
1156            typeName = typeName[0:pos]
1157        if typeName in self.qqEditable and not simpleType:
1158            #self.qqEditable[typeName](self, expr, value)
1159            expr = self.parseAndEvaluate(expr)
1160            self.qqEditable[typeName](self, expr, value)
1161        else:
1162            cmd = 'set variable (%s)=%s' % (expr, value)
1163            gdb.execute(cmd)
1164
1165    def appendSolibSearchPath(self, args):
1166        new = list(map(self.hexdecode, args['path']))
1167        old = [gdb.parameter('solib-search-path')]
1168        joined = os.pathsep.join([item for item in old + new if item != ''])
1169        gdb.execute('set solib-search-path %s' % joined)
1170
1171    def watchPoint(self, args):
1172        self.reportToken(args)
1173        ns = self.qtNamespace()
1174        lenns = len(ns)
1175        strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else ''
1176        sym = '_ZN%s12QApplication8widgetAtEii' % strns
1177        expr = '%s(%s,%s)' % (sym, args['x'], args['y'])
1178        res = self.parseAndEvaluate(expr)
1179        p = 0 if res is None else res.pointer()
1180        n = ("'%sQWidget'" % ns) if lenns else 'QWidget'
1181        self.reportResult('selected="0x%x",expr="(%s*)0x%x"' % (p, n, p), args)
1182
1183    def nativeValueDereferencePointer(self, value):
1184        # This is actually pretty expensive, up to 100ms.
1185        deref = value.nativeValue.dereference()
1186        if self.useDynamicType:
1187            deref = deref.cast(deref.dynamic_type)
1188        return self.fromNativeValue(deref)
1189
1190    def nativeValueDereferenceReference(self, value):
1191        nativeValue = value.nativeValue
1192        return self.fromNativeValue(nativeValue.cast(nativeValue.type.target()))
1193
1194    def nativeDynamicTypeName(self, address, baseType):
1195        # Needed for Gdb13393 test.
1196        nativeType = self.lookupNativeType(baseType.name)
1197        if nativeType is None:
1198            return None
1199        nativeTypePointer = nativeType.pointer()
1200        nativeValue = gdb.Value(address).cast(nativeTypePointer).dereference()
1201        val = nativeValue.cast(nativeValue.dynamic_type)
1202        return str(val.type)
1203        #try:
1204        #    vtbl = gdb.execute('info symbol {%s*}0x%x' % (baseType.name, address), to_string = True)
1205        #except:
1206        #    return None
1207        #pos1 = vtbl.find('vtable ')
1208        #if pos1 == -1:
1209        #    return None
1210        #pos1 += 11
1211        #pos2 = vtbl.find(' +', pos1)
1212        #if pos2 == -1:
1213        #    return None
1214        #return vtbl[pos1 : pos2]
1215
1216    def nativeDynamicType(self, address, baseType):
1217        # Needed for Gdb13393 test.
1218        nativeType = self.lookupNativeType(baseType.name)
1219        if nativeType is None:
1220            return baseType
1221        nativeTypePointer = nativeType.pointer()
1222        nativeValue = gdb.Value(address).cast(nativeTypePointer).dereference()
1223        return self.fromNativeType(nativeValue.dynamic_type)
1224
1225    def enumExpression(self, enumType, enumValue):
1226        return self.qtNamespace() + 'Qt::' + enumValue
1227
1228    def lookupNativeType(self, typeName):
1229        nativeType = self.lookupNativeTypeHelper(typeName)
1230        if nativeType is not None:
1231            self.check(isinstance(nativeType, gdb.Type))
1232        return nativeType
1233
1234    def lookupNativeTypeHelper(self, typeName):
1235        typeobj = self.typeCache.get(typeName)
1236        #DumperBase.warn('LOOKUP 1: %s -> %s' % (typeName, typeobj))
1237        if typeobj is not None:
1238            return typeobj
1239
1240        if typeName == 'void':
1241            typeobj = gdb.lookup_type(typeName)
1242            self.typeCache[typeName] = typeobj
1243            self.typesToReport[typeName] = typeobj
1244            return typeobj
1245
1246        #try:
1247        #    typeobj = gdb.parse_and_eval('{%s}&main' % typeName).typeobj
1248        #    if not typeobj is None:
1249        #        self.typeCache[typeName] = typeobj
1250        #        self.typesToReport[typeName] = typeobj
1251        #        return typeobj
1252        #except:
1253        #    pass
1254
1255        # See http://sourceware.org/bugzilla/show_bug.cgi?id=13269
1256        # gcc produces '{anonymous}', gdb '(anonymous namespace)'
1257        # '<unnamed>' has been seen too. The only thing gdb
1258        # understands when reading things back is '(anonymous namespace)'
1259        if typeName.find('{anonymous}') != -1:
1260            ts = typeName
1261            ts = ts.replace('{anonymous}', '(anonymous namespace)')
1262            typeobj = self.lookupNativeType(ts)
1263            if typeobj is not None:
1264                self.typeCache[typeName] = typeobj
1265                self.typesToReport[typeName] = typeobj
1266                return typeobj
1267
1268        #DumperBase.warn(" RESULT FOR 7.2: '%s': %s" % (typeName, typeobj))
1269
1270        # This part should only trigger for
1271        # gdb 7.1 for types with namespace separators.
1272        # And anonymous namespaces.
1273
1274        ts = typeName
1275        while True:
1276            if ts.startswith('class '):
1277                ts = ts[6:]
1278            elif ts.startswith('struct '):
1279                ts = ts[7:]
1280            elif ts.startswith('const '):
1281                ts = ts[6:]
1282            elif ts.startswith('volatile '):
1283                ts = ts[9:]
1284            elif ts.startswith('enum '):
1285                ts = ts[5:]
1286            elif ts.endswith(' const'):
1287                ts = ts[:-6]
1288            elif ts.endswith(' volatile'):
1289                ts = ts[:-9]
1290            elif ts.endswith('*const'):
1291                ts = ts[:-5]
1292            elif ts.endswith('*volatile'):
1293                ts = ts[:-8]
1294            else:
1295                break
1296
1297        if ts.endswith('*'):
1298            typeobj = self.lookupNativeType(ts[0:-1])
1299            if typeobj is not None:
1300                typeobj = typeobj.pointer()
1301                self.typeCache[typeName] = typeobj
1302                self.typesToReport[typeName] = typeobj
1303                return typeobj
1304
1305        try:
1306            #DumperBase.warn("LOOKING UP 1 '%s'" % ts)
1307            typeobj = gdb.lookup_type(ts)
1308        except RuntimeError as error:
1309            #DumperBase.warn("LOOKING UP 2 '%s' ERROR %s" % (ts, error))
1310            # See http://sourceware.org/bugzilla/show_bug.cgi?id=11912
1311            exp = "(class '%s'*)0" % ts
1312            try:
1313                typeobj = self.parse_and_eval(exp).type.target()
1314                #DumperBase.warn("LOOKING UP 3 '%s'" % typeobj)
1315            except:
1316                # Can throw 'RuntimeError: No type named class Foo.'
1317                pass
1318        except:
1319            #DumperBase.warn("LOOKING UP '%s' FAILED" % ts)
1320            pass
1321
1322        if typeobj is not None:
1323            #DumperBase.warn('CACHING: %s' % typeobj)
1324            self.typeCache[typeName] = typeobj
1325            self.typesToReport[typeName] = typeobj
1326
1327        # This could still be None as gdb.lookup_type('char[3]') generates
1328        # 'RuntimeError: No type named char[3]'
1329        #self.typeCache[typeName] = typeobj
1330        #self.typesToReport[typeName] = typeobj
1331        return typeobj
1332
1333    def doContinue(self):
1334        gdb.execute('continue')
1335
1336    def fetchStack(self, args):
1337
1338        def fromNativePath(string):
1339            return string.replace('\\', '/')
1340
1341        extraQml = int(args.get('extraqml', '0'))
1342        limit = int(args['limit'])
1343        if limit <= 0:
1344            limit = 10000
1345
1346        self.prepare(args)
1347        self.output = ''
1348
1349        i = 0
1350        if extraQml:
1351            frame = gdb.newest_frame()
1352            ns = self.qtNamespace()
1353            needle = self.qtNamespace() + 'QV4::ExecutionEngine'
1354            pats = [
1355                    '{0}qt_v4StackTraceForEngine((void*)0x{1:x})',
1356                    '{0}qt_v4StackTrace((({0}QV4::ExecutionEngine *)0x{1:x})->currentContext())',
1357                    '{0}qt_v4StackTrace((({0}QV4::ExecutionEngine *)0x{1:x})->currentContext)',
1358                   ]
1359            done = False
1360            while i < limit and frame and not done:
1361                block = None
1362                try:
1363                    block = frame.block()
1364                except:
1365                    pass
1366                if block is not None:
1367                    for symbol in block:
1368                        if symbol.is_variable or symbol.is_argument:
1369                            value = symbol.value(frame)
1370                            typeobj = value.type
1371                            if typeobj.code == gdb.TYPE_CODE_PTR:
1372                                dereftype = typeobj.target().unqualified()
1373                                if dereftype.name == needle:
1374                                    addr = toInteger(value)
1375                                    res = None
1376                                    for pat in pats:
1377                                        try:
1378                                            expr = pat.format(ns, addr)
1379                                            res = str(gdb.parse_and_eval(expr))
1380                                            break
1381                                        except:
1382                                            continue
1383
1384                                    if res is None:
1385                                        done = True
1386                                        break
1387
1388                                    pos = res.find('"stack=[')
1389                                    if pos != -1:
1390                                        res = res[pos + 8:-2]
1391                                        res = res.replace('\\\"', '\"')
1392                                        res = res.replace('func=', 'function=')
1393                                        self.put(res)
1394                                        done = True
1395                                        break
1396                frame = frame.older()
1397                i += 1
1398
1399        frame = gdb.newest_frame()
1400        self.currentCallContext = None
1401        while i < limit and frame:
1402            with OutputSaver(self):
1403                name = frame.name()
1404                functionName = '??' if name is None else name
1405                fileName = ''
1406                objfile = ''
1407                symtab = ''
1408                pc = frame.pc()
1409                sal = frame.find_sal()
1410                line = -1
1411                if sal:
1412                    line = sal.line
1413                    symtab = sal.symtab
1414                    if symtab is not None:
1415                        objfile = fromNativePath(symtab.objfile.filename)
1416                        fullname = symtab.fullname()
1417                        if fullname is None:
1418                            fileName = ''
1419                        else:
1420                            fileName = fromNativePath(fullname)
1421
1422                if self.nativeMixed and functionName == 'qt_qmlDebugMessageAvailable':
1423                    interpreterStack = self.extractInterpreterStack()
1424                    #print('EXTRACTED INTEPRETER STACK: %s' % interpreterStack)
1425                    for interpreterFrame in interpreterStack.get('frames', []):
1426                        function = interpreterFrame.get('function', '')
1427                        fileName = interpreterFrame.get('file', '')
1428                        language = interpreterFrame.get('language', '')
1429                        lineNumber = interpreterFrame.get('line', 0)
1430                        context = interpreterFrame.get('context', 0)
1431
1432                        self.put(('frame={function="%s",file="%s",'
1433                                  'line="%s",language="%s",context="%s"}')
1434                                 % (function, self.hexencode(fileName), lineNumber, language, context))
1435
1436                    if False and self.isInternalInterpreterFrame(functionName):
1437                        frame = frame.older()
1438                        self.put(('frame={address="0x%x",function="%s",'
1439                                  'file="%s",line="%s",'
1440                                  'module="%s",language="c",usable="0"}') %
1441                                 (pc, functionName, fileName, line, objfile))
1442                        i += 1
1443                        frame = frame.older()
1444                        continue
1445
1446                self.put(('frame={level="%s",address="0x%x",function="%s",'
1447                          'file="%s",line="%s",module="%s",language="c"}') %
1448                         (i, pc, functionName, fileName, line, objfile))
1449
1450            frame = frame.older()
1451            i += 1
1452        self.reportResult('stack={frames=[' + self.output + ']}')
1453
1454    def createResolvePendingBreakpointsHookBreakpoint(self, args):
1455        class Resolver(gdb.Breakpoint):
1456            def __init__(self, dumper, args):
1457                self.dumper = dumper
1458                self.args = args
1459                spec = 'qt_qmlDebugConnectorOpen'
1460                super(Resolver, self).\
1461                    __init__(spec, gdb.BP_BREAKPOINT, internal=True, temporary=False)
1462
1463            def stop(self):
1464                self.dumper.resolvePendingInterpreterBreakpoint(args)
1465                self.enabled = False
1466                return False
1467
1468        self.interpreterBreakpointResolvers.append(Resolver(self, args))
1469
1470    def exitGdb(self, _):
1471        gdb.execute('quit')
1472
1473    def reportResult(self, result, args={}):
1474        print('result={token="%s",%s}' % (args.get("token", 0), result))
1475
1476    def profile1(self, args):
1477        '''Internal profiling'''
1478        import cProfile
1479        tempDir = tempfile.gettempdir() + '/bbprof'
1480        cProfile.run('theDumper.fetchVariables(%s)' % args, tempDir)
1481        import pstats
1482        pstats.Stats(tempDir).sort_stats('time').print_stats()
1483
1484    def profile2(self, args):
1485        import timeit
1486        print(timeit.repeat('theDumper.fetchVariables(%s)' % args,
1487                            'from __main__ import theDumper', number=10))
1488
1489    def tracepointModified(self, tp):
1490        self.tpExpressions = {}
1491        self.tpExpressionWarnings = []
1492        s = self.resultToMi(tp.dicts())
1493        def handler():
1494            print("tracepointmodified=%s" % s)
1495        gdb.post_event(handler)
1496
1497    def tracepointHit(self, tp, result):
1498        expressions = '{' + ','.join(["%s=%s" % (k,v) for k,v in self.tpExpressions.items()]) + '}'
1499        warnings = []
1500        if 'warning' in result.keys():
1501            warnings.append(result.pop('warning'))
1502        warnings += self.tpExpressionWarnings
1503        r = self.resultToMi(result)
1504        w = self.resultToMi(warnings)
1505        def handler():
1506            print("tracepointhit={result=%s,expressions=%s,warnings=%s}" % (r, expressions, w))
1507        gdb.post_event(handler)
1508
1509    def tracepointExpression(self, tp, expression, value, args):
1510        key = "x" + str(len(self.tpExpressions))
1511        if (isinstance(value, gdb.Value)):
1512            try:
1513                val = self.fromNativeValue(value)
1514                self.prepare(args)
1515                with TopLevelItem(self, expression):
1516                    self.putItem(val)
1517                self.tpExpressions[key] = self.output
1518            except Exception as e:
1519                self.tpExpressions[key] = '"<N/A>"'
1520                self.tpExpressionWarnings.append(str(e))
1521        elif (isinstance(value, Exception)):
1522            self.tpExpressions[key] = '"<N/A>"'
1523            self.tpExpressionWarnings.append(str(value))
1524        else:
1525            self.tpExpressions[key] = '"<N/A>"'
1526            self.tpExpressionWarnings.append('Unknown expression value type')
1527        return key
1528
1529    def createTracepoint(self, args):
1530        """
1531        Creates a tracepoint
1532        """
1533        tp = GDBTracepoint.create(args,
1534            onModified=self.tracepointModified,
1535            onHit=self.tracepointHit,
1536            onExpression=lambda tp, expr, val: self.tracepointExpression(tp, expr, val, args))
1537        self.reportResult("tracepoint=%s" % self.resultToMi(tp.dicts()), args)
1538class CliDumper(Dumper):
1539    def __init__(self):
1540        Dumper.__init__(self)
1541        self.childrenPrefix = '['
1542        self.chidrenSuffix = '] '
1543        self.indent = 0
1544        self.isCli = True
1545        self.setupDumpers({})
1546
1547    def put(self, line):
1548        if self.output.endswith('\n'):
1549            self.output = self.output[0:-1]
1550        self.output += line
1551
1552    def putNumChild(self, numchild):
1553        pass
1554
1555    def putOriginalAddress(self, address):
1556        pass
1557
1558    def fetchVariable(self, line):
1559        # HACK: Currently, the response to the QtCore loading is completely
1560        # eaten by theDumper, so copy the results here. Better would be
1561        # some shared component.
1562        self.qtCustomEventFunc = theDumper.qtCustomEventFunc
1563        self.qtCustomEventPltFunc = theDumper.qtCustomEventPltFunc
1564        self.qtPropertyFunc = theDumper.qtPropertyFunc
1565
1566        names = line.split(' ')
1567        name = names[0]
1568
1569        toExpand = set()
1570        for n in names:
1571            while n:
1572                toExpand.add(n)
1573                n = n[0:n.rfind('.')]
1574
1575        args = {}
1576        args['fancy'] = 1
1577        args['passexceptions'] = 1
1578        args['autoderef'] = 1
1579        args['qobjectnames'] = 1
1580        args['varlist'] = name
1581        args['expanded'] = toExpand
1582        self.expandableINames = set()
1583        self.prepare(args)
1584
1585        self.output = name + ' = '
1586        value = self.parseAndEvaluate(name)
1587        with TopLevelItem(self, name):
1588            self.putItem(value)
1589
1590        if not self.expandableINames:
1591            return self.output + '\n\nNo drill down available.\n'
1592
1593        pattern = ' pp ' + name + ' ' + '%s'
1594        return (self.output
1595                + '\n\nDrill down:\n   '
1596                + '\n   '.join(pattern % x for x in self.expandableINames)
1597                + '\n')
1598
1599
1600# Global instances.
1601theDumper = Dumper()
1602theCliDumper = CliDumper()
1603
1604
1605######################################################################
1606#
1607# ThreadNames Command
1608#
1609#######################################################################
1610
1611def threadnames(arg):
1612    return theDumper.threadnames(int(arg))
1613
1614
1615registerCommand('threadnames', threadnames)
1616
1617#######################################################################
1618#
1619# Native Mixed
1620#
1621#######################################################################
1622
1623
1624class InterpreterMessageBreakpoint(gdb.Breakpoint):
1625    def __init__(self):
1626        spec = 'qt_qmlDebugMessageAvailable'
1627        super(InterpreterMessageBreakpoint, self).\
1628            __init__(spec, gdb.BP_BREAKPOINT, internal=True)
1629
1630    def stop(self):
1631        print('Interpreter event received.')
1632        return theDumper.handleInterpreterMessage()
1633
1634
1635#######################################################################
1636#
1637# Shared objects
1638#
1639#######################################################################
1640
1641def new_objfile_handler(event):
1642    return theDumper.handleNewObjectFile(event.new_objfile)
1643
1644
1645gdb.events.new_objfile.connect(new_objfile_handler)
1646
1647
1648#InterpreterMessageBreakpoint()
1649