https://github.com/ashgti created 
https://github.com/llvm/llvm-project/pull/140142

The recent change to the startup flow has highlighted a problem with breakpoint 
events being missed.

Previously, we would handle `attach`/`launch` commands immediately, leaving the 
process in a suspended until the `configurationDone` command was sent.

If a breakpoint was set between the `attach`/`launch` and the 
`configurationDone` request, it may have been resolved immediately, but there 
was a chance the module was not yet loaded.

With the new startup flow, the breakpoint is always set before the target is 
loaded. For some targets, this is fine and the breakpoint event fires 
eventually and marks the breakpoint as verified. However, if a 
`attach`/`launch` request has `attachCommands`/`launchCommands` that create a 
new target then we will miss the breakpoint events associated with that target 
because we haven't configured the target broadcaster for the debugger. We only 
configure that in the `DAP::SetTarget` call, which happens after the events 
would have occurred.

To address this, I adjusted the `DAP::EventThread` to listen for the broadcast 
class instead of an individual broadcaster.

I think this may also fix the unstable `TestDAP_breakpointEvents` tests.

We're not using the `Debugger::DefaultEventHandler` 
https://github.com/llvm/llvm-project/blob/090f46d8d246762401c41c5486dde299382d6c90/lldb/source/Core/Debugger.cpp#L2048
 which is why these broadcast classes are not registered on our debugger 
instance in lldb-dap.

>From ea5ee538a1ca384725f8166ec9289883683b52ce Mon Sep 17 00:00:00 2001
From: John Harrison <harj...@google.com>
Date: Thu, 15 May 2025 14:13:48 -0700
Subject: [PATCH] [lldb-dap] Listen for broadcast classes.

The recent change to the startup flow has highlighted a problem with
breakpoint events being missed.

Previously, we would handle `attach`/`launch` commands immediately,
leaving the process in a suspended until the `configurationDone` command
was sent.

If a breakpoint was set between the `attach`/`launch` and the
`configurationDone` request, it may have been resolved immediately, but
there was a chance the module was not yet loaded.

With the new startup flow, the breakpoint is always set before the
target is loaded. For some targets, this is fine and the breakpoint
event fires eventually and marks the breakpoint as verified. However,
if a `attach`/`launch` request has `attachCommands`/`launchCommands`
that create a new target then we will miss the breakpoint events
associated with that target because we haven't configured the target
broadcaster for the debugger. We only configure that in the
`DAP::SetTarget` call, which happens after the events would have
occurred.

To address this, I adjusted the `DAP::EventThread` to listen for the
broadcast class instead of an individual broadcaster.

I think this may also fix the unstable `TestDAP_breakpointEvents` tests.
---
 lldb/tools/lldb-dap/DAP.cpp                   | 53 +++++++++----------
 lldb/tools/lldb-dap/DAP.h                     |  3 +-
 .../lldb-dap/Handler/AttachRequestHandler.cpp |  4 +-
 .../tools/lldb-dap/Handler/RequestHandler.cpp |  2 +-
 4 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 51f9da854f4b6..827ebb1465938 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -26,6 +26,7 @@
 #include "lldb/API/SBListener.h"
 #include "lldb/API/SBProcess.h"
 #include "lldb/API/SBStream.h"
+#include "lldb/API/SBTarget.h"
 #include "lldb/Utility/IOObject.h"
 #include "lldb/Utility/Status.h"
 #include "lldb/lldb-defines.h"
@@ -719,23 +720,7 @@ lldb::SBTarget DAP::CreateTarget(lldb::SBError &error) {
   return target;
 }
 
