diff options
author | hjk <hjk121@nokiamail.com> | 2014-06-13 17:45:34 +0200 |
---|---|---|
committer | hjk <hjk121@nokiamail.com> | 2014-06-16 16:28:59 +0200 |
commit | fffbf9472ae8f78e286ac88ccde12c161aeeeb9b (patch) | |
tree | 7f0b331992cd29f78cab4807d4388fe6879c5ba3 /share | |
parent | 130e66cff25ebdcd375ba6d746fb822bddee3d73 (diff) | |
download | qt-creator-fffbf9472ae8f78e286ac88ccde12c161aeeeb9b.tar.gz |
Debugger: Make dumpers somewhat work in command line GDB
With
python sys.path.insert(1, '/data/dev/creator/share/qtcreator/debugger/')
python from gdbbridge import *
in .gdbinit there's a new "GDB command", called "pp".
With code like
int main(int argc, char *argv[])
{
QString ss = "Hello";
QApplication app(argc, argv);
app.setObjectName(ss);
// break here
}
the 'pp' command can be used as follows:
(gdb) pp app
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = <Myns::QObjectList> = {"<3 items>"}
[properties] = "<>0 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp app [properties],[children]
app =
[
<Myns::QGuiApplication> = {"Hello"}
staticMetaObject = <Myns::QMetaObject> = {""}
[parent] = <Myns::QObject *> = {"0x0"}
[children] = [
<Myns::QObject> = {""}
<Myns::QObject> = {""}
<Myns::QObject> = {"fusion"}
],<Myns::QObjectList> = {"<3 items>"}
[properties] = [
windowIcon = <Myns::QVariant (QIcon)> = {""}
cursorFlashTime = <Myns::QVariant (int)> = {"1000"}
doubleClickInterval = <Myns::QVariant (int)> = {"400"}
keyboardInputInterval = <Myns::QVariant (int)> = {"400"}
wheelScrollLines = <Myns::QVariant (int)> = {"3"}
globalStrut = <Myns::QVariant (QSize)> = {"(0, 0)"}
startDragTime = <Myns::QVariant (int)> = {"500"}
startDragDistance = <Myns::QVariant (int)> = {"10"}
styleSheet = <Myns::QVariant (QString)> = {""}
autoSipEnabled = <Myns::QVariant (bool)> = {"true"}
],"<10 items>"
[methods] = "<6 items>"
[signals] = "<1 items>"
],<Myns::QApplication> = {"Hello"}
(gdb) pp ss
ss =
<Myns::QString> = {"Hello"}
Change-Id: I6e4714a5cfe34c38917500d114ad9a70d20cff39
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
Diffstat (limited to 'share')
-rw-r--r-- | share/qtcreator/debugger/dumper.py | 45 | ||||
-rw-r--r-- | share/qtcreator/debugger/gdbbridge.py | 232 |
2 files changed, 204 insertions, 73 deletions
diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index c5ee7dc646..65c5e5f47b 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -216,7 +216,8 @@ class Children: self.childType = None else: self.childType = stripClassTag(str(childType)) - self.d.put('childtype="%s",' % self.childType) + if not self.d.isCli: + self.d.put('childtype="%s",' % self.childType) if childNumChild is None: pass #if self.d.isSimpleType(childType): @@ -228,15 +229,7 @@ class Children: else: self.d.put('childnumchild="%s",' % childNumChild) self.childNumChild = childNumChild - try: - if not addrBase is None and not addrStep is None: - self.d.put('addrbase="0x%x",' % toInteger(addrBase)) - self.d.put('addrstep="0x%x",' % toInteger(addrStep)) - self.printsAddress = False - except: - warn("ADDRBASE: %s" % addrBase) - warn("ADDRSTEP: %s" % addrStep) - #warn("CHILDREN: %s %s %s" % (numChild, childType, childNumChild)) + self.printsAddress = not self.d.putAddressRange(addrBase, addrStep) def __enter__(self): self.savedChildType = self.d.currentChildType @@ -249,7 +242,7 @@ class Children: self.d.currentNumChild = self.numChild self.d.currentMaxNumChild = self.maxNumChild self.d.currentPrintsAddress = self.printsAddress - self.d.put("children=[") + self.d.put(self.d.childrenPrefix) def __exit__(self, exType, exValue, exTraceBack): if not exType is None: @@ -265,7 +258,8 @@ class Children: self.d.currentNumChild = self.savedNumChild self.d.currentMaxNumChild = self.savedMaxNumChild self.d.currentPrintsAddress = self.savedPrintsAddress - self.d.put('],') + self.d.putNewline() + self.d.put(self.d.childrenSuffix) return True class PairedChildrenData: @@ -344,6 +338,7 @@ class DumperBase: self.isCdb = False self.isGdb = False self.isLldb = False + self.isCli = False # Later set, or not set: # cachedQtVersion @@ -366,6 +361,11 @@ class DumperBase: # to not be QObject derived, it contains a 0 value. self.knownStaticMetaObjects = {} + self.childrenPrefix = 'children=[' + self.childrenSuffix = '],' + + def putNewline(self): + pass def stripForFormat(self, typeName): if typeName in self.cachedFormats: @@ -576,6 +576,18 @@ class DumperBase: def call(self, value, func, *args): return self.callHelper(value, func, args) + def putAddressRange(self, base, step): + try: + if not addrBase is None and not step is None: + self.put('addrbase="0x%x",' % toInteger(base)) + self.put('addrstep="0x%x",' % toInteger(step)) + return True + except: + warn("ADDRBASE: %s" % base) + warn("ADDRSTEP: %s" % step) + return False + + #warn("CHILDREN: %s %s %s" % (numChild, childType, childNumChild)) def putMapName(self, value, index = -1): ns = self.qtNamespace() if str(value.type) == ns + "QString": @@ -942,8 +954,7 @@ class DumperBase: self.putItem(value.dereference()) self.currentChildType = savedCurrentChildType #self.putPointerValue(value) - if not value.address is None: - self.put('origaddr="0x%x",' % toInteger(value.address)) + self.putOriginalAddress(value) return #warn("GENERIC PLAIN POINTER: %s" % value.type) @@ -956,6 +967,10 @@ class DumperBase: with SubItem(self, "*"): self.putItem(value.dereference()) + def putOriginalAddress(self, value): + if not value.address is None: + self.put('origaddr="0x%x",' % toInteger(value.address)) + def putQObjectNameValue(self, value): try: intSize = self.intSize() @@ -1012,7 +1027,7 @@ class DumperBase: return True except: - #warn("NO QOBJECT: %s" % value.type) + # warn("NO QOBJECT: %s" % value.type) pass diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index 393ffcf759..f14bc7cad2 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -226,7 +226,8 @@ ScanStackCommand() def bbsetup(args = ''): - print(theDumper.bbsetup()) + theDumper.bbsetup() + print(theDumper.reportDumpers()) registerCommand("bbsetup", bbsetup) @@ -339,7 +340,6 @@ def bb(args): registerCommand("bb", bb) - ####################################################################### # # The Dumper Class @@ -359,7 +359,7 @@ class Dumper(DumperBase): self.typesToReport = {} self.qtNamespaceToReport = None - def run(self, args): + def prepare(self, args): self.output = [] self.currentIName = "" self.currentPrintsAddress = True @@ -380,21 +380,20 @@ class Dumper(DumperBase): # dumpers causing loading of shared objects etc). self.currentQtNamespaceGuess = None - watchers = "" - resultVarName = "" - options = [] - varList = [] + self.watchers = "" + self.resultVarName = "" + self.varList = [] + self.options = [] - self.output.append('data=[') for arg in args.split(' '): pos = arg.find(":") + 1 if arg.startswith("options:"): - options = arg[pos:].split(",") + self.options = arg[pos:].split(",") elif arg.startswith("vars:"): if len(arg[pos:]) > 0: - varList = arg[pos:].split(",") + self.varList = arg[pos:].split(",") elif arg.startswith("resultvarname:"): - resultVarName = arg[pos:] + self.resultVarName = arg[pos:] elif arg.startswith("expanded:"): self.expandedINames = set(arg[pos:].split(",")) elif arg.startswith("stringcutoff:"): @@ -413,31 +412,42 @@ class Dumper(DumperBase): if pos != -1: self.formats[f[0:pos]] = int(f[pos+1:]) elif arg.startswith("watchers:"): - watchers = self.hexdecode(arg[pos:]) + self.watchers = self.hexdecode(arg[pos:]) - self.useDynamicType = "dyntype" in options - self.useFancy = "fancy" in options - self.forceQtNamespace = "forcens" in options - self.passExceptions = "pe" in options + self.useDynamicType = "dyntype" in self.options + self.useFancy = "fancy" in self.options + self.forceQtNamespace = "forcens" in self.options + self.passExceptions = "pe" in self.options #self.passExceptions = True - self.autoDerefPointers = "autoderef" in options - self.partialUpdate = "partial" in options - self.tooltipOnly = "tooltiponly" in options + self.autoDerefPointers = "autoderef" in self.options + self.partialUpdate = "partial" in self.options + self.tooltipOnly = "tooltiponly" in self.options self.fallbackQtVersion = 0x50200 #warn("NAMESPACE: '%s'" % self.qtNamespace()) - #warn("VARIABLES: %s" % varList) + #warn("VARIABLES: %s" % self.varList) #warn("EXPANDED INAMES: %s" % self.expandedINames) - #warn("WATCHERS: %s" % watchers) + #warn("WATCHERS: %s" % self.watchers) #warn("PARTIAL: %s" % self.partialUpdate) + def handleWatches(self): + with OutputSafer(self): + if len(self.watchers) > 0: + for watcher in self.watchers.split("##"): + (exp, iname) = watcher.split("#") + self.handleWatch(exp, exp, iname) + + def run(self, args): + self.prepare(args) + # # Locals # + self.output.append('data=[') locals = [] fullUpdateNeeded = True - if self.partialUpdate and len(varList) == 1 and not self.tooltipOnly: - #warn("PARTIAL: %s" % varList) - parts = varList[0].split('.') + if self.partialUpdate and len(self.varList) == 1 and not self.tooltipOnly: + #warn("PARTIAL: %s" % self.varList) + parts = self.varList[0].split('.') #warn("PARTIAL PARTS: %s" % parts) name = parts[1] #warn("PARTIAL VAR: %s" % name) @@ -449,22 +459,22 @@ class Dumper(DumperBase): item.name = name item.value = frame.read_var(name) locals = [item] - #warn("PARTIAL LOCALS: %s" % locals) + warn("PARTIAL LOCALS: %s" % locals) fullUpdateNeeded = False except: pass - varList = [] + self.varList = [] if fullUpdateNeeded and not self.tooltipOnly: - locals = listOfLocals(varList) + locals = listOfLocals(self.varList) # Take care of the return value of the last function call. - if len(resultVarName) > 0: + if len(self.resultVarName) > 0: try: item = LocalItem() - item.name = resultVarName - item.iname = "return." + resultVarName - item.value = self.parseAndEvaluate(resultVarName) + item.name = self.resultVarName + item.iname = "return." + self.resultVarName + item.value = self.parseAndEvaluate(self.resultVarName) locals.append(item) except: # Don't bother. It's only supplementary information anyway. @@ -484,16 +494,9 @@ class Dumper(DumperBase): self.put('name="%s",' % item.name) self.putItem(value) - # - # Watchers - # - with OutputSafer(self): - if len(watchers) > 0: - for watcher in watchers.split("##"): - (exp, iname) = watcher.split("#") - self.handleWatch(exp, exp, iname) + self.handleWatches() - #print('data=[' + locals + sep + watchers + ']\n') + #print('data=[' + locals + sep + self.watchers + ']\n') self.output.append('],typeinfo=[') for name in self.typesToReport.keys(): @@ -505,7 +508,7 @@ class Dumper(DumperBase): self.output.append(']') self.typesToReport = {} - if "forcens" in options: + if "forcens" in self.options: self.qtNamepaceToRport = self.qtNamespace() if self.qtNamespaceToReport: @@ -880,7 +883,7 @@ class Dumper(DumperBase): return self.cachedIsQt3Suport def putAddress(self, addr): - if self.currentPrintsAddress: + if self.currentPrintsAddress and not self.isCli: try: # addr can be "None", int(None) fails. #self.put('addr="0x%x",' % int(addr)) @@ -904,11 +907,6 @@ class Dumper(DumperBase): self.putValue("0x%x" % value.cast( self.lookupType("unsigned long")), None, -1) - def isExpandedSubItem(self, component): - iname = "%s.%s" % (self.currentIName, component) - #warn("IS EXPANDED: %s in %s" % (iname, self.expandedINames)) - return iname in self.expandedINames - def stripNamespaceFromType(self, typeName): type = stripClassTag(typeName) ns = self.qtNamespace() @@ -1291,8 +1289,7 @@ class Dumper(DumperBase): baseNumber += 1 with UnnamedSubItem(self, "@%d" % baseNumber): baseValue = value.cast(field.type) - self.put('iname="%s",' % self.currentIName) - self.put('name="[%s]",' % field.name) + self.putBaseClassName(field.name) self.putAddress(baseValue.address) self.putItem(baseValue, False) elif len(field.name) == 0: @@ -1309,6 +1306,9 @@ class Dumper(DumperBase): # self.put("bitsize=\"%s\"" % bitsize) self.putItem(self.downcast(value[field.name])) + def putBaseClassName(self, name): + self.put('iname="%s",' % self.currentIName()) + self.put('name="[%s]",' % name) def listAnonymous(self, value, name, type): for field in type.fields(): @@ -1372,6 +1372,7 @@ class Dumper(DumperBase): for name in dic.keys(): self.registerDumper(name, dic[name]) + def reportDumpers(self): result = "dumpers=[" for key, value in self.qqFormats.items(): if key in self.qqEditable: @@ -1486,8 +1487,7 @@ class Dumper(DumperBase): def bbedit(self, args): (typeName, expr, data) = args.split(',') - d = Dumper() - typeName = d.hexdecode(typeName) + typeName = self.hexdecode(typeName) ns = self.qtNamespace() if typeName.startswith(ns): typeName = typeName[len(ns):] @@ -1495,8 +1495,8 @@ class Dumper(DumperBase): pos = typeName.find('<') if pos != -1: typeName = typeName[0:pos] - expr = d.hexdecode(expr) - data = d.hexdecode(data) + expr = self.hexdecode(expr) + data = self.hexdecode(data) if typeName in self.qqEditable: #self.qqEditable[typeName](self, expr, data) value = gdb.parse_and_eval(expr) @@ -1662,9 +1662,125 @@ class Dumper(DumperBase): return type +class CliDumper(Dumper): + def __init__(self): + Dumper.__init__(self) + self.childrenPrefix = '[' + self.chidrenSuffix = '] ' + self.indent = 0 + self.isCli = True + + def reportDumpers(self): + return "" + + def enterSubItem(self, item): + if not item.iname: + item.iname = "%s.%s" % (self.currentIName, item.name) + self.indent += 1 + self.putNewline() + if isinstance(item.name, str): + self.output += item.name + ' = ' + item.savedIName = self.currentIName + item.savedValue = self.currentValue + item.savedType = self.currentType + item.savedCurrentAddress = self.currentAddress + self.currentIName = item.iname + self.currentValue = ReportItem(); + self.currentType = ReportItem(); + self.currentAddress = None + + def exitSubItem(self, item, exType, exValue, exTraceBack): + self.indent -= 1 + #warn("CURRENT VALUE: %s: %s %s" % (self.currentIName, self.currentValue, self.currentType)) + if not exType is None: + if self.passExceptions: + showException("SUBITEM", exType, exValue, exTraceBack) + self.putNumChild(0) + self.putValue("<not accessible>") + try: + if self.currentType.value: + typeName = stripClassTag(self.currentType.value) + self.put('<%s> = {' % typeName) + + if self.currentValue.value is None: + self.put('<not accessible>') + else: + value = self.currentValue.value + if self.currentValue.encoding is Hex2EncodedLatin1: + value = self.hexdecode(value) + elif self.currentValue.encoding is Hex2EncodedUtf8: + value = self.hexdecode(value) + elif self.currentValue.encoding is Hex4EncodedLittleEndian: + b = bytes.fromhex(value) + value = codecs.decode(b, 'utf-16') + self.put('"%s"' % value) + if self.currentValue.elided: + self.put('...') + + if self.currentType.value: + self.put('}') + except: + pass + if not self.currentAddress is None: + self.put(self.currentAddress) + self.currentIName = item.savedIName + self.currentValue = item.savedValue + self.currentType = item.savedType + self.currentAddress = item.savedCurrentAddress + return True + + def putNewline(self): + self.output += '\n' + ' ' * self.indent + + def put(self, line): + if self.output.endswith('\n'): + self.output = self.output[0:-1] + self.output += line + + def putNumChild(self, numchild): + pass + + def putBaseClassName(self, name): + pass + + def putOriginalAddress(self, value): + pass + + def putAddressRange(self, base, step): + return True + + def run(self, args): + arglist = args.split(' ') + name = '' + if len(arglist) >= 1: + name = arglist[0] + allexpanded = [name] + if len(arglist) >= 2: + for sub in arglist[1].split(','): + allexpanded.append(name + '.' + sub) + self.prepare("options:fancy,pe,autoderef expanded:" + ','.join(allexpanded)) + self.output = name + ' = ' + frame = gdb.selected_frame() + value = frame.read_var(name) + with TopLevelItem(self, name): + self.putItem(value) + return self.output + + +def pp(args): + return theDumper.run(args) + +registerCommand("pp", pp) + # Global instance. -theDumper = Dumper() +if gdb.parameter('height') is None: + print("Using MI") + theDumper = Dumper() +else: + print("Using CLI") + import codecs + theDumper = CliDumper() ####################################################################### # @@ -1690,14 +1806,14 @@ def p2(args): registerCommand("p2", p2) -def profileit(args): +def p3(args): eval(args) -def profile(args): +def p3(args): import timeit - return timeit.repeat('profileit("%s")' % args, 'from __main__ import profileit', number=10000) + return timeit.repeat('p3("%s")' % args, 'from __main__ import p3', number=10000) -registerCommand("pp", profile) +registerCommand("p3", p3) ####################################################################### # |