[Lldb-commits] [lldb] [lldb][Windows] Lock DebuggerThread::m_process against the debug loop (PR #236560)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Fri Oct 30 11:54:35 PDT 2026
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/236560
>From 8437ae520085b53d6eb5d951aecf73d3174fb11f Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 27 Oct 2026 23:23:09 +0000
Subject: [PATCH 1/2] [lldb][Windows] Lock DebuggerThread::m_process against
the debug loop
---
.../Process/Windows/Common/DebuggerThread.cpp | 31 +++++++++++--------
.../Process/Windows/Common/DebuggerThread.h | 6 +++-
2 files changed, 23 insertions(+), 14 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
index cb1e31b4334f54..ea02647d8ae4db 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
@@ -168,7 +168,11 @@ lldb::thread_result_t DebuggerThread::DebuggerThreadAttachRoutine(
Status DebuggerThread::StopDebugging(bool terminate) {
Status error;
- lldb::pid_t pid = m_process.GetProcessId();
+ // 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 = GetProcess();
+ lldb::pid_t pid = process_copy.GetProcessId();
Log *log = GetLog(WindowsLog::Process);
LLDB_LOG(log, "terminate = {0}, inferior={1}.", terminate, pid);
@@ -179,11 +183,7 @@ Status DebuggerThread::StopDebugging(bool terminate) {
if (!m_is_shutting_down.compare_exchange_strong(expected, true))
return error;
- // 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();
+ lldb::process_t handle = process_copy.GetNativeProcess().GetSystemHandle();
if (terminate) {
if (handle != nullptr && handle != LLDB_INVALID_PROCESS) {
@@ -215,12 +215,11 @@ Status DebuggerThread::StopDebugging(bool terminate) {
if (!terminate) {
// Indicate that we want to detach.
- m_pid_to_detach = GetProcess().GetProcessId();
+ m_pid_to_detach = pid;
// Force a fresh break so that the detach can happen from the debugger
// thread.
- if (!::DebugBreakProcess(
- GetProcess().GetNativeProcess().GetSystemHandle())) {
+ if (!::DebugBreakProcess(handle)) {
error = Status(::GetLastError(), eErrorTypeWin32);
}
}
@@ -267,7 +266,10 @@ void DebuggerThread::ContinueAsyncDllEvent() {
}
void DebuggerThread::FreeProcessHandles() {
- m_process = HostProcess();
+ {
+ std::lock_guard<std::mutex> guard(m_process_mutex);
+ m_process = HostProcess();
+ }
m_main_thread = HostThread();
if (m_image_file) {
::CloseHandle(m_image_file);
@@ -526,9 +528,12 @@ DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info,
// info.hProcess and info.hThread are closed automatically by Windows when
// EXIT_PROCESS_DEBUG_EVENT is received.
- m_process = HostProcess(info.hProcess);
- static_cast<HostProcessWindows &>(m_process.GetNativeProcess())
- .SetOwnsHandle(false);
+ {
+ std::lock_guard<std::mutex> guard(m_process_mutex);
+ m_process = HostProcess(info.hProcess);
+ static_cast<HostProcessWindows &>(m_process.GetNativeProcess())
+ .SetOwnsHandle(false);
+ }
m_main_thread = HostThread(info.hThread);
m_main_thread.GetNativeThread().SetOwnsHandle(false);
m_image_file = info.hFile;
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h
index a9fa3ec0c7599d..51a5040f9b7aea 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h
@@ -33,7 +33,10 @@ class DebuggerThread : public std::enable_shared_from_this<DebuggerThread> {
Status DebugLaunch(const ProcessLaunchInfo &launch_info);
Status DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info);
- HostProcess GetProcess() const { return m_process; }
+ HostProcess GetProcess() const {
+ std::lock_guard<std::mutex> guard(m_process_mutex);
+ return m_process;
+ }
HostThread GetMainThread() const { return m_main_thread; }
/// The path the process was created with, as recorded in its process
@@ -76,6 +79,7 @@ class DebuggerThread : public std::enable_shared_from_this<DebuggerThread> {
DebugDelegateSP m_debug_delegate;
HostProcess m_process; // The process being debugged.
+ mutable std::mutex m_process_mutex; // Guards m_process.
HostThread m_main_thread; // The main thread of the inferior.
// The image file of the process being debugged.
>From 9edbdb1da948dd8509e406808b712fd518684666 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Fri, 30 Oct 2026 11:52:39 -0700
Subject: [PATCH 2/2] Use Guarded<HostProcess> for DebuggerThread::m_process
---
.../Process/Windows/Common/DebuggerThread.cpp | 37 +++++++++----------
.../Process/Windows/Common/DebuggerThread.h | 9 ++---
2 files changed, 20 insertions(+), 26 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
index ea02647d8ae4db..b0f99af965aa61 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
@@ -256,7 +256,7 @@ void DebuggerThread::ContinueAsyncException(ExceptionResult result) {
Log *log = GetLog(WindowsLog::Process | WindowsLog::Exception);
LLDB_LOG(log, "broadcasting for inferior process {0}.",
- m_process.GetProcessId());
+ GetProcess().GetProcessId());
m_exception_pred.SetValue(result, eBroadcastAlways);
}
@@ -266,10 +266,7 @@ void DebuggerThread::ContinueAsyncDllEvent() {
}
void DebuggerThread::FreeProcessHandles() {
- {
- std::lock_guard<std::mutex> guard(m_process_mutex);
- m_process = HostProcess();
- }
+ *m_process.Lock() = HostProcess();
m_main_thread = HostThread();
if (m_image_file) {
::CloseHandle(m_image_file);
@@ -316,10 +313,10 @@ void DebuggerThread::DebugLoop() {
HandleExitThreadEvent(dbe.u.ExitThread, dbe.dwThreadId);
break;
case EXIT_PROCESS_DEBUG_EVENT:
- if (!::DuplicateHandle(::GetCurrentProcess(),
- m_process.GetNativeProcess().GetSystemHandle(),
- ::GetCurrentProcess(), &exited_process,
- SYNCHRONIZE, FALSE, 0))
+ if (!::DuplicateHandle(
+ ::GetCurrentProcess(),
+ GetProcess().GetNativeProcess().GetSystemHandle(),
+ ::GetCurrentProcess(), &exited_process, SYNCHRONIZE, FALSE, 0))
exited_process = nullptr;
should_debug = false;
break;
@@ -463,7 +460,7 @@ DebuggerThread::HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info,
DWORD thread_id) {
Log *log = GetLog(WindowsLog::Event | WindowsLog::Thread);
LLDB_LOG(log, "Thread {0} spawned in process {1}", thread_id,
- m_process.GetProcessId());
+ GetProcess().GetProcessId());
HostThread thread(info.hThread);
thread.GetNativeThread().SetOwnsHandle(false);
m_debug_delegate->OnCreateThread(thread);
@@ -529,9 +526,9 @@ DebuggerThread::HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info,
// info.hProcess and info.hThread are closed automatically by Windows when
// EXIT_PROCESS_DEBUG_EVENT is received.
{
- std::lock_guard<std::mutex> guard(m_process_mutex);
- m_process = HostProcess(info.hProcess);
- static_cast<HostProcessWindows &>(m_process.GetNativeProcess())
+ auto process = m_process.Lock();
+ *process = HostProcess(info.hProcess);
+ static_cast<HostProcessWindows &>(process->GetNativeProcess())
.SetOwnsHandle(false);
}
m_main_thread = HostThread(info.hThread);
@@ -550,7 +547,7 @@ DebuggerThread::HandleExitThreadEvent(const EXIT_THREAD_DEBUG_INFO &info,
DWORD thread_id) {
Log *log = GetLog(WindowsLog::Event | WindowsLog::Thread);
LLDB_LOG(log, "Thread {0} exited with code {1} in process {2}", thread_id,
- info.dwExitCode, m_process.GetProcessId());
+ info.dwExitCode, GetProcess().GetProcessId());
m_debug_delegate->OnExitThread(thread_id, info.dwExitCode);
return DBG_CONTINUE;
}
@@ -559,7 +556,7 @@ DWORD
DebuggerThread::HandleExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO &info,
DWORD thread_id) {
Log *log = GetLog(WindowsLog::Event | WindowsLog::Thread);
- LLDB_LOG(log, "process {0} exited with code {1}", m_process.GetProcessId(),
+ LLDB_LOG(log, "process {0} exited with code {1}", GetProcess().GetProcessId(),
info.dwExitCode);
m_debug_delegate->OnExitProcess(info.dwExitCode);
@@ -805,13 +802,13 @@ DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info,
lldb::addr_t load_addr = reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll);
LLDB_LOG(log, "Inferior {0} - DLL '{1}' loaded at address {2:x}...",
- m_process.GetProcessId(), path, info.lpBaseOfDll);
+ GetProcess().GetProcessId(), path, info.lpBaseOfDll);
m_dll_event_pred.SetValue(false, eBroadcastNever);
action = m_debug_delegate->OnLoadDll(module_spec, load_addr, thread_id);
};
- HANDLE process = m_process.GetNativeProcess().GetSystemHandle();
+ HANDLE process = GetProcess().GetNativeProcess().GetSystemHandle();
// Prefer the path the loader used for the DLL, which keeps the subst drive,
// junction or symbolic link it was reached through.
@@ -862,7 +859,7 @@ DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info,
LLDB_LOG(log,
"Inferior {0} - could not resolve path for LOAD_DLL_DEBUG_EVENT "
"(hFile={1}, base={2:x}, last error={3})",
- m_process.GetProcessId(), info.hFile, info.lpBaseOfDll,
+ GetProcess().GetProcessId(), info.hFile, info.lpBaseOfDll,
::GetLastError());
// Windows does not automatically close info.hFile, so we need to do it.
@@ -879,7 +876,7 @@ DebuggerThread::HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info,
DWORD thread_id) {
Log *log = GetLog(WindowsLog::Event);
LLDB_LOG(log, "process {0} unloading DLL at addr {1:x}.",
- m_process.GetProcessId(), info.lpBaseOfDll);
+ GetProcess().GetProcessId(), info.lpBaseOfDll);
m_dll_event_pred.SetValue(false, eBroadcastNever);
DllEventAction action = m_debug_delegate->OnUnloadDll(
@@ -903,7 +900,7 @@ DWORD
DebuggerThread::HandleRipEvent(const RIP_INFO &info, DWORD thread_id) {
Log *log = GetLog(WindowsLog::Event);
LLDB_LOG(log, "encountered error {0} (type={1}) in process {2} thread {3}",
- info.dwError, info.dwType, m_process.GetProcessId(), thread_id);
+ info.dwError, info.dwType, GetProcess().GetProcessId(), thread_id);
Status error(info.dwError, eErrorTypeWin32);
m_debug_delegate->OnDebuggerError(error, info.dwType);
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h
index 51a5040f9b7aea..0a700b6bb21422 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h
@@ -17,6 +17,7 @@
#include "lldb/Host/HostProcess.h"
#include "lldb/Host/HostThread.h"
#include "lldb/Host/windows/windows.h"
+#include "lldb/Utility/Locked.h"
#include "lldb/Utility/Predicate.h"
namespace lldb_private {
@@ -33,10 +34,7 @@ class DebuggerThread : public std::enable_shared_from_this<DebuggerThread> {
Status DebugLaunch(const ProcessLaunchInfo &launch_info);
Status DebugAttach(lldb::pid_t pid, const ProcessAttachInfo &attach_info);
- HostProcess GetProcess() const {
- std::lock_guard<std::mutex> guard(m_process_mutex);
- return m_process;
- }
+ HostProcess GetProcess() const { return *m_process.LockShared(); }
HostThread GetMainThread() const { return m_main_thread; }
/// The path the process was created with, as recorded in its process
@@ -78,8 +76,7 @@ class DebuggerThread : public std::enable_shared_from_this<DebuggerThread> {
DebugDelegateSP m_debug_delegate;
- HostProcess m_process; // The process being debugged.
- mutable std::mutex m_process_mutex; // Guards m_process.
+ Guarded<HostProcess> m_process; // The process being debugged.
HostThread m_main_thread; // The main thread of the inferior.
// The image file of the process being debugged.
More information about the lldb-commits
mailing list