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 re
import time
import json
if sys.version_info[0] >= 3:
xrange = range
@@ -241,7 +242,7 @@ class Blob(object):
return struct.unpack_from("f", self.data, offset)[0]
def warn(message):
print("XXX: %s\n" % message.encode("latin1"))
print('bridgemessage={msg="%s"},' % message.replace('"', '$').encode("latin1"))
def showException(msg, exType, exValue, exTraceback):
@@ -413,6 +414,8 @@ class DumperBase:
"personaltypes",
]
self.interpreterSeq = 0
def resetCaches(self):
# This is a cache mapping from 'type name' to 'display alternatives'.
@@ -598,6 +601,9 @@ class DumperBase:
data = self.extractBlob(addr, size).toBytes()
return self.hexencode(data)
def readJsonFromMemory(self, addr, size):
return json.loads(self.hexdecode(self.readMemory(addr, size)))
def encodeByteArray(self, value, limit = 0):
elided, data = self.encodeByteArrayHelper(self.extractPointer(value), limit)
return data
@@ -718,7 +724,6 @@ class DumperBase:
pass
return False
#warn("CHILDREN: %s %s %s" % (numChild, childType, childNumChild))
def putMapName(self, value, index = None):
ns = self.qtNamespace()
typeName = self.stripClassTag(str(value.type))
@@ -829,6 +834,16 @@ class DumperBase:
self.putSpecialValue(SpecialItemCountValue, count)
self.putNumChild(count)
def dictToMi(self, value):
if type(value) is bool:
return '"%d"' % int(value)
if type(value) is dict:
return '{' + ','.join(['%s=%s' % (k, self.dictToMi(v))
for (k, v) in list(value.items())]) + '}'
if type(value) is list:
return '[' + ','.join([self.dictToMi(v) for v in value]) + ']'
return '"%s"' % value
def putField(self, name, value):
self.put('%s="%s",' % (name, value))
@@ -1719,69 +1734,120 @@ class DumperBase:
sys.path.insert(1, head)
self.dumpermodules.append(os.path.splitext(tail)[0])
def sendQmlCommand(self, command, data = ""):
data += '"version":"1","command":"%s"' % command
data = data.replace('"', '\\"')
expr = 'qt_v4DebuggerHook("{%s}")' % data
try:
res = self.parseAndEvaluate(expr)
print("QML command ok, RES: %s, CMD: %s" % (res, expr))
except RuntimeError as error:
#print("QML command failed: %s: %s" % (expr, error))
res = None
except AttributeError as error:
# Happens with LLDB and 'None' current thread.
#print("QML command failed: %s: %s" % (expr, error))
res = None
def extractQStringFromQDataStream(self, buf, offset):
""" Read a QString from the stream """
size = struct.unpack_from("!I", buf, offset)[0]
offset += 4
string = buf[offset:offset + size].decode('utf-16be')
return (string, offset + size)
def extractQByteArrayFromQDataStream(self, buf, offset):
""" Read a QByteArray from the stream """
size = struct.unpack_from("!I", buf, offset)[0]
offset += 4
string = buf[offset:offset + size].decode('latin1')
return (string, offset + size)
def extractIntFromQDataStream(self, buf, offset):
""" Read an int from the stream """
value = struct.unpack_from("!I", buf, offset)[0]
return (value, offset + 4)
def readInterpreterOutput(self):
buf = self.parseAndEvaluate("qt_qmlDebugOutputBuffer")
size = self.parseAndEvaluate("qt_qmlDebugOutputLength")
return self.readJsonFromMemory(buf, size)
def handleInterpreterEvent(self):
""" Return True if inferior stopped """
buf = self.parseAndEvaluate("qt_qmlDebugEventBuffer")
size = self.parseAndEvaluate("qt_qmlDebugEventLength")
resdict = self.readJsonFromMemory(buf, size)
warn("RES DICT : %s" % resdict)
return resdict.get('event') == 'break'
def removeInterpreterBreakpoint(self, args):
res = self.sendInterpreterRequest('removebreakpoint', { 'id' : args['id'] })
return res
def prepareQmlStep(self, _):
self.sendQmlCommand('prepareStep')
def removeQmlBreakpoint(self, args):
fullName = args['fileName']
lineNumber = args['lineNumber']
#print("Remove QML breakpoint %s:%s" % (fullName, lineNumber))
bp = self.sendQmlCommand('removeBreakpoint',
'"fullName":"%s","lineNumber":"%s",'
% (fullName, lineNumber))
if bp is None:
#print("Direct QML breakpoint removal failed: %s.")
return 0
#print("Removing QML breakpoint: %s" % bp)
return int(bp)
def insertQmlBreakpoint(self, args):
print("Insert QML breakpoint %s" % self.describeBreakpointData(args))
bp = self.doInsertQmlBreakpoint(args)
res = self.sendQmlCommand('prepareStep')
#if res is None:
# print("Resetting stepping failed.")
def insertInterpreterBreakpoint(self, args):
args['condition'] = self.hexdecode(args.get('condition', ''))
warn("Insert interpreter breakpoint %s:%s (%s)"
% (args['file'], args['line'], args['condition']))
bp = self.doInsertInterpreterBreakpoint(args, False)
return str(bp)
def doInsertQmlBreakpoint(self, args):
fullName = args['fileName']
lineNumber = args['lineNumber']
pos = fullName.rfind('/')
engineName = "qrc:/" + fullName[pos+1:]
bp = self.sendQmlCommand('insertBreakpoint',
'"fullName":"%s","lineNumber":"%s","engineName":"%s",'
% (fullName, lineNumber, engineName))
if bp is None:
#print("Direct QML breakpoint insertion failed.")
#print("Make pending.")
self.createResolvePendingBreakpointsHookBreakpoint(args)
return 0
def sendInterpreterRequest(self, command, args = {}):
self.interpreterSeq += 1
cmd = { 'seq': self.interpreterSeq, 'type': 'request', 'command': command, 'arguments': args }
encoded = json.dumps(cmd)
hexdata = self.hexencode(encoded)
expr = 'qt_qmlDebugSendDataToService("NativeQmlDebugger","%s")' % hexdata
try:
res = self.parseAndEvaluate(expr)
except RuntimeError as error:
warn("Interpreter command failed: %s: %s" % (encoded, error))
return {}
except AttributeError as error:
# Happens with LLDB and 'None' current thread.
warn("Interpreter command failed: %s: %s" % (encoded, error))
return {}
print("Resolving QML breakpoint: %s" % bp)
if not res:
warn("Interpreter command failed: %s " % encoded)
return {}
resdict = self.readInterpreterOutput()
warn("Interpreter command output: '%s'" % resdict)
service = resdict.get("service")
if service == "NativeQmlDebugger":
messages = resdict.get("messages", [])
if len(messages) == 1:
return messages[0]
warn("Unexpected multiple interpreter messages: %s" % messages)
else:
warn("Interpreter result from alien service: %s" % service)
return {'messages': messages }
def executeStep(self, args):
if self.nativeMixed:
response = self.sendInterpreterRequest('stepin', args)
self.doContinue()
def executeStepOut(self, args):
if self.nativeMixed:
response = self.sendInterpreterRequest('stepout', args)
self.doContinue()
def executeNext(self, args):
if self.nativeMixed:
response = self.sendInterpreterRequest('stepover', args)
self.doContinue()
def executeContinue(self, args):
if self.nativeMixed:
response = self.sendInterpreterRequest('continue', args)
self.doContinue()
def doInsertInterpreterBreakpoint(self, args, wasPending):
warn("DO INSERT INTERPRETER BREAKPOINT, WAS PENDING: %s" % wasPending)
# Will fail if the service is not yet up and running.
response = self.sendInterpreterRequest('setbreakpoint', args)
bp = None if response is None else response.get("breakpoint", None)
if wasPending:
if not bp:
warn("ERROR: Pending interpreter breakpoint insertion failed.")
return -1
else:
if not bp:
warn("Direct interpreter breakpoint insertion failed.")
warn("Make pending.")
self.createResolvePendingBreakpointsHookBreakpoint(args)
return -1
warn("Resolved interpreter breakpoint: BP: %s" % bp)
return int(bp)
def describeBreakpointData(self, args):
fullName = args['fileName']
lineNumber = args['lineNumber']
return "%s:%s" % (fullName, lineNumber)
def isInternalQmlFrame(self, functionName):
def isInternalInterpreterFrame(self, functionName):
if functionName is None:
return False
if functionName.startswith("qt_v4"):
@@ -1793,7 +1859,7 @@ class DumperBase:
def canCallLocale(self):
return True
def isReportableQmlFrame(self, functionName):
def isReportableInterpreterFrame(self, functionName):
return functionName and functionName.find("QV4::Moth::VME::exec") >= 0
def extractQmlData(self, value):
@@ -1802,112 +1868,13 @@ class DumperBase:
data = value["data"]
return data.cast(self.lookupType(str(value.type).replace("QV4::", "QV4::Heap::")))
def extractQmlRuntimeString(self, compilationUnitPtr, index):
# This mimics compilationUnit->runtimeStrings[index]
# typeof runtimeStrings = QV4.StringValue **
runtimeStrings = compilationUnitPtr.dereference()["runtimeStrings"]
entry = runtimeStrings[index]
text = self.extractPointer(entry.dereference(), self.ptrSize())
(elided, fn) = self.encodeStringHelper(text, 100)
return self.encodedUtf16ToUtf8(fn)
def extractQmlLocation(self, engine):
if self.currentCallContext is None:
context = engine["current"] # QV4.ExecutionContext * or derived
self.currentCallContext = context
else:
context = self.currentCallContext["parent"]
ctxCode = int(context["type"])
compilationUnit = context["compilationUnit"]
functionName = "Unknown JS";
ns = self.qtNamespace()
# QV4.ExecutionContext.Type_SimpleCallContext - 4
# QV4.ExecutionContext.Type_CallContext - 5
if ctxCode == 4 or ctxCode == 5:
callContextDataType = self.lookupQtType("QV4::Heap::CallContext")
callContext = context.cast(callContextDataType.pointer())
functionObject = callContext["function"]
function = functionObject["function"]
# QV4.CompiledData.Function
compiledFunction = function["compiledFunction"].dereference()
index = int(compiledFunction["nameIndex"])
functionName = "JS: " + self.extractQmlRuntimeString(compilationUnit, index)
string = self.parseAndEvaluate("((%s)0x%x)->fileName()"
% (compilationUnit.type, compilationUnit))
fileName = self.encodeStringUtf8(string)
return {'functionName': functionName,
'lineNumber': int(context["lineNumber"]),
'fileName': fileName,
'context': context }
# Contains iname, name, and value.
class LocalItem:
pass
def extractQmlVariables(self, qmlcontext):
items = []
contextType = self.lookupQtType("QV4::Heap::CallContext")
context = self.createPointerValue(self.qmlcontext, contextType)
contextItem = self.LocalItem()
contextItem.iname = "local.@context"
contextItem.name = "[context]"
contextItem.value = context.dereference()
items.append(contextItem)
argsItem = self.LocalItem()
argsItem.iname = "local.@args"
argsItem.name = "[args]"
argsItem.value = context["callData"]
items.append(argsItem)
functionObject = context["function"].dereference()
functionPtr = functionObject["function"]
if not self.isNull(functionPtr):
compilationUnit = context["compilationUnit"]
compiledFunction = functionPtr["compiledFunction"]
base = int(compiledFunction)
formalsOffset = int(compiledFunction["formalsOffset"])
formalsCount = int(compiledFunction["nFormals"])
for index in range(formalsCount):
stringIndex = self.extractInt(base + formalsOffset + 4 * index)
name = self.extractQmlRuntimeString(compilationUnit, stringIndex)
item = self.LocalItem()
item.iname = "local." + name
item.name = name
item.value = argsItem.value["args"][index]
items.append(item)
localsOffset = int(compiledFunction["localsOffset"])
localsCount = int(compiledFunction["nLocals"])
for index in range(localsCount):
stringIndex = self.extractInt(base + localsOffset + 4 * index)
name = self.extractQmlRuntimeString(compilationUnit, stringIndex)
item = self.LocalItem()
item.iname = "local." + name
item.name = name
item.value = context["locals"][index]
items.append(item)
for engine in self.qmlEngines:
engineItem = self.LocalItem()
engineItem.iname = "local.@qmlengine"
engineItem.name = "[engine]"
engineItem.value = engine
items.append(engineItem)
rootContext = self.LocalItem()
rootContext.iname = "local.@rootContext"
rootContext.name = "[rootContext]"
rootContext.value = engine["d_ptr"]
items.append(rootContext)
break
return items
def extractInterpreterStack(self):
return self.sendInterpreterRequest('backtrace', {'limit': 10 })
def extractInterpreterVariables(self, args):
return self.sendInterpreterRequest('variables', args)

