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