forked from qt-creator/qt-creator
Debugger: Infrastructure for reworked native mixed debugging
- Remove old experimental native mixed approach. - Move some common stack parsing to Stackhandler. - Mark gdbbridge.py debug output explicitly to remove it from actual reponse handling New native mixed needs QtDeclarative changes and QTC_DEBUGGER_NATIVE_MIXED=1 for now. Change-Id: I09eed1da51cea878636d36756015b7bfaed34203 Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
This commit is contained in:
@@ -34,6 +34,7 @@ import sys
|
|||||||
import base64
|
import base64
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
import json
|
||||||
|
|
||||||
if sys.version_info[0] >= 3:
|
if sys.version_info[0] >= 3:
|
||||||
xrange = range
|
xrange = range
|
||||||
@@ -241,7 +242,7 @@ class Blob(object):
|
|||||||
return struct.unpack_from("f", self.data, offset)[0]
|
return struct.unpack_from("f", self.data, offset)[0]
|
||||||
|
|
||||||
def warn(message):
|
def warn(message):
|
||||||
print("XXX: %s\n" % message.encode("latin1"))
|
print('bridgemessage={msg="%s"},' % message.replace('"', '$').encode("latin1"))
|
||||||
|
|
||||||
|
|
||||||
def showException(msg, exType, exValue, exTraceback):
|
def showException(msg, exType, exValue, exTraceback):
|
||||||
@@ -413,6 +414,8 @@ class DumperBase:
|
|||||||
"personaltypes",
|
"personaltypes",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
self.interpreterSeq = 0
|
||||||
|
|
||||||
|
|
||||||
def resetCaches(self):
|
def resetCaches(self):
|
||||||
# This is a cache mapping from 'type name' to 'display alternatives'.
|
# This is a cache mapping from 'type name' to 'display alternatives'.
|
||||||
@@ -598,6 +601,9 @@ class DumperBase:
|
|||||||
data = self.extractBlob(addr, size).toBytes()
|
data = self.extractBlob(addr, size).toBytes()
|
||||||
return self.hexencode(data)
|
return self.hexencode(data)
|
||||||
|
|
||||||
|
def readJsonFromMemory(self, addr, size):
|
||||||
|
return json.loads(self.hexdecode(self.readMemory(addr, size)))
|
||||||
|
|
||||||
def encodeByteArray(self, value, limit = 0):
|
def encodeByteArray(self, value, limit = 0):
|
||||||
elided, data = self.encodeByteArrayHelper(self.extractPointer(value), limit)
|
elided, data = self.encodeByteArrayHelper(self.extractPointer(value), limit)
|
||||||
return data
|
return data
|
||||||
@@ -718,7 +724,6 @@ class DumperBase:
|
|||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#warn("CHILDREN: %s %s %s" % (numChild, childType, childNumChild))
|
|
||||||
def putMapName(self, value, index = None):
|
def putMapName(self, value, index = None):
|
||||||
ns = self.qtNamespace()
|
ns = self.qtNamespace()
|
||||||
typeName = self.stripClassTag(str(value.type))
|
typeName = self.stripClassTag(str(value.type))
|
||||||
@@ -829,6 +834,16 @@ class DumperBase:
|
|||||||
self.putSpecialValue(SpecialItemCountValue, count)
|
self.putSpecialValue(SpecialItemCountValue, count)
|
||||||
self.putNumChild(count)
|
self.putNumChild(count)
|
||||||
|
|
||||||
|
def dictToMi(self, value):
|
||||||
|
if type(value) is bool:
|
||||||
|
return '"%d"' % int(value)
|
||||||
|
if type(value) is dict:
|
||||||
|
return '{' + ','.join(['%s=%s' % (k, self.dictToMi(v))
|
||||||
|
for (k, v) in list(value.items())]) + '}'
|
||||||
|
if type(value) is list:
|
||||||
|
return '[' + ','.join([self.dictToMi(v) for v in value]) + ']'
|
||||||
|
return '"%s"' % value
|
||||||
|
|
||||||
def putField(self, name, value):
|
def putField(self, name, value):
|
||||||
self.put('%s="%s",' % (name, value))
|
self.put('%s="%s",' % (name, value))
|
||||||
|
|
||||||
@@ -1719,69 +1734,120 @@ class DumperBase:
|
|||||||
sys.path.insert(1, head)
|
sys.path.insert(1, head)
|
||||||
self.dumpermodules.append(os.path.splitext(tail)[0])
|
self.dumpermodules.append(os.path.splitext(tail)[0])
|
||||||
|
|
||||||
def sendQmlCommand(self, command, data = ""):
|
def extractQStringFromQDataStream(self, buf, offset):
|
||||||
data += '"version":"1","command":"%s"' % command
|
""" Read a QString from the stream """
|
||||||
data = data.replace('"', '\\"')
|
size = struct.unpack_from("!I", buf, offset)[0]
|
||||||
expr = 'qt_v4DebuggerHook("{%s}")' % data
|
offset += 4
|
||||||
try:
|
string = buf[offset:offset + size].decode('utf-16be')
|
||||||
res = self.parseAndEvaluate(expr)
|
return (string, offset + size)
|
||||||
print("QML command ok, RES: %s, CMD: %s" % (res, expr))
|
|
||||||
except RuntimeError as error:
|
def extractQByteArrayFromQDataStream(self, buf, offset):
|
||||||
#print("QML command failed: %s: %s" % (expr, error))
|
""" Read a QByteArray from the stream """
|
||||||
res = None
|
size = struct.unpack_from("!I", buf, offset)[0]
|
||||||
except AttributeError as error:
|
offset += 4
|
||||||
# Happens with LLDB and 'None' current thread.
|
string = buf[offset:offset + size].decode('latin1')
|
||||||
#print("QML command failed: %s: %s" % (expr, error))
|
return (string, offset + size)
|
||||||
res = None
|
|
||||||
|
def extractIntFromQDataStream(self, buf, offset):
|
||||||
|
""" Read an int from the stream """
|
||||||
|
value = struct.unpack_from("!I", buf, offset)[0]
|
||||||
|
return (value, offset + 4)
|
||||||
|
|
||||||
|
def readInterpreterOutput(self):
|
||||||
|
buf = self.parseAndEvaluate("qt_qmlDebugOutputBuffer")
|
||||||
|
size = self.parseAndEvaluate("qt_qmlDebugOutputLength")
|
||||||
|
return self.readJsonFromMemory(buf, size)
|
||||||
|
|
||||||
|
def handleInterpreterEvent(self):
|
||||||
|
""" Return True if inferior stopped """
|
||||||
|
buf = self.parseAndEvaluate("qt_qmlDebugEventBuffer")
|
||||||
|
size = self.parseAndEvaluate("qt_qmlDebugEventLength")
|
||||||
|
resdict = self.readJsonFromMemory(buf, size)
|
||||||
|
warn("RES DICT : %s" % resdict)
|
||||||
|
return resdict.get('event') == 'break'
|
||||||
|
|
||||||
|
def removeInterpreterBreakpoint(self, args):
|
||||||
|
res = self.sendInterpreterRequest('removebreakpoint', { 'id' : args['id'] })
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def prepareQmlStep(self, _):
|
def insertInterpreterBreakpoint(self, args):
|
||||||
self.sendQmlCommand('prepareStep')
|
args['condition'] = self.hexdecode(args.get('condition', ''))
|
||||||
|
warn("Insert interpreter breakpoint %s:%s (%s)"
|
||||||
def removeQmlBreakpoint(self, args):
|
% (args['file'], args['line'], args['condition']))
|
||||||
fullName = args['fileName']
|
bp = self.doInsertInterpreterBreakpoint(args, False)
|
||||||
lineNumber = args['lineNumber']
|
|
||||||
#print("Remove QML breakpoint %s:%s" % (fullName, lineNumber))
|
|
||||||
bp = self.sendQmlCommand('removeBreakpoint',
|
|
||||||
'"fullName":"%s","lineNumber":"%s",'
|
|
||||||
% (fullName, lineNumber))
|
|
||||||
if bp is None:
|
|
||||||
#print("Direct QML breakpoint removal failed: %s.")
|
|
||||||
return 0
|
|
||||||
#print("Removing QML breakpoint: %s" % bp)
|
|
||||||
return int(bp)
|
|
||||||
|
|
||||||
def insertQmlBreakpoint(self, args):
|
|
||||||
print("Insert QML breakpoint %s" % self.describeBreakpointData(args))
|
|
||||||
bp = self.doInsertQmlBreakpoint(args)
|
|
||||||
res = self.sendQmlCommand('prepareStep')
|
|
||||||
#if res is None:
|
|
||||||
# print("Resetting stepping failed.")
|
|
||||||
return str(bp)
|
return str(bp)
|
||||||
|
|
||||||
def doInsertQmlBreakpoint(self, args):
|
def sendInterpreterRequest(self, command, args = {}):
|
||||||
fullName = args['fileName']
|
self.interpreterSeq += 1
|
||||||
lineNumber = args['lineNumber']
|
cmd = { 'seq': self.interpreterSeq, 'type': 'request', 'command': command, 'arguments': args }
|
||||||
pos = fullName.rfind('/')
|
encoded = json.dumps(cmd)
|
||||||
engineName = "qrc:/" + fullName[pos+1:]
|
hexdata = self.hexencode(encoded)
|
||||||
bp = self.sendQmlCommand('insertBreakpoint',
|
expr = 'qt_qmlDebugSendDataToService("NativeQmlDebugger","%s")' % hexdata
|
||||||
'"fullName":"%s","lineNumber":"%s","engineName":"%s",'
|
try:
|
||||||
% (fullName, lineNumber, engineName))
|
res = self.parseAndEvaluate(expr)
|
||||||
if bp is None:
|
except RuntimeError as error:
|
||||||
#print("Direct QML breakpoint insertion failed.")
|
warn("Interpreter command failed: %s: %s" % (encoded, error))
|
||||||
#print("Make pending.")
|
return {}
|
||||||
self.createResolvePendingBreakpointsHookBreakpoint(args)
|
except AttributeError as error:
|
||||||
return 0
|
# Happens with LLDB and 'None' current thread.
|
||||||
|
warn("Interpreter command failed: %s: %s" % (encoded, error))
|
||||||
|
return {}
|
||||||
|
|
||||||
print("Resolving QML breakpoint: %s" % bp)
|
if not res:
|
||||||
|
warn("Interpreter command failed: %s " % encoded)
|
||||||
|
return {}
|
||||||
|
resdict = self.readInterpreterOutput()
|
||||||
|
warn("Interpreter command output: '%s'" % resdict)
|
||||||
|
service = resdict.get("service")
|
||||||
|
if service == "NativeQmlDebugger":
|
||||||
|
messages = resdict.get("messages", [])
|
||||||
|
if len(messages) == 1:
|
||||||
|
return messages[0]
|
||||||
|
warn("Unexpected multiple interpreter messages: %s" % messages)
|
||||||
|
else:
|
||||||
|
warn("Interpreter result from alien service: %s" % service)
|
||||||
|
return {'messages': messages }
|
||||||
|
|
||||||
|
def executeStep(self, args):
|
||||||
|
if self.nativeMixed:
|
||||||
|
response = self.sendInterpreterRequest('stepin', args)
|
||||||
|
self.doContinue()
|
||||||
|
|
||||||
|
def executeStepOut(self, args):
|
||||||
|
if self.nativeMixed:
|
||||||
|
response = self.sendInterpreterRequest('stepout', args)
|
||||||
|
self.doContinue()
|
||||||
|
|
||||||
|
def executeNext(self, args):
|
||||||
|
if self.nativeMixed:
|
||||||
|
response = self.sendInterpreterRequest('stepover', args)
|
||||||
|
self.doContinue()
|
||||||
|
|
||||||
|
def executeContinue(self, args):
|
||||||
|
if self.nativeMixed:
|
||||||
|
response = self.sendInterpreterRequest('continue', args)
|
||||||
|
self.doContinue()
|
||||||
|
|
||||||
|
def doInsertInterpreterBreakpoint(self, args, wasPending):
|
||||||
|
warn("DO INSERT INTERPRETER BREAKPOINT, WAS PENDING: %s" % wasPending)
|
||||||
|
# Will fail if the service is not yet up and running.
|
||||||
|
response = self.sendInterpreterRequest('setbreakpoint', args)
|
||||||
|
bp = None if response is None else response.get("breakpoint", None)
|
||||||
|
if wasPending:
|
||||||
|
if not bp:
|
||||||
|
warn("ERROR: Pending interpreter breakpoint insertion failed.")
|
||||||
|
return -1
|
||||||
|
else:
|
||||||
|
if not bp:
|
||||||
|
warn("Direct interpreter breakpoint insertion failed.")
|
||||||
|
warn("Make pending.")
|
||||||
|
self.createResolvePendingBreakpointsHookBreakpoint(args)
|
||||||
|
return -1
|
||||||
|
|
||||||
|
warn("Resolved interpreter breakpoint: BP: %s" % bp)
|
||||||
return int(bp)
|
return int(bp)
|
||||||
|
|
||||||
def describeBreakpointData(self, args):
|
def isInternalInterpreterFrame(self, functionName):
|
||||||
fullName = args['fileName']
|
|
||||||
lineNumber = args['lineNumber']
|
|
||||||
return "%s:%s" % (fullName, lineNumber)
|
|
||||||
|
|
||||||
def isInternalQmlFrame(self, functionName):
|
|
||||||
if functionName is None:
|
if functionName is None:
|
||||||
return False
|
return False
|
||||||
if functionName.startswith("qt_v4"):
|
if functionName.startswith("qt_v4"):
|
||||||
@@ -1793,7 +1859,7 @@ class DumperBase:
|
|||||||
def canCallLocale(self):
|
def canCallLocale(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def isReportableQmlFrame(self, functionName):
|
def isReportableInterpreterFrame(self, functionName):
|
||||||
return functionName and functionName.find("QV4::Moth::VME::exec") >= 0
|
return functionName and functionName.find("QV4::Moth::VME::exec") >= 0
|
||||||
|
|
||||||
def extractQmlData(self, value):
|
def extractQmlData(self, value):
|
||||||
@@ -1802,112 +1868,13 @@ class DumperBase:
|
|||||||
data = value["data"]
|
data = value["data"]
|
||||||
return data.cast(self.lookupType(str(value.type).replace("QV4::", "QV4::Heap::")))
|
return data.cast(self.lookupType(str(value.type).replace("QV4::", "QV4::Heap::")))
|
||||||
|
|
||||||
def extractQmlRuntimeString(self, compilationUnitPtr, index):
|
|
||||||
# This mimics compilationUnit->runtimeStrings[index]
|
|
||||||
# typeof runtimeStrings = QV4.StringValue **
|
|
||||||
runtimeStrings = compilationUnitPtr.dereference()["runtimeStrings"]
|
|
||||||
entry = runtimeStrings[index]
|
|
||||||
text = self.extractPointer(entry.dereference(), self.ptrSize())
|
|
||||||
(elided, fn) = self.encodeStringHelper(text, 100)
|
|
||||||
return self.encodedUtf16ToUtf8(fn)
|
|
||||||
|
|
||||||
def extractQmlLocation(self, engine):
|
|
||||||
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"]
|
|
||||||
functionName = "Unknown 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"]
|
|
||||||
# QV4.CompiledData.Function
|
|
||||||
compiledFunction = function["compiledFunction"].dereference()
|
|
||||||
index = int(compiledFunction["nameIndex"])
|
|
||||||
functionName = "JS: " + self.extractQmlRuntimeString(compilationUnit, index)
|
|
||||||
|
|
||||||
string = self.parseAndEvaluate("((%s)0x%x)->fileName()"
|
|
||||||
% (compilationUnit.type, compilationUnit))
|
|
||||||
fileName = self.encodeStringUtf8(string)
|
|
||||||
|
|
||||||
return {'functionName': functionName,
|
|
||||||
'lineNumber': int(context["lineNumber"]),
|
|
||||||
'fileName': fileName,
|
|
||||||
'context': context }
|
|
||||||
|
|
||||||
# Contains iname, name, and value.
|
# Contains iname, name, and value.
|
||||||
class LocalItem:
|
class LocalItem:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def extractQmlVariables(self, qmlcontext):
|
def extractInterpreterStack(self):
|
||||||
items = []
|
return self.sendInterpreterRequest('backtrace', {'limit': 10 })
|
||||||
|
|
||||||
contextType = self.lookupQtType("QV4::Heap::CallContext")
|
|
||||||
context = self.createPointerValue(self.qmlcontext, contextType)
|
|
||||||
|
|
||||||
contextItem = self.LocalItem()
|
|
||||||
contextItem.iname = "local.@context"
|
|
||||||
contextItem.name = "[context]"
|
|
||||||
contextItem.value = context.dereference()
|
|
||||||
items.append(contextItem)
|
|
||||||
|
|
||||||
argsItem = self.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 = self.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 = self.LocalItem()
|
|
||||||
item.iname = "local." + name
|
|
||||||
item.name = name
|
|
||||||
item.value = context["locals"][index]
|
|
||||||
items.append(item)
|
|
||||||
|
|
||||||
for engine in self.qmlEngines:
|
|
||||||
engineItem = self.LocalItem()
|
|
||||||
engineItem.iname = "local.@qmlengine"
|
|
||||||
engineItem.name = "[engine]"
|
|
||||||
engineItem.value = engine
|
|
||||||
items.append(engineItem)
|
|
||||||
|
|
||||||
rootContext = self.LocalItem()
|
|
||||||
rootContext.iname = "local.@rootContext"
|
|
||||||
rootContext.name = "[rootContext]"
|
|
||||||
rootContext.value = engine["d_ptr"]
|
|
||||||
items.append(rootContext)
|
|
||||||
break
|
|
||||||
|
|
||||||
return items
|
|
||||||
|
|
||||||
|
def extractInterpreterVariables(self, args):
|
||||||
|
return self.sendInterpreterRequest('variables', args)
|
||||||
|
|
||||||
|
@@ -14,9 +14,6 @@ import sys
|
|||||||
import struct
|
import struct
|
||||||
import types
|
import types
|
||||||
|
|
||||||
def warn(message):
|
|
||||||
print("XXX: %s\n" % message.encode("latin1"))
|
|
||||||
|
|
||||||
from dumper import *
|
from dumper import *
|
||||||
|
|
||||||
|
|
||||||
@@ -221,15 +218,14 @@ class Dumper(DumperBase):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
DumperBase.__init__(self)
|
DumperBase.__init__(self)
|
||||||
|
|
||||||
# These values will be kept between calls to 'showData'.
|
# These values will be kept between calls to 'fetchVariables'.
|
||||||
self.isGdb = True
|
self.isGdb = True
|
||||||
self.childEventAddress = None
|
self.childEventAddress = None
|
||||||
self.typeCache = {}
|
self.typeCache = {}
|
||||||
self.typesReported = {}
|
self.typesReported = {}
|
||||||
self.typesToReport = {}
|
self.typesToReport = {}
|
||||||
self.qtNamespaceToReport = None
|
self.qtNamespaceToReport = None
|
||||||
self.qmlEngines = []
|
self.interpreterBreakpoints = []
|
||||||
self.qmlBreakpoints = []
|
|
||||||
|
|
||||||
def prepare(self, args):
|
def prepare(self, args):
|
||||||
self.output = []
|
self.output = []
|
||||||
@@ -243,7 +239,7 @@ class Dumper(DumperBase):
|
|||||||
self.currentType = ReportItem()
|
self.currentType = ReportItem()
|
||||||
self.currentAddress = None
|
self.currentAddress = None
|
||||||
|
|
||||||
# The guess does not need to be updated during a showData()
|
# The guess does not need to be updated during a fetchVariables()
|
||||||
# as the result is fixed during that time (ignoring "active"
|
# as the result is fixed during that time (ignoring "active"
|
||||||
# dumpers causing loading of shared objects etc).
|
# dumpers causing loading of shared objects etc).
|
||||||
self.currentQtNamespaceGuess = None
|
self.currentQtNamespaceGuess = None
|
||||||
@@ -255,11 +251,10 @@ class Dumper(DumperBase):
|
|||||||
self.typeformats = args.get("typeformats", {})
|
self.typeformats = args.get("typeformats", {})
|
||||||
self.formats = args.get("formats", {})
|
self.formats = args.get("formats", {})
|
||||||
self.watchers = args.get("watchers", {})
|
self.watchers = args.get("watchers", {})
|
||||||
self.qmlcontext = int(args.get("qmlcontext", "0"), 0)
|
|
||||||
self.useDynamicType = int(args.get("dyntype", "0"))
|
self.useDynamicType = int(args.get("dyntype", "0"))
|
||||||
self.useFancy = int(args.get("fancy", "0"))
|
self.useFancy = int(args.get("fancy", "0"))
|
||||||
self.forceQtNamespace = int(args.get("forcens", "0"))
|
self.forceQtNamespace = int(args.get("forcens", "0"))
|
||||||
self.passExceptions = int(args.get("passExceptions", "0"))
|
self.passExceptions = int(args.get("passexeptions", "0"))
|
||||||
self.nativeMixed = int(args.get("nativemixed", "0"))
|
self.nativeMixed = int(args.get("nativemixed", "0"))
|
||||||
self.autoDerefPointers = int(args.get("autoderef", "0"))
|
self.autoDerefPointers = int(args.get("autoderef", "0"))
|
||||||
self.partialUpdate = int(args.get("partial", "0"))
|
self.partialUpdate = int(args.get("partial", "0"))
|
||||||
@@ -359,21 +354,25 @@ class Dumper(DumperBase):
|
|||||||
def canCallLocale(self):
|
def canCallLocale(self):
|
||||||
return False if self.is32bit() else True
|
return False if self.is32bit() else True
|
||||||
|
|
||||||
def showData(self, args):
|
def fetchVariables(self, args):
|
||||||
self.prepare(args)
|
self.prepare(args)
|
||||||
|
|
||||||
partialVariable = args.get("partialVariable", "")
|
partialVariable = args.get("partialVariable", "")
|
||||||
isPartial = len(partialVariable) > 0
|
isPartial = len(partialVariable) > 0
|
||||||
|
|
||||||
|
if self.nativeMixed:
|
||||||
|
context = args.get('context', '')
|
||||||
|
if len(context):
|
||||||
|
res = self.extractInterpreterVariables(args)
|
||||||
|
if res:
|
||||||
|
safePrint('data=%s' % self.dictToMi(res.get('data', {})))
|
||||||
|
return
|
||||||
|
|
||||||
#
|
#
|
||||||
# Locals
|
# Locals
|
||||||
#
|
#
|
||||||
self.output.append('data=[')
|
self.output.append('data=[')
|
||||||
|
|
||||||
if self.qmlcontext:
|
if isPartial:
|
||||||
locals = self.extractQmlVariables(self.qmlcontext)
|
|
||||||
|
|
||||||
elif isPartial:
|
|
||||||
parts = partialVariable.split('.')
|
parts = partialVariable.split('.')
|
||||||
name = parts[1]
|
name = parts[1]
|
||||||
item = self.LocalItem()
|
item = self.LocalItem()
|
||||||
@@ -498,7 +497,7 @@ class Dumper(DumperBase):
|
|||||||
def parseAndEvaluate(self, exp):
|
def parseAndEvaluate(self, exp):
|
||||||
return gdb.parse_and_eval(exp)
|
return gdb.parse_and_eval(exp)
|
||||||
|
|
||||||
def callHelper(self, value, func, args):
|
def callHelper(self, value, function, args):
|
||||||
# args is a tuple.
|
# args is a tuple.
|
||||||
arg = ""
|
arg = ""
|
||||||
for i in range(len(args)):
|
for i in range(len(args)):
|
||||||
@@ -510,14 +509,14 @@ class Dumper(DumperBase):
|
|||||||
else:
|
else:
|
||||||
arg += a
|
arg += a
|
||||||
|
|
||||||
#warn("CALL: %s -> %s(%s)" % (value, func, arg))
|
#warn("CALL: %s -> %s(%s)" % (value, function, arg))
|
||||||
typeName = self.stripClassTag(str(value.type))
|
typeName = self.stripClassTag(str(value.type))
|
||||||
if typeName.find(":") >= 0:
|
if typeName.find(":") >= 0:
|
||||||
typeName = "'" + typeName + "'"
|
typeName = "'" + typeName + "'"
|
||||||
# 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
|
# 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
|
||||||
#exp = "((class %s*)%s)->%s(%s)" % (typeName, value.address, func, arg)
|
#exp = "((class %s*)%s)->%s(%s)" % (typeName, value.address, function, arg)
|
||||||
ptr = value.address if value.address else self.pokeValue(value)
|
ptr = value.address if value.address else self.pokeValue(value)
|
||||||
exp = "((%s*)%s)->%s(%s)" % (typeName, ptr, func, arg)
|
exp = "((%s*)%s)->%s(%s)" % (typeName, ptr, function, arg)
|
||||||
#warn("CALL: %s" % exp)
|
#warn("CALL: %s" % exp)
|
||||||
result = gdb.parse_and_eval(exp)
|
result = gdb.parse_and_eval(exp)
|
||||||
#warn(" -> %s" % result)
|
#warn(" -> %s" % result)
|
||||||
@@ -739,32 +738,32 @@ class Dumper(DumperBase):
|
|||||||
self.selectedInferior = lambda: self.cachedInferior
|
self.selectedInferior = lambda: self.cachedInferior
|
||||||
return self.cachedInferior
|
return self.cachedInferior
|
||||||
|
|
||||||
def readRawMemory(self, addr, size):
|
def readRawMemory(self, address, size):
|
||||||
mem = self.selectedInferior().read_memory(addr, size)
|
mem = self.selectedInferior().read_memory(address, size)
|
||||||
if sys.version_info[0] >= 3:
|
if sys.version_info[0] >= 3:
|
||||||
mem.tobytes()
|
mem.tobytes()
|
||||||
return mem
|
return mem
|
||||||
|
|
||||||
def extractInt64(self, addr):
|
def extractInt64(self, address):
|
||||||
return struct.unpack("q", self.readRawMemory(addr, 8))[0]
|
return struct.unpack("q", self.readRawMemory(address, 8))[0]
|
||||||
|
|
||||||
def extractUInt64(self, addr):
|
def extractUInt64(self, address):
|
||||||
return struct.unpack("Q", self.readRawMemory(addr, 8))[0]
|
return struct.unpack("Q", self.readRawMemory(address, 8))[0]
|
||||||
|
|
||||||
def extractInt(self, addr):
|
def extractInt(self, address):
|
||||||
return struct.unpack("i", self.readRawMemory(addr, 4))[0]
|
return struct.unpack("i", self.readRawMemory(address, 4))[0]
|
||||||
|
|
||||||
def extractUInt(self, addr):
|
def extractUInt(self, address):
|
||||||
return struct.unpack("I", self.readRawMemory(addr, 4))[0]
|
return struct.unpack("I", self.readRawMemory(address, 4))[0]
|
||||||
|
|
||||||
def extractShort(self, addr):
|
def extractShort(self, address):
|
||||||
return struct.unpack("h", self.readRawMemory(addr, 2))[0]
|
return struct.unpack("h", self.readRawMemory(address, 2))[0]
|
||||||
|
|
||||||
def extractUShort(self, addr):
|
def extractUShort(self, address):
|
||||||
return struct.unpack("H", self.readRawMemory(addr, 2))[0]
|
return struct.unpack("H", self.readRawMemory(address, 2))[0]
|
||||||
|
|
||||||
def extractByte(self, addr):
|
def extractByte(self, address):
|
||||||
return struct.unpack("b", self.readRawMemory(addr, 1))[0]
|
return struct.unpack("b", self.readRawMemory(address, 1))[0]
|
||||||
|
|
||||||
def findStaticMetaObject(self, typename):
|
def findStaticMetaObject(self, typename):
|
||||||
return self.findSymbol(typename + "::staticMetaObject")
|
return self.findSymbol(typename + "::staticMetaObject")
|
||||||
@@ -847,12 +846,12 @@ class Dumper(DumperBase):
|
|||||||
self.isQt3Support = lambda: self.cachedIsQt3Suport
|
self.isQt3Support = lambda: self.cachedIsQt3Suport
|
||||||
return self.cachedIsQt3Suport
|
return self.cachedIsQt3Suport
|
||||||
|
|
||||||
def putAddress(self, addr):
|
def putAddress(self, address):
|
||||||
if self.currentPrintsAddress and not self.isCli:
|
if self.currentPrintsAddress and not self.isCli:
|
||||||
try:
|
try:
|
||||||
# addr can be "None", int(None) fails.
|
# address can be "None", int(None) fails.
|
||||||
#self.put('addr="0x%x",' % int(addr))
|
#self.put('address="0x%x",' % int(address))
|
||||||
self.currentAddress = 'addr="0x%x",' % toInteger(addr)
|
self.currentAddress = 'address="0x%x",' % toInteger(address)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -1574,6 +1573,9 @@ class Dumper(DumperBase):
|
|||||||
self.typesToReport[typestring] = typeobj
|
self.typesToReport[typestring] = typeobj
|
||||||
return typeobj
|
return typeobj
|
||||||
|
|
||||||
|
def doContinue(self):
|
||||||
|
gdb.execute('continue')
|
||||||
|
|
||||||
def stackListFrames(self, args):
|
def stackListFrames(self, args):
|
||||||
def fromNativePath(str):
|
def fromNativePath(str):
|
||||||
return str.replace('\\', '/')
|
return str.replace('\\', '/')
|
||||||
@@ -1581,12 +1583,8 @@ class Dumper(DumperBase):
|
|||||||
limit = int(args['limit'])
|
limit = int(args['limit'])
|
||||||
if limit <= 0:
|
if limit <= 0:
|
||||||
limit = 10000
|
limit = 10000
|
||||||
options = args['options']
|
|
||||||
opts = {}
|
|
||||||
if options == "nativemixed":
|
|
||||||
opts["nativemixed"] = 1
|
|
||||||
|
|
||||||
self.prepare(opts)
|
self.prepare(args)
|
||||||
self.output = []
|
self.output = []
|
||||||
|
|
||||||
frame = gdb.newest_frame()
|
frame = gdb.newest_frame()
|
||||||
@@ -1598,7 +1596,7 @@ class Dumper(DumperBase):
|
|||||||
functionName = "??" if name is None else name
|
functionName = "??" if name is None else name
|
||||||
fileName = ""
|
fileName = ""
|
||||||
objfile = ""
|
objfile = ""
|
||||||
fullName = ""
|
symtab = ""
|
||||||
pc = frame.pc()
|
pc = frame.pc()
|
||||||
sal = frame.find_sal()
|
sal = frame.find_sal()
|
||||||
line = -1
|
line = -1
|
||||||
@@ -1607,62 +1605,57 @@ class Dumper(DumperBase):
|
|||||||
symtab = sal.symtab
|
symtab = sal.symtab
|
||||||
if not symtab is None:
|
if not symtab is None:
|
||||||
objfile = fromNativePath(symtab.objfile.filename)
|
objfile = fromNativePath(symtab.objfile.filename)
|
||||||
fileName = fromNativePath(symtab.filename)
|
fileName = fromNativePath(symtab.fullname())
|
||||||
fullName = symtab.fullname()
|
|
||||||
if fullName is None:
|
|
||||||
fullName = ""
|
|
||||||
else:
|
|
||||||
fullName = fromNativePath(fullName)
|
|
||||||
|
|
||||||
if self.nativeMixed:
|
if self.nativeMixed and functionName == "qt_qmlDebugEventFromService":
|
||||||
if self.isReportableQmlFrame(functionName):
|
interpreterStack = self.extractInterpreterStack()
|
||||||
engine = frame.read_var("engine")
|
#print("EXTRACTED INTEPRETER STACK: %s" % interpreterStack)
|
||||||
h = self.extractQmlLocation(engine)
|
for interpreterFrame in interpreterStack.get('frames', []):
|
||||||
self.put(('frame={level="%s",func="%s",file="%s",'
|
function = interpreterFrame.get('function', '')
|
||||||
'fullname="%s",line="%s",language="js",addr="0x%x"}')
|
fileName = interpreterFrame.get('file', '')
|
||||||
% (i, h['functionName'], h['fileName'], h['fileName'],
|
language = interpreterFrame.get('language', '')
|
||||||
h['lineNumber'], h['context']))
|
lineNumber = interpreterFrame.get('line', 0)
|
||||||
|
context = interpreterFrame.get('context', 0)
|
||||||
|
|
||||||
|
self.put(('frame={function="%s",file="%s",'
|
||||||
|
'line="%s",language="%s",context="%s"}')
|
||||||
|
% (function, fileName, lineNumber, language, context))
|
||||||
|
|
||||||
|
if False and self.isInternalInterpreterFrame(functionName):
|
||||||
|
frame = frame.older()
|
||||||
|
self.put(('frame={address="0x%x",function="%s",'
|
||||||
|
'file="%s",line="%s",'
|
||||||
|
'module="%s",language="c",usable="0"}') %
|
||||||
|
(pc, functionName, fileName, line, objfile))
|
||||||
i += 1
|
i += 1
|
||||||
frame = frame.older()
|
frame = frame.older()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self.isInternalQmlFrame(functionName):
|
self.put(('frame={level="%s",address="0x%x",function="%s",'
|
||||||
frame = frame.older()
|
'file="%s",line="%s",module="%s",language="c"}') %
|
||||||
self.put(('frame={level="%s",addr="0x%x",func="%s",'
|
(i, pc, functionName, fileName, line, objfile))
|
||||||
'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
|
||||||
safePrint(''.join(self.output))
|
safePrint('frames=[' + ','.join(self.output) + ']')
|
||||||
|
|
||||||
def createResolvePendingBreakpointsHookBreakpoint(self, args):
|
def createResolvePendingBreakpointsHookBreakpoint(self, args):
|
||||||
class Resolver(gdb.Breakpoint):
|
class Resolver(gdb.Breakpoint):
|
||||||
def __init__(self, dumper, args):
|
def __init__(self, dumper, args):
|
||||||
self.dumper = dumper
|
self.dumper = dumper
|
||||||
self.args = args
|
self.args = args
|
||||||
spec = "qt_v4ResolvePendingBreakpointsHook"
|
spec = "qt_qmlDebugConnectorOpen"
|
||||||
print("Preparing hook to resolve pending QML breakpoint at %s" % args)
|
print("Preparing hook to resolve pending QML breakpoint at %s" % args)
|
||||||
super(Resolver, self).\
|
super(Resolver, self).\
|
||||||
__init__(spec, gdb.BP_BREAKPOINT, internal=True, temporary=False)
|
__init__(spec, gdb.BP_BREAKPOINT, internal=True, temporary=False)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
bp = self.dumper.doInsertQmlBreakpoint(args)
|
bp = self.dumper.doInsertInterpreterBreakpoint(args, True)
|
||||||
print("Resolving QML breakpoint %s -> %s" % (args, bp))
|
print("Resolving QML breakpoint %s -> %s" % (args, bp))
|
||||||
self.enabled = False
|
self.enabled = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.qmlBreakpoints.append(Resolver(self, args))
|
self.interpreterBreakpoints.append(Resolver(self, args))
|
||||||
|
|
||||||
def exitGdb(self, _):
|
def exitGdb(self, _):
|
||||||
gdb.execute("quit")
|
gdb.execute("quit")
|
||||||
@@ -1675,13 +1668,13 @@ class Dumper(DumperBase):
|
|||||||
import tempfile
|
import tempfile
|
||||||
import cProfile
|
import cProfile
|
||||||
tempDir = tempfile.gettempdir() + "/bbprof"
|
tempDir = tempfile.gettempdir() + "/bbprof"
|
||||||
cProfile.run('theDumper.showData(%s)' % args, tempDir)
|
cProfile.run('theDumper.fetchVariables(%s)' % args, tempDir)
|
||||||
import pstats
|
import pstats
|
||||||
pstats.Stats(tempDir).sort_stats('time').print_stats()
|
pstats.Stats(tempDir).sort_stats('time').print_stats()
|
||||||
|
|
||||||
def profile2(self, args):
|
def profile2(self, args):
|
||||||
import timeit
|
import timeit
|
||||||
print(timeit.repeat('theDumper.showData(%s)' % args,
|
print(timeit.repeat('theDumper.fetchVariables(%s)' % args,
|
||||||
'from __main__ import theDumper', number=10))
|
'from __main__ import theDumper', number=10))
|
||||||
|
|
||||||
|
|
||||||
@@ -1774,7 +1767,7 @@ class CliDumper(Dumper):
|
|||||||
def putAddressRange(self, base, step):
|
def putAddressRange(self, base, step):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def showData(self, args):
|
def fetchVariables(self, args):
|
||||||
args['fancy'] = 1
|
args['fancy'] = 1
|
||||||
args['passException'] = 1
|
args['passException'] = 1
|
||||||
args['autoderef'] = 1
|
args['autoderef'] = 1
|
||||||
@@ -1811,20 +1804,6 @@ registerCommand("threadnames", threadnames)
|
|||||||
#
|
#
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
|
||||||
#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):
|
class TriggeredBreakpointHookBreakpoint(gdb.Breakpoint):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
spec = "qt_v4TriggeredBreakpointHook"
|
spec = "qt_v4TriggeredBreakpointHook"
|
||||||
@@ -1837,3 +1816,14 @@ class TriggeredBreakpointHookBreakpoint(gdb.Breakpoint):
|
|||||||
|
|
||||||
TriggeredBreakpointHookBreakpoint()
|
TriggeredBreakpointHookBreakpoint()
|
||||||
|
|
||||||
|
class QmlEngineEventBreakpoint(gdb.Breakpoint):
|
||||||
|
def __init__(self):
|
||||||
|
spec = "qt_qmlDebugEventFromService"
|
||||||
|
super(QmlEngineEventBreakpoint, self).\
|
||||||
|
__init__(spec, gdb.BP_BREAKPOINT, internal=True)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
print("QML engine event received.")
|
||||||
|
return theDumper.handleInterpreterEvent()
|
||||||
|
|
||||||
|
QmlEngineEventBreakpoint()
|
||||||
|
@@ -57,7 +57,7 @@ def showException(msg, exType, exValue, exTraceback):
|
|||||||
lines = [line for line in traceback.format_exception(exType, exValue, exTraceback)]
|
lines = [line for line in traceback.format_exception(exType, exValue, exTraceback)]
|
||||||
warn('\n'.join(lines))
|
warn('\n'.join(lines))
|
||||||
|
|
||||||
def fileName(file):
|
def fileNameAsString(file):
|
||||||
return str(file) if file.IsValid() else ''
|
return str(file) if file.IsValid() else ''
|
||||||
|
|
||||||
|
|
||||||
@@ -775,7 +775,7 @@ class Dumper(DumperBase):
|
|||||||
def describeLocation(self, frame):
|
def describeLocation(self, frame):
|
||||||
if int(frame.pc) == 0xffffffffffffffff:
|
if int(frame.pc) == 0xffffffffffffffff:
|
||||||
return ''
|
return ''
|
||||||
file = fileName(frame.line_entry.file)
|
file = fileNameAsString(frame.line_entry.file)
|
||||||
line = frame.line_entry.line
|
line = frame.line_entry.line
|
||||||
return 'location={file="%s",line="%s",addr="%s"}' % (file, line, frame.pc)
|
return 'location={file="%s",line="%s",addr="%s"}' % (file, line, frame.pc)
|
||||||
|
|
||||||
@@ -822,8 +822,8 @@ class Dumper(DumperBase):
|
|||||||
result += ',fp="0x%x"' % frame.fp
|
result += ',fp="0x%x"' % frame.fp
|
||||||
result += ',func="%s"' % frame.GetFunctionName()
|
result += ',func="%s"' % frame.GetFunctionName()
|
||||||
result += ',line="%s"' % frame.line_entry.line
|
result += ',line="%s"' % frame.line_entry.line
|
||||||
result += ',fullname="%s"' % fileName(frame.line_entry.file)
|
result += ',fullname="%s"' % fileNameAsString(frame.line_entry.file)
|
||||||
result += ',file="%s"' % fileName(frame.line_entry.file)
|
result += ',file="%s"' % fileNameAsString(frame.line_entry.file)
|
||||||
result += '}},'
|
result += '}},'
|
||||||
|
|
||||||
result += '],current-thread-id="%s"' % self.currentThread().id
|
result += '],current-thread-id="%s"' % self.currentThread().id
|
||||||
@@ -849,7 +849,7 @@ class Dumper(DumperBase):
|
|||||||
|
|
||||||
self.report(self.describeLocation(thread.GetFrameAtIndex(0))) # FIXME
|
self.report(self.describeLocation(thread.GetFrameAtIndex(0))) # FIXME
|
||||||
|
|
||||||
isNativeMixed = int(args.get('nativeMixed', 0))
|
isNativeMixed = int(args.get('nativemixed', 0))
|
||||||
|
|
||||||
limit = args.get('stacklimit', -1)
|
limit = args.get('stacklimit', -1)
|
||||||
(n, isLimited) = (limit, True) if limit > 0 else (thread.GetNumFrames(), False)
|
(n, isLimited) = (limit, True) if limit > 0 else (thread.GetNumFrames(), False)
|
||||||
@@ -869,39 +869,38 @@ class Dumper(DumperBase):
|
|||||||
level = frame.idx
|
level = frame.idx
|
||||||
addr = frame.GetPCAddress().GetLoadAddress(self.target)
|
addr = frame.GetPCAddress().GetLoadAddress(self.target)
|
||||||
functionName = frame.GetFunctionName()
|
functionName = frame.GetFunctionName()
|
||||||
fullname = fileName(lineEntry.file)
|
fileName = fileNameAsString(lineEntry.file)
|
||||||
usable = None
|
usable = None
|
||||||
language = None
|
language = None
|
||||||
|
|
||||||
if isNativeMixed:
|
if False and isNativeMixed:
|
||||||
if self.isReportableQmlFrame(functionName):
|
if self.isReportableInterpreterFrame(functionName):
|
||||||
engine = frame.FindVariable("engine")
|
engine = frame.FindVariable("engine")
|
||||||
self.context = engine
|
self.context = engine
|
||||||
h = self.extractQmlLocation(engine)
|
h = self.extractQmlLocation(engine)
|
||||||
pc = 0
|
pc = 0
|
||||||
functionName = h['functionName']
|
functionName = h['function']
|
||||||
fullname = h['fileName']
|
fileName = h['file']
|
||||||
lineNumber = h['lineNumber']
|
lineNumber = h['line']
|
||||||
addr = h['context']
|
addr = h['context']
|
||||||
language = 'js'
|
language = 'js'
|
||||||
|
|
||||||
elif not functionName is None:
|
#elif not functionName is None:
|
||||||
if functionName.startswith("qt_v4"):
|
# if functionName.startswith("qt_v4"):
|
||||||
usable = 0
|
# usable = 0
|
||||||
elif functionName.find("QV4::") >= 0:
|
# elif functionName.find("QV4::") >= 0:
|
||||||
usable = 0
|
# usable = 0
|
||||||
|
|
||||||
result += '{pc="0x%x"' % pc
|
result += '{pc="0x%x"' % pc
|
||||||
result += ',level="%d"' % level
|
result += ',level="%d"' % level
|
||||||
result += ',addr="0x%x"' % addr
|
result += ',address="0x%x"' % addr
|
||||||
if not usable is None:
|
if not usable is None:
|
||||||
result += ',usable="%s"' % usable
|
result += ',usable="%s"' % usable
|
||||||
result += ',func="%s"' % functionName
|
result += ',function="%s"' % functionName
|
||||||
result += ',line="%d"' % lineNumber
|
result += ',line="%d"' % lineNumber
|
||||||
if not language is None:
|
if not language is None:
|
||||||
result += ',language="%s"' % language
|
result += ',language="%s"' % language
|
||||||
result += ',fullname="%s"' % fullname
|
result += ',file="%s"},' % fileName
|
||||||
result += ',file="%s"},' % fullname
|
|
||||||
result += ']'
|
result += ']'
|
||||||
result += ',hasmore="%d"' % isLimited
|
result += ',hasmore="%d"' % isLimited
|
||||||
result += ',limit="%d"' % limit
|
result += ',limit="%d"' % limit
|
||||||
@@ -1400,7 +1399,7 @@ class Dumper(DumperBase):
|
|||||||
addr = loc.GetAddress()
|
addr = loc.GetAddress()
|
||||||
lineEntry = addr.GetLineEntry()
|
lineEntry = addr.GetLineEntry()
|
||||||
result += '{locid="%s"' % loc.GetID()
|
result += '{locid="%s"' % loc.GetID()
|
||||||
result += ',func="%s"' % addr.GetFunction().GetName()
|
result += ',function="%s"' % addr.GetFunction().GetName()
|
||||||
result += ',enabled="%s"' % (1 if loc.IsEnabled() else 0)
|
result += ',enabled="%s"' % (1 if loc.IsEnabled() else 0)
|
||||||
result += ',resolved="%s"' % (1 if loc.IsResolved() else 0)
|
result += ',resolved="%s"' % (1 if loc.IsResolved() else 0)
|
||||||
result += ',valid="%s"' % (1 if loc.IsValid() else 0)
|
result += ',valid="%s"' % (1 if loc.IsValid() else 0)
|
||||||
@@ -1421,16 +1420,16 @@ class Dumper(DumperBase):
|
|||||||
def insertBreakpoint(self, args):
|
def insertBreakpoint(self, args):
|
||||||
bpType = args["type"]
|
bpType = args["type"]
|
||||||
if bpType == BreakpointByFileAndLine:
|
if bpType == BreakpointByFileAndLine:
|
||||||
fileName = args["fileName"]
|
fileName = args["file"]
|
||||||
if fileName.endswith(".js") or fileName.endswith(".qml"):
|
if fileName.endswith(".js") or fileName.endswith(".qml"):
|
||||||
self.insertQmlBreakpoint(args)
|
self.doInsertInterpreterBreakpoint(args, False)
|
||||||
return
|
return
|
||||||
|
|
||||||
extra = ''
|
extra = ''
|
||||||
more = True
|
more = True
|
||||||
if bpType == BreakpointByFileAndLine:
|
if bpType == BreakpointByFileAndLine:
|
||||||
bp = self.target.BreakpointCreateByLocation(
|
bp = self.target.BreakpointCreateByLocation(
|
||||||
str(args["fileName"]), int(args["lineNumber"]))
|
str(args["file"]), int(args["line"]))
|
||||||
elif bpType == BreakpointByFunction:
|
elif bpType == BreakpointByFunction:
|
||||||
bp = self.target.BreakpointCreateByName(args["function"])
|
bp = self.target.BreakpointCreateByName(args["function"])
|
||||||
elif bpType == BreakpointByAddress:
|
elif bpType == BreakpointByAddress:
|
||||||
|
@@ -34,10 +34,12 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
namespace QmlDebug {
|
namespace QmlDebug {
|
||||||
|
|
||||||
enum QmlDebugServicesPreset {
|
enum QmlDebugServicesPreset {
|
||||||
NoQmlDebugServices,
|
NoQmlDebugServices,
|
||||||
QmlDebuggerServices,
|
QmlDebuggerServices,
|
||||||
QmlProfilerServices
|
QmlProfilerServices,
|
||||||
|
QmlNativeDebuggerServices
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline QString qmlDebugServices(QmlDebugServicesPreset preset)
|
static inline QString qmlDebugServices(QmlDebugServicesPreset preset)
|
||||||
@@ -49,6 +51,8 @@ static inline QString qmlDebugServices(QmlDebugServicesPreset preset)
|
|||||||
return QStringLiteral("DebugMessages,QmlDebugger,V8Debugger,QmlInspector");
|
return QStringLiteral("DebugMessages,QmlDebugger,V8Debugger,QmlInspector");
|
||||||
case QmlProfilerServices:
|
case QmlProfilerServices:
|
||||||
return QStringLiteral("CanvasFrameRate,EngineControl");
|
return QStringLiteral("CanvasFrameRate,EngineControl");
|
||||||
|
case QmlNativeDebuggerServices:
|
||||||
|
return QStringLiteral("NativeQmlDebugger");
|
||||||
default:
|
default:
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
return QString();
|
return QString();
|
||||||
@@ -61,6 +65,10 @@ static inline QString qmlDebugCommandLineArguments(QmlDebugServicesPreset servic
|
|||||||
if (services == NoQmlDebugServices)
|
if (services == NoQmlDebugServices)
|
||||||
return QString();
|
return QString();
|
||||||
|
|
||||||
|
if (services == QmlNativeDebuggerServices)
|
||||||
|
return QString::fromLatin1("-qmljsdebugger=native,services:%1")
|
||||||
|
.arg(qmlDebugServices(services));
|
||||||
|
|
||||||
return QString::fromLatin1("-qmljsdebugger=port:%1,block,services:%2")
|
return QString::fromLatin1("-qmljsdebugger=port:%1,block,services:%2")
|
||||||
.arg(port ? QString::number(port) : QStringLiteral("%qml_port%"))
|
.arg(port ? QString::number(port) : QStringLiteral("%qml_port%"))
|
||||||
.arg(qmlDebugServices(services));
|
.arg(qmlDebugServices(services));
|
||||||
|
@@ -757,14 +757,15 @@ const BreakpointParameters &Breakpoint::parameters() const
|
|||||||
void Breakpoint::addToCommand(DebuggerCommand *cmd) const
|
void Breakpoint::addToCommand(DebuggerCommand *cmd) const
|
||||||
{
|
{
|
||||||
cmd->arg("modelid", id().toByteArray());
|
cmd->arg("modelid", id().toByteArray());
|
||||||
|
cmd->arg("id", int(response().id.majorPart()));
|
||||||
cmd->arg("type", type());
|
cmd->arg("type", type());
|
||||||
cmd->arg("ignorecount", ignoreCount());
|
cmd->arg("ignorecount", ignoreCount());
|
||||||
cmd->arg("condition", condition().toHex());
|
cmd->arg("condition", condition().toHex());
|
||||||
cmd->arg("function", functionName().toUtf8());
|
cmd->arg("function", functionName().toUtf8());
|
||||||
cmd->arg("oneshot", isOneShot());
|
cmd->arg("oneshot", isOneShot());
|
||||||
cmd->arg("enabled", isEnabled());
|
cmd->arg("enabled", isEnabled());
|
||||||
cmd->arg("fileName", fileName().toUtf8());
|
cmd->arg("file", fileName().toUtf8());
|
||||||
cmd->arg("lineNumber", lineNumber());
|
cmd->arg("line", lineNumber());
|
||||||
cmd->arg("address", address());
|
cmd->arg("address", address());
|
||||||
cmd->arg("expression", expression());
|
cmd->arg("expression", expression());
|
||||||
}
|
}
|
||||||
|
@@ -2539,8 +2539,7 @@ bool CdbEngine::stateAcceptsBreakpointChanges() const
|
|||||||
|
|
||||||
bool CdbEngine::acceptsBreakpoint(Breakpoint bp) const
|
bool CdbEngine::acceptsBreakpoint(Breakpoint bp) const
|
||||||
{
|
{
|
||||||
if (!bp.parameters().isCppBreakpoint())
|
if (bp.parameters().isCppBreakpoint()) {
|
||||||
return false;
|
|
||||||
switch (bp.type()) {
|
switch (bp.type()) {
|
||||||
case UnknownBreakpointType:
|
case UnknownBreakpointType:
|
||||||
case LastBreakpointType:
|
case LastBreakpointType:
|
||||||
@@ -2561,6 +2560,8 @@ bool CdbEngine::acceptsBreakpoint(Breakpoint bp) const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
return isNativeMixedEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context for fixing file/line-type breakpoints, for delayed creation.
|
// Context for fixing file/line-type breakpoints, for delayed creation.
|
||||||
@@ -2801,7 +2802,7 @@ static StackFrames parseFrames(const GdbMi &gdbmi, bool *incomplete = 0)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
StackFrame frame;
|
StackFrame frame;
|
||||||
frame.level = i;
|
frame.level = QByteArray::number(i);
|
||||||
const GdbMi fullName = frameMi["fullname"];
|
const GdbMi fullName = frameMi["fullname"];
|
||||||
if (fullName.isValid()) {
|
if (fullName.isValid()) {
|
||||||
frame.file = QFile::decodeName(fullName.data());
|
frame.file = QFile::decodeName(fullName.data());
|
||||||
@@ -2811,9 +2812,10 @@ static StackFrames parseFrames(const GdbMi &gdbmi, bool *incomplete = 0)
|
|||||||
if (languageMi.isValid() && languageMi.data() == "js")
|
if (languageMi.isValid() && languageMi.data() == "js")
|
||||||
frame.language = QmlLanguage;
|
frame.language = QmlLanguage;
|
||||||
}
|
}
|
||||||
frame.function = QLatin1String(frameMi["func"].data());
|
frame.function = QLatin1String(frameMi["function"].data());
|
||||||
frame.from = QLatin1String(frameMi["from"].data());
|
frame.module = QLatin1String(frameMi["from"].data());
|
||||||
frame.address = frameMi["addr"].data().toULongLong(0, 16);
|
frame.context = frameMi["context"].data();
|
||||||
|
frame.address = frameMi["address"].data().toULongLong(0, 16);
|
||||||
rc.push_back(frame);
|
rc.push_back(frame);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
@@ -2895,7 +2897,7 @@ void CdbEngine::handleAdditionalQmlStack(const DebuggerResponse &response)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < qmlFrameCount; ++i)
|
for (int i = 0; i < qmlFrameCount; ++i)
|
||||||
qmlFrames[i].fixQmlFrame(runParameters());
|
qmlFrames[i].fixQrcFrame(runParameters());
|
||||||
stackHandler()->prependFrames(qmlFrames);
|
stackHandler()->prependFrames(qmlFrames);
|
||||||
} while (false);
|
} while (false);
|
||||||
if (!errorMessage.isEmpty())
|
if (!errorMessage.isEmpty())
|
||||||
|
@@ -15,6 +15,7 @@ QtcPlugin {
|
|||||||
Depends { name: "Core" }
|
Depends { name: "Core" }
|
||||||
Depends { name: "CppTools" }
|
Depends { name: "CppTools" }
|
||||||
Depends { name: "ProjectExplorer" }
|
Depends { name: "ProjectExplorer" }
|
||||||
|
Depends { name: "QtSupport" }
|
||||||
Depends { name: "TextEditor" }
|
Depends { name: "TextEditor" }
|
||||||
|
|
||||||
cpp.includePaths: base.concat([project.sharedSourcesDir + "/registryaccess"])
|
cpp.includePaths: base.concat([project.sharedSourcesDir + "/registryaccess"])
|
||||||
|
@@ -12,6 +12,7 @@ QTC_PLUGIN_DEPENDS += \
|
|||||||
coreplugin \
|
coreplugin \
|
||||||
cpptools \
|
cpptools \
|
||||||
projectexplorer \
|
projectexplorer \
|
||||||
|
qtsupport \
|
||||||
texteditor
|
texteditor
|
||||||
QTC_PLUGIN_RECOMMENDS += \
|
QTC_PLUGIN_RECOMMENDS += \
|
||||||
cppeditor
|
cppeditor
|
||||||
|
@@ -184,18 +184,6 @@ DebuggerSettings::DebuggerSettings()
|
|||||||
item->setIconVisibleInMenu(false);
|
item->setIconVisibleInMenu(false);
|
||||||
insertItem(OperateByInstruction, item);
|
insertItem(OperateByInstruction, item);
|
||||||
|
|
||||||
item = new SavedAction(this);
|
|
||||||
item->setText(tr("Native Mixed Mode"));
|
|
||||||
item->setCheckable(true);
|
|
||||||
item->setDefaultValue(true);
|
|
||||||
item->setIcon(QIcon(QLatin1String(Core::Constants::ICON_LINK)));
|
|
||||||
item->setToolTip(tr("<p>This switches the debugger to native-mixed "
|
|
||||||
"operation mode. In this mode, stepping and data display will "
|
|
||||||
"be handled by the native debugger backend (GDB, LLDB or CDB) "
|
|
||||||
"for C++, QML and JS sources."));
|
|
||||||
item->setIconVisibleInMenu(false);
|
|
||||||
insertItem(OperateNativeMixed, item);
|
|
||||||
|
|
||||||
item = new SavedAction(this);
|
item = new SavedAction(this);
|
||||||
item->setText(tr("Dereference Pointers Automatically"));
|
item->setText(tr("Dereference Pointers Automatically"));
|
||||||
item->setCheckable(true);
|
item->setCheckable(true);
|
||||||
|
@@ -99,7 +99,6 @@ enum DebuggerActionCode
|
|||||||
LogTimeStamps,
|
LogTimeStamps,
|
||||||
VerboseLog,
|
VerboseLog,
|
||||||
OperateByInstruction,
|
OperateByInstruction,
|
||||||
OperateNativeMixed,
|
|
||||||
CloseSourceBuffersOnExit,
|
CloseSourceBuffersOnExit,
|
||||||
CloseMemoryBuffersOnExit,
|
CloseMemoryBuffersOnExit,
|
||||||
SwitchModeOnExit,
|
SwitchModeOnExit,
|
||||||
|
@@ -61,7 +61,6 @@ const char NEXT[] = "Debugger.NextLine";
|
|||||||
const char REVERSE[] = "Debugger.ReverseDirection";
|
const char REVERSE[] = "Debugger.ReverseDirection";
|
||||||
const char RESET[] = "Debugger.Reset";
|
const char RESET[] = "Debugger.Reset";
|
||||||
const char OPERATE_BY_INSTRUCTION[] = "Debugger.OperateByInstruction";
|
const char OPERATE_BY_INSTRUCTION[] = "Debugger.OperateByInstruction";
|
||||||
const char OPERATE_NATIVE_MIXED[] = "Debugger.OperateNativeMixed";
|
|
||||||
const char QML_SHOW_APP_ON_TOP[] = "Debugger.QmlShowAppOnTop";
|
const char QML_SHOW_APP_ON_TOP[] = "Debugger.QmlShowAppOnTop";
|
||||||
const char QML_SELECTTOOL[] = "Debugger.QmlSelectTool";
|
const char QML_SELECTTOOL[] = "Debugger.QmlSelectTool";
|
||||||
const char QML_ZOOMTOOL[] = "Debugger.QmlZoomTool";
|
const char QML_ZOOMTOOL[] = "Debugger.QmlZoomTool";
|
||||||
|
@@ -110,8 +110,6 @@ DebuggerEngine *currentEngine();
|
|||||||
QMessageBox *showMessageBox(int icon, const QString &title,
|
QMessageBox *showMessageBox(int icon, const QString &title,
|
||||||
const QString &text, int buttons = 0);
|
const QString &text, int buttons = 0);
|
||||||
|
|
||||||
bool isNativeMixedActive();
|
|
||||||
bool isNativeMixedEnabled();
|
|
||||||
bool isReverseDebuggingEnabled();
|
bool isReverseDebuggingEnabled();
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -78,6 +78,11 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
using namespace Debugger::Internal;
|
using namespace Debugger::Internal;
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
@@ -126,7 +131,7 @@ Location::Location(const StackFrame &frame, bool marker)
|
|||||||
m_functionName = frame.function;
|
m_functionName = frame.function;
|
||||||
m_hasDebugInfo = frame.isUsable();
|
m_hasDebugInfo = frame.isUsable();
|
||||||
m_address = frame.address;
|
m_address = frame.address;
|
||||||
m_from = frame.from;
|
m_from = frame.module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -192,7 +197,7 @@ public:
|
|||||||
m_modulesHandler(engine),
|
m_modulesHandler(engine),
|
||||||
m_registerHandler(engine),
|
m_registerHandler(engine),
|
||||||
m_sourceFilesHandler(),
|
m_sourceFilesHandler(),
|
||||||
m_stackHandler(),
|
m_stackHandler(engine),
|
||||||
m_threadsHandler(),
|
m_threadsHandler(),
|
||||||
m_watchHandler(engine),
|
m_watchHandler(engine),
|
||||||
m_disassemblerAgent(engine),
|
m_disassemblerAgent(engine),
|
||||||
@@ -203,8 +208,6 @@ public:
|
|||||||
this, &DebuggerEnginePrivate::resetLocation);
|
this, &DebuggerEnginePrivate::resetLocation);
|
||||||
connect(action(IntelFlavor), &Utils::SavedAction::valueChanged,
|
connect(action(IntelFlavor), &Utils::SavedAction::valueChanged,
|
||||||
this, &DebuggerEnginePrivate::reloadDisassembly);
|
this, &DebuggerEnginePrivate::reloadDisassembly);
|
||||||
connect(action(OperateNativeMixed), &QAction::triggered,
|
|
||||||
engine, &DebuggerEngine::reloadFullStack);
|
|
||||||
|
|
||||||
Utils::globalMacroExpander()->registerFileVariables(PrefixDebugExecutable,
|
Utils::globalMacroExpander()->registerFileVariables(PrefixDebugExecutable,
|
||||||
tr("Debugged executable"),
|
tr("Debugged executable"),
|
||||||
@@ -2023,6 +2026,26 @@ void DebuggerEngine::checkState(DebuggerState state, const char *file, int line)
|
|||||||
qDebug("%s", qPrintable(msg));
|
qDebug("%s", qPrintable(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DebuggerEngine::isNativeMixedEnabled() const
|
||||||
|
{
|
||||||
|
return runParameters().nativeMixedEnabled && (runParameters().languages & QmlLanguage);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DebuggerEngine::isNativeMixedActive() const
|
||||||
|
{
|
||||||
|
return isNativeMixedEnabled(); //&& boolSetting(OperateNativeMixed);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DebuggerEngine::isNativeMixedActiveFrame() const
|
||||||
|
{
|
||||||
|
if (!isNativeMixedActive())
|
||||||
|
return false;
|
||||||
|
if (stackHandler()->frames().isEmpty())
|
||||||
|
return false;
|
||||||
|
StackFrame frame = stackHandler()->frameAt(0);
|
||||||
|
return frame.language == QmlLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
|
||||||
|
@@ -117,6 +117,8 @@ public:
|
|||||||
// Used by AttachCrashedExternal.
|
// Used by AttachCrashedExternal.
|
||||||
QString crashParameter;
|
QString crashParameter;
|
||||||
|
|
||||||
|
bool nativeMixedEnabled = false;
|
||||||
|
|
||||||
// For Debugger testing.
|
// For Debugger testing.
|
||||||
int testCase = 0;
|
int testCase = 0;
|
||||||
};
|
};
|
||||||
@@ -443,6 +445,9 @@ protected:
|
|||||||
|
|
||||||
void updateLocalsView(const GdbMi &all);
|
void updateLocalsView(const GdbMi &all);
|
||||||
void checkState(DebuggerState state, const char *file, int line);
|
void checkState(DebuggerState state, const char *file, int line);
|
||||||
|
bool isNativeMixedEnabled() const;
|
||||||
|
bool isNativeMixedActive() const;
|
||||||
|
bool isNativeMixedActiveFrame() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Wrapper engine needs access to state of its subengines.
|
// Wrapper engine needs access to state of its subengines.
|
||||||
|
@@ -474,7 +474,6 @@ bool DummyEngine::hasCapability(unsigned cap) const
|
|||||||
return cap & (WatchpointByAddressCapability
|
return cap & (WatchpointByAddressCapability
|
||||||
| BreakConditionCapability
|
| BreakConditionCapability
|
||||||
| TracePointCapability
|
| TracePointCapability
|
||||||
| OperateNativeMixed
|
|
||||||
| OperateByInstructionCapability);
|
| OperateByInstructionCapability);
|
||||||
|
|
||||||
// This is a Qml or unknown engine.
|
// This is a Qml or unknown engine.
|
||||||
@@ -2321,17 +2320,6 @@ QMessageBox *showMessageBox(int icon, const QString &title,
|
|||||||
return mb;
|
return mb;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isNativeMixedEnabled()
|
|
||||||
{
|
|
||||||
static bool enabled = qEnvironmentVariableIsSet("QTC_DEBUGGER_NATIVE_MIXED");
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isNativeMixedActive()
|
|
||||||
{
|
|
||||||
return isNativeMixedEnabled() && boolSetting(OperateNativeMixed);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isReverseDebuggingEnabled()
|
bool isReverseDebuggingEnabled()
|
||||||
{
|
{
|
||||||
static bool enabled = qEnvironmentVariableIsSet("QTC_DEBUGGER_ENABLE_REVERSE");
|
static bool enabled = qEnvironmentVariableIsSet("QTC_DEBUGGER_ENABLE_REVERSE");
|
||||||
@@ -2769,16 +2757,6 @@ void DebuggerPluginPrivate::extensionsInitialized()
|
|||||||
cmd->setAttribute(Command::CA_Hide);
|
cmd->setAttribute(Command::CA_Hide);
|
||||||
debugMenu->addAction(cmd);
|
debugMenu->addAction(cmd);
|
||||||
|
|
||||||
if (isNativeMixedEnabled()) {
|
|
||||||
SavedAction *act = action(OperateNativeMixed);
|
|
||||||
act->setValue(true);
|
|
||||||
cmd = ActionManager::registerAction(act, Constants::OPERATE_NATIVE_MIXED);
|
|
||||||
cmd->setAttribute(Command::CA_Hide);
|
|
||||||
debugMenu->addAction(cmd);
|
|
||||||
connect(cmd->action(), &QAction::triggered,
|
|
||||||
[this] { currentEngine()->updateAll(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd = ActionManager::registerAction(m_breakAction, "Debugger.ToggleBreak");
|
cmd = ActionManager::registerAction(m_breakAction, "Debugger.ToggleBreak");
|
||||||
cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("F8") : tr("F9")));
|
cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("F8") : tr("F9")));
|
||||||
debugMenu->addAction(cmd);
|
debugMenu->addAction(cmd);
|
||||||
@@ -2912,8 +2890,6 @@ void DebuggerPluginPrivate::extensionsInitialized()
|
|||||||
hbox->addWidget(toolButton(Constants::STEPOUT));
|
hbox->addWidget(toolButton(Constants::STEPOUT));
|
||||||
hbox->addWidget(toolButton(Constants::RESET));
|
hbox->addWidget(toolButton(Constants::RESET));
|
||||||
hbox->addWidget(toolButton(Constants::OPERATE_BY_INSTRUCTION));
|
hbox->addWidget(toolButton(Constants::OPERATE_BY_INSTRUCTION));
|
||||||
if (isNativeMixedEnabled())
|
|
||||||
hbox->addWidget(toolButton(Constants::OPERATE_NATIVE_MIXED));
|
|
||||||
|
|
||||||
if (isReverseDebuggingEnabled()) {
|
if (isReverseDebuggingEnabled()) {
|
||||||
m_reverseToolButton = toolButton(Constants::REVERSE);
|
m_reverseToolButton = toolButton(Constants::REVERSE);
|
||||||
|
@@ -80,7 +80,7 @@ void GdbMi::parseResultOrValue(const char *&from, const char *to)
|
|||||||
if (from == to || *from == '(')
|
if (from == to || *from == '(')
|
||||||
return;
|
return;
|
||||||
const char *ptr = from;
|
const char *ptr = from;
|
||||||
while (ptr < to && *ptr != '=') {
|
while (ptr < to && *ptr != '=' && *ptr != ':') {
|
||||||
//qDebug() << "adding" << QChar(*ptr) << "to name";
|
//qDebug() << "adding" << QChar(*ptr) << "to name";
|
||||||
++ptr;
|
++ptr;
|
||||||
}
|
}
|
||||||
@@ -770,6 +770,12 @@ QString decodeData(const QByteArray &ba, DebuggerEncoding encoding)
|
|||||||
case SpecialEmptyStructureValue: { // 39
|
case SpecialEmptyStructureValue: { // 39
|
||||||
return QLatin1String("{...}");
|
return QLatin1String("{...}");
|
||||||
}
|
}
|
||||||
|
case SpecialUndefinedValue: { // 40
|
||||||
|
return QLatin1String("Undefined");
|
||||||
|
}
|
||||||
|
case SpecialNullValue: { // 41
|
||||||
|
return QLatin1String("Null");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
qDebug() << "ENCODING ERROR: " << encoding;
|
qDebug() << "ENCODING ERROR: " << encoding;
|
||||||
return QCoreApplication::translate("Debugger", "<Encoding error>");
|
return QCoreApplication::translate("Debugger", "<Encoding error>");
|
||||||
@@ -857,5 +863,22 @@ QByteArray DebuggerCommand::argsToString() const
|
|||||||
return args.toString().toLatin1();
|
return args.toString().toLatin1();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DebuggerEncoding debuggerEncoding(const QByteArray &data)
|
||||||
|
{
|
||||||
|
if (data == "utf16")
|
||||||
|
return Hex4EncodedLittleEndianWithQuotes;
|
||||||
|
if (data == "empty")
|
||||||
|
return SpecialEmptyValue;
|
||||||
|
if (data == "minimumitemcount")
|
||||||
|
return SpecialMinimumItemCountValue;
|
||||||
|
if (data == "undefined")
|
||||||
|
return SpecialUndefinedValue;
|
||||||
|
if (data == "null")
|
||||||
|
return SpecialNullValue;
|
||||||
|
if (data == "itemcount")
|
||||||
|
return SpecialItemCountValue;
|
||||||
|
return DebuggerEncoding(data.toInt());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Debugger
|
} // namespace Debugger
|
||||||
|
@@ -252,9 +252,13 @@ enum DebuggerEncoding
|
|||||||
SpecialNotCallableValue = 36,
|
SpecialNotCallableValue = 36,
|
||||||
SpecialNullReferenceValue = 37,
|
SpecialNullReferenceValue = 37,
|
||||||
SpecialOptimizedOutValue = 38,
|
SpecialOptimizedOutValue = 38,
|
||||||
SpecialEmptyStructureValue = 39
|
SpecialEmptyStructureValue = 39,
|
||||||
|
SpecialUndefinedValue = 40,
|
||||||
|
SpecialNullValue = 41
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DebuggerEncoding debuggerEncoding(const QByteArray &data);
|
||||||
|
|
||||||
// Decode string data as returned by the dumper helpers.
|
// Decode string data as returned by the dumper helpers.
|
||||||
QString decodeData(const QByteArray &baIn, DebuggerEncoding encoding);
|
QString decodeData(const QByteArray &baIn, DebuggerEncoding encoding);
|
||||||
|
|
||||||
|
@@ -59,6 +59,8 @@
|
|||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <qmldebug/qmldebugcommandlinearguments.h>
|
#include <qmldebug/qmldebugcommandlinearguments.h>
|
||||||
|
|
||||||
|
#include <qtsupport/qtkitinformation.h>
|
||||||
|
|
||||||
#include <QTcpServer>
|
#include <QTcpServer>
|
||||||
|
|
||||||
using namespace Debugger::Internal;
|
using namespace Debugger::Internal;
|
||||||
@@ -416,6 +418,19 @@ void DebuggerRunControlCreator::enrich(const RunConfiguration *runConfig, const
|
|||||||
if (m_project && m_rp.projectSourceFiles.isEmpty())
|
if (m_project && m_rp.projectSourceFiles.isEmpty())
|
||||||
m_rp.projectSourceFiles = m_project->files(Project::ExcludeGeneratedFiles);
|
m_rp.projectSourceFiles = m_project->files(Project::ExcludeGeneratedFiles);
|
||||||
|
|
||||||
|
if (m_project && m_rp.projectSourceFiles.isEmpty())
|
||||||
|
m_rp.projectSourceFiles = m_project->files(Project::ExcludeGeneratedFiles);
|
||||||
|
|
||||||
|
if (false && m_project && m_kit) {
|
||||||
|
const QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(m_kit);
|
||||||
|
m_rp.nativeMixedEnabled = version && version->qtVersion() >= QtSupport::QtVersionNumber(5, 7, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
int nativeMixedOverride = qgetenv("QTC_DEBUGGER_NATIVE_MIXED").toInt(&ok);
|
||||||
|
if (ok)
|
||||||
|
m_rp.nativeMixedEnabled = bool(nativeMixedOverride);
|
||||||
|
|
||||||
// validate debugger if C++ debugging is enabled
|
// validate debugger if C++ debugging is enabled
|
||||||
if (m_rp.languages & CppLanguage) {
|
if (m_rp.languages & CppLanguage) {
|
||||||
const QList<Task> tasks = DebuggerKitInformation::validateDebugger(m_kit);
|
const QList<Task> tasks = DebuggerKitInformation::validateDebugger(m_kit);
|
||||||
@@ -482,9 +497,6 @@ void DebuggerRunControlCreator::enrich(const RunConfiguration *runConfig, const
|
|||||||
const QString optimizerKey = _("QML_DISABLE_OPTIMIZER");
|
const QString optimizerKey = _("QML_DISABLE_OPTIMIZER");
|
||||||
if (!m_rp.environment.hasKey(optimizerKey))
|
if (!m_rp.environment.hasKey(optimizerKey))
|
||||||
m_rp.environment.set(optimizerKey, _("1"));
|
m_rp.environment.set(optimizerKey, _("1"));
|
||||||
|
|
||||||
QtcProcess::addArg(&m_rp.processArgs, QmlDebug::qmlDebugCommandLineArguments(
|
|
||||||
QmlDebug::QmlDebuggerServices, m_rp.qmlServerPort));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -502,14 +514,23 @@ void DebuggerRunControlCreator::enrich(const RunConfiguration *runConfig, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_rp.masterEngineType == NoEngineType && m_debuggerAspect) {
|
if (m_rp.masterEngineType == NoEngineType && m_debuggerAspect) {
|
||||||
const bool useCppDebugger = m_debuggerAspect->useCppDebugger() && (m_rp.languages & CppLanguage);
|
const bool wantCppDebugger = m_debuggerAspect->useCppDebugger() && (m_rp.languages & CppLanguage);
|
||||||
const bool useQmlDebugger = m_debuggerAspect->useQmlDebugger() && (m_rp.languages & QmlLanguage);
|
const bool wantQmlDebugger = m_debuggerAspect->useQmlDebugger() && (m_rp.languages & QmlLanguage);
|
||||||
|
|
||||||
if (useQmlDebugger) {
|
if (wantQmlDebugger) {
|
||||||
if (useCppDebugger)
|
QString qmlArgs;
|
||||||
|
if (wantCppDebugger) {
|
||||||
|
if (m_rp.nativeMixedEnabled) {
|
||||||
|
qmlArgs = QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlNativeDebuggerServices);
|
||||||
|
} else {
|
||||||
m_rp.masterEngineType = QmlCppEngineType;
|
m_rp.masterEngineType = QmlCppEngineType;
|
||||||
else
|
qmlArgs = QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlDebuggerServices, m_rp.qmlServerPort);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
m_rp.masterEngineType = QmlEngineType;
|
m_rp.masterEngineType = QmlEngineType;
|
||||||
|
qmlArgs = QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlDebuggerServices, m_rp.qmlServerPort);
|
||||||
|
}
|
||||||
|
QtcProcess::addArg(&m_rp.processArgs, qmlArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -625,6 +625,18 @@ void GdbEngine::handleResponse(const QByteArray &buff)
|
|||||||
|
|
||||||
case '~': {
|
case '~': {
|
||||||
QByteArray data = GdbMi::parseCString(from, to);
|
QByteArray data = GdbMi::parseCString(from, to);
|
||||||
|
if (data.startsWith("bridgemessage={")) {
|
||||||
|
//showMessage(_(data), LogDebug);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (data.startsWith("bridgeresult={")) {
|
||||||
|
//showMessage(_(data), LogDebug);
|
||||||
|
DebuggerResponse response;
|
||||||
|
response.resultClass = ResultDone;
|
||||||
|
response.data.fromStringMultiple(data);
|
||||||
|
handleResultRecord(&response);
|
||||||
|
break;
|
||||||
|
}
|
||||||
m_pendingConsoleStreamOutput += data;
|
m_pendingConsoleStreamOutput += data;
|
||||||
|
|
||||||
// Parse pid from noise.
|
// Parse pid from noise.
|
||||||
@@ -1369,9 +1381,13 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
|
|||||||
int lineNumber = 0;
|
int lineNumber = 0;
|
||||||
QString fullName;
|
QString fullName;
|
||||||
QByteArray function;
|
QByteArray function;
|
||||||
|
QByteArray language;
|
||||||
if (frame.isValid()) {
|
if (frame.isValid()) {
|
||||||
const GdbMi lineNumberG = frame["line"];
|
const GdbMi lineNumberG = frame["line"];
|
||||||
function = frame["func"].data();
|
function = frame["function"].data(); // V4 protocol
|
||||||
|
if (function.isEmpty())
|
||||||
|
function = frame["func"].data(); // GDB's *stopped messages
|
||||||
|
language = frame["language"].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()));
|
||||||
@@ -1384,9 +1400,6 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
|
|||||||
|
|
||||||
if (rid.isValid() && frame.isValid() && !isQFatalBreakpoint(rid)) {
|
if (rid.isValid() && frame.isValid() && !isQFatalBreakpoint(rid)) {
|
||||||
// Use opportunity to update the breakpoint marker position.
|
// Use opportunity to update the breakpoint marker position.
|
||||||
//qDebug() << " PROBLEM: " << m_qmlBreakpointNumbers << rid
|
|
||||||
// << isQmlStepBreakpoint1(rid)
|
|
||||||
// << isQmlStepBreakpoint2(rid)
|
|
||||||
Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid);
|
Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid);
|
||||||
const BreakpointResponse &response = bp.response();
|
const BreakpointResponse &response = bp.response();
|
||||||
QString fileName = response.fileName;
|
QString fileName = response.fileName;
|
||||||
@@ -1403,7 +1416,9 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
|
|||||||
if (lineNumber && !boolSetting(OperateByInstruction)
|
if (lineNumber && !boolSetting(OperateByInstruction)
|
||||||
&& QFileInfo::exists(fullName)
|
&& QFileInfo::exists(fullName)
|
||||||
&& !isQFatalBreakpoint(rid)
|
&& !isQFatalBreakpoint(rid)
|
||||||
&& function != "qt_v4TriggeredBreakpointHook")
|
&& function != "qt_v4TriggeredBreakpointHook"
|
||||||
|
&& function != "qt_qmlDebugEventFromService"
|
||||||
|
&& language != "js")
|
||||||
gotoLocation(Location(fullName, lineNumber));
|
gotoLocation(Location(fullName, lineNumber));
|
||||||
|
|
||||||
if (state() == InferiorRunOk) {
|
if (state() == InferiorRunOk) {
|
||||||
@@ -1479,7 +1494,7 @@ void GdbEngine::handleStop1(const GdbMi &data)
|
|||||||
if (boolSetting(SkipKnownFrames)) {
|
if (boolSetting(SkipKnownFrames)) {
|
||||||
if (reason == "end-stepping-range" || reason == "function-finished") {
|
if (reason == "end-stepping-range" || reason == "function-finished") {
|
||||||
//showMessage(frame.toString());
|
//showMessage(frame.toString());
|
||||||
QString funcName = _(frame["func"].data());
|
QString funcName = _(frame["function"].data());
|
||||||
QString fileName = QString::fromLocal8Bit(frame["file"].data());
|
QString fileName = QString::fromLocal8Bit(frame["file"].data());
|
||||||
if (isLeavableFunction(funcName, fileName)) {
|
if (isLeavableFunction(funcName, fileName)) {
|
||||||
//showMessage(_("LEAVING ") + funcName);
|
//showMessage(_("LEAVING ") + funcName);
|
||||||
@@ -1978,9 +1993,15 @@ void GdbEngine::continueInferiorInternal()
|
|||||||
notifyInferiorRunRequested();
|
notifyInferiorRunRequested();
|
||||||
showStatusMessage(tr("Running requested..."), 5000);
|
showStatusMessage(tr("Running requested..."), 5000);
|
||||||
CHECK_STATE(InferiorRunRequested);
|
CHECK_STATE(InferiorRunRequested);
|
||||||
|
if (isNativeMixedActiveFrame()) {
|
||||||
|
DebuggerCommand cmd("executeContinue", RunRequest|PythonCommand);
|
||||||
|
cmd.callback = CB(handleExecuteContinue);
|
||||||
|
runCommand(cmd);
|
||||||
|
} else {
|
||||||
DebuggerCommand cmd("-exec-continue", RunRequest);
|
DebuggerCommand cmd("-exec-continue", RunRequest);
|
||||||
cmd.callback = CB(handleExecuteContinue);
|
cmd.callback = CB(handleExecuteContinue);
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::continueInferior()
|
void GdbEngine::continueInferior()
|
||||||
@@ -1996,10 +2017,10 @@ void GdbEngine::executeStep()
|
|||||||
setTokenBarrier();
|
setTokenBarrier();
|
||||||
notifyInferiorRunRequested();
|
notifyInferiorRunRequested();
|
||||||
showStatusMessage(tr("Step requested..."), 5000);
|
showStatusMessage(tr("Step requested..."), 5000);
|
||||||
if (isNativeMixedActive()) {
|
if (isNativeMixedActiveFrame()) {
|
||||||
DebuggerCommand cmd("prepareQmlStep", PythonCommand);
|
DebuggerCommand cmd("executeStep", RunRequest|PythonCommand);
|
||||||
|
cmd.callback = CB(handleExecuteStep);
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
continueInferiorInternal();
|
|
||||||
} else {
|
} else {
|
||||||
DebuggerCommand cmd(isReverseDebugging() ? "reverse-step" : "-exec-step", RunRequest);
|
DebuggerCommand cmd(isReverseDebugging() ? "reverse-step" : "-exec-step", RunRequest);
|
||||||
cmd.callback = CB(handleExecuteStep);
|
cmd.callback = CB(handleExecuteStep);
|
||||||
@@ -2061,12 +2082,17 @@ void GdbEngine::executeStepOut()
|
|||||||
setTokenBarrier();
|
setTokenBarrier();
|
||||||
notifyInferiorRunRequested();
|
notifyInferiorRunRequested();
|
||||||
showStatusMessage(tr("Finish function requested..."), 5000);
|
showStatusMessage(tr("Finish function requested..."), 5000);
|
||||||
|
if (isNativeMixedActiveFrame()) {
|
||||||
|
DebuggerCommand cmd("executeStepOut", RunRequest|PythonCommand);
|
||||||
|
runCommand(cmd);
|
||||||
|
} else {
|
||||||
runCommand("-exec-finish", CB(handleExecuteContinue), RunRequest);
|
runCommand("-exec-finish", CB(handleExecuteContinue), RunRequest);
|
||||||
// -exec-finish in 'main' results (correctly) in
|
// -exec-finish in 'main' results (correctly) in
|
||||||
// 40^error,msg="\"finish\" not meaningful in the outermost frame."
|
// 40^error,msg="\"finish\" not meaningful in the outermost frame."
|
||||||
// However, this message does not seem to get flushed before
|
// However, this message does not seem to get flushed before
|
||||||
// anything else happen - i.e. "never". Force some extra output.
|
// anything else happen - i.e. "never". Force some extra output.
|
||||||
runCommand("print 32");
|
runCommand("print 32");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::executeNext()
|
void GdbEngine::executeNext()
|
||||||
@@ -2075,10 +2101,9 @@ void GdbEngine::executeNext()
|
|||||||
setTokenBarrier();
|
setTokenBarrier();
|
||||||
notifyInferiorRunRequested();
|
notifyInferiorRunRequested();
|
||||||
showStatusMessage(tr("Step next requested..."), 5000);
|
showStatusMessage(tr("Step next requested..."), 5000);
|
||||||
if (isNativeMixedActive()) {
|
if (isNativeMixedActiveFrame()) {
|
||||||
DebuggerCommand cmd("prepareQmlStep", PythonCommand);
|
DebuggerCommand cmd("executeNext", RunRequest|PythonCommand);
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
continueInferiorInternal();
|
|
||||||
} else {
|
} else {
|
||||||
DebuggerCommand cmd(isReverseDebugging() ? "reverse-next" : "-exec-next", RunRequest);
|
DebuggerCommand cmd(isReverseDebugging() ? "reverse-next" : "-exec-next", RunRequest);
|
||||||
cmd.callback = CB(handleExecuteNext);
|
cmd.callback = CB(handleExecuteNext);
|
||||||
@@ -2656,10 +2681,9 @@ bool GdbEngine::acceptsBreakpoint(Breakpoint bp) const
|
|||||||
{
|
{
|
||||||
if (runParameters().startMode == AttachCore)
|
if (runParameters().startMode == AttachCore)
|
||||||
return false;
|
return false;
|
||||||
// We handle QML breakpoint unless specifically
|
if (bp.parameters().isCppBreakpoint())
|
||||||
if (isNativeMixedEnabled() && !(runParameters().languages & QmlLanguage))
|
|
||||||
return true;
|
return true;
|
||||||
return bp.parameters().isCppBreakpoint();
|
return isNativeMixedEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::insertBreakpoint(Breakpoint bp)
|
void GdbEngine::insertBreakpoint(Breakpoint bp)
|
||||||
@@ -2672,7 +2696,7 @@ void GdbEngine::insertBreakpoint(Breakpoint bp)
|
|||||||
const BreakpointParameters &data = bp.parameters();
|
const BreakpointParameters &data = bp.parameters();
|
||||||
|
|
||||||
if (!data.isCppBreakpoint()) {
|
if (!data.isCppBreakpoint()) {
|
||||||
DebuggerCommand cmd("insertQmlBreakpoint", PythonCommand);
|
DebuggerCommand cmd("insertInterpreterBreakpoint", PythonCommand);
|
||||||
bp.addToCommand(&cmd);
|
bp.addToCommand(&cmd);
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
bp.notifyBreakpointInsertOk();
|
bp.notifyBreakpointInsertOk();
|
||||||
@@ -2791,7 +2815,7 @@ void GdbEngine::removeBreakpoint(Breakpoint bp)
|
|||||||
|
|
||||||
const BreakpointParameters &data = bp.parameters();
|
const BreakpointParameters &data = bp.parameters();
|
||||||
if (!data.isCppBreakpoint()) {
|
if (!data.isCppBreakpoint()) {
|
||||||
DebuggerCommand cmd("removeQmlBreakpoint", PythonCommand);
|
DebuggerCommand cmd("removeInterpreterBreakpoint", PythonCommand);
|
||||||
bp.addToCommand(&cmd);
|
bp.addToCommand(&cmd);
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
bp.notifyBreakpointRemoveOk();
|
bp.notifyBreakpointRemoveOk();
|
||||||
@@ -3140,13 +3164,11 @@ void GdbEngine::reloadFullStack()
|
|||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::loadAdditionalQmlStack()
|
static QString msgCannotLoadQmlStack(const QString &why)
|
||||||
{
|
{
|
||||||
// Scan for QV4::ExecutionContext parameter in the parameter list of a V4 call.
|
return _("Unable to load QML stack: ") + why;
|
||||||
runCommand("-stack-list-arguments --simple-values", CB(handleQmlStackFrameArguments), NeedsStop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan the arguments of a stack list for the address of a QV4::ExecutionContext.
|
|
||||||
static quint64 findJsExecutionContextAddress(const GdbMi &stackArgsResponse, const QByteArray &qtNamespace)
|
static quint64 findJsExecutionContextAddress(const GdbMi &stackArgsResponse, const QByteArray &qtNamespace)
|
||||||
{
|
{
|
||||||
const GdbMi frameList = stackArgsResponse.childAt(0);
|
const GdbMi frameList = stackArgsResponse.childAt(0);
|
||||||
@@ -3169,13 +3191,11 @@ static quint64 findJsExecutionContextAddress(const GdbMi &stackArgsResponse, con
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString msgCannotLoadQmlStack(const QString &why)
|
void GdbEngine::loadAdditionalQmlStack()
|
||||||
{
|
|
||||||
return _("Unable to load QML stack: ") + why;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::handleQmlStackFrameArguments(const DebuggerResponse &response)
|
|
||||||
{
|
{
|
||||||
|
// Scan for QV4::ExecutionContext parameter in the parameter list of a V4 call.
|
||||||
|
DebuggerCommand cmd("-stack-list-arguments --simple-values", NeedsStop);
|
||||||
|
cmd.callback = [this](const DebuggerResponse &response) {
|
||||||
if (!response.data.isValid()) {
|
if (!response.data.isValid()) {
|
||||||
showMessage(msgCannotLoadQmlStack(_("No stack obtained.")), LogError);
|
showMessage(msgCannotLoadQmlStack(_("No stack obtained.")), LogError);
|
||||||
return;
|
return;
|
||||||
@@ -3190,8 +3210,11 @@ void GdbEngine::handleQmlStackFrameArguments(const DebuggerResponse &response)
|
|||||||
+ QByteArray::number(contextAddress, 16) + ")\"";
|
+ QByteArray::number(contextAddress, 16) + ")\"";
|
||||||
cmd.callback = CB(handleQmlStackTrace);
|
cmd.callback = CB(handleQmlStackTrace);
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
|
};
|
||||||
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scan the arguments of a stack list for the address of a QV4::ExecutionContext.
|
||||||
void GdbEngine::handleQmlStackTrace(const DebuggerResponse &response)
|
void GdbEngine::handleQmlStackTrace(const DebuggerResponse &response)
|
||||||
{
|
{
|
||||||
if (!response.data.isValid()) {
|
if (!response.data.isValid()) {
|
||||||
@@ -3216,11 +3239,8 @@ void GdbEngine::handleQmlStackTrace(const DebuggerResponse &response)
|
|||||||
}
|
}
|
||||||
QList<StackFrame> qmlFrames;
|
QList<StackFrame> qmlFrames;
|
||||||
qmlFrames.reserve(qmlFrameCount);
|
qmlFrames.reserve(qmlFrameCount);
|
||||||
for (int i = 0; i < qmlFrameCount; ++i) {
|
for (int i = 0; i < qmlFrameCount; ++i)
|
||||||
StackFrame frame = parseStackFrame(stackMi.childAt(i), i);
|
qmlFrames.append(StackFrame::parseFrame(stackMi.childAt(i), runParameters()));
|
||||||
frame.fixQmlFrame(runParameters());
|
|
||||||
qmlFrames.append(frame);
|
|
||||||
}
|
|
||||||
stackHandler()->prependFrames(qmlFrames);
|
stackHandler()->prependFrames(qmlFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3228,7 +3248,7 @@ DebuggerCommand GdbEngine::stackCommand(int depth)
|
|||||||
{
|
{
|
||||||
DebuggerCommand cmd("stackListFrames");
|
DebuggerCommand cmd("stackListFrames");
|
||||||
cmd.arg("limit", depth);
|
cmd.arg("limit", depth);
|
||||||
cmd.arg("options", isNativeMixedActive() ? "nativemixed" : "");
|
cmd.arg("nativemixed", isNativeMixedActive());
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3241,35 +3261,6 @@ void GdbEngine::reloadStack()
|
|||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
StackFrame GdbEngine::parseStackFrame(const GdbMi &frameMi, int level)
|
|
||||||
{
|
|
||||||
//qDebug() << "HANDLING FRAME:" << frameMi.toString();
|
|
||||||
StackFrame frame;
|
|
||||||
frame.level = level;
|
|
||||||
GdbMi fullName = frameMi["fullname"];
|
|
||||||
if (fullName.isValid())
|
|
||||||
frame.file = cleanupFullName(QFile::decodeName(fullName.data()));
|
|
||||||
else
|
|
||||||
frame.file = QFile::decodeName(frameMi["file"].data());
|
|
||||||
frame.function = _(frameMi["func"].data());
|
|
||||||
frame.from = _(frameMi["from"].data());
|
|
||||||
frame.line = frameMi["line"].toInt();
|
|
||||||
frame.address = frameMi["addr"].toAddress();
|
|
||||||
GdbMi usable = frameMi["usable"];
|
|
||||||
if (usable.isValid())
|
|
||||||
frame.usable = usable.data().toInt();
|
|
||||||
else
|
|
||||||
frame.usable = QFileInfo(frame.file).isReadable();
|
|
||||||
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.fixQmlFrame(runParameters());
|
|
||||||
}
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GdbEngine::handleStackListFrames(const DebuggerResponse &response, bool isFull)
|
void GdbEngine::handleStackListFrames(const DebuggerResponse &response, bool isFull)
|
||||||
{
|
{
|
||||||
if (response.resultClass != ResultDone) {
|
if (response.resultClass != ResultDone) {
|
||||||
@@ -3284,8 +3275,10 @@ void GdbEngine::handleStackListFrames(const DebuggerResponse &response, bool isF
|
|||||||
QList<StackFrame> stackFrames;
|
QList<StackFrame> stackFrames;
|
||||||
|
|
||||||
GdbMi stack = response.data["stack"]; // C++
|
GdbMi stack = response.data["stack"]; // C++
|
||||||
if (!stack.isValid() || stack.childCount() == 0) // Mixed.
|
if (!stack.isValid() || stack.childCount() == 0) { // Mixed.
|
||||||
stack.fromStringMultiple(response.consoleStreamOutput);
|
stack.fromStringMultiple(response.consoleStreamOutput);
|
||||||
|
stack = stack["frames"];
|
||||||
|
}
|
||||||
|
|
||||||
if (!stack.isValid()) {
|
if (!stack.isValid()) {
|
||||||
qDebug() << "FIXME: stack:" << stack.toString();
|
qDebug() << "FIXME: stack:" << stack.toString();
|
||||||
@@ -3296,7 +3289,7 @@ void GdbEngine::handleStackListFrames(const DebuggerResponse &response, bool isF
|
|||||||
|
|
||||||
int n = stack.childCount();
|
int n = stack.childCount();
|
||||||
for (int i = 0; i != n; ++i) {
|
for (int i = 0; i != n; ++i) {
|
||||||
stackFrames.append(parseStackFrame(stack.childAt(i), i));
|
stackFrames.append(StackFrame::parseFrame(stack.childAt(i), runParameters()));
|
||||||
const StackFrame &frame = stackFrames.back();
|
const StackFrame &frame = stackFrames.back();
|
||||||
|
|
||||||
// Initialize top frame to the first valid frame.
|
// Initialize top frame to the first valid frame.
|
||||||
@@ -3407,7 +3400,7 @@ void GdbEngine::handleThreadNames(const DebuggerResponse &response)
|
|||||||
ThreadData thread;
|
ThreadData thread;
|
||||||
thread.id = ThreadId(name["id"].toInt());
|
thread.id = ThreadId(name["id"].toInt());
|
||||||
thread.name = decodeData(name["value"].data(),
|
thread.name = decodeData(name["value"].data(),
|
||||||
DebuggerEncoding(name["valueencoded"].toInt()));
|
debuggerEncoding(name["valueencoded"].data()));
|
||||||
handler->updateThread(thread);
|
handler->updateThread(thread);
|
||||||
}
|
}
|
||||||
updateViews();
|
updateViews();
|
||||||
@@ -4622,7 +4615,7 @@ void GdbEngine::doUpdateLocals(const UpdateParameters ¶ms)
|
|||||||
|
|
||||||
watchHandler()->notifyUpdateStarted(params.partialVariables());
|
watchHandler()->notifyUpdateStarted(params.partialVariables());
|
||||||
|
|
||||||
DebuggerCommand cmd("showData", Discardable | InUpdateLocals | PythonCommand);
|
DebuggerCommand cmd("fetchVariables", Discardable|InUpdateLocals|PythonCommand);
|
||||||
watchHandler()->appendFormatRequests(&cmd);
|
watchHandler()->appendFormatRequests(&cmd);
|
||||||
watchHandler()->appendWatchersAndTooltipRequests(&cmd);
|
watchHandler()->appendWatchersAndTooltipRequests(&cmd);
|
||||||
|
|
||||||
@@ -4637,23 +4630,20 @@ void GdbEngine::doUpdateLocals(const UpdateParameters ¶ms)
|
|||||||
cmd.arg("dyntype", boolSetting(UseDynamicType));
|
cmd.arg("dyntype", boolSetting(UseDynamicType));
|
||||||
cmd.arg("nativemixed", isNativeMixedActive());
|
cmd.arg("nativemixed", isNativeMixedActive());
|
||||||
|
|
||||||
if (isNativeMixedActive()) {
|
|
||||||
StackFrame frame = stackHandler()->currentFrame();
|
StackFrame frame = stackHandler()->currentFrame();
|
||||||
if (frame.language == QmlLanguage)
|
cmd.arg("context", frame.context);
|
||||||
cmd.arg("qmlcontext", "0x" + QByteArray::number(frame.address, 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.arg("resultvarname", m_resultVarName);
|
cmd.arg("resultvarname", m_resultVarName);
|
||||||
cmd.arg("partialVariable", params.partialVariable);
|
cmd.arg("partialVariable", params.partialVariable);
|
||||||
cmd.arg("sortStructMembers", boolSetting(SortStructMembers));
|
cmd.arg("sortStructMembers", boolSetting(SortStructMembers));
|
||||||
cmd.callback = CB(handleStackFrame);
|
cmd.callback = CB(handleFetchVariables);
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
|
|
||||||
cmd.arg("passExceptions", true);
|
cmd.arg("passExceptions", true);
|
||||||
m_lastDebuggableCommand = cmd;
|
m_lastDebuggableCommand = cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::handleStackFrame(const DebuggerResponse &response)
|
void GdbEngine::handleFetchVariables(const DebuggerResponse &response)
|
||||||
{
|
{
|
||||||
m_inUpdateLocals = false;
|
m_inUpdateLocals = false;
|
||||||
|
|
||||||
|
@@ -216,7 +216,6 @@ protected:
|
|||||||
void handleStop1(const GdbMi &data);
|
void handleStop1(const GdbMi &data);
|
||||||
void handleStop2(const GdbMi &data);
|
void handleStop2(const GdbMi &data);
|
||||||
Q_SLOT void handleStop2();
|
Q_SLOT void handleStop2();
|
||||||
StackFrame parseStackFrame(const GdbMi &mi, int level);
|
|
||||||
void resetCommandQueue();
|
void resetCommandQueue();
|
||||||
|
|
||||||
bool isSynchronous() const override { return true; }
|
bool isSynchronous() const override { return true; }
|
||||||
@@ -367,7 +366,6 @@ protected:
|
|||||||
Q_SLOT void reloadStack();
|
Q_SLOT void reloadStack();
|
||||||
Q_SLOT virtual void reloadFullStack() override;
|
Q_SLOT virtual void reloadFullStack() override;
|
||||||
virtual void loadAdditionalQmlStack() override;
|
virtual void loadAdditionalQmlStack() override;
|
||||||
void handleQmlStackFrameArguments(const DebuggerResponse &response);
|
|
||||||
void handleQmlStackTrace(const DebuggerResponse &response);
|
void handleQmlStackTrace(const DebuggerResponse &response);
|
||||||
int currentFrame() const;
|
int currentFrame() const;
|
||||||
|
|
||||||
@@ -399,7 +397,7 @@ protected:
|
|||||||
Q_SLOT void createFullBacktrace();
|
Q_SLOT void createFullBacktrace();
|
||||||
|
|
||||||
void doUpdateLocals(const UpdateParameters ¶meters) override;
|
void doUpdateLocals(const UpdateParameters ¶meters) override;
|
||||||
void handleStackFrame(const DebuggerResponse &response);
|
void handleFetchVariables(const DebuggerResponse &response);
|
||||||
|
|
||||||
void setLocals(const QList<GdbMi> &locals);
|
void setLocals(const QList<GdbMi> &locals);
|
||||||
|
|
||||||
|
@@ -537,7 +537,7 @@ void LldbEngine::selectThread(ThreadId threadId)
|
|||||||
cmd.arg("id", threadId.raw());
|
cmd.arg("id", threadId.raw());
|
||||||
cmd.callback = [this](const DebuggerResponse &) {
|
cmd.callback = [this](const DebuggerResponse &) {
|
||||||
DebuggerCommand cmd("fetchStack");
|
DebuggerCommand cmd("fetchStack");
|
||||||
cmd.arg("nativeMixed", isNativeMixedActive());
|
cmd.arg("nativemixed", isNativeMixedActive());
|
||||||
cmd.arg("stacklimit", action(MaximalStackDepth)->value().toInt());
|
cmd.arg("stacklimit", action(MaximalStackDepth)->value().toInt());
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
updateLocals();
|
updateLocals();
|
||||||
@@ -563,10 +563,9 @@ bool LldbEngine::acceptsBreakpoint(Breakpoint bp) const
|
|||||||
{
|
{
|
||||||
if (runParameters().startMode == AttachCore)
|
if (runParameters().startMode == AttachCore)
|
||||||
return false;
|
return false;
|
||||||
// We handle QML breakpoint unless specifically disabled.
|
if (bp.parameters().isCppBreakpoint())
|
||||||
if (isNativeMixedEnabled() && !(runParameters().languages & QmlLanguage))
|
|
||||||
return true;
|
return true;
|
||||||
return bp.parameters().isCppBreakpoint();
|
return isNativeMixedEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LldbEngine::insertBreakpoint(Breakpoint bp)
|
void LldbEngine::insertBreakpoint(Breakpoint bp)
|
||||||
@@ -753,37 +752,12 @@ void LldbEngine::reloadFullStack()
|
|||||||
void LldbEngine::fetchStack(int limit)
|
void LldbEngine::fetchStack(int limit)
|
||||||
{
|
{
|
||||||
DebuggerCommand cmd("fetchStack");
|
DebuggerCommand cmd("fetchStack");
|
||||||
cmd.arg("nativeMixed", isNativeMixedActive());
|
cmd.arg("nativemixed", isNativeMixedActive());
|
||||||
cmd.arg("stacklimit", limit);
|
cmd.arg("stacklimit", limit);
|
||||||
|
cmd.arg("context", stackHandler()->currentFrame().context);
|
||||||
cmd.callback = [this](const DebuggerResponse &response) {
|
cmd.callback = [this](const DebuggerResponse &response) {
|
||||||
const GdbMi &stack = response.data["stack"];
|
const GdbMi &stack = response.data["stack"];
|
||||||
StackHandler *handler = stackHandler();
|
stackHandler()->setAllFrames(stack["frames"], stack["hasmore"].toInt());
|
||||||
StackFrames frames;
|
|
||||||
foreach (const GdbMi &item, stack["frames"].children()) {
|
|
||||||
StackFrame frame;
|
|
||||||
frame.level = item["level"].toInt();
|
|
||||||
frame.file = item["file"].toUtf8();
|
|
||||||
frame.function = item["func"].toUtf8();
|
|
||||||
frame.from = item["func"].toUtf8();
|
|
||||||
frame.line = item["line"].toInt();
|
|
||||||
frame.address = item["addr"].toAddress();
|
|
||||||
GdbMi usable = item["usable"];
|
|
||||||
if (usable.isValid())
|
|
||||||
frame.usable = usable.data().toInt();
|
|
||||||
else
|
|
||||||
frame.usable = QFileInfo(frame.file).isReadable();
|
|
||||||
if (item["language"].data() == "js"
|
|
||||||
|| frame.file.endsWith(QLatin1String(".js"))
|
|
||||||
|| frame.file.endsWith(QLatin1String(".qml"))) {
|
|
||||||
frame.language = QmlLanguage;
|
|
||||||
frame.fixQmlFrame(runParameters());
|
|
||||||
}
|
|
||||||
frames.append(frame);
|
|
||||||
}
|
|
||||||
bool canExpand = stack["hasmore"].toInt();
|
|
||||||
action(ExpandStack)->setEnabled(canExpand);
|
|
||||||
handler->setFrames(frames, canExpand);
|
|
||||||
|
|
||||||
updateLocals();
|
updateLocals();
|
||||||
};
|
};
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
@@ -816,7 +790,7 @@ void LldbEngine::doUpdateLocals(const UpdateParameters ¶ms)
|
|||||||
watchHandler()->notifyUpdateStarted(params.partialVariables());
|
watchHandler()->notifyUpdateStarted(params.partialVariables());
|
||||||
|
|
||||||
DebuggerCommand cmd("fetchLocals");
|
DebuggerCommand cmd("fetchLocals");
|
||||||
cmd.arg("nativeMixed", isNativeMixedActive());
|
cmd.arg("nativemixed", isNativeMixedActive());
|
||||||
watchHandler()->appendFormatRequests(&cmd);
|
watchHandler()->appendFormatRequests(&cmd);
|
||||||
watchHandler()->appendWatchersAndTooltipRequests(&cmd);
|
watchHandler()->appendWatchersAndTooltipRequests(&cmd);
|
||||||
|
|
||||||
|
@@ -532,12 +532,12 @@ void PdbEngine::refreshStack(const GdbMi &stack)
|
|||||||
StackFrames frames;
|
StackFrames frames;
|
||||||
foreach (const GdbMi &item, stack["frames"].children()) {
|
foreach (const GdbMi &item, stack["frames"].children()) {
|
||||||
StackFrame frame;
|
StackFrame frame;
|
||||||
frame.level = item["level"].toInt();
|
frame.level = item["level"].data();
|
||||||
frame.file = item["file"].toUtf8();
|
frame.file = item["file"].toUtf8();
|
||||||
frame.function = item["func"].toUtf8();
|
frame.function = item["function"].toUtf8();
|
||||||
frame.from = item["func"].toUtf8();
|
frame.module = item["function"].toUtf8();
|
||||||
frame.line = item["line"].toInt();
|
frame.line = item["line"].toInt();
|
||||||
frame.address = item["addr"].toAddress();
|
frame.address = item["address"].toAddress();
|
||||||
GdbMi usable = item["usable"];
|
GdbMi usable = item["usable"];
|
||||||
if (usable.isValid())
|
if (usable.isValid())
|
||||||
frame.usable = usable.data().toInt();
|
frame.usable = usable.data().toInt();
|
||||||
|
@@ -2062,10 +2062,9 @@ void QmlEnginePrivate::handleBacktrace(const QVariantMap &response)
|
|||||||
stackIndexLookup.clear();
|
stackIndexLookup.clear();
|
||||||
foreach (const QVariant &frame, frames) {
|
foreach (const QVariant &frame, frames) {
|
||||||
StackFrame stackFrame = extractStackFrame(frame);
|
StackFrame stackFrame = extractStackFrame(frame);
|
||||||
if (stackFrame.level < 0)
|
if (stackFrame.level.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
stackIndexLookup.insert(i, stackFrame.level);
|
stackIndexLookup.insert(i, stackFrame.level.toInt());
|
||||||
stackFrame.level = i;
|
|
||||||
stackFrames << stackFrame;
|
stackFrames << stackFrame;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -2111,10 +2110,10 @@ StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal)
|
|||||||
const QVariantMap body = bodyVal.toMap();
|
const QVariantMap body = bodyVal.toMap();
|
||||||
|
|
||||||
StackFrame stackFrame;
|
StackFrame stackFrame;
|
||||||
stackFrame.level = body.value(_("index")).toInt();
|
stackFrame.level = body.value(_("index")).toByteArray();
|
||||||
//Do not insert the frame corresponding to the internal function
|
//Do not insert the frame corresponding to the internal function
|
||||||
if (body.value(QLatin1String("sourceLineText")) == QLatin1String(INTERNAL_FUNCTION)) {
|
if (body.value(QLatin1String("sourceLineText")) == QLatin1String(INTERNAL_FUNCTION)) {
|
||||||
stackFrame.level = -1;
|
stackFrame.level.clear();
|
||||||
return stackFrame;
|
return stackFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2129,7 +2128,7 @@ StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal)
|
|||||||
stackFrame.usable = QFileInfo(stackFrame.file).isReadable();
|
stackFrame.usable = QFileInfo(stackFrame.file).isReadable();
|
||||||
|
|
||||||
objectData = extractData(body.value(_("receiver")));
|
objectData = extractData(body.value(_("receiver")));
|
||||||
stackFrame.to = objectData.value.toString();
|
stackFrame.receiver = objectData.value.toString();
|
||||||
|
|
||||||
stackFrame.line = body.value(_("line")).toInt() + 1;
|
stackFrame.line = body.value(_("line")).toInt() + 1;
|
||||||
|
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
#include "stackframe.h"
|
#include "stackframe.h"
|
||||||
|
|
||||||
#include "debuggerengine.h"
|
#include "debuggerengine.h"
|
||||||
|
#include "debuggerprotocol.h"
|
||||||
#include "watchutils.h"
|
#include "watchutils.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@@ -49,16 +50,16 @@ namespace Internal {
|
|||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
StackFrame::StackFrame()
|
StackFrame::StackFrame()
|
||||||
: language(CppLanguage), level(-1), line(-1), address(0), usable(false)
|
: language(CppLanguage), line(-1), address(0), usable(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void StackFrame::clear()
|
void StackFrame::clear()
|
||||||
{
|
{
|
||||||
line = level = -1;
|
line = -1;
|
||||||
function.clear();
|
function.clear();
|
||||||
file.clear();
|
file.clear();
|
||||||
from.clear();
|
module.clear();
|
||||||
to.clear();
|
receiver.clear();
|
||||||
address = 0;
|
address = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,11 +80,44 @@ QString StackFrame::toString() const
|
|||||||
<< tr("Function:") << ' ' << function << ' '
|
<< tr("Function:") << ' ' << function << ' '
|
||||||
<< tr("File:") << ' ' << file << ' '
|
<< tr("File:") << ' ' << file << ' '
|
||||||
<< tr("Line:") << ' ' << line << ' '
|
<< tr("Line:") << ' ' << line << ' '
|
||||||
<< tr("From:") << ' ' << from << ' '
|
<< tr("From:") << ' ' << module << ' '
|
||||||
<< tr("To:") << ' ' << to;
|
<< tr("To:") << ' ' << receiver;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<StackFrame> StackFrame::parseFrames(const GdbMi &data, const DebuggerRunParameters &rp)
|
||||||
|
{
|
||||||
|
StackFrames frames;
|
||||||
|
frames.reserve(data.children().size());
|
||||||
|
foreach (const GdbMi &item, data.children())
|
||||||
|
frames.append(parseFrame(item, rp));
|
||||||
|
return frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
StackFrame StackFrame::parseFrame(const GdbMi &frameMi, const DebuggerRunParameters &rp)
|
||||||
|
{
|
||||||
|
StackFrame frame;
|
||||||
|
frame.level = frameMi["level"].data();
|
||||||
|
frame.function = frameMi["function"].toUtf8();
|
||||||
|
frame.module = frameMi["module"].toUtf8();
|
||||||
|
frame.file = QFile::decodeName(frameMi["file"].data());
|
||||||
|
frame.line = frameMi["line"].toInt();
|
||||||
|
frame.address = frameMi["address"].toAddress();
|
||||||
|
frame.context = frameMi["context"].data();
|
||||||
|
if (frameMi["language"].data() == "js"
|
||||||
|
|| frame.file.endsWith(QLatin1String(".js"))
|
||||||
|
|| frame.file.endsWith(QLatin1String(".qml"))) {
|
||||||
|
frame.language = QmlLanguage;
|
||||||
|
frame.fixQrcFrame(rp);
|
||||||
|
}
|
||||||
|
GdbMi usable = frameMi["usable"];
|
||||||
|
if (usable.isValid())
|
||||||
|
frame.usable = usable.data().toInt();
|
||||||
|
else
|
||||||
|
frame.usable = QFileInfo(frame.file).isReadable();
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
QString StackFrame::toToolTip() const
|
QString StackFrame::toToolTip() const
|
||||||
{
|
{
|
||||||
const QString filePath = QDir::toNativeSeparators(file);
|
const QString filePath = QDir::toNativeSeparators(file);
|
||||||
@@ -101,10 +135,10 @@ QString StackFrame::toToolTip() const
|
|||||||
str << "<tr><td>" << tr("File:") << "</td><td>" << filePath << "</td></tr>";
|
str << "<tr><td>" << tr("File:") << "</td><td>" << filePath << "</td></tr>";
|
||||||
if (line != -1)
|
if (line != -1)
|
||||||
str << "<tr><td>" << tr("Line:") << "</td><td>" << line << "</td></tr>";
|
str << "<tr><td>" << tr("Line:") << "</td><td>" << line << "</td></tr>";
|
||||||
if (!from.isEmpty())
|
if (!module.isEmpty())
|
||||||
str << "<tr><td>" << tr("From:") << "</td><td>" << from << "</td></tr>";
|
str << "<tr><td>" << tr("Module:") << "</td><td>" << module << "</td></tr>";
|
||||||
if (!to.isEmpty())
|
if (!receiver.isEmpty())
|
||||||
str << "<tr><td>" << tr("To:") << "</td><td>" << to << "</td></tr>";
|
str << "<tr><td>" << tr("Receiver:") << "</td><td>" << receiver << "</td></tr>";
|
||||||
str << "</table>";
|
str << "</table>";
|
||||||
|
|
||||||
str <<"<br> <br><i>" << tr("Note:") << " </i> ";
|
str <<"<br> <br><i>" << tr("Note:") << " </i> ";
|
||||||
@@ -133,8 +167,8 @@ QString StackFrame::toToolTip() const
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to resolve files of a QML stack (resource files).
|
// Try to resolve files coming from resource files.
|
||||||
void StackFrame::fixQmlFrame(const DebuggerRunParameters &rp)
|
void StackFrame::fixQrcFrame(const DebuggerRunParameters &rp)
|
||||||
{
|
{
|
||||||
if (language != QmlLanguage)
|
if (language != QmlLanguage)
|
||||||
return;
|
return;
|
||||||
@@ -146,7 +180,8 @@ void StackFrame::fixQmlFrame(const DebuggerRunParameters &rp)
|
|||||||
if (!file.startsWith(QLatin1String("qrc:/")))
|
if (!file.startsWith(QLatin1String("qrc:/")))
|
||||||
return;
|
return;
|
||||||
const QString relativeFile = file.right(file.size() - 5);
|
const QString relativeFile = file.right(file.size() - 5);
|
||||||
if (!rp.projectSourceDirectory.isEmpty()) {
|
if (rp.projectSourceDirectory.isEmpty())
|
||||||
|
return;
|
||||||
const QFileInfo pFi(rp.projectSourceDirectory + QLatin1Char('/') + relativeFile);
|
const QFileInfo pFi(rp.projectSourceDirectory + QLatin1Char('/') + relativeFile);
|
||||||
if (pFi.isFile()) {
|
if (pFi.isFile()) {
|
||||||
file = pFi.absoluteFilePath();
|
file = pFi.absoluteFilePath();
|
||||||
@@ -159,7 +194,6 @@ void StackFrame::fixQmlFrame(const DebuggerRunParameters &rp)
|
|||||||
usable = true;
|
usable = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QDebug operator<<(QDebug d, const StackFrame &f)
|
QDebug operator<<(QDebug d, const StackFrame &f)
|
||||||
@@ -171,10 +205,10 @@ QDebug operator<<(QDebug d, const StackFrame &f)
|
|||||||
str << ' ' << f.function;
|
str << ' ' << f.function;
|
||||||
if (!f.file.isEmpty())
|
if (!f.file.isEmpty())
|
||||||
str << ' ' << f.file << ':' << f.line;
|
str << ' ' << f.file << ':' << f.line;
|
||||||
if (!f.from.isEmpty())
|
if (!f.module.isEmpty())
|
||||||
str << " from=" << f.from;
|
str << " from=" << f.module;
|
||||||
if (!f.to.isEmpty())
|
if (!f.receiver.isEmpty())
|
||||||
str << " to=" << f.to;
|
str << " to=" << f.receiver;
|
||||||
d.nospace() << res;
|
d.nospace() << res;
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
@@ -44,6 +44,7 @@ namespace Debugger {
|
|||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class DebuggerRunParameters;
|
class DebuggerRunParameters;
|
||||||
|
class GdbMi;
|
||||||
|
|
||||||
class StackFrame
|
class StackFrame
|
||||||
{
|
{
|
||||||
@@ -53,24 +54,25 @@ public:
|
|||||||
bool isUsable() const;
|
bool isUsable() const;
|
||||||
QString toToolTip() const;
|
QString toToolTip() const;
|
||||||
QString toString() const;
|
QString toString() const;
|
||||||
void fixQmlFrame(const DebuggerRunParameters &rp);
|
static StackFrame parseFrame(const GdbMi &data, const DebuggerRunParameters &rp);
|
||||||
|
static QList<StackFrame> parseFrames(const GdbMi &data, const DebuggerRunParameters &rp);
|
||||||
|
void fixQrcFrame(const DebuggerRunParameters &rp);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DebuggerLanguage language;
|
DebuggerLanguage language;
|
||||||
qint32 level;
|
QByteArray level;
|
||||||
QString function;
|
QString function;
|
||||||
QString file; // We try to put an absolute file name in there.
|
QString file; // We try to put an absolute file name in there.
|
||||||
QString from; // Sometimes something like "/usr/lib/libstdc++.so.6"
|
QString module; // Sometimes something like "/usr/lib/libstdc++.so.6"
|
||||||
QString to; // Used in ScriptEngine only.
|
QString receiver; // Used in ScriptEngine only.
|
||||||
qint32 line;
|
qint32 line;
|
||||||
quint64 address;
|
quint64 address;
|
||||||
bool usable;
|
bool usable;
|
||||||
|
QByteArray context; // Opaque value produced and consumed by the native backends.
|
||||||
|
|
||||||
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::StackHandler)
|
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::StackHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
QDebug operator<<(QDebug d, const StackFrame &frame);
|
|
||||||
|
|
||||||
typedef QList<StackFrame> StackFrames;
|
typedef QList<StackFrame> StackFrames;
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include "debuggeractions.h"
|
#include "debuggeractions.h"
|
||||||
#include "debuggercore.h"
|
#include "debuggercore.h"
|
||||||
|
#include "debuggerengine.h"
|
||||||
#include "simplifytype.h"
|
#include "simplifytype.h"
|
||||||
|
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
@@ -55,8 +56,9 @@ namespace Internal {
|
|||||||
QTreeView.
|
QTreeView.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
StackHandler::StackHandler()
|
StackHandler::StackHandler(DebuggerEngine *engine)
|
||||||
: m_positionIcon(QIcon(QLatin1String(":/debugger/images/location_16.png"))),
|
: m_engine(engine),
|
||||||
|
m_positionIcon(QIcon(QLatin1String(":/debugger/images/location_16.png"))),
|
||||||
m_emptyIcon(QIcon(QLatin1String(":/debugger/images/debugger_empty_14.png")))
|
m_emptyIcon(QIcon(QLatin1String(":/debugger/images/debugger_empty_14.png")))
|
||||||
{
|
{
|
||||||
setObjectName(QLatin1String("StackModel"));
|
setObjectName(QLatin1String("StackModel"));
|
||||||
@@ -103,11 +105,11 @@ QVariant StackHandler::data(const QModelIndex &index, int role) const
|
|||||||
if (role == Qt::DisplayRole) {
|
if (role == Qt::DisplayRole) {
|
||||||
switch (index.column()) {
|
switch (index.column()) {
|
||||||
case StackLevelColumn:
|
case StackLevelColumn:
|
||||||
return QString::number(frame.level);
|
return QString::number(index.row() + 1);
|
||||||
case StackFunctionNameColumn:
|
case StackFunctionNameColumn:
|
||||||
return simplifyType(frame.function);
|
return simplifyType(frame.function);
|
||||||
case StackFileNameColumn:
|
case StackFileNameColumn:
|
||||||
return frame.file.isEmpty() ? frame.from : Utils::FileName::fromString(frame.file).fileName();
|
return frame.file.isEmpty() ? frame.module : Utils::FileName::fromString(frame.file).fileName();
|
||||||
case StackLineNumberColumn:
|
case StackLineNumberColumn:
|
||||||
return frame.line > 0 ? QVariant(frame.line) : QVariant();
|
return frame.line > 0 ? QVariant(frame.line) : QVariant();
|
||||||
case StackAddressColumn:
|
case StackAddressColumn:
|
||||||
@@ -166,6 +168,12 @@ StackFrame StackHandler::currentFrame() const
|
|||||||
return m_stackFrames.at(m_currentIndex);
|
return m_stackFrames.at(m_currentIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StackHandler::setAllFrames(const GdbMi &frames, bool canExpand)
|
||||||
|
{
|
||||||
|
action(ExpandStack)->setEnabled(canExpand);
|
||||||
|
setFrames(StackFrame::parseFrames(frames, m_engine->runParameters()), canExpand);
|
||||||
|
}
|
||||||
|
|
||||||
void StackHandler::setCurrentIndex(int level)
|
void StackHandler::setCurrentIndex(int level)
|
||||||
{
|
{
|
||||||
if (level == -1 || level == m_currentIndex)
|
if (level == -1 || level == m_currentIndex)
|
||||||
|
@@ -38,6 +38,8 @@
|
|||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class DebuggerEngine;
|
||||||
|
|
||||||
enum StackColumns
|
enum StackColumns
|
||||||
{
|
{
|
||||||
StackLevelColumn,
|
StackLevelColumn,
|
||||||
@@ -48,18 +50,12 @@ enum StackColumns
|
|||||||
StackColumnCount
|
StackColumnCount
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// StackModel
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class StackHandler : public QAbstractTableModel
|
class StackHandler : public QAbstractTableModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StackHandler();
|
explicit StackHandler(DebuggerEngine *engine);
|
||||||
~StackHandler();
|
~StackHandler();
|
||||||
|
|
||||||
void setFrames(const StackFrames &frames, bool canExpand = false);
|
void setFrames(const StackFrames &frames, bool canExpand = false);
|
||||||
@@ -72,6 +68,7 @@ public:
|
|||||||
const StackFrame &frameAt(int index) const { return m_stackFrames.at(index); }
|
const StackFrame &frameAt(int index) const { return m_stackFrames.at(index); }
|
||||||
int stackSize() const { return m_stackFrames.size(); }
|
int stackSize() const { return m_stackFrames.size(); }
|
||||||
quint64 topAddress() const { return m_stackFrames.at(0).address; }
|
quint64 topAddress() const { return m_stackFrames.at(0).address; }
|
||||||
|
void setAllFrames(const GdbMi &frames, bool canExpand);
|
||||||
|
|
||||||
// Called from StackHandler after a new stack list has been received
|
// Called from StackHandler after a new stack list has been received
|
||||||
void removeAll();
|
void removeAll();
|
||||||
@@ -93,6 +90,7 @@ private:
|
|||||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||||
Q_SLOT void resetModel() { beginResetModel(); endResetModel(); }
|
Q_SLOT void resetModel() { beginResetModel(); endResetModel(); }
|
||||||
|
|
||||||
|
DebuggerEngine *m_engine;
|
||||||
StackFrames m_stackFrames;
|
StackFrames m_stackFrames;
|
||||||
int m_currentIndex;
|
int m_currentIndex;
|
||||||
const QVariant m_positionIcon;
|
const QVariant m_positionIcon;
|
||||||
|
@@ -108,7 +108,7 @@ static inline StackFrame inputFunctionForDisassembly()
|
|||||||
return frame;
|
return frame;
|
||||||
const int bangPos = function.indexOf(QLatin1Char('!'));
|
const int bangPos = function.indexOf(QLatin1Char('!'));
|
||||||
if (bangPos != -1) {
|
if (bangPos != -1) {
|
||||||
frame.from = function.left(bangPos);
|
frame.module = function.left(bangPos);
|
||||||
frame.function = function.mid(bangPos + 1);
|
frame.function = function.mid(bangPos + 1);
|
||||||
} else {
|
} else {
|
||||||
frame.function = function;
|
frame.function = function;
|
||||||
|
@@ -392,8 +392,8 @@ void WatchData::updateValue(const GdbMi &item)
|
|||||||
{
|
{
|
||||||
GdbMi value = item["value"];
|
GdbMi value = item["value"];
|
||||||
if (value.isValid()) {
|
if (value.isValid()) {
|
||||||
int encoding = item["valueencoded"].toInt();
|
DebuggerEncoding encoding = debuggerEncoding(item["valueencoded"].data());
|
||||||
setValue(decodeData(value.data(), DebuggerEncoding(encoding)));
|
setValue(decodeData(value.data(), encoding));
|
||||||
} else {
|
} else {
|
||||||
setValueNeeded();
|
setValueNeeded();
|
||||||
}
|
}
|
||||||
|
@@ -1258,7 +1258,8 @@ void tst_Dumpers::dumper()
|
|||||||
|
|
||||||
if (m_debuggerEngine == GdbEngine) {
|
if (m_debuggerEngine == GdbEngine) {
|
||||||
const QFileInfo gdbBinaryFile(QString::fromLatin1(exe));
|
const QFileInfo gdbBinaryFile(QString::fromLatin1(exe));
|
||||||
const QByteArray uninstalledData = gdbBinaryFile.absolutePath().toLocal8Bit() + "/data-directory/python";
|
const QByteArray uninstalledData = gdbBinaryFile.absolutePath().toLocal8Bit()
|
||||||
|
+ "/data-directory/python";
|
||||||
|
|
||||||
args << QLatin1String("-i")
|
args << QLatin1String("-i")
|
||||||
<< QLatin1String("mi")
|
<< QLatin1String("mi")
|
||||||
@@ -1280,8 +1281,9 @@ void tst_Dumpers::dumper()
|
|||||||
"python from gdbbridge import *\n"
|
"python from gdbbridge import *\n"
|
||||||
"python theDumper.setupDumpers()\n"
|
"python theDumper.setupDumpers()\n"
|
||||||
"run " + nograb + "\n"
|
"run " + nograb + "\n"
|
||||||
"python theDumper.showData({'fancy':1,'forcens':1,'autoderef':1,"
|
"python theDumper.fetchVariables({'fancy':1,'forcens':1,"
|
||||||
"'dyntype':1,'passExceptions':1,'expanded':[" + expandedq + "]})\n";
|
"'autoderef':1,'dyntype':1,'passExceptions':1,"
|
||||||
|
"'expanded':[" + expandedq + "]})\n";
|
||||||
|
|
||||||
cmds += "quit\n";
|
cmds += "quit\n";
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user