Files
qt-creator/share/qtcreator/debugger/cdbbridge.py
David Schulz ca5344ce60 Debugger: Create a pointer value from data returned from evaluate
The evaluate function of the cdb python module returns an integer
representing the address of the evaluated expression. If the expression
does not evaluate to a pointer the python module currently returns a
NoneType.

Change-Id: Ifc77109ef2161a3895a7661789f05134a4b64168
Reviewed-by: David Schulz <david.schulz@qt.io>
2016-11-02 09:11:34 +00:00

299 lines
9.6 KiB
Python

############################################################################
#
# 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