[Lldb-commits] [lldb] r240157 - Load executable module when attaching to process; implement detach from process.

Adrian McCarthy amccarth at google.com
Fri Jun 19 11:26:53 PDT 2015


Author: amccarth
Date: Fri Jun 19 13:26:53 2015
New Revision: 240157

URL: http://llvm.org/viewvc/llvm-project?rev=240157&view=rev
Log:
Load executable module when attaching to process; implement detach from process.

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

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=240157&r1=240156&r2=240157&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.cpp Fri Jun 19 13:26:53 2015
@@ -63,6 +63,8 @@ DebuggerThread::DebuggerThread(DebugDele
     : m_debug_delegate(debug_delegate)
     , m_image_file(nullptr)
     , m_debugging_ended_event(nullptr)
+    , m_pid_to_detach(0)
+    , m_detached(false)
 {
     m_debugging_ended_event = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
 }
@@ -153,7 +155,6 @@ DebuggerThread::DebuggerThreadLaunchRout
     else
         m_debug_delegate->OnDebuggerError(error, 0);
 
-    SetEvent(m_debugging_ended_event);
     return 0;
 }
 
@@ -193,55 +194,62 @@ DebuggerThread::StopDebugging(bool termi
         "StopDebugging('%s') called (inferior=%I64u).",
         (terminate ? "true" : "false"), pid);
 
+    // Make a copy of the process, since the termination sequence will reset
+    // DebuggerThread's internal copy and it needs to remain open for the Wait operation.
+    HostProcess process_copy = m_process;
+    lldb::process_t handle = m_process.GetNativeProcess().GetSystemHandle();
+
     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 the Wait operation.
-        HostProcess process_copy = m_process;
-        lldb::process_t handle = m_process.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);
         WINLOG_IFALL(WINDOWS_LOG_PROCESS,
             "StopDebugging called TerminateProcess(0x%p, 0) (inferior=%I64u), success='%s'",
             handle, pid, (terminate_suceeded ? "true" : "false"));
+    }
 
-        // If we're stuck waiting for an exception to continue (e.g. the user is at a breakpoint
-        // messing around in the debugger), continue it now.  But only AFTER calling TerminateProcess
-        // to make sure that the very next call to WaitForDebugEvent is an exit process event.
-        if (m_active_exception.get())
-        {
-            WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION,
-                "StopDebugging masking active exception");
+    // If we're stuck waiting for an exception to continue (e.g. the user is at a breakpoint
+    // messing around in the debugger), continue it now.  But only AFTER calling TerminateProcess
+    // to make sure that the very next call to WaitForDebugEvent is an exit process event.
+    if (m_active_exception.get())
+    {
+        WINLOG_IFANY(WINDOWS_LOG_PROCESS|WINDOWS_LOG_EXCEPTION,
+            "StopDebugging masking active exception");
 
-            ContinueAsyncException(ExceptionResult::MaskException);
-        }
+        ContinueAsyncException(ExceptionResult::MaskException);
+    }
 
-        WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging waiting for detach from process %u to complete.", pid);
+    if (!terminate)
+    {
+        // Indicate that we want to detach.
+        m_pid_to_detach = GetProcess().GetProcessId();
 
-        DWORD wait_result = WaitForSingleObject(m_debugging_ended_event, 5000);
-        if (wait_result != WAIT_OBJECT_0)
-        {
-            error.SetError(GetLastError(), eErrorTypeWin32);
-            WINERR_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging WaitForSingleObject(0x%p, 5000) returned %u",
-                         m_debugging_ended_event, wait_result);
-        }
-        else
+        // Force a fresh break so that the detach can happen from the debugger thread.
+        if (!::DebugBreakProcess(GetProcess().GetNativeProcess().GetSystemHandle()))
         {
-            WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging detach from process %u completed successfully.", pid);
+            error.SetError(::GetLastError(), eErrorTypeWin32);
         }
     }
+
+    WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging waiting for detach from process %u to complete.", pid);
+
+    DWORD wait_result = WaitForSingleObject(m_debugging_ended_event, 5000);
+    if (wait_result != WAIT_OBJECT_0)
+    {
+        error.SetError(GetLastError(), eErrorTypeWin32);
+        WINERR_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging WaitForSingleObject(0x%p, 5000) returned %u",
+                        m_debugging_ended_event, wait_result);
+    }
     else
     {
-        error.SetErrorString("Detach not yet supported on Windows.");
-        // TODO: Implement detach.
+        WINLOG_IFALL(WINDOWS_LOG_PROCESS, "StopDebugging detach from process %u completed successfully.", pid);
     }
 
     if (!error.Success())
     {
         WINERR_IFALL(WINDOWS_LOG_PROCESS,
-            "StopDebugging encountered an error while trying to  stop process %u.  %s",
+            "StopDebugging encountered an error while trying to stop process %u.  %s",
             pid, error.AsCString());
     }
     return error;
