forked from qt-creator/qt-creator
Debugger: Make basic native-mixed debugging work with LLDB
Change-Id: I4d55c6a486d5adbccaa93eaa1ee461238fecfea3 Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
This commit is contained in:
@@ -841,9 +841,32 @@ class DumperBase:
|
|||||||
return '{' + ','.join(['%s=%s' % (k, self.dictToMi(v))
|
return '{' + ','.join(['%s=%s' % (k, self.dictToMi(v))
|
||||||
for (k, v) in list(value.items())]) + '}'
|
for (k, v) in list(value.items())]) + '}'
|
||||||
if type(value) is list:
|
if type(value) is list:
|
||||||
return '[' + ','.join([self.dictToMi(v) for v in value]) + ']'
|
index = 0
|
||||||
|
pairs = []
|
||||||
|
for item in value:
|
||||||
|
name = item.get('name', '')
|
||||||
|
if len(name) == 0:
|
||||||
|
name = str(index)
|
||||||
|
index += 1
|
||||||
|
pairs.append((name, self.dictToMi(item)))
|
||||||
|
pairs.sort(key = lambda pair: pair[0])
|
||||||
|
return '[' + ','.join([pair[1] for pair in pairs]) + ']'
|
||||||
return '"%s"' % value
|
return '"%s"' % value
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
def putField(self, name, value):
|
def putField(self, name, value):
|
||||||
self.put('%s="%s",' % (name, value))
|
self.put('%s="%s",' % (name, value))
|
||||||
|
|
||||||
@@ -1763,7 +1786,7 @@ class DumperBase:
|
|||||||
buf = self.parseAndEvaluate("qt_qmlDebugEventBuffer")
|
buf = self.parseAndEvaluate("qt_qmlDebugEventBuffer")
|
||||||
size = self.parseAndEvaluate("qt_qmlDebugEventLength")
|
size = self.parseAndEvaluate("qt_qmlDebugEventLength")
|
||||||
resdict = self.readJsonFromMemory(buf, size)
|
resdict = self.readJsonFromMemory(buf, size)
|
||||||
warn("RES DICT : %s" % resdict)
|
warn("Interpreter event received: %s" % resdict)
|
||||||
return resdict.get('event') == 'break'
|
return resdict.get('event') == 'break'
|
||||||
|
|
||||||
def removeInterpreterBreakpoint(self, args):
|
def removeInterpreterBreakpoint(self, args):
|
||||||
@@ -1779,8 +1802,12 @@ class DumperBase:
|
|||||||
|
|
||||||
def sendInterpreterRequest(self, command, args = {}):
|
def sendInterpreterRequest(self, command, args = {}):
|
||||||
self.interpreterSeq += 1
|
self.interpreterSeq += 1
|
||||||
cmd = { 'seq': self.interpreterSeq, 'type': 'request', 'command': command, 'arguments': args }
|
encoded = json.dumps({
|
||||||
encoded = json.dumps(cmd)
|
'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:
|
||||||
|
|||||||
@@ -220,12 +220,11 @@ class Dumper(DumperBase):
|
|||||||
|
|
||||||
# These values will be kept between calls to 'fetchVariables'.
|
# These values will be kept between calls to 'fetchVariables'.
|
||||||
self.isGdb = True
|
self.isGdb = True
|
||||||
self.childEventAddress = None
|
|
||||||
self.typeCache = {}
|
self.typeCache = {}
|
||||||
self.typesReported = {}
|
self.typesReported = {}
|
||||||
self.typesToReport = {}
|
self.typesToReport = {}
|
||||||
self.qtNamespaceToReport = None
|
self.qtNamespaceToReport = None
|
||||||
self.interpreterBreakpoints = []
|
self.interpreterBreakpointResolvers = []
|
||||||
|
|
||||||
def prepare(self, args):
|
def prepare(self, args):
|
||||||
self.output = []
|
self.output = []
|
||||||
@@ -359,13 +358,10 @@ class Dumper(DumperBase):
|
|||||||
partialVariable = args.get("partialVariable", "")
|
partialVariable = args.get("partialVariable", "")
|
||||||
isPartial = len(partialVariable) > 0
|
isPartial = len(partialVariable) > 0
|
||||||
|
|
||||||
if self.nativeMixed:
|
(ok, res) = self.tryFetchInterpreterVariables(args)
|
||||||
context = args.get('context', '')
|
if ok:
|
||||||
if len(context):
|
safePrint(res)
|
||||||
res = self.extractInterpreterVariables(args)
|
return
|
||||||
if res:
|
|
||||||
safePrint('data=%s' % self.dictToMi(res.get('data', {})))
|
|
||||||
return
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Locals
|
# Locals
|
||||||
@@ -1655,7 +1651,7 @@ class Dumper(DumperBase):
|
|||||||
self.enabled = False
|
self.enabled = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.interpreterBreakpoints.append(Resolver(self, args))
|
self.interpreterBreakpointResolvers.append(Resolver(self, args))
|
||||||
|
|
||||||
def exitGdb(self, _):
|
def exitGdb(self, _):
|
||||||
gdb.execute("quit")
|
gdb.execute("quit")
|
||||||
@@ -1804,18 +1800,6 @@ registerCommand("threadnames", threadnames)
|
|||||||
#
|
#
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
|
||||||
class TriggeredBreakpointHookBreakpoint(gdb.Breakpoint):
|
|
||||||
def __init__(self):
|
|
||||||
spec = "qt_v4TriggeredBreakpointHook"
|
|
||||||
super(TriggeredBreakpointHookBreakpoint, self).\
|
|
||||||
__init__(spec, gdb.BP_BREAKPOINT, internal=True)
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
print("QML engine stopped.")
|
|
||||||
return True
|
|
||||||
|
|
||||||
TriggeredBreakpointHookBreakpoint()
|
|
||||||
|
|
||||||
class QmlEngineEventBreakpoint(gdb.Breakpoint):
|
class QmlEngineEventBreakpoint(gdb.Breakpoint):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
spec = "qt_qmlDebugEventFromService"
|
spec = "qt_qmlDebugEventFromService"
|
||||||
@@ -1823,7 +1807,7 @@ class QmlEngineEventBreakpoint(gdb.Breakpoint):
|
|||||||
__init__(spec, gdb.BP_BREAKPOINT, internal=True)
|
__init__(spec, gdb.BP_BREAKPOINT, internal=True)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
print("QML engine event received.")
|
print("Interpreter event received.")
|
||||||
return theDumper.handleInterpreterEvent()
|
return theDumper.handleInterpreterEvent()
|
||||||
|
|
||||||
QmlEngineEventBreakpoint()
|
QmlEngineEventBreakpoint()
|
||||||
|
|||||||
@@ -48,9 +48,6 @@ from dumper import *
|
|||||||
|
|
||||||
qqWatchpointOffset = 10000
|
qqWatchpointOffset = 10000
|
||||||
|
|
||||||
def warn(message):
|
|
||||||
print('\n\nWARNING="%s",\n' % message.encode("latin1").replace('"', "'"))
|
|
||||||
|
|
||||||
def showException(msg, exType, exValue, exTraceback):
|
def showException(msg, exType, exValue, exTraceback):
|
||||||
warn("**** CAUGHT EXCEPTION: %s ****" % msg)
|
warn("**** CAUGHT EXCEPTION: %s ****" % msg)
|
||||||
import traceback
|
import traceback
|
||||||
@@ -230,8 +227,7 @@ class Dumper(DumperBase):
|
|||||||
self.voidPtrType_ = None
|
self.voidPtrType_ = None
|
||||||
self.isShuttingDown_ = False
|
self.isShuttingDown_ = False
|
||||||
self.isInterrupting_ = False
|
self.isInterrupting_ = False
|
||||||
self.qmlBreakpointResolvers = {}
|
self.interpreterBreakpointResolvers = []
|
||||||
self.qmlTriggeredBreakpoint = None
|
|
||||||
|
|
||||||
self.report('lldbversion=\"%s\"' % lldb.SBDebugger.GetVersionString())
|
self.report('lldbversion=\"%s\"' % lldb.SBDebugger.GetVersionString())
|
||||||
self.reportState("enginesetupok")
|
self.reportState("enginesetupok")
|
||||||
@@ -549,7 +545,7 @@ class Dumper(DumperBase):
|
|||||||
self.report('error="%s"' % result.GetError())
|
self.report('error="%s"' % result.GetError())
|
||||||
|
|
||||||
def put(self, stuff):
|
def put(self, stuff):
|
||||||
self.out += stuff
|
self.output += stuff
|
||||||
|
|
||||||
def isMovableType(self, type):
|
def isMovableType(self, type):
|
||||||
if type.GetTypeClass() in (lldb.eTypeClassBuiltin, lldb.eTypeClassPointer):
|
if type.GetTypeClass() in (lldb.eTypeClassBuiltin, lldb.eTypeClassPointer):
|
||||||
@@ -667,6 +663,7 @@ class Dumper(DumperBase):
|
|||||||
self.sysRoot_ = args.get('sysRoot', '')
|
self.sysRoot_ = args.get('sysRoot', '')
|
||||||
self.remoteChannel_ = args.get('remoteChannel', '')
|
self.remoteChannel_ = args.get('remoteChannel', '')
|
||||||
self.platform_ = args.get('platform', '')
|
self.platform_ = args.get('platform', '')
|
||||||
|
self.nativeMixed = int(args.get('nativemixed', 0))
|
||||||
|
|
||||||
self.ignoreStops = 0
|
self.ignoreStops = 0
|
||||||
self.silentStops = 0
|
self.silentStops = 0
|
||||||
@@ -689,12 +686,15 @@ class Dumper(DumperBase):
|
|||||||
if self.sysRoot_:
|
if self.sysRoot_:
|
||||||
self.debugger.SetCurrentPlatformSDKRoot(self.sysRoot_)
|
self.debugger.SetCurrentPlatformSDKRoot(self.sysRoot_)
|
||||||
|
|
||||||
|
|
||||||
if os.path.isfile(self.executable_):
|
if os.path.isfile(self.executable_):
|
||||||
self.target = self.debugger.CreateTarget(self.executable_, None, None, True, error)
|
self.target = self.debugger.CreateTarget(self.executable_, None, None, True, error)
|
||||||
else:
|
else:
|
||||||
self.target = self.debugger.CreateTarget(None, None, None, True, error)
|
self.target = self.debugger.CreateTarget(None, None, None, True, error)
|
||||||
|
|
||||||
|
if self.nativeMixed:
|
||||||
|
self.interpreterEventBreakpoint = \
|
||||||
|
self.target.BreakpointCreateByName("qt_qmlDebugEventFromService")
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
@@ -775,9 +775,11 @@ class Dumper(DumperBase):
|
|||||||
def describeLocation(self, frame):
|
def describeLocation(self, frame):
|
||||||
if int(frame.pc) == 0xffffffffffffffff:
|
if int(frame.pc) == 0xffffffffffffffff:
|
||||||
return ''
|
return ''
|
||||||
file = fileNameAsString(frame.line_entry.file)
|
fileName = fileNameAsString(frame.line_entry.file)
|
||||||
|
function = frame.GetFunctionName()
|
||||||
line = frame.line_entry.line
|
line = frame.line_entry.line
|
||||||
return 'location={file="%s",line="%s",addr="%s"}' % (file, line, frame.pc)
|
return 'location={file="%s",line="%s",address="%s",function="%s"}' \
|
||||||
|
% (fileName, line, frame.pc, function)
|
||||||
|
|
||||||
def currentThread(self):
|
def currentThread(self):
|
||||||
return None if self.process is None else self.process.GetSelectedThread()
|
return None if self.process is None else self.process.GetSelectedThread()
|
||||||
@@ -847,8 +849,6 @@ class Dumper(DumperBase):
|
|||||||
self.reportResult('msg="No thread"', args)
|
self.reportResult('msg="No thread"', args)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.report(self.describeLocation(thread.GetFrameAtIndex(0))) # FIXME
|
|
||||||
|
|
||||||
isNativeMixed = int(args.get('nativemixed', 0))
|
isNativeMixed = int(args.get('nativemixed', 0))
|
||||||
|
|
||||||
limit = args.get('stacklimit', -1)
|
limit = args.get('stacklimit', -1)
|
||||||
@@ -868,38 +868,27 @@ class Dumper(DumperBase):
|
|||||||
pc = frame.GetPC()
|
pc = frame.GetPC()
|
||||||
level = frame.idx
|
level = frame.idx
|
||||||
addr = frame.GetPCAddress().GetLoadAddress(self.target)
|
addr = frame.GetPCAddress().GetLoadAddress(self.target)
|
||||||
|
|
||||||
functionName = frame.GetFunctionName()
|
functionName = frame.GetFunctionName()
|
||||||
|
|
||||||
|
if isNativeMixed and functionName == "::qt_qmlDebugEventFromService()":
|
||||||
|
interpreterStack = self.extractInterpreterStack()
|
||||||
|
for interpreterFrame in interpreterStack.get('frames', []):
|
||||||
|
function = interpreterFrame.get('function', '')
|
||||||
|
fileName = interpreterFrame.get('file', '')
|
||||||
|
language = interpreterFrame.get('language', '')
|
||||||
|
lineNumber = interpreterFrame.get('line', 0)
|
||||||
|
context = interpreterFrame.get('context', 0)
|
||||||
|
result += ('frame={function="%s",file="%s",'
|
||||||
|
'line="%s",language="%s",context="%s"}'
|
||||||
|
% (function, fileName, lineNumber, language, context))
|
||||||
|
|
||||||
fileName = fileNameAsString(lineEntry.file)
|
fileName = fileNameAsString(lineEntry.file)
|
||||||
usable = None
|
|
||||||
language = None
|
|
||||||
|
|
||||||
if False and isNativeMixed:
|
|
||||||
if self.isReportableInterpreterFrame(functionName):
|
|
||||||
engine = frame.FindVariable("engine")
|
|
||||||
self.context = engine
|
|
||||||
h = self.extractQmlLocation(engine)
|
|
||||||
pc = 0
|
|
||||||
functionName = h['function']
|
|
||||||
fileName = h['file']
|
|
||||||
lineNumber = h['line']
|
|
||||||
addr = h['context']
|
|
||||||
language = 'js'
|
|
||||||
|
|
||||||
#elif not functionName is None:
|
|
||||||
# if functionName.startswith("qt_v4"):
|
|
||||||
# usable = 0
|
|
||||||
# elif functionName.find("QV4::") >= 0:
|
|
||||||
# usable = 0
|
|
||||||
|
|
||||||
result += '{pc="0x%x"' % pc
|
result += '{pc="0x%x"' % pc
|
||||||
result += ',level="%d"' % level
|
result += ',level="%d"' % level
|
||||||
result += ',address="0x%x"' % addr
|
result += ',address="0x%x"' % addr
|
||||||
if not usable is None:
|
|
||||||
result += ',usable="%s"' % usable
|
|
||||||
result += ',function="%s"' % functionName
|
result += ',function="%s"' % functionName
|
||||||
result += ',line="%d"' % lineNumber
|
result += ',line="%d"' % lineNumber
|
||||||
if not language is None:
|
|
||||||
result += ',language="%s"' % language
|
|
||||||
result += ',file="%s"},' % fileName
|
result += ',file="%s"},' % fileName
|
||||||
result += ']'
|
result += ']'
|
||||||
result += ',hasmore="%d"' % isLimited
|
result += ',hasmore="%d"' % isLimited
|
||||||
@@ -1155,16 +1144,28 @@ class Dumper(DumperBase):
|
|||||||
with SubItem(self, child):
|
with SubItem(self, child):
|
||||||
self.putItem(child)
|
self.putItem(child)
|
||||||
|
|
||||||
def reportVariables(self, args):
|
def fetchVariables(self, args):
|
||||||
self.out = ""
|
(ok, res) = self.tryFetchInterpreterVariables(args)
|
||||||
self.reportVariablesHelper(args)
|
if ok:
|
||||||
self.reportResult(self.out, args)
|
self.reportResult(res, args)
|
||||||
|
|
||||||
def reportVariablesHelper(self, args = {}):
|
|
||||||
frame = self.currentFrame()
|
|
||||||
if frame is None:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.expandedINames = set(args.get('expanded', []))
|
||||||
|
self.autoDerefPointers = int(args.get('autoderef', '0'))
|
||||||
|
self.sortStructMembers = bool(args.get('sortStructMembers', True));
|
||||||
|
self.useDynamicType = int(args.get('dyntype', '0'))
|
||||||
|
self.useFancy = int(args.get('fancy', '0'))
|
||||||
|
self.passExceptions = int(args.get('passexceptions', '0'))
|
||||||
|
self.currentWatchers = args.get('watchers', {})
|
||||||
|
self.typeformats = args.get("typeformats", {})
|
||||||
|
self.formats = args.get("formats", {})
|
||||||
|
|
||||||
|
frame = self.currentFrame()
|
||||||
|
if frame is None:
|
||||||
|
self.reportResult('error="No frame"', args)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.output = ''
|
||||||
partialVariable = args.get("partialVariable", "")
|
partialVariable = args.get("partialVariable", "")
|
||||||
isPartial = len(partialVariable) > 0
|
isPartial = len(partialVariable) > 0
|
||||||
|
|
||||||
@@ -1232,6 +1233,7 @@ class Dumper(DumperBase):
|
|||||||
self.handleWatches(args)
|
self.handleWatches(args)
|
||||||
|
|
||||||
self.put('],partial="%d"' % isPartial)
|
self.put('],partial="%d"' % isPartial)
|
||||||
|
self.reportResult(self.output, args)
|
||||||
|
|
||||||
def fetchRegisters(self, args = None):
|
def fetchRegisters(self, args = None):
|
||||||
if self.process is None:
|
if self.process is None:
|
||||||
@@ -1281,7 +1283,7 @@ class Dumper(DumperBase):
|
|||||||
else:
|
else:
|
||||||
self.isInterrupting_ = True
|
self.isInterrupting_ = True
|
||||||
error = self.process.Stop()
|
error = self.process.Stop()
|
||||||
self.reportResult(describeError(error), args)
|
self.reportResult(self.describeError(error), args)
|
||||||
|
|
||||||
def detachInferior(self, args):
|
def detachInferior(self, args):
|
||||||
if self.process is None:
|
if self.process is None:
|
||||||
@@ -1330,28 +1332,31 @@ class Dumper(DumperBase):
|
|||||||
frame = stoppedThread.GetFrameAtIndex(0)
|
frame = stoppedThread.GetFrameAtIndex(0)
|
||||||
#self.report("FRAME: %s" % frame)
|
#self.report("FRAME: %s" % frame)
|
||||||
function = frame.GetFunction()
|
function = frame.GetFunction()
|
||||||
#self.report("FUNCTION: %s" % function)
|
functionName = function.GetName()
|
||||||
if function.GetName() == "qt_v4ResolvePendingBreakpointsHook":
|
if functionName == "::qt_qmlDebugConnectorOpen()":
|
||||||
#self.report("RESOLVER HIT")
|
self.report("RESOLVER HIT")
|
||||||
for bp in self.qmlBreakpointResolvers:
|
for resolver in self.interpreterBreakpointResolvers:
|
||||||
self.qmlBreakpointResolvers[bp]()
|
resolver()
|
||||||
self.target.BreakpointDelete(bp.GetID())
|
self.report("AUTO-CONTINUE AFTER RESOLVING")
|
||||||
self.qmlBreakpointResolvers = {}
|
self.reportState("inferiorstopok")
|
||||||
self.process.Continue();
|
self.process.Continue();
|
||||||
return
|
return
|
||||||
|
if functionName == "::qt_qmlDebugEventFromService()":
|
||||||
|
self.report("EVENT FROM SERVICE")
|
||||||
|
res = self.handleInterpreterEvent()
|
||||||
|
if not res:
|
||||||
|
self.report("EVENT NEEDS NO STOP")
|
||||||
|
self.reportState("stopped")
|
||||||
|
self.process.Continue();
|
||||||
|
return
|
||||||
if self.isInterrupting_:
|
if self.isInterrupting_:
|
||||||
self.isInterrupting_ = False
|
self.isInterrupting_ = False
|
||||||
self.reportState("inferiorstopok")
|
self.reportState("stopped")
|
||||||
elif self.ignoreStops > 0:
|
elif self.ignoreStops > 0:
|
||||||
self.ignoreStops -= 1
|
self.ignoreStops -= 1
|
||||||
self.process.Continue()
|
self.process.Continue()
|
||||||
elif self.silentStops > 0:
|
elif self.silentStops > 0:
|
||||||
self.silentStops -= 1
|
self.silentStops -= 1
|
||||||
#elif bp and bp in self.qmlBreakpointResolvers:
|
|
||||||
# self.report("RESOLVER HIT")
|
|
||||||
# self.qmlBreakpointResolvers[bp]()
|
|
||||||
# self.process.Continue();
|
|
||||||
else:
|
else:
|
||||||
self.reportState("stopped")
|
self.reportState("stopped")
|
||||||
else:
|
else:
|
||||||
@@ -1645,19 +1650,6 @@ class Dumper(DumperBase):
|
|||||||
error = str(result.GetError())
|
error = str(result.GetError())
|
||||||
self.report('success="%d",output="%s",error="%s"' % (success, output, error))
|
self.report('success="%d",output="%s",error="%s"' % (success, output, error))
|
||||||
|
|
||||||
def fetchLocals(self, args):
|
|
||||||
self.output = ''
|
|
||||||
self.expandedINames = set(args.get('expanded', []))
|
|
||||||
self.autoDerefPointers = int(args.get('autoderef', '0'))
|
|
||||||
self.sortStructMembers = bool(args.get("sortStructMembers", True));
|
|
||||||
self.useDynamicType = int(args.get('dyntype', '0'))
|
|
||||||
self.useFancy = int(args.get('fancy', '0'))
|
|
||||||
self.passExceptions = int(args.get('passexceptions', '0'))
|
|
||||||
self.currentWatchers = args.get('watchers', {})
|
|
||||||
self.typeformats = args.get("typeformats", {})
|
|
||||||
self.formats = args.get("formats", {})
|
|
||||||
self.reportVariables(args)
|
|
||||||
|
|
||||||
def fetchDisassembler(self, args):
|
def fetchDisassembler(self, args):
|
||||||
functionName = args.get('function', '')
|
functionName = args.get('function', '')
|
||||||
flavor = args.get('flavor', '')
|
flavor = args.get('flavor', '')
|
||||||
@@ -1744,17 +1736,13 @@ class Dumper(DumperBase):
|
|||||||
value = self.hexdecode(args['value'])
|
value = self.hexdecode(args['value'])
|
||||||
lhs = self.findValueByExpression(exp)
|
lhs = self.findValueByExpression(exp)
|
||||||
lhs.SetValueFromCString(value, error)
|
lhs.SetValueFromCString(value, error)
|
||||||
self.reportResult(describeError(error), args)
|
self.reportResult(self.describeError(error), args)
|
||||||
|
|
||||||
def createResolvePendingBreakpointsHookBreakpoint(self, args):
|
def createResolvePendingBreakpointsHookBreakpoint(self, args):
|
||||||
if self.qmlTriggeredBreakpoint is None:
|
bp = self.target.BreakpointCreateByName("qt_qmlDebugConnectorOpen")
|
||||||
self.qmlTriggeredBreakpoint = \
|
|
||||||
self.target.BreakpointCreateByName("qt_v4TriggeredBreakpointHook")
|
|
||||||
|
|
||||||
bp = self.target.BreakpointCreateByName("qt_v4ResolvePendingBreakpointsHook")
|
|
||||||
bp.SetOneShot(True)
|
bp.SetOneShot(True)
|
||||||
self.qmlBreakpointResolvers[bp] = lambda: \
|
self.interpreterBreakpointResolvers.append(
|
||||||
self.doInsertQmlBreakpoint(args)
|
lambda: self.doInsertInterpreterBreakpoint(args, True))
|
||||||
|
|
||||||
|
|
||||||
# Used in dumper auto test.
|
# Used in dumper auto test.
|
||||||
@@ -1825,7 +1813,7 @@ class Tester(Dumper):
|
|||||||
if line != 0:
|
if line != 0:
|
||||||
self.report = savedReport
|
self.report = savedReport
|
||||||
self.process.SetSelectedThread(stoppedThread)
|
self.process.SetSelectedThread(stoppedThread)
|
||||||
self.reportVariables({'token':2})
|
self.fetchVariables({'token':2, 'fancy':1})
|
||||||
#self.describeLocation(frame)
|
#self.describeLocation(frame)
|
||||||
self.report("@NS@%s@" % self.qtNamespace())
|
self.report("@NS@%s@" % self.qtNamespace())
|
||||||
#self.report("ENV=%s" % os.environ.items())
|
#self.report("ENV=%s" % os.environ.items())
|
||||||
|
|||||||
@@ -3272,54 +3272,14 @@ void GdbEngine::handleStackListFrames(const DebuggerResponse &response, bool isF
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<StackFrame> stackFrames;
|
GdbMi frames = response.data["stack"]; // C++
|
||||||
|
if (!frames.isValid() || frames.childCount() == 0) { // Mixed.
|
||||||
GdbMi stack = response.data["stack"]; // C++
|
frames.fromStringMultiple(response.consoleStreamOutput);
|
||||||
if (!stack.isValid() || stack.childCount() == 0) { // Mixed.
|
frames = frames["frames"];
|
||||||
stack.fromStringMultiple(response.consoleStreamOutput);
|
|
||||||
stack = stack["frames"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stack.isValid()) {
|
stackHandler()->setFramesAndCurrentIndex(frames, isFull);
|
||||||
qDebug() << "FIXME: stack:" << stack.toString();
|
activateFrame(stackHandler()->currentIndex());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int targetFrame = -1;
|
|
||||||
|
|
||||||
int n = stack.childCount();
|
|
||||||
for (int i = 0; i != n; ++i) {
|
|
||||||
stackFrames.append(StackFrame::parseFrame(stack.childAt(i), runParameters()));
|
|
||||||
const StackFrame &frame = stackFrames.back();
|
|
||||||
|
|
||||||
// Initialize top frame to the first valid frame.
|
|
||||||
const bool isValid = frame.isUsable() && !frame.function.isEmpty();
|
|
||||||
if (isValid && targetFrame == -1)
|
|
||||||
targetFrame = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool canExpand = !isFull && (n >= action(MaximalStackDepth)->value().toInt());
|
|
||||||
action(ExpandStack)->setEnabled(canExpand);
|
|
||||||
stackHandler()->setFrames(stackFrames, canExpand);
|
|
||||||
|
|
||||||
// We can't jump to any file if we don't have any frames.
|
|
||||||
if (stackFrames.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// targetFrame contains the top most frame for which we have source
|
|
||||||
// information. That's typically the frame we'd like to jump to, with
|
|
||||||
// a few exceptions:
|
|
||||||
|
|
||||||
// Always jump to frame #0 when stepping by instruction.
|
|
||||||
if (boolSetting(OperateByInstruction))
|
|
||||||
targetFrame = 0;
|
|
||||||
|
|
||||||
// If there is no frame with source, jump to frame #0.
|
|
||||||
if (targetFrame == -1)
|
|
||||||
targetFrame = 0;
|
|
||||||
|
|
||||||
stackHandler()->setCurrentIndex(targetFrame);
|
|
||||||
activateFrame(targetFrame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::activateFrame(int frameIndex)
|
void GdbEngine::activateFrame(int frameIndex)
|
||||||
|
|||||||
@@ -335,6 +335,7 @@ void LldbEngine::setupInferior()
|
|||||||
cmd2.arg("breakOnMain", rp.breakOnMain);
|
cmd2.arg("breakOnMain", rp.breakOnMain);
|
||||||
cmd2.arg("useTerminal", rp.useTerminal);
|
cmd2.arg("useTerminal", rp.useTerminal);
|
||||||
cmd2.arg("startMode", rp.startMode);
|
cmd2.arg("startMode", rp.startMode);
|
||||||
|
cmd2.arg("nativemixed", isNativeMixedActive());
|
||||||
|
|
||||||
QJsonArray processArgs;
|
QJsonArray processArgs;
|
||||||
foreach (const QString &arg, args.toUnixArgs())
|
foreach (const QString &arg, args.toUnixArgs())
|
||||||
@@ -757,8 +758,9 @@ void LldbEngine::fetchStack(int limit)
|
|||||||
cmd.arg("context", stackHandler()->currentFrame().context);
|
cmd.arg("context", stackHandler()->currentFrame().context);
|
||||||
cmd.callback = [this](const DebuggerResponse &response) {
|
cmd.callback = [this](const DebuggerResponse &response) {
|
||||||
const GdbMi &stack = response.data["stack"];
|
const GdbMi &stack = response.data["stack"];
|
||||||
stackHandler()->setAllFrames(stack["frames"], stack["hasmore"].toInt());
|
const bool isFull = !stack["hasmore"].toInt();
|
||||||
updateLocals();
|
stackHandler()->setFramesAndCurrentIndex(stack["frames"], isFull);
|
||||||
|
activateFrame(stackHandler()->currentIndex());
|
||||||
};
|
};
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
@@ -782,15 +784,9 @@ void LldbEngine::assignValueInDebugger(WatchItem *,
|
|||||||
|
|
||||||
void LldbEngine::doUpdateLocals(const UpdateParameters ¶ms)
|
void LldbEngine::doUpdateLocals(const UpdateParameters ¶ms)
|
||||||
{
|
{
|
||||||
if (stackHandler()->stackSize() == 0) {
|
|
||||||
showMessage(_("SKIPPING LOCALS DUE TO EMPTY STACK"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
watchHandler()->notifyUpdateStarted(params.partialVariables());
|
watchHandler()->notifyUpdateStarted(params.partialVariables());
|
||||||
|
|
||||||
DebuggerCommand cmd("fetchLocals");
|
DebuggerCommand cmd("fetchVariables");
|
||||||
cmd.arg("nativemixed", isNativeMixedActive());
|
|
||||||
watchHandler()->appendFormatRequests(&cmd);
|
watchHandler()->appendFormatRequests(&cmd);
|
||||||
watchHandler()->appendWatchersAndTooltipRequests(&cmd);
|
watchHandler()->appendWatchersAndTooltipRequests(&cmd);
|
||||||
|
|
||||||
@@ -802,6 +798,10 @@ void LldbEngine::doUpdateLocals(const UpdateParameters ¶ms)
|
|||||||
cmd.arg("partialVariable", params.partialVariable);
|
cmd.arg("partialVariable", params.partialVariable);
|
||||||
cmd.arg("sortStructMembers", boolSetting(SortStructMembers));
|
cmd.arg("sortStructMembers", boolSetting(SortStructMembers));
|
||||||
|
|
||||||
|
StackFrame frame = stackHandler()->currentFrame();
|
||||||
|
cmd.arg("context", frame.context);
|
||||||
|
cmd.arg("nativemixed", isNativeMixedActive());
|
||||||
|
|
||||||
//cmd.arg("resultvarname", m_resultVarName);
|
//cmd.arg("resultvarname", m_resultVarName);
|
||||||
|
|
||||||
m_lastDebuggableCommand = cmd;
|
m_lastDebuggableCommand = cmd;
|
||||||
@@ -908,7 +908,6 @@ void LldbEngine::handleStateNotification(const GdbMi &reportedState)
|
|||||||
}
|
}
|
||||||
} else if (newState == "inferiorstopok") {
|
} else if (newState == "inferiorstopok") {
|
||||||
notifyInferiorStopOk();
|
notifyInferiorStopOk();
|
||||||
updateAll();
|
|
||||||
} else if (newState == "inferiorstopfailed")
|
} else if (newState == "inferiorstopfailed")
|
||||||
notifyInferiorStopFailed();
|
notifyInferiorStopFailed();
|
||||||
else if (newState == "inferiorill")
|
else if (newState == "inferiorill")
|
||||||
@@ -941,16 +940,22 @@ void LldbEngine::handleStateNotification(const GdbMi &reportedState)
|
|||||||
|
|
||||||
void LldbEngine::handleLocationNotification(const GdbMi &reportedLocation)
|
void LldbEngine::handleLocationNotification(const GdbMi &reportedLocation)
|
||||||
{
|
{
|
||||||
qulonglong addr = reportedLocation["addr"].toAddress();
|
qulonglong address = reportedLocation["address"].toAddress();
|
||||||
QString file = reportedLocation["file"].toUtf8();
|
QString fileName = reportedLocation["file"].toUtf8();
|
||||||
int line = reportedLocation["line"].toInt();
|
QByteArray function = reportedLocation["function"].data();
|
||||||
Location loc = Location(file, line);
|
int lineNumber = reportedLocation["line"].toInt();
|
||||||
if (boolSetting(OperateByInstruction) || !QFileInfo::exists(file) || line <= 0) {
|
Location loc = Location(fileName, lineNumber);
|
||||||
loc = Location(addr);
|
if (boolSetting(OperateByInstruction) || !QFileInfo::exists(fileName) || lineNumber <= 0) {
|
||||||
|
loc = Location(address);
|
||||||
loc.setNeedsMarker(true);
|
loc.setNeedsMarker(true);
|
||||||
loc.setUseAssembler(true);
|
loc.setUseAssembler(true);
|
||||||
}
|
}
|
||||||
gotoLocation(loc);
|
|
||||||
|
// Quickly set the location marker.
|
||||||
|
if (lineNumber > 0
|
||||||
|
&& QFileInfo::exists(fileName)
|
||||||
|
&& function != "::qt_qmlDebugEventFromService()")
|
||||||
|
gotoLocation(Location(fileName, lineNumber));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LldbEngine::reloadRegisters()
|
void LldbEngine::reloadRegisters()
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "debuggeractions.h"
|
#include "debuggeractions.h"
|
||||||
#include "debuggercore.h"
|
#include "debuggercore.h"
|
||||||
#include "debuggerengine.h"
|
#include "debuggerengine.h"
|
||||||
|
#include "debuggerprotocol.h"
|
||||||
#include "simplifytype.h"
|
#include "simplifytype.h"
|
||||||
|
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
@@ -168,12 +169,6 @@ StackFrame StackHandler::currentFrame() const
|
|||||||
return m_stackFrames.at(m_currentIndex);
|
return m_stackFrames.at(m_currentIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackHandler::setAllFrames(const GdbMi &frames, bool canExpand)
|
|
||||||
{
|
|
||||||
action(ExpandStack)->setEnabled(canExpand);
|
|
||||||
setFrames(StackFrame::parseFrames(frames, m_engine->runParameters()), canExpand);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StackHandler::setCurrentIndex(int level)
|
void StackHandler::setCurrentIndex(int level)
|
||||||
{
|
{
|
||||||
if (level == -1 || level == m_currentIndex)
|
if (level == -1 || level == m_currentIndex)
|
||||||
@@ -214,6 +209,45 @@ void StackHandler::setFrames(const StackFrames &frames, bool canExpand)
|
|||||||
emit stackChanged();
|
emit stackChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StackHandler::setFramesAndCurrentIndex(const GdbMi &frames, bool isFull)
|
||||||
|
{
|
||||||
|
int targetFrame = -1;
|
||||||
|
|
||||||
|
StackFrames stackFrames;
|
||||||
|
const int n = frames.childCount();
|
||||||
|
for (int i = 0; i != n; ++i) {
|
||||||
|
stackFrames.append(StackFrame::parseFrame(frames.childAt(i), m_engine->runParameters()));
|
||||||
|
const StackFrame &frame = stackFrames.back();
|
||||||
|
|
||||||
|
// Initialize top frame to the first valid frame.
|
||||||
|
const bool isValid = frame.isUsable() && !frame.function.isEmpty();
|
||||||
|
if (isValid && targetFrame == -1)
|
||||||
|
targetFrame = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canExpand = !isFull && (n >= action(MaximalStackDepth)->value().toInt());
|
||||||
|
action(ExpandStack)->setEnabled(canExpand);
|
||||||
|
setFrames(stackFrames, canExpand);
|
||||||
|
|
||||||
|
// We can't jump to any file if we don't have any frames.
|
||||||
|
if (stackFrames.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// targetFrame contains the top most frame for which we have source
|
||||||
|
// information. That's typically the frame we'd like to jump to, with
|
||||||
|
// a few exceptions:
|
||||||
|
|
||||||
|
// Always jump to frame #0 when stepping by instruction.
|
||||||
|
if (boolSetting(OperateByInstruction))
|
||||||
|
targetFrame = 0;
|
||||||
|
|
||||||
|
// If there is no frame with source, jump to frame #0.
|
||||||
|
if (targetFrame == -1)
|
||||||
|
targetFrame = 0;
|
||||||
|
|
||||||
|
setCurrentIndex(targetFrame);
|
||||||
|
}
|
||||||
|
|
||||||
void StackHandler::prependFrames(const StackFrames &frames)
|
void StackHandler::prependFrames(const StackFrames &frames)
|
||||||
{
|
{
|
||||||
if (frames.isEmpty())
|
if (frames.isEmpty())
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ public:
|
|||||||
~StackHandler();
|
~StackHandler();
|
||||||
|
|
||||||
void setFrames(const StackFrames &frames, bool canExpand = false);
|
void setFrames(const StackFrames &frames, bool canExpand = false);
|
||||||
|
void setFramesAndCurrentIndex(const GdbMi &frames, bool isFull);
|
||||||
|
int updateTargetFrame(bool isFull);
|
||||||
void prependFrames(const StackFrames &frames);
|
void prependFrames(const StackFrames &frames);
|
||||||
const StackFrames &frames() const;
|
const StackFrames &frames() const;
|
||||||
void setCurrentIndex(int index);
|
void setCurrentIndex(int index);
|
||||||
@@ -68,7 +70,6 @@ public:
|
|||||||
const StackFrame &frameAt(int index) const { return m_stackFrames.at(index); }
|
const StackFrame &frameAt(int index) const { return m_stackFrames.at(index); }
|
||||||
int stackSize() const { return m_stackFrames.size(); }
|
int stackSize() const { return m_stackFrames.size(); }
|
||||||
quint64 topAddress() const { return m_stackFrames.at(0).address; }
|
quint64 topAddress() const { return m_stackFrames.at(0).address; }
|
||||||
void setAllFrames(const GdbMi &frames, bool canExpand);
|
|
||||||
|
|
||||||
// Called from StackHandler after a new stack list has been received
|
// Called from StackHandler after a new stack list has been received
|
||||||
void removeAll();
|
void removeAll();
|
||||||
|
|||||||
Reference in New Issue
Block a user