[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
Wed Mar 19 10:37:06 PDT 2025


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

>From 180e736b110c3ba6ba36719326138b6cece5a51b 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] [lldb] Expose the Target API lock through the SB API

---
 lldb/bindings/interface/SBLockExtensions.i    | 11 ++++
 lldb/bindings/interfaces.swig                 |  4 +-
 lldb/include/lldb/API/LLDB.h                  |  1 +
 lldb/include/lldb/API/SBDefines.h             |  1 +
 lldb/include/lldb/API/SBLock.h                | 46 +++++++++++++++
 lldb/include/lldb/API/SBTarget.h              |  4 +-
 lldb/include/lldb/Target/Target.h             | 17 +++++-
 lldb/source/API/CMakeLists.txt                |  1 +
 lldb/source/API/SBLock.cpp                    | 52 +++++++++++++++++
 lldb/source/API/SBTarget.cpp                  | 40 ++++++-------
 .../API/python_api/target/TestTargetAPI.py    | 28 +++++++++
 lldb/unittests/API/CMakeLists.txt             |  1 +
 lldb/unittests/API/SBLockTest.cpp             | 57 +++++++++++++++++++
 13 files changed, 241 insertions(+), 22 deletions(-)
 create mode 100644 lldb/bindings/interface/SBLockExtensions.i
 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/bindings/interface/SBLockExtensions.i b/lldb/bindings/interface/SBLockExtensions.i
new file mode 100644
index 0000000000000..27736c3f287b7
--- /dev/null
+++ b/lldb/bindings/interface/SBLockExtensions.i
@@ -0,0 +1,11 @@
+%extend lldb::SBLock {
+#ifdef SWIGPYTHON
+    %pythoncode %{
+        def __enter__(self):
+            return self
+
+        def __exit__(self, exc_type, exc_value, traceback):
+            self.Unlock()
+    %}
+#endif
+}
diff --git a/lldb/bindings/interfaces.swig b/lldb/bindings/interfaces.swig
index 08df9a1a8d539..3dffc23d1a4f7 100644
--- a/lldb/bindings/interfaces.swig
+++ b/lldb/bindings/interfaces.swig
@@ -47,6 +47,7 @@
 %include "./interface/SBLaunchInfoDocstrings.i"
 %include "./interface/SBLineEntryDocstrings.i"
 %include "./interface/SBListenerDocstrings.i"
+%include "./interface/SBLockExtensions.i"
 %include "./interface/SBMemoryRegionInfoDocstrings.i"
 %include "./interface/SBMemoryRegionInfoListDocstrings.i"
 %include "./interface/SBModuleDocstrings.i"
@@ -121,11 +122,12 @@
 %include "lldb/API/SBHostOS.h"
 %include "lldb/API/SBInstruction.h"
 %include "lldb/API/SBInstructionList.h"
-%include "lldb/API/SBLanguages.h"
 %include "lldb/API/SBLanguageRuntime.h"
+%include "lldb/API/SBLanguages.h"
 %include "lldb/API/SBLaunchInfo.h"
 %include "lldb/API/SBLineEntry.h"
 %include "lldb/API/SBListener.h"
+%include "lldb/API/SBLock.h"
 %include "lldb/API/SBMemoryRegionInfo.h"
 %include "lldb/API/SBMemoryRegionInfoList.h"
 %include "lldb/API/SBModule.h"
diff --git a/lldb/include/lldb/API/LLDB.h b/lldb/include/lldb/API/LLDB.h
index 126fcef31b416..47474d9ead1bf 100644
--- a/lldb/include/lldb/API/LLDB.h
+++ b/lldb/include/lldb/API/LLDB.h
@@ -46,6 +46,7 @@
 #include "lldb/API/SBLaunchInfo.h"
 #include "lldb/API/SBLineEntry.h"
 #include "lldb/API/SBListener.h"
