Commit e1760131 authored by hjk's avatar hjk
Browse files

Debugger: Move some global variables to the Dumper class



Change-Id: I5c8fd8a48f27ac70e6e39f645d64dcd788752e73
Reviewed-by: default avatarhjk <hjk121@nokiamail.com>
parent f9ff6303
......@@ -41,44 +41,6 @@ else:
verbosity = 0
verbosity = 1
qqStringCutOff = 10000
# This is a cache mapping from 'type name' to 'display alternatives'.
qqFormats = {}
# This is a cache of all known dumpers.
qqDumpers = {}
# This is a cache of all dumpers that support writing.
qqEditable = {}
# This is an approximation of the Qt Version found
qqVersion = None
# This keeps canonical forms of the typenames, without array indices etc.
qqStripForFormat = {}
def stripForFormat(typeName):
global qqStripForFormat
if typeName in qqStripForFormat:
return qqStripForFormat[typeName]
stripped = ""
inArray = 0
for c in stripClassTag(typeName):
if c == '<':
break
if c == ' ':
continue
if c == '[':
inArray += 1
elif c == ']':
inArray -= 1
if inArray and ord(c) >= 48 and ord(c) <= 57:
continue
stripped += c
qqStripForFormat[typeName] = stripped
return stripped
def hasPlot():
fileName = "/usr/bin/gnuplot"
return os.path.isfile(fileName) and os.access(fileName, os.X_OK)
......@@ -261,6 +223,44 @@ class DumperBase:
self.isGdb = False
self.isLldb = False
# Later set, or not set:
# cachedQtVersion
self.stringCutOff = 10000
# This is a cache mapping from 'type name' to 'display alternatives'.
self.qqFormats = {}
# This is a cache of all known dumpers.
self.qqDumpers = {}
# This is a cache of all dumpers that support writing.
self.qqEditable = {}
# This keeps canonical forms of the typenames, without array indices etc.
self.cachedFormats = {}
def stripForFormat(self, typeName):
if typeName in self.cachedFormats:
return self.cachedFormats[typeName]
stripped = ""
inArray = 0
for c in stripClassTag(typeName):
if c == '<':
break
if c == ' ':
continue
if c == '[':
inArray += 1
elif c == ']':
inArray -= 1
if inArray and ord(c) >= 48 and ord(c) <= 57:
continue
stripped += c
self.cachedFormats[typeName] = stripped
return stripped
def is32bit(self):
return self.ptrSize() == 4
......@@ -268,7 +268,7 @@ class DumperBase:
if limit is None:
return size
if limit == 0:
return min(size, qqStringCutOff)
return min(size, self.stringCutOff)
return min(size, limit)
def byteArrayDataHelper(self, addr):
......@@ -372,10 +372,11 @@ class DumperBase:
return self.putValue(self.encodeString(value), Hex4EncodedLittleEndian)
def putMapName(self, value):
if str(value.type) == self.ns + "QString":
ns = self.qtNamespace()
if str(value.type) == ns + "QString":
self.put('key="%s",' % self.encodeString(value))
self.put('keyencoded="%s",' % Hex4EncodedLittleEndian)
elif str(value.type) == self.ns + "QByteArray":
elif str(value.type) == ns + "QByteArray":
self.put('key="%s",' % self.encodeByteArray(value))
self.put('keyencoded="%s",' % Hex2EncodedLatin1)
else:
......@@ -416,9 +417,9 @@ class DumperBase:
def encodeCArray(self, p, innerType, suffix):
t = self.lookupType(innerType)
p = p.cast(t.pointer())
limit = self.findFirstZero(p, qqStringCutOff)
limit = self.findFirstZero(p, self.stringCutOff)
s = self.readMemory(p, limit * t.sizeof)
if limit > qqStringCutOff:
if limit > self.stringCutOff:
s += suffix
return s
......@@ -503,6 +504,14 @@ class DumperBase:
return type == "QStringList" and self.qtVersion() >= 0x050000
def currentItemFormat(self, type = None):
format = self.formats.get(self.currentIName)
if format is None:
if type is None:
type = self.currentType
needle = self.stripForFormat(str(type))
format = self.typeformats.get(needle)
return format
def cleanAddress(addr):
if addr is None:
......
......@@ -298,57 +298,9 @@ class ScanStackCommand(gdb.Command):
ScanStackCommand()
def registerDumper(funcname, function):
global qqDumpers, qqFormats, qqEditable
try:
#warn("FUNCTION: %s " % funcname)
#funcname = function.func_name
if funcname.startswith("qdump__"):
type = funcname[7:]
qqDumpers[type] = function
qqFormats[type] = qqFormats.get(type, "")
elif funcname.startswith("qform__"):
type = funcname[7:]
formats = ""
try:
formats = function()
except:
pass
qqFormats[type] = formats
elif funcname.startswith("qedit__"):
type = funcname[7:]
try:
qqEditable[type] = function
except:
pass
except:
pass
def bbsetup(args = ''):
global qqDumpers, qqFormats, qqEditable, typeCache
qqDumpers = {}
qqFormats = {}
qqEditable = {}
typeCache = {}
module = sys.modules[__name__]
#warn("KEYS: %s " % module.__dict__.keys())
for name in module.__dict__.keys():
#warn("KEY: %s " % name)
#warn("FUNCT: %s " % module.__dict__[name])
registerDumper(name, module.__dict__[name])
result = "dumpers=["
#qqNs = qtNamespace() # This is too early
for key, value in qqFormats.items():
if key in qqEditable:
result += '{type="%s",formats="%s",editable="true"},' % (key, value)
else:
result += '{type="%s",formats="%s"},' % (key, value)
result += ']'
#result += ',namespace="%s"' % qqNs
print(result)
return result
print(theDumper.bbsetup())
registerCommand("bbsetup", bbsetup)
......@@ -377,18 +329,8 @@ class PlainDumper:
for child in children:
d.putSubItem(child[0], child[1])
def importPlainDumper(printer):
global qqDumpers, qqFormats
name = printer.name.replace("::", "__")
qqDumpers[name] = PlainDumper(printer)
qqFormats[name] = ""
def importPlainDumpers(args):
return
for obj in gdb.objfiles():
for printers in obj.pretty_printers + gdb.pretty_printers:
for printer in printers.subprinters:
importPlainDumper(printer)
theDumper.importPlainDumpers()
registerCommand("importPlainDumpers", importPlainDumpers)
......@@ -651,27 +593,6 @@ def value(expr):
Value = gdb.Value
qqNs = None
def qtNamespace():
# FIXME: This only works when call from inside a Qt function frame.
global qqNs
if not qqNs is None:
return qqNs
try:
str = gdb.execute("ptype QString::Null", to_string=True)
# The result looks like:
# "type = const struct myns::QString::Null {"
# " <no data fields>"
# "}"
pos1 = str.find("struct") + 7
pos2 = str.find("QString::Null")
if pos1 > -1 and pos2 > -1:
qqNs = str[pos1:pos2]
return qqNs
return ""
except:
return ""
def stripTypedefs(type):
type = type.unqualified()
......@@ -698,23 +619,8 @@ class LocalItem:
#######################################################################
def bbedit(args):
global qqEditable
(type, expr, value) = args.split(",")
type = b16decode(type)
ns = qtNamespace()
if type.startswith(ns):
type = type[len(ns):]
type = type.replace("::", "__")
pos = type.find('<')
if pos != -1:
type = type[0:pos]
expr = b16decode(expr)
value = b16decode(value)
#warn("EDIT: %s %s %s %s: " % (pos, type, expr, value))
if qqEditable.has_key(type):
qqEditable[type](expr, value)
else:
gdb.execute("set (%s)=%s" % (expr, value))
#(type, expr, value) = args.split(",")
theDumper.bbedit(args.split(","))
registerCommand("bbedit", bbedit)
......@@ -781,7 +687,9 @@ class Dumper(DumperBase):
# These values will be kept between calls to 'run'.
self.isGdb = True
self.childEventAddress = None
self.cachedQtVersion = None
# Later set, or not set:
#self.cachedQtVersion
def run(self, args):
self.output = []
......@@ -841,9 +749,7 @@ class Dumper(DumperBase):
self.partialUpdate = "partial" in options
self.tooltipOnly = "tooltiponly" in options
self.noLocals = "nolocals" in options
self.ns = qtNamespace()
#warn("NAMESPACE: '%s'" % self.ns)
#warn("NAMESPACE: '%s'" % self.qtNamespace())
#warn("VARIABLES: %s" % varList)
#warn("EXPANDED INAMES: %s" % self.expandedINames)
#warn("WATCHERS: %s" % watchers)
......@@ -1209,10 +1115,14 @@ class Dumper(DumperBase):
def selectedInferior(self):
try:
# gdb.Inferior is new in gdb 7.2
return gdb.selected_inferior()
self.cachedInferior = gdb.selected_inferior()
except:
# Pre gdb 7.4. Right now we don't have more than one inferior anyway.
return gdb.inferiors()[0]
self.cachedInferior = gdb.inferiors()[0]
# Memoize result.
self.selectedInferior = lambda: self.cachedInferior
return self.cachedInferior
def readRawMemory(self, addr, size):
mem = self.selectedInferior().read_memory(addr, size)
......@@ -1289,16 +1199,20 @@ class Dumper(DumperBase):
return xrange(min(toInteger(self.currentMaxNumChild), toInteger(self.currentNumChild)))
def qtVersion(self):
if self.cachedQtVersion is None:
try:
self.cachedQtVersion = extractQtVersion()
except:
try:
self.cachedQtVersion = extractQtVersion()
# This will fail on Qt 5
gdb.execute("ptype QString::shared_empty", to_string=True)
self.cachedQtVersion = 0x040800
except:
try:
# This will fail on Qt 5
gdb.execute("ptype QString::shared_empty", to_string=True)
self.cachedQtVersion = 0x040800
except:
self.cachedQtVersion = 0x050000
#self.cachedQtVersion = 0x050000
# Assume Qt 5 until we have a definitive answer.
return 0x050000
# Memoize good results.
self.qtVersion = lambda: self.cachedQtVersion
return self.cachedQtVersion
# Convenience function.
......@@ -1390,8 +1304,9 @@ class Dumper(DumperBase):
def stripNamespaceFromType(self, typeName):
type = stripClassTag(typeName)
if len(self.ns) > 0 and type.startswith(self.ns):
type = type[len(self.ns):]
ns = self.qtNamespace()
if len(ns) > 0 and type.startswith(ns):
type = type[len(ns):]
pos = type.find("<")
# FIXME: make it recognize foo<A>::bar<B>::iterator?
while pos != -1:
......@@ -1425,12 +1340,6 @@ class Dumper(DumperBase):
self.putType(type)
self.putNumChild(0)
def currentItemFormat(self):
format = self.formats.get(self.currentIName)
if format is None:
format = self.typeformats.get(stripForFormat(str(self.currentType)))
return format
def putSubItem(self, component, value, tryDynamic=True):
with SubItem(self, component):
self.putItem(value, tryDynamic)
......@@ -1563,8 +1472,6 @@ class Dumper(DumperBase):
self.putNumChild(0)
return
global qqDumpers, qqFormats
type = value.type.unqualified()
typeName = str(type)
tryDynamic &= self.useDynamicType
......@@ -1653,9 +1560,9 @@ class Dumper(DumperBase):
return
if type.code == TypedefCode:
if typeName in qqDumpers:
if typeName in self.qqDumpers:
self.putType(typeName)
qqDumpers[typeName](self, value)
self.qqDumpers[typeName](self, value)
return
type = stripTypedefs(type)
......@@ -1708,9 +1615,7 @@ class Dumper(DumperBase):
innerType = type.target()
innerTypeName = str(innerType.unqualified())
format = self.formats.get(self.currentIName)
if format is None:
format = self.typeformats.get(stripForFormat(str(type)))
format = self.currentItemFormat(type)
if innerType.code == VoidCode:
#warn("VOID POINTER: %s" % format)
......@@ -1868,9 +1773,7 @@ class Dumper(DumperBase):
self.putItem(expensiveDowncast(value), False)
return
format = self.formats.get(self.currentIName)
if format is None:
format = self.typeformats.get(stripForFormat(typeName))
format = self.currentItemFormat(typeName)
if self.useFancy and (format is None or format >= 1):
self.putType(typeName)
......@@ -1890,9 +1793,9 @@ class Dumper(DumperBase):
return
#warn(" STRIPPED: %s" % nsStrippedType)
#warn(" DUMPERS: %s" % qqDumpers)
#warn(" DUMPERS: %s" % (nsStrippedType in qqDumpers))
dumper = qqDumpers.get(nsStrippedType, None)
#warn(" DUMPERS: %s" % self.qqDumpers)
#warn(" DUMPERS: %s" % (nsStrippedType in self.qqDumpers))
dumper = self.qqDumpers.get(nsStrippedType, None)
if not dumper is None:
if tryDynamic:
dumper(self, expensiveDowncast(value))
......@@ -2031,10 +1934,57 @@ class Dumper(DumperBase):
with Children(self, 1):
self.listAnonymous(value, name, field.type)
def registerDumper(self, funcname, function):
try:
#warn("FUNCTION: %s " % funcname)
#funcname = function.func_name
if funcname.startswith("qdump__"):
type = funcname[7:]
self.qqDumpers[type] = function
self.qqFormats[type] = self.qqFormats.get(type, "")
elif funcname.startswith("qform__"):
type = funcname[7:]
formats = ""
try:
formats = function()
except:
pass
self.qqFormats[type] = formats
elif funcname.startswith("qedit__"):
type = funcname[7:]
try:
self.qqEditable[type] = function
except:
pass
except:
pass
def bbsetup(self):
self.qqDumpers = {}
self.qqFormats = {}
self.qqEditable = {}
self.typeCache = {}
module = sys.modules[__name__]
#warn("KEYS: %s " % module.__dict__.keys())
for name in module.__dict__.keys():
#warn("KEY: %s " % name)
#warn("FUNCT: %s " % module.__dict__[name])
self.registerDumper(name, module.__dict__[name])
result = "dumpers=["
for key, value in self.qqFormats.items():
if key in self.qqEditable:
result += '{type="%s",formats="%s",editable="true"},' % (key, value)
else:
result += '{type="%s",formats="%s"},' % (key, value)
result += ']'
return result
def threadname(self, maximalStackDepth):
e = gdb.selected_frame()
ns = qtNamespace()
out = ""
ns = self.qtNamespace()
while True:
maximalStackDepth -= 1
if maximalStackDepth < 0:
......@@ -2087,6 +2037,54 @@ class Dumper(DumperBase):
return out + ']'
def importPlainDumper(self, printer):
name = printer.name.replace("::", "__")
self.qqDumpers[name] = PlainDumper(printer)
self.qqFormats[name] = ""
def importPlainDumpers(self):
for obj in gdb.objfiles():
for printers in obj.pretty_printers + gdb.pretty_printers:
for printer in printers.subprinters:
self.importPlainDumper(printer)
def qtNamespace(self):
# FIXME: This only works when call from inside a Qt function frame.
namespace = ""
try:
str = gdb.execute("ptype QString::Null", to_string=True)
# The result looks like:
# "type = const struct myns::QString::Null {"
# " <no data fields>"
# "}"
pos1 = str.find("struct") + 7
pos2 = str.find("QString::Null")
if pos1 > -1 and pos2 > -1:
namespace = str[pos1:pos2]
self.cachedQtNamespace = namespace
self.ns = lambda: self.cachedQtNamespace
except:
pass
return namespace
def bbedit(self, type, expr, value):
type = b16decode(type)
ns = self.qtNamespace()
if type.startswith(ns):
type = type[len(ns):]
type = type.replace("::", "__")
pos = type.find('<')
if pos != -1:
type = type[0:pos]
expr = b16decode(expr)
value = b16decode(value)
#warn("EDIT: %s %s %s %s: " % (pos, type, expr, value))
if self.qqEditable.has_key(type):
self.qqEditable[type](expr, value)
else:
gdb.execute("set (%s)=%s" % (expr, value))
# Global instance.
theDumper = Dumper()
......
......@@ -78,27 +78,6 @@ import lldb
qqWatchpointOffset = 10000
def registerDumper(function):
if hasattr(function, 'func_name'):
funcname = function.func_name
if funcname.startswith("qdump__"):
type = funcname[7:]
qqDumpers[type] = function
qqFormats[type] = qqFormats.get(type, "")
elif funcname.startswith("qform__"):
type = funcname[7:]
formats = ""
try:
formats = function()
except:
pass
qqFormats[type] = formats
elif funcname.startswith("qedit__"):
type = funcname[7:]
try:
qqEditable[type] = function
except:
pass
def warn(message):
print('\n\nWARNING="%s",\n' % message.encode("latin1").replace('"', "'"))
......@@ -293,7 +272,6 @@ class Dumper(DumperBase):
self.expandedINames = {}
self.passExceptions = True
self.useLldbDumpers = False
self.ns = ""
self.autoDerefPointers = True
self.useDynamicType = True
self.useFancy = True
......@@ -449,10 +427,7 @@ class Dumper(DumperBase):
return typeobj.GetTypeClass() in (lldb.eTypeClassStruct, lldb.eTypeClassClass)
def qtVersion(self):
global qqVersion
if not qqVersion is None:
return qqVersion
qqVersion = 0x0
self.cachedQtVersion = 0x0
coreExpression = re.compile(r"(lib)?Qt5?Core")
for n in range(0, self.target.GetNumModules()):
module = self.target.GetModuleAtIndex(n)
......@@ -461,10 +436,13 @@ class Dumper(DumperBase):
reverseVersion.reverse()
shift = 0
for v in reverseVersion:
qqVersion += v << shift
self.cachedQtVersion += v << shift
shift += 8
break
return qqVersion
# Memoize good results.
self.qtVersion = lambda: self.cachedQtVersion
return self.cachedQtVersion
def intSize(self):
return 4
......@@ -536,12 +514,6 @@ class Dumper(DumperBase):
def putField(self, name, value):
self.put('%s="%s",' % (name, value))
def currentItemFormat(self):
format = self.formats.get(self.currentIName)
if format is None:
format = self.typeformats.get(stripForFormat(str(self.currentType)))
return format
def isMovableType(self, type):
if type.GetTypeClass() in (lldb.eTypeClassBuiltin, lldb.eTypeClassPointer):
return True
......@@ -845,11 +817,16 @@ class Dumper(DumperBase):
self.currentValuePriority = priority
self.currentValueEncoding = encoding