Index: source/Plugins/Process/Windows/DebuggerThread.h =================================================================== --- source/Plugins/Process/Windows/DebuggerThread.h +++ source/Plugins/Process/Windows/DebuggerThread.h @@ -35,6 +35,7 @@ Error DebugLaunch(const ProcessLaunchInfo &launch_info); Error DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info); + void DebugDetach(lldb::pid_t pid); HostProcess GetProcess() const @@ -84,6 +85,9 @@ 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. + 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); Index: source/Plugins/Process/Windows/DebuggerThread.cpp =================================================================== --- source/Plugins/Process/Windows/DebuggerThread.cpp +++ source/Plugins/Process/Windows/DebuggerThread.cpp @@ -63,6 +63,8 @@ : 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); } @@ -111,6 +113,14 @@ return error; } +void +DebuggerThread::DebugDetach(lldb::pid_t pid) +{ + // Detaching must be done on the same thread as the debugger loop, so this + // just sets a flag to detach on the next breakpoint exception event. + m_pid_to_detach = pid; +} + lldb::thread_result_t DebuggerThread::DebuggerThreadLaunchRoutine(void *data) { @@ -331,6 +341,11 @@ dbe.dwProcessId, dbe.dwThreadId, continue_status, ::GetCurrentThreadId()); ::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status); + + if (m_detached) + { + should_debug = false; + } } else { @@ -356,6 +371,18 @@ "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); Index: source/Plugins/Process/Windows/ProcessWindows.h =================================================================== --- source/Plugins/Process/Windows/ProcessWindows.h +++ source/Plugins/Process/Windows/ProcessWindows.h @@ -92,11 +92,6 @@ bool CanDebug(lldb_private::Target &target, bool plugin_specified_by_name) override; bool - DetachRequiresHalt() override - { - return true; - } - bool DestroyRequiresHalt() override { return false; Index: source/Plugins/Process/Windows/ProcessWindows.cpp =================================================================== --- source/Plugins/Process/Windows/ProcessWindows.cpp +++ source/Plugins/Process/Windows/ProcessWindows.cpp @@ -9,6 +9,7 @@ // Windows includes #include "lldb/Host/windows/windows.h" +#include // C++ Includes #include @@ -54,6 +55,40 @@ #define BOOL_STR(b) ((b) ? "true" : "false") +namespace +{ + +std::string +GetProcessExecutableName(HANDLE process_handle) +{ + std::string 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[0], file_name_size); + } while (copied >= file_name_size); + file_name.resize(copied); + return file_name; +} + +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,8 +452,28 @@ Error ProcessWindows::DoDetach(bool keep_stopped) { - Error error; - return error; + llvm::sys::ScopedLock lock(m_mutex); + + if (!m_session_data) + { + WINWARN_IFALL(WINDOWS_LOG_PROCESS, + "WillDetach called while state = %u, but there is no active session.", + GetPrivateState()); + return Error(); + } + + // Tell the debugger thread that we want to detach. + m_session_data->m_debugger->DebugDetach(GetID()); + + // Force a break so that the detach can happen from the debugger thread. + if (!::DebugBreakProcess(m_session_data->m_debugger->GetProcess().GetNativeProcess().GetSystemHandle())) + { + return Error(::GetLastError(), eErrorTypeWin32); + } + + SetPrivateState(eStateDetached); + + return Error(); } Error @@ -717,7 +772,8 @@ 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 @@ -740,10 +796,32 @@ { 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, false); + 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,3 +989,4 @@ return; } } +