View File

@@ -14,9 +14,6 @@ import sys
import struct
import types
def warn(message):
print("XXX: %s\n" % message.encode("latin1"))
from dumper import *
@@ -221,15 +218,14 @@ class Dumper(DumperBase):
def __init__(self):
DumperBase.__init__(self)
# These values will be kept between calls to 'showData'.
# These values will be kept between calls to 'fetchVariables'.
self.isGdb = True
self.childEventAddress = None
self.typeCache = {}
self.typesReported = {}
self.typesToReport = {}
self.qtNamespaceToReport = None
self.qmlEngines = []
self.qmlBreakpoints = []
self.interpreterBreakpoints = []
def prepare(self, args):
self.output = []
@@ -243,7 +239,7 @@ class Dumper(DumperBase):
self.currentType = ReportItem()
self.currentAddress = None
# The guess does not need to be updated during a showData()
# The guess does not need to be updated during a fetchVariables()
# as the result is fixed during that time (ignoring "active"
# dumpers causing loading of shared objects etc).
self.currentQtNamespaceGuess = None
@@ -255,11 +251,10 @@ class Dumper(DumperBase):
self.typeformats = args.get("typeformats", {})
self.formats = args.get("formats", {})
self.watchers = args.get("watchers", {})
self.qmlcontext = int(args.get("qmlcontext", "0"), 0)
self.useDynamicType = int(args.get("dyntype", "0"))
self.useFancy = int(args.get("fancy", "0"))
self.forceQtNamespace = int(args.get("forcens", "0"))
self.passExceptions = int(args.get("passExceptions", "0"))
self.passExceptions = int(args.get("passexeptions", "0"))
self.nativeMixed = int(args.get("nativemixed", "0"))
self.autoDerefPointers = int(args.get("autoderef", "0"))
self.partialUpdate = int(args.get("partial", "0"))
@@ -359,21 +354,25 @@ class Dumper(DumperBase):
def canCallLocale(self):
return False if self.is32bit() else True
def showData(self, args):
def fetchVariables(self, args):
self.prepare(args)
partialVariable = args.get("partialVariable", "")
isPartial = len(partialVariable) > 0
if self.nativeMixed:
context = args.get('context', '')
if len(context):
res = self.extractInterpreterVariables(args)
if res:
safePrint('data=%s' % self.dictToMi(res.get('data', {})))
return
#
# Locals
#
self.output.append('data=[')
if self.qmlcontext:
locals = self.extractQmlVariables(self.qmlcontext)
elif isPartial:
if isPartial:
parts = partialVariable.split('.')
name = parts[1]
item = self.LocalItem()
@@ -498,7 +497,7 @@ class Dumper(DumperBase):
def parseAndEvaluate(self, exp):
return gdb.parse_and_eval(exp)
def callHelper(self, value, func, args):
def callHelper(self, value, function, args):
# args is a tuple.
arg = ""
for i in range(len(args)):
@@ -510,14 +509,14 @@ class Dumper(DumperBase):
else:
arg += a
#warn("CALL: %s -> %s(%s)" % (value, func, arg))
#warn("CALL: %s -> %s(%s)" % (value, function, arg))
typeName = self.stripClassTag(str(value.type))
if typeName.find(":") >= 0:
typeName = "'" + typeName + "'"
# 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
#exp = "((class %s*)%s)->%s(%s)" % (typeName, value.address, func, arg)
#exp = "((class %s*)%s)->%s(%s)" % (typeName, value.address, function, arg)
ptr = value.address if value.address else self.pokeValue(value)
exp = "((%s*)%s)->%s(%s)" % (typeName, ptr, func, arg)
exp = "((%s*)%s)->%s(%s)" % (typeName, ptr, function, arg)
#warn("CALL: %s" % exp)
result = gdb.parse_and_eval(exp)
#warn(" -> %s" % result)
@@ -739,32 +738,32 @@ class Dumper(DumperBase):
self.selectedInferior = lambda: self.cachedInferior
return self.cachedInferior
def readRawMemory(self, addr, size):
mem = self.selectedInferior().read_memory(addr, size)
def readRawMemory(self, address, size):
mem = self.selectedInferior().read_memory(address, size)
if sys.version_info[0] >= 3:
mem.tobytes()
return mem
def extractInt64(self, addr):
return struct.unpack("q", self.readRawMemory(addr, 8))[0]
def extractInt64(self, address):
return struct.unpack("q", self.readRawMemory(address, 8))[0]
def extractUInt64(self, addr):
return struct.unpack("Q", self.readRawMemory(addr, 8))[0]
def extractUInt64(self, address):
return struct.unpack("Q", self.readRawMemory(address, 8))[0]
def extractInt(self, addr):
return struct.unpack("i", self.readRawMemory(addr, 4))[0]
def extractInt(self, address):
return struct.unpack("i", self.readRawMemory(address, 4))[0]
def extractUInt(self, addr):
return struct.unpack("I", self.readRawMemory(addr, 4))[0]
def extractUInt(self, address):
return struct.unpack("I", self.readRawMemory(address, 4))[0]
def extractShort(self, addr):
return struct.unpack("h", self.readRawMemory(addr, 2))[0]
def extractShort(self, address):
return struct.unpack("h", self.readRawMemory(address, 2))[0]
def extractUShort(self, addr):
return struct.unpack("H", self.readRawMemory(addr, 2))[0]
def extractUShort(self, address):
return struct.unpack("H", self.readRawMemory(address, 2))[0]
def extractByte(self, addr):
return struct.unpack("b", self.readRawMemory(addr, 1))[0]
def extractByte(self, address):
return struct.unpack("b", self.readRawMemory(address, 1))[0]
def findStaticMetaObject(self, typename):
return self.findSymbol(typename + "::staticMetaObject")
@@ -847,12 +846,12 @@ class Dumper(DumperBase):
self.isQt3Support = lambda: self.cachedIsQt3Suport
return self.cachedIsQt3Suport
def putAddress(self, addr):
def putAddress(self, address):
if self.currentPrintsAddress and not self.isCli:
try:
# addr can be "None", int(None) fails.
#self.put('addr="0x%x",' % int(addr))
self.currentAddress = 'addr="0x%x",' % toInteger(addr)
# address can be "None", int(None) fails.
#self.put('address="0x%x",' % int(address))
self.currentAddress = 'address="0x%x",' % toInteger(address)
except:
pass
@@ -1574,6 +1573,9 @@ class Dumper(DumperBase):
self.typesToReport[typestring] = typeobj
return typeobj
def doContinue(self):
gdb.execute('continue')
def stackListFrames(self, args):
def fromNativePath(str):
return str.replace('\\', '/')
@@ -1581,12 +1583,8 @@ class Dumper(DumperBase):
limit = int(args['limit'])
if limit <= 0:
limit = 10000
options = args['options']
opts = {}
if options == "nativemixed":
opts["nativemixed"] = 1
self.prepare(opts)
self.prepare(args)
self.output = []
frame = gdb.newest_frame()
@@ -1598,7 +1596,7 @@ class Dumper(DumperBase):
functionName = "??" if name is None else name
fileName = ""
objfile = ""
fullName = ""
symtab = ""
pc = frame.pc()
sal = frame.find_sal()
line = -1
@@ -1607,62 +1605,57 @@ class Dumper(DumperBase):
symtab = sal.symtab
if not symtab is None:
objfile = fromNativePath(symtab.objfile.filename)
fileName = fromNativePath(symtab.filename)
fullName = symtab.fullname()
if fullName is None:
fullName = ""
else:
fullName = fromNativePath(fullName)
fileName = fromNativePath(symtab.fullname())
if self.nativeMixed:
if self.isReportableQmlFrame(functionName):
engine = frame.read_var("engine")
h = self.extractQmlLocation(engine)
self.put(('frame={level="%s",func="%s",file="%s",'
'fullname="%s",line="%s",language="js",addr="0x%x"}')
% (i, h['functionName'], h['fileName'], h['fileName'],
h['lineNumber'], h['context']))
if self.nativeMixed and functionName == "qt_qmlDebugEventFromService":
interpreterStack = self.extractInterpreterStack()
#print("EXTRACTED INTEPRETER STACK: %s" % interpreterStack)
for interpreterFrame in interpreterStack.get('frames', []):
function = interpreterFrame.get('function', '')
fileName = interpreterFrame.get('file', '')
language = interpreterFrame.get('language', '')
lineNumber = interpreterFrame.get('line', 0)
context = interpreterFrame.get('context', 0)
self.put(('frame={function="%s",file="%s",'
'line="%s",language="%s",context="%s"}')
% (function, fileName, lineNumber, language, context))
if False and self.isInternalInterpreterFrame(functionName):
frame = frame.older()
self.put(('frame={address="0x%x",function="%s",'
'file="%s",line="%s",'
'module="%s",language="c",usable="0"}') %
(pc, functionName, fileName, line, objfile))
i += 1
frame = frame.older()
continue
if self.isInternalQmlFrame(functionName):
frame = frame.older()
self.put(('frame={level="%s",addr="0x%x",func="%s",'
'file="%s",fullname="%s",line="%s",'
'from="%s",language="c",usable="0"}') %
(i, pc, functionName, fileName, fullName, line, objfile))
i += 1
frame = frame.older()
continue
self.put(('frame={level="%s",addr="0x%x",func="%s",'
'file="%s",fullname="%s",line="%s",'
'from="%s",language="c"}') %
(i, pc, functionName, fileName, fullName, line, objfile))
self.put(('frame={level="%s",address="0x%x",function="%s",'
'file="%s",line="%s",module="%s",language="c"}') %
(i, pc, functionName, fileName, line, objfile))
frame = frame.older()
i += 1
safePrint(''.join(self.output))
safePrint('frames=[' + ','.join(self.output) + ']')
def createResolvePendingBreakpointsHookBreakpoint(self, args):
class Resolver(gdb.Breakpoint):
def __init__(self, dumper, args):
self.dumper = dumper
self.args = args
spec = "qt_v4ResolvePendingBreakpointsHook"
spec = "qt_qmlDebugConnectorOpen"
print("Preparing hook to resolve pending QML breakpoint at %s" % args)
super(Resolver, self).\
__init__(spec, gdb.BP_BREAKPOINT, internal=True, temporary=False)
def stop(self):
bp = self.dumper.doInsertQmlBreakpoint(args)
bp = self.dumper.doInsertInterpreterBreakpoint(args, True)
print("Resolving QML breakpoint %s -> %s" % (args, bp))
self.enabled = False
return False
self.qmlBreakpoints.append(Resolver(self, args))
self.interpreterBreakpoints.append(Resolver(self, args))
def exitGdb(self, _):
gdb.execute("quit")
@@ -1675,13 +1668,13 @@ class Dumper(DumperBase):
import tempfile
import cProfile
tempDir = tempfile.gettempdir() + "/bbprof"
cProfile.run('theDumper.showData(%s)' % args, tempDir)
cProfile.run('theDumper.fetchVariables(%s)' % args, tempDir)
import pstats
pstats.Stats(tempDir).sort_stats('time').print_stats()
def profile2(self, args):
import timeit
print(timeit.repeat('theDumper.showData(%s)' % args,
print(timeit.repeat('theDumper.fetchVariables(%s)' % args,
'from __main__ import theDumper', number=10))
@@ -1774,7 +1767,7 @@ class CliDumper(Dumper):
def putAddressRange(self, base, step):
return True
def showData(self, args):
def fetchVariables(self, args):
args['fancy'] = 1
args['passException'] = 1
args['autoderef'] = 1
@@ -1811,20 +1804,6 @@ registerCommand("threadnames", threadnames)
#
#######################################################################
#class QmlEngineCreationTracker(gdb.Breakpoint):
# def __init__(self):
# spec = "QQmlEnginePrivate::init"
# super(QmlEngineCreationTracker, self).\
# __init__(spec, gdb.BP_BREAKPOINT, internal=True)
#
# def stop(self):
# engine = gdb.parse_and_eval("q_ptr")
# print("QML engine created: %s" % engine)
# theDumper.qmlEngines.append(engine)
# return False
#
#QmlEngineCreationTracker()
class TriggeredBreakpointHookBreakpoint(gdb.Breakpoint):
def __init__(self):
spec = "qt_v4TriggeredBreakpointHook"
@@ -1837,3 +1816,14 @@ class TriggeredBreakpointHookBreakpoint(gdb.Breakpoint):
TriggeredBreakpointHookBreakpoint()
class QmlEngineEventBreakpoint(gdb.Breakpoint):
def __init__(self):
spec = "qt_qmlDebugEventFromService"
super(QmlEngineEventBreakpoint, self).\
__init__(spec, gdb.BP_BREAKPOINT, internal=True)
def stop(self):
print("QML engine event received.")
return theDumper.handleInterpreterEvent()
QmlEngineEventBreakpoint()

View File

@@ -57,7 +57,7 @@ def showException(msg, exType, exValue, exTraceback):
lines = [line for line in traceback.format_exception(exType, exValue, exTraceback)]
warn('\n'.join(lines))
def fileName(file):
def fileNameAsString(file):
return str(file) if file.IsValid() else ''
@@ -775,7 +775,7 @@ class Dumper(DumperBase):
def describeLocation(self, frame):
if int(frame.pc) == 0xffffffffffffffff:
return ''
file = fileName(frame.line_entry.file)
file = fileNameAsString(frame.line_entry.file)
line = frame.line_entry.line
return 'location={file="%s",line="%s",addr="%s"}' % (file, line, frame.pc)
@@ -822,8 +822,8 @@ class Dumper(DumperBase):
result += ',fp="0x%x"' % frame.fp
result += ',func="%s"' % frame.GetFunctionName()
result += ',line="%s"' % frame.line_entry.line
result += ',fullname="%s"' % fileName(frame.line_entry.file)
result += ',file="%s"' % fileName(frame.line_entry.file)
result += ',fullname="%s"' % fileNameAsString(frame.line_entry.file)
result += ',file="%s"' % fileNameAsString(frame.line_entry.file)
result += '}},'
result += '],current-thread-id="%s"' % self.currentThread().id
@@ -849,7 +849,7 @@ class Dumper(DumperBase):
self.report(self.describeLocation(thread.GetFrameAtIndex(0))) # FIXME
isNativeMixed = int(args.get('nativeMixed', 0))
isNativeMixed = int(args.get('nativemixed', 0))
limit = args.get('stacklimit', -1)
(n, isLimited) = (limit, True) if limit > 0 else (thread.GetNumFrames(), False)
@@ -869,39 +869,38 @@ class Dumper(DumperBase):
level = frame.idx
addr = frame.GetPCAddress().GetLoadAddress(self.target)
functionName = frame.GetFunctionName()
fullname = fileName(lineEntry.file)
fileName = fileNameAsString(lineEntry.file)
usable = None
language = None
if isNativeMixed:
if self.isReportableQmlFrame(functionName):
if False and isNativeMixed:
if self.isReportableInterpreterFrame(functionName):
engine = frame.FindVariable("engine")
self.context = engine
h = self.extractQmlLocation(engine)
pc = 0
functionName = h['functionName']
fullname = h['fileName']
lineNumber = h['lineNumber']
functionName = h['function']
fileName = h['file']
lineNumber = h['line']
addr = h['context']
language = 'js'
elif not functionName is None:
if functionName.startswith("qt_v4"):
usable = 0
elif functionName.find("QV4::") >= 0:
usable = 0
#elif not functionName is None:
# if functionName.startswith("qt_v4"):
# usable = 0
# elif functionName.find("QV4::") >= 0:
# usable = 0
result += '{pc="0x%x"' % pc
result += ',level="%d"' % level
result += ',addr="0x%x"' % addr
result += ',address="0x%x"' % addr
if not usable is None:
result += ',usable="%s"' % usable
result += ',func="%s"' % functionName
result += ',function="%s"' % functionName
result += ',line="%d"' % lineNumber
if not language is None:
result += ',language="%s"' % language
result += ',fullname="%s"' % fullname
result += ',file="%s"},' % fullname
result += ',file="%s"},' % fileName
result += ']'
result += ',hasmore="%d"' % isLimited
result += ',limit="%d"' % limit
@@ -1400,7 +1399,7 @@ class Dumper(DumperBase):
addr = loc.GetAddress()
lineEntry = addr.GetLineEntry()
result += '{locid="%s"' % loc.GetID()
result += ',func="%s"' % addr.GetFunction().GetName()
result += ',function="%s"' % addr.GetFunction().GetName()
result += ',enabled="%s"' % (1 if loc.IsEnabled() else 0)
result += ',resolved="%s"' % (1 if loc.IsResolved() else 0)
result += ',valid="%s"' % (1 if loc.IsValid() else 0)
@@ -1421,16 +1420,16 @@ class Dumper(DumperBase):
def insertBreakpoint(self, args):
bpType = args["type"]
if bpType == BreakpointByFileAndLine:
fileName = args["fileName"]
fileName = args["file"]
if fileName.endswith(".js") or fileName.endswith(".qml"):
self.insertQmlBreakpoint(args)
self.doInsertInterpreterBreakpoint(args, False)
return
extra = ''
more = True
if bpType == BreakpointByFileAndLine:
bp = self.target.BreakpointCreateByLocation(
str(args["fileName"]), int(args["lineNumber"]))
str(args["file"]), int(args["line"]))
elif bpType == BreakpointByFunction:
bp = self.target.BreakpointCreateByName(args["function"])
elif bpType == BreakpointByAddress:

View File

@@ -34,10 +34,12 @@
#include <QString>
namespace QmlDebug {
enum QmlDebugServicesPreset {
NoQmlDebugServices,
QmlDebuggerServices,
QmlProfilerServices
QmlProfilerServices,
QmlNativeDebuggerServices
};
static inline QString qmlDebugServices(QmlDebugServicesPreset preset)
@@ -49,6 +51,8 @@ static inline QString qmlDebugServices(QmlDebugServicesPreset preset)
return QStringLiteral("DebugMessages,QmlDebugger,V8Debugger,QmlInspector");
case QmlProfilerServices:
return QStringLiteral("CanvasFrameRate,EngineControl");
case QmlNativeDebuggerServices:
return QStringLiteral("NativeQmlDebugger");
default:
Q_ASSERT(false);
return QString();
@@ -61,6 +65,10 @@ static inline QString qmlDebugCommandLineArguments(QmlDebugServicesPreset servic
if (services == NoQmlDebugServices)
return QString();
if (services == QmlNativeDebuggerServices)
return QString::fromLatin1("-qmljsdebugger=native,services:%1")
.arg(qmlDebugServices(services));
return QString::fromLatin1("-qmljsdebugger=port:%1,block,services:%2")
.arg(port ? QString::number(port) : QStringLiteral("%qml_port%"))
.arg(qmlDebugServices(services));

View File

@@ -757,14 +757,15 @@ const BreakpointParameters &Breakpoint::parameters() const
void Breakpoint::addToCommand(DebuggerCommand *cmd) const
{
cmd->arg("modelid", id().toByteArray());
cmd->arg("id", int(response().id.majorPart()));
cmd->arg("type", type());
cmd->arg("ignorecount", ignoreCount());
cmd->arg("condition", condition().toHex());
cmd->arg("function", functionName().toUtf8());
cmd->arg("oneshot", isOneShot());
cmd->arg("enabled", isEnabled());
cmd->arg("fileName", fileName().toUtf8());
cmd->arg("lineNumber", lineNumber());
cmd->arg("file", fileName().toUtf8());
cmd->arg("line", lineNumber());
cmd->arg("address", address());
cmd->arg("expression", expression());
}

View File

@@ -2539,8 +2539,7 @@ bool CdbEngine::stateAcceptsBreakpointChanges() const
bool CdbEngine::acceptsBreakpoint(Breakpoint bp) const
{
if (!bp.parameters().isCppBreakpoint())
return false;
if (bp.parameters().isCppBreakpoint()) {
switch (bp.type()) {
case UnknownBreakpointType:
case LastBreakpointType:
@@ -2562,6 +2561,8 @@ bool CdbEngine::acceptsBreakpoint(Breakpoint bp) const
}
return true;
}
return isNativeMixedEnabled();
}
// Context for fixing file/line-type breakpoints, for delayed creation.
class BreakpointCorrectionContext
@@ -2801,7 +2802,7 @@ static StackFrames parseFrames(const GdbMi &gdbmi, bool *incomplete = 0)
break;
}
StackFrame frame;
frame.level = i;
frame.level = QByteArray::number(i);
const GdbMi fullName = frameMi["fullname"];
if (fullName.isValid()) {
frame.file = QFile::decodeName(fullName.data());
@@ -2811,9 +2812,10 @@ static StackFrames parseFrames(const GdbMi &gdbmi, bool *incomplete = 0)
if (languageMi.isValid() && languageMi.data() == "js")
frame.language = QmlLanguage;
}
frame.function = QLatin1String(frameMi["func"].data());
frame.from = QLatin1String(frameMi["from"].data());
frame.address = frameMi["addr"].data().toULongLong(0, 16);
frame.function = QLatin1String(frameMi["function"].data());
frame.module = QLatin1String(frameMi["from"].data());
frame.context = frameMi["context"].data();
frame.address = frameMi["address"].data().toULongLong(0, 16);
rc.push_back(frame);
}
return rc;
@@ -2895,7 +2897,7 @@ void CdbEngine::handleAdditionalQmlStack(const DebuggerResponse &response)
break;
}
for (int i = 0; i < qmlFrameCount; ++i)
qmlFrames[i].fixQmlFrame(runParameters());
qmlFrames[i].fixQrcFrame(runParameters());
stackHandler()->prependFrames(qmlFrames);
} while (false);
if (!errorMessage.isEmpty())

