[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