From c35a97fb54dfc046be3bdcde2a6297603aa9b297 Mon Sep 17 00:00:00 2001 From: Roy Shi Date: Sun, 31 Aug 2025 01:45:23 -0700 Subject: [PATCH 1/5] [lldb-dap] Destroy debugger when debug session terminates --- lldb/tools/lldb-dap/DAP.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index b1ad38d983893..4226bfa83c4cf 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -901,9 +901,15 @@ void DAP::SendTerminatedEvent() { // Prevent races if the process exits while we're being asked to disconnect. llvm::call_once(terminated_event_flag, [&] { RunTerminateCommands(); + // Send a "terminated" event llvm::json::Object event(CreateTerminatedEventObject(target)); SendJSON(llvm::json::Value(std::move(event))); + + // Destroy the debugger when the session ends. This will trigger the + // debugger's destroy callbacks for earlier logging and clean-ups, rather + // than waiting for the termination of the lldb-dap process. + lldb::SBDebugger::Destroy(debugger); }); } From 275db0244c02d57877e8c746712e4d0aebe55150 Mon Sep 17 00:00:00 2001 From: Roy Shi Date: Tue, 2 Sep 2025 12:48:31 -0700 Subject: [PATCH 2/5] Reuse code in SendTerminatedEvent() --- lldb/tools/lldb-dap/EventHelper.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index bfb05a387d04d..30272ce6bc298 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -231,13 +231,7 @@ llvm::Error SendThreadStoppedEvent(DAP &dap, bool on_entry) { // Send a "terminated" event to indicate the process is done being // debugged. void SendTerminatedEvent(DAP &dap) { - // Prevent races if the process exits while we're being asked to disconnect. - llvm::call_once(dap.terminated_event_flag, [&] { - dap.RunTerminateCommands(); - // Send a "terminated" event - llvm::json::Object event(CreateTerminatedEventObject(dap.target)); - dap.SendJSON(llvm::json::Value(std::move(event))); - }); + dap.SendTerminatedEvent(); } // Grab any STDOUT and STDERR from the process and send it up to VS Code From b01185635a81efe2a9b32b8b71c4a76884a71e6a Mon Sep 17 00:00:00 2001 From: Roy Shi Date: Tue, 2 Sep 2025 14:11:17 -0700 Subject: [PATCH 3/5] Fix race condition --- lldb/tools/lldb-dap/DAP.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 4226bfa83c4cf..071f4ec38ea55 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -898,6 +898,26 @@ bool DAP::HandleObject(const Message &M) { } void DAP::SendTerminatedEvent() { + // The folloiwng is to prevent races between the request handler thread and + // the event thread, where we are being asked to disconnect while the debugee + // process exits. + // + // The request handler thread, when entering this method, would have already + // acquired the API mutex (in BaseRequestHandler::Run). The next thing it + // tries to do is to acquire the call_once() mutex below. + // + // A deadlock will happen if the event thread happens to acquire the + // call_once() mutex first, then go into SBDebugger::Destroy() and eventually + // Target::Destroy(), where it tries to acquire the API mutex. + // + // To avoid this deadlock, we require that both threads acquire the API mutex + // first. Whoever gets it can go through the call_once() without issue. + // + // This is during the termination of a debug session, so performance loss + // introduced by this additional lock are hopefully not too critical. + lldb::SBMutex lock = GetAPIMutex(); + std::lock_guard guard(lock); + // Prevent races if the process exits while we're being asked to disconnect. llvm::call_once(terminated_event_flag, [&] { RunTerminateCommands(); From a803ad29b8d100ab4b103bd8e38fdca62c02af71 Mon Sep 17 00:00:00 2001 From: Roy Shi Date: Wed, 3 Sep 2025 12:49:56 -0700 Subject: [PATCH 4/5] Move debugger destroy to the end of the DAP lifecycle --- lldb/tools/lldb-dap/DAP.cpp | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 071f4ec38ea55..f5c098363fbc5 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -898,38 +898,12 @@ bool DAP::HandleObject(const Message &M) { } void DAP::SendTerminatedEvent() { - // The folloiwng is to prevent races between the request handler thread and - // the event thread, where we are being asked to disconnect while the debugee - // process exits. - // - // The request handler thread, when entering this method, would have already - // acquired the API mutex (in BaseRequestHandler::Run). The next thing it - // tries to do is to acquire the call_once() mutex below. - // - // A deadlock will happen if the event thread happens to acquire the - // call_once() mutex first, then go into SBDebugger::Destroy() and eventually - // Target::Destroy(), where it tries to acquire the API mutex. - // - // To avoid this deadlock, we require that both threads acquire the API mutex - // first. Whoever gets it can go through the call_once() without issue. - // - // This is during the termination of a debug session, so performance loss - // introduced by this additional lock are hopefully not too critical. - lldb::SBMutex lock = GetAPIMutex(); - std::lock_guard guard(lock); - // Prevent races if the process exits while we're being asked to disconnect. llvm::call_once(terminated_event_flag, [&] { RunTerminateCommands(); - // Send a "terminated" event llvm::json::Object event(CreateTerminatedEventObject(target)); SendJSON(llvm::json::Value(std::move(event))); - - // Destroy the debugger when the session ends. This will trigger the - // debugger's destroy callbacks for earlier logging and clean-ups, rather - // than waiting for the termination of the lldb-dap process. - lldb::SBDebugger::Destroy(debugger); }); } @@ -1094,6 +1068,11 @@ llvm::Error DAP::Loop() { out.Stop(); err.Stop(); StopEventHandlers(); + + // Destroy the debugger when the session ends. This will trigger the + // debugger's destroy callbacks for earlier logging and clean-ups, rather + // than waiting for the termination of the lldb-dap process. + lldb::SBDebugger::Destroy(debugger); }); while (true) { From 6a0693cc2b7986753671242723abb2bc66b86685 Mon Sep 17 00:00:00 2001 From: Roy Shi Date: Wed, 3 Sep 2025 13:06:26 -0700 Subject: [PATCH 5/5] Fix format --- lldb/tools/lldb-dap/EventHelper.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp index 30272ce6bc298..ecd630cb530d6 100644 --- a/lldb/tools/lldb-dap/EventHelper.cpp +++ b/lldb/tools/lldb-dap/EventHelper.cpp @@ -230,9 +230,7 @@ llvm::Error SendThreadStoppedEvent(DAP &dap, bool on_entry) { // Send a "terminated" event to indicate the process is done being // debugged. -void SendTerminatedEvent(DAP &dap) { - dap.SendTerminatedEvent(); -} +void SendTerminatedEvent(DAP &dap) { dap.SendTerminatedEvent(); } // Grab any STDOUT and STDERR from the process and send it up to VS Code // via an "output" event to the "stdout" and "stderr" categories.