summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhjk <hjk@theqtcompany.com>2015-10-14 13:26:22 +0200
committerChristian Stenger <christian.stenger@theqtcompany.com>2015-10-19 12:40:29 +0000
commitb8ae9fd46d2c02e2b4901abf140ac106e35bdf51 (patch)
treebe44a7952b8eef22b6f177721c05388245caf245
parentbe1e0f7ec00fd0e23abb49dfe989b48a816b538e (diff)
downloadqt-creator-b8ae9fd46d2c02e2b4901abf140ac106e35bdf51.tar.gz
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>
-rw-r--r--share/qtcreator/debugger/dumper.py212
-rw-r--r--share/qtcreator/debugger/gdbbridge.py16
-rw-r--r--share/qtcreator/debugger/lldbbridge.py14
-rw-r--r--share/qtcreator/debugger/qttypes.py8
-rw-r--r--src/plugins/debugger/breakhandler.cpp2
-rw-r--r--src/plugins/debugger/debuggerprotocol.cpp2
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp426
-rw-r--r--src/plugins/debugger/gdb/gdbengine.h3
-rw-r--r--src/plugins/debugger/lldb/lldbengine.cpp2
-rw-r--r--src/plugins/debugger/stackframe.cpp41
-rw-r--r--src/plugins/debugger/watchdata.cpp7
11 files changed, 446 insertions, 287 deletions
diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py
index dfe5f7da15..a94dccedef 100644
--- a/share/qtcreator/debugger/dumper.py
+++ b/share/qtcreator/debugger/dumper.py
@@ -414,8 +414,6 @@ class DumperBase:
"personaltypes",
]
- self.interpreterSeq = 0
-
def resetCaches(self):
# This is a cache mapping from 'type name' to 'display alternatives'.
@@ -601,9 +599,6 @@ 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
@@ -834,38 +829,98 @@ class DumperBase:
self.putSpecialValue(SpecialItemCountValue, count)
self.putNumChild(count)
- def dictToMi(self, value):
+ def resultToMi(self, value):
if type(value) is bool:
return '"%d"' % int(value)
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())]) + '}'
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:
index = 0
pairs = []
for item in value:
+ if item.get('type', '') == 'function':
+ continue
name = item.get('name', '')
if len(name) == 0:
name = str(index)
index += 1
- pairs.append((name, self.dictToMi(item)))
+ pairs.append((name, self.variablesToMi(item, prefix)))
pairs.sort(key = lambda pair: pair[0])
return '[' + ','.join([pair[1] for pair in pairs]) + ']'
return '"%s"' % value
+ def filterPrefix(self, prefix, items):
+ return [i[len(prefix):] for i in items if i.startswith(prefix)]
+
def tryFetchInterpreterVariables(self, args):
if not int(args.get('nativemixed', 0)):
return (False, '')
context = args.get('context', '')
if not len(context):
return (False, '')
- res = self.extractInterpreterVariables(args)
- if res:
- return (True, 'data=%s' % self.dictToMi(res.get('data', {})))
- return (False, '')
- def variableDictToMi(self, res):
- return self.dictToMi(res.get('data', {}), self.SortStructMembers)
+ expanded = args.get('expanded')
+ args['expanded'] = self.filterPrefix('local', expanded)
+
+ res = self.sendInterpreterRequest('variables', args)
+ if not res:
+ return (False, '')
+
+ reslist = []
+ 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):
self.put('%s="%s",' % (name, value))
@@ -1776,38 +1831,86 @@ class DumperBase:
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):
+ def handleInterpreterMessage(self):
""" Return True if inferior stopped """
- buf = self.parseAndEvaluate("qt_qmlDebugEventBuffer")
- size = self.parseAndEvaluate("qt_qmlDebugEventLength")
- resdict = self.readJsonFromMemory(buf, size)
- warn("Interpreter event received: %s" % resdict)
+ resdict = self.fetchInterpreterResult()
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):
res = self.sendInterpreterRequest('removebreakpoint', { 'id' : args['id'] })
return res
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)
+ # Will fail if the service is not yet up and running.
+ response = self.sendInterpreterRequest('setbreakpoint', args)
+ resdict = args.copy()
+ 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 = {}):
- self.interpreterSeq += 1
- encoded = json.dumps({
- 'seq': self.interpreterSeq,
- 'type': 'request',
- 'command': command,
- 'arguments': args
- })
+ encoded = json.dumps({ 'command': command, 'arguments': args })
hexdata = self.hexencode(encoded)
expr = 'qt_qmlDebugSendDataToService("NativeQmlDebugger","%s")' % hexdata
try:
@@ -1819,21 +1922,10 @@ class DumperBase:
# Happens with LLDB and 'None' current thread.
warn("Interpreter command failed: %s: %s" % (encoded, error))
return {}
-
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 }
+ return self.fetchInterpreterResult()
def executeStep(self, args):
if self.nativeMixed:
@@ -1856,23 +1948,22 @@ class DumperBase:
self.doContinue()
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.
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
+ self.reportInterpreterResult({'bpnr': -1, 'pending': 1,
+ 'error': 'Pending interpreter breakpoint insertion failed.'}, args)
+ return
else:
if not bp:
- warn("Direct interpreter breakpoint insertion failed.")
- warn("Make pending.")
+ self.reportInterpreterResult({'bpnr': -1, 'pending': 1,
+ 'warning': 'Direct interpreter breakpoint insertion failed.'}, args)
self.createResolvePendingBreakpointsHookBreakpoint(args)
- return -1
-
- warn("Resolved interpreter breakpoint: BP: %s" % bp)
- return int(bp)
+ return
+ self.reportInterpreterResult({'bpnr': bp, 'pending': 0}, args)
def isInternalInterpreterFrame(self, functionName):
if functionName is None:
@@ -1902,6 +1993,11 @@ class DumperBase:
def extractInterpreterStack(self):
return self.sendInterpreterRequest('backtrace', {'limit': 10 })
- def extractInterpreterVariables(self, args):
- return self.sendInterpreterRequest('variables', args)
+ def polishWatchers(self, watchers):
+ 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
diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py
index c6baf25b9b..492ec4144a 100644
--- a/share/qtcreator/debugger/gdbbridge.py
+++ b/share/qtcreator/debugger/gdbbridge.py
@@ -1603,7 +1603,7 @@ class Dumper(DumperBase):
objfile = fromNativePath(symtab.objfile.filename)
fileName = fromNativePath(symtab.fullname())
- if self.nativeMixed and functionName == "qt_qmlDebugEventFromService":
+ if self.nativeMixed and functionName == "qt_qmlDebugMessageAvailable":
interpreterStack = self.extractInterpreterStack()
#print("EXTRACTED INTEPRETER STACK: %s" % interpreterStack)
for interpreterFrame in interpreterStack.get('frames', []):
@@ -1641,13 +1641,11 @@ class Dumper(DumperBase):
self.dumper = dumper
self.args = args
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.doInsertInterpreterBreakpoint(args, True)
- print("Resolving QML breakpoint %s -> %s" % (args, bp))
+ self.dumper.resolvePendingInterpreterBreakpoint(args)
self.enabled = False
return False
@@ -1800,14 +1798,14 @@ registerCommand("threadnames", threadnames)
#
#######################################################################
-class QmlEngineEventBreakpoint(gdb.Breakpoint):
+class InterpreterMessageBreakpoint(gdb.Breakpoint):
def __init__(self):
- spec = "qt_qmlDebugEventFromService"
- super(QmlEngineEventBreakpoint, self).\
+ spec = "qt_qmlDebugMessageAvailable"
+ super(InterpreterMessageBreakpoint, self).\
__init__(spec, gdb.BP_BREAKPOINT, internal=True)
def stop(self):
print("Interpreter event received.")
- return theDumper.handleInterpreterEvent()
+ return theDumper.handleInterpreterMessage()
-QmlEngineEventBreakpoint()
+InterpreterMessageBreakpoint()
diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py
index f54ab9cdd8..093797e7ab 100644
--- a/share/qtcreator/debugger/lldbbridge.py
+++ b/share/qtcreator/debugger/lldbbridge.py
@@ -693,7 +693,7 @@ class Dumper(DumperBase):
if self.nativeMixed:
self.interpreterEventBreakpoint = \
- self.target.BreakpointCreateByName("qt_qmlDebugEventFromService")
+ self.target.BreakpointCreateByName("qt_qmlDebugMessageAvailable")
state = 1 if self.target.IsValid() else 0
self.reportResult('success="%s",msg="%s",exe="%s"' % (state, error, self.executable_), args)
@@ -871,7 +871,7 @@ class Dumper(DumperBase):
functionName = frame.GetFunctionName()
- if isNativeMixed and functionName == "::qt_qmlDebugEventFromService()":
+ if isNativeMixed and functionName == "::qt_qmlDebugMessageAvailable()":
interpreterStack = self.extractInterpreterStack()
for interpreterFrame in interpreterStack.get('frames', []):
function = interpreterFrame.get('function', '')
@@ -1341,9 +1341,9 @@ class Dumper(DumperBase):
self.reportState("inferiorstopok")
self.process.Continue();
return
- if functionName == "::qt_qmlDebugEventFromService()":
- self.report("EVENT FROM SERVICE")
- res = self.handleInterpreterEvent()
+ if functionName == "::qt_qmlDebugMessageAvailable()":
+ self.report("ASYNC MESSAGE FROM SERVICE")
+ res = self.handleInterpreterMessage()
if not res:
self.report("EVENT NEEDS NO STOP")
self.reportState("stopped")
@@ -1427,7 +1427,7 @@ class Dumper(DumperBase):
if bpType == BreakpointByFileAndLine:
fileName = args["file"]
if fileName.endswith(".js") or fileName.endswith(".qml"):
- self.doInsertInterpreterBreakpoint(args, False)
+ self.insertInterpreterBreakpoint(args)
return
extra = ''
@@ -1742,7 +1742,7 @@ class Dumper(DumperBase):
bp = self.target.BreakpointCreateByName("qt_qmlDebugConnectorOpen")
bp.SetOneShot(True)
self.interpreterBreakpointResolvers.append(
- lambda: self.doInsertInterpreterBreakpoint(args, True))
+ lambda: self.resolvePendingInterpreterBreakpoint(args))
# Used in dumper auto test.
diff --git a/share/qtcreator/debugger/qttypes.py b/share/qtcreator/debugger/qttypes.py
index 41f515e542..5518a12065 100644
--- a/share/qtcreator/debugger/qttypes.py
+++ b/share/qtcreator/debugger/qttypes.py
@@ -2308,7 +2308,7 @@ def qdump__QV4__String(d, value):
d.putStringValue(d.addressOf(value) + 2 * d.ptrSize())
def qdump__QV4__Value(d, value):
- v = toInteger(str(value["val"]))
+ v = toInteger(str(value["_val"]))
NaNEncodeMask = 0xffff800000000000
IsInt32Mask = 0x0002000000000000
IsDoubleMask = 0xfffc000000000000
@@ -2319,7 +2319,10 @@ def qdump__QV4__Value(d, value):
ns = d.qtNamespace()
if v & IsInt32Mask:
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:
d.putBetterType("%sQV4::Value (double)" % ns)
d.putValue("%x" % (v ^ 0xffff800000000000), Hex2EncodedFloat8)
@@ -2332,6 +2335,7 @@ def qdump__QV4__Value(d, value):
elif v & IsNullOrBooleanMask:
d.putBetterType("%sQV4::Value (null/bool)" % ns)
d.putValue("(null/bool)")
+ d.putValue(v & 1)
else:
vtable = value["m"]["vtable"]
if toInteger(vtable["isString"]):
diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp
index b95d196589..6f913f5a1c 100644
--- a/src/plugins/debugger/breakhandler.cpp
+++ b/src/plugins/debugger/breakhandler.cpp
@@ -1470,6 +1470,8 @@ QString BreakpointItem::toToolTip() const
<< "</td><td>" << QDir::toNativeSeparators(markerFileName()) << "</td></tr>"
<< "<tr><td>" << tr("Marker Line:")
<< "</td><td>" << markerLineNumber() << "</td></tr>"
+ << "<tr><td>" << tr("Hit Count:")
+ << "</td><td>" << m_response.hitCount << "</td></tr>"
<< "</table><br><hr><table>"
<< "<tr><th>" << tr("Property")
<< "</th><th>" << tr("Requested")
diff --git a/src/plugins/debugger/debuggerprotocol.cpp b/src/plugins/debugger/debuggerprotocol.cpp
index 7f3525b0a0..35c2d220f3 100644
--- a/src/plugins/debugger/debuggerprotocol.cpp
+++ b/src/plugins/debugger/debuggerprotocol.cpp
@@ -877,6 +877,8 @@ DebuggerEncoding debuggerEncoding(const QByteArray &data)
return SpecialNullValue;
if (data == "itemcount")
return SpecialItemCountValue;
+ if (data == "notaccessible")
+ return SpecialNotAccessibleValue;
return DebuggerEncoding(data.toInt());
}
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 6563e527e0..5303b65c9b 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -437,206 +437,34 @@ void GdbEngine::handleResponse(const QByteArray &buff)
result.m_type = GdbMi::Tuple;
}
}
- 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);
- 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();
- }
+ handleAsyncOutput(asyncClass, result);
break;
}
case '~': {
QByteArray data = GdbMi::parseCString(from, to);
if (data.startsWith("bridgemessage={")) {
- //showMessage(_(data), LogDebug);
+ // It's already logged.
break;
}
- if (data.startsWith("bridgeresult={")) {
- //showMessage(_(data), LogDebug);
+ if (data.startsWith("interpreterresult={")) {
+ GdbMi allData;
+ allData.fromStringMultiple(data);
DebuggerResponse response;
response.resultClass = ResultDone;
- response.data.fromStringMultiple(data);
+ response.data = allData["interpreterresult"];
+ response.token = allData["token"].toInt();
handleResultRecord(&response);
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;
// 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()
{
QByteArray err = m_gdbProc.readAllStandardError();
@@ -901,9 +914,13 @@ void GdbEngine::runCommand(const QByteArray &command, int flags)
void GdbEngine::runCommand(const DebuggerCommand &command)
{
+ const int token = ++currentToken();
+
DebuggerCommand cmd = command;
- if (command.flags & PythonCommand)
+ if (command.flags & PythonCommand) {
+ cmd.arg("token", token);
cmd.function = "python theDumper." + cmd.function + "(" + cmd.argsToPython() + ")";
+ }
if (!stateAcceptsGdbCommands(state())) {
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);
- const int token = ++currentToken();
-
cmd.postTime = QTime::currentTime().msecsSinceStartOfDay();
m_commandForToken[token] = cmd;
m_flagsForToken[token] = command.flags;
@@ -1417,7 +1432,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
&& QFileInfo::exists(fullName)
&& !isQFatalBreakpoint(rid)
&& function != "qt_v4TriggeredBreakpointHook"
- && function != "qt_qmlDebugEventFromService"
+ && function != "qt_qmlDebugMessageAvailable"
&& language != "js")
gotoLocation(Location(fullName, lineNumber));
@@ -2356,6 +2371,8 @@ void GdbEngine::updateResponse(BreakpointResponse &response, const GdbMi &bkpt)
else if (catchType == "syscall")
response.type = BreakpointAtSysCall;
}
+ } else if (child.hasName("hitcount")) {
+ response.hitCount = child.toInt();
} else if (child.hasName("original-location")) {
originalLocation = child.data();
}
@@ -2432,6 +2449,29 @@ QByteArray GdbEngine::breakpointLocation2(const BreakpointParameters &data)
+ 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)
{
if (bp && response.resultClass == ResultDone) {
@@ -2696,10 +2736,10 @@ void GdbEngine::insertBreakpoint(Breakpoint bp)
const BreakpointParameters &data = bp.parameters();
if (!data.isCppBreakpoint()) {
- DebuggerCommand cmd("insertInterpreterBreakpoint", PythonCommand);
+ DebuggerCommand cmd("insertInterpreterBreakpoint", PythonCommand | NeedsStop);
bp.addToCommand(&cmd);
+ cmd.callback = [this, bp](const DebuggerResponse &r) { handleInsertInterpreterBreakpoint(r, bp); };
runCommand(cmd);
- bp.notifyBreakpointInsertOk();
return;
}
diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h
index 70e2816404..cee83ca3a9 100644
--- a/src/plugins/debugger/gdb/gdbengine.h
+++ b/src/plugins/debugger/gdb/gdbengine.h
@@ -211,6 +211,7 @@ private:
private: ////////// Gdb Output, State & Capability Handling //////////
protected:
Q_SLOT void handleResponse(const QByteArray &buff);
+ void handleAsyncOutput(const QByteArray &asyncClass, const GdbMi &result);
void handleStopResponse(const GdbMi &data);
void handleResultRecord(DebuggerResponse *response);
void handleStop1(const GdbMi &data);
@@ -282,6 +283,8 @@ private: ////////// View & Data Stuff //////////
void handleBreakCondition(const DebuggerResponse &response, Breakpoint bp);
void handleBreakThreadSpec(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 handleCatchInsert(const DebuggerResponse &response, Breakpoint bp);
void handleBkpt(const GdbMi &bkpt, Breakpoint bp);
diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp
index 3a04f5bb52..c9536e92c6 100644
--- a/src/plugins/debugger/lldb/lldbengine.cpp
+++ b/src/plugins/debugger/lldb/lldbengine.cpp
@@ -954,7 +954,7 @@ void LldbEngine::handleLocationNotification(const GdbMi &reportedLocation)
// Quickly set the location marker.
if (lineNumber > 0
&& QFileInfo::exists(fileName)
- && function != "::qt_qmlDebugEventFromService()")
+ && function != "::qt_qmlDebugMessageAvailable()")
gotoLocation(Location(fileName, lineNumber));
}
diff --git a/src/plugins/debugger/stackframe.cpp b/src/plugins/debugger/stackframe.cpp
index 11fa1a99e5..689437b52a 100644
--- a/src/plugins/debugger/stackframe.cpp
+++ b/src/plugins/debugger/stackframe.cpp
@@ -167,6 +167,21 @@ QString StackFrame::toToolTip() const
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.
void StackFrame::fixQrcFrame(const DebuggerRunParameters &rp)
{
@@ -179,21 +194,19 @@ void StackFrame::fixQrcFrame(const DebuggerRunParameters &rp)
}
if (!file.startsWith(QLatin1String("qrc:/")))
return;
- const QString relativeFile = file.right(file.size() - 5);
- if (rp.projectSourceDirectory.isEmpty())
- return;
- const QFileInfo pFi(rp.projectSourceDirectory + QLatin1Char('/') + relativeFile);
- if (pFi.isFile()) {
- file = pFi.absoluteFilePath();
- usable = true;
- return;
- }
- const QFileInfo cFi(QDir::currentPath() + QLatin1Char('/') + relativeFile);
- if (cFi.isFile()) {
- file = cFi.absoluteFilePath();
- usable = true;
+
+ 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;
- }
+ file = absFile;
+ usable = true;
}
QDebug operator<<(QDebug d, const StackFrame &f)
diff --git a/src/plugins/debugger/watchdata.cpp b/src/plugins/debugger/watchdata.cpp
index 188fe5cd82..8b271c92b1 100644
--- a/src/plugins/debugger/watchdata.cpp
+++ b/src/plugins/debugger/watchdata.cpp
@@ -391,8 +391,8 @@ QByteArray WatchData::hexAddress() const
void WatchData::updateValue(const GdbMi &item)
{
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));
} else {
setValueNeeded();
@@ -576,7 +576,8 @@ void parseChildrenData(const WatchData &data0, const GdbMi &item,
setWatchDataValueEnabled(data, item["valueenabled"]);
setWatchDataValueEditable(data, item["valueeditable"]);
- data.updateChildCount(item["numchild"]);
+ data.updateChildCount(item["numchild"]); // GDB/MI
+ data.updateChildCount(item["haschild"]); // native-mixed
itemHandler(data);
bool ok = false;