+#include "lldb/API/SBLock.h"
 #include "lldb/API/SBMemoryRegionInfo.h"
 #include "lldb/API/SBMemoryRegionInfoList.h"
 #include "lldb/API/SBModule.h"
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..93503d051edaa
--- /dev/null
+++ b/lldb/include/lldb/API/SBLock.h
@@ -0,0 +1,46 @@
+//===-- 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 {
+class APILock;
+}
+
+namespace lldb {
+
+class LLDB_API SBLock {
+public:
+  SBLock();
+  SBLock(SBLock &&rhs);
+  SBLock &operator=(SBLock &&rhs);
+  ~SBLock();
+
+  bool IsValid() const;
+
+  void Unlock() const;
+
+private:
+  // Private constructor used by SBTarget to create the Target API lock.
+  // Requires a friend declaration.
+  SBLock(lldb::TargetSP target_sp);
+  friend class SBTarget;
+
+  SBLock(const SBLock &rhs) = delete;
+  const SBLock &operator=(const SBLock &rhs) = delete;
+
+  std::unique_ptr<lldb_private::APILock> m_opaque_up;
+};
+#endif
+
+} // namespace lldb
diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h
index bb912ab41d0fe..4c3850bad8df9 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,8 @@ class LLDB_API SBTarget {
   ///     An error if a Trace already exists or the trace couldn't be created.
   lldb::SBTrace CreateTrace(SBError &error);
 
+  lldb::SBLock AcquireAPILock() const;
+
 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..bca57d8bd63e5 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,21 @@ class Target : public std::enable_shared_from_this<Target>,
   }
 };
 
+/// The private implementation backing SBLock.
+class APILock {
+public:
+  APILock(std::shared_ptr<std::recursive_mutex> mutex_sp)
+      : m_mutex(std::move(mutex_sp)), m_lock(*m_mutex) {}
+
+  void Unlock() { m_lock.unlock(); }
+
+  operator bool() const { return static_cast<bool>(m_lock); }
+
+private:
+  std::shared_ptr<std::recursive_mutex> m_mutex;
+  std::unique_lock<std::recursive_mutex> m_lock;
+};
+
 } // 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..598fd26720763
--- /dev/null
+++ b/lldb/source/API/SBLock.cpp
@@ -0,0 +1,52 @@
+//===-- 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;
+
+SBLock::SBLock() { LLDB_INSTRUMENT_VA(this); }
+
+SBLock::SBLock(SBLock &&rhs) : m_opaque_up(std::move(rhs.m_opaque_up)) {
+  LLDB_INSTRUMENT_VA(this);
+}
+
+SBLock &SBLock::operator=(SBLock &&rhs) {
+  LLDB_INSTRUMENT_VA(this);
+
+  m_opaque_up = std::move(rhs.m_opaque_up);
+  return *this;
+}
+
+SBLock::SBLock(lldb::TargetSP target_sp)
+    : m_opaque_up(
+          std::make_unique<APILock>(std::shared_ptr<std::recursive_mutex>(
+              target_sp, &target_sp->GetAPIMutex()))) {
+  LLDB_INSTRUMENT_VA(this, target_sp);
+}
+
+SBLock::~SBLock() { LLDB_INSTRUMENT_VA(this); }
+
+bool SBLock::IsValid() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  return static_cast<bool>(m_opaque_up) && static_cast<bool>(*m_opaque_up);
+}
+
+void SBLock::Unlock() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  if (m_opaque_up)
+    m_opaque_up->Unlock();
+}
diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp
index dd9caa724ea36..b41cc8911b362 100644
--- a/lldb/source/API/SBTarget.cpp
+++ b/lldb/source/API/SBTarget.cpp
@@ -7,10 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/API/SBTarget.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"
@@ -18,6 +14,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"
@@ -58,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"
@@ -544,9 +544,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 +1039,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 +1059,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 +1686,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 +2187,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 +2433,11 @@ lldb::SBTrace SBTarget::CreateTrace(lldb::SBError &error) {
   }
   return SBTrace();
 }