@@ -331,6 +339,11 @@ DebuggerThread::DebugLoop()
                           dbe.dwProcessId, dbe.dwThreadId, continue_status, ::GetCurrentThreadId());
 
             ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status);
+
+            if (m_detached)
+            {
+                should_debug = false;
+            }
         }
         else
         {
@@ -344,6 +357,7 @@ DebuggerThread::DebugLoop()
     FreeProcessHandles();
 
     WINLOG_IFALL(WINDOWS_LOG_EVENT, "WaitForDebugEvent loop completed, exiting.");
+    SetEvent(m_debugging_ended_event);
 }
 
 ExceptionResult
@@ -356,6 +370,18 @@ DebuggerThread::HandleExceptionEvent(con
                  "HandleExceptionEvent encountered %s chance exception 0x%x on thread 0x%x",
                  first_chance ? "first" : "second", info.ExceptionRecord.ExceptionCode, thread_id);
 
+    if (m_pid_to_detach != 0 && m_active_exception->GetExceptionCode() == EXCEPTION_BREAKPOINT) {
+        WINLOG_IFANY(WINDOWS_LOG_EVENT | WINDOWS_LOG_EXCEPTION | WINDOWS_LOG_PROCESS,
+                     "Breakpoint exception is cue to detach from process 0x%x",
+                     m_pid_to_detach);
+        if (::DebugActiveProcessStop(m_pid_to_detach)) {
+            m_detached = true;
+            return ExceptionResult::MaskException;
+        } else {
+            WINLOG_IFANY(WINDOWS_LOG_PROCESS, "Failed to detach, treating as a regular breakpoint");
+        }
+    }
+
     ExceptionResult result = m_debug_delegate->OnDebugException(first_chance,
                                                                 *m_active_exception);
     m_exception_pred.SetValue(result, eBroadcastNever);

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=240157&r1=240156&r2=240157&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h (original)
+++ lldb/trunk/source/Plugins/Process/Windows/DebuggerThread.h Fri Jun 19 13:26:53 2015
@@ -10,6 +10,7 @@
 #ifndef liblldb_Plugins_Process_Windows_DebuggerThread_H_
 #define liblldb_Plugins_Process_Windows_DebuggerThread_H_
 
+#include <atomic>
 #include <memory>
 
 #include "ForwardDecl.h"
@@ -84,6 +85,9 @@ class DebuggerThread : public std::enabl
     HANDLE m_debugging_ended_event; // An event which gets signalled by the debugger thread when it
                                     // exits the debugger loop and is detached from the inferior.
 
+    std::atomic<DWORD> m_pid_to_detach;  // Signals the loop to detach from the process (specified by pid).
+    bool m_detached;  // Indicates we've detached from the inferior process and the debug loop can exit.
+
     static lldb::thread_result_t DebuggerThreadLaunchRoutine(void *data);
     lldb::thread_result_t DebuggerThreadLaunchRoutine(const ProcessLaunchInfo &launch_info);
     static lldb::thread_result_t DebuggerThreadAttachRoutine(void *data);

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=240157&r1=240156&r2=240157&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.cpp Fri Jun 19 13:26:53 2015
@@ -9,6 +9,7 @@
 
 // Windows includes
 #include "lldb/Host/windows/windows.h"
+#include <psapi.h>
 
 // C++ Includes
 #include <list>
@@ -54,6 +55,40 @@ using namespace lldb_private;
 
 #define BOOL_STR(b) ((b) ? "true" : "false")
 
