Commit 00199039 authored by hjk's avatar hjk

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: default avatarChristian Stenger <christian.stenger@theqtcompany.com>
parent 54438b21
......@@ -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)
......
......@@ -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 = "<no 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)
......
......@@ -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:
......
......@@ -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<QByteArray, TypeInfo> 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<QByteArray> toDelete;
if (!partial) {
foreach (WatchItem *item, handler->model()->treeLevelItems<WatchItem *>(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(_("<Rebuild Watchmodel %1 @ %2 >")
.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
......
......@@ -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;
......
......@@ -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<IOptionsPage *> *opts)
opts->push_back(new GdbOptionsPage2());
}
void GdbEngine::updateLocalsPython(const UpdateParameters &params)
void GdbEngine::doUpdateLocals(const UpdateParameters &params)
{
m_pendingBreakpointRequests = 0;
......@@ -4734,7 +4732,6 @@ void GdbEngine::updateLocalsPython(const UpdateParameters &params)
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 &params)
}
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<QByteArray> toDelete;
if (!partial) {
foreach (WatchItem *item, handler->model()->treeLevelItems<WatchItem *>(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(_("<Rebuild Watchmodel %1 @ %2 >")
.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()));
......
......@@ -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 &parameters);
void handleStackFramePython(const DebuggerResponse &response, bool partial);
void doUpdateLocals(const UpdateParameters &parameters);
void handleStackFramePython(const DebuggerResponse &response);
void setLocals(const QList<GdbMi> &locals);
struct TypeInfo
{
TypeInfo(uint s = 0) : size(s) {}
uint size;
};
QHash<QByteArray, TypeInfo> m_typeInfoCache;
//
// Dumper Management
//
void reloadDebuggingHelpers();
QByteArray m_qtNamespace;
QString m_gdb;
//
......
......@@ -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<QByteArray> toDelete;
foreach (WatchItem *item, handler->model()->treeLevelItems<WatchItem *>(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();
......
......@@ -151,7 +151,6 @@ private:
void refreshCurrentThread(const GdbMi &data);
void refreshStack(const GdbMi &stack);
void refreshRegisters(const GdbMi &registers);
void refreshLocals(const GdbMi &vars);
void refreshTypeInfo(const GdbMi &typeInfo);
void refreshState(const GdbMi &state);
void refreshLocation(const GdbMi &location);
......
......@@ -128,10 +128,9 @@ private:
class UpdateParameters
{
public:
UpdateParameters() { tryPartial = false; }
UpdateParameters() {}
bool tryPartial;
QByteArray varList;
QByteArray partialVariable;
};
class WatchModelBase : public Utils::TreeModel
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment