[Lldb-commits] [lldb] r223272 - Correctly shutdown when DoDestroy is called with an active exception.

Zachary Turner zturner at google.com
Wed Dec 3 14:04:18 PST 2014


Author: zturner
Date: Wed Dec  3 16:04:18 2014
New Revision: 223272

URL: http://llvm.org/viewvc/llvm-project?rev=223272&view=rev
Log:
Correctly shutdown when DoDestroy is called with an active exception.

Previously if we got a DoDestroy while stopped at a breakpoint, we
would detach and then say the process had exited.  This is completely
wrong, as it resulted in the python script incorrectly assuming that
the process had actually exited and trying to delete the image, when
in fact it had done no such thing.

The fix employed here is that when we get a DoDestroy, we do 3 steps:

1) initiate a termination sequence on the process
2) If we were stopped handling an exception of any kind, mask it and
   let the program resume, causing the program to see the termination
   request and exit on its own.
3) Let the program exit normally, and close all of our handles before
   returning control back to DoDestroy.

This fixes Bug 21722 and Bug 21723.

Modified:
    lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp
    lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h
    lldb/trunk/source/Plugins/Process/Windows/ForwardDecl.h
    lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp

Modified: lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp?rev=223272&r1=223271&r2=223272&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp Wed Dec  3 16:04:18 2014
@@ -97,18 +97,74 @@ DebuggerThread::DebuggerThreadRoutine(co
     return 0;
 }
 
+Error
+DebuggerThread::StopDebugging(bool terminate)
+{
+    Error error;
+
+    if (terminate)
+    {
+        // Make a copy of the process, since the termination sequence will reset DebuggerThread's
+        // internal copy and it needs to remain open for us to perform the Wait operation.
+        HostProcess process_copy = m_process;
+        lldb::process_t handle = process_copy.GetNativeProcess().GetSystemHandle();
+
+        // Initiate the termination before continuing the exception, so that the next debug event
+        // we get is the exit process event, and not some other event.
+        BOOL terminate_suceeded = TerminateProcess(handle, 0);
+
+        // If we're stuck waiting for an exception to continue, continue it now.  But only
+        // AFTER setting the termination event, to make sure that we don't race and enter
+        // another wait for another debug event.
+        if (m_active_exception.get())
+            ContinueAsyncException(ExceptionResult::MaskException);
+
+        // Don't return until the process has exited.
+        if (terminate_suceeded)
+        {
+            DWORD wait_result = ::WaitForSingleObject(handle, 5000);
+            if (wait_result != WAIT_OBJECT_0)
+                terminate_suceeded = false;
+        }
+
+        if (!terminate_suceeded)
+            error.SetError(GetLastError(), eErrorTypeWin32);
+    }
+    else
+    {
+        error.SetErrorString("Detach not yet supported on Windows.");
+        // TODO: Implement detach.
+    }
+    return error;
+}
+
 void
 DebuggerThread::ContinueAsyncException(ExceptionResult result)
 {
-    m_exception.SetValue(result, eBroadcastAlways);
+    if (!m_active_exception.get())
+        return;
+
+    m_active_exception.reset();
+    m_exception_pred.SetValue(result, eBroadcastAlways);
+}
+
+void
+DebuggerThread::FreeProcessHandles()
+{
+    m_process = HostProcess();
+    m_main_thread = HostThread();
+    if (m_image_file)
+    {
+        ::CloseHandle(m_image_file);
+        m_image_file = nullptr;
+    }
 }
 
 void
 DebuggerThread::DebugLoop()
 {
     DEBUG_EVENT dbe = {0};
-    bool exit = false;
-    while (!exit && WaitForDebugEvent(&dbe, INFINITE))
+    while (WaitForDebugEvent(&dbe, INFINITE))
     {
         DWORD continue_status = DBG_CONTINUE;
         switch (dbe.dwDebugEventCode)
@@ -116,8 +172,6 @@ DebuggerThread::DebugLoop()
             case EXCEPTION_DEBUG_EVENT:
             {
                 ExceptionResult status = HandleExceptionEvent(dbe.u.Exception, dbe.dwThreadId);
-                m_exception.SetValue(status, eBroadcastNever);
-                m_exception.WaitForValueNotEqualTo(ExceptionResult::BreakInDebugger, status);
 
                 if (status == ExceptionResult::MaskException)
                     continue_status = DBG_CONTINUE;
@@ -136,7 +190,7 @@ DebuggerThread::DebugLoop()
                 break;
             case EXIT_PROCESS_DEBUG_EVENT:
                 continue_status = HandleExitProcessEvent(dbe.u.ExitProcess, dbe.dwThreadId);
-                exit = true;
+                should_debug = false;
                 break;
             case LOAD_DLL_DEBUG_EVENT:
                 continue_status = HandleLoadDllEvent(dbe.u.LoadDll, dbe.dwThreadId);
@@ -150,19 +204,27 @@ DebuggerThread::DebugLoop()
             case RIP_EVENT:
                 continue_status = HandleRipEvent(dbe.u.RipInfo, dbe.dwThreadId);
                 if (dbe.u.RipInfo.dwType == SLE_ERROR)
-                    exit = true;
+                    should_debug = false;
                 break;
         }
 
         ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status);
     }
