Skip to content

[LLDB][ProcessWindows] Set exit status on instance rather than going through all targets#159308

Merged
Nerixyz merged 1 commit intollvm:mainfrom
Nerixyz:fix/lldb-target-lock
Sep 18, 2025
Merged

[LLDB][ProcessWindows] Set exit status on instance rather than going through all targets#159308
Nerixyz merged 1 commit intollvm:mainfrom
Nerixyz:fix/lldb-target-lock

Conversation

@Nerixyz
Copy link
Contributor

@Nerixyz Nerixyz commented Sep 17, 2025

When quitting LLDB on Windows while a process was still running, LLDB would take unusually long to exit. This was due to a temporary deadlock:

The main thread was destroying the processes. In doing so, it iterated over the target list:

for (TargetSP target_sp : m_target_list.Targets()) {
if (target_sp) {
if (ProcessSP process_sp = target_sp->GetProcessSP())
process_sp->Finalize(false /* not destructing */);

This locks the list for the whole iteration. Finalizing the process would eventually lead to DebuggerThread::StopDebugging, which terminates the process and waits for it to exit:

DWORD wait_result = WaitForSingleObject(m_debugging_ended_event, 5000);

The debugger loop (on a separate thread) would see that the process exited and call ProcessWindows::OnExitProcess. This calls the static function Process::SetProcessExitStatus. This tries to find the process by its ID from the debugger's target list. Doing so requires locking the list, so the debugger thread would then be stuck on
std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);

After 5s, the main thread would give up waiting. So every exit where the process was still running would be delayed by about 5s.

Since ProcessWindows would find itself when calling SetProcessExitStatus, we can call SetExitStatus directly.

This can also make some tests run faster. For example, the DIA PDB tests previously took 15s to run on my PC (24 jobs) and now take 5s. For all shell tests, the difference isn't that big (only about 3s), because most don't run into this and the tests run in parallel.

@llvmbot
Copy link
Member

llvmbot commented Sep 17, 2025

@llvm/pr-subscribers-lldb

Author: nerix (Nerixyz)

Changes

When quitting LLDB on Windows while a process was still running, LLDB would take unusually long to exit. This was due to a temporary deadlock:

The main thread was destroying the processes. In doing so, it iterated over the target list:

for (TargetSP target_sp : m_target_list.Targets()) {
if (target_sp) {
if (ProcessSP process_sp = target_sp->GetProcessSP())
process_sp->Finalize(false /* not destructing */);

This locks the list for the whole iteration. Finalizing the process would eventually lead to DebuggerThread::StopDebugging, which terminates the process and waits for it to exit:

DWORD wait_result = WaitForSingleObject(m_debugging_ended_event, 5000);

The debugger loop (on a separate thread) would see that the process exited and call ProcessWindows::OnExitProcess. This calls the static function Process::SetProcessExitStatus. This tries to find the process by its ID from the debugger's target list. Doing so requires locking the list, so the debugger thread would then be stuck on
std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);

After 5s, the main thread would give up waiting. So every exit where the process was still running would be delayed by about 5s.

Since ProcessWindows would find itself when calling SetProcessExitStatus, we can call SetExitStatus directly.

This can also make some tests run faster. For example, the DIA PDB tests previously took 15s to run on my PC (24 jobs) and now take 5s. For all shell tests, the difference isn't that big (only about 3s), because most don't run into this and the tests run in parallel.


Full diff: https://github.com/llvm/llvm-project/pull/159308.diff

1 Files Affected:

  • (modified) lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp (+1-1)
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 27530f032ce51..0fecefe23b88e 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -666,7 +666,7 @@ void ProcessWindows::OnExitProcess(uint32_t exit_code) {
     target->ModulesDidUnload(unloaded_modules, true);
   }
 
-  SetProcessExitStatus(GetID(), true, 0, exit_code);
+  SetExitStatus(exit_code, /*exit_string=*/"");
   SetPrivateState(eStateExited);
 
   ProcessDebugger::OnExitProcess(exit_code);

Copy link
Collaborator

@DavidSpickett DavidSpickett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great find! I had noticed that lldb was slow to quit but I always thought it would be something inherent to how things work on Windows.

@mstorsjo
Copy link
Member

Nice, thanks! I've also observed this from time to time, but haven't had time to dig into it!

@Nerixyz Nerixyz merged commit a868f28 into llvm:main Sep 18, 2025
11 checks passed
@mstorsjo
Copy link
Member

mstorsjo commented Oct 1, 2025

Do you think it'd make sense to backport this fix to 21.x?

@Nerixyz
Copy link
Contributor Author

Nerixyz commented Oct 1, 2025

Do you think it'd make sense to backport this fix to 21.x?

That could make sense, and I'm fine with doing this, though, the bug was already in 20.x. Maybe @DavidSpickett has some thoughts on this?

@DavidSpickett
Copy link
Collaborator

It always "worked", just slowly. So it's like backporting a code generation improvement, which we have been known to do. Worth a try.

I would support it on the basis that the patch is very simple and we haven't seen any regressions.

@Nerixyz
Copy link
Contributor Author

Nerixyz commented Oct 1, 2025

/cherry-pick a868f28

@llvmbot
Copy link
Member

llvmbot commented Oct 1, 2025

/pull-request #161541

@llvmbot llvmbot moved this from Needs Triage to Done in LLVM Release Status Oct 1, 2025
@mstorsjo
Copy link
Member

mstorsjo commented Oct 1, 2025

Yeah - it doesn't need to be a regression in 21.x for the fix to be backported, any reasonable bug fix can be eligible IMO.

c-rhodes pushed a commit that referenced this pull request Oct 13, 2025
…through all targets (#159308)

When quitting LLDB on Windows while a process was still running, LLDB
would take unusually long to exit. This was due to a temporary deadlock:

The main thread was destroying the processes. In doing so, it iterated
over the target list:

https://github.com/llvm/llvm-project/blob/88c64f76ed2ca226da99b99f60d316b1519fc7d8/lldb/source/Core/Debugger.cpp#L1095-L1098

This locks the list for the whole iteration. Finalizing the process
would eventually lead to `DebuggerThread::StopDebugging`, which
terminates the process and waits for it to exit:

https://github.com/llvm/llvm-project/blob/88c64f76ed2ca226da99b99f60d316b1519fc7d8/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp#L196
The debugger loop (on a separate thread) would see that the process
exited and call
[`ProcessWindows::OnExitProcess`](https://github.com/llvm/llvm-project/blob/88c64f76ed2ca226da99b99f60d316b1519fc7d8/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp#L656-L673).
This calls the static function
[`Process::SetProcessExitStatus`](https://github.com/llvm/llvm-project/blob/0a7a7d56fc882653335beba0d1f8ea9f26089c22/lldb/source/Target/Process.cpp#L1098-L1126).
This tries to find the process by its ID from the debugger's target
list. Doing so requires locking the list, so the debugger thread would
then be stuck on

https://github.com/llvm/llvm-project/blob/0a7a7d56fc882653335beba0d1f8ea9f26089c22/lldb/source/Target/TargetList.cpp#L403

After 5s, the main thread would give up waiting. So every exit where the
process was still running would be delayed by about 5s.

Since `ProcessWindows` would find itself when calling
`SetProcessExitStatus`, we can call `SetExitStatus` directly.

This can also make some tests run faster. For example, the DIA PDB tests
previously took 15s to run on my PC (24 jobs) and now take 5s. For all
shell tests, the difference isn't that big (only about 3s), because most
don't run into this and the tests run in parallel.

(cherry picked from commit a868f28)
@Nerixyz Nerixyz deleted the fix/lldb-target-lock branch November 7, 2025 14:16
Nerixyz added a commit that referenced this pull request Jan 16, 2026
I noticed that LLDB takes longer to stop than it should. Running a tiny
program like `int main() { return 0; }` in LLDB with `lldb test.exe -o r
-o q` takes about five seconds.

This is caused by the `WaitForMultipleObjects` in
[`ConnectionGenericFile::Read`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/windows/ConnectionGenericFileWindows.cpp#L191-L192)
timing out (it has a timeout of 5s).

It times out, because we never close the PTY created in
[`ProcessLaunchInfo::SetUpPtyRedirection`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/common/ProcessLaunchInfo.cpp#L213).
When we call `target->GetProcessLaunchInfo().GetPTY().Close()` in
`ProcessWindows::OnExitProcess`, we don't access the PTY we created when
setting up the redirection - we're closing a default constructed one.
This is because the target's `m_launch_info` isn't necessarily the
`launch_info` we get in
[`Target::FinalizeFileActions`](https://github.com/llvm/llvm-project/blob/4a8a0593bdecee49331cb5c31a6c9b18d87ea41c/lldb/source/Target/Target.cpp#L3850)
when calling `SetUpPtyRedirection`.

With this PR, we store the PTY that a process was launched with inside
`ProcessWindows`, so we can later close it.

The wait of five seconds and a timed out `WaitForMultipleObjects` sounds
similar to #159308, but it's a
different call to `WaitForMultipleObjects` here. Still, I wonder if we
could do something to detect this earlier. Maybe some warning message or
debug-only assert if these waits time out?
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Jan 16, 2026
I noticed that LLDB takes longer to stop than it should. Running a tiny
program like `int main() { return 0; }` in LLDB with `lldb test.exe -o r
-o q` takes about five seconds.

This is caused by the `WaitForMultipleObjects` in
[`ConnectionGenericFile::Read`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/windows/ConnectionGenericFileWindows.cpp#L191-L192)
timing out (it has a timeout of 5s).

It times out, because we never close the PTY created in
[`ProcessLaunchInfo::SetUpPtyRedirection`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/common/ProcessLaunchInfo.cpp#L213).
When we call `target->GetProcessLaunchInfo().GetPTY().Close()` in
`ProcessWindows::OnExitProcess`, we don't access the PTY we created when
setting up the redirection - we're closing a default constructed one.
This is because the target's `m_launch_info` isn't necessarily the
`launch_info` we get in
[`Target::FinalizeFileActions`](https://github.com/llvm/llvm-project/blob/4a8a0593bdecee49331cb5c31a6c9b18d87ea41c/lldb/source/Target/Target.cpp#L3850)
when calling `SetUpPtyRedirection`.

With this PR, we store the PTY that a process was launched with inside
`ProcessWindows`, so we can later close it.

The wait of five seconds and a timed out `WaitForMultipleObjects` sounds
similar to llvm/llvm-project#159308, but it's a
different call to `WaitForMultipleObjects` here. Still, I wonder if we
could do something to detect this earlier. Maybe some warning message or
debug-only assert if these waits time out?
kkwli pushed a commit to kkwli/llvm-project that referenced this pull request Jan 16, 2026
I noticed that LLDB takes longer to stop than it should. Running a tiny
program like `int main() { return 0; }` in LLDB with `lldb test.exe -o r
-o q` takes about five seconds.

This is caused by the `WaitForMultipleObjects` in
[`ConnectionGenericFile::Read`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/windows/ConnectionGenericFileWindows.cpp#L191-L192)
timing out (it has a timeout of 5s).

It times out, because we never close the PTY created in
[`ProcessLaunchInfo::SetUpPtyRedirection`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/common/ProcessLaunchInfo.cpp#L213).
When we call `target->GetProcessLaunchInfo().GetPTY().Close()` in
`ProcessWindows::OnExitProcess`, we don't access the PTY we created when
setting up the redirection - we're closing a default constructed one.
This is because the target's `m_launch_info` isn't necessarily the
`launch_info` we get in
[`Target::FinalizeFileActions`](https://github.com/llvm/llvm-project/blob/4a8a0593bdecee49331cb5c31a6c9b18d87ea41c/lldb/source/Target/Target.cpp#L3850)
when calling `SetUpPtyRedirection`.

With this PR, we store the PTY that a process was launched with inside
`ProcessWindows`, so we can later close it.

The wait of five seconds and a timed out `WaitForMultipleObjects` sounds
similar to llvm#159308, but it's a
different call to `WaitForMultipleObjects` here. Still, I wonder if we
could do something to detect this earlier. Maybe some warning message or
debug-only assert if these waits time out?
Priyanshu3820 pushed a commit to Priyanshu3820/llvm-project that referenced this pull request Jan 18, 2026
I noticed that LLDB takes longer to stop than it should. Running a tiny
program like `int main() { return 0; }` in LLDB with `lldb test.exe -o r
-o q` takes about five seconds.

This is caused by the `WaitForMultipleObjects` in
[`ConnectionGenericFile::Read`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/windows/ConnectionGenericFileWindows.cpp#L191-L192)
timing out (it has a timeout of 5s).

It times out, because we never close the PTY created in
[`ProcessLaunchInfo::SetUpPtyRedirection`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/common/ProcessLaunchInfo.cpp#L213).
When we call `target->GetProcessLaunchInfo().GetPTY().Close()` in
`ProcessWindows::OnExitProcess`, we don't access the PTY we created when
setting up the redirection - we're closing a default constructed one.
This is because the target's `m_launch_info` isn't necessarily the
`launch_info` we get in
[`Target::FinalizeFileActions`](https://github.com/llvm/llvm-project/blob/4a8a0593bdecee49331cb5c31a6c9b18d87ea41c/lldb/source/Target/Target.cpp#L3850)
when calling `SetUpPtyRedirection`.

With this PR, we store the PTY that a process was launched with inside
`ProcessWindows`, so we can later close it.

The wait of five seconds and a timed out `WaitForMultipleObjects` sounds
similar to llvm#159308, but it's a
different call to `WaitForMultipleObjects` here. Still, I wonder if we
could do something to detect this earlier. Maybe some warning message or
debug-only assert if these waits time out?
c-rhodes pushed a commit to llvmbot/llvm-project that referenced this pull request Jan 20, 2026
I noticed that LLDB takes longer to stop than it should. Running a tiny
program like `int main() { return 0; }` in LLDB with `lldb test.exe -o r
-o q` takes about five seconds.

This is caused by the `WaitForMultipleObjects` in
[`ConnectionGenericFile::Read`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/windows/ConnectionGenericFileWindows.cpp#L191-L192)
timing out (it has a timeout of 5s).

It times out, because we never close the PTY created in
[`ProcessLaunchInfo::SetUpPtyRedirection`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/common/ProcessLaunchInfo.cpp#L213).
When we call `target->GetProcessLaunchInfo().GetPTY().Close()` in
`ProcessWindows::OnExitProcess`, we don't access the PTY we created when
setting up the redirection - we're closing a default constructed one.
This is because the target's `m_launch_info` isn't necessarily the
`launch_info` we get in
[`Target::FinalizeFileActions`](https://github.com/llvm/llvm-project/blob/4a8a0593bdecee49331cb5c31a6c9b18d87ea41c/lldb/source/Target/Target.cpp#L3850)
when calling `SetUpPtyRedirection`.

With this PR, we store the PTY that a process was launched with inside
`ProcessWindows`, so we can later close it.

The wait of five seconds and a timed out `WaitForMultipleObjects` sounds
similar to llvm#159308, but it's a
different call to `WaitForMultipleObjects` here. Still, I wonder if we
could do something to detect this earlier. Maybe some warning message or
debug-only assert if these waits time out?

(cherry picked from commit 783fbdc)
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Jan 20, 2026
I noticed that LLDB takes longer to stop than it should. Running a tiny
program like `int main() { return 0; }` in LLDB with `lldb test.exe -o r
-o q` takes about five seconds.

This is caused by the `WaitForMultipleObjects` in
[`ConnectionGenericFile::Read`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/windows/ConnectionGenericFileWindows.cpp#L191-L192)
timing out (it has a timeout of 5s).

It times out, because we never close the PTY created in
[`ProcessLaunchInfo::SetUpPtyRedirection`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/common/ProcessLaunchInfo.cpp#L213).
When we call `target->GetProcessLaunchInfo().GetPTY().Close()` in
`ProcessWindows::OnExitProcess`, we don't access the PTY we created when
setting up the redirection - we're closing a default constructed one.
This is because the target's `m_launch_info` isn't necessarily the
`launch_info` we get in
[`Target::FinalizeFileActions`](https://github.com/llvm/llvm-project/blob/4a8a0593bdecee49331cb5c31a6c9b18d87ea41c/lldb/source/Target/Target.cpp#L3850)
when calling `SetUpPtyRedirection`.

With this PR, we store the PTY that a process was launched with inside
`ProcessWindows`, so we can later close it.

The wait of five seconds and a timed out `WaitForMultipleObjects` sounds
similar to llvm/llvm-project#159308, but it's a
different call to `WaitForMultipleObjects` here. Still, I wonder if we
could do something to detect this earlier. Maybe some warning message or
debug-only assert if these waits time out?

(cherry picked from commit 783fbdc)
BStott6 pushed a commit to BStott6/llvm-project that referenced this pull request Jan 22, 2026
I noticed that LLDB takes longer to stop than it should. Running a tiny
program like `int main() { return 0; }` in LLDB with `lldb test.exe -o r
-o q` takes about five seconds.

This is caused by the `WaitForMultipleObjects` in
[`ConnectionGenericFile::Read`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/windows/ConnectionGenericFileWindows.cpp#L191-L192)
timing out (it has a timeout of 5s).

It times out, because we never close the PTY created in
[`ProcessLaunchInfo::SetUpPtyRedirection`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/common/ProcessLaunchInfo.cpp#L213).
When we call `target->GetProcessLaunchInfo().GetPTY().Close()` in
`ProcessWindows::OnExitProcess`, we don't access the PTY we created when
setting up the redirection - we're closing a default constructed one.
This is because the target's `m_launch_info` isn't necessarily the
`launch_info` we get in
[`Target::FinalizeFileActions`](https://github.com/llvm/llvm-project/blob/4a8a0593bdecee49331cb5c31a6c9b18d87ea41c/lldb/source/Target/Target.cpp#L3850)
when calling `SetUpPtyRedirection`.

With this PR, we store the PTY that a process was launched with inside
`ProcessWindows`, so we can later close it.

The wait of five seconds and a timed out `WaitForMultipleObjects` sounds
similar to llvm#159308, but it's a
different call to `WaitForMultipleObjects` here. Still, I wonder if we
could do something to detect this earlier. Maybe some warning message or
debug-only assert if these waits time out?
navaneethshan pushed a commit to qualcomm/cpullvm-toolchain that referenced this pull request Jan 30, 2026
I noticed that LLDB takes longer to stop than it should. Running a tiny
program like `int main() { return 0; }` in LLDB with `lldb test.exe -o r
-o q` takes about five seconds.

This is caused by the `WaitForMultipleObjects` in
[`ConnectionGenericFile::Read`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/windows/ConnectionGenericFileWindows.cpp#L191-L192)
timing out (it has a timeout of 5s).

It times out, because we never close the PTY created in
[`ProcessLaunchInfo::SetUpPtyRedirection`](https://github.com/llvm/llvm-project/blob/25976e83606f1a7615e3725e6038bb53ee96c3d5/lldb/source/Host/common/ProcessLaunchInfo.cpp#L213).
When we call `target->GetProcessLaunchInfo().GetPTY().Close()` in
`ProcessWindows::OnExitProcess`, we don't access the PTY we created when
setting up the redirection - we're closing a default constructed one.
This is because the target's `m_launch_info` isn't necessarily the
`launch_info` we get in
[`Target::FinalizeFileActions`](https://github.com/llvm/llvm-project/blob/4a8a0593bdecee49331cb5c31a6c9b18d87ea41c/lldb/source/Target/Target.cpp#L3850)
when calling `SetUpPtyRedirection`.

With this PR, we store the PTY that a process was launched with inside
`ProcessWindows`, so we can later close it.

The wait of five seconds and a timed out `WaitForMultipleObjects` sounds
similar to llvm/llvm-project#159308, but it's a
different call to `WaitForMultipleObjects` here. Still, I wonder if we
could do something to detect this earlier. Maybe some warning message or
debug-only assert if these waits time out?

(cherry picked from commit 783fbdc54eff8e39457e7116628df8774726ec80)
(cherry picked from commit 915ef143416a49e446bf4d22f486d406c42ca7a0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Development

Successfully merging this pull request may close these issues.

4 participants