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