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:
hjk
2015-10-09 15:00:20 +02:00
parent d6ef70573d
commit ec2e01faec
7 changed files with 176 additions and 177 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 &params) void LldbEngine::doUpdateLocals(const UpdateParameters &params)
{ {
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 &params)
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()

View File

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

View File

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