Files
qt-creator/share/qtcreator/debugger/cdbbridge.py

299 lines
9.6 KiB
Python
Raw Normal View History

############################################################################
#
# Copyright (C) 2016 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# This file is part of Qt Creator.
#
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
############################################################################
import inspect
import os
import sys
import cdbext
sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))
from dumper import *
class FakeVoidType(cdbext.Type):
def __init__(self, name , dumper):
cdbext.Type.__init__(self)
self.typeName = name.strip()
self.dumper = dumper
def name(self):
return self.typeName
def bitsize(self):
return 0 if self.typeName == 'void' else self.dumper.ptrSize() * 8
def code(self):
if self.typeName.endswith('*'):
return TypeCodePointer
if self.typeName.endswith(']'):
return TypeCodeArray
return TypeCodeVoid
def unqualified(self):
return self
def target(self):
code = self.code()
if code == TypeCodePointer:
return FakeVoidType(self.typeName[:-1], self.dumper)
if code == TypeCodeVoid:
return self
try:
return FakeVoidType(self.typeName[:self.typeName.rindex('[')], self.dumper)
except:
return FakeVoidType('void', self.dumper)
def stripTypedef(self):
return self
def fields(self):
return []
def templateArgument(self, pos, numeric):
return None
def templateArguments(self):
return []
class Dumper(DumperBase):
def __init__(self):
DumperBase.__init__(self)
self.outputLock = threading.Lock()
self.isCdb = True
def fromNativeValue(self, nativeValue):
val = self.Value(self)
val.nativeValue = nativeValue
val.type = self.fromNativeType(nativeValue.type())
val.lIsInScope = True
val.laddress = nativeValue.address()
return val
def nativeTypeId(self, nativeType):
self.check(isinstance(nativeType, cdbext.Type))
name = nativeType.name()
if name is None or len(name) == 0:
c = '0'
elif name == 'struct {...}':
c = 's'
elif name == 'union {...}':
c = 'u'
else:
return name
typeId = c + ''.join(['{%s:%s}' % (f.name(), self.nativeTypeId(f.type())) for f in nativeType.fields()])
return typeId
def fromNativeType(self, nativeType):
self.check(isinstance(nativeType, cdbext.Type))
code = nativeType.code()
if code == TypeCodePointer:
targetType = self.fromNativeType(nativeType.target().unqualified())
return self.createPointerType(targetType)
if code == TypeCodeArray:
nativeTargetType = nativeType.target().unqualified()
targetType = self.fromNativeType(nativeTargetType)
count = nativeType.bitsize() // nativeTargetType.bitsize()
return self.createArrayType(targetType, count)
typeId = self.nativeTypeId(nativeType)
if self.typeData.get(typeId, None) is None:
tdata = self.TypeData(self)
tdata.name = nativeType.name()
tdata.typeId = typeId
tdata.lbitsize = nativeType.bitsize()
tdata.code = code
self.registerType(typeId, tdata) # Prevent recursion in fields.
tdata.lfields = self.listFields(nativeType, self.Type(self, typeId))
tdata.templateArguments = self.listTemplateParameters(nativeType)
self.registerType(typeId, tdata) # Fix up fields and template args
return self.Type(self, typeId)
def listFields(self, nativeType, parentType):
fields = []
if not nativeType.code() == TypeCodeStruct:
return fields
nativeIndex = 0
nativeFields = nativeType.fields()
for nativeField in nativeFields:
field = self.Field(self)
fieldType = nativeField.type().unqualified()
if fieldType is None:
fieldType = FakeVoidType('void', self)
field.ltype = self.fromNativeType(fieldType)
field.parentType = parentType
field.name = nativeField.name()
field.isBaseClass = False
field.lbitpos = nativeField.bitpos()
field.lbitsize = nativeField.bitsize()
field.nativeIndex = nativeIndex
#warn("FIELD RESULT: %s" % field)
fields.append(field)
nativeIndex += 1
return fields
def listTemplateParameters(self, nativeType):
targs = []
for targ in nativeType.templateArguments():
if isinstance(targ, str):
if self.typeData.get(targ, None) is None:
targs.append(self.lookupType(targ))
else:
targs.append(self.Type(self, targ))
elif isinstance(targ, int):
value = self.Value(self)
value.type = self.lookupType('int')
value.ldata = targ.to_bytes(4, sys.byteorder)
targs.append(value)
else:
error('CDBCRAP %s' % type(targ))
return targs
def nativeTypeEnumDisplay(self, nativeType, intval):
# TODO: generate fake value
return None
def enumExpression(self, enumType, enumValue):
ns = self.qtNamespace()
return ns + "Qt::" + enumType + "(" \
+ ns + "Qt::" + enumType + "::" + enumValue + ")"
def pokeValue(self, typeName, *args):
return None
def parseAndEvaluate(self, exp):
val = cdbext.parseAndEvaluate(exp)
if val is None:
return None
value = self.Value(self)
value.type = self.lookupType('void *')
value.ldata = val.to_bytes(8, sys.byteorder)
return value
def isWindowsTarget(self):
return True
def isQnxTarget(self):
return False
def isArmArchitecture(self):
return False
def isMsvcTarget(self):
return True
def qtVersionAndNamespace(self):
return ('', 0x50700) #FIXME: use a general approach in dumper or qttypes
def qtNamespace(self):
return self.qtVersionAndNamespace()[0]
def qtVersion(self):
return self.qtVersionAndNamespace()[1]
def ptrSize(self):
return cdbext.pointerSize()
def put(self, stuff):
self.output += stuff
def lookupType(self, typeName):
if len(typeName) == 0:
return None
if self.typeData.get(typeName, None) is None:
nativeType = self.lookupNativeType(typeName)
return None if nativeType is None else self.fromNativeType(nativeType)
return self.Type(self, typeName)
def lookupNativeType(self, name):
if name.startswith('void'):
return FakeVoidType(name, self)
return cdbext.lookupType(name)
def reportResult(self, result, args):
self.report('result={%s}' % (result))
def readRawMemory(self, address, size):
return cdbext.readRawMemory(address, size)
def findStaticMetaObject(self, typeName):
symbolName = self.mangleName(typeName + '::staticMetaObject')
symbol = self.target.FindFirstGlobalVariable(symbolName)
return symbol.AddressOf().GetValueAsUnsigned() if symbol.IsValid() else 0
def warn(self, msg):
self.put('{name="%s",value="",type="",numchild="0"},' % msg)
def fetchVariables(self, args):
(ok, res) = self.tryFetchInterpreterVariables(args)
if ok:
self.reportResult(res, args)
return
self.setVariableFetchingOptions(args)
self.output = ''
self.currentIName = 'local'
self.put('data=[')
self.anonNumber = 0
variables = []
for val in cdbext.listOfLocals():
self.currentContextValue = val
name = val.name()
value = self.fromNativeValue(val)
value.name = name
variables.append(value)
self.handleLocals(variables)
self.handleWatches(args)
self.put('],partial="%d"' % (len(self.partialVariable) > 0))
self.reportResult(self.output, args)
def report(self, stuff):
sys.stdout.write(stuff + "\n")
def loadDumpers(self, args):
msg = self.setupDumpers()
self.reportResult(msg, args)
def findValueByExpression(self, exp):
return cdbext.parseAndEvaluate(exp)
def nativeDynamicTypeName(self, address, baseType):
return None # FIXME: Seems sufficient, no idea why.
def callHelper(self, rettype, value, function, args):
raise Exception("cdb does not support calling functions")
def putCallItem(self, name, rettype, value, func, *args):
return