forked from qt-creator/qt-creator
Debugger: Adjust native mixed debugging after upstream changes
Change-Id: I4d137fadd0de2aa346f2f49932faac4ee9ed41e7 Reviewed-by: Ulf Hermann <ulf.hermann@theqtcompany.com> Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
This commit is contained in:
@@ -414,8 +414,6 @@ 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'.
|
||||||
@@ -601,9 +599,6 @@ 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
|
||||||
@@ -834,38 +829,98 @@ class DumperBase:
|
|||||||
self.putSpecialValue(SpecialItemCountValue, count)
|
self.putSpecialValue(SpecialItemCountValue, count)
|
||||||
self.putNumChild(count)
|
self.putNumChild(count)
|
||||||
|
|
||||||
def dictToMi(self, value):
|
def resultToMi(self, value):
|
||||||
if type(value) is bool:
|
if type(value) is bool:
|
||||||
return '"%d"' % int(value)
|
return '"%d"' % int(value)
|
||||||
if type(value) is dict:
|
if type(value) is dict:
|
||||||
return '{' + ','.join(['%s=%s' % (k, self.dictToMi(v))
|
return '{' + ','.join(['%s=%s' % (k, self.resultToMi(v))
|
||||||
for (k, v) in list(value.items())]) + '}'
|
for (k, v) in list(value.items())]) + '}'
|
||||||
|
if type(value) is list:
|
||||||
|
return '[' + ','.join([self.resultToMi(k)
|
||||||
|
for k in list(value.items())]) + ']'
|
||||||
|
return '"%s"' % value
|
||||||
|
|
||||||
|
def variablesToMi(self, value, prefix):
|
||||||
|
if type(value) is bool:
|
||||||
|
return '"%d"' % int(value)
|
||||||
|
if type(value) is dict:
|
||||||
|
pairs = []
|
||||||
|
for (k, v) in list(value.items()):
|
||||||
|
if k == 'iname':
|
||||||
|
if v.startswith('.'):
|
||||||
|
v = '"%s%s"' % (prefix, v)
|
||||||
|
else:
|
||||||
|
v = '"%s"' % v
|
||||||
|
else:
|
||||||
|
v = self.variablesToMi(v, prefix)
|
||||||
|
pairs.append('%s=%s' % (k, v))
|
||||||
|
return '{' + ','.join(pairs) + '}'
|
||||||
if type(value) is list:
|
if type(value) is list:
|
||||||
index = 0
|
index = 0
|
||||||
pairs = []
|
pairs = []
|
||||||
for item in value:
|
for item in value:
|
||||||
|
if item.get('type', '') == 'function':
|
||||||
|
continue
|
||||||
name = item.get('name', '')
|
name = item.get('name', '')
|
||||||
if len(name) == 0:
|
if len(name) == 0:
|
||||||
name = str(index)
|
name = str(index)
|
||||||
index += 1
|
index += 1
|
||||||
pairs.append((name, self.dictToMi(item)))
|
pairs.append((name, self.variablesToMi(item, prefix)))
|
||||||
pairs.sort(key = lambda pair: pair[0])
|
pairs.sort(key = lambda pair: pair[0])
|
||||||
return '[' + ','.join([pair[1] for pair in pairs]) + ']'
|
return '[' + ','.join([pair[1] for pair in pairs]) + ']'
|
||||||
return '"%s"' % value
|
return '"%s"' % value
|
||||||
|
|
||||||
|
def filterPrefix(self, prefix, items):
|
||||||
|
return [i[len(prefix):] for i in items if i.startswith(prefix)]
|
||||||
|
|
||||||
def tryFetchInterpreterVariables(self, args):
|
def tryFetchInterpreterVariables(self, args):
|
||||||
if not int(args.get('nativemixed', 0)):
|
if not int(args.get('nativemixed', 0)):
|
||||||
return (False, '')
|
return (False, '')
|
||||||
context = args.get('context', '')
|
context = args.get('context', '')
|
||||||
if not len(context):
|
if not len(context):
|
||||||
return (False, '')
|
return (False, '')
|
||||||
res = self.extractInterpreterVariables(args)
|
|
||||||
if res:
|
expanded = args.get('expanded')
|
||||||
return (True, 'data=%s' % self.dictToMi(res.get('data', {})))
|
args['expanded'] = self.filterPrefix('local', expanded)
|
||||||
|
|
||||||
|
res = self.sendInterpreterRequest('variables', args)
|
||||||
|
if not res:
|
||||||
return (False, '')
|
return (False, '')
|
||||||
|
|
||||||
def variableDictToMi(self, res):
|
reslist = []
|
||||||
return self.dictToMi(res.get('data', {}), self.SortStructMembers)
|
for item in res.get('variables', {}):
|
||||||
|
if not 'iname' in item:
|
||||||
|
item['iname'] = '.' + item.get('name')
|
||||||
|
reslist.append(self.variablesToMi(item, 'local'))
|
||||||
|
|
||||||
|
watchers = args.get('watchers', None)
|
||||||
|
if watchers:
|
||||||
|
toevaluate = []
|
||||||
|
name2expr = {}
|
||||||
|
seq = 0
|
||||||
|
for watcher in watchers:
|
||||||
|
expr = self.hexdecode(watcher.get('exp'))
|
||||||
|
name = str(seq)
|
||||||
|
toevaluate.append({'name': name, 'expression': expr})
|
||||||
|
name2expr[name] = expr
|
||||||
|
seq += 1
|
||||||
|
args['expressions'] = toevaluate
|
||||||
|
|
||||||
|
args['expanded'] = self.filterPrefix('watch', expanded)
|
||||||
|
del args['watchers']
|
||||||
|
res = self.sendInterpreterRequest('expressions', args)
|
||||||
|
|
||||||
|
if res:
|
||||||
|
for item in res.get('expressions', {}):
|
||||||
|
name = item.get('name')
|
||||||
|
iname = 'watch.' + name
|
||||||
|
expr = name2expr.get(name)
|
||||||
|
item['iname'] = iname
|
||||||
|
item['wname'] = self.hexencode(expr)
|
||||||
|
item['exp'] = expr
|
||||||
|
reslist.append(self.variablesToMi(item, 'watch'))
|
||||||
|
|
||||||
|
return (True, 'data=[%s]' % ','.join(reslist))
|
||||||
|
|
||||||
def putField(self, name, value):
|
def putField(self, name, value):
|
||||||
self.put('%s="%s",' % (name, value))
|
self.put('%s="%s",' % (name, value))
|
||||||
@@ -1776,38 +1831,86 @@ class DumperBase:
|
|||||||
value = struct.unpack_from("!I", buf, offset)[0]
|
value = struct.unpack_from("!I", buf, offset)[0]
|
||||||
return (value, offset + 4)
|
return (value, offset + 4)
|
||||||
|
|
||||||
def readInterpreterOutput(self):
|
def handleInterpreterMessage(self):
|
||||||
buf = self.parseAndEvaluate("qt_qmlDebugOutputBuffer")
|
|
||||||
size = self.parseAndEvaluate("qt_qmlDebugOutputLength")
|
|
||||||
return self.readJsonFromMemory(buf, size)
|
|
||||||
|
|
||||||
def handleInterpreterEvent(self):
|
|
||||||
""" Return True if inferior stopped """
|
""" Return True if inferior stopped """
|
||||||
buf = self.parseAndEvaluate("qt_qmlDebugEventBuffer")
|
resdict = self.fetchInterpreterResult()
|
||||||
size = self.parseAndEvaluate("qt_qmlDebugEventLength")
|
|
||||||
resdict = self.readJsonFromMemory(buf, size)
|
|
||||||
warn("Interpreter event received: %s" % resdict)
|
|
||||||
return resdict.get('event') == 'break'
|
return resdict.get('event') == 'break'
|
||||||
|
|
||||||
|
def reportInterpreterResult(self, resdict, args):
|
||||||
|
print('interpreterresult=%s,token="%s"'
|
||||||
|
% (self.resultToMi(resdict), args.get('token', -1)))
|
||||||
|
|
||||||
|
def reportInterpreterAsync(self, resdict, asyncclass):
|
||||||
|
print('interpreterasync=%s,asyncclass="%s"'
|
||||||
|
% (self.resultToMi(resdict), asyncclass))
|
||||||
|
|
||||||
def removeInterpreterBreakpoint(self, args):
|
def removeInterpreterBreakpoint(self, args):
|
||||||
res = self.sendInterpreterRequest('removebreakpoint', { 'id' : args['id'] })
|
res = self.sendInterpreterRequest('removebreakpoint', { 'id' : args['id'] })
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def insertInterpreterBreakpoint(self, args):
|
def insertInterpreterBreakpoint(self, args):
|
||||||
args['condition'] = self.hexdecode(args.get('condition', ''))
|
args['condition'] = self.hexdecode(args.get('condition', ''))
|
||||||
warn("Insert interpreter breakpoint %s:%s (%s)"
|
# Will fail if the service is not yet up and running.
|
||||||
% (args['file'], args['line'], args['condition']))
|
response = self.sendInterpreterRequest('setbreakpoint', args)
|
||||||
bp = self.doInsertInterpreterBreakpoint(args, False)
|
resdict = args.copy()
|
||||||
return str(bp)
|
bp = None if response is None else response.get("breakpoint", None)
|
||||||
|
if bp:
|
||||||
|
resdict['number'] = bp
|
||||||
|
resdict['pending'] = 0
|
||||||
|
else:
|
||||||
|
self.createResolvePendingBreakpointsHookBreakpoint(args)
|
||||||
|
resdict['number'] = -1
|
||||||
|
resdict['pending'] = 1
|
||||||
|
resdict['warning'] = 'Direct interpreter breakpoint insertion failed.'
|
||||||
|
self.reportInterpreterResult(resdict, args)
|
||||||
|
|
||||||
|
def resolvePendingInterpreterBreakpoint(self, args):
|
||||||
|
self.parseAndEvaluate('qt_qmlDebugEnableService("NativeQmlDebugger")')
|
||||||
|
response = self.sendInterpreterRequest('setbreakpoint', args)
|
||||||
|
bp = None if response is None else response.get("breakpoint", None)
|
||||||
|
resdict = args.copy()
|
||||||
|
if bp:
|
||||||
|
resdict['number'] = bp
|
||||||
|
resdict['pending'] = 0
|
||||||
|
else:
|
||||||
|
resdict['number'] = -1
|
||||||
|
resdict['pending'] = 0
|
||||||
|
resdict['error'] = 'Pending interpreter breakpoint insertion failed.'
|
||||||
|
self.reportInterpreterAsync(resdict, 'breakpointmodified')
|
||||||
|
|
||||||
|
def fetchInterpreterResult(self):
|
||||||
|
buf = self.parseAndEvaluate("qt_qmlDebugMessageBuffer")
|
||||||
|
size = self.parseAndEvaluate("qt_qmlDebugMessageLength")
|
||||||
|
msg = self.hexdecode(self.readMemory(buf, size))
|
||||||
|
# msg is a sequence of 'servicename<space>msglen<space>msg' items.
|
||||||
|
resdict = {} # Native payload.
|
||||||
|
while len(msg):
|
||||||
|
pos0 = msg.index(' ') # End of service name
|
||||||
|
pos1 = msg.index(' ', pos0 + 1) # End of message length
|
||||||
|
service = msg[0:pos0]
|
||||||
|
msglen = int(msg[pos0+1:pos1])
|
||||||
|
msgend = pos1+1+msglen
|
||||||
|
payload = msg[pos1+1:msgend]
|
||||||
|
msg = msg[msgend:]
|
||||||
|
if service == 'NativeQmlDebugger':
|
||||||
|
try:
|
||||||
|
resdict = json.loads(payload)
|
||||||
|
continue
|
||||||
|
except:
|
||||||
|
warn("Cannot parse native payload: %s" % payload)
|
||||||
|
else:
|
||||||
|
print('interpreteralien=%s'
|
||||||
|
% {'service': service, 'payload': self.hexencode(payload)})
|
||||||
|
try:
|
||||||
|
expr = 'qt_qmlDebugClearBuffer()'
|
||||||
|
res = self.parseAndEvaluate(expr)
|
||||||
|
except RuntimeError as error:
|
||||||
|
warn("Cleaning buffer failed: %s: %s" % (expr, error))
|
||||||
|
|
||||||
|
return resdict
|
||||||
|
|
||||||
def sendInterpreterRequest(self, command, args = {}):
|
def sendInterpreterRequest(self, command, args = {}):
|
||||||
self.interpreterSeq += 1
|
encoded = json.dumps({ 'command': command, 'arguments': args })
|
||||||
encoded = json.dumps({
|
|
||||||
'seq': self.interpreterSeq,
|
|
||||||
'type': 'request',
|
|
||||||
'command': command,
|
|
||||||
'arguments': args
|
|
||||||
})
|
|
||||||
hexdata = self.hexencode(encoded)
|
hexdata = self.hexencode(encoded)
|
||||||
expr = 'qt_qmlDebugSendDataToService("NativeQmlDebugger","%s")' % hexdata
|
expr = 'qt_qmlDebugSendDataToService("NativeQmlDebugger","%s")' % hexdata
|
||||||
try:
|
try:
|
||||||
@@ -1819,21 +1922,10 @@ class DumperBase:
|
|||||||
# Happens with LLDB and 'None' current thread.
|
# Happens with LLDB and 'None' current thread.
|
||||||
warn("Interpreter command failed: %s: %s" % (encoded, error))
|
warn("Interpreter command failed: %s: %s" % (encoded, error))
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
if not res:
|
if not res:
|
||||||
warn("Interpreter command failed: %s " % encoded)
|
warn("Interpreter command failed: %s " % encoded)
|
||||||
return {}
|
return {}
|
||||||
resdict = self.readInterpreterOutput()
|
return self.fetchInterpreterResult()
|
||||||
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):
|
def executeStep(self, args):
|
||||||
if self.nativeMixed:
|
if self.nativeMixed:
|
||||||
@@ -1856,23 +1948,22 @@ class DumperBase:
|
|||||||
self.doContinue()
|
self.doContinue()
|
||||||
|
|
||||||
def doInsertInterpreterBreakpoint(self, args, wasPending):
|
def doInsertInterpreterBreakpoint(self, args, wasPending):
|
||||||
warn("DO INSERT INTERPRETER BREAKPOINT, WAS PENDING: %s" % wasPending)
|
#warn("DO INSERT INTERPRETER BREAKPOINT, WAS PENDING: %s" % wasPending)
|
||||||
# Will fail if the service is not yet up and running.
|
# Will fail if the service is not yet up and running.
|
||||||
response = self.sendInterpreterRequest('setbreakpoint', args)
|
response = self.sendInterpreterRequest('setbreakpoint', args)
|
||||||
bp = None if response is None else response.get("breakpoint", None)
|
bp = None if response is None else response.get("breakpoint", None)
|
||||||
if wasPending:
|
if wasPending:
|
||||||
if not bp:
|
if not bp:
|
||||||
warn("ERROR: Pending interpreter breakpoint insertion failed.")
|
self.reportInterpreterResult({'bpnr': -1, 'pending': 1,
|
||||||
return -1
|
'error': 'Pending interpreter breakpoint insertion failed.'}, args)
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
if not bp:
|
if not bp:
|
||||||
warn("Direct interpreter breakpoint insertion failed.")
|
self.reportInterpreterResult({'bpnr': -1, 'pending': 1,
|
||||||
warn("Make pending.")
|
'warning': 'Direct interpreter breakpoint insertion failed.'}, args)
|
||||||
self.createResolvePendingBreakpointsHookBreakpoint(args)
|
self.createResolvePendingBreakpointsHookBreakpoint(args)
|
||||||
return -1
|
return
|
||||||
|
self.reportInterpreterResult({'bpnr': bp, 'pending': 0}, args)
|
||||||
warn("Resolved interpreter breakpoint: BP: %s" % bp)
|
|
||||||
return int(bp)
|
|
||||||
|
|
||||||
def isInternalInterpreterFrame(self, functionName):
|
def isInternalInterpreterFrame(self, functionName):
|
||||||
if functionName is None:
|
if functionName is None:
|
||||||
@@ -1902,6 +1993,11 @@ class DumperBase:
|
|||||||
def extractInterpreterStack(self):
|
def extractInterpreterStack(self):
|
||||||
return self.sendInterpreterRequest('backtrace', {'limit': 10 })
|
return self.sendInterpreterRequest('backtrace', {'limit': 10 })
|
||||||
|
|
||||||
def extractInterpreterVariables(self, args):
|
def polishWatchers(self, watchers):
|
||||||
return self.sendInterpreterRequest('variables', args)
|
out = []
|
||||||
|
for watcher in watchers:
|
||||||
|
iname = watcher.get('iname')
|
||||||
|
exp = self.hexdecode(watcher.get('exp'))
|
||||||
|
out.append({'iname': iname, 'expression': exp, 'name': exp })
|
||||||
|
return out
|
||||||
|
|
||||||
|
@@ -1603,7 +1603,7 @@ class Dumper(DumperBase):
|
|||||||
objfile = fromNativePath(symtab.objfile.filename)
|
objfile = fromNativePath(symtab.objfile.filename)
|
||||||
fileName = fromNativePath(symtab.fullname())
|
fileName = fromNativePath(symtab.fullname())
|
||||||
|
|
||||||
if self.nativeMixed and functionName == "qt_qmlDebugEventFromService":
|
if self.nativeMixed and functionName == "qt_qmlDebugMessageAvailable":
|
||||||
interpreterStack = self.extractInterpreterStack()
|
interpreterStack = self.extractInterpreterStack()
|
||||||
#print("EXTRACTED INTEPRETER STACK: %s" % interpreterStack)
|
#print("EXTRACTED INTEPRETER STACK: %s" % interpreterStack)
|
||||||
for interpreterFrame in interpreterStack.get('frames', []):
|
for interpreterFrame in interpreterStack.get('frames', []):
|
||||||
@@ -1641,13 +1641,11 @@ class Dumper(DumperBase):
|
|||||||
self.dumper = dumper
|
self.dumper = dumper
|
||||||
self.args = args
|
self.args = args
|
||||||
spec = "qt_qmlDebugConnectorOpen"
|
spec = "qt_qmlDebugConnectorOpen"
|
||||||
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.doInsertInterpreterBreakpoint(args, True)
|
self.dumper.resolvePendingInterpreterBreakpoint(args)
|
||||||
print("Resolving QML breakpoint %s -> %s" % (args, bp))
|
|
||||||
self.enabled = False
|
self.enabled = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -1800,14 +1798,14 @@ registerCommand("threadnames", threadnames)
|
|||||||
#
|
#
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
|
||||||
class QmlEngineEventBreakpoint(gdb.Breakpoint):
|
class InterpreterMessageBreakpoint(gdb.Breakpoint):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
spec = "qt_qmlDebugEventFromService"
|
spec = "qt_qmlDebugMessageAvailable"
|
||||||
super(QmlEngineEventBreakpoint, self).\
|
super(InterpreterMessageBreakpoint, self).\
|
||||||
__init__(spec, gdb.BP_BREAKPOINT, internal=True)
|
__init__(spec, gdb.BP_BREAKPOINT, internal=True)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
print("Interpreter event received.")
|
print("Interpreter event received.")
|
||||||
return theDumper.handleInterpreterEvent()
|
return theDumper.handleInterpreterMessage()
|
||||||
|
|
||||||
QmlEngineEventBreakpoint()
|
InterpreterMessageBreakpoint()
|
||||||
|
@@ -693,7 +693,7 @@ class Dumper(DumperBase):
|
|||||||
|
|
||||||
if self.nativeMixed:
|
if self.nativeMixed:
|
||||||
self.interpreterEventBreakpoint = \
|
self.interpreterEventBreakpoint = \
|
||||||
self.target.BreakpointCreateByName("qt_qmlDebugEventFromService")
|
self.target.BreakpointCreateByName("qt_qmlDebugMessageAvailable")
|
||||||
|
|
||||||
state = 1 if self.target.IsValid() else 0
|
state = 1 if self.target.IsValid() else 0
|
||||||
self.reportResult('success="%s",msg="%s",exe="%s"' % (state, error, self.executable_), args)
|
self.reportResult('success="%s",msg="%s",exe="%s"' % (state, error, self.executable_), args)
|
||||||
@@ -871,7 +871,7 @@ class Dumper(DumperBase):
|
|||||||
|
|
||||||
functionName = frame.GetFunctionName()
|
functionName = frame.GetFunctionName()
|
||||||
|
|
||||||
if isNativeMixed and functionName == "::qt_qmlDebugEventFromService()":
|
if isNativeMixed and functionName == "::qt_qmlDebugMessageAvailable()":
|
||||||
interpreterStack = self.extractInterpreterStack()
|
interpreterStack = self.extractInterpreterStack()
|
||||||
for interpreterFrame in interpreterStack.get('frames', []):
|
for interpreterFrame in interpreterStack.get('frames', []):
|
||||||
function = interpreterFrame.get('function', '')
|
function = interpreterFrame.get('function', '')
|
||||||
@@ -1341,9 +1341,9 @@ class Dumper(DumperBase):
|
|||||||
self.reportState("inferiorstopok")
|
self.reportState("inferiorstopok")
|
||||||
self.process.Continue();
|
self.process.Continue();
|
||||||
return
|
return
|
||||||
if functionName == "::qt_qmlDebugEventFromService()":
|
if functionName == "::qt_qmlDebugMessageAvailable()":
|
||||||
self.report("EVENT FROM SERVICE")
|
self.report("ASYNC MESSAGE FROM SERVICE")
|
||||||
res = self.handleInterpreterEvent()
|
res = self.handleInterpreterMessage()
|
||||||
if not res:
|
if not res:
|
||||||
self.report("EVENT NEEDS NO STOP")
|
self.report("EVENT NEEDS NO STOP")
|
||||||
self.reportState("stopped")
|
self.reportState("stopped")
|
||||||
@@ -1427,7 +1427,7 @@ class Dumper(DumperBase):
|
|||||||
if bpType == BreakpointByFileAndLine:
|
if bpType == BreakpointByFileAndLine:
|
||||||
fileName = args["file"]
|
fileName = args["file"]
|
||||||
if fileName.endswith(".js") or fileName.endswith(".qml"):
|
if fileName.endswith(".js") or fileName.endswith(".qml"):
|
||||||
self.doInsertInterpreterBreakpoint(args, False)
|
self.insertInterpreterBreakpoint(args)
|
||||||
return
|
return
|
||||||
|
|
||||||
extra = ''
|
extra = ''
|
||||||
@@ -1742,7 +1742,7 @@ class Dumper(DumperBase):
|
|||||||
bp = self.target.BreakpointCreateByName("qt_qmlDebugConnectorOpen")
|
bp = self.target.BreakpointCreateByName("qt_qmlDebugConnectorOpen")
|
||||||
bp.SetOneShot(True)
|
bp.SetOneShot(True)
|
||||||
self.interpreterBreakpointResolvers.append(
|
self.interpreterBreakpointResolvers.append(
|
||||||
lambda: self.doInsertInterpreterBreakpoint(args, True))
|
lambda: self.resolvePendingInterpreterBreakpoint(args))
|
||||||
|
|
||||||
|
|
||||||
# Used in dumper auto test.
|
# Used in dumper auto test.
|
||||||
|
@@ -2308,7 +2308,7 @@ def qdump__QV4__String(d, value):
|
|||||||
d.putStringValue(d.addressOf(value) + 2 * d.ptrSize())
|
d.putStringValue(d.addressOf(value) + 2 * d.ptrSize())
|
||||||
|
|
||||||
def qdump__QV4__Value(d, value):
|
def qdump__QV4__Value(d, value):
|
||||||
v = toInteger(str(value["val"]))
|
v = toInteger(str(value["_val"]))
|
||||||
NaNEncodeMask = 0xffff800000000000
|
NaNEncodeMask = 0xffff800000000000
|
||||||
IsInt32Mask = 0x0002000000000000
|
IsInt32Mask = 0x0002000000000000
|
||||||
IsDoubleMask = 0xfffc000000000000
|
IsDoubleMask = 0xfffc000000000000
|
||||||
@@ -2319,7 +2319,10 @@ def qdump__QV4__Value(d, value):
|
|||||||
ns = d.qtNamespace()
|
ns = d.qtNamespace()
|
||||||
if v & IsInt32Mask:
|
if v & IsInt32Mask:
|
||||||
d.putBetterType("%sQV4::Value (int32)" % ns)
|
d.putBetterType("%sQV4::Value (int32)" % ns)
|
||||||
d.putValue(value["int_32"])
|
vv = v & 0xffffffff
|
||||||
|
vv = vv if vv < 0x80000000 else -(0x100000000 - vv)
|
||||||
|
d.putBetterType("%sQV4::Value (int32)" % ns)
|
||||||
|
d.putValue("%d" % vv)
|
||||||
elif v & IsDoubleMask:
|
elif v & IsDoubleMask:
|
||||||
d.putBetterType("%sQV4::Value (double)" % ns)
|
d.putBetterType("%sQV4::Value (double)" % ns)
|
||||||
d.putValue("%x" % (v ^ 0xffff800000000000), Hex2EncodedFloat8)
|
d.putValue("%x" % (v ^ 0xffff800000000000), Hex2EncodedFloat8)
|
||||||
@@ -2332,6 +2335,7 @@ def qdump__QV4__Value(d, value):
|
|||||||
elif v & IsNullOrBooleanMask:
|
elif v & IsNullOrBooleanMask:
|
||||||
d.putBetterType("%sQV4::Value (null/bool)" % ns)
|
d.putBetterType("%sQV4::Value (null/bool)" % ns)
|
||||||
d.putValue("(null/bool)")
|
d.putValue("(null/bool)")
|
||||||
|
d.putValue(v & 1)
|
||||||
else:
|
else:
|
||||||
vtable = value["m"]["vtable"]
|
vtable = value["m"]["vtable"]
|
||||||
if toInteger(vtable["isString"]):
|
if toInteger(vtable["isString"]):
|
||||||
|
@@ -1470,6 +1470,8 @@ QString BreakpointItem::toToolTip() const
|
|||||||
<< "</td><td>" << QDir::toNativeSeparators(markerFileName()) << "</td></tr>"
|
<< "</td><td>" << QDir::toNativeSeparators(markerFileName()) << "</td></tr>"
|
||||||
<< "<tr><td>" << tr("Marker Line:")
|
<< "<tr><td>" << tr("Marker Line:")
|
||||||
<< "</td><td>" << markerLineNumber() << "</td></tr>"
|
<< "</td><td>" << markerLineNumber() << "</td></tr>"
|
||||||
|
<< "<tr><td>" << tr("Hit Count:")
|
||||||
|
<< "</td><td>" << m_response.hitCount << "</td></tr>"
|
||||||
<< "</table><br><hr><table>"
|
<< "</table><br><hr><table>"
|
||||||
<< "<tr><th>" << tr("Property")
|
<< "<tr><th>" << tr("Property")
|
||||||
<< "</th><th>" << tr("Requested")
|
<< "</th><th>" << tr("Requested")
|
||||||
|
@@ -877,6 +877,8 @@ DebuggerEncoding debuggerEncoding(const QByteArray &data)
|
|||||||
return SpecialNullValue;
|
return SpecialNullValue;
|
||||||
if (data == "itemcount")
|
if (data == "itemcount")
|
||||||
return SpecialItemCountValue;
|
return SpecialItemCountValue;
|
||||||
|
if (data == "notaccessible")
|
||||||
|
return SpecialNotAccessibleValue;
|
||||||
return DebuggerEncoding(data.toInt());
|
return DebuggerEncoding(data.toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -437,206 +437,34 @@ void GdbEngine::handleResponse(const QByteArray &buff)
|
|||||||
result.m_type = GdbMi::Tuple;
|
result.m_type = GdbMi::Tuple;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (asyncClass == "stopped") {
|
handleAsyncOutput(asyncClass, result);
|
||||||
if (m_inUpdateLocals) {
|
|
||||||
showMessage(_("UNEXPECTED *stopped NOTIFICATION IGNORED"), LogWarning);
|
|
||||||
} else {
|
|
||||||
handleStopResponse(result);
|
|
||||||
m_pendingLogStreamOutput.clear();
|
|
||||||
m_pendingConsoleStreamOutput.clear();
|
|
||||||
}
|
|
||||||
} else if (asyncClass == "running") {
|
|
||||||
if (m_inUpdateLocals) {
|
|
||||||
showMessage(_("UNEXPECTED *running NOTIFICATION IGNORED"), LogWarning);
|
|
||||||
} else {
|
|
||||||
GdbMi threads = result["thread-id"];
|
|
||||||
threadsHandler()->notifyRunning(threads.data());
|
|
||||||
if (state() == InferiorRunOk || state() == InferiorSetupRequested) {
|
|
||||||
// We get multiple *running after thread creation and in Windows terminals.
|
|
||||||
showMessage(QString::fromLatin1("NOTE: INFERIOR STILL RUNNING IN STATE %1.").
|
|
||||||
arg(QLatin1String(DebuggerEngine::stateName(state()))));
|
|
||||||
} else if (HostOsInfo::isWindowsHost() && (state() == InferiorStopRequested
|
|
||||||
|| state() == InferiorShutdownRequested)) {
|
|
||||||
// FIXME: Breakpoints on Windows are exceptions which are thrown in newly
|
|
||||||
// created threads so we have to filter out the running threads messages when
|
|
||||||
// we request a stop.
|
|
||||||
} else {
|
|
||||||
notifyInferiorRunOk();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (asyncClass == "library-loaded") {
|
|
||||||
// Archer has 'id="/usr/lib/libdrm.so.2",
|
|
||||||
// target-name="/usr/lib/libdrm.so.2",
|
|
||||||
// host-name="/usr/lib/libdrm.so.2",
|
|
||||||
// symbols-loaded="0"
|
|
||||||
|
|
||||||
// id="/lib/i386-linux-gnu/libc.so.6"
|
|
||||||
// target-name="/lib/i386-linux-gnu/libc.so.6"
|
|
||||||
// host-name="/lib/i386-linux-gnu/libc.so.6"
|
|
||||||
// symbols-loaded="0",thread-group="i1"
|
|
||||||
QByteArray id = result["id"].data();
|
|
||||||
if (!id.isEmpty())
|
|
||||||
showStatusMessage(tr("Library %1 loaded").arg(_(id)), 1000);
|
|
||||||
progressPing();
|
|
||||||
Module module;
|
|
||||||
module.startAddress = 0;
|
|
||||||
module.endAddress = 0;
|
|
||||||
module.hostPath = _(result["host-name"].data());
|
|
||||||
module.modulePath = _(result["target-name"].data());
|
|
||||||
module.moduleName = QFileInfo(module.hostPath).baseName();
|
|
||||||
modulesHandler()->updateModule(module);
|
|
||||||
} else if (asyncClass == "library-unloaded") {
|
|
||||||
// Archer has 'id="/usr/lib/libdrm.so.2",
|
|
||||||
// target-name="/usr/lib/libdrm.so.2",
|
|
||||||
// host-name="/usr/lib/libdrm.so.2"
|
|
||||||
QByteArray id = result["id"].data();
|
|
||||||
progressPing();
|
|
||||||
showStatusMessage(tr("Library %1 unloaded").arg(_(id)), 1000);
|
|
||||||
} else if (asyncClass == "thread-group-added") {
|
|
||||||
// 7.1-symbianelf has "{id="i1"}"
|
|
||||||
} else if (asyncClass == "thread-group-created"
|
|
||||||
|| asyncClass == "thread-group-started") {
|
|
||||||
// Archer had only "{id="28902"}" at some point of 6.8.x.
|
|
||||||
// *-started seems to be standard in 7.1, but in early
|
|
||||||
// 7.0.x, there was a *-created instead.
|
|
||||||
progressPing();
|
|
||||||
// 7.1.50 has thread-group-started,id="i1",pid="3529"
|
|
||||||
QByteArray id = result["id"].data();
|
|
||||||
showStatusMessage(tr("Thread group %1 created").arg(_(id)), 1000);
|
|
||||||
int pid = id.toInt();
|
|
||||||
if (!pid) {
|
|
||||||
id = result["pid"].data();
|
|
||||||
pid = id.toInt();
|
|
||||||
}
|
|
||||||
if (pid)
|
|
||||||
notifyInferiorPid(pid);
|
|
||||||
handleThreadGroupCreated(result);
|
|
||||||
} else if (asyncClass == "thread-created") {
|
|
||||||
//"{id="1",group-id="28902"}"
|
|
||||||
QByteArray id = result["id"].data();
|
|
||||||
showStatusMessage(tr("Thread %1 created").arg(_(id)), 1000);
|
|
||||||
ThreadData thread;
|
|
||||||
thread.id = ThreadId(id.toLong());
|
|
||||||
thread.groupId = result["group-id"].data();
|
|
||||||
threadsHandler()->updateThread(thread);
|
|
||||||
} else if (asyncClass == "thread-group-exited") {
|
|
||||||
// Archer has "{id="28902"}"
|
|
||||||
QByteArray id = result["id"].data();
|
|
||||||
showStatusMessage(tr("Thread group %1 exited").arg(_(id)), 1000);
|
|
||||||
handleThreadGroupExited(result);
|
|
||||||
} else if (asyncClass == "thread-exited") {
|
|
||||||
//"{id="1",group-id="28902"}"
|
|
||||||
QByteArray id = result["id"].data();
|
|
||||||
QByteArray groupid = result["group-id"].data();
|
|
||||||
showStatusMessage(tr("Thread %1 in group %2 exited")
|
|
||||||
.arg(_(id)).arg(_(groupid)), 1000);
|
|
||||||
threadsHandler()->removeThread(ThreadId(id.toLong()));
|
|
||||||
} else if (asyncClass == "thread-selected") {
|
|
||||||
QByteArray id = result["id"].data();
|
|
||||||
showStatusMessage(tr("Thread %1 selected").arg(_(id)), 1000);
|
|
||||||
//"{id="2"}"
|
|
||||||
} else if (asyncClass == "breakpoint-modified") {
|
|
||||||
// New in FSF gdb since 2011-04-27.
|
|
||||||
// "{bkpt={number="3",type="breakpoint",disp="keep",
|
|
||||||
// enabled="y",addr="<MULTIPLE>",times="1",
|
|
||||||
// original-location="\\",simple_gdbtest_app.cpp\\":135"},
|
|
||||||
// {number="3.1",enabled="y",addr="0x0805ff68",
|
|
||||||
// func="Vector<int>::Vector(int)",
|
|
||||||
// file="simple_gdbtest_app.cpp",
|
|
||||||
// fullname="/data/...line="135"},{number="3.2"...}}.."
|
|
||||||
|
|
||||||
// Note the leading comma in original-location. Filter it out.
|
|
||||||
// We don't need the field anyway.
|
|
||||||
QByteArray ba = result.toString();
|
|
||||||
ba = '[' + ba.mid(6, ba.size() - 7) + ']';
|
|
||||||
const int pos1 = ba.indexOf(",original-location");
|
|
||||||
const int pos2 = ba.indexOf("\":", pos1 + 2);
|
|
||||||
const int pos3 = ba.indexOf('"', pos2 + 2);
|
|
||||||
ba.remove(pos1, pos3 - pos1 + 1);
|
|
||||||
result = GdbMi();
|
|
||||||
result.fromString(ba);
|
|
||||||
BreakHandler *handler = breakHandler();
|
|
||||||
Breakpoint bp;
|
|
||||||
BreakpointResponse br;
|
|
||||||
foreach (const GdbMi &bkpt, result.children()) {
|
|
||||||
const QByteArray nr = bkpt["number"].data();
|
|
||||||
BreakpointResponseId rid(nr);
|
|
||||||
if (!isHiddenBreakpoint(rid)) {
|
|
||||||
if (nr.contains('.')) {
|
|
||||||
// A sub-breakpoint.
|
|
||||||
BreakpointResponse sub;
|
|
||||||
updateResponse(sub, bkpt);
|
|
||||||
sub.id = rid;
|
|
||||||
sub.type = br.type;
|
|
||||||
bp.insertSubBreakpoint(sub);
|
|
||||||
} else {
|
|
||||||
// A primary breakpoint.
|
|
||||||
bp = handler->findBreakpointByResponseId(rid);
|
|
||||||
//qDebug() << "NR: " << nr << "RID: " << rid
|
|
||||||
// << "ID: " << bp.id();
|
|
||||||
br = bp.response();
|
|
||||||
updateResponse(br, bkpt);
|
|
||||||
bp.setResponse(br);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (asyncClass == "breakpoint-created") {
|
|
||||||
// "{bkpt={number="1",type="breakpoint",disp="del",enabled="y",
|
|
||||||
// addr="<PENDING>",pending="main",times="0",
|
|
||||||
// original-location="main"}}" -- or --
|
|
||||||
// {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y",
|
|
||||||
// what="*0xbfffed48",times="0",original-location="*0xbfffed48"}}
|
|
||||||
BreakHandler *handler = breakHandler();
|
|
||||||
foreach (const GdbMi &bkpt, result.children()) {
|
|
||||||
BreakpointResponse br;
|
|
||||||
br.type = BreakpointByFileAndLine;
|
|
||||||
updateResponse(br, bkpt);
|
|
||||||
handler->handleAlienBreakpoint(br, this);
|
|
||||||
}
|
|
||||||
} else if (asyncClass == "breakpoint-deleted") {
|
|
||||||
// "breakpoint-deleted" "{id="1"}"
|
|
||||||
// New in FSF gdb since 2011-04-27.
|
|
||||||
QByteArray nr = result["id"].data();
|
|
||||||
BreakpointResponseId rid(nr);
|
|
||||||
if (Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid)) {
|
|
||||||
// This also triggers when a temporary breakpoint is hit.
|
|
||||||
// We do not really want that, as this loses all information.
|
|
||||||
// FIXME: Use a special marker for this case?
|
|
||||||
// if (!bp.isOneShot()) ... is not sufficient.
|
|
||||||
// It keeps temporary "Jump" breakpoints alive.
|
|
||||||
bp.removeAlienBreakpoint();
|
|
||||||
}
|
|
||||||
} else if (asyncClass == "cmd-param-changed") {
|
|
||||||
// New since 2012-08-09
|
|
||||||
// "{param="debug remote",value="1"}"
|
|
||||||
} else if (asyncClass == "memory-changed") {
|
|
||||||
// New since 2013
|
|
||||||
// "{thread-group="i1",addr="0x0918a7a8",len="0x10"}"
|
|
||||||
} else if (asyncClass == "tsv-created") {
|
|
||||||
// New since 2013-02-06
|
|
||||||
} else if (asyncClass == "tsv-modified") {
|
|
||||||
// New since 2013-02-06
|
|
||||||
} else {
|
|
||||||
qDebug() << "IGNORED ASYNC OUTPUT"
|
|
||||||
<< asyncClass << result.toString();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case '~': {
|
case '~': {
|
||||||
QByteArray data = GdbMi::parseCString(from, to);
|
QByteArray data = GdbMi::parseCString(from, to);
|
||||||
if (data.startsWith("bridgemessage={")) {
|
if (data.startsWith("bridgemessage={")) {
|
||||||
//showMessage(_(data), LogDebug);
|
// It's already logged.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (data.startsWith("bridgeresult={")) {
|
if (data.startsWith("interpreterresult={")) {
|
||||||
//showMessage(_(data), LogDebug);
|
GdbMi allData;
|
||||||
|
allData.fromStringMultiple(data);
|
||||||
DebuggerResponse response;
|
DebuggerResponse response;
|
||||||
response.resultClass = ResultDone;
|
response.resultClass = ResultDone;
|
||||||
response.data.fromStringMultiple(data);
|
response.data = allData["interpreterresult"];
|
||||||
|
response.token = allData["token"].toInt();
|
||||||
handleResultRecord(&response);
|
handleResultRecord(&response);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (data.startsWith("interpreterasync={")) {
|
||||||
|
GdbMi allData;
|
||||||
|
allData.fromStringMultiple(data);
|
||||||
|
QByteArray asyncClass = allData["asyncclass"].data();
|
||||||
|
if (asyncClass == "breakpointmodified")
|
||||||
|
handleInterpreterBreakpointModified(allData["interpreterasync"]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
m_pendingConsoleStreamOutput += data;
|
m_pendingConsoleStreamOutput += data;
|
||||||
|
|
||||||
// Parse pid from noise.
|
// Parse pid from noise.
|
||||||
@@ -781,6 +609,191 @@ void GdbEngine::handleResponse(const QByteArray &buff)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GdbEngine::handleAsyncOutput(const QByteArray &asyncClass, const GdbMi &result)
|
||||||
|
{
|
||||||
|
if (asyncClass == "stopped") {
|
||||||
|
if (m_inUpdateLocals) {
|
||||||
|
showMessage(_("UNEXPECTED *stopped NOTIFICATION IGNORED"), LogWarning);
|
||||||
|
} else {
|
||||||
|
handleStopResponse(result);
|
||||||
|
m_pendingLogStreamOutput.clear();
|
||||||
|
m_pendingConsoleStreamOutput.clear();
|
||||||
|
}
|
||||||
|
} else if (asyncClass == "running") {
|
||||||
|
if (m_inUpdateLocals) {
|
||||||
|
showMessage(_("UNEXPECTED *running NOTIFICATION IGNORED"), LogWarning);
|
||||||
|
} else {
|
||||||
|
GdbMi threads = result["thread-id"];
|
||||||
|
threadsHandler()->notifyRunning(threads.data());
|
||||||
|
if (state() == InferiorRunOk || state() == InferiorSetupRequested) {
|
||||||
|
// We get multiple *running after thread creation and in Windows terminals.
|
||||||
|
showMessage(QString::fromLatin1("NOTE: INFERIOR STILL RUNNING IN STATE %1.").
|
||||||
|
arg(QLatin1String(DebuggerEngine::stateName(state()))));
|
||||||
|
} else if (HostOsInfo::isWindowsHost() && (state() == InferiorStopRequested
|
||||||
|
|| state() == InferiorShutdownRequested)) {
|
||||||
|
// FIXME: Breakpoints on Windows are exceptions which are thrown in newly
|
||||||
|
// created threads so we have to filter out the running threads messages when
|
||||||
|
// we request a stop.
|
||||||
|
} else {
|
||||||
|
notifyInferiorRunOk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (asyncClass == "library-loaded") {
|
||||||
|
// Archer has 'id="/usr/lib/libdrm.so.2",
|
||||||
|
// target-name="/usr/lib/libdrm.so.2",
|
||||||
|
// host-name="/usr/lib/libdrm.so.2",
|
||||||
|
// symbols-loaded="0"
|
||||||
|
|
||||||
|
// id="/lib/i386-linux-gnu/libc.so.6"
|
||||||
|
// target-name="/lib/i386-linux-gnu/libc.so.6"
|
||||||
|
// host-name="/lib/i386-linux-gnu/libc.so.6"
|
||||||
|
// symbols-loaded="0",thread-group="i1"
|
||||||
|
QByteArray id = result["id"].data();
|
||||||
|
if (!id.isEmpty())
|
||||||
|
showStatusMessage(tr("Library %1 loaded").arg(_(id)), 1000);
|
||||||
|
progressPing();
|
||||||
|
Module module;
|
||||||
|
module.startAddress = 0;
|
||||||
|
module.endAddress = 0;
|
||||||
|
module.hostPath = _(result["host-name"].data());
|
||||||
|
module.modulePath = _(result["target-name"].data());
|
||||||
|
module.moduleName = QFileInfo(module.hostPath).baseName();
|
||||||
|
modulesHandler()->updateModule(module);
|
||||||
|
} else if (asyncClass == "library-unloaded") {
|
||||||
|
// Archer has 'id="/usr/lib/libdrm.so.2",
|
||||||
|
// target-name="/usr/lib/libdrm.so.2",
|
||||||
|
// host-name="/usr/lib/libdrm.so.2"
|
||||||
|
QByteArray id = result["id"].data();
|
||||||
|
progressPing();
|
||||||
|
showStatusMessage(tr("Library %1 unloaded").arg(_(id)), 1000);
|
||||||
|
} else if (asyncClass == "thread-group-added") {
|
||||||
|
// 7.1-symbianelf has "{id="i1"}"
|
||||||
|
} else if (asyncClass == "thread-group-created"
|
||||||
|
|| asyncClass == "thread-group-started") {
|
||||||
|
// Archer had only "{id="28902"}" at some point of 6.8.x.
|
||||||
|
// *-started seems to be standard in 7.1, but in early
|
||||||
|
// 7.0.x, there was a *-created instead.
|
||||||
|
progressPing();
|
||||||
|
// 7.1.50 has thread-group-started,id="i1",pid="3529"
|
||||||
|
QByteArray id = result["id"].data();
|
||||||
|
showStatusMessage(tr("Thread group %1 created").arg(_(id)), 1000);
|
||||||
|
int pid = id.toInt();
|
||||||
|
if (!pid) {
|
||||||
|
id = result["pid"].data();
|
||||||
|
pid = id.toInt();
|
||||||
|
}
|
||||||
|
if (pid)
|
||||||
|
notifyInferiorPid(pid);
|
||||||
|
handleThreadGroupCreated(result);
|
||||||
|
} else if (asyncClass == "thread-created") {
|
||||||
|
//"{id="1",group-id="28902"}"
|
||||||
|
QByteArray id = result["id"].data();
|
||||||
|
showStatusMessage(tr("Thread %1 created").arg(_(id)), 1000);
|
||||||
|
ThreadData thread;
|
||||||
|
thread.id = ThreadId(id.toLong());
|
||||||
|
thread.groupId = result["group-id"].data();
|
||||||
|
threadsHandler()->updateThread(thread);
|
||||||
|
} else if (asyncClass == "thread-group-exited") {
|
||||||
|
// Archer has "{id="28902"}"
|
||||||
|
QByteArray id = result["id"].data();
|
||||||
|
showStatusMessage(tr("Thread group %1 exited").arg(_(id)), 1000);
|
||||||
|
handleThreadGroupExited(result);
|
||||||
|
} else if (asyncClass == "thread-exited") {
|
||||||
|
//"{id="1",group-id="28902"}"
|
||||||
|
QByteArray id = result["id"].data();
|
||||||
|
QByteArray groupid = result["group-id"].data();
|
||||||
|
showStatusMessage(tr("Thread %1 in group %2 exited")
|
||||||
|
.arg(_(id)).arg(_(groupid)), 1000);
|
||||||
|
threadsHandler()->removeThread(ThreadId(id.toLong()));
|
||||||
|
} else if (asyncClass == "thread-selected") {
|
||||||
|
QByteArray id = result["id"].data();
|
||||||
|
showStatusMessage(tr("Thread %1 selected").arg(_(id)), 1000);
|
||||||
|
//"{id="2"}"
|
||||||
|
} else if (asyncClass == "breakpoint-modified") {
|
||||||
|
// New in FSF gdb since 2011-04-27.
|
||||||
|
// "{bkpt={number="3",type="breakpoint",disp="keep",
|
||||||
|
// enabled="y",addr="<MULTIPLE>",times="1",
|
||||||
|
// original-location="\\",simple_gdbtest_app.cpp\\":135"},
|
||||||
|
// {number="3.1",enabled="y",addr="0x0805ff68",
|
||||||
|
// func="Vector<int>::Vector(int)",
|
||||||
|
// file="simple_gdbtest_app.cpp",
|
||||||
|
// fullname="/data/...line="135"},{number="3.2"...}}.."
|
||||||
|
|
||||||
|
// Note the leading comma in original-location. Filter it out.
|
||||||
|
// We don't need the field anyway.
|
||||||
|
QByteArray ba = result.toString();
|
||||||
|
ba = '[' + ba.mid(6, ba.size() - 7) + ']';
|
||||||
|
const int pos1 = ba.indexOf(",original-location");
|
||||||
|
const int pos2 = ba.indexOf("\":", pos1 + 2);
|
||||||
|
const int pos3 = ba.indexOf('"', pos2 + 2);
|
||||||
|
ba.remove(pos1, pos3 - pos1 + 1);
|
||||||
|
GdbMi res;
|
||||||
|
res.fromString(ba);
|
||||||
|
BreakHandler *handler = breakHandler();
|
||||||
|
Breakpoint bp;
|
||||||
|
BreakpointResponse br;
|
||||||
|
foreach (const GdbMi &bkpt, res.children()) {
|
||||||
|
const QByteArray nr = bkpt["number"].data();
|
||||||
|
BreakpointResponseId rid(nr);
|
||||||
|
if (!isHiddenBreakpoint(rid)) {
|
||||||
|
if (nr.contains('.')) {
|
||||||
|
// A sub-breakpoint.
|
||||||
|
BreakpointResponse sub;
|
||||||
|
updateResponse(sub, bkpt);
|
||||||
|
sub.id = rid;
|
||||||
|
sub.type = br.type;
|
||||||
|
bp.insertSubBreakpoint(sub);
|
||||||
|
} else {
|
||||||
|
// A primary breakpoint.
|
||||||
|
bp = handler->findBreakpointByResponseId(rid);
|
||||||
|
br = bp.response();
|
||||||
|
updateResponse(br, bkpt);
|
||||||
|
bp.setResponse(br);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (asyncClass == "breakpoint-created") {
|
||||||
|
// "{bkpt={number="1",type="breakpoint",disp="del",enabled="y",
|
||||||
|
// addr="<PENDING>",pending="main",times="0",
|
||||||
|
// original-location="main"}}" -- or --
|
||||||
|
// {bkpt={number="2",type="hw watchpoint",disp="keep",enabled="y",
|
||||||
|
// what="*0xbfffed48",times="0",original-location="*0xbfffed48"}}
|
||||||
|
BreakHandler *handler = breakHandler();
|
||||||
|
foreach (const GdbMi &bkpt, result.children()) {
|
||||||
|
BreakpointResponse br;
|
||||||
|
br.type = BreakpointByFileAndLine;
|
||||||
|
updateResponse(br, bkpt);
|
||||||
|
handler->handleAlienBreakpoint(br, this);
|
||||||
|
}
|
||||||
|
} else if (asyncClass == "breakpoint-deleted") {
|
||||||
|
// "breakpoint-deleted" "{id="1"}"
|
||||||
|
// New in FSF gdb since 2011-04-27.
|
||||||
|
QByteArray nr = result["id"].data();
|
||||||
|
BreakpointResponseId rid(nr);
|
||||||
|
if (Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid)) {
|
||||||
|
// This also triggers when a temporary breakpoint is hit.
|
||||||
|
// We do not really want that, as this loses all information.
|
||||||
|
// FIXME: Use a special marker for this case?
|
||||||
|
// if (!bp.isOneShot()) ... is not sufficient.
|
||||||
|
// It keeps temporary "Jump" breakpoints alive.
|
||||||
|
bp.removeAlienBreakpoint();
|
||||||
|
}
|
||||||
|
} else if (asyncClass == "cmd-param-changed") {
|
||||||
|
// New since 2012-08-09
|
||||||
|
// "{param="debug remote",value="1"}"
|
||||||
|
} else if (asyncClass == "memory-changed") {
|
||||||
|
// New since 2013
|
||||||
|
// "{thread-group="i1",addr="0x0918a7a8",len="0x10"}"
|
||||||
|
} else if (asyncClass == "tsv-created") {
|
||||||
|
// New since 2013-02-06
|
||||||
|
} else if (asyncClass == "tsv-modified") {
|
||||||
|
// New since 2013-02-06
|
||||||
|
} else {
|
||||||
|
qDebug() << "IGNORED ASYNC OUTPUT"
|
||||||
|
<< asyncClass << result.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GdbEngine::readGdbStandardError()
|
void GdbEngine::readGdbStandardError()
|
||||||
{
|
{
|
||||||
QByteArray err = m_gdbProc.readAllStandardError();
|
QByteArray err = m_gdbProc.readAllStandardError();
|
||||||
@@ -901,9 +914,13 @@ void GdbEngine::runCommand(const QByteArray &command, int flags)
|
|||||||
|
|
||||||
void GdbEngine::runCommand(const DebuggerCommand &command)
|
void GdbEngine::runCommand(const DebuggerCommand &command)
|
||||||
{
|
{
|
||||||
|
const int token = ++currentToken();
|
||||||
|
|
||||||
DebuggerCommand cmd = command;
|
DebuggerCommand cmd = command;
|
||||||
if (command.flags & PythonCommand)
|
if (command.flags & PythonCommand) {
|
||||||
|
cmd.arg("token", token);
|
||||||
cmd.function = "python theDumper." + cmd.function + "(" + cmd.argsToPython() + ")";
|
cmd.function = "python theDumper." + cmd.function + "(" + cmd.argsToPython() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
if (!stateAcceptsGdbCommands(state())) {
|
if (!stateAcceptsGdbCommands(state())) {
|
||||||
PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: " + cmd.function));
|
PENDING_DEBUG(_("NO GDB PROCESS RUNNING, CMD IGNORED: " + cmd.function));
|
||||||
@@ -943,8 +960,6 @@ void GdbEngine::runCommand(const DebuggerCommand &command)
|
|||||||
|
|
||||||
QTC_ASSERT(m_gdbProc.state() == QProcess::Running, return);
|
QTC_ASSERT(m_gdbProc.state() == QProcess::Running, return);
|
||||||
|
|
||||||
const int token = ++currentToken();
|
|
||||||
|
|
||||||
cmd.postTime = QTime::currentTime().msecsSinceStartOfDay();
|
cmd.postTime = QTime::currentTime().msecsSinceStartOfDay();
|
||||||
m_commandForToken[token] = cmd;
|
m_commandForToken[token] = cmd;
|
||||||
m_flagsForToken[token] = command.flags;
|
m_flagsForToken[token] = command.flags;
|
||||||
@@ -1417,7 +1432,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
|
|||||||
&& QFileInfo::exists(fullName)
|
&& QFileInfo::exists(fullName)
|
||||||
&& !isQFatalBreakpoint(rid)
|
&& !isQFatalBreakpoint(rid)
|
||||||
&& function != "qt_v4TriggeredBreakpointHook"
|
&& function != "qt_v4TriggeredBreakpointHook"
|
||||||
&& function != "qt_qmlDebugEventFromService"
|
&& function != "qt_qmlDebugMessageAvailable"
|
||||||
&& language != "js")
|
&& language != "js")
|
||||||
gotoLocation(Location(fullName, lineNumber));
|
gotoLocation(Location(fullName, lineNumber));
|
||||||
|
|
||||||
@@ -2356,6 +2371,8 @@ void GdbEngine::updateResponse(BreakpointResponse &response, const GdbMi &bkpt)
|
|||||||
else if (catchType == "syscall")
|
else if (catchType == "syscall")
|
||||||
response.type = BreakpointAtSysCall;
|
response.type = BreakpointAtSysCall;
|
||||||
}
|
}
|
||||||
|
} else if (child.hasName("hitcount")) {
|
||||||
|
response.hitCount = child.toInt();
|
||||||
} else if (child.hasName("original-location")) {
|
} else if (child.hasName("original-location")) {
|
||||||
originalLocation = child.data();
|
originalLocation = child.data();
|
||||||
}
|
}
|
||||||
@@ -2432,6 +2449,29 @@ QByteArray GdbEngine::breakpointLocation2(const BreakpointParameters &data)
|
|||||||
+ QByteArray::number(data.lineNumber);
|
+ QByteArray::number(data.lineNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GdbEngine::handleInsertInterpreterBreakpoint(const DebuggerResponse &response, Breakpoint bp)
|
||||||
|
{
|
||||||
|
BreakpointResponse br = bp.response();
|
||||||
|
bool pending = response.data["pending"].toInt();
|
||||||
|
if (pending) {
|
||||||
|
bp.notifyBreakpointInsertOk();
|
||||||
|
} else {
|
||||||
|
br.id = BreakpointResponseId(response.data["number"].data());
|
||||||
|
updateResponse(br, response.data);
|
||||||
|
bp.setResponse(br);
|
||||||
|
bp.notifyBreakpointInsertOk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GdbEngine::handleInterpreterBreakpointModified(const GdbMi &data)
|
||||||
|
{
|
||||||
|
BreakpointModelId id(data["modelid"].data());
|
||||||
|
Breakpoint bp = breakHandler()->breakpointById(id);
|
||||||
|
BreakpointResponse br = bp.response();
|
||||||
|
updateResponse(br, data);
|
||||||
|
bp.setResponse(br);
|
||||||
|
}
|
||||||
|
|
||||||
void GdbEngine::handleWatchInsert(const DebuggerResponse &response, Breakpoint bp)
|
void GdbEngine::handleWatchInsert(const DebuggerResponse &response, Breakpoint bp)
|
||||||
{
|
{
|
||||||
if (bp && response.resultClass == ResultDone) {
|
if (bp && response.resultClass == ResultDone) {
|
||||||
@@ -2696,10 +2736,10 @@ void GdbEngine::insertBreakpoint(Breakpoint bp)
|
|||||||
const BreakpointParameters &data = bp.parameters();
|
const BreakpointParameters &data = bp.parameters();
|
||||||
|
|
||||||
if (!data.isCppBreakpoint()) {
|
if (!data.isCppBreakpoint()) {
|
||||||
DebuggerCommand cmd("insertInterpreterBreakpoint", PythonCommand);
|
DebuggerCommand cmd("insertInterpreterBreakpoint", PythonCommand | NeedsStop);
|
||||||
bp.addToCommand(&cmd);
|
bp.addToCommand(&cmd);
|
||||||
|
cmd.callback = [this, bp](const DebuggerResponse &r) { handleInsertInterpreterBreakpoint(r, bp); };
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
bp.notifyBreakpointInsertOk();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -211,6 +211,7 @@ private:
|
|||||||
private: ////////// Gdb Output, State & Capability Handling //////////
|
private: ////////// Gdb Output, State & Capability Handling //////////
|
||||||
protected:
|
protected:
|
||||||
Q_SLOT void handleResponse(const QByteArray &buff);
|
Q_SLOT void handleResponse(const QByteArray &buff);
|
||||||
|
void handleAsyncOutput(const QByteArray &asyncClass, const GdbMi &result);
|
||||||
void handleStopResponse(const GdbMi &data);
|
void handleStopResponse(const GdbMi &data);
|
||||||
void handleResultRecord(DebuggerResponse *response);
|
void handleResultRecord(DebuggerResponse *response);
|
||||||
void handleStop1(const GdbMi &data);
|
void handleStop1(const GdbMi &data);
|
||||||
@@ -282,6 +283,8 @@ private: ////////// View & Data Stuff //////////
|
|||||||
void handleBreakCondition(const DebuggerResponse &response, Breakpoint bp);
|
void handleBreakCondition(const DebuggerResponse &response, Breakpoint bp);
|
||||||
void handleBreakThreadSpec(const DebuggerResponse &response, Breakpoint bp);
|
void handleBreakThreadSpec(const DebuggerResponse &response, Breakpoint bp);
|
||||||
void handleBreakLineNumber(const DebuggerResponse &response, Breakpoint bp);
|
void handleBreakLineNumber(const DebuggerResponse &response, Breakpoint bp);
|
||||||
|
void handleInsertInterpreterBreakpoint(const DebuggerResponse &response, Breakpoint bp);
|
||||||
|
void handleInterpreterBreakpointModified(const GdbMi &data);
|
||||||
void handleWatchInsert(const DebuggerResponse &response, Breakpoint bp);
|
void handleWatchInsert(const DebuggerResponse &response, Breakpoint bp);
|
||||||
void handleCatchInsert(const DebuggerResponse &response, Breakpoint bp);
|
void handleCatchInsert(const DebuggerResponse &response, Breakpoint bp);
|
||||||
void handleBkpt(const GdbMi &bkpt, Breakpoint bp);
|
void handleBkpt(const GdbMi &bkpt, Breakpoint bp);
|
||||||
|
@@ -954,7 +954,7 @@ void LldbEngine::handleLocationNotification(const GdbMi &reportedLocation)
|
|||||||
// Quickly set the location marker.
|
// Quickly set the location marker.
|
||||||
if (lineNumber > 0
|
if (lineNumber > 0
|
||||||
&& QFileInfo::exists(fileName)
|
&& QFileInfo::exists(fileName)
|
||||||
&& function != "::qt_qmlDebugEventFromService()")
|
&& function != "::qt_qmlDebugMessageAvailable()")
|
||||||
gotoLocation(Location(fileName, lineNumber));
|
gotoLocation(Location(fileName, lineNumber));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -167,6 +167,21 @@ QString StackFrame::toToolTip() const
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString findFile(const QString &baseDir, const QString &relativeFile)
|
||||||
|
{
|
||||||
|
QDir dir(baseDir);
|
||||||
|
while (true) {
|
||||||
|
const QString path = dir.absoluteFilePath(relativeFile);
|
||||||
|
const QFileInfo fi(path);
|
||||||
|
if (fi.isFile())
|
||||||
|
return path;
|
||||||
|
if (dir.isRoot())
|
||||||
|
break;
|
||||||
|
dir.cdUp();
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
// Try to resolve files coming from resource files.
|
// Try to resolve files coming from resource files.
|
||||||
void StackFrame::fixQrcFrame(const DebuggerRunParameters &rp)
|
void StackFrame::fixQrcFrame(const DebuggerRunParameters &rp)
|
||||||
{
|
{
|
||||||
@@ -179,21 +194,19 @@ void StackFrame::fixQrcFrame(const DebuggerRunParameters &rp)
|
|||||||
}
|
}
|
||||||
if (!file.startsWith(QLatin1String("qrc:/")))
|
if (!file.startsWith(QLatin1String("qrc:/")))
|
||||||
return;
|
return;
|
||||||
const QString relativeFile = file.right(file.size() - 5);
|
|
||||||
if (rp.projectSourceDirectory.isEmpty())
|
QString relativeFile = file.right(file.size() - 5);
|
||||||
|
while (relativeFile.startsWith(QLatin1Char('/')))
|
||||||
|
relativeFile = relativeFile.mid(1);
|
||||||
|
|
||||||
|
QString absFile = findFile(rp.projectSourceDirectory, relativeFile);
|
||||||
|
if (absFile.isEmpty())
|
||||||
|
absFile = findFile(QDir::currentPath(), relativeFile);
|
||||||
|
|
||||||
|
if (absFile.isEmpty())
|
||||||
return;
|
return;
|
||||||
const QFileInfo pFi(rp.projectSourceDirectory + QLatin1Char('/') + relativeFile);
|
file = absFile;
|
||||||
if (pFi.isFile()) {
|
|
||||||
file = pFi.absoluteFilePath();
|
|
||||||
usable = true;
|
usable = true;
|
||||||
return;
|
|
||||||
}
|
|
||||||
const QFileInfo cFi(QDir::currentPath() + QLatin1Char('/') + relativeFile);
|
|
||||||
if (cFi.isFile()) {
|
|
||||||
file = cFi.absoluteFilePath();
|
|
||||||
usable = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QDebug operator<<(QDebug d, const StackFrame &f)
|
QDebug operator<<(QDebug d, const StackFrame &f)
|
||||||
|
@@ -391,8 +391,8 @@ QByteArray WatchData::hexAddress() const
|
|||||||
void WatchData::updateValue(const GdbMi &item)
|
void WatchData::updateValue(const GdbMi &item)
|
||||||
{
|
{
|
||||||
GdbMi value = item["value"];
|
GdbMi value = item["value"];
|
||||||
if (value.isValid()) {
|
|
||||||
DebuggerEncoding encoding = debuggerEncoding(item["valueencoded"].data());
|
DebuggerEncoding encoding = debuggerEncoding(item["valueencoded"].data());
|
||||||
|
if (value.isValid() || encoding != Unencoded8Bit) {
|
||||||
setValue(decodeData(value.data(), encoding));
|
setValue(decodeData(value.data(), encoding));
|
||||||
} else {
|
} else {
|
||||||
setValueNeeded();
|
setValueNeeded();
|
||||||
@@ -576,7 +576,8 @@ void parseChildrenData(const WatchData &data0, const GdbMi &item,
|
|||||||
|
|
||||||
setWatchDataValueEnabled(data, item["valueenabled"]);
|
setWatchDataValueEnabled(data, item["valueenabled"]);
|
||||||
setWatchDataValueEditable(data, item["valueeditable"]);
|
setWatchDataValueEditable(data, item["valueeditable"]);
|
||||||
data.updateChildCount(item["numchild"]);
|
data.updateChildCount(item["numchild"]); // GDB/MI
|
||||||
|
data.updateChildCount(item["haschild"]); // native-mixed
|
||||||
itemHandler(data);
|
itemHandler(data);
|
||||||
|
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
Reference in New Issue
Block a user