OSDN Git Service

CDB: Fix missing session cleanup on inferior exit, new state model
authorFriedemann Kleint <Friedemann.Kleint@nokia.com>
Wed, 30 Sep 2009 09:44:04 +0000 (11:44 +0200)
committerFriedemann Kleint <Friedemann.Kleint@nokia.com>
Wed, 30 Sep 2009 09:44:04 +0000 (11:44 +0200)
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
src/plugins/debugger/cdb/cdbdebugengine.h
src/plugins/debugger/cdb/cdbdebugengine_p.h

index 73b773e..834ce2c 100644 (file)
@@ -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, &currentThreadId))) {
         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);
index cbd5210..740cbfe 100644 (file)
@@ -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();
index 7e81359..e5b4faf 100644 (file)
@@ -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);