[Lldb-commits] [lldb] Reland "[lldb] Clear thread-creation breakpoints in ProcessGDBRemote::Clear (#134397)" (PR #135296)

Felipe de Azevedo Piovezan via lldb-commits lldb-commits at lists.llvm.org
Thu Apr 10 18:40:54 PDT 2025


https://github.com/felipepiovezan created https://github.com/llvm/llvm-project/pull/135296



This reapplies commit 232525f06942adb3b9977632e38dcd5f08c0642d.

The original commit triggered a sanitizer failure when Target was destroyed. In Target::Destroy, `DeleteCurrentProcess` was called, but it did not destroy the thread creation breakpoints for the underlying ProcessGDBRemote, because said method would not call `ProcessGDBRemote::Clear`.
Target then proceeded to destroy its breakpoints, which resulted in a call to the destructor a std::vector containing the breakpoints. Through a sequence of complicated events, destroying breakpoints caused the reference count of the underlying to finally reach zero. This, in turn, called `ProcessGDBRemote::Clear`, which attempted to destroy the breakpoints. To do that, it would query back into the Target vector of breakpoint, which we are in the middle of destroying.

We solve this by moving the breakpoint deletion into `Process:DoDestroy`, which is a virtual Process method that will be called much earlier.

>From ae63d5b00fe1fd3734de7181661e155820681d37 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Wed, 9 Apr 2025 07:38:13 -0700
Subject: [PATCH] Reland "[lldb] Clear thread-creation breakpoints in
 ProcessGDBRemote::Clear (#134397)"

This reapplies commit 232525f06942adb3b9977632e38dcd5f08c0642d.

The original commit triggered a sanitizer failure when Target was
destroyed. In Target::Destroy, `DeleteCurrentProcess` was called, but it
did not destroy the thread creation breakpoints for the underlying
ProcessGDBRemote, because said method would not call
`ProcessGDBRemote::Clear`.
Target then proceeded to destroy its breakpoints, which resulted in a
call to the destructor a std::vector containing the breakpoints. Through
a sequence of complicated events, destroying breakpoints caused the
reference count of the underlying to finally reach zero. This, in
turn, called `ProcessGDBRemote::Clear`, which attempted to destroy the
breakpoints. To do that, it would query back into the Target vector of
breakpoint, which we are in the middle of destroying.

We solve this by moving the breakpoint deletion into
`Process:DoDestroy`, which is a virtual Process method that will be
called much earlier.
---
 .../Process/gdb-remote/ProcessGDBRemote.cpp   |  9 +++++++++
 .../Process/gdb-remote/ProcessGDBRemote.h     |  3 +++
 .../TestBreakpointsThreadInit.py              | 20 +++++++++++++++++++
 3 files changed, 32 insertions(+)

diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 68360788c96e6..b616e99be83b2 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -2571,9 +2571,18 @@ Status ProcessGDBRemote::DoDestroy() {
 
   StopAsyncThread();
   KillDebugserverProcess();
+  RemoveNewThreadBreakpoints();
   return Status();
 }
 
+void ProcessGDBRemote::RemoveNewThreadBreakpoints() {
+  if (m_thread_create_bp_sp) {
+    if (TargetSP target_sp = m_target_wp.lock())
+      target_sp->RemoveBreakpointByID(m_thread_create_bp_sp->GetID());
+    m_thread_create_bp_sp.reset();
+  }
+}
+
 void ProcessGDBRemote::SetLastStopPacket(
     const StringExtractorGDBRemote &response) {
   const bool did_exec =
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 1cbd1e82b381d..20d7fc0801eb3 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -436,6 +436,9 @@ class ProcessGDBRemote : public Process,
                                            lldb::user_id_t break_id,
                                            lldb::user_id_t break_loc_id);
 
+  /// Remove the breakpoints associated with thread creation from the Target.
+  void RemoveNewThreadBreakpoints();
+
   // ContinueDelegate interface
   void HandleAsyncStdout(llvm::StringRef out) override;
   void HandleAsyncMisc(llvm::StringRef data) override;
diff --git a/lldb/test/API/macosx/thread_start_bps/TestBreakpointsThreadInit.py b/lldb/test/API/macosx/thread_start_bps/TestBreakpointsThreadInit.py
index 1c6fd4f91c73e..bf667f6f7d336 100644
--- a/lldb/test/API/macosx/thread_start_bps/TestBreakpointsThreadInit.py
+++ b/lldb/test/API/macosx/thread_start_bps/TestBreakpointsThreadInit.py
@@ -35,3 +35,23 @@ def test_internal_bps_resolved(self):
         for bp in bps:
             num_resolved += bp.GetNumResolvedLocations()
         self.assertGreater(num_resolved, 0)
+
+    @skipUnlessDarwin
+    def test_internal_bps_deleted_on_relaunch(self):
+        self.build()
+
+        source_file = lldb.SBFileSpec("main.c")
+        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+            self, "initial hello", source_file
+        )
+
+        self.runCmd("break list --internal")
+        output = self.res.GetOutput()
+        self.assertEqual(output.count("thread-creation"), 1)
+
+        process.Kill()
+        self.runCmd("run", RUN_SUCCEEDED)
+
+        self.runCmd("break list --internal")
+        output = self.res.GetOutput()
+        self.assertEqual(output.count("thread-creation"), 1)



More information about the lldb-commits mailing list