+namespace
+{
+
+std::string
+GetProcessExecutableName(HANDLE process_handle)
+{
+    std::vector<char> file_name;
+    DWORD file_name_size = MAX_PATH;  // first guess, not an absolute limit
+    DWORD copied = 0;
+    do
+    {
+        file_name_size *= 2;
+        file_name.resize(file_name_size);
+        copied = ::GetModuleFileNameEx(process_handle, NULL, file_name.data(), file_name_size);
+    } while (copied >= file_name_size);
+    file_name.resize(copied);
+    return std::string(file_name.begin(), file_name.end());
+}
+
+std::string
+GetProcessExecutableName(DWORD pid)
+{
+    std::string file_name;
+    HANDLE process_handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
+    if (process_handle != NULL)
+    {
+        file_name = GetProcessExecutableName(process_handle);
+        ::CloseHandle(process_handle);
+    }
+    return file_name;
+}
+
+}  // anonymous namespace
+
 namespace lldb_private
 {
 
@@ -417,7 +452,49 @@ ProcessWindows::GetPluginVersion()
 Error
 ProcessWindows::DoDetach(bool keep_stopped)
 {
+    DebuggerThreadSP debugger_thread;
+    StateType private_state;
+    {
+        // Acquire the lock only long enough to get the DebuggerThread.
+        // StopDebugging() will trigger a call back into ProcessWindows which
+        // will also acquire the lock.  Thus we have to release the lock before
+        // calling StopDebugging().
+        llvm::sys::ScopedLock lock(m_mutex);
+
+        private_state = GetPrivateState();
+
+        if (!m_session_data)
+        {
+            WINWARN_IFALL(WINDOWS_LOG_PROCESS, "DoDetach called while state = %u, but there is no active session.",
+                          private_state);
+            return Error();
+        }
+
+        debugger_thread = m_session_data->m_debugger;
+    }
+
     Error error;
+    if (private_state != eStateExited && private_state != eStateDetached)
+    {
+        WINLOG_IFALL(WINDOWS_LOG_PROCESS, "DoDetach called for process %I64u while state = %u.  Detaching...",
+                     debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state);
+        error = debugger_thread->StopDebugging(false);
+        if (error.Success())
+        {
+            SetPrivateState(eStateDetached);
+        }
+
+        // By the time StopDebugging returns, there is no more debugger thread, so
+        // we can be assured that no other thread will race for the session data.
+        m_session_data.reset();
+    }
+    else
+    {
+        WINERR_IFALL(WINDOWS_LOG_PROCESS,
+                     "DoDetach called for process %I64u while state = %u, but cannot destroy in this state.",
+                     debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state);
+    }
+
     return error;
 }
 
@@ -451,9 +528,8 @@ ProcessWindows::DoDestroy()
                      debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle(), private_state);
         error = debugger_thread->StopDebugging(true);
 
-        // By the time StopDebugging returns, there is no more debugger thread, so we can be assured that no other
-        // thread
-        // will race for the session data.  So it's safe to reset it without holding a lock.
+        // By the time StopDebugging returns, there is no more debugger thread, so
+        // we can be assured that no other thread will race for the session data.
         m_session_data.reset();
     }
     else
@@ -718,7 +794,8 @@ ProcessWindows::CanDebug(Target &target,
     ModuleSP exe_module_sp(target.GetExecutableModule());
     if (exe_module_sp.get())
         return exe_module_sp->GetFileSpec().Exists();
-    return false;
+    // However, if there is no executable module, we return true since we might be preparing to attach.
+    return true;
 }
 
 void
@@ -741,10 +818,32 @@ ProcessWindows::OnDebuggerConnected(lldb
 {
     DebuggerThreadSP debugger = m_session_data->m_debugger;
 
-    WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Debugger established connected to process %I64u.  Image base = 0x%I64x",
+    WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Debugger connected to process %I64u.  Image base = 0x%I64x",
                  debugger->GetProcess().GetProcessId(), image_base);
 
     ModuleSP module = GetTarget().GetExecutableModule();
+    if (!module)
+    {
+        // During attach, we won't have the executable module, so find it now.
+        const DWORD pid = debugger->GetProcess().GetProcessId();
+        const std::string file_name = GetProcessExecutableName(pid);
+        if (file_name.empty())
+        {
+            return;
+        }
+
+        FileSpec executable_file(file_name, true);
+        ModuleSpec module_spec(executable_file);
+        Error error;
+        module = GetTarget().GetSharedModule(module_spec, &error);
+        if (!module)
+        {
+            return;
+        }
+
+        GetTarget().SetExecutableModule(module, false);
+    }
+
     bool load_addr_changed;
     module->SetLoadAddress(GetTarget(), image_base, false, load_addr_changed);
 
@@ -911,4 +1010,4 @@ ProcessWindows::OnDebuggerError(const Er
                      error.GetError(), error.AsCString());
         return;
     }
-}
+}
\ No newline at end of file

Modified: lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.h?rev=240157&r1=240156&r2=240157&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.h (original)
+++ lldb/trunk/source/Plugins/Process/Windows/ProcessWindows.h Fri Jun 19 13:26:53 2015
@@ -92,11 +92,6 @@ public:
 
     bool CanDebug(lldb_private::Target &target, bool plugin_specified_by_name) override;
     bool
-    DetachRequiresHalt() override
-    {
-        return true;
-    }
-    bool
     DestroyRequiresHalt() override
     {
         return false;





More information about the lldb-commits mailing list