View File

@@ -15,6 +15,7 @@ QtcPlugin {
Depends { name: "Core" }
Depends { name: "CppTools" }
Depends { name: "ProjectExplorer" }
Depends { name: "QtSupport" }
Depends { name: "TextEditor" }
cpp.includePaths: base.concat([project.sharedSourcesDir + "/registryaccess"])

View File

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

View File

@@ -184,18 +184,6 @@ DebuggerSettings::DebuggerSettings()
item->setIconVisibleInMenu(false);
insertItem(OperateByInstruction, item);
item = new SavedAction(this);
item->setText(tr("Native Mixed Mode"));
item->setCheckable(true);
item->setDefaultValue(true);
item->setIcon(QIcon(QLatin1String(Core::Constants::ICON_LINK)));
item->setToolTip(tr("<p>This switches the debugger to native-mixed "
"operation mode. In this mode, stepping and data display will "
"be handled by the native debugger backend (GDB, LLDB or CDB) "
"for C++, QML and JS sources."));
item->setIconVisibleInMenu(false);
insertItem(OperateNativeMixed, item);
item = new SavedAction(this);
item->setText(tr("Dereference Pointers Automatically"));
item->setCheckable(true);

View File

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

View File

@@ -61,7 +61,6 @@ const char NEXT[] = "Debugger.NextLine";
const char REVERSE[] = "Debugger.ReverseDirection";
const char RESET[] = "Debugger.Reset";
const char OPERATE_BY_INSTRUCTION[] = "Debugger.OperateByInstruction";
const char OPERATE_NATIVE_MIXED[] = "Debugger.OperateNativeMixed";
const char QML_SHOW_APP_ON_TOP[] = "Debugger.QmlShowAppOnTop";
const char QML_SELECTTOOL[] = "Debugger.QmlSelectTool";
const char QML_ZOOMTOOL[] = "Debugger.QmlZoomTool";

View File

@@ -110,8 +110,6 @@ DebuggerEngine *currentEngine();
QMessageBox *showMessageBox(int icon, const QString &title,
const QString &text, int buttons = 0);
bool isNativeMixedActive();
bool isNativeMixedEnabled();
bool isReverseDebuggingEnabled();
} // namespace Internal