+
+lldb::SBLock SBTarget::AcquireAPILock() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  if (TargetSP target_sp = GetSP())
+    return lldb::SBLock(target_sp);
+  return lldb::SBLock();
+}
diff --git a/lldb/test/API/python_api/target/TestTargetAPI.py b/lldb/test/API/python_api/target/TestTargetAPI.py
index 155a25b576b03..b529c6381fc61 100644
--- a/lldb/test/API/python_api/target/TestTargetAPI.py
+++ b/lldb/test/API/python_api/target/TestTargetAPI.py
@@ -537,3 +537,31 @@ def test_setting_selected_target_with_invalid_target(self):
         """Make sure we don't crash when trying to select invalid target."""
         target = lldb.SBTarget()
         self.dbg.SetSelectedTarget(target)
+
+    @no_debug_info_test
+    def test_acquire_sblock(self):
+        """Make sure we can acquire the API lock from Python."""
+        target = self.dbg.GetDummyTarget()
+
+        lock = target.AcquireAPILock()
+        self.assertTrue(lock.IsValid())
+        # The API call below doesn't actually matter, it's just there to
+        # confirm we don't block on the API lock.
+        target.BreakpointCreateByName("foo", "bar")
+        lock.Unlock()
+        self.assertFalse(lock.IsValid())
+
+    @no_debug_info_test
+    def test_acquire_sblock_with_statement(self):
+        """Make sure we can acquire the API lock using a with-statement from Python."""
+        target = self.dbg.GetDummyTarget()
+
+        lock_copy = None
+        with target.AcquireAPILock() as lock:
+            self.assertTrue(lock.IsValid())
+            # The API call below doesn't actually matter, it's just there to
+            # confirm we don't block on the API lock.
+            target.BreakpointCreateByName("foo", "bar")
+            lock_copy = lock
+            self.assertTrue(lock.IsValid())
+        self.assertFalse(lock_copy.IsValid())
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..57705c2cb722c
--- /dev/null
+++ b/lldb/unittests/API/SBLockTest.cpp
@@ -0,0 +1,57 @@
+//===-- 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
+//
+//===----------------------------------------------------------------------===/
+
+// Use the umbrella header for -Wdocumentation.
+#include "lldb/API/LLDB.h"
+
+#include "TestingSupport/SubsystemRAII.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBTarget.h"
+#include "gtest/gtest.h"
+#include <atomic>
+#include <chrono>
+#include <future>
+
+using namespace lldb;
+using namespace lldb_private;
+
+class SBLockTest : public testing::Test {
+protected:
+  void SetUp() override { debugger = SBDebugger::Create(); }
+  void TearDown() override { SBDebugger::Destroy(debugger); }
+
+  SubsystemRAII<lldb::SBDebugger> subsystems;
+  SBDebugger debugger;
+};
+
+TEST_F(SBLockTest, LockTest) {
+  lldb::SBTarget target = debugger.GetDummyTarget();
+
+  std::future<void> f;
+  {
+    std::atomic<bool> locked = false;
+    lldb::SBLock lock = target.AcquireAPILock();
+    ASSERT_FALSE(locked.exchange(true));
+
+    f = std::async(std::launch::async, [&]() {
+      {
+        ASSERT_TRUE(locked);
+        target.BreakpointCreateByName("foo", "bar");
+        ASSERT_FALSE(locked);
+      }
+    });
+    ASSERT_TRUE(f.valid());
+
+    // Wait 500ms to confirm the thread is blocked.
+    auto status = f.wait_for(std::chrono::milliseconds(500));
+    ASSERT_EQ(status, std::future_status::timeout);
+
+    ASSERT_TRUE(locked.exchange(false));
+  }
+  f.wait();
+}



More information about the lldb-commits mailing list