[Lldb-commits] [lldb] [lldb] Expose the Target API lock through the SB API (PR #131404)

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Fri Mar 14 16:21:00 PDT 2025


https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/131404

>From 5ea2ad6ecb388818b009fa89c4aebe1e0e4213df Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Fri, 14 Mar 2025 15:19:58 -0700
Subject: [PATCH 1/2] [lldb] Expose the Target API lock through the SB API

---
 lldb/include/lldb/API/SBDefines.h |  1 +
 lldb/include/lldb/API/SBLock.h    | 45 ++++++++++++++++++++++
 lldb/include/lldb/API/SBTarget.h  |  6 ++-
 lldb/include/lldb/Target/Target.h | 16 +++++++-
 lldb/source/API/CMakeLists.txt    |  1 +
 lldb/source/API/SBLock.cpp        | 40 +++++++++++++++++++
 lldb/source/API/SBTarget.cpp      | 36 +++++++++--------
 lldb/unittests/API/CMakeLists.txt |  1 +
 lldb/unittests/API/SBLockTest.cpp | 64 +++++++++++++++++++++++++++++++
 9 files changed, 193 insertions(+), 17 deletions(-)
 create mode 100644 lldb/include/lldb/API/SBLock.h
 create mode 100644 lldb/source/API/SBLock.cpp
 create mode 100644 lldb/unittests/API/SBLockTest.cpp

diff --git a/lldb/include/lldb/API/SBDefines.h b/lldb/include/lldb/API/SBDefines.h
index ed5a80da117a5..7fa5150d69d8a 100644
--- a/lldb/include/lldb/API/SBDefines.h
+++ b/lldb/include/lldb/API/SBDefines.h
@@ -84,6 +84,7 @@ class LLDB_API SBLanguageRuntime;
 class LLDB_API SBLaunchInfo;
 class LLDB_API SBLineEntry;
 class LLDB_API SBListener;
+class LLDB_API SBLock;
 class LLDB_API SBMemoryRegionInfo;
 class LLDB_API SBMemoryRegionInfoList;
 class LLDB_API SBModule;
diff --git a/lldb/include/lldb/API/SBLock.h b/lldb/include/lldb/API/SBLock.h
new file mode 100644
index 0000000000000..8d6fdfb959dfb
--- /dev/null
+++ b/lldb/include/lldb/API/SBLock.h
@@ -0,0 +1,45 @@
+//===-- SBLock.h ----------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_API_SBLOCK_H
+#define LLDB_API_SBLOCK_H
+
+#include "lldb/API/SBDefines.h"
+#include "lldb/lldb-forward.h"
+
+#include <mutex>
+
+namespace lldb_private {
+struct APILock;
+}
+
+namespace lldb {
+
+#ifndef SWIG
+class LLDB_API SBLock {
+public:
+  ~SBLock();
+
+  bool IsValid() const;
+
+private:
+  friend class SBTarget;
+
+  SBLock() = default;
+  SBLock(std::recursive_mutex &mutex);
+  SBLock(std::recursive_mutex &mutex, lldb::TargetSP target_sp);
+  SBLock(const SBLock &rhs) = delete;
+  const SBLock &operator=(const SBLock &rhs) = delete;
+
+  std::unique_ptr<lldb_private::APILock> m_opaque_up;
+};
+#endif
+
+} // namespace lldb
+
+#endif
diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h
index bb912ab41d0fe..6120c289743e3 100644
--- a/lldb/include/lldb/API/SBTarget.h
+++ b/lldb/include/lldb/API/SBTarget.h
@@ -342,7 +342,7 @@ class LLDB_API SBTarget {
   uint32_t GetAddressByteSize();
 
   const char *GetTriple();
-  
+
   const char *GetABIName();
 
   const char *GetLabel() const;
@@ -946,6 +946,10 @@ class LLDB_API SBTarget {
   ///     An error if a Trace already exists or the trace couldn't be created.
   lldb::SBTrace CreateTrace(SBError &error);
 
+#ifndef SWIG
+  lldb::SBLock GetAPILock() const;
+#endif
+
 protected:
   friend class SBAddress;
   friend class SBAddressRange;
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 80ce5f013344c..8321a963d4c5b 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -1327,7 +1327,7 @@ class Target : public std::enable_shared_from_this<Target>,
     StopHook(const StopHook &rhs);
     virtual ~StopHook() = default;
 
-    enum class StopHookKind  : uint32_t { CommandBased = 0, ScriptBased };
+    enum class StopHookKind : uint32_t { CommandBased = 0, ScriptBased };
     enum class StopHookResult : uint32_t {
       KeepStopped = 0,
       RequestContinue,
@@ -1692,6 +1692,20 @@ class Target : public std::enable_shared_from_this<Target>,
   }
 };
 
+/// The private implementation backing SBLock.
+struct APILock {
+  APILock(std::recursive_mutex &mutex) : lock(mutex) {}
+  std::lock_guard<std::recursive_mutex> lock;
+};
+
+/// The private implementation used by SBLock to hand out the target API mutex.
+/// It has a TargetSP to ensure the lock cannot outlive the target.
+struct TargetAPILock : public APILock {
+  TargetAPILock(std::recursive_mutex &mutex, lldb::TargetSP target_sp)
+      : APILock(mutex), target_sp(target_sp) {}
+  lldb::TargetSP target_sp;
+};
+
 } // namespace lldb_private
 
 #endif // LLDB_TARGET_TARGET_H
diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt
index 48d5cde5bf592..c9a9433b2329d 100644
--- a/lldb/source/API/CMakeLists.txt
+++ b/lldb/source/API/CMakeLists.txt
@@ -77,6 +77,7 @@ add_lldb_library(liblldb SHARED ${option_framework}
   SBLaunchInfo.cpp
   SBLineEntry.cpp
   SBListener.cpp
+  SBLock.cpp
   SBMemoryRegionInfo.cpp
   SBMemoryRegionInfoList.cpp
   SBModule.cpp
diff --git a/lldb/source/API/SBLock.cpp b/lldb/source/API/SBLock.cpp
new file mode 100644
index 0000000000000..448c8ccd46747
--- /dev/null
+++ b/lldb/source/API/SBLock.cpp
@@ -0,0 +1,40 @@
+//===-- SBLock.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBLock.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Instrumentation.h"
+#include "lldb/lldb-forward.h"
+
+#include <memory>
+#include <mutex>
+
+using namespace lldb;
+using namespace lldb_private;
+
+#ifndef SWIG
+
+SBLock::SBLock(std::recursive_mutex &mutex)
+    : m_opaque_up(std::make_unique<APILock>(mutex)) {
+  LLDB_INSTRUMENT_VA(this);
+}
+
+SBLock::SBLock(std::recursive_mutex &mutex, lldb::TargetSP target_sp)
+    : m_opaque_up(std::make_unique<TargetAPILock>(mutex, target_sp)) {
+  LLDB_INSTRUMENT_VA(this);
+}
+
+SBLock::~SBLock() { LLDB_INSTRUMENT_VA(this); }
+
+bool SBLock::IsValid() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  return static_cast<bool>(m_opaque_up);
+}
+
+#endif
diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp
index dd9caa724ea36..a2761de8bfc5e 100644
--- a/lldb/source/API/SBTarget.cpp
+++ b/lldb/source/API/SBTarget.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/API/SBTarget.h"
+#include "lldb/API/SBLock.h"
 #include "lldb/Utility/Instrumentation.h"
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/lldb-public.h"
@@ -18,6 +19,7 @@
 #include "lldb/API/SBExpressionOptions.h"
 #include "lldb/API/SBFileSpec.h"
 #include "lldb/API/SBListener.h"
+#include "lldb/API/SBLock.h"
 #include "lldb/API/SBModule.h"
 #include "lldb/API/SBModuleSpec.h"
 #include "lldb/API/SBProcess.h"
@@ -544,9 +546,8 @@ lldb::SBProcess SBTarget::ConnectRemote(SBListener &listener, const char *url,
   if (target_sp) {
     std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
     if (listener.IsValid())
-      process_sp =
-          target_sp->CreateProcess(listener.m_opaque_sp, plugin_name, nullptr,
-                                   true);
+      process_sp = target_sp->CreateProcess(listener.m_opaque_sp, plugin_name,
+                                            nullptr, true);
     else
       process_sp = target_sp->CreateProcess(
           target_sp->GetDebugger().GetListener(), plugin_name, nullptr, true);
@@ -1040,7 +1041,7 @@ SBTarget::BreakpointCreateForException(lldb::LanguageType language,
     std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
     const bool hardware = false;
     sb_bp = target_sp->CreateExceptionBreakpoint(language, catch_bp, throw_bp,
-                                                  hardware);
+                                                 hardware);
   }
 
   return sb_bp;
@@ -1060,14 +1061,9 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateFromScript(
     Status error;
 
     StructuredData::ObjectSP obj_sp = extra_args.m_impl_up->GetObjectSP();
-    sb_bp =
-        target_sp->CreateScriptedBreakpoint(class_name,
-                                            module_list.get(),
-                                            file_list.get(),
-                                            false, /* internal */
-                                            request_hardware,
-                                            obj_sp,
-                                            &error);
+    sb_bp = target_sp->CreateScriptedBreakpoint(
+        class_name, module_list.get(), file_list.get(), false, /* internal */
+        request_hardware, obj_sp, &error);
   }
 
   return sb_bp;
@@ -1692,8 +1688,8 @@ uint32_t SBTarget::GetMaximumNumberOfChildrenToDisplay() const {
   LLDB_INSTRUMENT_VA(this);
 
   TargetSP target_sp(GetSP());
-  if(target_sp){
-     return target_sp->GetMaximumNumberOfChildrenToDisplay();
+  if (target_sp) {
+    return target_sp->GetMaximumNumberOfChildrenToDisplay();
   }
   return 0;
 }
@@ -2193,7 +2189,7 @@ SBError SBTarget::SetModuleLoadAddress(lldb::SBModule module,
 }
 
 SBError SBTarget::SetModuleLoadAddress(lldb::SBModule module,
-                                               uint64_t slide_offset) {
+                                       uint64_t slide_offset) {
 
   SBError sb_error;
 
@@ -2439,3 +2435,13 @@ lldb::SBTrace SBTarget::CreateTrace(lldb::SBError &error) {
   }
   return SBTrace();
 }
+
+#ifndef SWIG
+lldb::SBLock SBTarget::GetAPILock() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  if (TargetSP target_sp = GetSP())
+    return lldb::SBLock(target_sp->GetAPIMutex(), target_sp);
+  return lldb::SBLock();
+}
+#endif
diff --git a/lldb/unittests/API/CMakeLists.txt b/lldb/unittests/API/CMakeLists.txt
index fe2ff684a5d92..5a88d06b601a3 100644
--- a/lldb/unittests/API/CMakeLists.txt
+++ b/lldb/unittests/API/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_lldb_unittest(APITests
   SBCommandInterpreterTest.cpp
   SBLineEntryTest.cpp
+  SBLockTest.cpp
 
   LINK_LIBS
     liblldb
diff --git a/lldb/unittests/API/SBLockTest.cpp b/lldb/unittests/API/SBLockTest.cpp
new file mode 100644
index 0000000000000..1fa9c184bf79b
--- /dev/null
+++ b/lldb/unittests/API/SBLockTest.cpp
@@ -0,0 +1,64 @@
+//===-- SBLockTest.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===/
+
+#include "lldb/API/SBLock.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBTarget.h"
+#include "gtest/gtest.h"
+#include <atomic>
+#include <chrono>
+#include <thread>
+
+TEST(SBLockTest, LockTest) {
+  lldb::SBDebugger debugger = lldb::SBDebugger::Create();
+  lldb::SBTarget target = debugger.GetDummyTarget();
+
+  std::mutex m;
+  std::condition_variable cv;
+  bool wakeup = false;
+  std::atomic<bool> locked = false;
+
+  std::thread test_thread([&]() {
+    {
+      {
+        std::unique_lock lk(m);
+        cv.wait(lk, [&] { return wakeup; });
+      }
+
+      ASSERT_TRUE(locked);
+      target.BreakpointCreateByName("foo", "bar");
+      ASSERT_FALSE(locked);
+
+      cv.notify_one();
+    }
+  });
+
+  // Take the API lock.
+  {
+    lldb::SBLock lock = target.GetAPILock();
+    ASSERT_FALSE(locked.exchange(true));
+
+    // Wake up the test thread.
+    {
+      std::lock_guard lk(m);
+      wakeup = true;
+    }
+    cv.notify_one();
+
+    // Wait 500ms to confirm the thread is blocked.
+    {
+      std::unique_lock<std::mutex> lk(m);
+      auto result = cv.wait_for(lk, std::chrono::milliseconds(500));
+      ASSERT_EQ(result, std::cv_status::timeout);
+    }
+
+    ASSERT_TRUE(locked.exchange(false));
+  }
+
+  test_thread.join();
+}

>From acb6a6ed6051d5d8297645f12a136ad7b2754b8e Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Fri, 14 Mar 2025 16:20:35 -0700
Subject: [PATCH 2/2] Address code review feedback

---
 lldb/include/lldb/API/SBLock.h    |  8 +++++---
 lldb/include/lldb/API/SBTarget.h  |  2 +-
 lldb/include/lldb/Target/Target.h |  2 ++
 lldb/source/API/SBLock.cpp        |  5 -----
 lldb/source/API/SBTarget.cpp      | 10 ++++------
 lldb/unittests/API/SBLockTest.cpp |  2 +-
 6 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/lldb/include/lldb/API/SBLock.h b/lldb/include/lldb/API/SBLock.h
index 8d6fdfb959dfb..d13fec989990b 100644
--- a/lldb/include/lldb/API/SBLock.h
+++ b/lldb/include/lldb/API/SBLock.h
@@ -28,11 +28,13 @@ class LLDB_API SBLock {
   bool IsValid() const;
 
 private:
-  friend class SBTarget;
-
   SBLock() = default;
-  SBLock(std::recursive_mutex &mutex);
+
+  // Private constructor used by SBTarget to create the Target API lock.
+  // Requires a friend declaration.
   SBLock(std::recursive_mutex &mutex, lldb::TargetSP target_sp);
+  friend class SBTarget;
+
   SBLock(const SBLock &rhs) = delete;
   const SBLock &operator=(const SBLock &rhs) = delete;
 
diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h
index 6120c289743e3..03d6b623fd425 100644
--- a/lldb/include/lldb/API/SBTarget.h
+++ b/lldb/include/lldb/API/SBTarget.h
@@ -947,7 +947,7 @@ class LLDB_API SBTarget {
   lldb::SBTrace CreateTrace(SBError &error);
 
 #ifndef SWIG
-  lldb::SBLock GetAPILock() const;
+  lldb::SBLock AcquireAPILock() const;
 #endif
 
 protected:
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 8321a963d4c5b..4379f73936d0c 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -1695,6 +1695,7 @@ class Target : public std::enable_shared_from_this<Target>,
 /// The private implementation backing SBLock.
 struct APILock {
   APILock(std::recursive_mutex &mutex) : lock(mutex) {}
+  virtual ~APILock() = default;
   std::lock_guard<std::recursive_mutex> lock;
 };
 
@@ -1703,6 +1704,7 @@ struct APILock {
 struct TargetAPILock : public APILock {
   TargetAPILock(std::recursive_mutex &mutex, lldb::TargetSP target_sp)
       : APILock(mutex), target_sp(target_sp) {}
+  ~TargetAPILock() override = default;
   lldb::TargetSP target_sp;
 };
 
diff --git a/lldb/source/API/SBLock.cpp b/lldb/source/API/SBLock.cpp
index 448c8ccd46747..4338049cb1689 100644
--- a/lldb/source/API/SBLock.cpp
+++ b/lldb/source/API/SBLock.cpp
@@ -19,11 +19,6 @@ using namespace lldb_private;
 
 #ifndef SWIG
 
-SBLock::SBLock(std::recursive_mutex &mutex)
-    : m_opaque_up(std::make_unique<APILock>(mutex)) {
-  LLDB_INSTRUMENT_VA(this);
-}
-
 SBLock::SBLock(std::recursive_mutex &mutex, lldb::TargetSP target_sp)
     : m_opaque_up(std::make_unique<TargetAPILock>(mutex, target_sp)) {
   LLDB_INSTRUMENT_VA(this);
diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp
index a2761de8bfc5e..96d5fd1cbf31a 100644
--- a/lldb/source/API/SBTarget.cpp
+++ b/lldb/source/API/SBTarget.cpp
@@ -7,11 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/API/SBTarget.h"
-#include "lldb/API/SBLock.h"
-#include "lldb/Utility/Instrumentation.h"
-#include "lldb/Utility/LLDBLog.h"
-#include "lldb/lldb-public.h"
-
 #include "lldb/API/SBBreakpoint.h"
 #include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBEnvironment.h"
@@ -60,11 +55,14 @@
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/Args.h"
 #include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Instrumentation.h"
+#include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/ProcessInfo.h"
 #include "lldb/Utility/RegularExpression.h"
 #include "lldb/ValueObject/ValueObjectConstResult.h"
 #include "lldb/ValueObject/ValueObjectList.h"
 #include "lldb/ValueObject/ValueObjectVariable.h"
+#include "lldb/lldb-public.h"
 
 #include "Commands/CommandObjectBreakpoint.h"
 #include "lldb/Interpreter/CommandReturnObject.h"
@@ -2437,7 +2435,7 @@ lldb::SBTrace SBTarget::CreateTrace(lldb::SBError &error) {
 }
 
 #ifndef SWIG
-lldb::SBLock SBTarget::GetAPILock() const {
+lldb::SBLock SBTarget::AcquireAPILock() const {
   LLDB_INSTRUMENT_VA(this);
 
   if (TargetSP target_sp = GetSP())
diff --git a/lldb/unittests/API/SBLockTest.cpp b/lldb/unittests/API/SBLockTest.cpp
index 1fa9c184bf79b..33bf94ead1dd3 100644
--- a/lldb/unittests/API/SBLockTest.cpp
+++ b/lldb/unittests/API/SBLockTest.cpp
@@ -40,7 +40,7 @@ TEST(SBLockTest, LockTest) {
 
   // Take the API lock.
   {
-    lldb::SBLock lock = target.GetAPILock();
+    lldb::SBLock lock = target.AcquireAPILock();
     ASSERT_FALSE(locked.exchange(true));
 
     // Wake up the test thread.



More information about the lldb-commits mailing list