View File

@@ -78,6 +78,11 @@
#include <QFileInfo>
#include <QDir>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
using namespace Core;
using namespace Debugger::Internal;
using namespace ProjectExplorer;
@@ -126,7 +131,7 @@ Location::Location(const StackFrame &frame, bool marker)
m_functionName = frame.function;
m_hasDebugInfo = frame.isUsable();
m_address = frame.address;
m_from = frame.from;
m_from = frame.module;
}
@@ -192,7 +197,7 @@ public:
m_modulesHandler(engine),
m_registerHandler(engine),
m_sourceFilesHandler(),
m_stackHandler(),
m_stackHandler(engine),
m_threadsHandler(),
m_watchHandler(engine),
m_disassemblerAgent(engine),
@@ -203,8 +208,6 @@ public:
this, &DebuggerEnginePrivate::resetLocation);
connect(action(IntelFlavor), &Utils::SavedAction::valueChanged,
this, &DebuggerEnginePrivate::reloadDisassembly);
connect(action(OperateNativeMixed), &QAction::triggered,
engine, &DebuggerEngine::reloadFullStack);
Utils::globalMacroExpander()->registerFileVariables(PrefixDebugExecutable,
tr("Debugged executable"),
@@ -2023,6 +2026,26 @@ void DebuggerEngine::checkState(DebuggerState state, const char *file, int line)
qDebug("%s", qPrintable(msg));
}
bool DebuggerEngine::isNativeMixedEnabled() const
{
return runParameters().nativeMixedEnabled && (runParameters().languages & QmlLanguage);
}
bool DebuggerEngine::isNativeMixedActive() const
{
return isNativeMixedEnabled(); //&& boolSetting(OperateNativeMixed);
}
bool DebuggerEngine::isNativeMixedActiveFrame() const
{
if (!isNativeMixedActive())
return false;
if (stackHandler()->frames().isEmpty())
return false;
StackFrame frame = stackHandler()->frameAt(0);
return frame.language == QmlLanguage;
}
} // namespace Internal
} // namespace Debugger

View File

@@ -117,6 +117,8 @@ public:
// Used by AttachCrashedExternal.
QString crashParameter;
bool nativeMixedEnabled = false;
// For Debugger testing.
int testCase = 0;
};
@@ -443,6 +445,9 @@ protected:
void updateLocalsView(const GdbMi &all);
void checkState(DebuggerState state, const char *file, int line);
bool isNativeMixedEnabled() const;
bool isNativeMixedActive() const;
bool isNativeMixedActiveFrame() const;
private:
// Wrapper engine needs access to state of its subengines.

