Commit b70c0978 authored by hjk's avatar hjk
Browse files

debugger: rework error handling in python dumpers

parent 0a93ca92
......@@ -137,6 +137,131 @@ def catchCliOutput(command):
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 = ""
self.d.currentValuePriority = 0
self.d.currentValueEncoding = None
self.d.currentType = ""
self.d.currentTypePriority = 0
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)
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)
#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()) ?
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
......@@ -345,7 +470,7 @@ def listOfLocals(varList):
# 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
# that break subsequent parsing. Chicken out and take the
# next "usable" line.
continue
items.append(item)
......@@ -447,6 +572,8 @@ movableTypes = set([
def stripClassTag(type):
if type.startswith("class "):
return type[6:]
elif type.startswith("struct "):
return type[7:]
return type
def checkPointerRange(p, n):
......@@ -461,7 +588,11 @@ def call(value, func):
type = "'" + type + "'"
exp = "((%s*)%s)->%s" % (type, value.address, func)
#warn("CALL: %s" % exp)
result = parseAndEvaluate(exp)
result = None
try:
result = parseAndEvaluate(exp)
except:
pass
#warn(" -> %s" % result)
return result
......@@ -684,6 +815,7 @@ class FrameCommand(gdb.Command):
# Locals
#
for item in listOfLocals(varList):
with OutputSafer(d, "", ""):
d.anonNumber = -1
#warn("ITEM NAME %s: " % item.name)
try:
......@@ -695,14 +827,13 @@ class FrameCommand(gdb.Command):
pass
except:
# Locals with failing memory access.
d.beginHash()
d.put('iname="%s",' % item.iname)
d.put('name="%s",' % item.name)
d.put('addr="<not accessible>",')
d.put('value="<not accessible>",')
d.put('type="%s",' % item.value.type)
d.put('numchild="0"');
d.endHash()
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
......@@ -717,74 +848,56 @@ class FrameCommand(gdb.Command):
p += 1
n += 1
d.beginHash()
d.put('iname="%s",' % item.iname)
d.putName(item.name)
d.putItemCount(select(n <= 100, n, "> 100"))
d.putType(type)
d.putNumChild(n)
if d.isExpanded(item):
p = item.value
d.beginChildren(n)
for i in xrange(n):
value = p.dereference()
d.putItem(Item(value, item.iname, i, None))
p += 1
if n > 100:
d.putEllipsis()
d.endChildren()
d.endHash()
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)
d.beginHash()
d.put('iname="%s",' % item.iname)
d.put('addr="%s",' % addr)
d.safePutItemHelper(item)
d.endHash()
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.
d.beginHash()
d.put('iname="%s",' % item.iname)
d.put('name="%s",' % item.name)
d.put('addr="<optimized out>",')
d.put('value="<optimized out>",')
d.put('type="%s"' % item.value.type)
d.endHash()
d.pushOutput()
locals = d.safeoutput
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
#
d.safeoutput = ""
if len(watchers) > 0:
for watcher in watchers.split("##"):
(exp, iname) = watcher.split("#")
self.handleWatch(d, exp, iname)
d.pushOutput()
watchers = d.safeoutput
sep = ""
if len(locals) and len(watchers):
sep = ","
with OutputSafer(d, ",", ""):
if len(watchers) > 0:
for watcher in watchers.split("##"):
(exp, iname) = watcher.split("#")
self.handleWatch(d, exp, iname)
#
# Breakpoints
#
#breakpoints = ""
#d.safeoutput = ""
#listOfBreakpoints(d)
#d.pushOutput()
#breakpoints = d.safeoutput
#print('data=[' + locals + sep + watchers + '],bkpts=[' + breakpoints + ']\n')
print('data=[' + locals + sep + watchers + ']\n')
print('data=[' + d.output + ']')
def handleWatch(self, d, exp, iname):
......@@ -793,49 +906,46 @@ class FrameCommand(gdb.Command):
#warn("HANDLING WATCH %s, INAME: '%s'" % (exp, iname))
if exp.startswith("[") and exp.endswith("]"):
#warn("EVAL: EXP: %s" % exp)
d.beginHash()
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)
try:
list = eval(exp)
d.putValue("")
d.putType(" ")
d.putNumChild(len(list))
# This is a list of expressions to evaluate
d.beginChildren(len(list))
itemNumber = 0
for item in list:
self.handleWatch(d, item, "%s.%d" % (iname, itemNumber))
itemNumber += 1
d.endChildren()
except RuntimeError, error:
warn("EVAL: ERROR CAUGHT %s" % error)
d.putValue("<syntax error>")
d.putType(" ")
d.putNumChild(0)
d.beginChildren(0)
d.endChildren()
d.endHash()
return
d.beginHash()
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",')
d.endHash()
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()
......@@ -882,11 +992,15 @@ SalCommand()
class Dumper:
def __init__(self):
self.output = ""
self.safeoutput = ""
self.childTypes = [""]
self.childNumChilds = [-1]
self.maxNumChilds = [-1]
self.numChilds = [-1]
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
......@@ -894,60 +1008,14 @@ class Dumper:
def putField(self, name, value):
self.put('%s="%s",' % (name, value))
def beginHash(self):
self.put('{')
def endHash(self):
self.put('},')
def beginItem(self, name):
self.put('%s="' % name)
def endItem(self):
self.put('",')
def beginChildren(self, numChild_ = 1, childType_ = None, childNumChild_ = None):
childType = ""
childNumChild = -1
if type(numChild_) is list:
numChild = numChild_[0]
maxNumChild = numChild_[1]
else:
numChild = numChild_
maxNumChild = numChild_
if numChild == 0:
childType_ = None
if not childType_ is None:
childType = stripClassTag(str(childType_))
self.put('childtype="%s",' % childType)
if isSimpleType(childType_) or isStringType(self, childType_):
self.put('childnumchild="0",')
childNumChild = 0
elif childType_.code == gdb.TYPE_CODE_PTR:
self.put('childnumchild="1",')
childNumChild = 1
if not childNumChild_ is None:
self.put('childnumchild="%s",' % childNumChild_)
childNumChild = childNumChild_
self.childTypes.append(childType)
self.childNumChilds.append(childNumChild)
self.numChilds.append(numChild)
self.maxNumChilds.append(maxNumChild)
#warn("BEGIN: %s" % self.childTypes)
self.put("children=[")
def endChildren(self):
#warn("END: %s" % self.childTypes)
numChild = self.numChilds.pop()
maxNumChild = self.maxNumChilds.pop()
if maxNumChild < numChild:
self.putEllipsis();
self.childTypes.pop()
self.childNumChilds.pop()
self.put('],')
def childRange(self):
return xrange(qmin(self.maxNumChilds[-1], self.numChilds[-1]))
return xrange(qmin(self.currentMaxNumChilds, self.currentNumChilds))
# convenience
def putItemCount(self, count):
......@@ -956,28 +1024,30 @@ class Dumper:
def putEllipsis(self):
self.put('{name="<incomplete>",value="",type="",numchild="0"},')
def putType(self, type):
#warn("TYPES: '%s' '%s'" % (type, self.childTypes))
#warn(" EQUAL 2: %s " % (str(type) == self.childTypes[-1]))
type = stripClassTag(str(type))
if len(type) > 0 and type != self.childTypes[-1]:
self.put('type="%s",' % type) # str(type.unqualified()) ?
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.childNumChilds[-1]))
if numchild != self.childNumChilds[-1]:
#warn("NUM CHILD: '%s' '%s'" % (numchild, self.currentChildNumChild))
if numchild != self.currentChildNumChild:
self.put('numchild="%s",' % numchild)
def putValue(self, value, encoding = None):
if not encoding is None:
self.putField("valueencoded", encoding)
self.putField("value", value)
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):
self.putValue("0x%x" % value.dereference().cast(
# Use a lower priority
self.putField("value2", "0x%x" % value.dereference().cast(
gdb.lookup_type("unsigned long")))
def putStringValue(self, value):
......@@ -1014,11 +1084,6 @@ class Dumper:
def isExpandedIName(self, iname):
return iname in self.expandedINames
def unputField(self, name):
pos = self.output.rfind(",")
if self.output[pos + 1:].startswith(name):
self.output = self.output[0:pos]
def stripNamespaceFromType(self, typeobj):
# This breaks for dumpers type names containing '__star'.
# But this should not happen as identifiers containing two
......@@ -1044,29 +1109,18 @@ class Dumper:
return self.stripNamespaceFromType(type) in movableTypes
def putIntItem(self, name, value):
self.beginHash()
self.putName(name)
self.putValue(value)
self.putType("int")
self.putNumChild(0)
self.endHash()
with SubItem(self):
self.putName(name)
self.putValue(value)
self.putType("int")
self.putNumChild(0)
def putBoolItem(self, name, value):
self.beginHash()
self.putName(name)
self.putValue(value)
self.putType("bool")
self.putNumChild(0)
self.endHash()
def pushOutput(self):
#warn("PUSH OUTPUT: %s " % self.output)
self.safeoutput += self.output
self.output = ""
def dumpInnerValueHelper(self, item):
if isSimpleType(item.value.type):
self.safePutItemHelper(item)
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)))
......@@ -1074,58 +1128,13 @@ class Dumper:
format = self.typeformats.get(stripClassTag(str(item.value.type)))
return format
def safePutItem(self, item):
self.beginHash()
self.safePutItemHelper(item)
self.endHash()
def safePutItemHelper(self, item):
self.pushOutput()
# This is only used at the top level to ensure continuation
# after failures due to uninitialized or corrupted data.
if self.passExceptions:
# for debugging reasons propagate errors.
self.putItemHelper(item)
else:
try:
self.putItemHelper(item)
except RuntimeError:
self.output = ""
# FIXME: Only catch debugger related exceptions
#exType, exValue, exTraceback = sys.exc_info()
#tb = traceback.format_exception(exType, exValue, exTraceback)
#warn("Exception: %s" % ex.message)
# DeprecationWarning: BaseException.message
# has been deprecated
#warn("Exception.")
#for line in tb:
# warn("%s" % line)
self.putName(item.name)
try:
d.putAddress(item.value.address)
except:
pass
self.putValue("<invalid>")