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:
hjk
2015-10-08 16:19:57 +02:00
committed by Christian Stenger
parent ea39476ef2
commit 525c33f999
30 changed files with 568 additions and 558 deletions

View File

@@ -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)

View File

@@ -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()

View File

@@ -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:

View File

@@ -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));

View File

@@ -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());
} }

View File

@@ -2539,28 +2539,29 @@ 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: case BreakpointAtFork:
case BreakpointAtFork: case WatchpointAtExpression:
case WatchpointAtExpression: case BreakpointAtSysCall:
case BreakpointAtSysCall: case BreakpointOnQmlSignalEmit:
case BreakpointOnQmlSignalEmit: case BreakpointAtJavaScriptThrow:
case BreakpointAtJavaScriptThrow: return false;
return false; case WatchpointAtAddress:
case WatchpointAtAddress: case BreakpointByFileAndLine:
case BreakpointByFileAndLine: case BreakpointByFunction:
case BreakpointByFunction: case BreakpointByAddress:
case BreakpointByAddress: case BreakpointAtThrow:
case BreakpointAtThrow: case BreakpointAtCatch:
case BreakpointAtCatch: case BreakpointAtMain:
case BreakpointAtMain: case BreakpointAtExec:
case BreakpointAtExec: 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())

View File

@@ -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"])

View File

@@ -12,6 +12,7 @@ QTC_PLUGIN_DEPENDS += \
coreplugin \ coreplugin \
cpptools \ cpptools \
projectexplorer \ projectexplorer \
qtsupport \
texteditor texteditor
QTC_PLUGIN_RECOMMENDS += \ QTC_PLUGIN_RECOMMENDS += \
cppeditor cppeditor

View File

@@ -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);

View File

@@ -99,7 +99,6 @@ enum DebuggerActionCode
LogTimeStamps, LogTimeStamps,
VerboseLog, VerboseLog,
OperateByInstruction, OperateByInstruction,
OperateNativeMixed,
CloseSourceBuffersOnExit, CloseSourceBuffersOnExit,
CloseMemoryBuffersOnExit, CloseMemoryBuffersOnExit,
SwitchModeOnExit, SwitchModeOnExit,

View File

@@ -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";

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
m_rp.masterEngineType = QmlCppEngineType; if (wantCppDebugger) {
else if (m_rp.nativeMixedEnabled) {
qmlArgs = QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlNativeDebuggerServices);
} else {
m_rp.masterEngineType = QmlCppEngineType;
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);
} }
} }

View File

@@ -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);
DebuggerCommand cmd("-exec-continue", RunRequest); if (isNativeMixedActiveFrame()) {
cmd.callback = CB(handleExecuteContinue); DebuggerCommand cmd("executeContinue", RunRequest|PythonCommand);
runCommand(cmd); cmd.callback = CB(handleExecuteContinue);
runCommand(cmd);
} else {
DebuggerCommand cmd("-exec-continue", RunRequest);
cmd.callback = CB(handleExecuteContinue);
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);
runCommand("-exec-finish", CB(handleExecuteContinue), RunRequest); if (isNativeMixedActiveFrame()) {
// -exec-finish in 'main' results (correctly) in DebuggerCommand cmd("executeStepOut", RunRequest|PythonCommand);
// 40^error,msg="\"finish\" not meaningful in the outermost frame." runCommand(cmd);
// However, this message does not seem to get flushed before } else {
// anything else happen - i.e. "never". Force some extra output. runCommand("-exec-finish", CB(handleExecuteContinue), RunRequest);
runCommand("print 32"); // -exec-finish in 'main' results (correctly) in
// 40^error,msg="\"finish\" not meaningful in the outermost frame."
// However, this message does not seem to get flushed before
// anything else happen - i.e. "never". Force some extra output.
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,29 +3191,30 @@ 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; // 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) {
void GdbEngine::handleQmlStackFrameArguments(const DebuggerResponse &response) if (!response.data.isValid()) {
{ showMessage(msgCannotLoadQmlStack(_("No stack obtained.")), LogError);
if (!response.data.isValid()) { return;
showMessage(msgCannotLoadQmlStack(_("No stack obtained.")), LogError); }
return; const quint64 contextAddress = findJsExecutionContextAddress(response.data, qtNamespace());
} if (!contextAddress) {
const quint64 contextAddress = findJsExecutionContextAddress(response.data, qtNamespace()); showMessage(msgCannotLoadQmlStack(_("The address of the JS execution context could not be found.")), LogError);
if (!contextAddress) { return;
showMessage(msgCannotLoadQmlStack(_("The address of the JS execution context could not be found.")), LogError); }
return; // Call the debug function of QML with the context address to obtain the QML stack trace.
} DebuggerCommand cmd = "-data-evaluate-expression \"qt_v4StackTrace((QV4::ExecutionContext *)0x"
// Call the debug function of QML with the context address to obtain the QML stack trace. + QByteArray::number(contextAddress, 16) + ")\"";
DebuggerCommand cmd = "-data-evaluate-expression \"qt_v4StackTrace((QV4::ExecutionContext *)0x" cmd.callback = CB(handleQmlStackTrace);
+ QByteArray::number(contextAddress, 16) + ")\""; runCommand(cmd);
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) 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 &params)
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 &params)
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(); cmd.arg("context", frame.context);
if (frame.language == QmlLanguage)
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;

View File

@@ -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 &parameters) override; void doUpdateLocals(const UpdateParameters &parameters) override;
void handleStackFrame(const DebuggerResponse &response); void handleFetchVariables(const DebuggerResponse &response);
void setLocals(const QList<GdbMi> &locals); void setLocals(const QList<GdbMi> &locals);

View File

@@ -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 &params)
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);

View File

@@ -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();

View File

@@ -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;

View File

@@ -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,19 +180,19 @@ 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())
const QFileInfo pFi(rp.projectSourceDirectory + QLatin1Char('/') + relativeFile); return;
if (pFi.isFile()) { const QFileInfo pFi(rp.projectSourceDirectory + QLatin1Char('/') + relativeFile);
file = pFi.absoluteFilePath(); if (pFi.isFile()) {
usable = true; file = pFi.absoluteFilePath();
return; usable = true;
} return;
const QFileInfo cFi(QDir::currentPath() + QLatin1Char('/') + relativeFile); }
if (cFi.isFile()) { const QFileInfo cFi(QDir::currentPath() + QLatin1Char('/') + relativeFile);
file = cFi.absoluteFilePath(); if (cFi.isFile()) {
usable = true; file = cFi.absoluteFilePath();
return; usable = true;
} return;
} }
} }
@@ -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;
} }

View File

@@ -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

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();
} }

View File

@@ -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";