forked from qt-creator/qt-creator
Debugger: Initial shot at native Qml-and-C++ debugging
Only for GDB now, requires a debug build of Qt, not feature complete. To enable it, run with QTC_DEBUGGER_NATIVE_MIXED=1 and press the 'oo' button in the debugger toolbar. Change-Id: I28aac9db13f7067e03cc364b89cc8046fa213dae Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
This commit is contained in:
@@ -285,6 +285,7 @@ class Dumper(DumperBase):
|
|||||||
self.typesReported = {}
|
self.typesReported = {}
|
||||||
self.typesToReport = {}
|
self.typesToReport = {}
|
||||||
self.qtNamespaceToReport = None
|
self.qtNamespaceToReport = None
|
||||||
|
self.qmlEngines = []
|
||||||
|
|
||||||
def prepare(self, args):
|
def prepare(self, args):
|
||||||
self.output = []
|
self.output = []
|
||||||
@@ -301,6 +302,7 @@ class Dumper(DumperBase):
|
|||||||
self.formats = {}
|
self.formats = {}
|
||||||
self.useDynamicType = True
|
self.useDynamicType = True
|
||||||
self.expandedINames = {}
|
self.expandedINames = {}
|
||||||
|
self.qmlcontext = ""
|
||||||
|
|
||||||
# The guess does not need to be updated during a run()
|
# The guess does not need to be updated during a run()
|
||||||
# as the result is fixed during that time (ignoring "active"
|
# as the result is fixed during that time (ignoring "active"
|
||||||
@@ -340,12 +342,14 @@ class Dumper(DumperBase):
|
|||||||
self.formats[f[0:pos]] = int(f[pos+1:])
|
self.formats[f[0:pos]] = int(f[pos+1:])
|
||||||
elif arg.startswith("watchers:"):
|
elif arg.startswith("watchers:"):
|
||||||
self.watchers = self.hexdecode(arg[pos:])
|
self.watchers = self.hexdecode(arg[pos:])
|
||||||
|
elif arg.startswith("qmlcontext:"):
|
||||||
|
self.qmlcontext = int(arg[pos:], 0)
|
||||||
|
|
||||||
self.useDynamicType = "dyntype" in self.options
|
self.useDynamicType = "dyntype" in self.options
|
||||||
self.useFancy = "fancy" in self.options
|
self.useFancy = "fancy" in self.options
|
||||||
self.forceQtNamespace = "forcens" in self.options
|
self.forceQtNamespace = "forcens" in self.options
|
||||||
self.passExceptions = "pe" in self.options
|
self.passExceptions = "pe" in self.options
|
||||||
#self.passExceptions = True
|
self.nativeMixed = "nativemixed" in self.options
|
||||||
self.autoDerefPointers = "autoderef" in self.options
|
self.autoDerefPointers = "autoderef" in self.options
|
||||||
self.partialUpdate = "partial" in self.options
|
self.partialUpdate = "partial" in self.options
|
||||||
self.tooltipOnly = "tooltiponly" in self.options
|
self.tooltipOnly = "tooltiponly" in self.options
|
||||||
@@ -366,6 +370,68 @@ class Dumper(DumperBase):
|
|||||||
def listOfLocals(self):
|
def listOfLocals(self):
|
||||||
frame = gdb.selected_frame()
|
frame = gdb.selected_frame()
|
||||||
|
|
||||||
|
if self.qmlcontext:
|
||||||
|
items = []
|
||||||
|
|
||||||
|
contextType = self.lookupQtType("QV4::Heap::CallContext")
|
||||||
|
context = self.createPointerValue(self.qmlcontext, contextType)
|
||||||
|
|
||||||
|
contextItem = LocalItem()
|
||||||
|
contextItem.iname = "local.@context"
|
||||||
|
contextItem.name = "[context]"
|
||||||
|
contextItem.value = context.dereference()
|
||||||
|
items.append(contextItem)
|
||||||
|
|
||||||
|
argsItem = LocalItem()
|
||||||
|
argsItem.iname = "local.@args"
|
||||||
|
argsItem.name = "[args]"
|
||||||
|
argsItem.value = context["callData"]
|
||||||
|
items.append(argsItem)
|
||||||
|
|
||||||
|
functionObject = context["function"].dereference()
|
||||||
|
functionPtr = functionObject["function"]
|
||||||
|
if not self.isNull(functionPtr):
|
||||||
|
compilationUnit = context["compilationUnit"]
|
||||||
|
compiledFunction = functionPtr["compiledFunction"]
|
||||||
|
base = int(compiledFunction)
|
||||||
|
|
||||||
|
formalsOffset = int(compiledFunction["formalsOffset"])
|
||||||
|
formalsCount = int(compiledFunction["nFormals"])
|
||||||
|
for index in range(formalsCount):
|
||||||
|
stringIndex = self.extractInt(base + formalsOffset + 4 * index)
|
||||||
|
name = self.extractQmlRuntimeString(compilationUnit, stringIndex)
|
||||||
|
item = LocalItem()
|
||||||
|
item.iname = "local." + name
|
||||||
|
item.name = name
|
||||||
|
item.value = argsItem.value["args"][index]
|
||||||
|
items.append(item)
|
||||||
|
|
||||||
|
localsOffset = int(compiledFunction["localsOffset"])
|
||||||
|
localsCount = int(compiledFunction["nLocals"])
|
||||||
|
for index in range(localsCount):
|
||||||
|
stringIndex = self.extractInt(base + localsOffset + 4 * index)
|
||||||
|
name = self.extractQmlRuntimeString(compilationUnit, stringIndex)
|
||||||
|
item = LocalItem()
|
||||||
|
item.iname = "local." + name
|
||||||
|
item.name = name
|
||||||
|
item.value = contextData["locals"][index]
|
||||||
|
items.append(item)
|
||||||
|
|
||||||
|
for engine in self.qmlEngines:
|
||||||
|
engineItem = LocalItem()
|
||||||
|
engineItem.iname = "local.@qmlengine"
|
||||||
|
engineItem.name = "[engine]"
|
||||||
|
engineItem.value = engine
|
||||||
|
items.append(engineItem)
|
||||||
|
|
||||||
|
rootContext = LocalItem()
|
||||||
|
rootContext.iname = "local.@rootContext"
|
||||||
|
rootContext.name = "[rootContext]"
|
||||||
|
rootContext.value = engine["d_ptr"]
|
||||||
|
items.append(rootContext)
|
||||||
|
break
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
try:
|
try:
|
||||||
block = frame.block()
|
block = frame.block()
|
||||||
@@ -452,7 +518,8 @@ class Dumper(DumperBase):
|
|||||||
# Locals
|
# Locals
|
||||||
#
|
#
|
||||||
self.output.append('data=[')
|
self.output.append('data=[')
|
||||||
if self.partialUpdate and len(self.varList) == 1:
|
if self.partialUpdate and len(self.varList) == 1 \
|
||||||
|
and not self.qmlcontext:
|
||||||
#warn("PARTIAL: %s" % self.varList)
|
#warn("PARTIAL: %s" % self.varList)
|
||||||
parts = self.varList[0].split('.')
|
parts = self.varList[0].split('.')
|
||||||
#warn("PARTIAL PARTS: %s" % parts)
|
#warn("PARTIAL PARTS: %s" % parts)
|
||||||
@@ -1699,6 +1766,59 @@ class Dumper(DumperBase):
|
|||||||
self.typesToReport[typestring] = typeobj
|
self.typesToReport[typestring] = typeobj
|
||||||
return typeobj
|
return typeobj
|
||||||
|
|
||||||
|
def extractQmlData(self, value):
|
||||||
|
if value.type.code == PointerCode:
|
||||||
|
value = value.dereference()
|
||||||
|
data = value["data"]
|
||||||
|
return data.cast(self.lookupType(str(value.type).replace("QV4::", "QV4::Heap::")))
|
||||||
|
|
||||||
|
def extractQmlRuntimeString(self, compilationUnitPtr, index):
|
||||||
|
# This mimics compilationUnit->runtimeStrings[index]
|
||||||
|
runtimeStrings = compilationUnitPtr.dereference()["runtimeStrings"] # QV4.StringValue *
|
||||||
|
entry = (runtimeStrings + index) # QV4.StringValue *
|
||||||
|
text = entry["text"]
|
||||||
|
(elided, fn) = self.encodeStringHelper(toInteger(text), 100)
|
||||||
|
return self.encodedUtf16ToUtf8(fn) # string
|
||||||
|
|
||||||
|
def putQmlLocation(self, level, frame, sal):
|
||||||
|
engine = frame.read_var("engine") # QV4.ExecutionEngine *
|
||||||
|
if self.currentCallContext is None:
|
||||||
|
context = engine["current"] # QV4.ExecutionContext * or derived
|
||||||
|
self.currentCallContext = context
|
||||||
|
else:
|
||||||
|
context = self.currentCallContext["parent"]
|
||||||
|
ctxCode = int(context["type"])
|
||||||
|
compilationUnit = context["compilationUnit"] # QV4.CompiledData.CompilationUnit *
|
||||||
|
functionName = "### JS ###";
|
||||||
|
ns = self.qtNamespace()
|
||||||
|
|
||||||
|
# QV4.ExecutionContext.Type_SimpleCallContext - 4
|
||||||
|
# QV4.ExecutionContext.Type_CallContext - 5
|
||||||
|
if ctxCode == 4 or ctxCode == 5:
|
||||||
|
callContextDataType = self.lookupQtType("QV4::Heap::CallContext")
|
||||||
|
callContext = context.cast(callContextDataType.pointer())
|
||||||
|
functionObject = callContext["function"]
|
||||||
|
function = functionObject["function"]
|
||||||
|
compiledFunction = function["compiledFunction"].dereference() # QV4.CompiledData.Function
|
||||||
|
index = int(compiledFunction["nameIndex"])
|
||||||
|
functionName = "### JS ### " + self.extractQmlRuntimeString(compilationUnit, index)
|
||||||
|
|
||||||
|
string = gdb.parse_and_eval("((%s)0x%x)->fileName()"
|
||||||
|
% (compilationUnit.type, compilationUnit))
|
||||||
|
fileName = self.encodeStringUtf8(string)
|
||||||
|
|
||||||
|
lineNumber = int(context["lineNumber"])
|
||||||
|
self.put(('frame={level="%s",func="%s",file="%s",'
|
||||||
|
'fullname="%s",line="%s",language="js",addr="0x%x"}')
|
||||||
|
% (level, functionName, fileName, fileName, lineNumber, context))
|
||||||
|
|
||||||
|
def isInternalQmlFrame(self, functionName):
|
||||||
|
if functionName.startswith("qt_v4"):
|
||||||
|
return True
|
||||||
|
return functionName.startswith(self.qtNamespace() + "QV4::")
|
||||||
|
|
||||||
|
def isReportableQmlFrame(self, functionName):
|
||||||
|
return functionName.find("QV4::Moth::VME::exec") >= 0
|
||||||
|
|
||||||
def stackListFrames(self, n, options):
|
def stackListFrames(self, n, options):
|
||||||
self.prepare("options:" + options + ",pe")
|
self.prepare("options:" + options + ",pe")
|
||||||
@@ -1706,8 +1826,7 @@ class Dumper(DumperBase):
|
|||||||
|
|
||||||
frame = gdb.newest_frame()
|
frame = gdb.newest_frame()
|
||||||
i = 0
|
i = 0
|
||||||
t1 = 'frame={level="%s",addr="0x%x",func="%s",'
|
self.currentCallContext = None
|
||||||
t1 += 'file="%s",fullname="%s",line="%s",from="%s"}'
|
|
||||||
while i < n and frame:
|
while i < n and frame:
|
||||||
with OutputSafer(self):
|
with OutputSafer(self):
|
||||||
name = frame.name()
|
name = frame.name()
|
||||||
@@ -1725,7 +1844,28 @@ class Dumper(DumperBase):
|
|||||||
objfile = symtab.objfile.filename
|
objfile = symtab.objfile.filename
|
||||||
fileName = symtab.filename
|
fileName = symtab.filename
|
||||||
fullName = symtab.fullname()
|
fullName = symtab.fullname()
|
||||||
self.put(t1 % (i, pc, functionName, fileName, fullName, line, objfile))
|
|
||||||
|
if self.nativeMixed:
|
||||||
|
if self.isReportableQmlFrame(functionName):
|
||||||
|
self.putQmlLocation(i, frame, sal)
|
||||||
|
i += 1
|
||||||
|
frame = frame.older()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self.isInternalQmlFrame(functionName):
|
||||||
|
frame = frame.older()
|
||||||
|
self.put(('frame={level="%s",addr="0x%x",func="%s",'
|
||||||
|
'file="%s",fullname="%s",line="%s",'
|
||||||
|
'from="%s",language="c",usable="0"}') %
|
||||||
|
(i, pc, functionName, fileName, fullName, line, objfile))
|
||||||
|
i += 1
|
||||||
|
frame = frame.older()
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.put(('frame={level="%s",addr="0x%x",func="%s",'
|
||||||
|
'file="%s",fullname="%s",line="%s",'
|
||||||
|
'from="%s",language="c"}') %
|
||||||
|
(i, pc, functionName, fileName, fullName, line, objfile))
|
||||||
|
|
||||||
frame = frame.older()
|
frame = frame.older()
|
||||||
i += 1
|
i += 1
|
||||||
@@ -1941,3 +2081,104 @@ def addExtraDumper(args):
|
|||||||
return str((head, tail))
|
return str((head, tail))
|
||||||
|
|
||||||
registerCommand("addExtraDumper", addExtraDumper)
|
registerCommand("addExtraDumper", addExtraDumper)
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
#
|
||||||
|
# Native Mixed
|
||||||
|
#
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
class QmlEngineCreationTracker(gdb.Breakpoint):
|
||||||
|
def __init__(self):
|
||||||
|
spec = "QQmlEnginePrivate::init"
|
||||||
|
super(QmlEngineCreationTracker, self).\
|
||||||
|
__init__(spec, gdb.BP_BREAKPOINT, internal=True)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
engine = gdb.parse_and_eval("q_ptr")
|
||||||
|
print("QML engine created: %s" % engine)
|
||||||
|
theDumper.qmlEngines.append(engine)
|
||||||
|
return False
|
||||||
|
|
||||||
|
#QmlEngineCreationTracker()
|
||||||
|
|
||||||
|
class TriggeredBreakpointHookBreakpoint(gdb.Breakpoint):
|
||||||
|
def __init__(self):
|
||||||
|
spec = "qt_v4TriggeredBreakpointHook"
|
||||||
|
super(TriggeredBreakpointHookBreakpoint, self).\
|
||||||
|
__init__(spec, gdb.BP_BREAKPOINT, internal=True)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
print("QML engine stopped.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
TriggeredBreakpointHookBreakpoint()
|
||||||
|
|
||||||
|
|
||||||
|
class ResolvePendingBreakpointsHookBreakpoint(gdb.Breakpoint):
|
||||||
|
def __init__(self, fullName, lineNumber):
|
||||||
|
self.fullName = fullName
|
||||||
|
self.lineNumber = lineNumber
|
||||||
|
spec = "qt_v4ResolvePendingBreakpointsHook"
|
||||||
|
print("Preparing hook to resolve pending QML breakpoint at %s:%s"
|
||||||
|
% (self.fullName, self.lineNumber))
|
||||||
|
super(ResolvePendingBreakpointsHookBreakpoint, self).\
|
||||||
|
__init__(spec, gdb.BP_BREAKPOINT, internal=True, temporary=False)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
bp = doInsertQmlBreakPoint(self.fullName, self.lineNumber)
|
||||||
|
print("Resolving QML breakpoint %s:%s -> %s"
|
||||||
|
% (self.fullName, self.lineNumber, bp))
|
||||||
|
self.enabled = False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def doInsertQmlBreakPoint(fullName, lineNumber):
|
||||||
|
pos = fullName.rfind('/')
|
||||||
|
engineName = "qrc:/" + fullName[pos+1:]
|
||||||
|
cmd = 'qt_v4InsertBreakpoint("%s",%s,"%s","")' \
|
||||||
|
% (fullName, lineNumber, engineName)
|
||||||
|
try:
|
||||||
|
bp = gdb.parse_and_eval(cmd)
|
||||||
|
print("Resolving QML breakpoint: %s" % bp)
|
||||||
|
return int(bp)
|
||||||
|
except RuntimeError as error:
|
||||||
|
print("Direct QML breakpoint insertion failed: %s" % error)
|
||||||
|
print("Make pending.")
|
||||||
|
ResolvePendingBreakpointsHookBreakpoint(fullName, lineNumber)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def insertQmlBreakpoint(arg):
|
||||||
|
(fullName, lineNumber) = arg.split(' ')
|
||||||
|
print("Insert QML breakpoint %s:%s" % (fullName, lineNumber))
|
||||||
|
bp = doInsertQmlBreakPoint(fullName, lineNumber)
|
||||||
|
try:
|
||||||
|
gdb.execute("set variable qt_v4IsStepping=0")
|
||||||
|
except RuntimeError as error:
|
||||||
|
print("Resetting stepping failed: %s" % error)
|
||||||
|
return str(bp)
|
||||||
|
|
||||||
|
registerCommand("insertQmlBreakpoint", insertQmlBreakpoint)
|
||||||
|
|
||||||
|
def removeQmlBreakpoint(arg):
|
||||||
|
(fullName, lineNumber) = arg.split(' ')
|
||||||
|
pos = fullName.rfind('/')
|
||||||
|
engineName = "qrc:/" + fullName[pos+1:]
|
||||||
|
fsName = fullName
|
||||||
|
cmd = 'qt_v4RemoveBreakpoint("%s",%s)' % (fullName, lineNumber)
|
||||||
|
print("Remove QML breakpoint %s:%s" % (fullName, lineNumber))
|
||||||
|
try:
|
||||||
|
res = gdb.parse_and_eval(cmd)
|
||||||
|
print("Removing QML breakpoint: %s" % (cmd, res))
|
||||||
|
return int(res)
|
||||||
|
except RuntimeError as error:
|
||||||
|
print("Direct QML breakpoint removal failed: %s." % error)
|
||||||
|
return 0
|
||||||
|
return str(bp)
|
||||||
|
|
||||||
|
registerCommand("removeQmlBreakpoint", removeQmlBreakpoint)
|
||||||
|
|
||||||
|
def prepareQmlStep(arg):
|
||||||
|
gdb.execute("set variable qt_v4IsStepping=1")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
registerCommand("prepareQmlStep", prepareQmlStep)
|
||||||
|
@@ -2255,19 +2255,49 @@ def qdump__QXmlStreamAttribute(d, value):
|
|||||||
#
|
#
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
|
||||||
|
def qdump__QV4__Object(d, value):
|
||||||
|
d.putBetterType(d.currentType)
|
||||||
|
d.putItem(d.extractQmlData(value))
|
||||||
|
|
||||||
|
def qdump__QV4__FunctionObject(d, value):
|
||||||
|
d.putBetterType(d.currentType)
|
||||||
|
d.putItem(d.extractQmlData(value))
|
||||||
|
|
||||||
|
def qdump__QV4__CompilationUnit(d, value):
|
||||||
|
d.putBetterType(d.currentType)
|
||||||
|
d.putItem(d.extractQmlData(value))
|
||||||
|
|
||||||
|
def qdump__QV4__CallContext(d, value):
|
||||||
|
d.putBetterType(d.currentType)
|
||||||
|
d.putItem(d.extractQmlData(value))
|
||||||
|
|
||||||
|
def qdump__QV4__ScriptFunction(d, value):
|
||||||
|
d.putBetterType(d.currentType)
|
||||||
|
d.putItem(d.extractQmlData(value))
|
||||||
|
|
||||||
|
def qdump__QV4__SimpleScriptFunction(d, value):
|
||||||
|
d.putBetterType(d.currentType)
|
||||||
|
d.putItem(d.extractQmlData(value))
|
||||||
|
|
||||||
|
def qdump__QV4__ExecutionContext(d, value):
|
||||||
|
d.putBetterType(d.currentType)
|
||||||
|
d.putItem(d.extractQmlData(value))
|
||||||
|
|
||||||
def qdump__QV4__TypedValue(d, value):
|
def qdump__QV4__TypedValue(d, value):
|
||||||
|
d.putBetterType(d.currentType)
|
||||||
qdump__QV4__Value(d, d.directBaseObject(value))
|
qdump__QV4__Value(d, d.directBaseObject(value))
|
||||||
d.putBetterType(value.type)
|
|
||||||
|
|
||||||
def qdump__QV4__CallData(d, value):
|
def qdump__QV4__CallData(d, value):
|
||||||
argc = toInteger(value["argc"])
|
argc = toInteger(value["argc"])
|
||||||
d.putValue("<%s args>" % argc)
|
d.putItemCount(argc)
|
||||||
d.putNumChild(1)
|
|
||||||
if d.isExpanded():
|
if d.isExpanded():
|
||||||
with Children(d):
|
with Children(d):
|
||||||
for i in range(0, argc + 1):
|
d.putSubItem("[this]", value["thisObject"])
|
||||||
|
for i in range(0, argc):
|
||||||
d.putSubItem(i, value["args"][i])
|
d.putSubItem(i, value["args"][i])
|
||||||
d.putFields(value)
|
|
||||||
|
def qdump__QV4__String(d, value):
|
||||||
|
d.putStringValue(d.addressOf(value) + 2 * d.ptrSize())
|
||||||
|
|
||||||
def qdump__QV4__Value(d, value):
|
def qdump__QV4__Value(d, value):
|
||||||
v = toInteger(str(value["val"]))
|
v = toInteger(str(value["val"]))
|
||||||
@@ -2295,7 +2325,7 @@ def qdump__QV4__Value(d, value):
|
|||||||
d.putBetterType("%sQV4::Value (null/bool)" % ns)
|
d.putBetterType("%sQV4::Value (null/bool)" % ns)
|
||||||
d.putValue("(null/bool)")
|
d.putValue("(null/bool)")
|
||||||
else:
|
else:
|
||||||
vtable = value["m"]["data"]["internalClass"]["vtable"]
|
vtable = value["m"]["vtable"]
|
||||||
if toInteger(vtable["isString"]):
|
if toInteger(vtable["isString"]):
|
||||||
d.putBetterType("%sQV4::Value (string)" % ns)
|
d.putBetterType("%sQV4::Value (string)" % ns)
|
||||||
d.putStringValue(d.extractPointer(value) + 2 * d.ptrSize())
|
d.putStringValue(d.extractPointer(value) + 2 * d.ptrSize())
|
||||||
|
@@ -1379,8 +1379,10 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
|
|||||||
|
|
||||||
int lineNumber = 0;
|
int lineNumber = 0;
|
||||||
QString fullName;
|
QString fullName;
|
||||||
|
QByteArray function;
|
||||||
if (frame.isValid()) {
|
if (frame.isValid()) {
|
||||||
const GdbMi lineNumberG = frame["line"];
|
const GdbMi lineNumberG = frame["line"];
|
||||||
|
function = frame["func"].data();
|
||||||
if (lineNumberG.isValid()) {
|
if (lineNumberG.isValid()) {
|
||||||
lineNumber = lineNumberG.toInt();
|
lineNumber = lineNumberG.toInt();
|
||||||
fullName = cleanupFullName(QString::fromLocal8Bit(frame["fullname"].data()));
|
fullName = cleanupFullName(QString::fromLocal8Bit(frame["fullname"].data()));
|
||||||
@@ -1411,7 +1413,8 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
|
|||||||
// Quickly set the location marker.
|
// Quickly set the location marker.
|
||||||
if (lineNumber && !boolSetting(OperateByInstruction)
|
if (lineNumber && !boolSetting(OperateByInstruction)
|
||||||
&& QFileInfo::exists(fullName)
|
&& QFileInfo::exists(fullName)
|
||||||
&& !isQFatalBreakpoint(rid))
|
&& !isQFatalBreakpoint(rid)
|
||||||
|
&& function != "qt_v4TriggeredBreakpointHook")
|
||||||
gotoLocation(Location(fullName, lineNumber));
|
gotoLocation(Location(fullName, lineNumber));
|
||||||
|
|
||||||
if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
|
if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
|
||||||
@@ -1453,7 +1456,6 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
|
|||||||
|
|
||||||
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
|
||||||
|
|
||||||
|
|
||||||
if (gotoHandleStop1)
|
if (gotoHandleStop1)
|
||||||
handleStop1(data);
|
handleStop1(data);
|
||||||
}
|
}
|
||||||
@@ -2060,6 +2062,11 @@ void GdbEngine::executeStep()
|
|||||||
setTokenBarrier();
|
setTokenBarrier();
|
||||||
notifyInferiorRunRequested();
|
notifyInferiorRunRequested();
|
||||||
showStatusMessage(tr("Step requested..."), 5000);
|
showStatusMessage(tr("Step requested..."), 5000);
|
||||||
|
if (isNativeMixedActive()) {
|
||||||
|
postCommand("prepareQmlStep 0");
|
||||||
|
postCommand("-exec-continue", RunRequest, CB(handleExecuteContinue));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isReverseDebugging())
|
if (isReverseDebugging())
|
||||||
postCommand("reverse-step", RunRequest, CB(handleExecuteStep));
|
postCommand("reverse-step", RunRequest, CB(handleExecuteStep));
|
||||||
else
|
else
|
||||||
@@ -2132,6 +2139,11 @@ void GdbEngine::executeNext()
|
|||||||
setTokenBarrier();
|
setTokenBarrier();
|
||||||
notifyInferiorRunRequested();
|
notifyInferiorRunRequested();
|
||||||
showStatusMessage(tr("Step next requested..."), 5000);
|
showStatusMessage(tr("Step next requested..."), 5000);
|
||||||
|
if (isNativeMixedActive()) {
|
||||||
|
postCommand("prepareQmlStep 1");
|
||||||
|
postCommand("-exec-continue", RunRequest, CB(handleExecuteContinue));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isReverseDebugging()) {
|
if (isReverseDebugging()) {
|
||||||
postCommand("reverse-next", RunRequest, CB(handleExecuteNext));
|
postCommand("reverse-next", RunRequest, CB(handleExecuteNext));
|
||||||
} else {
|
} else {
|
||||||
@@ -2887,6 +2899,15 @@ void GdbEngine::removeBreakpoint(Breakpoint bp)
|
|||||||
{
|
{
|
||||||
QTC_CHECK(bp.state() == BreakpointRemoveRequested);
|
QTC_CHECK(bp.state() == BreakpointRemoveRequested);
|
||||||
BreakpointResponse br = bp.response();
|
BreakpointResponse br = bp.response();
|
||||||
|
|
||||||
|
const BreakpointParameters &data = bp.parameters();
|
||||||
|
if (!data.isCppBreakpoint()) {
|
||||||
|
postCommand("removeQmlBreakpoint " + data.fileName.toUtf8() + ' '
|
||||||
|
+ QByteArray::number(data.lineNumber));
|
||||||
|
bp.notifyBreakpointRemoveOk();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (br.id.isValid()) {
|
if (br.id.isValid()) {
|
||||||
// We already have a fully inserted breakpoint.
|
// We already have a fully inserted breakpoint.
|
||||||
bp.notifyBreakpointRemoveProceeding();
|
bp.notifyBreakpointRemoveProceeding();
|
||||||
@@ -3291,7 +3312,7 @@ QByteArray GdbEngine::stackCommand(int depth)
|
|||||||
if (isNativeMixedEnabled()) {
|
if (isNativeMixedEnabled()) {
|
||||||
cmd = "stackListFrames " + QByteArray::number(depth) + ' ';
|
cmd = "stackListFrames " + QByteArray::number(depth) + ' ';
|
||||||
if (isNativeMixedActive())
|
if (isNativeMixedActive())
|
||||||
cmd += "mixed";
|
cmd += "nativemixed";
|
||||||
else
|
else
|
||||||
cmd += "noopt";
|
cmd += "noopt";
|
||||||
} else {
|
} else {
|
||||||
@@ -3325,8 +3346,15 @@ StackFrame GdbEngine::parseStackFrame(const GdbMi &frameMi, int level)
|
|||||||
frame.from = _(frameMi["from"].data());
|
frame.from = _(frameMi["from"].data());
|
||||||
frame.line = frameMi["line"].toInt();
|
frame.line = frameMi["line"].toInt();
|
||||||
frame.address = frameMi["addr"].toAddress();
|
frame.address = frameMi["addr"].toAddress();
|
||||||
|
GdbMi usable = frameMi["usable"];
|
||||||
|
if (usable.isValid())
|
||||||
|
frame.usable = usable.data().toInt();
|
||||||
|
else
|
||||||
frame.usable = QFileInfo(frame.file).isReadable();
|
frame.usable = QFileInfo(frame.file).isReadable();
|
||||||
if (frameMi["language"].data() == "js") {
|
if (frameMi["language"].data() == "js"
|
||||||
|
|| frame.file.endsWith(QLatin1String(".js"))
|
||||||
|
|| frame.file.endsWith(QLatin1String(".qml"))) {
|
||||||
|
frame.file = QFile::decodeName(frameMi["file"].data());
|
||||||
frame.language = QmlLanguage;
|
frame.language = QmlLanguage;
|
||||||
frame.fixQmlFrame(startParameters());
|
frame.fixQmlFrame(startParameters());
|
||||||
}
|
}
|
||||||
@@ -4847,6 +4875,8 @@ void GdbEngine::updateLocalsPython(const UpdateParameters ¶ms)
|
|||||||
options += "autoderef,";
|
options += "autoderef,";
|
||||||
if (boolSetting(UseDynamicType))
|
if (boolSetting(UseDynamicType))
|
||||||
options += "dyntype,";
|
options += "dyntype,";
|
||||||
|
if (isNativeMixedActive())
|
||||||
|
options += "nativemixed,";
|
||||||
if (options.isEmpty())
|
if (options.isEmpty())
|
||||||
options += "defaults,";
|
options += "defaults,";
|
||||||
if (params.tryPartial)
|
if (params.tryPartial)
|
||||||
@@ -4855,16 +4885,23 @@ void GdbEngine::updateLocalsPython(const UpdateParameters ¶ms)
|
|||||||
options += "tooltiponly,";
|
options += "tooltiponly,";
|
||||||
options.chop(1);
|
options.chop(1);
|
||||||
|
|
||||||
|
QByteArray context;
|
||||||
|
if (isNativeMixedActive()) {
|
||||||
|
StackFrame frame = stackHandler()->currentFrame();
|
||||||
|
if (frame.language == QmlLanguage)
|
||||||
|
context += " qmlcontext:0x" + QByteArray::number(frame.address, 16);
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray resultVar;
|
QByteArray resultVar;
|
||||||
if (!m_resultVarName.isEmpty())
|
if (!m_resultVarName.isEmpty())
|
||||||
resultVar = "resultvarname:" + m_resultVarName + ' ';
|
resultVar = "resultvarname:" + m_resultVarName + ' ';
|
||||||
|
|
||||||
m_lastDebuggableCommand =
|
m_lastDebuggableCommand =
|
||||||
"bb options:pe," + options + " vars:" + params.varList + ' '
|
"bb options:pe," + options + " vars:" + params.varList + ' '
|
||||||
+ expanded + " watchers:" + watchers.toHex() + cutOff;
|
+ expanded + " watchers:" + watchers.toHex() + cutOff + context;
|
||||||
|
|
||||||
postCommand("bb options:" + options + " vars:" + params.varList + ' '
|
postCommand("bb options:" + options + " vars:" + params.varList + ' '
|
||||||
+ resultVar + expanded + " watchers:" + watchers.toHex() + cutOff,
|
+ resultVar + expanded + " watchers:" + watchers.toHex() + cutOff + context,
|
||||||
Discardable, CB(handleStackFramePython), QVariant(params.tryPartial));
|
Discardable, CB(handleStackFramePython), QVariant(params.tryPartial));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
#include "gdbprocess.h"
|
#include "gdbprocess.h"
|
||||||
|
|
||||||
#include <debugger/debuggerconstants.h>
|
#include <debugger/debuggerconstants.h>
|
||||||
|
#include <debugger/debuggercore.h>
|
||||||
#include <debugger/procinterrupt.h>
|
#include <debugger/procinterrupt.h>
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
@@ -115,8 +116,11 @@ void GdbProcess::setProcessEnvironment(const QProcessEnvironment &env)
|
|||||||
m_gdbProc.setProcessEnvironment(env);
|
m_gdbProc.setProcessEnvironment(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbProcess::setEnvironment(const QStringList &env)
|
void GdbProcess::setEnvironment(const QStringList &env_)
|
||||||
{
|
{
|
||||||
|
QStringList env = env_;
|
||||||
|
if (isNativeMixedActive())
|
||||||
|
env.append(QLatin1String("QV4_FORCE_INTERPRETER=1")); // FIXME: REMOVE!
|
||||||
m_gdbProc.setEnvironment(Utils::Environment(env));
|
m_gdbProc.setEnvironment(Utils::Environment(env));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user