From 48850dfa4d47cd8ae6ac871a0438c9c164e073f5 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 18 Jul 2018 11:58:50 +0200 Subject: [PATCH] Debugger: add callback for interrupt inferior This allows to run any command without knowing the current state of the debugger engine. Replacing the old special stop mode for any command that may be triggered while the engine is running, with a check in runCommand. If the engine is running while a command is triggered a callback is added to the interrupt that will execute the command as soon as the engine is accessible again. Change-Id: I8bb1914b92da2b03e76c2c2ec8293d13041c72fd Reviewed-by: Christian Stenger --- src/plugins/debugger/cdb/cdbengine.cpp | 199 ++++++------------------- src/plugins/debugger/cdb/cdbengine.h | 24 ++- 2 files changed, 54 insertions(+), 169 deletions(-) diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 951d3ad4964..b431441459c 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -151,35 +151,6 @@ namespace Internal { static const char localsPrefixC[] = "local."; -struct MemoryViewCookie -{ - explicit MemoryViewCookie(MemoryAgent *a = nullptr, quint64 addr = 0, quint64 l = 0) - : agent(a), address(addr), length(l) - {} - - MemoryAgent *agent; - quint64 address; - quint64 length; -}; - -struct MemoryChangeCookie -{ - explicit MemoryChangeCookie(quint64 addr = 0, const QByteArray &d = QByteArray()) : - address(addr), data(d) {} - - quint64 address; - QByteArray data; -}; - -} // namespace Internal -} // namespace Debugger - -Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie) -Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie) - -namespace Debugger { -namespace Internal { - // Base data structure for command queue entries with callback class CdbCommand { @@ -251,11 +222,10 @@ void CdbEngine::init() { m_effectiveStartMode = NoStartMode; m_accessible = false; - m_specialStopMode = NoSpecialStop; + m_stopMode = NoStopRequested; m_nextCommandToken = 0; m_currentBuiltinResponseToken = -1; - m_operateByInstructionPending = action(OperateByInstruction)->isChecked(); - m_operateByInstruction = true; // Default CDB setting + m_operateByInstruction = true; m_hasDebuggee = false; m_sourceStepInto = false; m_watchPointX = m_watchPointY = 0; @@ -270,7 +240,7 @@ void CdbEngine::init() m_pendingBreakpointMap.clear(); m_insertSubBreakpointMap.clear(); m_pendingSubBreakpointMap.clear(); - m_customSpecialStopData.clear(); + m_interrupCallbacks.clear(); m_symbolAddressCache.clear(); m_coreStopReason.reset(); @@ -303,19 +273,8 @@ CdbEngine::~CdbEngine() void CdbEngine::operateByInstructionTriggered(bool operateByInstruction) { - // To be set next time session becomes accessible - m_operateByInstructionPending = operateByInstruction; - if (state() == InferiorStopOk) - syncOperateByInstruction(operateByInstruction); -} - -void CdbEngine::syncOperateByInstruction(bool operateByInstruction) -{ - if (debug) - qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction); if (m_operateByInstruction == operateByInstruction) return; - QTC_ASSERT(m_accessible, return); m_operateByInstruction = operateByInstruction; runCommand({QLatin1String(m_operateByInstruction ? "l-t" : "l+t"), NoFlags}); runCommand({QLatin1String(m_operateByInstruction ? "l-s" : "l+s"), NoFlags}); @@ -566,6 +525,7 @@ void CdbEngine::handleInitialSessionIdle() const DebuggerRunParameters &rp = runParameters(); if (!rp.commandsAfterConnect.isEmpty()) runCommand({rp.commandsAfterConnect, NoFlags}); + operateByInstructionTriggered(action(OperateByInstruction)->isChecked()); // QmlCppEngine expects the QML engine to be connected before any breakpoints are hit // (attemptBreakpointSynchronization() will be directly called then) attemptBreakpointSynchronization(); @@ -864,14 +824,7 @@ void CdbEngine::interruptInferior() notifyInferiorRunOk(); return; } - doInterruptInferior(NoSpecialStop); -} - -void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v) -{ - if (m_specialStopMode == NoSpecialStop) - doInterruptInferior(CustomSpecialStop); - m_customSpecialStopData.push_back(v); + doInterruptInferior(); } void CdbEngine::handleDoInterruptInferior(const QString &errorMessage) @@ -886,11 +839,20 @@ void CdbEngine::handleDoInterruptInferior(const QString &errorMessage) m_signalOperation.clear(); } -void CdbEngine::doInterruptInferior(SpecialStopMode sm) +void CdbEngine::doInterruptInferior(const InterruptCallback &callback) { - showMessage(QString("Interrupting process %1...").arg(inferiorPid()), LogMisc); + if (callback) { + m_interrupCallbacks.push_back(callback); + if (!m_initialSessionIdleHandled) + return; + if (m_stopMode == NoStopRequested) + m_stopMode = Callback; + } else { + m_stopMode = Interrupt; + } - QTC_ASSERT(!m_signalOperation, notifyInferiorStopFailed(); return;); + showMessage(QString("Interrupting process %1...").arg(inferiorPid()), LogMisc); + QTC_ASSERT(!m_signalOperation, notifyInferiorStopFailed(); return); if (DebuggerRunTool *rt = runTool()) { IDevice::ConstPtr device = rt->device(); if (!device) @@ -898,7 +860,6 @@ void CdbEngine::doInterruptInferior(SpecialStopMode sm) if (device) m_signalOperation = device->signalOperation(); } - m_specialStopMode = sm; QTC_ASSERT(m_signalOperation, notifyInferiorStopFailed(); return;); connect(m_signalOperation.data(), &DeviceProcessSignalOperation::finished, this, &CdbEngine::handleDoInterruptInferior); @@ -1064,9 +1025,12 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd) { QString cmd = dbgCmd.function + dbgCmd.argsToString(); if (!m_accessible) { - const QString msg = QString("Attempt to issue command \"%1\" to non-accessible session (%2)") + doInterruptInferior([this, dbgCmd](){ + runCommand(dbgCmd); + }); + const QString msg = QString("Attempt to issue command \"%1\" to non-accessible session (%2)... interrupting") .arg(cmd, stateName(state())); - showMessage(msg, LogError); + showMessage(msg, LogMisc); return; } @@ -1492,34 +1456,26 @@ void CdbEngine::handleResolveSymbolHelper(const QList &addresses, Disas } } -void CdbEngine::fetchMemory(MemoryAgent *agent, quint64 addr, quint64 length) +void CdbEngine::fetchMemory(MemoryAgent *agent, quint64 address, quint64 length) { if (debug) - qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr); - const MemoryViewCookie cookie(agent, addr, length); - if (m_accessible) - postFetchMemory(cookie); - else - doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie)); -} - -void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie) -{ + qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, address); DebuggerCommand cmd("memory", ExtensionCommand); QString args; StringInputStream str(args); - str << cookie.address << ' ' << cookie.length; + str << address << ' ' << length; cmd.args = args; - cmd.callback = [this, cookie](const DebuggerResponse &response) { - if (!cookie.agent) + cmd.callback = [this, agent = QPointer(agent), address, length] + (const DebuggerResponse &response) { + if (!agent) return; if (response.resultClass == ResultDone) { const QByteArray data = QByteArray::fromHex(response.data.data().toUtf8()); - if (unsigned(data.size()) == cookie.length) - cookie.agent->addData(cookie.address, data); + if (unsigned(data.size()) == length) + agent->addData(address, data); } else { showMessage(response.data["msg"].data(), LogWarning); - cookie.agent->addData(cookie.address, QByteArray(int(cookie.length), char())); + agent->addData(address, QByteArray(int(length), char())); } }; runCommand(cmd); @@ -1528,12 +1484,7 @@ void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie) void CdbEngine::changeMemory(MemoryAgent *, quint64 addr, const QByteArray &data) { QTC_ASSERT(!data.isEmpty(), return); - if (!m_accessible) { - const MemoryChangeCookie cookie(addr, data); - doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie)); - } else { - runCommand({cdbWriteMemoryCommand(addr, data), NoFlags}); - } + runCommand({cdbWriteMemoryCommand(addr, data), NoFlags}); } void CdbEngine::reloadModules() @@ -1820,6 +1771,8 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason, return rc; } if (reason == "exception") { + if (m_stopMode == Callback) + rc |= StopIgnoreContinue; WinException exception; exception.fromGdbMI(stopReason); QString description = exception.toString(); @@ -1862,6 +1815,7 @@ void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointT ThreadId forcedThreadId; const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage, conditionalBreakPointTriggered); + m_stopMode = NoStopRequested; // Do the non-blocking log reporting if (stopFlags & StopReportLog) showMessage(message, LogMisc); @@ -2049,9 +2003,9 @@ void CdbEngine::handleSessionAccessible(unsigned long cdbExState) return; if (debug) - qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d", + qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s'", elapsedLogTime(), cdbStatusName(cdbExState), - qPrintable(stateName(state())), m_specialStopMode); + qPrintable(stateName(state()))); switch (s) { case EngineShutdownRequested: @@ -2074,9 +2028,9 @@ void CdbEngine::handleSessionInaccessible(unsigned long cdbExState) return; if (debug) - qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d", + qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s'", elapsedLogTime(), cdbStatusName(cdbExState), - qPrintable(stateName(state())), m_specialStopMode); + qPrintable(stateName(state()))); switch (state()) { case EngineSetupRequested: @@ -2113,37 +2067,13 @@ void CdbEngine::handleSessionIdle(const QString &message) return; if (debug) - qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d", + qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s'", elapsedLogTime(), qPrintable(message), - qPrintable(stateName(state())), m_specialStopMode); + qPrintable(stateName(state()))); - // Switch source level debugging - syncOperateByInstruction(m_operateByInstructionPending); - - // Engine-special stop reasons: Breakpoints and setup - const SpecialStopMode specialStopMode = m_specialStopMode; - - m_specialStopMode = NoSpecialStop; - - switch (specialStopMode) { - case SpecialStopSynchronizeBreakpoints: - if (debug) - qDebug("attemptBreakpointSynchronization in special stop"); - attemptBreakpointSynchronization(); - doContinueInferior(); - return; - case SpecialStopGetWidgetAt: - postWidgetAtCommand(); - return; - case CustomSpecialStop: - foreach (const QVariant &data, m_customSpecialStopData) - handleCustomSpecialStop(data); - m_customSpecialStopData.clear(); - doContinueInferior(); - return; - case NoSpecialStop: - break; - } + for (const InterruptCallback &callback : m_interrupCallbacks) + callback(); + m_interrupCallbacks.clear(); if (!m_initialSessionIdleHandled) { // Temporary stop at beginning handleInitialSessionIdle(); @@ -2603,12 +2533,6 @@ void CdbEngine::attemptBreakpointSynchronization() if (!changed) return; - if (!m_accessible) { - // No nested calls. - if (m_specialStopMode != SpecialStopSynchronizeBreakpoints) - doInterruptInferior(SpecialStopSynchronizeBreakpoints); - return; - } // Add/Change breakpoints and store pending ones in map, since // Breakhandler::setResponse() on pending breakpoints clears the pending flag. // handleBreakPoints will the complete that information and set it on the break handler. @@ -3076,44 +3000,11 @@ void CdbEngine::watchPoint(const QPoint &p) { m_watchPointX = p.x(); m_watchPointY = p.y(); - switch (state()) { - case InferiorStopOk: - postWidgetAtCommand(); - break; - case InferiorRunOk: - // "Select Widget to Watch" from a running application is currently not - // supported. It could be implemented via SpecialStopGetWidgetAt-mode, - // but requires some work as not to confuse the engine by state-change notifications - // emitted by the debuggee function call. - showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning); - break; - default: - showMessage(tr("\"Select Widget to Watch\": Not supported in state \"%1\"."). - arg(stateName(state())), LogWarning); - break; - } -} - -void CdbEngine::postWidgetAtCommand() -{ DebuggerCommand cmd("widgetat", ExtensionCommand); - cmd.args = QString("%1 %2").arg(m_watchPointX, m_watchPointY); + cmd.args = QString("%1 %2").arg(p.x(), p.y()); runCommand(cmd); } -void CdbEngine::handleCustomSpecialStop(const QVariant &v) -{ - if (v.canConvert()) { - const MemoryChangeCookie changeData = qvariant_cast(v); - runCommand({cdbWriteMemoryCommand(changeData.address, changeData.data), NoFlags}); - return; - } - if (v.canConvert()) { - postFetchMemory(qvariant_cast(v)); - return; - } -} - } // namespace Internal } // namespace Debugger diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index 97bfa62821a..6f6e7089e8a 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -129,13 +129,12 @@ private: bool exists; }; - enum SpecialStopMode - { - NoSpecialStop, - SpecialStopSynchronizeBreakpoints, - SpecialStopGetWidgetAt, - CustomSpecialStop // Associated with m_specialStopData, handleCustomSpecialStop() + enum StopMode { + NoStopRequested, + Interrupt, + Callback }; + enum ParseStackResultFlags // Flags returned by parseStackTrace { ParseStackStepInto = 1, // Need to execute a step, hit on a call frame in "Step into" @@ -160,16 +159,12 @@ private: void handleSessionAccessible(unsigned long cdbExState); void handleSessionInaccessible(unsigned long cdbExState); void handleSessionIdle(const QString &message); - void doInterruptInferior(SpecialStopMode sm); - void doInterruptInferiorCustomSpecialStop(const QVariant &v); + using InterruptCallback = std::function; + void doInterruptInferior(const InterruptCallback &cb = InterruptCallback()); void doContinueInferior(); void parseOutputLine(QString line); bool isCdbProcessRunning() const { return m_process.state() != QProcess::NotRunning; } bool canInterruptInferior() const; - void syncOperateByInstruction(bool operateByInstruction); - void postWidgetAtCommand(); - void handleCustomSpecialStop(const QVariant &v); - void postFetchMemory(const MemoryViewCookie &c); inline void postDisassemblerCommand(quint64 address, DisassemblerAgent *agent); void postDisassemblerCommand(quint64 address, quint64 endAddress, DisassemblerAgent *agent); @@ -215,7 +210,7 @@ private: QByteArray m_outputBuffer; //! Debugger accessible (expecting commands) bool m_accessible = false; - SpecialStopMode m_specialStopMode = NoSpecialStop; + StopMode m_stopMode = NoStopRequested; ProjectExplorer::DeviceProcessSignalOperation::Ptr m_signalOperation; int m_nextCommandToken = 0; QHash m_commandForToken; @@ -223,7 +218,6 @@ private: int m_currentBuiltinResponseToken = -1; QMap m_normalizedFileCache; const QString m_extensionCommandPrefix; //!< Library name used as prefix - bool m_operateByInstructionPending = true; //!< Creator operate by instruction action changed. bool m_operateByInstruction = true; // Default CDB setting. bool m_hasDebuggee = false; enum Wow64State { @@ -245,7 +239,7 @@ private: QHash m_fileNameModuleHash; QMultiHash m_symbolAddressCache; bool m_ignoreCdbOutput = false; - QVariantList m_customSpecialStopData; + QList m_interrupCallbacks; QList m_sourcePathMappings; QScopedPointer m_coreStopReason; int m_pythonVersion = 0; // 0xMMmmpp MM = major; mm = minor; pp = patch