View File

@@ -474,7 +474,6 @@ bool DummyEngine::hasCapability(unsigned cap) const
return cap & (WatchpointByAddressCapability
| BreakConditionCapability
| TracePointCapability
| OperateNativeMixed
| OperateByInstructionCapability);
// This is a Qml or unknown engine.
@@ -2321,17 +2320,6 @@ QMessageBox *showMessageBox(int icon, const QString &title,
return mb;
}
bool isNativeMixedEnabled()
{
static bool enabled = qEnvironmentVariableIsSet("QTC_DEBUGGER_NATIVE_MIXED");
return enabled;
}
bool isNativeMixedActive()
{
return isNativeMixedEnabled() && boolSetting(OperateNativeMixed);
}
bool isReverseDebuggingEnabled()
{
static bool enabled = qEnvironmentVariableIsSet("QTC_DEBUGGER_ENABLE_REVERSE");
@@ -2769,16 +2757,6 @@ void DebuggerPluginPrivate::extensionsInitialized()
cmd->setAttribute(Command::CA_Hide);
debugMenu->addAction(cmd);
if (isNativeMixedEnabled()) {
SavedAction *act = action(OperateNativeMixed);
act->setValue(true);
cmd = ActionManager::registerAction(act, Constants::OPERATE_NATIVE_MIXED);
cmd->setAttribute(Command::CA_Hide);
debugMenu->addAction(cmd);
connect(cmd->action(), &QAction::triggered,
[this] { currentEngine()->updateAll(); });
}
cmd = ActionManager::registerAction(m_breakAction, "Debugger.ToggleBreak");
cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("F8") : tr("F9")));
debugMenu->addAction(cmd);
@@ -2912,8 +2890,6 @@ void DebuggerPluginPrivate::extensionsInitialized()
hbox->addWidget(toolButton(Constants::STEPOUT));
hbox->addWidget(toolButton(Constants::RESET));
hbox->addWidget(toolButton(Constants::OPERATE_BY_INSTRUCTION));
if (isNativeMixedEnabled())
hbox->addWidget(toolButton(Constants::OPERATE_NATIVE_MIXED));
if (isReverseDebuggingEnabled()) {
m_reverseToolButton = toolButton(Constants::REVERSE);

View File

@@ -80,7 +80,7 @@ void GdbMi::parseResultOrValue(const char *&from, const char *to)
if (from == to || *from == '(')
return;
const char *ptr = from;
while (ptr < to && *ptr != '=') {
while (ptr < to && *ptr != '=' && *ptr != ':') {
//qDebug() << "adding" << QChar(*ptr) << "to name";
++ptr;
}
@@ -770,6 +770,12 @@ QString decodeData(const QByteArray &ba, DebuggerEncoding encoding)
case SpecialEmptyStructureValue: { // 39
return QLatin1String("{...}");
}
case SpecialUndefinedValue: { // 40
return QLatin1String("Undefined");
}
case SpecialNullValue: { // 41
return QLatin1String("Null");
}
}
qDebug() << "ENCODING ERROR: " << encoding;
return QCoreApplication::translate("Debugger", "<Encoding error>");
@@ -857,5 +863,22 @@ QByteArray DebuggerCommand::argsToString() const
return args.toString().toLatin1();
}
DebuggerEncoding debuggerEncoding(const QByteArray &data)
{
if (data == "utf16")
return Hex4EncodedLittleEndianWithQuotes;
if (data == "empty")
return SpecialEmptyValue;
if (data == "minimumitemcount")
return SpecialMinimumItemCountValue;
if (data == "undefined")
return SpecialUndefinedValue;
if (data == "null")
return SpecialNullValue;
if (data == "itemcount")
return SpecialItemCountValue;
return DebuggerEncoding(data.toInt());
}
} // namespace Internal
} // namespace Debugger

View File

@@ -252,9 +252,13 @@ enum DebuggerEncoding
SpecialNotCallableValue = 36,
SpecialNullReferenceValue = 37,
SpecialOptimizedOutValue = 38,
SpecialEmptyStructureValue = 39
SpecialEmptyStructureValue = 39,
SpecialUndefinedValue = 40,
SpecialNullValue = 41
};
DebuggerEncoding debuggerEncoding(const QByteArray &data);
// Decode string data as returned by the dumper helpers.
QString decodeData(const QByteArray &baIn, DebuggerEncoding encoding);

View File

@@ -59,6 +59,8 @@
#include <coreplugin/icore.h>
#include <qmldebug/qmldebugcommandlinearguments.h>
#include <qtsupport/qtkitinformation.h>
#include <QTcpServer>
using namespace Debugger::Internal;
@@ -416,6 +418,19 @@ void DebuggerRunControlCreator::enrich(const RunConfiguration *runConfig, const
if (m_project && m_rp.projectSourceFiles.isEmpty())
m_rp.projectSourceFiles = m_project->files(Project::ExcludeGeneratedFiles);
if (m_project && m_rp.projectSourceFiles.isEmpty())
m_rp.projectSourceFiles = m_project->files(Project::ExcludeGeneratedFiles);
if (false && m_project && m_kit) {
const QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(m_kit);
m_rp.nativeMixedEnabled = version && version->qtVersion() >= QtSupport::QtVersionNumber(5, 7, 0);
}
bool ok = false;
int nativeMixedOverride = qgetenv("QTC_DEBUGGER_NATIVE_MIXED").toInt(&ok);
if (ok)
m_rp.nativeMixedEnabled = bool(nativeMixedOverride);
// validate debugger if C++ debugging is enabled
if (m_rp.languages & CppLanguage) {
const QList<Task> tasks = DebuggerKitInformation::validateDebugger(m_kit);
@@ -482,9 +497,6 @@ void DebuggerRunControlCreator::enrich(const RunConfiguration *runConfig, const
const QString optimizerKey = _("QML_DISABLE_OPTIMIZER");
if (!m_rp.environment.hasKey(optimizerKey))
m_rp.environment.set(optimizerKey, _("1"));
QtcProcess::addArg(&m_rp.processArgs, QmlDebug::qmlDebugCommandLineArguments(
QmlDebug::QmlDebuggerServices, m_rp.qmlServerPort));
}
}
}
@@ -502,14 +514,23 @@ void DebuggerRunControlCreator::enrich(const RunConfiguration *runConfig, const
}
if (m_rp.masterEngineType == NoEngineType && m_debuggerAspect) {
const bool useCppDebugger = m_debuggerAspect->useCppDebugger() && (m_rp.languages & CppLanguage);
const bool useQmlDebugger = m_debuggerAspect->useQmlDebugger() && (m_rp.languages & QmlLanguage);
const bool wantCppDebugger = m_debuggerAspect->useCppDebugger() && (m_rp.languages & CppLanguage);
const bool wantQmlDebugger = m_debuggerAspect->useQmlDebugger() && (m_rp.languages & QmlLanguage);
if (useQmlDebugger) {
if (useCppDebugger)
if (wantQmlDebugger) {
QString qmlArgs;
if (wantCppDebugger) {
if (m_rp.nativeMixedEnabled) {
qmlArgs = QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlNativeDebuggerServices);
} else {
m_rp.masterEngineType = QmlCppEngineType;
else
qmlArgs = QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlDebuggerServices, m_rp.qmlServerPort);
}
} else {
m_rp.masterEngineType = QmlEngineType;
qmlArgs = QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlDebuggerServices, m_rp.qmlServerPort);
}
QtcProcess::addArg(&m_rp.processArgs, qmlArgs);
}
}

View File