+    FreeProcessHandles();
 }
 
 ExceptionResult
 DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id)
 {
     bool first_chance = (info.dwFirstChance != 0);
-    return m_debug_delegate->OnDebugException(first_chance, ExceptionRecord(info.ExceptionRecord));
+
+    m_active_exception.reset(new ExceptionRecord(info.ExceptionRecord));
+
+    ExceptionResult result = m_debug_delegate->OnDebugException(first_chance, *m_active_exception);
+    m_exception_pred.SetValue(result, eBroadcastNever);
+    m_exception_pred.WaitForValueNotEqualTo(ExceptionResult::BreakInDebugger, result);
+
+    return result;
 }
 
 DWORD
@@ -203,12 +265,9 @@ DebuggerThread::HandleExitThreadEvent(co
 DWORD
 DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info, DWORD thread_id)
 {
-    m_debug_delegate->OnExitProcess(info.dwExitCode);
+    FreeProcessHandles();
 
-    m_process = HostProcess();
-    m_main_thread = HostThread();
-    ::CloseHandle(m_image_file);
-    m_image_file = nullptr;
+    m_debug_delegate->OnExitProcess(info.dwExitCode);
     return DBG_CONTINUE;
 }
 

Modified: lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h?rev=223272&r1=223271&r2=223272&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h (original)
+++ lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h Wed Dec  3 16:04:18 2014
@@ -45,10 +45,18 @@ class DebuggerThread : public std::enabl
     {
         return m_main_thread;
     }
+    ExceptionRecord *
+    GetActiveException()
+    {
+        return m_active_exception.get();
+    }
+
+    Error StopDebugging(bool terminate);
 
     void ContinueAsyncException(ExceptionResult result);
 
   private:
+    void FreeProcessHandles();
     void DebugLoop();
     ExceptionResult HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info, DWORD thread_id);
     DWORD HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info, DWORD thread_id);
@@ -66,9 +74,11 @@ class DebuggerThread : public std::enabl
     HostThread m_main_thread; // The main thread of the inferior.
     HANDLE m_image_file;      // The image file of the process being debugged.
 
-    Predicate<ExceptionResult> m_exception; // A predicate which gets signalled when an exception
-                                            // is finished processing and the debug loop can be
-                                            // continued.
+    ExceptionRecordUP m_active_exception; // The current exception waiting to be handled
+
+    Predicate<ExceptionResult> m_exception_pred; // A predicate which gets signalled when an exception
+                                                 // is finished processing and the debug loop can be
+                                                 // continued.
 
     static lldb::thread_result_t DebuggerThreadRoutine(void *data);
     lldb::thread_result_t DebuggerThreadRoutine(const ProcessLaunchInfo &launch_info);

Modified: lldb/trunk/source/Plugins/Process/Windows/ForwardDecl.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Windows/ForwardDecl.h?rev=223272&r1=223271&r2=223272&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Windows/ForwardDecl.h (original)
+++ lldb/trunk/source/Plugins/Process/Windows/ForwardDecl.h Wed Dec  3 16:04:18 2014
@@ -34,6 +34,7 @@ class ExceptionRecord;
 
 typedef std::shared_ptr<IDebugDelegate> DebugDelegateSP;
 typedef std::shared_ptr<DebuggerThread> DebuggerThreadSP;
