forked from qt-creator/qt-creator
Debugger: Handle extra dumpers with LLDB
This generalizes part of the previously GDB-only code paths Change-Id: Id32798073e3c8bcb23bfedf463bebc866c8275e2 Reviewed-by: hjk <hjk@theqtcompany.com>
This commit is contained in:
@@ -34,6 +34,8 @@ import sys
|
|||||||
import base64
|
import base64
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
import importlib
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import subprocess
|
import subprocess
|
||||||
hasSubprocess = True
|
hasSubprocess = True
|
||||||
@@ -1635,6 +1637,65 @@ class DumperBase:
|
|||||||
self.currentNumChild = 0
|
self.currentNumChild = 0
|
||||||
self.putNumChild(0)
|
self.putNumChild(0)
|
||||||
|
|
||||||
|
def registerDumper(self, funcname, function):
|
||||||
|
try:
|
||||||
|
#warn("FUNCTION: %s " % funcname)
|
||||||
|
#funcname = function.func_name
|
||||||
|
if funcname.startswith("qdump__"):
|
||||||
|
typename = funcname[7:]
|
||||||
|
self.qqDumpers[typename] = function
|
||||||
|
self.qqFormats[typename] = self.qqFormats.get(typename, "")
|
||||||
|
elif funcname.startswith("qform__"):
|
||||||
|
typename = funcname[7:]
|
||||||
|
formats = ""
|
||||||
|
try:
|
||||||
|
formats = function()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.qqFormats[typename] = formats
|
||||||
|
elif funcname.startswith("qedit__"):
|
||||||
|
typename = funcname[7:]
|
||||||
|
try:
|
||||||
|
self.qqEditable[typename] = function
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def findDumperFunctions(self):
|
||||||
|
self.qqDumpers = {}
|
||||||
|
self.qqFormats = {}
|
||||||
|
self.qqEditable = {}
|
||||||
|
self.typeCache = {}
|
||||||
|
|
||||||
|
for mod in dumpermodules:
|
||||||
|
m = importlib.import_module(mod)
|
||||||
|
dic = m.__dict__
|
||||||
|
for name in dic.keys():
|
||||||
|
item = dic[name]
|
||||||
|
self.registerDumper(name, item)
|
||||||
|
|
||||||
|
return self.reportDumpers()
|
||||||
|
|
||||||
|
def reportDumpers(self):
|
||||||
|
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 reloadDumper(self):
|
||||||
|
for mod in dumpermodules:
|
||||||
|
m = sys.modules[mod]
|
||||||
|
if sys.version_info[0] >= 3:
|
||||||
|
importlib.reload(m)
|
||||||
|
else:
|
||||||
|
reload(m)
|
||||||
|
|
||||||
|
findDumperFunctions()
|
||||||
|
|
||||||
# Some "Enums"
|
# Some "Enums"
|
||||||
|
|
||||||
@@ -1681,3 +1742,12 @@ DisplayUtf8String \
|
|||||||
= range(6)
|
= range(6)
|
||||||
|
|
||||||
|
|
||||||
|
dumpermodules = [
|
||||||
|
"qttypes",
|
||||||
|
"stdtypes",
|
||||||
|
"misctypes",
|
||||||
|
"boosttypes",
|
||||||
|
"creatortypes",
|
||||||
|
"personaltypes",
|
||||||
|
]
|
||||||
|
|
||||||
|
@@ -14,25 +14,11 @@ import sys
|
|||||||
import struct
|
import struct
|
||||||
import types
|
import types
|
||||||
|
|
||||||
import importlib
|
|
||||||
|
|
||||||
def warn(message):
|
def warn(message):
|
||||||
print("XXX: %s\n" % message.encode("latin1"))
|
print("XXX: %s\n" % message.encode("latin1"))
|
||||||
|
|
||||||
from dumper import *
|
from dumper import *
|
||||||
|
|
||||||
dumpermodules = [
|
|
||||||
"qttypes",
|
|
||||||
"stdtypes",
|
|
||||||
"misctypes",
|
|
||||||
"boosttypes",
|
|
||||||
"creatortypes",
|
|
||||||
"personaltypes",
|
|
||||||
]
|
|
||||||
|
|
||||||
for mod in dumpermodules:
|
|
||||||
importlib.import_module(mod)
|
|
||||||
|
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
#
|
#
|
||||||
@@ -150,8 +136,7 @@ ScanStackCommand()
|
|||||||
|
|
||||||
|
|
||||||
def bbsetup(args = ''):
|
def bbsetup(args = ''):
|
||||||
theDumper.bbsetup()
|
print(theDumper.findDumperFunctions())
|
||||||
print(theDumper.reportDumpers())
|
|
||||||
|
|
||||||
registerCommand("bbsetup", bbsetup)
|
registerCommand("bbsetup", bbsetup)
|
||||||
|
|
||||||
@@ -1423,55 +1408,6 @@ class Dumper(DumperBase):
|
|||||||
with Children(self, 1):
|
with Children(self, 1):
|
||||||
self.listAnonymous(value, name, field.type)
|
self.listAnonymous(value, name, field.type)
|
||||||
|
|
||||||
def registerDumper(self, funcname, function):
|
|
||||||
try:
|
|
||||||
#warn("FUNCTION: %s " % funcname)
|
|
||||||
#funcname = function.func_name
|
|
||||||
if funcname.startswith("qdump__"):
|
|
||||||
typename = funcname[7:]
|
|
||||||
self.qqDumpers[typename] = function
|
|
||||||
self.qqFormats[typename] = self.qqFormats.get(typename, "")
|
|
||||||
elif funcname.startswith("qform__"):
|
|
||||||
typename = funcname[7:]
|
|
||||||
formats = ""
|
|
||||||
try:
|
|
||||||
formats = function()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
self.qqFormats[typename] = formats
|
|
||||||
elif funcname.startswith("qedit__"):
|
|
||||||
typename = funcname[7:]
|
|
||||||
try:
|
|
||||||
self.qqEditable[typename] = function
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def bbsetup(self):
|
|
||||||
self.qqDumpers = {}
|
|
||||||
self.qqFormats = {}
|
|
||||||
self.qqEditable = {}
|
|
||||||
self.typeCache = {}
|
|
||||||
|
|
||||||
for mod in dumpermodules:
|
|
||||||
m = importlib.import_module(mod)
|
|
||||||
dic = m.__dict__
|
|
||||||
for name in dic.keys():
|
|
||||||
item = dic[name]
|
|
||||||
self.registerDumper(name, item)
|
|
||||||
|
|
||||||
|
|
||||||
def reportDumpers(self):
|
|
||||||
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, objectPrivateType):
|
#def threadname(self, maximalStackDepth, objectPrivateType):
|
||||||
# e = gdb.selected_frame()
|
# e = gdb.selected_frame()
|
||||||
# out = ""
|
# out = ""
|
||||||
@@ -2044,15 +1980,8 @@ registerCommand("threadnames", threadnames)
|
|||||||
#
|
#
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
|
||||||
def reloadDumper(arg):
|
def reloadDumper(_):
|
||||||
for mod in dumpermodules:
|
theDumper.reloadDumper();
|
||||||
m = sys.modules[mod]
|
|
||||||
if sys.version_info[0] >= 3:
|
|
||||||
importlib.reload(m)
|
|
||||||
else:
|
|
||||||
reload(m)
|
|
||||||
|
|
||||||
bbsetup()
|
|
||||||
|
|
||||||
registerCommand("reload", reloadDumper)
|
registerCommand("reload", reloadDumper)
|
||||||
|
|
||||||
|
@@ -672,7 +672,6 @@ class Dumper(DumperBase):
|
|||||||
self.target = self.debugger.CreateTarget(self.executable_, None, None, True, error)
|
self.target = self.debugger.CreateTarget(self.executable_, None, None, True, error)
|
||||||
else:
|
else:
|
||||||
self.target = self.debugger.CreateTarget(None, None, None, True, error)
|
self.target = self.debugger.CreateTarget(None, None, None, True, error)
|
||||||
self.importDumpers()
|
|
||||||
|
|
||||||
state = "inferiorsetupok" if self.target.IsValid() else "inferiorsetupfailed"
|
state = "inferiorsetupok" if self.target.IsValid() else "inferiorsetupfailed"
|
||||||
self.report('state="%s",msg="%s",exe="%s"' % (state, error, self.executable_))
|
self.report('state="%s",msg="%s",exe="%s"' % (state, error, self.executable_))
|
||||||
@@ -1583,7 +1582,7 @@ class Dumper(DumperBase):
|
|||||||
path = args['path']
|
path = args['path']
|
||||||
(head, tail) = os.path.split(path)
|
(head, tail) = os.path.split(path)
|
||||||
sys.path.insert(1, head)
|
sys.path.insert(1, head)
|
||||||
#dumpermodules.append(os.path.splitext(tail)[0])
|
dumpermodules.append(os.path.splitext(tail)[0])
|
||||||
self.report('ok')
|
self.report('ok')
|
||||||
|
|
||||||
def updateData(self, args):
|
def updateData(self, args):
|
||||||
@@ -1642,6 +1641,10 @@ class Dumper(DumperBase):
|
|||||||
result += ',offset="%s"},' % (addr - base)
|
result += ',offset="%s"},' % (addr - base)
|
||||||
self.report(result + ']')
|
self.report(result + ']')
|
||||||
|
|
||||||
|
def loadDumperFiles(self, _ = None):
|
||||||
|
result = self.findDumperFunctions()
|
||||||
|
self.report(result)
|
||||||
|
|
||||||
def fetchMemory(self, args):
|
def fetchMemory(self, args):
|
||||||
address = args['address']
|
address = args['address']
|
||||||
length = args['length']
|
length = args['length']
|
||||||
@@ -1668,35 +1671,6 @@ class Dumper(DumperBase):
|
|||||||
self.reportError(error)
|
self.reportError(error)
|
||||||
self.reportVariables()
|
self.reportVariables()
|
||||||
|
|
||||||
def registerDumper(self, function):
|
|
||||||
if hasattr(function, 'func_name'):
|
|
||||||
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
|
|
||||||
|
|
||||||
def importDumpers(self, _ = None):
|
|
||||||
result = lldb.SBCommandReturnObject()
|
|
||||||
interpreter = self.debugger.GetCommandInterpreter()
|
|
||||||
items = globals()
|
|
||||||
for key in items:
|
|
||||||
self.registerDumper(items[key])
|
|
||||||
|
|
||||||
def execute(self, args):
|
def execute(self, args):
|
||||||
getattr(self, args['cmd'])(args)
|
getattr(self, args['cmd'])(args)
|
||||||
self.report('token="%s"' % args['token'])
|
self.report('token="%s"' % args['token'])
|
||||||
@@ -1754,7 +1728,7 @@ class Tester(Dumper):
|
|||||||
self.expandedINames = set(sys.argv[3].split(','))
|
self.expandedINames = set(sys.argv[3].split(','))
|
||||||
self.passExceptions = True
|
self.passExceptions = True
|
||||||
|
|
||||||
self.importDumpers()
|
self.loadDumperFiles()
|
||||||
error = lldb.SBError()
|
error = lldb.SBError()
|
||||||
self.target = self.debugger.CreateTarget(sys.argv[2],
|
self.target = self.debugger.CreateTarget(sys.argv[2],
|
||||||
None, None, True, error)
|
None, None, True, error)
|
||||||
|
@@ -1737,22 +1737,7 @@ void GdbEngine::handlePythonSetup(const GdbResponse &response)
|
|||||||
if (response.resultClass == GdbResultDone) {
|
if (response.resultClass == GdbResultDone) {
|
||||||
GdbMi data;
|
GdbMi data;
|
||||||
data.fromStringMultiple(response.consoleStreamOutput);
|
data.fromStringMultiple(response.consoleStreamOutput);
|
||||||
const GdbMi dumpers = data["dumpers"];
|
watchHandler()->addDumpers(data["dumpers"]);
|
||||||
foreach (const GdbMi &dumper, dumpers.children()) {
|
|
||||||
QByteArray type = dumper["type"].data();
|
|
||||||
QStringList formats(tr("Raw structure"));
|
|
||||||
foreach (const QByteArray &format,
|
|
||||||
dumper["formats"].data().split(',')) {
|
|
||||||
if (format == "Normal")
|
|
||||||
formats.append(tr("Normal"));
|
|
||||||
else if (format == "Displayed")
|
|
||||||
formats.append(tr("Displayed"));
|
|
||||||
else if (!format.isEmpty())
|
|
||||||
formats.append(_(format));
|
|
||||||
}
|
|
||||||
watchHandler()->addTypeFormats(type, formats);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadInitScript();
|
loadInitScript();
|
||||||
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
|
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state());
|
||||||
showMessage(_("ENGINE SUCCESSFULLY STARTED"));
|
showMessage(_("ENGINE SUCCESSFULLY STARTED"));
|
||||||
|
@@ -281,10 +281,13 @@ void LldbEngine::setupInferior()
|
|||||||
const QString commands = stringSetting(ExtraDumperCommands);
|
const QString commands = stringSetting(ExtraDumperCommands);
|
||||||
if (!commands.isEmpty()) {
|
if (!commands.isEmpty()) {
|
||||||
Command cmd("executeDebuggerCommand");
|
Command cmd("executeDebuggerCommand");
|
||||||
cmd.arg(commands.toUtf8());
|
cmd.arg("commands", commands.toUtf8());
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Command cmd1("loadDumperFiles");
|
||||||
|
runCommand(cmd1);
|
||||||
|
|
||||||
QString executable;
|
QString executable;
|
||||||
QtcProcess::Arguments args;
|
QtcProcess::Arguments args;
|
||||||
QtcProcess::prepareCommand(QFileInfo(sp.executable).absoluteFilePath(),
|
QtcProcess::prepareCommand(QFileInfo(sp.executable).absoluteFilePath(),
|
||||||
@@ -409,6 +412,8 @@ void LldbEngine::handleResponse(const QByteArray &response)
|
|||||||
const QByteArray name = item.name();
|
const QByteArray name = item.name();
|
||||||
if (name == "data")
|
if (name == "data")
|
||||||
refreshLocals(item);
|
refreshLocals(item);
|
||||||
|
else if (name == "dumpers")
|
||||||
|
watchHandler()->addDumpers(item);
|
||||||
else if (name == "stack")
|
else if (name == "stack")
|
||||||
refreshStack(item);
|
refreshStack(item);
|
||||||
else if (name == "stack-position")
|
else if (name == "stack-position")
|
||||||
|
@@ -1783,6 +1783,22 @@ QByteArray WatchHandler::individualFormatRequests() const
|
|||||||
return ba;
|
return ba;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WatchHandler::addDumpers(const GdbMi &dumpers)
|
||||||
|
{
|
||||||
|
foreach (const GdbMi &dumper, dumpers.children()) {
|
||||||
|
QStringList formats(tr("Raw structure"));
|
||||||
|
foreach (const QByteArray &format, dumper["formats"].data().split(',')) {
|
||||||
|
if (format == "Normal")
|
||||||
|
formats.append(tr("Normal"));
|
||||||
|
else if (format == "Displayed")
|
||||||
|
formats.append(tr("Displayed"));
|
||||||
|
else if (!format.isEmpty())
|
||||||
|
formats.append(QString::fromLatin1(format));
|
||||||
|
}
|
||||||
|
addTypeFormats(dumper["type"].data(), formats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WatchHandler::addTypeFormats(const QByteArray &type, const QStringList &formats)
|
void WatchHandler::addTypeFormats(const QByteArray &type, const QStringList &formats)
|
||||||
{
|
{
|
||||||
m_model->m_reportedTypeFormats.insert(QLatin1String(stripForFormat(type)), formats);
|
m_model->m_reportedTypeFormats.insert(QLatin1String(stripForFormat(type)), formats);
|
||||||
|
@@ -217,6 +217,7 @@ public:
|
|||||||
|
|
||||||
int format(const QByteArray &iname) const;
|
int format(const QByteArray &iname) const;
|
||||||
|
|
||||||
|
void addDumpers(const GdbMi &dumpers);
|
||||||
void addTypeFormats(const QByteArray &type, const QStringList &formats);
|
void addTypeFormats(const QByteArray &type, const QStringList &formats);
|
||||||
void setTypeFormats(const DumperTypeFormats &typeFormats);
|
void setTypeFormats(const DumperTypeFormats &typeFormats);
|
||||||
DumperTypeFormats typeFormats() const;
|
DumperTypeFormats typeFormats() const;
|
||||||
|
Reference in New Issue
Block a user