@@ -625,6 +625,18 @@ void GdbEngine::handleResponse(const QByteArray &buff)
case '~': {
QByteArray data = GdbMi::parseCString(from, to);
if (data.startsWith("bridgemessage={")) {
//showMessage(_(data), LogDebug);
break;
}
if (data.startsWith("bridgeresult={")) {
//showMessage(_(data), LogDebug);
DebuggerResponse response;
response.resultClass = ResultDone;
response.data.fromStringMultiple(data);
handleResultRecord(&response);
break;
}
m_pendingConsoleStreamOutput += data;
// Parse pid from noise.
@@ -1369,9 +1381,13 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
int lineNumber = 0;
QString fullName;
QByteArray function;
QByteArray language;
if (frame.isValid()) {
const GdbMi lineNumberG = frame["line"];
function = frame["func"].data();
function = frame["function"].data(); // V4 protocol
if (function.isEmpty())
function = frame["func"].data(); // GDB's *stopped messages
language = frame["language"].data();
if (lineNumberG.isValid()) {
lineNumber = lineNumberG.toInt();
fullName = cleanupFullName(QString::fromLocal8Bit(frame["fullname"].data()));
@@ -1384,9 +1400,6 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
if (rid.isValid() && frame.isValid() && !isQFatalBreakpoint(rid)) {
// Use opportunity to update the breakpoint marker position.
//qDebug() << " PROBLEM: " << m_qmlBreakpointNumbers << rid
// << isQmlStepBreakpoint1(rid)
// << isQmlStepBreakpoint2(rid)
Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid);
const BreakpointResponse &response = bp.response();
QString fileName = response.fileName;
@@ -1403,7 +1416,9 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
if (lineNumber && !boolSetting(OperateByInstruction)
&& QFileInfo::exists(fullName)
&& !isQFatalBreakpoint(rid)
&& function != "qt_v4TriggeredBreakpointHook")
&& function != "qt_v4TriggeredBreakpointHook"
&& function != "qt_qmlDebugEventFromService"
&& language != "js")
gotoLocation(Location(fullName, lineNumber));
if (state() == InferiorRunOk) {
@@ -1479,7 +1494,7 @@ void GdbEngine::handleStop1(const GdbMi &data)
if (boolSetting(SkipKnownFrames)) {
if (reason == "end-stepping-range" || reason == "function-finished") {
//showMessage(frame.toString());
QString funcName = _(frame["func"].data());
QString funcName = _(frame["function"].data());
QString fileName = QString::fromLocal8Bit(frame["file"].data());
if (isLeavableFunction(funcName, fileName)) {
//showMessage(_("LEAVING ") + funcName);
@@ -1978,10 +1993,16 @@ void GdbEngine::continueInferiorInternal()
notifyInferiorRunRequested();
showStatusMessage(tr("Running requested..."), 5000);
CHECK_STATE(InferiorRunRequested);
if (isNativeMixedActiveFrame()) {
DebuggerCommand cmd("executeContinue", RunRequest|PythonCommand);
cmd.callback = CB(handleExecuteContinue);
runCommand(cmd);
} else {
DebuggerCommand cmd("-exec-continue", RunRequest);
cmd.callback = CB(handleExecuteContinue);
runCommand(cmd);
}
}
void GdbEngine::continueInferior()
{
@@ -1996,10 +2017,10 @@ void GdbEngine::executeStep()
setTokenBarrier();
notifyInferiorRunRequested();
showStatusMessage(tr("Step requested..."), 5000);
if (isNativeMixedActive()) {
DebuggerCommand cmd("prepareQmlStep", PythonCommand);
if (isNativeMixedActiveFrame()) {
DebuggerCommand cmd("executeStep", RunRequest|PythonCommand);
cmd.callback = CB(handleExecuteStep);
runCommand(cmd);
continueInferiorInternal();
} else {
DebuggerCommand cmd(isReverseDebugging() ? "reverse-step" : "-exec-step", RunRequest);
cmd.callback = CB(handleExecuteStep);
@@ -2061,6 +2082,10 @@ void GdbEngine::executeStepOut()
setTokenBarrier();
notifyInferiorRunRequested();
showStatusMessage(tr("Finish function requested..."), 5000);
if (isNativeMixedActiveFrame()) {
DebuggerCommand cmd("executeStepOut", RunRequest|PythonCommand);
runCommand(cmd);
} else {
runCommand("-exec-finish", CB(handleExecuteContinue), RunRequest);
// -exec-finish in 'main' results (correctly) in
// 40^error,msg="\"finish\" not meaningful in the outermost frame."
@@ -2068,6 +2093,7 @@ void GdbEngine::executeStepOut()
// anything else happen - i.e. "never". Force some extra output.
runCommand("print 32");
}
}
void GdbEngine::executeNext()
{
@@ -2075,10 +2101,9 @@ void GdbEngine::executeNext()
setTokenBarrier();
notifyInferiorRunRequested();
showStatusMessage(tr("Step next requested..."), 5000);
if (isNativeMixedActive()) {
DebuggerCommand cmd("prepareQmlStep", PythonCommand);
if (isNativeMixedActiveFrame()) {
DebuggerCommand cmd("executeNext", RunRequest|PythonCommand);
runCommand(cmd);
continueInferiorInternal();
} else {
DebuggerCommand cmd(isReverseDebugging() ? "reverse-next" : "-exec-next", RunRequest);
cmd.callback = CB(handleExecuteNext);
@@ -2656,10 +2681,9 @@ bool GdbEngine::acceptsBreakpoint(Breakpoint bp) const
{
if (runParameters().startMode == AttachCore)
return false;
// We handle QML breakpoint unless specifically
if (isNativeMixedEnabled() && !(runParameters().languages & QmlLanguage))
if (bp.parameters().isCppBreakpoint())
return true;
return bp.parameters().isCppBreakpoint();
return isNativeMixedEnabled();
}
void GdbEngine::insertBreakpoint(Breakpoint bp)
@@ -2672,7 +2696,7 @@ void GdbEngine::insertBreakpoint(Breakpoint bp)
const BreakpointParameters &data = bp.parameters();
if (!data.isCppBreakpoint()) {
DebuggerCommand cmd("insertQmlBreakpoint", PythonCommand);
DebuggerCommand cmd("insertInterpreterBreakpoint", PythonCommand);
bp.addToCommand(&cmd);
runCommand(cmd);
bp.notifyBreakpointInsertOk();
@@ -2791,7 +2815,7 @@ void GdbEngine::removeBreakpoint(Breakpoint bp)
const BreakpointParameters &data = bp.parameters();
if (!data.isCppBreakpoint()) {
DebuggerCommand cmd("removeQmlBreakpoint", PythonCommand);
DebuggerCommand cmd("removeInterpreterBreakpoint", PythonCommand);
bp.addToCommand(&cmd);
runCommand(cmd);
bp.notifyBreakpointRemoveOk();
@@ -3140,13 +3164,11 @@ void GdbEngine::reloadFullStack()
runCommand(cmd);
}
void GdbEngine::loadAdditionalQmlStack()
static QString msgCannotLoadQmlStack(const QString &why)
{
// Scan for QV4::ExecutionContext parameter in the parameter list of a V4 call.
runCommand("-stack-list-arguments --simple-values", CB(handleQmlStackFrameArguments), NeedsStop);
return _("Unable to load QML stack: ") + why;
}
// Scan the arguments of a stack list for the address of a QV4::ExecutionContext.
static quint64 findJsExecutionContextAddress(const GdbMi &stackArgsResponse, const QByteArray &qtNamespace)
{
const GdbMi frameList = stackArgsResponse.childAt(0);
@@ -3169,13 +3191,11 @@ static quint64 findJsExecutionContextAddress(const GdbMi &stackArgsResponse, con
return 0;
}
static QString msgCannotLoadQmlStack(const QString &why)
{
return _("Unable to load QML stack: ") + why;
}
void GdbEngine::handleQmlStackFrameArguments(const DebuggerResponse &response)
void GdbEngine::loadAdditionalQmlStack()
{
// Scan for QV4::ExecutionContext parameter in the parameter list of a V4 call.
DebuggerCommand cmd("-stack-list-arguments --simple-values", NeedsStop);
cmd.callback = [this](const DebuggerResponse &response) {
if (!response.data.isValid()) {
showMessage(msgCannotLoadQmlStack(_("No stack obtained.")), LogError);
return;
@@ -3190,8 +3210,11 @@ void GdbEngine::handleQmlStackFrameArguments(const DebuggerResponse &response)
+ QByteArray::number(contextAddress, 16) + ")\"";
cmd.callback = CB(handleQmlStackTrace);
runCommand(cmd);
};
runCommand(cmd);
}
// Scan the arguments of a stack list for the address of a QV4::ExecutionContext.
void GdbEngine::handleQmlStackTrace(const DebuggerResponse &response)
{
if (!response.data.isValid()) {
@@ -3216,11 +3239,8 @@ void GdbEngine::handleQmlStackTrace(const DebuggerResponse &response)
}
QList<StackFrame> qmlFrames;
qmlFrames.reserve(qmlFrameCount);
for (int i = 0; i < qmlFrameCount; ++i) {
StackFrame frame = parseStackFrame(stackMi.childAt(i), i);
frame.fixQmlFrame(runParameters());
qmlFrames.append(frame);
}
for (int i = 0; i < qmlFrameCount; ++i)
qmlFrames.append(StackFrame::parseFrame(stackMi.childAt(i), runParameters()));
stackHandler()->prependFrames(qmlFrames);
}
@@ -3228,7 +3248,7 @@ DebuggerCommand GdbEngine::stackCommand(int depth)
{
DebuggerCommand cmd("stackListFrames");
cmd.arg("limit", depth);
cmd.arg("options", isNativeMixedActive() ? "nativemixed" : "");
cmd.arg("nativemixed", isNativeMixedActive());
return cmd;
}
@@ -3241,35 +3261,6 @@ void GdbEngine::reloadStack()
runCommand(cmd);
}
StackFrame GdbEngine::parseStackFrame(const GdbMi &frameMi, int level)
{
//qDebug() << "HANDLING FRAME:" << frameMi.toString();
StackFrame frame;
frame.level = level;
GdbMi fullName = frameMi["fullname"];
if (fullName.isValid())
frame.file = cleanupFullName(QFile::decodeName(fullName.data()));
else
frame.file = QFile::decodeName(frameMi["file"].data());
frame.function = _(frameMi["func"].data());
frame.from = _(frameMi["from"].data());
frame.line = frameMi["line"].toInt();
frame.address = frameMi["addr"].toAddress();
GdbMi usable = frameMi["usable"];
if (usable.isValid())
frame.usable = usable.data().toInt();
else
frame.usable = QFileInfo(frame.file).isReadable();
if (frameMi["language"].data() == "js"
|| frame.file.endsWith(QLatin1String(".js"))
|| frame.file.endsWith(QLatin1String(".qml"))) {
frame.file = QFile::decodeName(frameMi["file"].data());
frame.language = QmlLanguage;
frame.fixQmlFrame(runParameters());
}
return frame;
}
void GdbEngine::handleStackListFrames(const DebuggerResponse &response, bool isFull)
{
if (response.resultClass != ResultDone) {
@@ -3284,8 +3275,10 @@ void GdbEngine::handleStackListFrames(const DebuggerResponse &response, bool isF
QList<StackFrame> stackFrames;
GdbMi stack = response.data["stack"]; // C++
if (!stack.isValid() || stack.childCount() == 0) // Mixed.
if (!stack.isValid() || stack.childCount() == 0) { // Mixed.
stack.fromStringMultiple(response.consoleStreamOutput);
stack = stack["frames"];
}
if (!stack.isValid()) {
qDebug() << "FIXME: stack:" << stack.toString();
@@ -3296,7 +3289,7 @@ void GdbEngine::handleStackListFrames(const DebuggerResponse &response, bool isF
int n = stack.childCount();
for (int i = 0; i != n; ++i) {
stackFrames.append(parseStackFrame(stack.childAt(i), i));
stackFrames.append(StackFrame::parseFrame(stack.childAt(i), runParameters()));
const StackFrame &frame = stackFrames.back();
// Initialize top frame to the first valid frame.
@@ -3407,7 +3400,7 @@ void GdbEngine::handleThreadNames(const DebuggerResponse &response)
ThreadData thread;
thread.id = ThreadId(name["id"].toInt());
thread.name = decodeData(name["value"].data(),
DebuggerEncoding(name["valueencoded"].toInt()));
debuggerEncoding(name["valueencoded"].data()));
handler->updateThread(thread);
}
updateViews();
@@ -4622,7 +4615,7 @@ void GdbEngine::doUpdateLocals(const UpdateParameters &params)
watchHandler()->notifyUpdateStarted(params.partialVariables());
DebuggerCommand cmd("showData", Discardable | InUpdateLocals | PythonCommand);
DebuggerCommand cmd("fetchVariables", Discardable|InUpdateLocals|PythonCommand);
watchHandler()->appendFormatRequests(&cmd);
watchHandler()->appendWatchersAndTooltipRequests(&cmd);
@@ -4637,23 +4630,20 @@ void GdbEngine::doUpdateLocals(const UpdateParameters &params)
cmd.arg("dyntype", boolSetting(UseDynamicType));
cmd.arg("nativemixed", isNativeMixedActive());
if (isNativeMixedActive()) {
StackFrame frame = stackHandler()->currentFrame();
if (frame.language == QmlLanguage)
cmd.arg("qmlcontext", "0x" + QByteArray::number(frame.address, 16));
}
cmd.arg("context", frame.context);
cmd.arg("resultvarname", m_resultVarName);
cmd.arg("partialVariable", params.partialVariable);
cmd.arg("sortStructMembers", boolSetting(SortStructMembers));
cmd.callback = CB(handleStackFrame);
cmd.callback = CB(handleFetchVariables);
runCommand(cmd);
cmd.arg("passExceptions", true);
m_lastDebuggableCommand = cmd;
}
void GdbEngine::handleStackFrame(const DebuggerResponse &response)
void GdbEngine::handleFetchVariables(const DebuggerResponse &response)
{
m_inUpdateLocals = false;

View File

@@ -216,7 +216,6 @@ protected:
void handleStop1(const GdbMi &data);
void handleStop2(const GdbMi &data);
Q_SLOT void handleStop2();
StackFrame parseStackFrame(const GdbMi &mi, int level);
void resetCommandQueue();
bool isSynchronous() const override { return true; }
@@ -367,7 +366,6 @@ protected:
Q_SLOT void reloadStack();
Q_SLOT virtual void reloadFullStack() override;
virtual void loadAdditionalQmlStack() override;
void handleQmlStackFrameArguments(const DebuggerResponse &response);
void handleQmlStackTrace(const DebuggerResponse &response);
int currentFrame() const;
@@ -399,7 +397,7 @@ protected:
Q_SLOT void createFullBacktrace();
void doUpdateLocals(const UpdateParameters &parameters) override;
void handleStackFrame(const DebuggerResponse &response);
void handleFetchVariables(const DebuggerResponse &response);
void setLocals(const QList<GdbMi> &locals);

View File

@@ -537,7 +537,7 @@ void LldbEngine::selectThread(ThreadId threadId)
cmd.arg("id", threadId.raw());
cmd.callback = [this](const DebuggerResponse &) {
DebuggerCommand cmd("fetchStack");
cmd.arg("nativeMixed", isNativeMixedActive());
cmd.arg("nativemixed", isNativeMixedActive());
cmd.arg("stacklimit", action(MaximalStackDepth)->value().toInt());
runCommand(cmd);
updateLocals();
@@ -563,10 +563,9 @@ bool LldbEngine::acceptsBreakpoint(Breakpoint bp) const
{
if (runParameters().startMode == AttachCore)
return false;
// We handle QML breakpoint unless specifically disabled.
if (isNativeMixedEnabled() && !(runParameters().languages & QmlLanguage))
if (bp.parameters().isCppBreakpoint())
return true;
return bp.parameters().isCppBreakpoint();
return isNativeMixedEnabled();
}
void LldbEngine::insertBreakpoint(Breakpoint bp)
@@ -753,37 +752,12 @@ void LldbEngine::reloadFullStack()
void LldbEngine::fetchStack(int limit)
{
DebuggerCommand cmd("fetchStack");
cmd.arg("nativeMixed", isNativeMixedActive());
cmd.arg("nativemixed", isNativeMixedActive());
cmd.arg("stacklimit", limit);
cmd.arg("context", stackHandler()->currentFrame().context);
cmd.callback = [this](const DebuggerResponse &response) {
const GdbMi &stack = response.data["stack"];
StackHandler *handler = stackHandler();
StackFrames frames;
foreach (const GdbMi &item, stack["frames"].children()) {
StackFrame frame;
frame.level = item["level"].toInt();
frame.file = item["file"].toUtf8();
frame.function = item["func"].toUtf8();
frame.from = item["func"].toUtf8();
frame.line = item["line"].toInt();
frame.address = item["addr"].toAddress();
GdbMi usable = item["usable"];
if (usable.isValid())
frame.usable = usable.data().toInt();
else
frame.usable = QFileInfo(frame.file).isReadable();
if (item["language"].data() == "js"
|| frame.file.endsWith(QLatin1String(".js"))
|| frame.file.endsWith(QLatin1String(".qml"))) {
frame.language = QmlLanguage;
frame.fixQmlFrame(runParameters());
}
frames.append(frame);
}
bool canExpand = stack["hasmore"].toInt();
action(ExpandStack)->setEnabled(canExpand);
handler->setFrames(frames, canExpand);
stackHandler()->setAllFrames(stack["frames"], stack["hasmore"].toInt());
updateLocals();
};
runCommand(cmd);
@@ -816,7 +790,7 @@ void LldbEngine::doUpdateLocals(const UpdateParameters &params)
watchHandler()->notifyUpdateStarted(params.partialVariables());
DebuggerCommand cmd("fetchLocals");
cmd.arg("nativeMixed", isNativeMixedActive());
cmd.arg("nativemixed", isNativeMixedActive());
watchHandler()->appendFormatRequests(&cmd);
watchHandler()->appendWatchersAndTooltipRequests(&cmd);

View File

@@ -532,12 +532,12 @@ void PdbEngine::refreshStack(const GdbMi &stack)
StackFrames frames;
foreach (const GdbMi &item, stack["frames"].children()) {
StackFrame frame;
frame.level = item["level"].toInt();
frame.level = item["level"].data();
frame.file = item["file"].toUtf8();
frame.function = item["func"].toUtf8();
frame.from = item["func"].toUtf8();
frame.function = item["function"].toUtf8();
frame.module = item["function"].toUtf8();
frame.line = item["line"].toInt();
frame.address = item["addr"].toAddress();
frame.address = item["address"].toAddress();
GdbMi usable = item["usable"];
if (usable.isValid())
frame.usable = usable.data().toInt();

View File

@@ -2062,10 +2062,9 @@ void QmlEnginePrivate::handleBacktrace(const QVariantMap &response)
stackIndexLookup.clear();
foreach (const QVariant &frame, frames) {
StackFrame stackFrame = extractStackFrame(frame);
if (stackFrame.level < 0)
if (stackFrame.level.isEmpty())
continue;
stackIndexLookup.insert(i, stackFrame.level);
stackFrame.level = i;
stackIndexLookup.insert(i, stackFrame.level.toInt());
stackFrames << stackFrame;
i++;
}
@@ -2111,10 +2110,10 @@ StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal)
const QVariantMap body = bodyVal.toMap();
StackFrame stackFrame;
stackFrame.level = body.value(_("index")).toInt();
stackFrame.level = body.value(_("index")).toByteArray();
//Do not insert the frame corresponding to the internal function
if (body.value(QLatin1String("sourceLineText")) == QLatin1String(INTERNAL_FUNCTION)) {
stackFrame.level = -1;
stackFrame.level.clear();
return stackFrame;
}
@@ -2129,7 +2128,7 @@ StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal)
stackFrame.usable = QFileInfo(stackFrame.file).isReadable();
objectData = extractData(body.value(_("receiver")));
stackFrame.to = objectData.value.toString();
stackFrame.receiver = objectData.value.toString();
stackFrame.line = body.value(_("line")).toInt() + 1;

View File

@@ -31,6 +31,7 @@
#include "stackframe.h"
#include "debuggerengine.h"
#include "debuggerprotocol.h"
#include "watchutils.h"
#include <QDebug>
@@ -49,16 +50,16 @@ namespace Internal {
////////////////////////////////////////////////////////////////////////
StackFrame::StackFrame()
: language(CppLanguage), level(-1), line(-1), address(0), usable(false)
: language(CppLanguage), line(-1), address(0), usable(false)
{}
void StackFrame::clear()
{
line = level = -1;
line = -1;
function.clear();
file.clear();
from.clear();
to.clear();
module.clear();
receiver.clear();
address = 0;
}
@@ -79,11 +80,44 @@ QString StackFrame::toString() const
<< tr("Function:") << ' ' << function << ' '
<< tr("File:") << ' ' << file << ' '
<< tr("Line:") << ' ' << line << ' '
<< tr("From:") << ' ' << from << ' '
<< tr("To:") << ' ' << to;
<< tr("From:") << ' ' << module << ' '
<< tr("To:") << ' ' << receiver;
return res;
}
QList<StackFrame> StackFrame::parseFrames(const GdbMi &data, const DebuggerRunParameters &rp)
{
StackFrames frames;
frames.reserve(data.children().size());
foreach (const GdbMi &item, data.children())
frames.append(parseFrame(item, rp));
return frames;
}
StackFrame StackFrame::parseFrame(const GdbMi &frameMi, const DebuggerRunParameters &rp)
{
StackFrame frame;
frame.level = frameMi["level"].data();
frame.function = frameMi["function"].toUtf8();
frame.module = frameMi["module"].toUtf8();
frame.file = QFile::decodeName(frameMi["file"].data());
frame.line = frameMi["line"].toInt();
frame.address = frameMi["address"].toAddress();
frame.context = frameMi["context"].data();
if (frameMi["language"].data() == "js"
|| frame.file.endsWith(QLatin1String(".js"))
|| frame.file.endsWith(QLatin1String(".qml"))) {
frame.language = QmlLanguage;
frame.fixQrcFrame(rp);
}
GdbMi usable = frameMi["usable"];
if (usable.isValid())
frame.usable = usable.data().toInt();
else
frame.usable = QFileInfo(frame.file).isReadable();
return frame;
}
QString StackFrame::toToolTip() const
{
const QString filePath = QDir::toNativeSeparators(file);
@@ -101,10 +135,10 @@ QString StackFrame::toToolTip() const
str << "<tr><td>" << tr("File:") << "</td><td>" << filePath << "</td></tr>";
if (line != -1)
str << "<tr><td>" << tr("Line:") << "</td><td>" << line << "</td></tr>";
if (!from.isEmpty())
str << "<tr><td>" << tr("From:") << "</td><td>" << from << "</td></tr>";
if (!to.isEmpty())
str << "<tr><td>" << tr("To:") << "</td><td>" << to << "</td></tr>";
if (!module.isEmpty())
str << "<tr><td>" << tr("Module:") << "</td><td>" << module << "</td></tr>";
if (!receiver.isEmpty())
str << "<tr><td>" << tr("Receiver:") << "</td><td>" << receiver << "</td></tr>";
str << "</table>";
str <<"<br> <br><i>" << tr("Note:") << " </i> ";
@@ -133,8 +167,8 @@ QString StackFrame::toToolTip() const
return res;
}
// Try to resolve files of a QML stack (resource files).
void StackFrame::fixQmlFrame(const DebuggerRunParameters &rp)
// Try to resolve files coming from resource files.
void StackFrame::fixQrcFrame(const DebuggerRunParameters &rp)
{
if (language != QmlLanguage)
return;
@@ -146,7 +180,8 @@ void StackFrame::fixQmlFrame(const DebuggerRunParameters &rp)
if (!file.startsWith(QLatin1String("qrc:/")))
return;
const QString relativeFile = file.right(file.size() - 5);
if (!rp.projectSourceDirectory.isEmpty()) {
if (rp.projectSourceDirectory.isEmpty())
return;
const QFileInfo pFi(rp.projectSourceDirectory + QLatin1Char('/') + relativeFile);
if (pFi.isFile()) {
file = pFi.absoluteFilePath();
@@ -160,7 +195,6 @@ void StackFrame::fixQmlFrame(const DebuggerRunParameters &rp)
return;
}
}
}
QDebug operator<<(QDebug d, const StackFrame &f)
{
@@ -171,10 +205,10 @@ QDebug operator<<(QDebug d, const StackFrame &f)
str << ' ' << f.function;
if (!f.file.isEmpty())
str << ' ' << f.file << ':' << f.line;
if (!f.from.isEmpty())
str << " from=" << f.from;
if (!f.to.isEmpty())
str << " to=" << f.to;
if (!f.module.isEmpty())
str << " from=" << f.module;
if (!f.receiver.isEmpty())
str << " to=" << f.receiver;
d.nospace() << res;
return d;
}

View File

@@ -44,6 +44,7 @@ namespace Debugger {
namespace Internal {
class DebuggerRunParameters;
class GdbMi;
class StackFrame
{
@@ -53,24 +54,25 @@ public:
bool isUsable() const;
QString toToolTip() const;
QString toString() const;
void fixQmlFrame(const DebuggerRunParameters &rp);
static StackFrame parseFrame(const GdbMi &data, const DebuggerRunParameters &rp);
static QList<StackFrame> parseFrames(const GdbMi &data, const DebuggerRunParameters &rp);
void fixQrcFrame(const DebuggerRunParameters &rp);
public:
DebuggerLanguage language;
qint32 level;
QByteArray level;
QString function;
QString file; // We try to put an absolute file name in there.
QString from; // Sometimes something like "/usr/lib/libstdc++.so.6"
QString to; // Used in ScriptEngine only.
QString module; // Sometimes something like "/usr/lib/libstdc++.so.6"
QString receiver; // Used in ScriptEngine only.
qint32 line;
quint64 address;
bool usable;
QByteArray context; // Opaque value produced and consumed by the native backends.
Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::StackHandler)
};
QDebug operator<<(QDebug d, const StackFrame &frame);
typedef QList<StackFrame> StackFrames;
} // namespace Internal

View File

@@ -32,6 +32,7 @@
#include "debuggeractions.h"
#include "debuggercore.h"
#include "debuggerengine.h"
#include "simplifytype.h"
#include <utils/fileutils.h>
@@ -55,8 +56,9 @@ namespace Internal {
QTreeView.
*/
StackHandler::StackHandler()
: m_positionIcon(QIcon(QLatin1String(":/debugger/images/location_16.png"))),
StackHandler::StackHandler(DebuggerEngine *engine)
: m_engine(engine),
m_positionIcon(QIcon(QLatin1String(":/debugger/images/location_16.png"))),
m_emptyIcon(QIcon(QLatin1String(":/debugger/images/debugger_empty_14.png")))
{
setObjectName(QLatin1String("StackModel"));
@@ -103,11 +105,11 @@ QVariant StackHandler::data(const QModelIndex &index, int role) const
if (role == Qt::DisplayRole) {
switch (index.column()) {
case StackLevelColumn:
return QString::number(frame.level);
return QString::number(index.row() + 1);
case StackFunctionNameColumn:
return simplifyType(frame.function);
case StackFileNameColumn:
return frame.file.isEmpty() ? frame.from : Utils::FileName::fromString(frame.file).fileName();
return frame.file.isEmpty() ? frame.module : Utils::FileName::fromString(frame.file).fileName();
case StackLineNumberColumn:
return frame.line > 0 ? QVariant(frame.line) : QVariant();
case StackAddressColumn:
@@ -166,6 +168,12 @@ StackFrame StackHandler::currentFrame() const
return m_stackFrames.at(m_currentIndex);
}
void StackHandler::setAllFrames(const GdbMi &frames, bool canExpand)
{
action(ExpandStack)->setEnabled(canExpand);
setFrames(StackFrame::parseFrames(frames, m_engine->runParameters()), canExpand);
}
void StackHandler::setCurrentIndex(int level)
{
if (level == -1 || level == m_currentIndex)

View File

@@ -38,6 +38,8 @@
namespace Debugger {
namespace Internal {
class DebuggerEngine;
enum StackColumns
{
StackLevelColumn,
@@ -48,18 +50,12 @@ enum StackColumns
StackColumnCount
};
////////////////////////////////////////////////////////////////////////
//
// StackModel
//
////////////////////////////////////////////////////////////////////////
class StackHandler : public QAbstractTableModel
{
Q_OBJECT
public:
StackHandler();
explicit StackHandler(DebuggerEngine *engine);
~StackHandler();
void setFrames(const StackFrames &frames, bool canExpand = false);
@@ -72,6 +68,7 @@ public:
const StackFrame &frameAt(int index) const { return m_stackFrames.at(index); }
int stackSize() const { return m_stackFrames.size(); }
quint64 topAddress() const { return m_stackFrames.at(0).address; }
void setAllFrames(const GdbMi &frames, bool canExpand);
// Called from StackHandler after a new stack list has been received
void removeAll();
@@ -93,6 +90,7 @@ private:
Qt::ItemFlags flags(const QModelIndex &index) const;
Q_SLOT void resetModel() { beginResetModel(); endResetModel(); }
DebuggerEngine *m_engine;
StackFrames m_stackFrames;
int m_currentIndex;
const QVariant m_positionIcon;

View File

@@ -108,7 +108,7 @@ static inline StackFrame inputFunctionForDisassembly()
return frame;
const int bangPos = function.indexOf(QLatin1Char('!'));
if (bangPos != -1) {
frame.from = function.left(bangPos);
frame.module = function.left(bangPos);
frame.function = function.mid(bangPos + 1);
} else {
frame.function = function;

View File

@@ -392,8 +392,8 @@ void WatchData::updateValue(const GdbMi &item)
{
GdbMi value = item["value"];
if (value.isValid()) {
int encoding = item["valueencoded"].toInt();
setValue(decodeData(value.data(), DebuggerEncoding(encoding)));
DebuggerEncoding encoding = debuggerEncoding(item["valueencoded"].data());
setValue(decodeData(value.data(), encoding));
} else {
setValueNeeded();
}

View File

@@ -1258,7 +1258,8 @@ void tst_Dumpers::dumper()
if (m_debuggerEngine == GdbEngine) {
const QFileInfo gdbBinaryFile(QString::fromLatin1(exe));
const QByteArray uninstalledData = gdbBinaryFile.absolutePath().toLocal8Bit() + "/data-directory/python";
const QByteArray uninstalledData = gdbBinaryFile.absolutePath().toLocal8Bit()
+ "/data-directory/python";
args << QLatin1String("-i")
<< QLatin1String("mi")
@@ -1280,8 +1281,9 @@ void tst_Dumpers::dumper()
"python from gdbbridge import *\n"
"python theDumper.setupDumpers()\n"
"run " + nograb + "\n"
"python theDumper.showData({'fancy':1,'forcens':1,'autoderef':1,"
"'dyntype':1,'passExceptions':1,'expanded':[" + expandedq + "]})\n";
"python theDumper.fetchVariables({'fancy':1,'forcens':1,"
"'autoderef':1,'dyntype':1,'passExceptions':1,"
"'expanded':[" + expandedq + "]})\n";
cmds += "quit\n";