+typedef std::unique_ptr<ExceptionRecord> ExceptionRecordUP;
 }
 
 #endif
\ No newline at end of file

Modified: lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp?rev=223272&r1=223271&r2=223272&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp Wed Dec  3 16:04:18 2014
@@ -61,7 +61,6 @@ class ProcessWindowsData
     ~ProcessWindowsData() { ::CloseHandle(m_initial_stop_event); }
 
     ProcessLaunchInfo m_launch_info;
-    std::shared_ptr<lldb_private::ExceptionRecord> m_active_exception;
     lldb_private::Error m_launch_error;
     lldb_private::DebuggerThreadSP m_debugger;
     StopInfoSP m_pending_stop_info;
@@ -237,11 +236,10 @@ ProcessWindows::DoResume()
     Error error;
     if (GetPrivateState() == eStateStopped)
     {
-        if (m_session_data->m_active_exception)
+        if (m_session_data->m_debugger->GetActiveException())
         {
             // Resume the process and continue processing debug events.  Mask the exception so that
             // from the process's view, there is no indication that anything happened.
-            m_session_data->m_active_exception.reset();
             m_session_data->m_debugger->ContinueAsyncException(ExceptionResult::MaskException);
         }
 
@@ -279,10 +277,10 @@ ProcessWindows::DoDestroy()
     Error error;
     if (GetPrivateState() != eStateExited && GetPrivateState() != eStateDetached && m_session_data)
     {
-        // Ends the debugging session and terminates the inferior process.
-        DebugActiveProcessStop(m_session_data->m_debugger->GetProcess().GetProcessId());
-        SetPrivateState(eStateExited);
+        DebuggerThread &debugger = *m_session_data->m_debugger;
+        error = debugger.StopDebugging(true);
     }
+    m_session_data.reset();
     return error;
 }
 
@@ -291,28 +289,28 @@ ProcessWindows::RefreshStateAfterStop()
 {
     m_thread_list.RefreshStateAfterStop();
 
-    if (m_session_data->m_active_exception)
-    {
-        StopInfoSP stop_info;
-        ThreadSP stop_thread = m_thread_list.GetSelectedThread();
-        RegisterContextSP register_context = stop_thread->GetRegisterContext();
-
-        ExceptionRecord &exception = *m_session_data->m_active_exception;
-        if (exception.GetExceptionCode() == EXCEPTION_BREAKPOINT)
+    ExceptionRecord *active_exception = m_session_data->m_debugger->GetActiveException();
+    if (!active_exception)
+        return;
+
+    StopInfoSP stop_info;
+    ThreadSP stop_thread = m_thread_list.GetSelectedThread();
+    RegisterContextSP register_context = stop_thread->GetRegisterContext();
+
+    if (active_exception->GetExceptionCode() == EXCEPTION_BREAKPOINT)
+    {
+        uint64_t pc = register_context->GetPC();
+        BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc));
+        lldb::break_id_t break_id = LLDB_INVALID_BREAK_ID;
+        bool should_stop = true;
+        if (site)
         {
-            uint64_t pc = register_context->GetPC();
-            BreakpointSiteSP site(GetBreakpointSiteList().FindByAddress(pc));
-            lldb::break_id_t break_id = LLDB_INVALID_BREAK_ID;
-            bool should_stop = true;
-            if (site)
-            {
-                should_stop = site->ValidForThisThread(stop_thread.get());
-                break_id = site->GetID();
-            }
-
-            stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID(*stop_thread, break_id, should_stop);
-            stop_thread->SetStopInfo(stop_info);
+            should_stop = site->ValidForThisThread(stop_thread.get());
+            break_id = site->GetID();
         }
+
+        stop_info = StopInfo::CreateStopReasonWithBreakpointSiteID(*stop_thread, break_id, should_stop);
+        stop_thread->SetStopInfo(stop_info);
     }
 }
 
@@ -442,7 +440,6 @@ ExceptionResult
 ProcessWindows::OnDebugException(bool first_chance, const ExceptionRecord &record)
 {
     ExceptionResult result = ExceptionResult::SendToApplication;
-    m_session_data->m_active_exception.reset(new ExceptionRecord(record));
     switch (record.GetExceptionCode())
     {
         case EXCEPTION_BREAKPOINT:





More information about the lldb-commits mailing list