-void DAP::SetTarget(const lldb::SBTarget target) {
-  this->target = target;
-
-  if (target.IsValid()) {
-    // Configure breakpoint event listeners for the target.
-    lldb::SBListener listener = this->debugger.GetListener();
-    listener.StartListeningForEvents(
-        this->target.GetBroadcaster(),
-        lldb::SBTarget::eBroadcastBitBreakpointChanged |
-            lldb::SBTarget::eBroadcastBitModulesLoaded |
-            lldb::SBTarget::eBroadcastBitModulesUnloaded |
-            lldb::SBTarget::eBroadcastBitSymbolsLoaded |
-            lldb::SBTarget::eBroadcastBitSymbolsChanged);
-    listener.StartListeningForEvents(this->broadcaster,
-                                     eBroadcastBitStopEventThread);
-  }
-}
+void DAP::SetTarget(const lldb::SBTarget target) { this->target = target; }
 
 bool DAP::HandleObject(const Message &M) {
   TelemetryDispatcher dispatcher(&debugger);
@@ -1489,17 +1474,31 @@ void DAP::ProgressEventThread() {
 }
 
 // All events from the debugger, target, process, thread and frames are
-// received in this function that runs in its own thread. We are using a
-// "FILE *" to output packets back to VS Code and they have mutexes in them
-// them prevent multiple threads from writing simultaneously so no locking
-// is required.
+// received in this function that runs in its own thread.
 void DAP::EventThread() {
   llvm::set_thread_name(transport.GetClientName() + ".event_handler");
-  lldb::SBEvent event;
+
+  // Configure the debugger listener for all events lldb-dap is interested in.
   lldb::SBListener listener = debugger.GetListener();
-  broadcaster.AddListener(listener, eBroadcastBitStopEventThread);
-  debugger.GetBroadcaster().AddListener(
-      listener, lldb::eBroadcastBitError | lldb::eBroadcastBitWarning);
+  listener.StartListeningForEventClass(
+      debugger, lldb::SBTarget::GetBroadcasterClassName(),
+      lldb::SBTarget::eBroadcastBitBreakpointChanged |
+          lldb::SBTarget::eBroadcastBitModulesLoaded |
+          lldb::SBTarget::eBroadcastBitModulesUnloaded |
+          lldb::SBTarget::eBroadcastBitSymbolsLoaded |
+          lldb::SBTarget::eBroadcastBitSymbolsChanged);
+  listener.StartListeningForEventClass(
+      debugger, lldb::SBProcess::GetBroadcasterClassName(),
+      lldb::SBProcess::eBroadcastBitStateChanged |
+          lldb::SBProcess::eBroadcastBitSTDOUT |
+          lldb::SBProcess::eBroadcastBitSTDERR);
+  listener.StartListeningForEvents(debugger.GetBroadcaster(),
+                                   lldb::SBDebugger::eBroadcastBitError |
+                                       lldb::SBDebugger::eBroadcastBitWarning);
+  // Listen for the lldb-dap stop event.
+  listener.StartListeningForEvents(broadcaster, eBroadcastBitStopEventThread);
+
+  lldb::SBEvent event;
   bool done = false;
   while (!done) {
     if (listener.WaitForEvent(1, event)) {
@@ -1633,8 +1632,8 @@ void DAP::EventThread() {
             SendJSON(llvm::json::Value(std::move(bp_event)));
           }
         }
-      } else if (event_mask & lldb::eBroadcastBitError ||
-                 event_mask & lldb::eBroadcastBitWarning) {
+      } else if (event_mask & lldb::SBDebugger::eBroadcastBitError ||
+                 event_mask & lldb::SBDebugger::eBroadcastBitWarning) {
         lldb::SBStructuredData data =
             lldb::SBDebugger::GetDiagnosticFromEvent(event);
         if (!data.IsValid())
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index c2e4c2dea582e..c76f6d2ba1ffc 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -334,8 +334,7 @@ struct DAP {
   ///     An SBTarget object.
   lldb::SBTarget CreateTarget(lldb::SBError &error);
 
-  /// Set given target object as a current target for lldb-dap and start
-  /// listeing for its breakpoint events.
+  /// Set given target object as a current target for lldb-dap.
   void SetTarget(const lldb::SBTarget target);
 
   bool HandleObject(const protocol::Message &M);
diff --git a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
index 0293ffbd0c922..167ddb9eb73ed 100644
--- a/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
@@ -96,7 +96,9 @@ Error AttachRequestHandler::Run(const AttachRequestArguments 
&args) const {
       if (llvm::Error err = dap.RunAttachCommands(args.attachCommands))
         return err;
 
-      dap.target = dap.debugger.GetSelectedTarget();
+      // The custom commands might have created a new target so we should use
+      // the selected target after these commands are run.
+      dap.SetTarget(dap.debugger.GetSelectedTarget());
 
       // Validate the attachCommand results.
       if (!dap.target.GetProcess().IsValid())
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index 93bc80a38e29d..7de582ee94320 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -209,7 +209,7 @@ llvm::Error BaseRequestHandler::LaunchProcess(
 
       // The custom commands might have created a new target so we should use
       // the selected target after these commands are run.
-      dap.target = dap.debugger.GetSelectedTarget();
+      dap.SetTarget(dap.debugger.GetSelectedTarget());
     }
   }
 

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to