From ec2e01faec87e273486b81d0c3bf8ded2619c047 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 9 Oct 2015 15:00:20 +0200 Subject: [PATCH] Debugger: Make basic native-mixed debugging work with LLDB Change-Id: I4d55c6a486d5adbccaa93eaa1ee461238fecfea3 Reviewed-by: Christian Stenger --- share/qtcreator/debugger/dumper.py | 35 +++++- share/qtcreator/debugger/gdbbridge.py | 30 ++--- share/qtcreator/debugger/lldbbridge.py | 148 +++++++++++------------ src/plugins/debugger/gdb/gdbengine.cpp | 52 +------- src/plugins/debugger/lldb/lldbengine.cpp | 39 +++--- src/plugins/debugger/stackhandler.cpp | 46 ++++++- src/plugins/debugger/stackhandler.h | 3 +- 7 files changed, 176 insertions(+), 177 deletions(-) diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index efc5249e4e3..dfe5f7da155 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -841,9 +841,32 @@ class DumperBase: return '{' + ','.join(['%s=%s' % (k, self.dictToMi(v)) for (k, v) in list(value.items())]) + '}' if type(value) is list: - return '[' + ','.join([self.dictToMi(v) for v in value]) + ']' + 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 + 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): self.put('%s="%s",' % (name, value)) @@ -1763,7 +1786,7 @@ class DumperBase: buf = self.parseAndEvaluate("qt_qmlDebugEventBuffer") size = self.parseAndEvaluate("qt_qmlDebugEventLength") resdict = self.readJsonFromMemory(buf, size) - warn("RES DICT : %s" % resdict) + warn("Interpreter event received: %s" % resdict) return resdict.get('event') == 'break' def removeInterpreterBreakpoint(self, args): @@ -1779,8 +1802,12 @@ class DumperBase: def sendInterpreterRequest(self, command, args = {}): self.interpreterSeq += 1 - cmd = { 'seq': self.interpreterSeq, 'type': 'request', 'command': command, 'arguments': args } - encoded = json.dumps(cmd) + encoded = json.dumps({ + 'seq': self.interpreterSeq, + 'type': 'request', + 'command': command, + 'arguments': args + }) hexdata = self.hexencode(encoded) expr = 'qt_qmlDebugSendDataToService("NativeQmlDebugger","%s")' % hexdata try: diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index 526e0359faf..c6baf25b9bd 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -220,12 +220,11 @@ class Dumper(DumperBase): # These values will be kept between calls to 'fetchVariables'. self.isGdb = True - self.childEventAddress = None self.typeCache = {} self.typesReported = {} self.typesToReport = {} self.qtNamespaceToReport = None - self.interpreterBreakpoints = [] + self.interpreterBreakpointResolvers = [] def prepare(self, args): self.output = [] @@ -359,13 +358,10 @@ class Dumper(DumperBase): partialVariable = args.get("partialVariable", "") isPartial = len(partialVariable) > 0 - if self.nativeMixed: - context = args.get('context', '') - if len(context): - res = self.extractInterpreterVariables(args) - if res: - safePrint('data=%s' % self.dictToMi(res.get('data', {}))) - return + (ok, res) = self.tryFetchInterpreterVariables(args) + if ok: + safePrint(res) + return # # Locals @@ -1655,7 +1651,7 @@ class Dumper(DumperBase): self.enabled = False return False - self.interpreterBreakpoints.append(Resolver(self, args)) + self.interpreterBreakpointResolvers.append(Resolver(self, args)) def exitGdb(self, _): 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): def __init__(self): spec = "qt_qmlDebugEventFromService" @@ -1823,7 +1807,7 @@ class QmlEngineEventBreakpoint(gdb.Breakpoint): __init__(spec, gdb.BP_BREAKPOINT, internal=True) def stop(self): - print("QML engine event received.") + print("Interpreter event received.") return theDumper.handleInterpreterEvent() QmlEngineEventBreakpoint() diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index ed4d3e4053e..f54ab9cdd8c 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -48,9 +48,6 @@ from dumper import * qqWatchpointOffset = 10000 -def warn(message): - print('\n\nWARNING="%s",\n' % message.encode("latin1").replace('"', "'")) - def showException(msg, exType, exValue, exTraceback): warn("**** CAUGHT EXCEPTION: %s ****" % msg) import traceback @@ -230,8 +227,7 @@ class Dumper(DumperBase): self.voidPtrType_ = None self.isShuttingDown_ = False self.isInterrupting_ = False - self.qmlBreakpointResolvers = {} - self.qmlTriggeredBreakpoint = None + self.interpreterBreakpointResolvers = [] self.report('lldbversion=\"%s\"' % lldb.SBDebugger.GetVersionString()) self.reportState("enginesetupok") @@ -549,7 +545,7 @@ class Dumper(DumperBase): self.report('error="%s"' % result.GetError()) def put(self, stuff): - self.out += stuff + self.output += stuff def isMovableType(self, type): if type.GetTypeClass() in (lldb.eTypeClassBuiltin, lldb.eTypeClassPointer): @@ -667,6 +663,7 @@ class Dumper(DumperBase): self.sysRoot_ = args.get('sysRoot', '') self.remoteChannel_ = args.get('remoteChannel', '') self.platform_ = args.get('platform', '') + self.nativeMixed = int(args.get('nativemixed', 0)) self.ignoreStops = 0 self.silentStops = 0 @@ -689,12 +686,15 @@ class Dumper(DumperBase): if self.sysRoot_: self.debugger.SetCurrentPlatformSDKRoot(self.sysRoot_) - if os.path.isfile(self.executable_): self.target = self.debugger.CreateTarget(self.executable_, None, None, True, error) else: 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 self.reportResult('success="%s",msg="%s",exe="%s"' % (state, error, self.executable_), args) @@ -775,9 +775,11 @@ class Dumper(DumperBase): def describeLocation(self, frame): if int(frame.pc) == 0xffffffffffffffff: return '' - file = fileNameAsString(frame.line_entry.file) + fileName = fileNameAsString(frame.line_entry.file) + function = frame.GetFunctionName() 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): return None if self.process is None else self.process.GetSelectedThread() @@ -847,8 +849,6 @@ class Dumper(DumperBase): self.reportResult('msg="No thread"', args) return - self.report(self.describeLocation(thread.GetFrameAtIndex(0))) # FIXME - isNativeMixed = int(args.get('nativemixed', 0)) limit = args.get('stacklimit', -1) @@ -868,38 +868,27 @@ class Dumper(DumperBase): pc = frame.GetPC() level = frame.idx addr = frame.GetPCAddress().GetLoadAddress(self.target) + 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) - 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 += ',level="%d"' % level result += ',address="0x%x"' % addr - if not usable is None: - result += ',usable="%s"' % usable result += ',function="%s"' % functionName result += ',line="%d"' % lineNumber - if not language is None: - result += ',language="%s"' % language result += ',file="%s"},' % fileName result += ']' result += ',hasmore="%d"' % isLimited @@ -1155,16 +1144,28 @@ class Dumper(DumperBase): with SubItem(self, child): self.putItem(child) - def reportVariables(self, args): - self.out = "" - self.reportVariablesHelper(args) - self.reportResult(self.out, args) - - def reportVariablesHelper(self, args = {}): - frame = self.currentFrame() - if frame is None: + def fetchVariables(self, args): + (ok, res) = self.tryFetchInterpreterVariables(args) + if ok: + self.reportResult(res, args) 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", "") isPartial = len(partialVariable) > 0 @@ -1232,6 +1233,7 @@ class Dumper(DumperBase): self.handleWatches(args) self.put('],partial="%d"' % isPartial) + self.reportResult(self.output, args) def fetchRegisters(self, args = None): if self.process is None: @@ -1281,7 +1283,7 @@ class Dumper(DumperBase): else: self.isInterrupting_ = True error = self.process.Stop() - self.reportResult(describeError(error), args) + self.reportResult(self.describeError(error), args) def detachInferior(self, args): if self.process is None: @@ -1330,28 +1332,31 @@ class Dumper(DumperBase): frame = stoppedThread.GetFrameAtIndex(0) #self.report("FRAME: %s" % frame) function = frame.GetFunction() - #self.report("FUNCTION: %s" % function) - if function.GetName() == "qt_v4ResolvePendingBreakpointsHook": - #self.report("RESOLVER HIT") - for bp in self.qmlBreakpointResolvers: - self.qmlBreakpointResolvers[bp]() - self.target.BreakpointDelete(bp.GetID()) - self.qmlBreakpointResolvers = {} + functionName = function.GetName() + if functionName == "::qt_qmlDebugConnectorOpen()": + self.report("RESOLVER HIT") + for resolver in self.interpreterBreakpointResolvers: + resolver() + self.report("AUTO-CONTINUE AFTER RESOLVING") + self.reportState("inferiorstopok") self.process.Continue(); 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_: self.isInterrupting_ = False - self.reportState("inferiorstopok") + self.reportState("stopped") elif self.ignoreStops > 0: self.ignoreStops -= 1 self.process.Continue() elif self.silentStops > 0: self.silentStops -= 1 - #elif bp and bp in self.qmlBreakpointResolvers: - # self.report("RESOLVER HIT") - # self.qmlBreakpointResolvers[bp]() - # self.process.Continue(); else: self.reportState("stopped") else: @@ -1645,19 +1650,6 @@ class Dumper(DumperBase): error = str(result.GetError()) 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): functionName = args.get('function', '') flavor = args.get('flavor', '') @@ -1744,17 +1736,13 @@ class Dumper(DumperBase): value = self.hexdecode(args['value']) lhs = self.findValueByExpression(exp) lhs.SetValueFromCString(value, error) - self.reportResult(describeError(error), args) + self.reportResult(self.describeError(error), args) def createResolvePendingBreakpointsHookBreakpoint(self, args): - if self.qmlTriggeredBreakpoint is None: - self.qmlTriggeredBreakpoint = \ - self.target.BreakpointCreateByName("qt_v4TriggeredBreakpointHook") - - bp = self.target.BreakpointCreateByName("qt_v4ResolvePendingBreakpointsHook") + bp = self.target.BreakpointCreateByName("qt_qmlDebugConnectorOpen") bp.SetOneShot(True) - self.qmlBreakpointResolvers[bp] = lambda: \ - self.doInsertQmlBreakpoint(args) + self.interpreterBreakpointResolvers.append( + lambda: self.doInsertInterpreterBreakpoint(args, True)) # Used in dumper auto test. @@ -1825,7 +1813,7 @@ class Tester(Dumper): if line != 0: self.report = savedReport self.process.SetSelectedThread(stoppedThread) - self.reportVariables({'token':2}) + self.fetchVariables({'token':2, 'fancy':1}) #self.describeLocation(frame) self.report("@NS@%s@" % self.qtNamespace()) #self.report("ENV=%s" % os.environ.items()) diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 5692a00245c..75a36fdfbe3 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -3272,54 +3272,14 @@ void GdbEngine::handleStackListFrames(const DebuggerResponse &response, bool isF return; } - QList stackFrames; - - GdbMi stack = response.data["stack"]; // C++ - if (!stack.isValid() || stack.childCount() == 0) { // Mixed. - stack.fromStringMultiple(response.consoleStreamOutput); - stack = stack["frames"]; + GdbMi frames = response.data["stack"]; // C++ + if (!frames.isValid() || frames.childCount() == 0) { // Mixed. + frames.fromStringMultiple(response.consoleStreamOutput); + frames = frames["frames"]; } - if (!stack.isValid()) { - qDebug() << "FIXME: stack:" << stack.toString(); - 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); + stackHandler()->setFramesAndCurrentIndex(frames, isFull); + activateFrame(stackHandler()->currentIndex()); } void GdbEngine::activateFrame(int frameIndex) diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index de99a107b34..3a04f5bb528 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -335,6 +335,7 @@ void LldbEngine::setupInferior() cmd2.arg("breakOnMain", rp.breakOnMain); cmd2.arg("useTerminal", rp.useTerminal); cmd2.arg("startMode", rp.startMode); + cmd2.arg("nativemixed", isNativeMixedActive()); QJsonArray processArgs; foreach (const QString &arg, args.toUnixArgs()) @@ -757,8 +758,9 @@ void LldbEngine::fetchStack(int limit) cmd.arg("context", stackHandler()->currentFrame().context); cmd.callback = [this](const DebuggerResponse &response) { const GdbMi &stack = response.data["stack"]; - stackHandler()->setAllFrames(stack["frames"], stack["hasmore"].toInt()); - updateLocals(); + const bool isFull = !stack["hasmore"].toInt(); + stackHandler()->setFramesAndCurrentIndex(stack["frames"], isFull); + activateFrame(stackHandler()->currentIndex()); }; runCommand(cmd); } @@ -782,15 +784,9 @@ void LldbEngine::assignValueInDebugger(WatchItem *, void LldbEngine::doUpdateLocals(const UpdateParameters ¶ms) { - if (stackHandler()->stackSize() == 0) { - showMessage(_("SKIPPING LOCALS DUE TO EMPTY STACK")); - return; - } - watchHandler()->notifyUpdateStarted(params.partialVariables()); - DebuggerCommand cmd("fetchLocals"); - cmd.arg("nativemixed", isNativeMixedActive()); + DebuggerCommand cmd("fetchVariables"); watchHandler()->appendFormatRequests(&cmd); watchHandler()->appendWatchersAndTooltipRequests(&cmd); @@ -802,6 +798,10 @@ void LldbEngine::doUpdateLocals(const UpdateParameters ¶ms) cmd.arg("partialVariable", params.partialVariable); cmd.arg("sortStructMembers", boolSetting(SortStructMembers)); + StackFrame frame = stackHandler()->currentFrame(); + cmd.arg("context", frame.context); + cmd.arg("nativemixed", isNativeMixedActive()); + //cmd.arg("resultvarname", m_resultVarName); m_lastDebuggableCommand = cmd; @@ -908,7 +908,6 @@ void LldbEngine::handleStateNotification(const GdbMi &reportedState) } } else if (newState == "inferiorstopok") { notifyInferiorStopOk(); - updateAll(); } else if (newState == "inferiorstopfailed") notifyInferiorStopFailed(); else if (newState == "inferiorill") @@ -941,16 +940,22 @@ void LldbEngine::handleStateNotification(const GdbMi &reportedState) void LldbEngine::handleLocationNotification(const GdbMi &reportedLocation) { - qulonglong addr = reportedLocation["addr"].toAddress(); - QString file = reportedLocation["file"].toUtf8(); - int line = reportedLocation["line"].toInt(); - Location loc = Location(file, line); - if (boolSetting(OperateByInstruction) || !QFileInfo::exists(file) || line <= 0) { - loc = Location(addr); + qulonglong address = reportedLocation["address"].toAddress(); + QString fileName = reportedLocation["file"].toUtf8(); + QByteArray function = reportedLocation["function"].data(); + int lineNumber = reportedLocation["line"].toInt(); + Location loc = Location(fileName, lineNumber); + if (boolSetting(OperateByInstruction) || !QFileInfo::exists(fileName) || lineNumber <= 0) { + loc = Location(address); loc.setNeedsMarker(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() diff --git a/src/plugins/debugger/stackhandler.cpp b/src/plugins/debugger/stackhandler.cpp index 39bbae9a20a..a792718d968 100644 --- a/src/plugins/debugger/stackhandler.cpp +++ b/src/plugins/debugger/stackhandler.cpp @@ -33,6 +33,7 @@ #include "debuggeractions.h" #include "debuggercore.h" #include "debuggerengine.h" +#include "debuggerprotocol.h" #include "simplifytype.h" #include @@ -168,12 +169,6 @@ StackFrame StackHandler::currentFrame() const return m_stackFrames.at(m_currentIndex); } -void StackHandler::setAllFrames(const GdbMi &frames, bool canExpand) -{ - action(ExpandStack)->setEnabled(canExpand); - setFrames(StackFrame::parseFrames(frames, m_engine->runParameters()), canExpand); -} - void StackHandler::setCurrentIndex(int level) { if (level == -1 || level == m_currentIndex) @@ -214,6 +209,45 @@ void StackHandler::setFrames(const StackFrames &frames, bool canExpand) 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) { if (frames.isEmpty()) diff --git a/src/plugins/debugger/stackhandler.h b/src/plugins/debugger/stackhandler.h index 3607eb7d6c4..19c9631b55f 100644 --- a/src/plugins/debugger/stackhandler.h +++ b/src/plugins/debugger/stackhandler.h @@ -59,6 +59,8 @@ public: ~StackHandler(); void setFrames(const StackFrames &frames, bool canExpand = false); + void setFramesAndCurrentIndex(const GdbMi &frames, bool isFull); + int updateTargetFrame(bool isFull); void prependFrames(const StackFrames &frames); const StackFrames &frames() const; void setCurrentIndex(int index); @@ -68,7 +70,6 @@ public: const StackFrame &frameAt(int index) const { return m_stackFrames.at(index); } int stackSize() const { return m_stackFrames.size(); } quint64 topAddress() const { return m_stackFrames.at(0).address; } - void setAllFrames(const GdbMi &frames, bool canExpand); // Called from StackHandler after a new stack list has been received void removeAll();