forked from qt-creator/qt-creator
The value was adjusted to the referenced value but it was not used when dumping the members, basically leading to an empty child list in such cases.
1450 lines
49 KiB
Python
1450 lines
49 KiB
Python
from __future__ import with_statement
|
|
#Note: Keep name-type-value-numchild-extra order
|
|
|
|
#return
|
|
|
|
import sys
|
|
import gdb
|
|
import base64
|
|
import __builtin__
|
|
import os
|
|
|
|
# Fails on Windows.
|
|
try:
|
|
import curses.ascii
|
|
def printableChar(ucs):
|
|
return select(curses.ascii.isprint(ucs), ucs, '?')
|
|
except:
|
|
def printableChar(ucs):
|
|
if ucs >= 32 and ucs <= 126:
|
|
return ucs
|
|
return '?'
|
|
|
|
# Fails on SimulatorQt.
|
|
tempFileCounter = 0
|
|
try:
|
|
import tempfile
|
|
# Test if 2.6 is used (Windows), trigger exception and default
|
|
# to 2nd version.
|
|
tempfile.NamedTemporaryFile(prefix="gdbpy_",delete=True)
|
|
def createTempFile():
|
|
file = tempfile.NamedTemporaryFile(prefix="gdbpy_",delete=False)
|
|
file.close()
|
|
return file.name, file
|
|
|
|
except:
|
|
def createTempFile():
|
|
global tempFileCounter
|
|
tempFileCounter += 1
|
|
fileName = "%s/gdbpy_tmp_%d_%d" % (tempfile.gettempdir(), os.getpid(), tempFileCounter)
|
|
return fileName, None
|
|
|
|
def removeTempFile(name, file):
|
|
try:
|
|
os.remove(name)
|
|
except:
|
|
pass
|
|
|
|
verbosity = 0
|
|
verbosity = 1
|
|
|
|
# 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)
|
|
|
|
|
|
def select(condition, if_expr, else_expr):
|
|
if condition:
|
|
return if_expr
|
|
return else_expr
|
|
|
|
def qmin(n, m):
|
|
if n < m:
|
|
return n
|
|
return m
|
|
|
|
def isGoodGdb():
|
|
#return gdb.VERSION.startswith("6.8.50.2009") \
|
|
# and gdb.VERSION != "6.8.50.20090630-cvs"
|
|
return 'parse_and_eval' in __builtin__.dir(gdb)
|
|
|
|
def cleanAddress(addr):
|
|
if addr is None:
|
|
return "<no address>"
|
|
# We cannot use str(addr) as it yields rubbish for char pointers
|
|
# that might trigger Unicode encoding errors.
|
|
return addr.cast(gdb.lookup_type("void").pointer())
|
|
|
|
# Workaround for gdb < 7.1
|
|
def numericTemplateArgument(type, position):
|
|
try:
|
|
return int(type.template_argument(position))
|
|
except RuntimeError, error:
|
|
# ": No type named 30."
|
|
msg = str(error)
|
|
return int(msg[14:-1])
|
|
|
|
def parseAndEvaluate(exp):
|
|
if isGoodGdb():
|
|
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")
|
|
try:
|
|
gdb.execute("print %s" % exp)
|
|
except:
|
|
gdb.execute("set logging off")
|
|
gdb.execute("set logging redirect off")
|
|
return None
|
|
gdb.execute("set logging off")
|
|
gdb.execute("set logging redirect off")
|
|
return gdb.history(0)
|
|
|
|
|
|
def catchCliOutput(command):
|
|
filename, file = createTempFile()
|
|
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")
|
|
temp = open(filename, "r")
|
|
lines = []
|
|
for line in temp:
|
|
lines.append(line)
|
|
temp.close()
|
|
removeTempFile(filename, file)
|
|
return lines
|
|
|
|
|
|
def showException(msg, exType, exValue, exTraceback):
|
|
warn("**** CAUGHT EXCEPTION: %s ****" % msg)
|
|
try:
|
|
import traceback
|
|
for line in traceback.format_exception(exType, exValue, exTraceback):
|
|
warn("%s" % line)
|
|
except:
|
|
pass
|
|
|
|
|
|
class OutputSafer:
|
|
def __init__(self, d, pre = "", post = ""):
|
|
self.d = d
|
|
self.pre = pre
|
|
self.post = post
|
|
|
|
def __enter__(self):
|
|
self.d.put(self.pre)
|
|
self.savedOutput = self.d.output
|
|
self.d.output = ""
|
|
|
|
def __exit__(self, exType, exValue, exTraceBack):
|
|
self.d.put(self.post)
|
|
if self.d.passExceptions and not exType is None:
|
|
showException("OUTPUTSAFER", exType, exValue, exTraceBack)
|
|
self.d.output = self.savedOutput
|
|
else:
|
|
self.d.output = self.savedOutput + self.d.output
|
|
return False
|
|
|
|
|
|
class SubItem:
|
|
def __init__(self, d):
|
|
self.d = d
|
|
|
|
def __enter__(self):
|
|
self.d.put('{')
|
|
self.savedValue = self.d.currentValue
|
|
self.savedValuePriority = self.d.currentValuePriority
|
|
self.savedValueEncoding = self.d.currentValueEncoding
|
|
self.savedType = self.d.currentType
|
|
self.savedTypePriority = self.d.currentTypePriority
|
|
self.d.currentValue = "<not accessible>"
|
|
self.d.currentValuePriority = -100
|
|
self.d.currentValueEncoding = None
|
|
self.d.currentType = ""
|
|
self.d.currentTypePriority = -100
|
|
|
|
def __exit__(self, exType, exValue, exTraceBack):
|
|
#warn(" CURRENT VALUE: %s %s %s" % (self.d.currentValue,
|
|
# self.d.currentValueEncoding, self.d.currentValuePriority))
|
|
if self.d.passExceptions and not exType is None:
|
|
showException("SUBITEM", exType, exValue, exTraceBack)
|
|
try:
|
|
#warn("TYPE CURRENT: %s" % self.d.currentType)
|
|
type = stripClassTag(str(self.d.currentType))
|
|
#warn("TYPE: '%s' DEFAULT: '%s'" % (type, self.d.currentChildType))
|
|
if len(type) > 0 and type != self.d.currentChildType:
|
|
self.d.put('type="%s",' % type) # str(type.unqualified()) ?
|
|
if not self.d.currentValueEncoding is None:
|
|
self.d.putField("valueencoded", self.d.currentValueEncoding)
|
|
if not self.d.currentValue is None:
|
|
self.d.putField("value", self.d.currentValue)
|
|
except:
|
|
pass
|
|
self.d.put('},')
|
|
self.d.currentValue = self.savedValue
|
|
self.d.currentValuePriority = self.savedValuePriority
|
|
self.d.currentValueEncoding = self.savedValueEncoding
|
|
self.d.currentType = self.savedType
|
|
self.d.currentTypePriority = self.savedTypePriority
|
|
return True
|
|
|
|
|
|
class Children:
|
|
def __init__(self, d, numChild = 1, childType = None, childNumChild = None):
|
|
self.d = d
|
|
self.numChild = numChild
|
|
self.childType = childType
|
|
self.childNumChild = childNumChild
|
|
#warn("CHILDREN: %s %s %s" % (numChild, childType, childNumChild))
|
|
|
|
def __enter__(self):
|
|
childType = ""
|
|
childNumChild = -1
|
|
if type(self.numChild) is list:
|
|
numChild = self.numChild[0]
|
|
maxNumChild = self.numChild[1]
|
|
else:
|
|
numChild = self.numChild
|
|
maxNumChild = self.numChild
|
|
if numChild == 0:
|
|
self.childType = None
|
|
if not self.childType is None:
|
|
childType = stripClassTag(str(self.childType))
|
|
self.d.put('childtype="%s",' % childType)
|
|
if isSimpleType(self.childType) or isStringType(self.d, self.childType):
|
|
self.d.put('childnumchild="0",')
|
|
childNumChild = 0
|
|
elif self.childType.code == gdb.TYPE_CODE_PTR:
|
|
self.d.put('childnumchild="1",')
|
|
childNumChild = 1
|
|
if not self.childNumChild is None:
|
|
self.d.put('childnumchild="%s",' % self.childNumChild)
|
|
childNumChild = self.childNumChild
|
|
self.savedChildType = self.d.currentChildType
|
|
self.savedChildNumChild = self.d.currentChildNumChild
|
|
self.savedNumChilds = self.d.currentNumChilds
|
|
self.savedMaxNumChilds = self.d.currentNumChilds
|
|
self.d.currentChildType = childType
|
|
self.d.currentChildNumChild = childNumChild
|
|
self.d.currentNumChilds = numChild
|
|
self.d.currentMaxNumChilds = maxNumChild
|
|
self.d.put("children=[")
|
|
|
|
def __exit__(self, exType, exValue, exTraceBack):
|
|
if self.d.passExceptions and not exType is None:
|
|
showException("CHILDREN", exType, exValue, exTraceBack)
|
|
if self.d.currentMaxNumChilds < self.d.currentNumChilds:
|
|
self.d.putEllipsis();
|
|
self.d.currentChildType = self.savedChildType
|
|
self.d.currentChildNumChild = self.savedChildNumChild
|
|
self.d.currentNumChilds = self.savedNumChilds
|
|
self.d.currentMaxNumChilds = self.savedMaxNumChilds
|
|
self.d.put('],')
|
|
return True
|
|
|
|
|
|
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"
|
|
#6 breakpoint keep y 0xb6cf18e5 <__cxa_throw+5>\n"
|
|
lines = catchCliOutput("info break")
|
|
|
|
lines.reverse()
|
|
bp = Breakpoint()
|
|
for line in lines:
|
|
if len(line) == 0 or line.startswith(" "):
|
|
continue
|
|
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")
|
|
posin = line.find(" in ", pos0x)
|
|
poslt = line.find(" <", pos0x)
|
|
posat = line.find(" at ", posin)
|
|
poscol = line.find(":", posat)
|
|
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)]
|
|
# Take "no address" as indication that the bp is pending.
|
|
#if line.find("<PENDING>") >= 0:
|
|
# bp.address.append("<PENDING>")
|
|
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)
|
|
d.put('bkpt={number="%s",' % bp.number)
|
|
d.put('type="breakpoint",')
|
|
d.put('disp="keep",')
|
|
d.put('enabled="y",')
|
|
for address in bp.address:
|
|
d.put('addr="%s",' % address)
|
|
if not bp.function is None:
|
|
d.put('func="%s",' % bp.function)
|
|
if not bp.filename is None:
|
|
d.put('file="%s",' % bp.filename)
|
|
if not bp.fullname is None:
|
|
d.put('fullname="%s",' % bp.fullname)
|
|
if not bp.linenumber is None:
|
|
d.put('line="%s",' % bp.linenumber)
|
|
if not bp.condition is None:
|
|
d.put('cond="%s",' % bp.condition)
|
|
if not bp.fullname is None:
|
|
d.put('fullname="%s",' % bt.fullname)
|
|
if not bp.times is None:
|
|
d.put('times="1",' % bp.times)
|
|
#d.put('original-location="-"')
|
|
d.put('},')
|
|
bp = Breakpoint()
|
|
|
|
|
|
# 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
|
|
|
|
|
|
def listOfLocals(varList):
|
|
try:
|
|
frame = gdb.selected_frame()
|
|
#warn("FRAME %s: " % frame)
|
|
except RuntimeError:
|
|
warn("FRAME NOT ACCESSIBLE")
|
|
return []
|
|
|
|
# gdb-6.8-symbianelf fails here
|
|
hasBlock = 'block' in __builtin__.dir(frame)
|
|
|
|
items = []
|
|
if hasBlock and isGoodGdb():
|
|
#warn("IS GOOD: %s " % varList)
|
|
try:
|
|
block = frame.block()
|
|
#warn("BLOCK: %s " % block)
|
|
except:
|
|
warn("BLOCK NOT ACCESSIBLE")
|
|
return items
|
|
|
|
while True:
|
|
if block is None:
|
|
warn("UNEXPECTED 'None' BLOCK")
|
|
break
|
|
for symbol in block:
|
|
name = symbol.print_name
|
|
|
|
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:
|
|
# Assuming gdb 7.0 release or 6.8-symbianelf.
|
|
filename, file = createTempFile()
|
|
#warn("VARLIST: %s " % varList)
|
|
#warn("FILENAME: %s " % filename)
|
|
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")
|
|
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
|
|
gdb.execute("set logging off")
|
|
gdb.execute("set logging redirect off")
|
|
|
|
try:
|
|
temp = open(filename, "r")
|
|
for line in temp:
|
|
if len(line) == 0 or line.startswith(" "):
|
|
continue
|
|
# The function parameters
|
|
pos = line.find(" = ")
|
|
if pos < 0:
|
|
continue
|
|
varList.append(line[0:pos])
|
|
temp.close()
|
|
except:
|
|
pass
|
|
removeTempFile(filename, file)
|
|
#warn("VARLIST: %s " % varList)
|
|
for name in varList:
|
|
#warn("NAME %s " % name)
|
|
item = Item(0, "local", name, name)
|
|
try:
|
|
item.value = frame.read_var(name) # this is a gdb value
|
|
except RuntimeError:
|
|
pass
|
|
#continue
|
|
except:
|
|
# Something breaking the list, like intermediate gdb warnings
|
|
# like 'Warning: can't find linker symbol for virtual table for
|
|
# `std::less<char const*>' value\n\nwarning: found
|
|
# `myns::QHashData::shared_null' instead [...]
|
|
# that break subsequent parsing. Chicken out and take the
|
|
# next "usable" line.
|
|
continue
|
|
items.append(item)
|
|
|
|
return items
|
|
|
|
|
|
def value(expr):
|
|
value = parseAndEvaluate(expr)
|
|
try:
|
|
return int(value)
|
|
except:
|
|
return str(value)
|
|
|
|
|
|
def isSimpleType(typeobj):
|
|
if typeobj.code == gdb.TYPE_CODE_PTR:
|
|
return False
|
|
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 ") \
|
|
or type == "signed" or type.startswith("signed ") \
|
|
or type == "unsigned" or type.startswith("unsigned ")
|
|
|
|
|
|
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):
|
|
if True or verbosity > 0:
|
|
print "XXX: %s\n" % message.encode("latin1")
|
|
pass
|
|
|
|
def check(exp):
|
|
if not exp:
|
|
raise RuntimeError("Check failed")
|
|
|
|
def checkRef(ref):
|
|
count = ref["_q_value"]
|
|
check(count > 0)
|
|
check(count < 1000000) # assume there aren't a million references to any object
|
|
|
|
#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):
|
|
# 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 ")
|
|
return p.cast(gdb.lookup_type("void").pointer()) == 0
|
|
|
|
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:]
|
|
elif type.startswith("struct "):
|
|
return type[7:]
|
|
return type
|
|
|
|
def checkPointerRange(p, n):
|
|
for i in xrange(n):
|
|
checkPointer(p)
|
|
++p
|
|
|
|
def call(value, func):
|
|
#warn("CALL: %s -> %s" % (value, func))
|
|
type = stripClassTag(str(value.type))
|
|
if type.find(":") >= 0:
|
|
type = "'" + type + "'"
|
|
exp = "((%s*)%s)->%s" % (type, value.address, func)
|
|
#warn("CALL: %s" % exp)
|
|
result = None
|
|
try:
|
|
result = parseAndEvaluate(exp)
|
|
except:
|
|
pass
|
|
#warn(" -> %s" % result)
|
|
return result
|
|
|
|
def makeValue(type, init):
|
|
type = stripClassTag(type)
|
|
if type.find(":") >= 0:
|
|
type = "'" + type + "'"
|
|
gdb.execute("set $d = (%s*)malloc(sizeof(%s))" % (type, type))
|
|
gdb.execute("set *$d = {%s}" % init)
|
|
value = parseAndEvaluate("$d").dereference()
|
|
#warn(" TYPE: %s" % value.type)
|
|
#warn(" ADDR: %s" % value.address)
|
|
#warn(" VALUE: %s" % value)
|
|
return value
|
|
|
|
def makeExpression(value):
|
|
type = stripClassTag(str(value.type))
|
|
if type.find(":") >= 0:
|
|
type = "'" + type + "'"
|
|
#warn(" TYPE: %s" % type)
|
|
#exp = "(*(%s*)(&%s))" % (type, value.address)
|
|
exp = "(*(%s*)(%s))" % (type, value.address)
|
|
#warn(" EXP: %s" % exp)
|
|
return exp
|
|
|
|
def qtNamespace():
|
|
try:
|
|
type = str(parseAndEvaluate("&QString::null").type.target().unqualified())
|
|
return type[0:len(type) - len("QString::null")]
|
|
except RuntimeError:
|
|
return ""
|
|
except AttributeError:
|
|
# Happens for none-Qt applications
|
|
return ""
|
|
|
|
def findFirstZero(p, max):
|
|
for i in xrange(max):
|
|
if p.dereference() == 0:
|
|
return i
|
|
p = p + 1
|
|
return -1
|
|
|
|
|
|
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)
|
|
|
|
def encodeCharArray(p, maxsize, size = -1):
|
|
t = gdb.lookup_type("unsigned char").pointer()
|
|
p = p.cast(t)
|
|
if size == -1:
|
|
i = findFirstZero(p, maxsize)
|
|
else:
|
|
i = size
|
|
limit = select(i < 0, maxsize, i)
|
|
s = ""
|
|
for i in xrange(limit):
|
|
s += "%02x" % int(p.dereference())
|
|
p += 1
|
|
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"
|
|
return s
|
|
|
|
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)
|
|
checkRef(d_ptr["ref"])
|
|
if size > 0:
|
|
checkAccess(data, 4)
|
|
checkAccess(data + size) == 0
|
|
return encodeCharArray(data, size)
|
|
|
|
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) == 0
|
|
checkRef(d_ptr["ref"])
|
|
p = gdb.Value(d_ptr["data"])
|
|
s = ""
|
|
for i in xrange(size):
|
|
val = int(p.dereference())
|
|
s += "%02x" % (val % 256)
|
|
s += "%02x" % (val / 256)
|
|
p += 1
|
|
return s
|
|
|
|
#######################################################################
|
|
#
|
|
# Item
|
|
#
|
|
#######################################################################
|
|
|
|
class Item:
|
|
def __init__(self, value, parentiname, iname = None, name = None):
|
|
self.value = value
|
|
if iname is None:
|
|
self.iname = parentiname
|
|
else:
|
|
self.iname = "%s.%s" % (parentiname, iname)
|
|
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)
|
|
|
|
def invoke(self, args, from_tty):
|
|
options = []
|
|
varList = []
|
|
typeformats = {}
|
|
formats = {}
|
|
watchers = ""
|
|
expandedINames = ""
|
|
for arg in args.split(' '):
|
|
pos = arg.find(":") + 1
|
|
if arg.startswith("options:"):
|
|
options = arg[pos:].split(",")
|
|
elif arg.startswith("vars:"):
|
|
if len(arg[pos:]) > 0:
|
|
varList = 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)
|
|
|
|
useFancy = "fancy" in options
|
|
|
|
#warn("VARIABLES: %s" % varList)
|
|
#warn("EXPANDED INAMES: %s" % expandedINames)
|
|
module = sys.modules[__name__]
|
|
self.dumpers = {}
|
|
|
|
if False:
|
|
dumpers = ""
|
|
typeformats = ""
|
|
for key, value in module.__dict__.items():
|
|
if key.startswith("qdump__"):
|
|
dumpers += '"' + key[7:] + '",'
|
|
output = "dumpers=[%s]," % dumpers
|
|
#output += "qtversion=[%d,%d,%d]"
|
|
#output += "qtversion=[4,6,0],"
|
|
output += "namespace=\"%s\"," % qtNamespace()
|
|
output += "dumperversion=\"2.0\","
|
|
output += "sizes=[],"
|
|
output += "expressions=[]"
|
|
output += "]"
|
|
print output
|
|
return
|
|
|
|
|
|
if useFancy:
|
|
for key, value in module.__dict__.items():
|
|
if key.startswith("qdump__"):
|
|
self.dumpers[key[7:]] = value
|
|
|
|
d = Dumper()
|
|
d.dumpers = self.dumpers
|
|
d.typeformats = typeformats
|
|
d.formats = formats
|
|
d.useFancy = useFancy
|
|
d.passExceptions = "passexceptions" in options
|
|
d.autoDerefPointers = "autoderef" in options
|
|
d.ns = qtNamespace()
|
|
d.expandedINames = expandedINames
|
|
#warn(" NAMESPACE IS: '%s'" % d.ns)
|
|
|
|
#
|
|
# Locals
|
|
#
|
|
for item in listOfLocals(varList):
|
|
with OutputSafer(d, "", ""):
|
|
d.anonNumber = -1
|
|
#warn("ITEM NAME %s: " % item.name)
|
|
try:
|
|
#warn("ITEM VALUE %s: " % item.value)
|
|
# Throw on funny stuff, catch below.
|
|
# Unfortunately, this fails also with a "Unicode encoding error"
|
|
# in testArray().
|
|
#dummy = str(item.value)
|
|
pass
|
|
except:
|
|
# Locals with failing memory access.
|
|
with SubItem(d):
|
|
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"');
|
|
continue
|
|
|
|
type = item.value.type
|
|
if type.code == gdb.TYPE_CODE_PTR \
|
|
and item.name == "argv" and str(type) == "char **":
|
|
# Special handling for char** argv.
|
|
n = 0
|
|
p = item.value
|
|
# p is 0 for "optimized out" cases.
|
|
if not isNull(p):
|
|
while not isNull(p.dereference()) and n <= 100:
|
|
p += 1
|
|
n += 1
|
|
|
|
with SubItem(d):
|
|
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):
|
|
p = item.value
|
|
with Children(d, n):
|
|
for i in xrange(n):
|
|
value = p.dereference()
|
|
d.putItem(Item(value, item.iname, i, None))
|
|
p += 1
|
|
if n > 100:
|
|
d.putEllipsis()
|
|
|
|
else:
|
|
# A "normal" local variable or parameter.
|
|
try:
|
|
addr = cleanAddress(item.value.address)
|
|
with SubItem(d):
|
|
d.put('iname="%s",' % item.iname)
|
|
d.put('addr="%s",' % addr)
|
|
d.putItemHelper(item)
|
|
except AttributeError:
|
|
# Thrown by cleanAddress with message "'NoneType' object
|
|
# has no attribute 'cast'" for optimized-out values.
|
|
with SubItem(d):
|
|
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)
|
|
|
|
#
|
|
# Watchers
|
|
#
|
|
with OutputSafer(d, ",", ""):
|
|
if len(watchers) > 0:
|
|
for watcher in watchers.split("##"):
|
|
(exp, iname) = watcher.split("#")
|
|
self.handleWatch(d, exp, iname)
|
|
|
|
#
|
|
# Breakpoints
|
|
#
|
|
#listOfBreakpoints(d)
|
|
|
|
#print('data=[' + locals + sep + watchers + '],bkpts=[' + breakpoints + ']\n')
|
|
print('data=[' + d.output + ']')
|
|
|
|
|
|
def handleWatch(self, d, exp, iname):
|
|
exp = str(exp)
|
|
escapedExp = exp.replace('"', '\\"');
|
|
#warn("HANDLING WATCH %s, INAME: '%s'" % (exp, iname))
|
|
if exp.startswith("[") and exp.endswith("]"):
|
|
#warn("EVAL: EXP: %s" % exp)
|
|
with SubItem(d):
|
|
d.putField("iname", iname)
|
|
d.putField("name", escapedExp)
|
|
d.putField("exp", escapedExp)
|
|
try:
|
|
list = eval(exp)
|
|
d.putValue("")
|
|
d.putType(" ")
|
|
d.putNumChild(len(list))
|
|
# This is a list of expressions to evaluate
|
|
with Children(d, len(list)):
|
|
itemNumber = 0
|
|
for item in list:
|
|
self.handleWatch(d, item, "%s.%d" % (iname, itemNumber))
|
|
itemNumber += 1
|
|
except RuntimeError, error:
|
|
warn("EVAL: ERROR CAUGHT %s" % error)
|
|
d.putValue("<syntax error>")
|
|
d.putType(" ")
|
|
d.putNumChild(0)
|
|
with Children(d, 0):
|
|
pass
|
|
return
|
|
|
|
with SubItem(d):
|
|
d.putField("iname", iname)
|
|
d.putField("name", escapedExp)
|
|
d.putField("exp", escapedExp)
|
|
handled = False
|
|
if exp == "<Edit>" or len(exp) == 0:
|
|
d.put('value=" ",type=" ",numchild="0",')
|
|
else:
|
|
try:
|
|
value = parseAndEvaluate(exp)
|
|
item = Item(value, iname, None, None)
|
|
if not value is None:
|
|
d.putAddress(value.address)
|
|
d.putItemHelper(item)
|
|
except RuntimeError:
|
|
d.put('value="<invalid>",type="<unknown>",numchild="0",')
|
|
|
|
|
|
FrameCommand()
|
|
|
|
|
|
#######################################################################
|
|
#
|
|
# 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):
|
|
(cmd, addr) = arg.split(",")
|
|
lines = catchCliOutput("info line *" + addr)
|
|
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]
|
|
gdb.execute("maint packet sal%s,%s,%s" % (cmd,fromAddr, toAddr))
|
|
|
|
SalCommand()
|
|
|
|
|
|
#######################################################################
|
|
#
|
|
# The Dumper Class
|
|
#
|
|
#######################################################################
|
|
|
|
|
|
class Dumper:
|
|
def __init__(self):
|
|
self.output = ""
|
|
self.currentChildType = ""
|
|
self.currentChildNumChild = -1
|
|
self.currentMaxNumChilds = -1
|
|
self.currentNumChilds = -1
|
|
self.currentValue = None
|
|
self.currentValuePriority = -100
|
|
self.currentValueEncoding = None
|
|
self.currentType = None
|
|
self.currentTypePriority = -100
|
|
|
|
def put(self, value):
|
|
self.output += value
|
|
|
|
def putField(self, name, value):
|
|
self.put('%s="%s",' % (name, value))
|
|
|
|
def beginItem(self, name):
|
|
self.put('%s="' % name)
|
|
|
|
def endItem(self):
|
|
self.put('",')
|
|
|
|
def childRange(self):
|
|
return xrange(qmin(self.currentMaxNumChilds, self.currentNumChilds))
|
|
|
|
# convenience
|
|
def putItemCount(self, count):
|
|
self.put('value="<%s items>",' % count)
|
|
|
|
def putEllipsis(self):
|
|
self.put('{name="<incomplete>",value="",type="",numchild="0"},')
|
|
|
|
def putType(self, type, priority = 0):
|
|
# higher priority values override lower ones
|
|
if priority >= self.currentTypePriority:
|
|
self.currentType = type
|
|
self.currentTypePriority = priority
|
|
|
|
def putAddress(self, addr):
|
|
self.put('addr="%s",' % cleanAddress(addr))
|
|
|
|
def putNumChild(self, numchild):
|
|
#warn("NUM CHILD: '%s' '%s'" % (numchild, self.currentChildNumChild))
|
|
if numchild != self.currentChildNumChild:
|
|
self.put('numchild="%s",' % numchild)
|
|
|
|
def putValue(self, value, encoding = None, priority = 0):
|
|
# higher priority values override lower ones
|
|
if priority >= self.currentValuePriority:
|
|
self.currentValue = value
|
|
self.currentValuePriority = priority
|
|
self.currentValueEncoding = encoding
|
|
|
|
def putPointerValue(self, value):
|
|
# Use a lower priority
|
|
self.putValue("0x%x" % value.dereference().cast(
|
|
gdb.lookup_type("unsigned long")), None, -1)
|
|
|
|
def putStringValue(self, value):
|
|
if value is None:
|
|
self.put('value="<not available>",')
|
|
else:
|
|
str = encodeString(value)
|
|
self.put('valueencoded="%d",value="%s",' % (Hex4EncodedLittleEndian, str))
|
|
|
|
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))
|
|
|
|
def putByteArrayValue(self, value):
|
|
str = encodeByteArray(value)
|
|
self.put('valueencoded="%d",value="%s",' % (Hex2EncodedLatin1, str))
|
|
|
|
def putName(self, name):
|
|
self.put('name="%s",' % name)
|
|
|
|
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 stripNamespaceFromType(self, typeobj):
|
|
# 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"
|
|
type = stripClassTag(str(typeobj))
|
|
if len(self.ns) > 0 and type.startswith(self.ns):
|
|
type = type[len(self.ns):]
|
|
pos = type.find("<")
|
|
# 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("<")
|
|
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):
|
|
with SubItem(self):
|
|
self.putName(name)
|
|
self.putValue(value)
|
|
self.putType("int")
|
|
self.putNumChild(0)
|
|
|
|
def putBoolItem(self, name, value):
|
|
with SubItem(self):
|
|
self.putName(name)
|
|
self.putValue(value)
|
|
self.putType("bool")
|
|
self.putNumChild(0)
|
|
|
|
def itemFormat(self, item):
|
|
format = self.formats.get(str(cleanAddress(item.value.address)))
|
|
if format is None:
|
|
format = self.typeformats.get(stripClassTag(str(item.value.type)))
|
|
return format
|
|
|
|
def putItem(self, item):
|
|
with SubItem(self):
|
|
self.putItemHelper(item)
|
|
|
|
def putCallItem(self, name, item, func):
|
|
result = call(item.value, func)
|
|
self.putItem(Item(result, item.iname, name, name))
|
|
|
|
def putItemHelper(self, item):
|
|
name = getattr(item, "name", None)
|
|
if not name is None:
|
|
self.putName(name)
|
|
|
|
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
|
|
|
|
# FIXME: Gui shows references stripped?
|
|
#warn(" ");
|
|
#warn("REAL INAME: %s " % item.iname)
|
|
#warn("REAL NAME: %s " % name)
|
|
#warn("REAL TYPE: %s " % item.value.type)
|
|
#warn("REAL VALUE: %s " % item.value)
|
|
#try:
|
|
# warn("REAL VALUE: %s " % item.value)
|
|
#except:
|
|
# #UnicodeEncodeError:
|
|
# warn("REAL VALUE: <unprintable>")
|
|
|
|
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("::", "__")
|
|
|
|
#warn(" STRIPPED: %s" % strippedType)
|
|
#warn(" DUMPERS: %s" % self.dumpers)
|
|
#warn(" DUMPERS: %s" % (strippedType in self.dumpers))
|
|
|
|
if isSimpleType(type.unqualified()):
|
|
#warn("IS SIMPLE: %s " % type)
|
|
self.putType(item.value.type)
|
|
self.putValue(value)
|
|
self.putNumChild(0)
|
|
|
|
elif strippedType in self.dumpers:
|
|
#warn("IS DUMPABLE: %s " % type)
|
|
self.putType(item.value.type)
|
|
self.dumpers[strippedType](self, item)
|
|
#warn(" RESULT: %s " % self.output)
|
|
|
|
elif type.code == gdb.TYPE_CODE_ENUM:
|
|
#warn("GENERIC ENUM: %s" % value)
|
|
self.putType(item.value.type)
|
|
self.putValue(value)
|
|
self.putNumChild(0)
|
|
|
|
|
|
elif type.code == gdb.TYPE_CODE_PTR:
|
|
isHandled = False
|
|
|
|
format = self.itemFormat(item)
|
|
|
|
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.putPointerValue(value.address)
|
|
elif format == 1 or format == 2:
|
|
# Latin1 or UTF-8
|
|
f = select(format == 1, Hex2EncodedLatin1, Hex2EncodedUtf8)
|
|
self.putValue(encodeCharArray(value, 100), f)
|
|
elif format == 3:
|
|
# UTF-16.
|
|
self.putValue(encodeChar2Array(value, 100), Hex4EncodedBigEndian)
|
|
elif format == 4:
|
|
# UCS-4:
|
|
self.putValue(encodeChar4Array(value, 100), Hex8EncodedBigEndian)
|
|
|
|
strippedType = str(type.strip_typedefs()) \
|
|
.replace("(anonymous namespace)", "")
|
|
if (not isHandled) and strippedType.find("(") != -1:
|
|
# A function pointer.
|
|
self.putValue(str(item.value))
|
|
self.putAddress(value.address)
|
|
self.putType(item.value.type)
|
|
self.putNumChild(0)
|
|
isHandled = True
|
|
|
|
if (not isHandled) and self.useFancy:
|
|
if isNull(value):
|
|
self.putValue("0x0")
|
|
self.putType(item.value.type)
|
|
self.putNumChild(0)
|
|
isHandled = True
|
|
|
|
target = str(type.target().strip_typedefs().unqualified())
|
|
if (not isHandled) and target == "void":
|
|
self.putType(item.value.type)
|
|
self.putValue(str(value))
|
|
self.putNumChild(0)
|
|
isHandled = True
|
|
|
|
#warn("TARGET: %s " % target)
|
|
if (not isHandled) and (target == "char"
|
|
or target == "signed char" or target == "unsigned char"):
|
|
# Display values up to given length directly
|
|
#warn("CHAR AUTODEREF: %s" % value.address)
|
|
self.putType(item.value.type)
|
|
self.putValue(encodeCharArray(value, 100), Hex2EncodedLatin1)
|
|
self.putNumChild(0)
|
|
isHandled = True
|
|
|
|
#warn("AUTODEREF: %s" % self.autoDerefPointers)
|
|
#warn("IS HANDLED: %s" % isHandled)
|
|
#warn("RES: %s" % (self.autoDerefPointers and not isHandled))
|
|
if (not isHandled) and (self.autoDerefPointers or name == "this"):
|
|
## Generic pointer type.
|
|
#warn("GENERIC AUTODEREF POINTER: %s" % value.address)
|
|
innerType = item.value.type.target()
|
|
self.putType(innerType)
|
|
savedCurrentChildType = self.currentChildType
|
|
self.currentChildType = stripClassTag(str(innerType))
|
|
self.putItemHelper(
|
|
Item(item.value.dereference(), item.iname, None, None))
|
|
self.currentChildType = savedCurrentChildType
|
|
self.putPointerValue(value.address)
|
|
isHandled = True
|
|
|
|
# Fall back to plain pointer printing.
|
|
if not isHandled:
|
|
#warn("GENERIC PLAIN POINTER: %s" % value.type)
|
|
self.putType(item.value.type)
|
|
self.putAddress(value.address)
|
|
self.putNumChild(1)
|
|
if self.isExpanded(item):
|
|
with Children(self):
|
|
self.putItem(
|
|
Item(item.value.dereference(), item.iname, "*", "*"))
|
|
self.putPointerValue(value.address)
|
|
|
|
elif str(type).startswith("<anon"):
|
|
# Anonymous union. We need a dummy name to distinguish
|
|
# multiple anonymous unions in the struct.
|
|
self.putType(item.value.type)
|
|
self.putValue("{...}")
|
|
self.anonNumber += 1
|
|
with Children(self, 1):
|
|
self.listAnonymous(item, "#%d" % self.anonNumber, type)
|
|
|
|
else:
|
|
#warn("GENERIC STRUCT: %s" % item.value.type)
|
|
#warn("INAME: %s " % item.iname)
|
|
#warn("INAMES: %s " % self.expandedINames)
|
|
#warn("EXPANDED: %s " % (item.iname in self.expandedINames))
|
|
|
|
# Insufficient, see http://sourceware.org/bugzilla/show_bug.cgi?id=10953
|
|
#fields = value.type.fields()
|
|
fields = value.type.strip_typedefs().fields()
|
|
|
|
self.putType(item.value.type)
|
|
try:
|
|
self.putAddress(item.value.address)
|
|
except:
|
|
pass
|
|
self.putValue("{...}")
|
|
|
|
if False:
|
|
numfields = 0
|
|
for field in fields:
|
|
bitpos = getattr(field, "bitpos", None)
|
|
if not bitpos is None:
|
|
++numfields
|
|
else:
|
|
numfields = len(fields)
|
|
self.putNumChild(numfields)
|
|
|
|
if self.isExpanded(item):
|
|
if value.type.code == gdb.TYPE_CODE_ARRAY:
|
|
baseptr = value.cast(value.type.target().pointer())
|
|
charptr = gdb.lookup_type("unsigned char").pointer()
|
|
addr1 = (baseptr+1).cast(charptr)
|
|
addr0 = baseptr.cast(charptr)
|
|
self.putField("addrbase", cleanAddress(addr0))
|
|
self.putField("addrstep", addr1 - addr0)
|
|
|
|
innerType = None
|
|
if len(fields) == 1 and fields[0].name is None:
|
|
innerType = value.type.target()
|
|
with Children(self, 1, innerType):
|
|
child = Item(value, item.iname, None, item.name)
|
|
self.putFields(child)
|
|
|
|
def putFields(self, item, innerType = None):
|
|
value = item.value
|
|
fields = value.type.strip_typedefs().fields()
|
|
baseNumber = 0
|
|
for field in fields:
|
|
#warn("FIELD: %s" % field)
|
|
#warn(" BITSIZE: %s" % field.bitsize)
|
|
#warn(" ARTIFICIAL: %s" % field.artificial)
|
|
bitpos = getattr(field, "bitpos", None)
|
|
if bitpos is None: # FIXME: Is check correct?
|
|
continue # A static class member(?).
|
|
|
|
if field.name is None:
|
|
innerType = value.type.target()
|
|
p = value.cast(innerType.pointer())
|
|
for i in xrange(value.type.sizeof / innerType.sizeof):
|
|
self.putItem(Item(p.dereference(), item.iname, i, None))
|
|
p = p + 1
|
|
continue
|
|
|
|
# Ignore vtable pointers for virtual inheritance.
|
|
if field.name.startswith("_vptr."):
|
|
continue
|
|
|
|
#warn("FIELD NAME: %s" % field.name)
|
|
#warn("FIELD TYPE: %s" % field.type)
|
|
if field.name == stripClassTag(str(field.type)):
|
|
# Field is base type. We cannot use field.name as part
|
|
# of the iname as it might contain spaces and other
|
|
# strange characters.
|
|
child = Item(value.cast(field.type),
|
|
item.iname, "@%d" % baseNumber, field.name)
|
|
baseNumber += 1
|
|
with SubItem(self):
|
|
self.putField("iname", child.iname)
|
|
self.putItemHelper(child)
|
|
elif len(field.name) == 0:
|
|
# Anonymous union. We need a dummy name to distinguish
|
|
# multiple anonymous unions in the struct.
|
|
self.anonNumber += 1
|
|
self.listAnonymous(item, "#%d" % self.anonNumber,
|
|
field.type)
|
|
else:
|
|
# Named field.
|
|
with SubItem(self):
|
|
child = Item(value[field.name],
|
|
item.iname, field.name, field.name)
|
|
self.putItemHelper(child)
|
|
|
|
|
|
def listAnonymous(self, item, name, type):
|
|
for field in type.fields():
|
|
#warn("FIELD NAME: %s" % field.name)
|
|
if len(field.name) > 0:
|
|
value = item.value[field.name]
|
|
child = Item(value, item.iname, field.name, field.name)
|
|
with SubItem(self):
|
|
self.putAddress(value.address)
|
|
self.putItemHelper(child)
|
|
else:
|
|
# Further nested.
|
|
self.anonNumber += 1
|
|
name = "#%d" % self.anonNumber
|
|
iname = "%s.%s" % (item.iname, name)
|
|
child = Item(item.value, iname, None, name)
|
|
with SubItem(self):
|
|
self.putField("name", name)
|
|
self.putField("value", " ")
|
|
if str(field.type).endswith("<anonymous union>"):
|
|
self.putField("type", "<anonymous union>")
|
|
elif str(field.type).endswith("<anonymous struct>"):
|
|
self.putField("type", "<anonymous struct>")
|
|
else:
|
|
self.putField("type", field.type)
|
|
with Children(self, 1):
|
|
self.listAnonymous(child, name, field.type)
|
|
|
|
|