From 00199039e723c57f66aa90e1ffcaf19f00e2efff Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 26 Mar 2015 13:03:38 +0100 Subject: [PATCH] Debugger: Fix display of expandable items in GDB and LLDB Move common code to dumper.py and debuggerengine.cpp and fix it there. Change-Id: I20d91d1aa7400fbdb27938c10cf40c8f6019df0a Reviewed-by: Christian Stenger --- share/qtcreator/debugger/dumper.py | 10 ++++ share/qtcreator/debugger/gdbbridge.py | 34 ++++-------- share/qtcreator/debugger/lldbbridge.py | 34 +++++------- src/plugins/debugger/debuggerengine.cpp | 70 +++++++++++++++++++++++- src/plugins/debugger/debuggerengine.h | 4 ++ src/plugins/debugger/gdb/gdbengine.cpp | 69 ++++------------------- src/plugins/debugger/gdb/gdbengine.h | 16 +----- src/plugins/debugger/lldb/lldbengine.cpp | 34 ++---------- src/plugins/debugger/lldb/lldbengine.h | 1 - src/plugins/debugger/watchhandler.h | 5 +- 10 files changed, 128 insertions(+), 149 deletions(-) diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index 52e475e27f8..0326b0dcbcc 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -1650,6 +1650,16 @@ class DumperBase: pass return False, 0, 1, 1, exp + def putNumChild(self, numchild): + if numchild != self.currentChildNumChild: + self.put('numchild="%s",' % numchild) + + def handleWatches(self, args): + for watcher in args.get("watchers", []): + iname = watcher['iname'] + exp = self.hexdecode(watcher['exp']) + self.handleWatch(exp, exp, iname) + def handleWatch(self, origexp, exp, iname): exp = str(exp).strip() escapedExp = self.hexencode(exp) diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index ecc0e03d46f..e8f07d68f38 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -246,7 +246,6 @@ class Dumper(DumperBase): # dumpers causing loading of shared objects etc). self.currentQtNamespaceGuess = None - self.partialVariable = args.get("vars", "") self.resultVarName = args.get("resultvarname", "") self.expandedINames = set(args.get("expanded", [])) self.stringCutOff = int(args.get("stringcutoff", 10000)) @@ -264,17 +263,8 @@ class Dumper(DumperBase): self.partialUpdate = int(args.get("partial", "0")) self.fallbackQtVersion = 0x50200 #warn("NAMESPACE: '%s'" % self.qtNamespace()) - #warn("VARIABLES: %s" % self.varList) #warn("EXPANDED INAMES: %s" % self.expandedINames) #warn("WATCHERS: %s" % self.watchers) - #warn("PARTIAL: %s" % self.partialUpdate) - - def handleWatches(self): - with OutputSafer(self): - for watcher in self.watchers: - iname = watcher['iname'] - exp = self.hexdecode(watcher['exp']) - self.handleWatch(exp, exp, iname) def listOfLocals(self): frame = gdb.selected_frame() @@ -360,6 +350,9 @@ class Dumper(DumperBase): def showData(self, args): self.prepare(args) + partialVariable = args.get("partialVariable", "") + isPartial = len(partialVariable) > 0 + # # Locals # @@ -368,12 +361,9 @@ class Dumper(DumperBase): if self.qmlcontext: locals = self.extractQmlVariables(self.qmlcontext) - elif self.partialUpdate: - #warn("PARTIAL: %s" % self.varList) - parts = self.partialVariable.split('.') - #warn("PARTIAL PARTS: %s" % parts) + elif isPartial: + parts = partialVariable.split('.') name = parts[1] - #warn("PARTIAL VAR: %s" % name) item = self.LocalItem() item.iname = parts[0] + '.' + name item.name = name @@ -389,7 +379,6 @@ class Dumper(DumperBase): except: item.value = "" locals = [item] - #warn("PARTIAL LOCALS: %s" % locals) else: locals = self.listOfLocals() @@ -419,9 +408,8 @@ class Dumper(DumperBase): self.put('name="%s",' % item.name) self.putItem(value) - self.handleWatches() - - #print('data=[' + locals + sep + self.watchers + ']\n') + with OutputSafer(self): + self.handleWatches(args) self.output.append('],typeinfo=[') for name in self.typesToReport.keys(): @@ -439,6 +427,9 @@ class Dumper(DumperBase): if self.qtNamespaceToReport: self.output.append(',qtnamespace="%s"' % self.qtNamespaceToReport) self.qtNamespaceToReport = None + + self.output.append(',partial="%d"' % isPartial) + print(''.join(self.output)) def enterSubItem(self, item): @@ -823,11 +814,6 @@ class Dumper(DumperBase): except: pass - def putNumChild(self, numchild): - #warn("NUM CHILD: '%s' '%s'" % (numchild, self.currentChildNumChild)) - if numchild != self.currentChildNumChild: - self.put('numchild="%s",' % numchild) - def putSimpleValue(self, value, encoding = None, priority = 0): self.putValue(value, encoding, priority) diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index 85bb4c1c1a4..f8b1f930d9d 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -211,7 +211,7 @@ class Dumper(DumperBase): self.currentMaxNumChild = None self.currentPrintsAddress = None self.currentChildType = None - self.currentChildNumChild = None + self.currentChildNumChild = -1 self.currentWatchers = {} self.executable_ = None @@ -535,11 +535,6 @@ class Dumper(DumperBase): return True return self.isKnownMovableType(self.stripNamespaceFromType(type.GetName())) - def putNumChild(self, numchild): - #self.warn("NUM CHILD: '%s' '%s'" % (numchild, self.currentChildNumChild)) - #if numchild != self.currentChildNumChild: - self.put('numchild="%s",' % numchild) - def putPointerValue(self, value): # Use a lower priority if value is None: @@ -1138,18 +1133,26 @@ class Dumper(DumperBase): self.reportVariablesHelper(args) sys.stdout.write("@\n") - def reportVariablesHelper(self, _ = None): + def reportVariablesHelper(self, args = None): frame = self.currentFrame() if frame is None: return + + partialVariable = args.get("partialVariable", "") + isPartial = len(partialVariable) > 0 + self.currentIName = 'local' - self.put('data=[') + self.put('all={data=[') self.anonNumber = 0 shadowed = {} ids = {} # Filter out duplicates entries at the same address. - values = list(frame.GetVariables(True, True, False, False)) - values.reverse() # To get shadowed vars numbered backwards. + if isPartial: + values = [frame.FindVariable(partialVariable)] + else: + values = list(frame.GetVariables(True, True, False, False)) + values.reverse() # To get shadowed vars numbered backwards. + for value in values: if not value.IsValid(): continue @@ -1198,16 +1201,9 @@ class Dumper(DumperBase): self.putEmptyValue() self.putNumChild(0) - # 'watchers':[{'id':'watch.0','exp':'23'},...] - #if not self.dummyValue is None: - for watcher in self.currentWatchers: - iname = watcher['iname'] - # could be 'watch.0' or 'tooltip.deadbead' - (base, component) = iname.split('.') - exp = self.hexdecode(watcher['exp']) - self.handleWatch(exp, exp, iname) + self.handleWatches(args) - self.put(']') + self.put('],partial="%d"}' % isPartial) def reportData(self, _ = None): if self.process is None: diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 8c7989bd72c..b2d028c23d3 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -40,8 +40,10 @@ #include "breakhandler.h" #include "disassembleragent.h" +#include "logwindow.h" #include "memoryagent.h" #include "moduleshandler.h" +#include "gdb/gdbengine.h" // REMOVE #include "registerhandler.h" #include "sourcefileshandler.h" #include "stackhandler.h" @@ -153,6 +155,12 @@ enum RemoteSetupState { RemoteSetupNone, RemoteSetupRequested, RemoteSetupSucceeded, RemoteSetupFailed, RemoteSetupCancelled }; +struct TypeInfo +{ + TypeInfo(uint s = 0) : size(s) {} + uint size; +}; + class DebuggerEnginePrivate : public QObject { Q_OBJECT @@ -323,6 +331,8 @@ public: bool m_isStateDebugging; Utils::FileInProjectFinder m_fileFinder; + QHash m_typeInfoCache; + QByteArray m_qtNamespace; }; @@ -1514,7 +1524,12 @@ bool DebuggerEngine::isSynchronous() const QByteArray DebuggerEngine::qtNamespace() const { - return QByteArray(); + return d->m_qtNamespace; +} + +void DebuggerEngine::setQtNamespace(const QByteArray &ns) +{ + d->m_qtNamespace = ns; } void DebuggerEngine::createSnapshot() @@ -1908,6 +1923,59 @@ void DebuggerEngine::validateExecutable(DebuggerStartParameters *sp) } } +void DebuggerEngine::updateLocalsView(const GdbMi &all) +{ + WatchHandler *handler = watchHandler(); + + const bool partial = all["partial"].toInt(); + + const GdbMi typeInfo = all["typeinfo"]; + if (typeInfo.type() == GdbMi::List) { + foreach (const GdbMi &s, typeInfo.children()) { + const GdbMi name = s["name"]; + const GdbMi size = s["size"]; + if (name.isValid() && size.isValid()) + d->m_typeInfoCache.insert(QByteArray::fromHex(name.data()), + TypeInfo(size.data().toUInt())); + } + } + + QSet toDelete; + if (!partial) { + foreach (WatchItem *item, handler->model()->treeLevelItems(2)) + toDelete.insert(item->iname); + } + + GdbMi data = all["data"]; + foreach (const GdbMi &child, data.children()) { + WatchItem *item = new WatchItem(child); + const TypeInfo ti = d->m_typeInfoCache.value(item->type); + if (ti.size) + item->size = ti.size; + + handler->insertItem(item); + toDelete.remove(item->iname); + } + + GdbMi ns = all["qtnamespace"]; + if (ns.isValid()) { + setQtNamespace(ns.data()); + showMessage(_("FOUND NAMESPACED QT: " + ns.data())); + } + + handler->purgeOutdatedItems(toDelete); + + static int count = 0; + showMessage(_("") + .arg(++count).arg(LogWindow::logTimeStamp()), LogMiscInput); + showStatusMessage(GdbEngine::tr("Finished retrieving data"), 400); // FIXME: String + + DebuggerToolTipManager::updateEngine(this); + + if (!partial) + emit stackFrameCompleted(); +} + } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index fb11e6553bf..33b943e6cc6 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -60,6 +60,7 @@ namespace Internal { class DebuggerEnginePrivate; class DebuggerPluginPrivate; class DisassemblerAgent; +class GdbMi; class MemoryAgent; class WatchData; class WatchItem; @@ -185,6 +186,7 @@ public: virtual bool isSynchronous() const; virtual QByteArray qtNamespace() const; + void setQtNamespace(const QByteArray &ns); virtual void createSnapshot(); virtual void updateAll(); @@ -374,6 +376,8 @@ protected: virtual void slaveEngineStateChanged(DebuggerEngine *engine, DebuggerState state); + void updateLocalsView(const GdbMi &all); + private: // Wrapper engine needs access to state of its subengines. friend class QmlCppEngine; diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index fa6810d8400..f2f683454d3 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -3705,9 +3705,8 @@ bool GdbEngine::setToolTipExpression(const DebuggerToolTipContext &context) return false; UpdateParameters params; - params.tryPartial = true; - params.varList = context.iname; - updateLocalsPython(params); + params.partialVariable = context.iname; + doUpdateLocals(params); return true; } @@ -3727,9 +3726,8 @@ void GdbEngine::reloadLocals() void GdbEngine::updateWatchItem(WatchItem *item) { UpdateParameters params; - params.tryPartial = m_pendingBreakpointRequests == 0; - params.varList = item->iname; - updateLocalsPython(params); + params.partialVariable = item->iname; + doUpdateLocals(params); } void GdbEngine::handleVarAssign(const DebuggerResponse &) @@ -3743,7 +3741,7 @@ void GdbEngine::updateLocals() { watchHandler()->resetValueCache(); watchHandler()->notifyUpdateStarted(); - updateLocalsPython(UpdateParameters()); + doUpdateLocals(UpdateParameters()); } void GdbEngine::assignValueInDebugger(WatchItem *item, @@ -4696,7 +4694,7 @@ void addGdbOptionPages(QList *opts) opts->push_back(new GdbOptionsPage2()); } -void GdbEngine::updateLocalsPython(const UpdateParameters ¶ms) +void GdbEngine::doUpdateLocals(const UpdateParameters ¶ms) { m_pendingBreakpointRequests = 0; @@ -4734,7 +4732,6 @@ void GdbEngine::updateLocalsPython(const UpdateParameters ¶ms) cmd.arg("autoderef", boolSetting(AutoDerefPointers)); cmd.arg("dyntype", boolSetting(UseDynamicType)); cmd.arg("nativemixed", isNativeMixedActive()); - cmd.arg("partial", params.tryPartial); if (isNativeMixedActive()) { StackFrame frame = stackHandler()->currentFrame(); @@ -4743,16 +4740,16 @@ void GdbEngine::updateLocalsPython(const UpdateParameters ¶ms) } cmd.arg("resultvarname", m_resultVarName); - cmd.arg("vars", params.varList); + cmd.arg("partialVariable", params.partialVariable); cmd.flags = Discardable; - cmd.callback = [this, params](const DebuggerResponse &r) { handleStackFramePython(r, params.tryPartial); }; + cmd.callback = [this, params](const DebuggerResponse &r) { handleStackFramePython(r); }; runCommand(cmd); cmd.arg("passExceptions", true); m_lastDebuggableCommand = cmd; } -void GdbEngine::handleStackFramePython(const DebuggerResponse &response, bool partial) +void GdbEngine::handleStackFramePython(const DebuggerResponse &response) { watchHandler()->notifyUpdateFinished(); if (response.resultClass == ResultDone) { @@ -4767,54 +4764,8 @@ void GdbEngine::handleStackFramePython(const DebuggerResponse &response, bool pa } GdbMi all; all.fromStringMultiple(out); - GdbMi data = all["data"]; - GdbMi ns = all["qtnamespace"]; - if (ns.isValid()) { - setQtNamespace(ns.data()); - showMessage(_("FOUND NAMESPACED QT: " + ns.data())); - } - - WatchHandler *handler = watchHandler(); - - const GdbMi typeInfo = all["typeinfo"]; - if (typeInfo.type() == GdbMi::List) { - foreach (const GdbMi &s, typeInfo.children()) { - const GdbMi name = s["name"]; - const GdbMi size = s["size"]; - if (name.isValid() && size.isValid()) - m_typeInfoCache.insert(QByteArray::fromHex(name.data()), - TypeInfo(size.data().toUInt())); - } - } - - QSet toDelete; - if (!partial) { - foreach (WatchItem *item, handler->model()->treeLevelItems(2)) - toDelete.insert(item->iname); - } - - foreach (const GdbMi &child, data.children()) { - WatchItem *item = new WatchItem(child); - const TypeInfo ti = m_typeInfoCache.value(item->type); - if (ti.size) - item->size = ti.size; - - handler->insertItem(item); - toDelete.remove(item->iname); - } - - handler->purgeOutdatedItems(toDelete); - - static int count = 0; - showMessage(_("") - .arg(++count).arg(LogWindow::logTimeStamp()), LogMiscInput); - showStatusMessage(tr("Finished retrieving data"), 400); - - DebuggerToolTipManager::updateEngine(this); - - if (!partial) - emit stackFrameCompleted(); + updateLocalsView(all); } else { showMessage(_("DUMPER FAILED: " + response.toString())); diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index dff3c80d3ef..6c079805856 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -87,8 +87,6 @@ private: ////////// General Interface ////////// virtual bool acceptsDebuggerCommands() const; virtual void executeDebuggerCommand(const QString &command, DebuggerLanguages languages); - virtual QByteArray qtNamespace() const { return m_qtNamespace; } - virtual void setQtNamespace(const QByteArray &ns) { m_qtNamespace = ns; } private: ////////// General State ////////// @@ -409,26 +407,16 @@ protected: void handleCreateFullBacktrace(const DebuggerResponse &response); void updateLocals(); - void updateLocalsPython(const UpdateParameters ¶meters); - void handleStackFramePython(const DebuggerResponse &response, bool partial); + void doUpdateLocals(const UpdateParameters ¶meters); + void handleStackFramePython(const DebuggerResponse &response); void setLocals(const QList &locals); - struct TypeInfo - { - TypeInfo(uint s = 0) : size(s) {} - - uint size; - }; - - QHash m_typeInfoCache; - // // Dumper Management // void reloadDebuggingHelpers(); - QByteArray m_qtNamespace; QString m_gdb; // diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 795b1f77662..7362a242117 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -445,9 +445,10 @@ void LldbEngine::handleResponse(const QByteArray &response) foreach (const GdbMi &item, all.children()) { const QByteArray name = item.name(); - if (name == "data") - refreshLocals(item); - else if (name == "dumpers") { + if (name == "all") { + watchHandler()->notifyUpdateFinished(); + updateLocalsView(item); + } else if (name == "dumpers") { watchHandler()->addDumpers(item); setupInferiorStage2(); } else if (name == "stack") @@ -821,8 +822,7 @@ bool LldbEngine::setToolTipExpression(const DebuggerToolTipContext &context) } UpdateParameters params; - params.tryPartial = true; - params.varList = context.iname; + params.partialVariable = context.iname; doUpdateLocals(params); return true; @@ -893,7 +893,7 @@ void LldbEngine::doUpdateLocals(UpdateParameters params) cmd.arg("fancy", boolSetting(UseDebuggingHelpers)); cmd.arg("autoderef", boolSetting(AutoDerefPointers)); cmd.arg("dyntype", boolSetting(UseDynamicType)); - cmd.arg("partial", params.tryPartial); + cmd.arg("partialVariable", params.partialVariable); cmd.beginList("watchers"); @@ -1004,28 +1004,6 @@ void LldbEngine::readLldbStandardOutput() } } -void LldbEngine::refreshLocals(const GdbMi &vars) -{ - //const bool partial = response.cookie.toBool(); - WatchHandler *handler = watchHandler(); - handler->resetValueCache(); - - QSet toDelete; - foreach (WatchItem *item, handler->model()->treeLevelItems(2)) - toDelete.insert(item->iname); - - foreach (const GdbMi &child, vars.children()) { - WatchItem *item = new WatchItem(child); - handler->insertItem(item); - toDelete.remove(item->iname); - } - - handler->purgeOutdatedItems(toDelete); - handler->notifyUpdateFinished(); - - DebuggerToolTipManager::updateEngine(this); - } - void LldbEngine::refreshStack(const GdbMi &stack) { StackHandler *handler = stackHandler(); diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index a72e5157db2..f334d218eb1 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -151,7 +151,6 @@ private: void refreshCurrentThread(const GdbMi &data); void refreshStack(const GdbMi &stack); void refreshRegisters(const GdbMi ®isters); - void refreshLocals(const GdbMi &vars); void refreshTypeInfo(const GdbMi &typeInfo); void refreshState(const GdbMi &state); void refreshLocation(const GdbMi &location); diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h index 5f7a6cb128e..0d9f53abec3 100644 --- a/src/plugins/debugger/watchhandler.h +++ b/src/plugins/debugger/watchhandler.h @@ -128,10 +128,9 @@ private: class UpdateParameters { public: - UpdateParameters() { tryPartial = false; } + UpdateParameters() {} - bool tryPartial; - QByteArray varList; + QByteArray partialVariable; }; class WatchModelBase : public Utils::TreeModel