From b8df455b824f4da919b0d9402845407c00a59e59 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint <Friedemann.Kleint@nokia.com> Date: Wed, 30 Sep 2009 11:44:04 +0200 Subject: [PATCH] CDB: Fix missing session cleanup on inferior exit, new state model Adapt to new state model, make sure session is always cleaned when invoked from processExited event handler, remove calls to DebuggerManager::notifyXX(). --- src/plugins/debugger/cdb/cdbdebugengine.cpp | 243 +++++++++++++------- src/plugins/debugger/cdb/cdbdebugengine.h | 1 + src/plugins/debugger/cdb/cdbdebugengine_p.h | 3 + 3 files changed, 159 insertions(+), 88 deletions(-) diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index 73b773edae0..834ce2c18b7 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -476,6 +476,13 @@ CdbDebugEngine::~CdbDebugEngine() delete m_d; } +void CdbDebugEngine::setState(DebuggerState state, const char *func, int line) +{ + if (debugCDB) + qDebug() << "setState(" << state << ") at " << func << ':' << line; + IDebuggerEngine::setState(state); +} + void CdbDebugEngine::startWatchTimer() { if (debugCDB) @@ -571,13 +578,17 @@ void CdbDebugEnginePrivate::clearDisplay() } void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> &sp) -{ +{ + setState(AdapterStarting, Q_FUNC_INFO, __LINE__); if (m_d->m_hDebuggeeProcess) { warning(QLatin1String("Internal error: Attempt to start debugger while another process is being debugged.")); + setState(AdapterStartFailed, Q_FUNC_INFO, __LINE__); emit startFailed(); } m_d->clearDisplay(); + setState(AdapterStarted, Q_FUNC_INFO, __LINE__); + setState(InferiorPreparing, Q_FUNC_INFO, __LINE__); const DebuggerStartMode mode = sp->startMode; // Figure out dumper. @TODO: same in gdb... const QString dumperLibName = QDir::toNativeSeparators(manager()->qtDumperLibraryName()); @@ -595,7 +606,11 @@ void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> } } m_d->m_dumper->reset(dumperLibName, dumperEnabled); + + setState(InferiorPrepared, Q_FUNC_INFO, __LINE__); + setState(InferiorStarting, Q_FUNC_INFO, __LINE__); manager()->showStatusMessage("Starting Debugger", -1); + QString errorMessage; bool rc = false; bool needWatchTimer = false; @@ -631,14 +646,12 @@ void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> manager()->showStatusMessage(tr("Debugger running"), -1); if (needWatchTimer) startWatchTimer(); + emit startSuccessful(); } else { warning(errorMessage); - } - - if (rc) - emit startSuccessful(); - else + setState(InferiorStartFailed, Q_FUNC_INFO, __LINE__); emit startFailed(); + } } bool CdbDebugEngine::startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage) @@ -709,19 +722,17 @@ bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString * workingDirC, env); if (FAILED(hr)) { *errorMessage = tr("Unable to create a process '%1': %2").arg(cmd, msgDebugEngineComResult(hr)); - manager()->notifyInferiorExited(); return false; } else { m_d->m_mode = sm; } - manager()->notifyInferiorRunning(); return true; } void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle) { + m_engine->setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__); setDebuggeeHandles(reinterpret_cast<HANDLE>(processHandle), reinterpret_cast<HANDLE>(initialThreadHandle)); - manager()->notifyInferiorRunning(); ULONG currentThreadId; if (SUCCEEDED(m_cif.debugSystemObjects->GetThreadIdByHandle(initialThreadHandle, ¤tThreadId))) { m_currentThreadId = currentThreadId; @@ -743,84 +754,119 @@ void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG6 m_engine->warning(QString::fromLatin1("Handshake failed on event #%1: %2").arg(evtNr).arg(msgComFailed("SetNotifyEventHandle", hr))); } } + m_engine->setState(InferiorRunning, Q_FUNC_INFO, __LINE__); if (debugCDB) qDebug() << Q_FUNC_INFO << '\n' << executionStatusString(m_cif.debugControl); } void CdbDebugEngine::processTerminated(unsigned long exitCode) { - if (debugCDB) - qDebug() << Q_FUNC_INFO << exitCode; - - m_d->clearForRun(); + manager()->showDebuggerOutput(LogMisc, tr("The process exited with exit code %1.").arg(exitCode)); + setState(InferiorShuttingDown, Q_FUNC_INFO, __LINE__); m_d->setDebuggeeHandles(0, 0); - manager()->notifyInferiorExited(); - manager()->exitDebugger(); + m_d->clearForRun(); + setState(InferiorShutDown, Q_FUNC_INFO, __LINE__); + // Avoid calls from event handler. + QTimer::singleShot(0, manager(), SLOT(exitDebugger())); } -// End debugging using -void CdbDebugEnginePrivate::endDebugging(EndDebuggingMode em) +bool CdbDebugEnginePrivate::endInferior(EndInferiorAction action, QString *errorMessage) { - enum Action { Detach, Terminate }; - if (debugCDB) - qDebug() << Q_FUNC_INFO << em; - - if (m_mode == AttachCore || !m_hDebuggeeProcess) - return; - // Figure out action - Action action; - switch (em) { - case EndDebuggingAuto: - action = (m_mode == AttachExternal || m_mode == AttachCrashedExternal) ? - Detach : Terminate; - break; - case EndDebuggingDetach: - action = Detach; - break; - case EndDebuggingTerminate: - action = Terminate; - break; - } - if (debugCDB) - qDebug() << Q_FUNC_INFO << action; - // Need a stopped debuggee to act - QString errorMessage; + // Process must be stopped in order to terminate + m_engine->setState(InferiorShuttingDown, Q_FUNC_INFO, __LINE__); // pretend it is shutdown const bool wasRunning = isDebuggeeRunning(); - if (wasRunning) { // Process must be stopped in order to terminate - interruptInterferiorProcess(&errorMessage); + if (wasRunning) { + interruptInterferiorProcess(errorMessage); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } - HRESULT hr; + bool success = false; switch (action) { - case Detach: - hr = m_cif.debugClient->DetachCurrentProcess(); - if (FAILED(hr)) - errorMessage += msgComFailed("DetachCurrentProcess", hr); - break; - case Terminate: - hr = m_cif.debugClient->TerminateCurrentProcess(); - if (FAILED(hr)) - errorMessage += msgComFailed("TerminateCurrentProcess", hr); - if (!wasRunning) { - hr = m_cif.debugClient->TerminateProcesses(); - if (FAILED(hr)) - errorMessage += msgComFailed("TerminateProcesses", hr); + case DetachInferior: { + const HRESULT hr = m_cif.debugClient->DetachCurrentProcess(); + if (SUCCEEDED(hr)) { + success = true; + } else { + *errorMessage += msgComFailed("DetachCurrentProcess", hr); + } + } + break; + case TerminateInferior: { + do { + // The exit process event handler will not be called. + HRESULT hr = m_cif.debugClient->TerminateCurrentProcess(); + if (FAILED(hr)) { + *errorMessage += msgComFailed("TerminateCurrentProcess", hr); + break; + } + if (wasRunning) { + success = true; + break; + } + hr = m_cif.debugClient->TerminateProcesses(); + if (SUCCEEDED(hr)) { + success = true; + } else { + *errorMessage += msgComFailed("TerminateProcesses", hr); + } + } while (false); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + break; } - QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); - break; } + // Perform cleanup even when failed..no point clinging to the process setDebuggeeHandles(0, 0); m_engine->killWatchTimer(); + m_engine->setState(success ? InferiorShutDown : InferiorShutdownFailed, Q_FUNC_INFO, __LINE__); + return success; +} - // Clean up resources (open files, etc.) - hr = m_cif.debugClient->EndSession(DEBUG_END_PASSIVE); - if (FAILED(hr)) - errorMessage += msgComFailed("EndSession", hr); +// End debugging. Note that this can invoked via user action +// or the processTerminated() event handler, in which case it +// must not kill the process again. +void CdbDebugEnginePrivate::endDebugging(EndDebuggingMode em) +{ + if (debugCDB) + qDebug() << Q_FUNC_INFO << em; - if (!errorMessage.isEmpty()) { - errorMessage = QString::fromLatin1("There were errors trying to end debugging: %1").arg(errorMessage); + const DebuggerState oldState = m_engine->state(); + if (oldState == DebuggerNotReady || m_mode == AttachCore) + return; + // Do we need to stop the process? + QString errorMessage; + if (oldState != InferiorShutDown && m_hDebuggeeProcess) { + EndInferiorAction action; + switch (em) { + case EndDebuggingAuto: + action = (m_mode == AttachExternal || m_mode == AttachCrashedExternal) ? + DetachInferior : TerminateInferior; + break; + case EndDebuggingDetach: + action = DetachInferior; + break; + case EndDebuggingTerminate: + action = TerminateInferior; + break; + } + if (debugCDB) + qDebug() << Q_FUNC_INFO << action; + // Need a stopped debuggee to act + if (!endInferior(action, &errorMessage)) { + errorMessage = QString::fromLatin1("Unable to detach from/end the debuggee: %1").arg(errorMessage); + manager()->showDebuggerOutput(LogError, errorMessage); + } + errorMessage.clear(); + } + // Clean up resources (open files, etc.) + m_engine->setState(AdapterShuttingDown, Q_FUNC_INFO, __LINE__); + clearForRun(); + const HRESULT hr = m_cif.debugClient->EndSession(DEBUG_END_PASSIVE); + if (SUCCEEDED(hr)) { + m_engine->setState(DebuggerNotReady, Q_FUNC_INFO, __LINE__); + } else { + m_engine->setState(AdapterShutdownFailed, Q_FUNC_INFO, __LINE__); + m_engine->setState(DebuggerNotReady, Q_FUNC_INFO, __LINE__); + errorMessage = QString::fromLatin1("There were errors trying to end debugging: %1").arg(msgComFailed("EndSession", hr)); manager()->showDebuggerOutput(LogError, errorMessage); - m_engine->warning(errorMessage); } } @@ -904,10 +950,14 @@ void CdbDebugEngine::stepExec() m_d->clearForRun(); m_d->setCodeLevel(); + setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__); const HRESULT hr = m_d->m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_STEP_INTO); - if (FAILED(hr)) + if (SUCCEEDED(hr)) { + setState(InferiorRunning, Q_FUNC_INFO, __LINE__); + } else { + setState(InferiorStopped, Q_FUNC_INFO, __LINE__); warning(msgFunctionFailed(Q_FUNC_INFO, msgComFailed("SetExecutionStatus", hr))); - + } m_d->m_breakEventMode = CdbDebugEnginePrivate::BreakEventIgnoreOnce; startWatchTimer(); } @@ -921,7 +971,7 @@ void CdbDebugEngine::stepOutExec() const int idx = sh->currentIndex() + 1; QList<StackFrame> stackframes = sh->frames(); if (idx < 0 || idx >= stackframes.size()) { - warning(QString::fromLatin1("cannot step out")); + warning(QString::fromLatin1("Cannot step out of stack frame %1").arg(idx)); return; } @@ -961,10 +1011,13 @@ void CdbDebugEngine::nextExec() m_d->clearForRun(); m_d->setCodeLevel(); + setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__); const HRESULT hr = m_d->m_cif.debugControl->SetExecutionStatus(DEBUG_STATUS_STEP_OVER); if (SUCCEEDED(hr)) { - startWatchTimer(); + setState(InferiorRunning, Q_FUNC_INFO, __LINE__); + startWatchTimer(); } else { + setState(InferiorStopped, Q_FUNC_INFO, __LINE__); warning(msgFunctionFailed(Q_FUNC_INFO, msgComFailed("SetExecutionStatus", hr))); } } @@ -981,11 +1034,14 @@ void CdbDebugEngine::nextIExec() m_d->clearForRun(); m_d->setCodeLevel(); + setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__); const HRESULT hr = m_d->m_cif.debugControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, "p", 0); if (SUCCEEDED(hr)) { + setState(InferiorRunning, Q_FUNC_INFO, __LINE__); startWatchTimer(); } else { - warning(msgFunctionFailed(Q_FUNC_INFO, msgDebugEngineComResult(hr))); + setState(InferiorStopped, Q_FUNC_INFO, __LINE__); + warning(msgFunctionFailed(Q_FUNC_INFO, msgDebugEngineComResult(hr))); } } @@ -1017,6 +1073,7 @@ bool CdbDebugEnginePrivate::continueInferiorProcess(QString *errorMessagePtr /* // Continue process with notifications bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage) { + // Check state: Are we running? ULONG executionStatus; if (!getExecutionStatus(m_cif.debugControl, &executionStatus, errorMessage)) return false; @@ -1028,19 +1085,27 @@ bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage) m_engine->warning(QLatin1String("continueInferior() called while debuggee is running.")); return true; } + // Request continue + m_engine->setState(InferiorRunningRequested, Q_FUNC_INFO, __LINE__); + bool success = false; + do { + clearForRun(); + setCodeLevel(); + m_engine->killWatchTimer(); + manager()->resetLocation(); + manager()->showStatusMessage(CdbDebugEngine::tr("Running requested..."), 5000); - clearForRun(); - setCodeLevel(); - m_engine->killWatchTimer(); - manager()->resetLocation(); - m_engine->setState(InferiorRunningRequested); - manager()->showStatusMessage(CdbDebugEngine::tr("Running requested..."), 5000); - - if (!continueInferiorProcess(errorMessage)) - return false; + if (!continueInferiorProcess(errorMessage)) + break; - m_engine->startWatchTimer(); - manager()->notifyInferiorRunning(); + m_engine->startWatchTimer(); + success = true; + } while (false); + if (success) { + m_engine->setState(InferiorRunning, Q_FUNC_INFO, __LINE__); + } else { + m_engine->setState(InferiorStopped, Q_FUNC_INFO, __LINE__); // No RunningRequestFailed? + } return true; } @@ -1074,9 +1139,9 @@ void CdbDebugEngine::interruptInferior() return; QString errorMessage; - if (m_d->interruptInterferiorProcess(&errorMessage)) { - manager()->notifyInferiorStopped(); - } else { + setState(InferiorStopping, Q_FUNC_INFO, __LINE__); + if (!m_d->interruptInterferiorProcess(&errorMessage)) { + setState(InferiorStopFailed, Q_FUNC_INFO, __LINE__); warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage)); } } @@ -1457,7 +1522,6 @@ void CdbDebugEngine::slotConsoleStubStarted() if (startAttachDebugger(appPid, AttachExternal, &errorMessage)) { startWatchTimer(); manager()->notifyInferiorPidChanged(appPid); - manager()->notifyInferiorRunning(); } else { QMessageBox::critical(manager()->mainWindow(), tr("Debugger Error"), errorMessage); } @@ -1507,8 +1571,11 @@ void CdbDebugEnginePrivate::handleDebugEvent() switch (mode) { case BreakEventHandle: { - m_engine->setState(InferiorStopping); - manager()->notifyInferiorStopped(); + // If this is triggered by breakpoint/crash: Set state to stopping + // to avoid warnings as opposed to interrupt inferior + if (m_engine->state() != InferiorStopping) + m_engine->setState(InferiorStopping, Q_FUNC_INFO, __LINE__); + m_engine->setState(InferiorStopped, Q_FUNC_INFO, __LINE__); m_currentThreadId = updateThreadList(); ThreadsHandler *threadsHandler = manager()->threadsHandler(); const int threadIndex = threadIndexById(threadsHandler, m_currentThreadId); diff --git a/src/plugins/debugger/cdb/cdbdebugengine.h b/src/plugins/debugger/cdb/cdbdebugengine.h index cbd52105cbd..740cbfec275 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.h +++ b/src/plugins/debugger/cdb/cdbdebugengine.h @@ -114,6 +114,7 @@ private slots: void warning(const QString &w); private: + void setState(DebuggerState state, const char *func, int line); bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage); bool startDebuggerWithExecutable(DebuggerStartMode sm, QString *errorMessage); void startWatchTimer(); diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h index 7e813599d9a..e5b4fafc538 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine_p.h +++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h @@ -135,6 +135,9 @@ struct CdbDebugEnginePrivate bool attemptBreakpointSynchronization(QString *errorMessage); void notifyCrashed(); + enum EndInferiorAction { DetachInferior, TerminateInferior }; + bool endInferior(EndInferiorAction a, QString *errorMessage); + enum EndDebuggingMode { EndDebuggingDetach, EndDebuggingTerminate, EndDebuggingAuto }; void endDebugging(EndDebuggingMode em = EndDebuggingAuto); -- GitLab