[Lldb-commits] [lldb] [lldb] Fix self-deadlock from frame identity aliasing in scripted frame providers (PR #208992)
Med Ismail Bennani via lldb-commits
lldb-commits at lists.llvm.org
Fri Aug 14 11:55:33 PDT 2026
https://github.com/medismailben updated https://github.com/llvm/llvm-project/pull/208992
>From 19a70ccc7404e835b26eac762b8c2a2e266fc5c3 Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <ismail at bennani.ma>
Date: Mon, 10 Aug 2026 15:07:10 -0700
Subject: [PATCH 1/3] [lldb] Wrap Target::GetAPIMutex() into a Lockable handle
(NFC)
While implementing #208242, we realized that we needed a Lockable
TargetAPIMutex wrapper that could skip the locking on re-entrant
threads (when a command (i.e `bt`) triggers scripted extension (i.e
`ScriptedFrameProvider`) that uses SBAPI (i.e. `SBFrame`), and still
behave as a normal mutex otherwise. However, since
`Target::GetAPIMutex()` returns a `std::recursive_mutex&`, that can't
represent "no synchronization at all".
This is why this PR introduces `TargetAPIMutex`, a small Lockable type
that behaves exactly like `std::recursive_mutex`, with no RAII of its
own, so callers can wrap it in `std::lock_guard`/`std::unique_lock`
like they would any other Lockable.
A `TargetAPIMutex` is bound to a `Target` rather than to a specific
mutex, so `lock()`/`try_lock()` resolve which real mutex to use fresh
on every call instead of caching one resolution for the handle's
lifetime. `unlock()` replays whatever the matching `lock()`/`try_lock()`
resolved rather than re-resolving, so a policy change between the two
calls can't release the wrong mutex. That's also what lets a handle be
constructed on one thread and locked/unlocked on another, which the
deferred-lock `SBMutex` needs.
`GetAPIMutex()` now returns `TargetAPIMutex` by value, so every caller
that took its old `std::recursive_mutex&` materializes the handle into
a local and wraps that in a `lock_guard`/`unique_lock` instead. A few
places where the lock outlives its constructing statement
(`CommandObject`'s `m_api_locker`, `ValueLocker`,
`StoppedExecutionContext`) keep the resolved handle as a member and
release it explicitly instead.
`SBMutex` hides its opaque member behind a forward-declared
`MutexVariant`, defined in the .cpp as a
`std::variant<std::recursive_mutex, TargetAPIMutex>`: the no-target
case still owns a real mutex directly, `SBMutex`'s own size doesn't
grow across the ABI boundary, and `std::variant` -- a C++17 feature --
never leaks into the public header, which must stay usable from a
C++11 client.
Signed-off-by: Med Ismail Bennani <ismail at bennani.ma>
---
lldb/include/lldb/API/SBMutex.h | 5 +-
lldb/include/lldb/Interpreter/CommandObject.h | 3 +-
lldb/include/lldb/Target/ExecutionContext.h | 18 +-
lldb/include/lldb/Target/Target.h | 9 +-
lldb/include/lldb/Target/TargetAPIMutex.h | 78 +++++++
lldb/include/lldb/ValueObject/ValueObject.h | 8 +-
lldb/source/API/SBAddress.cpp | 3 +-
lldb/source/API/SBBreakpoint.cpp | 148 ++++++-------
lldb/source/API/SBBreakpointLocation.cpp | 104 +++++-----
lldb/source/API/SBBreakpointName.cpp | 98 +++++----
lldb/source/API/SBCommandInterpreter.cpp | 30 ++-
lldb/source/API/SBDebugger.cpp | 12 +-
lldb/source/API/SBFunction.cpp | 6 +-
lldb/source/API/SBInstruction.cpp | 24 ++-
lldb/source/API/SBMutex.cpp | 39 +++-
lldb/source/API/SBProcess.cpp | 173 ++++++++--------
lldb/source/API/SBSymbol.cpp | 6 +-
lldb/source/API/SBTarget.cpp | 111 ++++++----
lldb/source/API/SBThread.cpp | 3 +-
lldb/source/API/SBValue.cpp | 10 +-
lldb/source/API/SBWatchpoint.cpp | 59 +++---
lldb/source/Interpreter/CommandObject.cpp | 7 +-
lldb/source/Target/CMakeLists.txt | 1 +
lldb/source/Target/ExecutionContext.cpp | 12 +-
lldb/source/Target/Target.cpp | 9 +-
lldb/source/Target/TargetAPIMutex.cpp | 34 +++
lldb/source/ValueObject/ValueObject.cpp | 10 +-
lldb/tools/lldb-dap/ProtocolUtils.cpp | 1 +
lldb/unittests/Target/CMakeLists.txt | 1 +
lldb/unittests/Target/TargetAPIMutexTest.cpp | 195 ++++++++++++++++++
30 files changed, 815 insertions(+), 402 deletions(-)
create mode 100644 lldb/include/lldb/Target/TargetAPIMutex.h
create mode 100644 lldb/source/Target/TargetAPIMutex.cpp
create mode 100644 lldb/unittests/Target/TargetAPIMutexTest.cpp
diff --git a/lldb/include/lldb/API/SBMutex.h b/lldb/include/lldb/API/SBMutex.h
index 826ad077f159f..856c648cf96ab 100644
--- a/lldb/include/lldb/API/SBMutex.h
+++ b/lldb/include/lldb/API/SBMutex.h
@@ -11,7 +11,7 @@
#include "lldb/API/SBDefines.h"
#include "lldb/lldb-forward.h"
-#include <mutex>
+#include <memory>
namespace lldb {
@@ -41,7 +41,8 @@ class LLDB_API SBMutex {
SBMutex(lldb::TargetSP target_sp);
friend class SBTarget;
- std::shared_ptr<std::recursive_mutex> m_opaque_sp;
+ class MutexVariant;
+ std::shared_ptr<MutexVariant> m_opaque_sp;
};
} // namespace lldb
diff --git a/lldb/include/lldb/Interpreter/CommandObject.h b/lldb/include/lldb/Interpreter/CommandObject.h
index 925377159d749..2b08db93c1f9c 100644
--- a/lldb/include/lldb/Interpreter/CommandObject.h
+++ b/lldb/include/lldb/Interpreter/CommandObject.h
@@ -410,7 +410,8 @@ class CommandObject : public std::enable_shared_from_this<CommandObject> {
CommandInterpreter &m_interpreter;
ExecutionContext m_exe_ctx;
- std::unique_lock<std::recursive_mutex> m_api_locker;
+ TargetAPIMutex m_api_mutex;
+ std::unique_lock<TargetAPIMutex> m_api_locker;
std::string m_cmd_name;
std::string m_cmd_help_short;
std::string m_cmd_help_long;
diff --git a/lldb/include/lldb/Target/ExecutionContext.h b/lldb/include/lldb/Target/ExecutionContext.h
index bf976f4db8c87..8288bf10ee9e0 100644
--- a/lldb/include/lldb/Target/ExecutionContext.h
+++ b/lldb/include/lldb/Target/ExecutionContext.h
@@ -14,6 +14,7 @@
#include "lldb/Host/ProcessRunLock.h"
#include "lldb/Target/StackID.h"
#include "lldb/Target/SyntheticFrameProvider.h"
+#include "lldb/Target/TargetAPIMutex.h"
#include "lldb/lldb-private.h"
namespace lldb_private {
@@ -569,12 +570,13 @@ struct StoppedExecutionContext : ExecutionContext {
lldb::ProcessSP &process_sp,
lldb::ThreadSP &thread_sp,
lldb::StackFrameSP &frame_sp,
- std::unique_lock<std::recursive_mutex> api_lock,
+ TargetAPIMutex api_mutex,
ProcessRunLock::ProcessRunLocker stop_locker)
- : m_api_lock(std::move(api_lock)), m_stop_locker(std::move(stop_locker)) {
+ : m_api_mutex(std::move(api_mutex)),
+ m_api_locker(m_api_mutex, std::adopt_lock),
+ m_stop_locker(std::move(stop_locker)) {
assert(target_sp);
assert(process_sp);
- assert(m_api_lock.owns_lock());
assert(m_stop_locker.IsLocked());
SetTargetSP(target_sp);
SetProcessSP(process_sp);
@@ -587,18 +589,22 @@ struct StoppedExecutionContext : ExecutionContext {
StoppedExecutionContext(StoppedExecutionContext &&other)
: StoppedExecutionContext(other.m_target_sp, other.m_process_sp,
other.m_thread_sp, other.m_frame_sp,
- std::move(other.m_api_lock),
+ std::move(other.m_api_mutex),
std::move(other.m_stop_locker)) {
+ // Ownership already transferred above; don't let `other`'s destructor
+ // unlock the mutex `this` now owns.
+ other.m_api_locker.release();
other.Clear();
}
/// Clears this context, unlocking the ProcessRunLock and returning the
/// locked API lock, allowing callers to resume the process. Similar to
/// a move operation, this object is no longer usable.
- [[nodiscard]] std::unique_lock<std::recursive_mutex> AllowResume();
+ [[nodiscard]] TargetAPIMutex AllowResume();
private:
- std::unique_lock<std::recursive_mutex> m_api_lock;
+ TargetAPIMutex m_api_mutex;
+ std::unique_lock<TargetAPIMutex> m_api_locker;
ProcessRunLock::ProcessRunLocker m_stop_locker;
};
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index a5247376cbdf5..71ba92e624404 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -12,6 +12,7 @@
#include <list>
#include <map>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -33,6 +34,7 @@
#include "lldb/Target/SectionLoadHistory.h"
#include "lldb/Target/Statistics.h"
#include "lldb/Target/SyntheticFrameProvider.h"
+#include "lldb/Target/TargetAPIMutex.h"
#include "lldb/Target/ThreadSpec.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/Broadcaster.h"
@@ -585,6 +587,7 @@ class Target : public std::enable_shared_from_this<Target>,
public:
friend class TargetList;
friend class Debugger;
+ friend class TargetAPIMutex;
/// Broadcaster event bits definitions.
enum {
@@ -765,7 +768,11 @@ class Target : public std::enable_shared_from_this<Target>,
static TargetProperties &GetGlobalProperties();
- std::recursive_mutex &GetAPIMutex();
+ /// Returns a handle resolved to the mutex to serialize on before
+ /// touching the target through the SB API. The handle isn't locked yet;
+ /// lock()/try_lock() it (typically via std::lock_guard<TargetAPIMutex>/
+ /// std::unique_lock<TargetAPIMutex>) to actually acquire it.
+ TargetAPIMutex GetAPIMutex();
void DeleteCurrentProcess();
diff --git a/lldb/include/lldb/Target/TargetAPIMutex.h b/lldb/include/lldb/Target/TargetAPIMutex.h
new file mode 100644
index 0000000000000..dc5a2ee9867c1
--- /dev/null
+++ b/lldb/include/lldb/Target/TargetAPIMutex.h
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_TARGET_TARGETAPIMUTEX_H
+#define LLDB_TARGET_TARGETAPIMUTEX_H
+
+#include "lldb/lldb-forward.h"
+#include <mutex>
+
+namespace lldb_private {
+
+/// A Lockable handle over a Target's API mutex, returned by
+/// Target::GetAPIMutex() and backing the public lldb::SBMutex.
+///
+/// Behaves like std::recursive_mutex: lock()/try_lock()/unlock() drive
+/// the actual synchronization, with the same contract (unlock() without
+/// a matching successful lock()/try_lock() is caller error). It carries
+/// no RAII of its own; wrap it in std::lock_guard<TargetAPIMutex> or
+/// std::unique_lock<TargetAPIMutex> for scope-based locking, exactly as
+/// with any other Lockable.
+///
+/// A handle may be constructed on one thread and then locked/unlocked
+/// on a different one, so lock()/try_lock() (re-)resolve which real
+/// mutex to use fresh on every call, rather than caching a single
+/// resolution for the handle's lifetime. The matching unlock() replays
+/// the exact resolution that call produced, rather than re-resolving,
+/// so the calling thread's policy at unlock() time can't cause it to
+/// release the wrong mutex (or fail to release the one it actually
+/// holds).
+///
+/// Default-constructed (or moved-from) handles are a genuine no-op: no
+/// synchronization primitive is touched at all.
+class TargetAPIMutex {
+public:
+ TargetAPIMutex() = default;
+ explicit TargetAPIMutex(lldb::TargetSP target_sp)
+ : m_target_sp(std::move(target_sp)) {}
+
+ TargetAPIMutex(TargetAPIMutex &&other) noexcept
+ : m_mutex(other.m_mutex), m_target_sp(std::move(other.m_target_sp)) {
+ other.m_mutex = nullptr;
+ }
+ TargetAPIMutex &operator=(TargetAPIMutex &&other) noexcept {
+ if (this != &other) {
+ m_mutex = other.m_mutex;
+ m_target_sp = std::move(other.m_target_sp);
+ other.m_mutex = nullptr;
+ }
+ return *this;
+ }
+
+ TargetAPIMutex(const TargetAPIMutex &) = delete;
+ TargetAPIMutex &operator=(const TargetAPIMutex &) = delete;
+
+ void lock();
+ bool try_lock();
+ void unlock() {
+ if (m_mutex)
+ m_mutex->unlock();
+ }
+
+private:
+ /// A pointer into m_target_sp's own mutex, resolved fresh on every
+ /// lock()/try_lock() call. Null when this handle is a genuine no-op.
+ /// Safe to leave dangling-free without m_target_sp: it's only ever set
+ /// while m_target_sp is held, so the pointee outlives the pointer.
+ std::recursive_mutex *m_mutex = nullptr;
+ lldb::TargetSP m_target_sp;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_TARGETAPIMUTEX_H
diff --git a/lldb/include/lldb/ValueObject/ValueObject.h b/lldb/include/lldb/ValueObject/ValueObject.h
index 6032e56ee5c6a..c370993d372b7 100644
--- a/lldb/include/lldb/ValueObject/ValueObject.h
+++ b/lldb/include/lldb/ValueObject/ValueObject.h
@@ -1290,7 +1290,8 @@ class ValueImpl {
lldb::ValueObjectSP GetRootSP() { return m_valobj_sp; }
lldb::ValueObjectSP GetSP(Process::StopLocker &stop_locker,
- std::unique_lock<std::recursive_mutex> &lock,
+ TargetAPIMutex &api_mutex,
+ std::unique_lock<TargetAPIMutex> &lock,
Status &error);
void SetUseDynamic(lldb::DynamicValueType use_dynamic) {
@@ -1335,14 +1336,15 @@ class ValueLocker {
ValueLocker() = default;
lldb::ValueObjectSP GetLockedSP(ValueImpl &in_value) {
- return in_value.GetSP(m_stop_locker, m_lock, m_lock_error);
+ return in_value.GetSP(m_stop_locker, m_api_mutex, m_lock, m_lock_error);
}
Status &GetError() { return m_lock_error; }
private:
Process::StopLocker m_stop_locker;
- std::unique_lock<std::recursive_mutex> m_lock;
+ TargetAPIMutex m_api_mutex;
+ std::unique_lock<TargetAPIMutex> m_lock;
Status m_lock_error;
};
diff --git a/lldb/source/API/SBAddress.cpp b/lldb/source/API/SBAddress.cpp
index 78acc2e34564d..5015b6d81d732 100644
--- a/lldb/source/API/SBAddress.cpp
+++ b/lldb/source/API/SBAddress.cpp
@@ -110,7 +110,8 @@ lldb::addr_t SBAddress::GetLoadAddress(const SBTarget &target) const {
TargetSP target_sp(target.GetSP());
if (target_sp) {
if (m_opaque_up->IsValid()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
addr = m_opaque_up->GetLoadAddress(target_sp.get());
}
}
diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp
index bcef2bd366f73..75d54cabfed22 100644
--- a/lldb/source/API/SBBreakpoint.cpp
+++ b/lldb/source/API/SBBreakpoint.cpp
@@ -119,8 +119,8 @@ void SBBreakpoint::ClearAllBreakpointSites() {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bkpt_sp->ClearAllBreakpointSites();
}
}
@@ -133,8 +133,8 @@ SBBreakpointLocation SBBreakpoint::FindLocationByAddress(addr_t vm_addr) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
if (vm_addr != LLDB_INVALID_ADDRESS) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
Address address;
Target &target = bkpt_sp->GetTarget();
if (!target.ResolveLoadAddress(vm_addr, address)) {
@@ -153,8 +153,8 @@ break_id_t SBBreakpoint::FindLocationIDByAddress(addr_t vm_addr) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp && vm_addr != LLDB_INVALID_ADDRESS) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
Address address;
Target &target = bkpt_sp->GetTarget();
if (!target.ResolveLoadAddress(vm_addr, address)) {
@@ -173,8 +173,8 @@ SBBreakpointLocation SBBreakpoint::FindLocationByID(break_id_t bp_loc_id) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
sb_bp_location.SetLocation(bkpt_sp->FindLocationByID(bp_loc_id));
}
@@ -188,8 +188,8 @@ SBBreakpointLocation SBBreakpoint::GetLocationAtIndex(uint32_t index) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
sb_bp_location.SetLocation(bkpt_sp->GetLocationAtIndex(index));
}
@@ -202,8 +202,8 @@ void SBBreakpoint::SetEnabled(bool enable) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bkpt_sp->SetEnabled(enable);
}
}
@@ -213,8 +213,8 @@ bool SBBreakpoint::IsEnabled() {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return bkpt_sp->IsEnabled();
} else
return false;
@@ -226,8 +226,8 @@ void SBBreakpoint::SetOneShot(bool one_shot) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bkpt_sp->SetOneShot(one_shot);
}
}
@@ -237,8 +237,8 @@ bool SBBreakpoint::IsOneShot() const {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return bkpt_sp->IsOneShot();
} else
return false;
@@ -249,8 +249,8 @@ bool SBBreakpoint::IsInternal() {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return bkpt_sp->IsInternal();
} else
return false;
@@ -262,8 +262,8 @@ void SBBreakpoint::SetIgnoreCount(uint32_t count) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bkpt_sp->SetIgnoreCount(count);
}
}
@@ -273,8 +273,8 @@ void SBBreakpoint::SetCondition(const char *condition) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
// Treat a null pointer as resetting the condition.
if (!condition)
bkpt_sp->SetCondition(StopCondition());
@@ -290,8 +290,8 @@ const char *SBBreakpoint::GetCondition() {
if (!bkpt_sp)
return nullptr;
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
StopCondition cond = bkpt_sp->GetCondition();
if (!cond)
return nullptr;
@@ -303,8 +303,8 @@ void SBBreakpoint::SetAutoContinue(bool auto_continue) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bkpt_sp->SetAutoContinue(auto_continue);
}
}
@@ -314,8 +314,8 @@ bool SBBreakpoint::GetAutoContinue() {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return bkpt_sp->IsAutoContinue();
}
return false;
@@ -327,8 +327,8 @@ uint32_t SBBreakpoint::GetHitCount() const {
uint32_t count = 0;
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
count = bkpt_sp->GetHitCount();
}
@@ -341,8 +341,8 @@ uint32_t SBBreakpoint::GetIgnoreCount() const {
uint32_t count = 0;
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
count = bkpt_sp->GetIgnoreCount();
}
@@ -354,8 +354,8 @@ void SBBreakpoint::SetThreadID(lldb::tid_t tid) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bkpt_sp->SetThreadID(tid);
}
}
@@ -366,8 +366,8 @@ lldb::tid_t SBBreakpoint::GetThreadID() {
lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
tid = bkpt_sp->GetThreadID();
}
@@ -379,8 +379,8 @@ void SBBreakpoint::SetThreadIndex(uint32_t index) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bkpt_sp->GetOptions().GetThreadSpec()->SetIndex(index);
}
}
@@ -391,8 +391,8 @@ uint32_t SBBreakpoint::GetThreadIndex() const {
uint32_t thread_idx = UINT32_MAX;
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
const ThreadSpec *thread_spec =
bkpt_sp->GetOptions().GetThreadSpecNoCreate();
if (thread_spec != nullptr)
@@ -408,8 +408,8 @@ void SBBreakpoint::SetThreadName(const char *thread_name) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bkpt_sp->GetOptions().GetThreadSpec()->SetName(thread_name);
}
}
@@ -421,8 +421,8 @@ const char *SBBreakpoint::GetThreadName() const {
if (!bkpt_sp)
return nullptr;
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (const ThreadSpec *thread_spec =
bkpt_sp->GetOptions().GetThreadSpecNoCreate())
return ConstString(thread_spec->GetName()).GetCString();
@@ -435,8 +435,8 @@ void SBBreakpoint::SetQueueName(const char *queue_name) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bkpt_sp->GetOptions().GetThreadSpec()->SetQueueName(queue_name);
}
}
@@ -448,8 +448,8 @@ const char *SBBreakpoint::GetQueueName() const {
if (!bkpt_sp)
return nullptr;
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (const ThreadSpec *thread_spec =
bkpt_sp->GetOptions().GetThreadSpecNoCreate())
return ConstString(thread_spec->GetQueueName()).GetCString();
@@ -463,8 +463,8 @@ size_t SBBreakpoint::GetNumResolvedLocations() const {
size_t num_resolved = 0;
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
num_resolved = bkpt_sp->GetNumResolvedLocations();
}
return num_resolved;
@@ -476,8 +476,8 @@ size_t SBBreakpoint::GetNumLocations() const {
BreakpointSP bkpt_sp = GetSP();
size_t num_locs = 0;
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
num_locs = bkpt_sp->GetNumLocations();
}
return num_locs;
@@ -492,8 +492,8 @@ void SBBreakpoint::SetCommandLineCommands(SBStringList &commands) {
if (commands.GetSize() == 0)
return;
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
std::unique_ptr<BreakpointOptions::CommandData> cmd_data_up(
new BreakpointOptions::CommandData(*commands, eScriptLanguageNone));
@@ -525,8 +525,8 @@ bool SBBreakpoint::GetDescription(SBStream &s, bool include_locations) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
s.Printf("SBBreakpoint: id = %i, ", bkpt_sp->GetID());
bkpt_sp->GetResolverDescription(s.get());
bkpt_sp->GetFilterDescription(s.get());
@@ -603,8 +603,8 @@ void SBBreakpoint::SetCallback(SBBreakpointHitCallback callback, void *baton) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
BatonSP baton_sp(new SBBreakpointCallbackBaton(callback, baton));
bkpt_sp->SetCallback(SBBreakpointCallbackBaton
::PrivateBreakpointHitCallback, baton_sp,
@@ -628,8 +628,8 @@ SBError SBBreakpoint::SetScriptCallbackFunction(
if (bkpt_sp) {
Status error;
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
BreakpointOptions &bp_options = bkpt_sp->GetOptions();
error = bkpt_sp->GetTarget()
.GetDebugger()
@@ -652,8 +652,8 @@ SBError SBBreakpoint::SetScriptCallbackBody(const char *callback_body_text) {
SBError sb_error;
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
BreakpointOptions &bp_options = bkpt_sp->GetOptions();
Status error =
bkpt_sp->GetTarget()
@@ -682,8 +682,8 @@ SBError SBBreakpoint::AddNameWithErrorHandling(const char *new_name) {
SBError status;
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
Status error;
bkpt_sp->GetTarget().AddNameToBreakpoint(bkpt_sp, new_name, error);
status.SetError(std::move(error));
@@ -700,8 +700,8 @@ void SBBreakpoint::RemoveName(const char *name_to_remove) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bkpt_sp->GetTarget().RemoveNameFromBreakpoint(
bkpt_sp, llvm::StringRef(name_to_remove));
}
@@ -713,8 +713,8 @@ bool SBBreakpoint::MatchesName(const char *name) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return bkpt_sp->MatchesName(name);
}
@@ -727,8 +727,8 @@ void SBBreakpoint::GetNames(SBStringList &names) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
std::vector<std::string> names_vec;
bkpt_sp->GetNames(names_vec);
for (const std::string &name : names_vec) {
@@ -802,8 +802,8 @@ lldb::SBError SBBreakpoint::SetIsHardware(bool is_hardware) {
BreakpointSP bkpt_sp = GetSP();
if (bkpt_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- bkpt_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return SBError(Status::FromError(bkpt_sp->SetIsHardware(is_hardware)));
}
return SBError();
diff --git a/lldb/source/API/SBBreakpointLocation.cpp b/lldb/source/API/SBBreakpointLocation.cpp
index 2feaa5c805a15..729721943ebdd 100644
--- a/lldb/source/API/SBBreakpointLocation.cpp
+++ b/lldb/source/API/SBBreakpointLocation.cpp
@@ -87,8 +87,8 @@ addr_t SBBreakpointLocation::GetLoadAddress() {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
ret_addr = loc_sp->GetLoadAddress();
}
@@ -100,8 +100,8 @@ void SBBreakpointLocation::SetEnabled(bool enabled) {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
llvm::consumeError(loc_sp->SetEnabled(enabled));
}
}
@@ -111,8 +111,8 @@ bool SBBreakpointLocation::IsEnabled() {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return loc_sp->IsEnabled();
} else
return false;
@@ -123,8 +123,8 @@ uint32_t SBBreakpointLocation::GetHitCount() {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return loc_sp->GetHitCount();
} else
return 0;
@@ -135,8 +135,8 @@ uint32_t SBBreakpointLocation::GetIgnoreCount() {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return loc_sp->GetIgnoreCount();
} else
return 0;
@@ -147,8 +147,8 @@ void SBBreakpointLocation::SetIgnoreCount(uint32_t n) {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
loc_sp->SetIgnoreCount(n);
}
}
@@ -158,8 +158,8 @@ void SBBreakpointLocation::SetCondition(const char *condition) {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
// Treat a nullptr as clearing the condition
if (!condition)
loc_sp->SetCondition(StopCondition());
@@ -175,8 +175,8 @@ const char *SBBreakpointLocation::GetCondition() {
if (!loc_sp)
return nullptr;
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
StopCondition cond = loc_sp->GetCondition();
if (!cond)
return nullptr;
@@ -188,8 +188,8 @@ void SBBreakpointLocation::SetAutoContinue(bool auto_continue) {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
loc_sp->SetAutoContinue(auto_continue);
}
}
@@ -199,8 +199,8 @@ bool SBBreakpointLocation::GetAutoContinue() {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return loc_sp->IsAutoContinue();
}
return false;
@@ -213,8 +213,8 @@ void SBBreakpointLocation::SetCallback(SBBreakpointHitCallback callback,
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
BatonSP baton_sp(new SBBreakpointCallbackBaton(callback, baton));
loc_sp->SetCallback(SBBreakpointCallbackBaton::PrivateBreakpointHitCallback,
baton_sp, false);
@@ -235,8 +235,8 @@ SBError SBBreakpointLocation::SetScriptCallbackFunction(
if (loc_sp) {
Status error;
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
BreakpointOptions &bp_options = loc_sp->GetLocationOptions();
error = loc_sp->GetBreakpoint()
.GetTarget()
@@ -261,8 +261,8 @@ SBBreakpointLocation::SetScriptCallbackBody(const char *callback_body_text) {
SBError sb_error;
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
BreakpointOptions &bp_options = loc_sp->GetLocationOptions();
Status error =
loc_sp->GetBreakpoint()
@@ -287,8 +287,8 @@ void SBBreakpointLocation::SetCommandLineCommands(SBStringList &commands) {
if (commands.GetSize() == 0)
return;
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
std::unique_ptr<BreakpointOptions::CommandData> cmd_data_up(
new BreakpointOptions::CommandData(*commands, eScriptLanguageNone));
@@ -314,8 +314,8 @@ void SBBreakpointLocation::SetThreadID(lldb::tid_t thread_id) {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
loc_sp->SetThreadID(thread_id);
}
}
@@ -326,8 +326,8 @@ lldb::tid_t SBBreakpointLocation::GetThreadID() {
lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return loc_sp->GetThreadID();
}
return tid;
@@ -338,8 +338,8 @@ void SBBreakpointLocation::SetThreadIndex(uint32_t index) {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
loc_sp->SetThreadIndex(index);
}
}
@@ -350,8 +350,8 @@ uint32_t SBBreakpointLocation::GetThreadIndex() const {
uint32_t thread_idx = UINT32_MAX;
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return loc_sp->GetThreadIndex();
}
return thread_idx;
@@ -362,8 +362,8 @@ void SBBreakpointLocation::SetThreadName(const char *thread_name) {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
loc_sp->SetThreadName(thread_name);
}
}
@@ -375,8 +375,8 @@ const char *SBBreakpointLocation::GetThreadName() const {
if (!loc_sp)
return nullptr;
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return ConstString(loc_sp->GetThreadName()).GetCString();
}
@@ -385,8 +385,8 @@ void SBBreakpointLocation::SetQueueName(const char *queue_name) {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
loc_sp->SetQueueName(queue_name);
}
}
@@ -398,8 +398,8 @@ const char *SBBreakpointLocation::GetQueueName() const {
if (!loc_sp)
return nullptr;
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return ConstString(loc_sp->GetQueueName()).GetCString();
}
@@ -408,8 +408,8 @@ bool SBBreakpointLocation::IsResolved() {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return loc_sp->IsResolved();
}
return false;
@@ -429,8 +429,8 @@ bool SBBreakpointLocation::GetDescription(SBStream &description,
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
loc_sp->GetDescription(&strm, level);
strm.EOL();
} else
@@ -444,8 +444,8 @@ break_id_t SBBreakpointLocation::GetID() {
BreakpointLocationSP loc_sp = GetSP();
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return loc_sp->GetID();
} else
return LLDB_INVALID_BREAK_ID;
@@ -458,8 +458,8 @@ SBBreakpoint SBBreakpointLocation::GetBreakpoint() {
SBBreakpoint sb_bp;
if (loc_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- loc_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = loc_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
sb_bp = loc_sp->GetBreakpoint().shared_from_this();
}
diff --git a/lldb/source/API/SBBreakpointName.cpp b/lldb/source/API/SBBreakpointName.cpp
index 1dcbecaf6da76..f57a0918743ea 100644
--- a/lldb/source/API/SBBreakpointName.cpp
+++ b/lldb/source/API/SBBreakpointName.cpp
@@ -209,8 +209,8 @@ void SBBreakpointName::SetEnabled(bool enable) {
if (!bp_name)
return;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bp_name->GetOptions().SetEnabled(enable);
UpdateName(*bp_name);
@@ -234,8 +234,8 @@ bool SBBreakpointName::IsEnabled() {
if (!bp_name)
return false;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return bp_name->GetOptions().IsEnabled();
}
@@ -247,8 +247,8 @@ void SBBreakpointName::SetOneShot(bool one_shot) {
if (!bp_name)
return;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bp_name->GetOptions().SetOneShot(one_shot);
UpdateName(*bp_name);
@@ -261,8 +261,8 @@ bool SBBreakpointName::IsOneShot() const {
if (!bp_name)
return false;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return bp_name->GetOptions().IsOneShot();
}
@@ -274,8 +274,8 @@ void SBBreakpointName::SetIgnoreCount(uint32_t count) {
if (!bp_name)
return;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bp_name->GetOptions().SetIgnoreCount(count);
UpdateName(*bp_name);
@@ -288,8 +288,8 @@ uint32_t SBBreakpointName::GetIgnoreCount() const {
if (!bp_name)
return false;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return bp_name->GetOptions().GetIgnoreCount();
}
@@ -301,8 +301,8 @@ void SBBreakpointName::SetCondition(const char *condition) {
if (!bp_name)
return;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bp_name->GetOptions().SetCondition(StopCondition(condition));
UpdateName(*bp_name);
@@ -315,8 +315,8 @@ const char *SBBreakpointName::GetCondition() {
if (!bp_name)
return nullptr;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return ConstString(bp_name->GetOptions().GetCondition().GetText())
.GetCString();
@@ -329,8 +329,8 @@ void SBBreakpointName::SetAutoContinue(bool auto_continue) {
if (!bp_name)
return;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bp_name->GetOptions().SetAutoContinue(auto_continue);
UpdateName(*bp_name);
@@ -343,8 +343,8 @@ bool SBBreakpointName::GetAutoContinue() {
if (!bp_name)
return false;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return bp_name->GetOptions().IsAutoContinue();
}
@@ -356,8 +356,8 @@ void SBBreakpointName::SetThreadID(lldb::tid_t tid) {
if (!bp_name)
return;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bp_name->GetOptions().SetThreadID(tid);
UpdateName(*bp_name);
@@ -370,8 +370,8 @@ lldb::tid_t SBBreakpointName::GetThreadID() {
if (!bp_name)
return LLDB_INVALID_THREAD_ID;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return bp_name->GetOptions().GetThreadSpec()->GetTID();
}
@@ -383,8 +383,8 @@ void SBBreakpointName::SetThreadIndex(uint32_t index) {
if (!bp_name)
return;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bp_name->GetOptions().GetThreadSpec()->SetIndex(index);
UpdateName(*bp_name);
@@ -397,8 +397,8 @@ uint32_t SBBreakpointName::GetThreadIndex() const {
if (!bp_name)
return LLDB_INVALID_THREAD_ID;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return bp_name->GetOptions().GetThreadSpec()->GetIndex();
}
@@ -410,8 +410,8 @@ void SBBreakpointName::SetThreadName(const char *thread_name) {
if (!bp_name)
return;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bp_name->GetOptions().GetThreadSpec()->SetName(thread_name);
UpdateName(*bp_name);
@@ -424,8 +424,8 @@ const char *SBBreakpointName::GetThreadName() const {
if (!bp_name)
return nullptr;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return ConstString(bp_name->GetOptions().GetThreadSpec()->GetName())
.GetCString();
@@ -438,8 +438,8 @@ void SBBreakpointName::SetQueueName(const char *queue_name) {
if (!bp_name)
return;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bp_name->GetOptions().GetThreadSpec()->SetQueueName(queue_name);
UpdateName(*bp_name);
@@ -452,8 +452,8 @@ const char *SBBreakpointName::GetQueueName() const {
if (!bp_name)
return nullptr;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return ConstString(bp_name->GetOptions().GetThreadSpec()->GetQueueName())
.GetCString();
@@ -468,9 +468,8 @@ void SBBreakpointName::SetCommandLineCommands(SBStringList &commands) {
if (commands.GetSize() == 0)
return;
-
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
std::unique_ptr<BreakpointOptions::CommandData> cmd_data_up(
new BreakpointOptions::CommandData(*commands, eScriptLanguageNone));
@@ -510,9 +509,8 @@ void SBBreakpointName::SetHelpString(const char *help_string) {
if (!bp_name)
return;
-
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bp_name->SetHelp(help_string);
}
@@ -526,8 +524,8 @@ bool SBBreakpointName::GetDescription(SBStream &s) {
return false;
}
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bp_name->GetDescription(s.get(), eDescriptionLevelFull);
return true;
}
@@ -539,8 +537,8 @@ void SBBreakpointName::SetCallback(SBBreakpointHitCallback callback,
BreakpointName *bp_name = GetBreakpointName();
if (!bp_name)
return;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
BatonSP baton_sp(new SBBreakpointCallbackBaton(callback, baton));
bp_name->GetOptions().SetCallback(SBBreakpointCallbackBaton
@@ -568,8 +566,8 @@ SBError SBBreakpointName::SetScriptCallbackFunction(
return sb_error;
}
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
BreakpointOptions &bp_options = bp_name->GetOptions();
Status error = m_impl_up->GetTarget()
@@ -592,8 +590,8 @@ SBBreakpointName::SetScriptCallbackBody(const char *callback_body_text) {
if (!bp_name)
return sb_error;
- std::lock_guard<std::recursive_mutex> guard(
- m_impl_up->GetTarget()->GetAPIMutex());
+ TargetAPIMutex api_lock = m_impl_up->GetTarget()->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
BreakpointOptions &bp_options = bp_name->GetOptions();
Status error = m_impl_up->GetTarget()
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp
index ae6b3d3418655..57c6ae375c0ac 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -385,7 +385,8 @@ SBProcess SBCommandInterpreter::GetProcess() {
if (IsValid()) {
TargetSP target_sp(m_opaque_ptr->GetSelectedTarget());
if (target_sp) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
process_sp = target_sp->GetProcessSP();
sb_process.SetSP(process_sp);
}
@@ -472,9 +473,12 @@ void SBCommandInterpreter::SourceInitFileInGlobalDirectory(
result.Clear();
if (IsValid()) {
TargetSP target_sp(m_opaque_ptr->GetSelectedTarget());
- std::unique_lock<std::recursive_mutex> lock;
- if (target_sp)
- lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock;
+ std::unique_lock<TargetAPIMutex> guard;
+ if (target_sp) {
+ api_lock = TargetAPIMutex(target_sp->GetAPIMutex());
+ guard = std::unique_lock<TargetAPIMutex>(api_lock);
+ }
m_opaque_ptr->SourceInitFileGlobal(result.ref());
} else {
result->AppendError("SBCommandInterpreter is not valid");
@@ -495,9 +499,12 @@ void SBCommandInterpreter::SourceInitFileInHomeDirectory(
result.Clear();
if (IsValid()) {
TargetSP target_sp(m_opaque_ptr->GetSelectedTarget());
- std::unique_lock<std::recursive_mutex> lock;
- if (target_sp)
- lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock;
+ std::unique_lock<TargetAPIMutex> guard;
+ if (target_sp) {
+ api_lock = TargetAPIMutex(target_sp->GetAPIMutex());
+ guard = std::unique_lock<TargetAPIMutex>(api_lock);
+ }
m_opaque_ptr->SourceInitFileHome(result.ref(), is_repl);
} else {
result->AppendError("SBCommandInterpreter is not valid");
@@ -511,9 +518,12 @@ void SBCommandInterpreter::SourceInitFileInCurrentWorkingDirectory(
result.Clear();
if (IsValid()) {
TargetSP target_sp(m_opaque_ptr->GetSelectedTarget());
- std::unique_lock<std::recursive_mutex> lock;
- if (target_sp)
- lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock;
+ std::unique_lock<TargetAPIMutex> guard;
+ if (target_sp) {
+ api_lock = TargetAPIMutex(target_sp->GetAPIMutex());
+ guard = std::unique_lock<TargetAPIMutex>(api_lock);
+ }
m_opaque_ptr->SourceInitFileCwd(result.ref());
} else {
result->AppendError("SBCommandInterpreter is not valid");
diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index 95c4f761c9963..71cd0f5841654 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -531,9 +531,12 @@ void SBDebugger::HandleCommand(const char *command) {
if (m_opaque_sp) {
TargetSP target_sp(
m_opaque_sp->GetCommandInterpreter().GetSelectedTarget());
- std::unique_lock<std::recursive_mutex> lock;
- if (target_sp)
- lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock;
+ std::unique_lock<TargetAPIMutex> guard;
+ if (target_sp) {
+ api_lock = TargetAPIMutex(target_sp->GetAPIMutex());
+ guard = std::unique_lock<TargetAPIMutex>(api_lock);
+ }
SBCommandInterpreter sb_interpreter(GetCommandInterpreter());
SBCommandReturnObject result;
@@ -606,7 +609,8 @@ void SBDebugger::HandleProcessEvent(const SBProcess &process,
char stdio_buffer[1024];
size_t len;
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (event_type &
(Process::eBroadcastBitSTDOUT | Process::eBroadcastBitStateChanged)) {
diff --git a/lldb/source/API/SBFunction.cpp b/lldb/source/API/SBFunction.cpp
index c3cdba64417c8..2029069cdb3cb 100644
--- a/lldb/source/API/SBFunction.cpp
+++ b/lldb/source/API/SBFunction.cpp
@@ -129,10 +129,12 @@ SBInstructionList SBFunction::GetInstructions(SBTarget target,
SBInstructionList sb_instructions;
if (m_opaque_ptr) {
TargetSP target_sp(target.GetSP());
- std::unique_lock<std::recursive_mutex> lock;
+ TargetAPIMutex api_lock;
+ std::unique_lock<TargetAPIMutex> guard;
ModuleSP module_sp(m_opaque_ptr->GetAddress().GetModule());
if (target_sp && module_sp) {
- lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ api_lock = TargetAPIMutex(target_sp->GetAPIMutex());
+ guard = std::unique_lock<TargetAPIMutex>(api_lock);
const bool force_live_memory = true;
sb_instructions.SetDisassembler(Disassembler::DisassembleRange(
module_sp->GetArchitecture(), nullptr, flavor,
diff --git a/lldb/source/API/SBInstruction.cpp b/lldb/source/API/SBInstruction.cpp
index dc4d475f5fb3d..ad66ebec3abcf 100644
--- a/lldb/source/API/SBInstruction.cpp
+++ b/lldb/source/API/SBInstruction.cpp
@@ -117,9 +117,11 @@ const char *SBInstruction::GetMnemonic(SBTarget target) {
ExecutionContext exe_ctx;
TargetSP target_sp(target.GetSP());
- std::unique_lock<std::recursive_mutex> lock;
+ TargetAPIMutex api_lock;
+ std::unique_lock<TargetAPIMutex> guard;
if (target_sp) {
- lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ api_lock = TargetAPIMutex(target_sp->GetAPIMutex());
+ guard = std::unique_lock<TargetAPIMutex>(api_lock);
target_sp->CalculateExecutionContext(exe_ctx);
exe_ctx.SetProcessSP(target_sp->GetProcessSP());
@@ -136,9 +138,11 @@ const char *SBInstruction::GetOperands(SBTarget target) {
ExecutionContext exe_ctx;
TargetSP target_sp(target.GetSP());
- std::unique_lock<std::recursive_mutex> lock;
+ TargetAPIMutex api_lock;
+ std::unique_lock<TargetAPIMutex> guard;
if (target_sp) {
- lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ api_lock = TargetAPIMutex(target_sp->GetAPIMutex());
+ guard = std::unique_lock<TargetAPIMutex>(api_lock);
target_sp->CalculateExecutionContext(exe_ctx);
exe_ctx.SetProcessSP(target_sp->GetProcessSP());
@@ -155,9 +159,11 @@ const char *SBInstruction::GetComment(SBTarget target) {
ExecutionContext exe_ctx;
TargetSP target_sp(target.GetSP());
- std::unique_lock<std::recursive_mutex> lock;
+ TargetAPIMutex api_lock;
+ std::unique_lock<TargetAPIMutex> guard;
if (target_sp) {
- lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ api_lock = TargetAPIMutex(target_sp->GetAPIMutex());
+ guard = std::unique_lock<TargetAPIMutex>(api_lock);
target_sp->CalculateExecutionContext(exe_ctx);
exe_ctx.SetProcessSP(target_sp->GetProcessSP());
@@ -173,9 +179,11 @@ SBInstruction::GetControlFlowKind(lldb::SBTarget target) {
if (inst_sp) {
ExecutionContext exe_ctx;
TargetSP target_sp(target.GetSP());
- std::unique_lock<std::recursive_mutex> lock;
+ TargetAPIMutex api_lock;
+ std::unique_lock<TargetAPIMutex> guard;
if (target_sp) {
- lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ api_lock = TargetAPIMutex(target_sp->GetAPIMutex());
+ guard = std::unique_lock<TargetAPIMutex>(api_lock);
target_sp->CalculateExecutionContext(exe_ctx);
exe_ctx.SetProcessSP(target_sp->GetProcessSP());
diff --git a/lldb/source/API/SBMutex.cpp b/lldb/source/API/SBMutex.cpp
index c7844dec658cc..c28cf9374cb21 100644
--- a/lldb/source/API/SBMutex.cpp
+++ b/lldb/source/API/SBMutex.cpp
@@ -7,16 +7,41 @@
//===----------------------------------------------------------------------===//
#include "lldb/API/SBMutex.h"
-#include "lldb/Target/Target.h"
+#include "lldb/Target/TargetAPIMutex.h"
#include "lldb/Utility/Instrumentation.h"
#include "lldb/lldb-forward.h"
#include <memory>
#include <mutex>
+#include <variant>
using namespace lldb;
using namespace lldb_private;
-SBMutex::SBMutex() : m_opaque_sp(std::make_shared<std::recursive_mutex>()) {
+/// Holds either a standalone std::recursive_mutex (default-constructed
+/// SBMutex, no Target to resolve through) or a TargetAPIMutex (constructed
+/// from a Target). Kept out of SBMutex.h since std::variant is a C++17
+/// feature and the public SB headers must stay usable from a C++11 client.
+class SBMutex::MutexVariant {
+public:
+ MutexVariant() : m_variant(std::in_place_type<std::recursive_mutex>) {}
+ explicit MutexVariant(lldb::TargetSP target_sp)
+ : m_variant(std::in_place_type<TargetAPIMutex>, std::move(target_sp)) {}
+
+ void lock() {
+ std::visit([](auto &mutex) { mutex.lock(); }, m_variant);
+ }
+ void unlock() {
+ std::visit([](auto &mutex) { mutex.unlock(); }, m_variant);
+ }
+ bool try_lock() {
+ return std::visit([](auto &mutex) { return mutex.try_lock(); }, m_variant);
+ }
+
+private:
+ std::variant<std::recursive_mutex, TargetAPIMutex> m_variant;
+};
+
+SBMutex::SBMutex() : m_opaque_sp(std::make_shared<MutexVariant>()) {
LLDB_INSTRUMENT_VA(this);
}
@@ -32,8 +57,7 @@ const SBMutex &SBMutex::operator=(const SBMutex &rhs) {
}
SBMutex::SBMutex(lldb::TargetSP target_sp)
- : m_opaque_sp(std::shared_ptr<std::recursive_mutex>(
- target_sp, &target_sp->GetAPIMutex())) {
+ : m_opaque_sp(std::make_shared<MutexVariant>(target_sp)) {
LLDB_INSTRUMENT_VA(this, target_sp);
}
@@ -62,8 +86,7 @@ void SBMutex::unlock() const {
bool SBMutex::try_lock() const {
LLDB_INSTRUMENT_VA(this);
- if (m_opaque_sp)
- return m_opaque_sp->try_lock();
-
- return false;
+ if (!m_opaque_sp)
+ return false;
+ return m_opaque_sp->try_lock();
}
diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp
index 08e39f754cf85..0984daa0e92b9 100644
--- a/lldb/source/API/SBProcess.cpp
+++ b/lldb/source/API/SBProcess.cpp
@@ -136,8 +136,8 @@ bool SBProcess::RemoteLaunch(char const **argv, char const **envp,
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (process_sp->GetState() == eStateConnected) {
if (stop_at_entry)
launch_flags |= eLaunchFlagStopAtEntry;
@@ -169,8 +169,8 @@ bool SBProcess::RemoteAttachToProcessWithID(lldb::pid_t pid,
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (process_sp->GetState() == eStateConnected) {
ProcessAttachInfo attach_info;
attach_info.SetProcessID(pid);
@@ -195,8 +195,8 @@ uint32_t SBProcess::GetNumThreads() {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
num_threads = process_sp->GetThreadList().GetSize();
}
}
@@ -211,8 +211,8 @@ SBThread SBProcess::GetSelectedThread() const {
ThreadSP thread_sp;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
thread_sp = process_sp->GetThreadList().GetSelectedThread();
sb_thread.SetThread(thread_sp);
}
@@ -228,8 +228,8 @@ SBThread SBProcess::CreateOSPluginThread(lldb::tid_t tid,
ThreadSP thread_sp;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
thread_sp = process_sp->CreateOSPluginThread(tid, context);
sb_thread.SetThread(thread_sp);
}
@@ -352,8 +352,8 @@ bool SBProcess::SetSelectedThread(const SBThread &thread) {
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return process_sp->GetThreadList().SetSelectedThreadByID(
thread.GetThreadID());
}
@@ -366,8 +366,8 @@ bool SBProcess::SetSelectedThreadByID(lldb::tid_t tid) {
bool ret_val = false;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
ret_val = process_sp->GetThreadList().SetSelectedThreadByID(tid);
}
@@ -380,8 +380,8 @@ bool SBProcess::SetSelectedThreadByIndexID(uint32_t index_id) {
bool ret_val = false;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
ret_val = process_sp->GetThreadList().SetSelectedThreadByIndexID(index_id);
}
@@ -397,8 +397,8 @@ SBThread SBProcess::GetThreadAtIndex(size_t index) {
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
thread_sp = process_sp->GetThreadList().GetThreadAtIndex(index, false);
sb_thread.SetThread(thread_sp);
}
@@ -415,8 +415,8 @@ uint32_t SBProcess::GetNumQueues() {
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
num_queues = process_sp->GetQueueList().GetSize();
}
}
@@ -433,8 +433,8 @@ SBQueue SBProcess::GetQueueAtIndex(size_t index) {
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
queue_sp = process_sp->GetQueueList().GetQueueAtIndex(index);
sb_queue.SetQueue(queue_sp);
}
@@ -448,8 +448,8 @@ uint32_t SBProcess::GetStopID(bool include_expression_stops) {
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (include_expression_stops)
return process_sp->GetStopID();
else
@@ -465,8 +465,8 @@ SBEvent SBProcess::GetStopEventForStopID(uint32_t stop_id) {
EventSP event_sp;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
event_sp = process_sp->GetStopEventForStopID(stop_id);
sb_event.reset(event_sp);
}
@@ -478,8 +478,8 @@ void SBProcess::ForceScriptedState(StateType new_state) {
LLDB_INSTRUMENT_VA(this, new_state);
if (ProcessSP process_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
process_sp->ForceScriptedState(new_state);
}
}
@@ -490,8 +490,8 @@ StateType SBProcess::GetState() {
StateType ret_val = eStateInvalid;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
ret_val = process_sp->GetState();
}
@@ -504,8 +504,8 @@ int SBProcess::GetExitStatus() {
int exit_status = 0;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
exit_status = process_sp->GetExitStatus();
}
@@ -519,8 +519,8 @@ const char *SBProcess::GetExitDescription() {
if (!process_sp)
return nullptr;
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return ConstString(process_sp->GetExitDescription()).GetCString();
}
@@ -574,8 +574,8 @@ SBError SBProcess::Continue() {
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (process_sp->GetTarget().GetDebugger().GetAsyncExecution())
sb_error.ref() = process_sp->Resume();
@@ -605,8 +605,8 @@ SBError SBProcess::Destroy() {
SBError sb_error;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
sb_error.SetError(process_sp->Destroy(false));
} else
sb_error = Status::FromErrorString("SBProcess is invalid");
@@ -620,8 +620,8 @@ SBError SBProcess::Stop() {
SBError sb_error;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
sb_error.SetError(process_sp->Halt());
} else
sb_error = Status::FromErrorString("SBProcess is invalid");
@@ -635,8 +635,8 @@ SBError SBProcess::Kill() {
SBError sb_error;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
sb_error.SetError(process_sp->Destroy(true));
} else
sb_error = Status::FromErrorString("SBProcess is invalid");
@@ -658,8 +658,8 @@ SBError SBProcess::Detach(bool keep_stopped) {
SBError sb_error;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
sb_error.SetError(process_sp->Detach(keep_stopped));
} else
sb_error = Status::FromErrorString("SBProcess is invalid");
@@ -673,8 +673,8 @@ SBError SBProcess::Signal(int signo) {
SBError sb_error;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
sb_error.SetError(process_sp->Signal(signo));
} else
sb_error = Status::FromErrorString("SBProcess is invalid");
@@ -709,8 +709,8 @@ SBThread SBProcess::GetThreadByID(tid_t tid) {
if (process_sp) {
Process::StopLocker stop_locker;
const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock());
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
thread_sp = process_sp->GetThreadList().FindThreadByID(tid, can_update);
sb_thread.SetThread(thread_sp);
}
@@ -727,8 +727,8 @@ SBThread SBProcess::GetThreadByIndexID(uint32_t index_id) {
if (process_sp) {
Process::StopLocker stop_locker;
const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock());
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
thread_sp =
process_sp->GetThreadList().FindThreadByIndexID(index_id, can_update);
sb_thread.SetThread(thread_sp);
@@ -844,8 +844,8 @@ lldb::SBAddressRangeList SBProcess::FindRangesInMemory(
error = Status::FromErrorString("process is running");
return matches;
}
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
matches.m_opaque_up->ref() = process_sp->FindRangesInMemory(
reinterpret_cast<const uint8_t *>(buf), size, ranges.ref().ref(),
alignment, max_matches, error.ref());
@@ -870,8 +870,8 @@ lldb::addr_t SBProcess::FindInMemory(const void *buf, uint64_t size,
return LLDB_INVALID_ADDRESS;
}
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return process_sp->FindInMemory(reinterpret_cast<const uint8_t *>(buf), size,
range.ref(), alignment, error.ref());
}
@@ -889,12 +889,11 @@ size_t SBProcess::ReadMemory(addr_t addr, void *dst, size_t dst_len,
size_t bytes_read = 0;
ProcessSP process_sp(GetSP());
-
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bytes_read = process_sp->ReadMemory(addr, dst, dst_len, sb_error.ref());
} else {
sb_error = Status::FromErrorString("process is running");
@@ -915,8 +914,8 @@ size_t SBProcess::ReadCStringFromMemory(addr_t addr, void *buf, size_t size,
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bytes_read = process_sp->ReadCStringFromMemory(addr, (char *)buf, size,
sb_error.ref());
} else {
@@ -937,8 +936,8 @@ uint64_t SBProcess::ReadUnsignedFromMemory(addr_t addr, uint32_t byte_size,
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
value = process_sp->ReadUnsignedIntegerFromMemory(addr, byte_size, 0,
sb_error.ref());
} else {
@@ -959,8 +958,8 @@ lldb::addr_t SBProcess::ReadPointerFromMemory(addr_t addr,
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
ptr = process_sp->ReadPointerFromMemory(addr, sb_error.ref());
} else {
sb_error = Status::FromErrorString("process is running");
@@ -982,8 +981,8 @@ size_t SBProcess::WriteMemory(addr_t addr, const void *src, size_t src_len,
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bytes_written =
process_sp->WriteMemory(addr, src, src_len, sb_error.ref());
} else {
@@ -1060,8 +1059,8 @@ SBProcess::GetNumSupportedHardwareWatchpoints(lldb::SBError &sb_error) const {
uint32_t num = 0;
ProcessSP process_sp(GetSP());
if (process_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
std::optional<uint32_t> actual_num = process_sp->GetWatchpointSlotCount();
if (actual_num) {
num = *actual_num;
@@ -1091,8 +1090,8 @@ uint32_t SBProcess::LoadImage(const lldb::SBFileSpec &sb_local_image_spec,
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
PlatformSP platform_sp = process_sp->GetTarget().GetPlatform();
return platform_sp->LoadImage(process_sp.get(), *sb_local_image_spec,
*sb_remote_image_spec, sb_error.ref());
@@ -1115,8 +1114,8 @@ uint32_t SBProcess::LoadImageUsingPaths(const lldb::SBFileSpec &image_spec,
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
PlatformSP platform_sp = process_sp->GetTarget().GetPlatform();
size_t num_paths = paths.GetSize();
std::vector<std::string> paths_vec;
@@ -1148,8 +1147,8 @@ lldb::SBError SBProcess::UnloadImage(uint32_t image_token) {
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
PlatformSP platform_sp = process_sp->GetTarget().GetPlatform();
sb_error.SetError(
platform_sp->UnloadImage(process_sp.get(), image_token));
@@ -1169,8 +1168,8 @@ lldb::SBError SBProcess::SendEventData(const char *event_data) {
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
sb_error.SetError(process_sp->SendEventData(event_data));
} else {
sb_error = Status::FromErrorString("process is running");
@@ -1225,8 +1224,8 @@ bool SBProcess::IsInstrumentationRuntimePresent(
if (!process_sp)
return false;
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
InstrumentationRuntimeSP runtime_sp =
process_sp->GetInstrumentationRuntime(type);
@@ -1278,8 +1277,8 @@ lldb::SBError SBProcess::SaveCore(SBSaveCoreOptions &options) {
return error;
}
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (process_sp->GetState() != eStateStopped) {
error = Status::FromErrorString("the process is not stopped");
@@ -1301,8 +1300,8 @@ SBProcess::GetMemoryRegionInfo(lldb::addr_t load_addr,
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
sb_error.ref() =
process_sp->GetMemoryRegionInfo(load_addr, sb_region_info.ref());
@@ -1323,8 +1322,8 @@ lldb::SBMemoryRegionInfoList SBProcess::GetMemoryRegions() {
ProcessSP process_sp(GetSP());
Process::StopLocker stop_locker;
if (process_sp && stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
process_sp->GetMemoryRegions(sb_region_list.ref());
}
@@ -1465,8 +1464,8 @@ lldb::addr_t SBProcess::AllocateMemory(size_t size, uint32_t permissions,
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
addr = process_sp->AllocateMemory(size, permissions, sb_error.ref());
} else {
sb_error = Status::FromErrorString("process is running");
@@ -1485,8 +1484,8 @@ lldb::SBError SBProcess::DeallocateMemory(lldb::addr_t ptr) {
if (process_sp) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
- std::lock_guard<std::recursive_mutex> guard(
- process_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = process_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
Status error = process_sp->DeallocateMemory(ptr);
sb_error.SetError(std::move(error));
} else {
diff --git a/lldb/source/API/SBSymbol.cpp b/lldb/source/API/SBSymbol.cpp
index 19f2f5e62fd48..25468f88c2de7 100644
--- a/lldb/source/API/SBSymbol.cpp
+++ b/lldb/source/API/SBSymbol.cpp
@@ -127,9 +127,11 @@ SBInstructionList SBSymbol::GetInstructions(SBTarget target,
SBInstructionList sb_instructions;
if (m_opaque_ptr) {
TargetSP target_sp(target.GetSP());
- std::unique_lock<std::recursive_mutex> lock;
+ TargetAPIMutex api_lock;
+ std::unique_lock<TargetAPIMutex> guard;
if (target_sp && m_opaque_ptr->ValueIsAddress()) {
- lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ api_lock = TargetAPIMutex(target_sp->GetAPIMutex());
+ guard = std::unique_lock<TargetAPIMutex>(api_lock);
const Address &symbol_addr = m_opaque_ptr->GetAddressRef();
ModuleSP module_sp = symbol_addr.GetModule();
if (module_sp) {
diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp
index 9eca813d8584b..b6ee17091ef6b 100644
--- a/lldb/source/API/SBTarget.cpp
+++ b/lldb/source/API/SBTarget.cpp
@@ -82,7 +82,8 @@ using namespace lldb_private;
#define DEFAULT_DISASM_BYTE_SIZE 32
static Status AttachToProcess(ProcessAttachInfo &attach_info, Target &target) {
- std::lock_guard<std::recursive_mutex> guard(target.GetAPIMutex());
+ TargetAPIMutex api_lock = target.GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
auto process_sp = target.GetProcessSP();
if (process_sp) {
@@ -310,7 +311,8 @@ SBError SBTarget::Install() {
SBError sb_error;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
sb_error.ref() = target_sp->Install(nullptr);
}
return sb_error;
@@ -329,7 +331,8 @@ SBProcess SBTarget::Launch(SBListener &listener, char const **argv,
SBProcess sb_process;
ProcessSP process_sp;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (stop_at_entry)
launch_flags |= eLaunchFlagStopAtEntry;
@@ -407,7 +410,8 @@ SBProcess SBTarget::Launch(SBLaunchInfo &sb_launch_info, SBError &error) {
SBProcess sb_process;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
StateType state = eStateInvalid;
{
ProcessSP process_sp = target_sp->GetProcessSP();
@@ -545,7 +549,8 @@ lldb::SBProcess SBTarget::ConnectRemote(SBListener &listener, const char *url,
SBProcess sb_process;
ProcessSP process_sp;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (listener.IsValid())
process_sp =
target_sp->CreateProcess(listener.m_opaque_sp, plugin_name, nullptr,
@@ -604,7 +609,8 @@ lldb::SBAddress SBTarget::ResolveLoadAddress(lldb::addr_t vm_addr) {
lldb::SBAddress sb_addr;
Address &addr = sb_addr.ref();
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (target_sp->ResolveLoadAddress(vm_addr, addr))
return sb_addr;
}
@@ -621,7 +627,8 @@ lldb::SBAddress SBTarget::ResolveFileAddress(lldb::addr_t file_addr) {
lldb::SBAddress sb_addr;
Address &addr = sb_addr.ref();
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (target_sp->ResolveFileAddress(file_addr, addr))
return sb_addr;
}
@@ -637,7 +644,8 @@ lldb::SBAddress SBTarget::ResolvePastLoadAddress(uint32_t stop_id,
lldb::SBAddress sb_addr;
Address &addr = sb_addr.ref();
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (target_sp->ResolveLoadAddress(vm_addr, addr))
return sb_addr;
}
@@ -672,7 +680,8 @@ size_t SBTarget::ReadMemory(const SBAddress addr, void *buf, size_t size,
size_t bytes_read = 0;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
bytes_read =
target_sp->ReadMemory(addr.ref(), buf, size, error.ref(), true);
} else {
@@ -767,7 +776,8 @@ SBBreakpoint SBTarget::BreakpointCreateByLocation(
SBBreakpoint sb_bp;
if (TargetSP target_sp = GetSP(); target_sp && line != 0) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
const LazyBool check_inlines = eLazyBoolCalculate;
const LazyBool skip_prologue = eLazyBoolCalculate;
@@ -795,7 +805,8 @@ SBBreakpoint SBTarget::BreakpointCreateByLocation(
SBBreakpoint sb_bp;
if (TargetSP target_sp = GetSP(); target_sp && line != 0) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
const LazyBool check_inlines = eLazyBoolCalculate;
const LazyBool skip_prologue = eLazyBoolCalculate;
@@ -820,7 +831,8 @@ SBBreakpoint SBTarget::BreakpointCreateByName(const char *symbol_name,
SBBreakpoint sb_bp;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
const bool internal = false;
const bool hardware = false;
@@ -892,7 +904,8 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateByName(
const bool internal = false;
const bool hardware = false;
const LazyBool skip_prologue = eLazyBoolCalculate;
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
FunctionNameType mask = static_cast<FunctionNameType>(name_type_mask);
sb_bp = target_sp->CreateBreakpoint(module_list.get(), comp_unit_list.get(),
symbol_name, mask, symbol_language,
@@ -935,7 +948,8 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateByNames(
SBBreakpoint sb_bp;
if (TargetSP target_sp = GetSP(); target_sp && num_names > 0) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
const bool internal = false;
const bool hardware = false;
FunctionNameType mask = static_cast<FunctionNameType>(name_type_mask);
@@ -980,7 +994,8 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateByRegex(
SBBreakpoint sb_bp;
if (TargetSP target_sp = GetSP();
target_sp && symbol_name_regex && symbol_name_regex[0]) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
RegularExpression regexp((llvm::StringRef(symbol_name_regex)));
const bool internal = false;
const bool hardware = false;
@@ -999,7 +1014,8 @@ SBBreakpoint SBTarget::BreakpointCreateByAddress(addr_t address) {
SBBreakpoint sb_bp;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
const bool hardware = false;
sb_bp = target_sp->CreateBreakpoint(address, false, hardware);
}
@@ -1016,7 +1032,8 @@ SBBreakpoint SBTarget::BreakpointCreateBySBAddress(SBAddress &sb_address) {
}
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
const bool hardware = false;
sb_bp = target_sp->CreateBreakpoint(sb_address.ref(), false, hardware);
}
@@ -1064,7 +1081,8 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateBySourceRegex(
SBBreakpoint sb_bp;
if (TargetSP target_sp = GetSP();
target_sp && source_regex && source_regex[0]) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
const bool hardware = false;
const LazyBool move_to_nearest_code = eLazyBoolCalculate;
RegularExpression regexp((llvm::StringRef(source_regex)));
@@ -1088,7 +1106,8 @@ SBTarget::BreakpointCreateForException(lldb::LanguageType language,
SBBreakpoint sb_bp;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
const bool hardware = false;
sb_bp = target_sp->CreateExceptionBreakpoint(language, catch_bp, throw_bp,
hardware);
@@ -1106,7 +1125,8 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateFromScript(
SBBreakpoint sb_bp;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
Status error;
StructuredData::ObjectSP obj_sp = extra_args.m_impl_up->GetObjectSP();
@@ -1149,7 +1169,8 @@ bool SBTarget::BreakpointDelete(break_id_t bp_id) {
bool result = false;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
result = target_sp->RemoveBreakpointByID(bp_id);
}
@@ -1162,7 +1183,8 @@ SBBreakpoint SBTarget::FindBreakpointByID(break_id_t bp_id) {
SBBreakpoint sb_breakpoint;
if (TargetSP target_sp = GetSP();
target_sp && bp_id != LLDB_INVALID_BREAK_ID) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
sb_breakpoint = target_sp->GetBreakpointByID(bp_id);
}
@@ -1174,7 +1196,8 @@ bool SBTarget::FindBreakpointsByName(const char *name,
LLDB_INSTRUMENT_VA(this, name, bkpts);
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
llvm::Expected<std::vector<BreakpointSP>> expected_vector =
target_sp->GetBreakpointList().FindBreakpointsByName(name);
if (!expected_vector) {
@@ -1195,7 +1218,8 @@ void SBTarget::GetBreakpointNames(SBStringList &names) {
names.Clear();
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
std::vector<std::string> name_vec;
target_sp->GetBreakpointNames(name_vec);
@@ -1208,7 +1232,8 @@ void SBTarget::DeleteBreakpointName(const char *name) {
LLDB_INSTRUMENT_VA(this, name);
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
target_sp->DeleteBreakpointName(llvm::StringRef(name));
}
}
@@ -1217,7 +1242,8 @@ bool SBTarget::EnableAllBreakpoints() {
LLDB_INSTRUMENT_VA(this);
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
target_sp->EnableAllowedBreakpoints();
return true;
}
@@ -1228,7 +1254,8 @@ bool SBTarget::DisableAllBreakpoints() {
LLDB_INSTRUMENT_VA(this);
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
target_sp->DisableAllowedBreakpoints();
return true;
}
@@ -1239,7 +1266,8 @@ bool SBTarget::DeleteAllBreakpoints() {
LLDB_INSTRUMENT_VA(this);
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
target_sp->RemoveAllowedBreakpoints();
return true;
}
@@ -1261,7 +1289,8 @@ lldb::SBError SBTarget::BreakpointsCreateFromFile(SBFileSpec &source_file,
SBError sberr;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
BreakpointIDList bp_ids;
@@ -1306,7 +1335,8 @@ lldb::SBError SBTarget::BreakpointsWriteToFile(SBFileSpec &dest_file,
SBError sberr;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
BreakpointIDList bp_id_list;
bkpt_list.CopyToBreakpointIDList(bp_id_list);
sberr.ref() = target_sp->SerializeBreakpointsToFile(dest_file.ref(),
@@ -1343,7 +1373,8 @@ bool SBTarget::DeleteWatchpoint(watch_id_t wp_id) {
bool result = false;
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
std::unique_lock<std::recursive_mutex> lock;
target_sp->GetWatchpointList().GetListMutex(lock);
result = target_sp->RemoveWatchpointByID(wp_id);
@@ -1359,7 +1390,8 @@ SBWatchpoint SBTarget::FindWatchpointByID(lldb::watch_id_t wp_id) {
lldb::WatchpointSP watchpoint_sp;
if (TargetSP target_sp = GetSP();
target_sp && wp_id != LLDB_INVALID_WATCH_ID) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
std::unique_lock<std::recursive_mutex> lock;
target_sp->GetWatchpointList().GetListMutex(lock);
watchpoint_sp = target_sp->GetWatchpointList().FindByID(wp_id);
@@ -1404,7 +1436,8 @@ SBTarget::WatchpointCreateByAddress(lldb::addr_t addr, size_t size,
if (TargetSP target_sp = GetSP();
target_sp && addr != LLDB_INVALID_ADDRESS && size > 0) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
// Target::CreateWatchpoint() is thread safe.
Status cw_error;
// This API doesn't take in a type, so we can't figure out what it is.
@@ -1422,7 +1455,8 @@ bool SBTarget::EnableAllWatchpoints() {
LLDB_INSTRUMENT_VA(this);
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
std::unique_lock<std::recursive_mutex> lock;
target_sp->GetWatchpointList().GetListMutex(lock);
target_sp->EnableAllWatchpoints();
@@ -1435,7 +1469,8 @@ bool SBTarget::DisableAllWatchpoints() {
LLDB_INSTRUMENT_VA(this);
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
std::unique_lock<std::recursive_mutex> lock;
target_sp->GetWatchpointList().GetListMutex(lock);
target_sp->DisableAllWatchpoints();
@@ -1500,7 +1535,8 @@ bool SBTarget::DeleteAllWatchpoints() {
LLDB_INSTRUMENT_VA(this);
if (TargetSP target_sp = GetSP()) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
std::unique_lock<std::recursive_mutex> lock;
target_sp->GetWatchpointList().GetListMutex(lock);
target_sp->RemoveAllWatchpoints();
@@ -2457,7 +2493,8 @@ lldb::SBValue SBTarget::EvaluateExpression(const char *expr,
if (expr == nullptr || expr[0] == '\0')
return expr_result;
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
ExecutionContext exe_ctx(m_opaque_sp.get());
frame = exe_ctx.GetFramePtr();
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index fafb533771e56..90cd0fe447540 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -464,7 +464,8 @@ static Status ResumeNewPlan(StoppedExecutionContext exe_ctx,
process->GetThreadList().SetSelectedThreadByID(thread->GetID());
// Release the run lock but keep the API lock.
- std::unique_lock<std::recursive_mutex> api_lock = exe_ctx.AllowResume();
+ TargetAPIMutex api_mutex = exe_ctx.AllowResume();
+ std::lock_guard<TargetAPIMutex> guard(api_mutex, std::adopt_lock);
if (process->GetTarget().GetDebugger().GetAsyncExecution())
return process->Resume();
return process->ResumeSynchronous(nullptr);
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index 61a4a1a6b1835..f76e923595544 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -999,9 +999,9 @@ lldb::ValueObjectSP SBValue::GetSP(ValueLocker &locker) const {
// IsValid means that the SBValue has a value in it. But that's not the
// only time that ValueObjects are useful. We also want to return the value
// if there's an error state in it.
- if (!m_opaque_sp || (!m_opaque_sp->IsValid()
- && (m_opaque_sp->GetRootSP()
- && !m_opaque_sp->GetRootSP()->GetError().Fail()))) {
+ if (!m_opaque_sp || (!m_opaque_sp->IsValid() &&
+ (m_opaque_sp->GetRootSP() &&
+ !m_opaque_sp->GetRootSP()->GetError().Fail()))) {
locker.GetError() = Status::FromErrorString("No value");
return ValueObjectSP();
}
@@ -1131,7 +1131,6 @@ lldb::SBValue SBValue::EvaluateExpression(const char *expr,
return SBValue();
}
-
ValueLocker locker;
lldb::ValueObjectSP value_sp(GetSP(locker));
if (!value_sp) {
@@ -1143,7 +1142,8 @@ lldb::SBValue SBValue::EvaluateExpression(const char *expr,
return SBValue();
}
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+ TargetAPIMutex api_lock = target_sp->GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
ExecutionContext exe_ctx(target_sp.get());
StackFrame *frame = exe_ctx.GetFramePtr();
diff --git a/lldb/source/API/SBWatchpoint.cpp b/lldb/source/API/SBWatchpoint.cpp
index 30528b8d34652..065c90e997088 100644
--- a/lldb/source/API/SBWatchpoint.cpp
+++ b/lldb/source/API/SBWatchpoint.cpp
@@ -108,8 +108,8 @@ addr_t SBWatchpoint::GetWatchAddress() {
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
ret_addr = watchpoint_sp->GetLoadAddress();
}
@@ -123,8 +123,8 @@ size_t SBWatchpoint::GetWatchSize() {
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
watch_size = watchpoint_sp->GetByteSize();
}
@@ -137,7 +137,8 @@ void SBWatchpoint::SetEnabled(bool enabled) {
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
Target &target = watchpoint_sp->GetTarget();
- std::lock_guard<std::recursive_mutex> guard(target.GetAPIMutex());
+ TargetAPIMutex api_lock = target.GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
ProcessSP process_sp = target.GetProcessSP();
const bool notify = true;
if (process_sp) {
@@ -156,8 +157,8 @@ bool SBWatchpoint::IsEnabled() {
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return watchpoint_sp->IsEnabled();
} else
return false;
@@ -169,8 +170,8 @@ uint32_t SBWatchpoint::GetHitCount() {
uint32_t count = 0;
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
count = watchpoint_sp->GetHitCount();
}
@@ -182,8 +183,8 @@ uint32_t SBWatchpoint::GetIgnoreCount() {
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return watchpoint_sp->GetIgnoreCount();
} else
return 0;
@@ -194,8 +195,8 @@ void SBWatchpoint::SetIgnoreCount(uint32_t n) {
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
watchpoint_sp->SetIgnoreCount(n);
}
}
@@ -207,8 +208,8 @@ const char *SBWatchpoint::GetCondition() {
if (!watchpoint_sp)
return nullptr;
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return ConstString(watchpoint_sp->GetConditionText()).GetCString();
}
@@ -217,8 +218,8 @@ void SBWatchpoint::SetCondition(const char *condition) {
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
watchpoint_sp->SetCondition(condition);
}
}
@@ -231,8 +232,8 @@ bool SBWatchpoint::GetDescription(SBStream &description,
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
watchpoint_sp->GetDescription(&strm, level);
strm.EOL();
} else
@@ -291,8 +292,8 @@ lldb::SBType SBWatchpoint::GetType() {
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
const CompilerType &type = watchpoint_sp->GetCompilerType();
return lldb::SBType(type);
}
@@ -304,8 +305,8 @@ WatchpointValueKind SBWatchpoint::GetWatchValueKind() {
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
if (watchpoint_sp->IsWatchVariable())
return WatchpointValueKind::eWatchPointValueKindVariable;
return WatchpointValueKind::eWatchPointValueKindExpression;
@@ -320,8 +321,8 @@ const char *SBWatchpoint::GetWatchSpec() {
if (!watchpoint_sp)
return nullptr;
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
// Store the result of `GetWatchSpec()` as a ConstString
// so that the C string we return has a sufficiently long
// lifetime. Note this a memory leak but should be fairly
@@ -333,8 +334,8 @@ bool SBWatchpoint::IsWatchingReads() {
LLDB_INSTRUMENT_VA(this);
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return watchpoint_sp->WatchpointRead();
}
@@ -346,8 +347,8 @@ bool SBWatchpoint::IsWatchingWrites() {
LLDB_INSTRUMENT_VA(this);
lldb::WatchpointSP watchpoint_sp(GetSP());
if (watchpoint_sp) {
- std::lock_guard<std::recursive_mutex> guard(
- watchpoint_sp->GetTarget().GetAPIMutex());
+ TargetAPIMutex api_lock = watchpoint_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
return watchpoint_sp->WatchpointWrite() ||
watchpoint_sp->WatchpointModify();
diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp
index aa0d4cc58d0ae..efa02e60a23e2 100644
--- a/lldb/source/Interpreter/CommandObject.cpp
+++ b/lldb/source/Interpreter/CommandObject.cpp
@@ -234,9 +234,10 @@ bool CommandObject::CheckRequirements(CommandReturnObject &result) {
}
if (flags & eCommandTryTargetAPILock) {
- if (target && !target->IsDummyTarget())
- m_api_locker =
- std::unique_lock<std::recursive_mutex>(target->GetAPIMutex());
+ if (target && !target->IsDummyTarget()) {
+ m_api_mutex = target->GetAPIMutex();
+ m_api_locker = std::unique_lock<TargetAPIMutex>(m_api_mutex);
+ }
}
}
diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt
index 90fe6fc3ad973..2874394c400aa 100644
--- a/lldb/source/Target/CMakeLists.txt
+++ b/lldb/source/Target/CMakeLists.txt
@@ -56,6 +56,7 @@ add_lldb_library(lldbTarget
StructuredDataPlugin.cpp
SystemRuntime.cpp
Target.cpp
+ TargetAPIMutex.cpp
TargetList.cpp
Thread.cpp
ThreadCollection.cpp
diff --git a/lldb/source/Target/ExecutionContext.cpp b/lldb/source/Target/ExecutionContext.cpp
index e4b2f07d8d8d1..e26faa4cd1db1 100644
--- a/lldb/source/Target/ExecutionContext.cpp
+++ b/lldb/source/Target/ExecutionContext.cpp
@@ -145,8 +145,8 @@ lldb_private::GetStoppedExecutionContext(
return llvm::createStringError(
"StoppedExecutionContext created with a null target");
- auto api_lock =
- std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+ auto api_mutex = target_sp->GetAPIMutex();
+ std::unique_lock<TargetAPIMutex> api_locker(api_mutex);
auto process_sp = exe_ctx_ref_ptr->GetProcessSP();
if (!process_sp)
@@ -169,14 +169,16 @@ lldb_private::GetStoppedExecutionContext(
exe_ctx_ref_ptr->m_frame_list_id->second);
}
+ api_locker.release();
return StoppedExecutionContext(target_sp, process_sp, thread_sp, frame_sp,
- std::move(api_lock), std::move(stop_locker));
+ std::move(api_mutex), std::move(stop_locker));
}
-std::unique_lock<std::recursive_mutex> StoppedExecutionContext::AllowResume() {
+TargetAPIMutex StoppedExecutionContext::AllowResume() {
Clear();
m_stop_locker = ProcessRunLock::ProcessRunLocker();
- return std::move(m_api_lock);
+ m_api_locker.release();
+ return std::move(m_api_mutex);
}
ExecutionContext::ExecutionContext(ExecutionContextScope *exe_scope_ptr)
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 6d4e200ea2241..ca42d027f4c22 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -66,7 +66,6 @@
#include "lldb/Utility/LLDBAssert.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
-#include "lldb/Utility/Policy.h"
#include "lldb/Utility/RealpathPrefixes.h"
#include "lldb/Utility/State.h"
#include "lldb/Utility/StreamString.h"
@@ -6020,12 +6019,8 @@ Target::TargetEventData::GetModuleListFromEvent(const Event *event_ptr) {
return module_list;
}
-std::recursive_mutex &Target::GetAPIMutex() {
- Policy policy = PolicyStack::Get().Current();
- if (policy.view == Policy::View::Private)
- return m_private_mutex;
-
- return m_mutex;
+TargetAPIMutex Target::GetAPIMutex() {
+ return TargetAPIMutex(shared_from_this());
}
/// Get metrics associated with this target in JSON format.
diff --git a/lldb/source/Target/TargetAPIMutex.cpp b/lldb/source/Target/TargetAPIMutex.cpp
new file mode 100644
index 0000000000000..c7c89e17e57f1
--- /dev/null
+++ b/lldb/source/Target/TargetAPIMutex.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Target/TargetAPIMutex.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Policy.h"
+
+using namespace lldb_private;
+
+void TargetAPIMutex::lock() {
+ if (m_target_sp) {
+ Policy policy = PolicyStack::Get().Current();
+ m_mutex = policy.view == Policy::View::Private
+ ? &m_target_sp->m_private_mutex
+ : &m_target_sp->m_mutex;
+ }
+ if (m_mutex)
+ m_mutex->lock();
+}
+
+bool TargetAPIMutex::try_lock() {
+ if (m_target_sp) {
+ Policy policy = PolicyStack::Get().Current();
+ m_mutex = policy.view == Policy::View::Private
+ ? &m_target_sp->m_private_mutex
+ : &m_target_sp->m_mutex;
+ }
+ return m_mutex ? m_mutex->try_lock() : true;
+}
diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp
index 7c681327d9268..8df1395e0ee15 100644
--- a/lldb/source/ValueObject/ValueObject.cpp
+++ b/lldb/source/ValueObject/ValueObject.cpp
@@ -3853,9 +3853,10 @@ bool ValueImpl::IsValid() {
return target_sp && target_sp->IsValid();
}
-lldb::ValueObjectSP
-ValueImpl::GetSP(Process::StopLocker &stop_locker,
- std::unique_lock<std::recursive_mutex> &lock, Status &error) {
+lldb::ValueObjectSP ValueImpl::GetSP(Process::StopLocker &stop_locker,
+ TargetAPIMutex &api_mutex,
+ std::unique_lock<TargetAPIMutex> &lock,
+ Status &error) {
if (!m_valobj_sp) {
error = Status::FromErrorString("invalid value object");
return m_valobj_sp;
@@ -3871,7 +3872,8 @@ ValueImpl::GetSP(Process::StopLocker &stop_locker,
if (!target)
return ValueObjectSP();
- lock = std::unique_lock<std::recursive_mutex>(target->GetAPIMutex());
+ api_mutex = target->GetAPIMutex();
+ lock = std::unique_lock<TargetAPIMutex>(api_mutex);
ProcessSP process_sp(value_sp->GetProcessSP());
if (process_sp && !stop_locker.TryLock(&process_sp->GetRunLock())) {
diff --git a/lldb/tools/lldb-dap/ProtocolUtils.cpp b/lldb/tools/lldb-dap/ProtocolUtils.cpp
index 016be5542e761..3bd5115f02972 100644
--- a/lldb/tools/lldb-dap/ProtocolUtils.cpp
+++ b/lldb/tools/lldb-dap/ProtocolUtils.cpp
@@ -21,6 +21,7 @@
#include "lldb/Host/PosixApi.h" // Adds PATH_MAX for windows
#include <iomanip>
+#include <mutex>
#include <optional>
#include <sstream>
diff --git a/lldb/unittests/Target/CMakeLists.txt b/lldb/unittests/Target/CMakeLists.txt
index ebfb06e8fc780..fab0b7353d4b7 100644
--- a/lldb/unittests/Target/CMakeLists.txt
+++ b/lldb/unittests/Target/CMakeLists.txt
@@ -14,6 +14,7 @@ add_lldb_unittest(TargetTests
ScratchTypeSystemTest.cpp
StackFrameRecognizerTest.cpp
SummaryStatisticsTest.cpp
+ TargetAPIMutexTest.cpp
FindFileTest.cpp
LINK_COMPONENTS
diff --git a/lldb/unittests/Target/TargetAPIMutexTest.cpp b/lldb/unittests/Target/TargetAPIMutexTest.cpp
new file mode 100644
index 0000000000000..2650723ec6f4e
--- /dev/null
+++ b/lldb/unittests/Target/TargetAPIMutexTest.cpp
@@ -0,0 +1,195 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Target/TargetAPIMutex.h"
+#include "Plugins/Platform/Linux/PlatformLinux.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "gtest/gtest.h"
+
+#include <thread>
+
+using namespace lldb_private;
+using namespace lldb;
+
+TEST(TargetAPIMutexTest, DefaultConstructedIsANoOp) {
+ // No synchronization primitive is touched at all in this state, so
+ // there is no pairing requirement: try_lock() always succeeds, and
+ // lock()/unlock() are callable with no invariant to violate.
+ TargetAPIMutex lock;
+ EXPECT_TRUE(lock.try_lock());
+ lock.lock();
+ lock.unlock();
+ lock.lock();
+ lock.unlock();
+}
+
+namespace {
+class TargetAPIMutexTargetTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ FileSystem::Initialize();
+ HostInfo::Initialize();
+ platform_linux::PlatformLinux::Initialize();
+ }
+ void TearDown() override {
+ platform_linux::PlatformLinux::Terminate();
+ HostInfo::Terminate();
+ FileSystem::Terminate();
+ }
+};
+
+TargetSP CreateTarget() {
+ ArchSpec arch("x86_64-pc-linux");
+ Platform::SetHostPlatform(
+ platform_linux::PlatformLinux::CreateInstance(true, &arch));
+
+ DebuggerSP debugger_sp = Debugger::CreateInstance();
+ TargetSP target_sp;
+ PlatformSP platform_sp;
+ Status error = debugger_sp->GetTargetList().CreateTarget(
+ *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);
+ return target_sp;
+}
+} // namespace
+
+TEST_F(TargetAPIMutexTargetTest, WrapsTheTargetMutex) {
+ TargetSP target_sp = CreateTarget();
+ ASSERT_TRUE(target_sp);
+
+ TargetAPIMutex lock(target_sp);
+ lock.lock();
+
+ // Recursive reentrancy is delegated straight to the underlying
+ // std::recursive_mutex: a second handle over the same target, locked
+ // from the same thread, must not block.
+ TargetAPIMutex second_lock(target_sp);
+ EXPECT_TRUE(second_lock.try_lock());
+ second_lock.unlock();
+
+ lock.unlock();
+
+ std::thread t([target_sp]() {
+ TargetAPIMutex background_lock(target_sp);
+ EXPECT_TRUE(background_lock.try_lock());
+ background_lock.unlock();
+ });
+ t.join();
+}
+
+TEST_F(TargetAPIMutexTargetTest, RealMutexBlocksOtherThreads) {
+ TargetSP target_sp = CreateTarget();
+ ASSERT_TRUE(target_sp);
+
+ TargetAPIMutex lock(target_sp);
+ lock.lock();
+
+ std::thread t([target_sp]() {
+ TargetAPIMutex background_lock(target_sp);
+ EXPECT_FALSE(background_lock.try_lock());
+ });
+ t.join();
+
+ lock.unlock();
+}
+
+TEST_F(TargetAPIMutexTargetTest, BareHandleDoesNotAutoUnlockOnDestruction) {
+ // TargetAPIMutex carries no RAII of its own -- exactly like
+ // std::recursive_mutex, a bare handle going out of scope without an
+ // explicit unlock() leaves the real mutex held. Callers that want
+ // scope-based release must wrap it in std::lock_guard/std::unique_lock.
+ TargetSP target_sp = CreateTarget();
+ ASSERT_TRUE(target_sp);
+
+ {
+ TargetAPIMutex lock(target_sp);
+ lock.lock();
+ }
+
+ std::thread t([target_sp]() {
+ TargetAPIMutex background_lock(target_sp);
+ EXPECT_FALSE(background_lock.try_lock());
+ });
+ t.join();
+}
+
+TEST_F(TargetAPIMutexTargetTest, LockGuardReleasesOnScopeExit) {
+ TargetSP target_sp = CreateTarget();
+ ASSERT_TRUE(target_sp);
+
+ {
+ TargetAPIMutex lock(target_sp);
+ std::lock_guard<TargetAPIMutex> guard(lock);
+ }
+
+ std::thread t([target_sp]() {
+ TargetAPIMutex background_lock(target_sp);
+ EXPECT_TRUE(background_lock.try_lock());
+ background_lock.unlock();
+ });
+ t.join();
+}
+
+TEST_F(TargetAPIMutexTargetTest, MoveTransfersOwnership) {
+ TargetSP target_sp = CreateTarget();
+ ASSERT_TRUE(target_sp);
+
+ TargetAPIMutex lock(target_sp);
+ lock.lock();
+
+ TargetAPIMutex moved(std::move(lock));
+
+ // The moved-from handle no longer references the real mutex: unlocking
+ // it is a no-op, so the mutex stays held until `moved` releases it.
+ lock.unlock();
+ std::thread contended([target_sp]() {
+ TargetAPIMutex background_lock(target_sp);
+ EXPECT_FALSE(background_lock.try_lock());
+ });
+ contended.join();
+
+ moved.unlock();
+ std::thread released([target_sp]() {
+ TargetAPIMutex background_lock(target_sp);
+ EXPECT_TRUE(background_lock.try_lock());
+ background_lock.unlock();
+ });
+ released.join();
+}
+
+TEST_F(TargetAPIMutexTargetTest, ResolvesFreshOnEachLockCall) {
+ // lock()/try_lock() re-resolve the real mutex on every call rather
+ // than caching a single resolution for the handle's lifetime: a
+ // handle can be locked, unlocked, and locked again, each time
+ // correctly contending with other threads for the same target mutex.
+ TargetSP target_sp = CreateTarget();
+ ASSERT_TRUE(target_sp);
+
+ TargetAPIMutex lock(target_sp);
+ lock.lock();
+ lock.unlock();
+
+ std::thread t([target_sp]() {
+ TargetAPIMutex background_lock(target_sp);
+ background_lock.lock();
+ background_lock.unlock();
+ });
+ t.join();
+
+ lock.lock();
+ std::thread contended([target_sp]() {
+ TargetAPIMutex background_lock(target_sp);
+ EXPECT_FALSE(background_lock.try_lock());
+ });
+ contended.join();
+ lock.unlock();
+}
>From 64812c6b2396415e463f192985252eec837a656f Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <ismail at bennani.ma>
Date: Thu, 6 Aug 2026 15:06:56 -0700
Subject: [PATCH 2/3] [lldb] Fix scripted frame provider cross-thread
re-entrant deadlock
GetStoppedExecutionContext unconditionally blocked acquiring the
target's API mutex. A thread already holding that mutex (for example a
`bt` command thread, through CommandObjectParsed's
eCommandTryTargetAPILock) can end up waiting on a StackFrameList lock
held by another thread (for example the debugger's event-handler
thread) that is itself blocked re-acquiring the API mutex from inside a
scripted frame provider's Python code that touches the SB API. This is
a classic AB-BA deadlock.
This patch introduces Policy::Capabilities::can_bypass_target_api_mutex,
pushed around every scripted-extension callback in
ScriptedPythonInterface::Dispatch and CallStaticMethod. A thread running
one of these callbacks isn't servicing a client-facing SB API entry
point; it doesn't need the same locking guarantees a top-level SB API
call does for any of the calls it makes during that window, not just the
one that happens to deadlock.
TargetAPILock::lock()/try_lock() check this capability when resolving
which mutex to use: when the current thread's policy says it can
bypass, they leave the handle pointing at nothing -- no synchronization
primitive touched at all -- instead of the real mutex. Every existing
caller keeps its own lock_guard/unique_lock code unchanged and becomes
deadlock-safe automatically, since a no-op handle can be locked/unlocked
from any thread with no cross-thread hazard.
This patch adds regression tests for both the original deadlock and for
a blocking SBMutex.lock() call made from inside a callback, including
TestSBMutexReflectsTargetMutex, which confirms SBMutex aliases the
real, shared target mutex rather than the bypass no-op.
Depends on #212872, which introduces TargetAPILock's per-call
resolve/replay behavior that makes this bypass safe to observe through
a deferred-lock SBMutex.
Signed-off-by: Med Ismail Bennani <ismail at bennani.ma>
---
lldb/include/lldb/Utility/Policy.h | 11 ++
.../Interfaces/ScriptedPythonInterface.h | 7 +
lldb/source/Target/TargetAPIMutex.cpp | 20 ++-
lldb/source/Utility/Policy.cpp | 7 +
.../Makefile | 2 +
...ProviderRegisterCommandAPIMutexDeadlock.py | 88 +++++++++++
.../frame_provider.py | 28 ++++
.../main.c | 7 +
.../sbmutex_reflects_target_mutex/Makefile | 2 +
.../TestHoldMutexNoDeadlock.py | 87 +++++++++++
.../TestSBMutexReflectsTargetMutex.py | 139 ++++++++++++++++++
.../hold_mutex_frame_provider.py | 35 +++++
.../sbmutex_reflects_target_mutex/main.c | 12 ++
.../sbmutex_frame_provider.py | 84 +++++++++++
lldb/unittests/Target/TargetAPIMutexTest.cpp | 43 ++++++
lldb/unittests/Utility/PolicyTest.cpp | 17 ++-
16 files changed, 581 insertions(+), 8 deletions(-)
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/Makefile
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/TestFrameProviderRegisterCommandAPIMutexDeadlock.py
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/frame_provider.py
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/main.c
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/Makefile
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/TestHoldMutexNoDeadlock.py
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/TestSBMutexReflectsTargetMutex.py
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/hold_mutex_frame_provider.py
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/main.c
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/sbmutex_frame_provider.py
diff --git a/lldb/include/lldb/Utility/Policy.h b/lldb/include/lldb/Utility/Policy.h
index afeeab19c2ed0..83248cf61d0c6 100644
--- a/lldb/include/lldb/Utility/Policy.h
+++ b/lldb/include/lldb/Utility/Policy.h
@@ -50,6 +50,11 @@ struct Policy {
bool can_run_breakpoint_actions = true;
bool can_load_frame_providers = true;
bool can_run_frame_recognizers = true;
+ /// Whether the current thread may bypass the target's API mutex
+ /// entirely when it re-enters it, because the thread is already
+ /// running under whatever protections its caller set up rather than
+ /// servicing a top-level SB API entry point itself.
+ bool can_bypass_target_api_mutex = false;
};
/// Why a private-state policy is being pushed. Distinguishes a PST's
@@ -75,6 +80,7 @@ struct Policy {
static Policy CreatePrivateState(
PrivateStatePurpose purpose = PrivateStatePurpose::Default);
static Policy CreatePublicStateRunningExpression();
+ static Policy CreateScriptedExtensionCall();
/// @}
void Dump(Stream &s) const;
@@ -140,6 +146,11 @@ class PolicyStack {
return Guard();
}
+ [[nodiscard]] Guard PushScriptedExtensionCall() {
+ Push(Policy::CreateScriptedExtensionCall());
+ return Guard();
+ }
+
private:
void Push(Policy policy) { m_stack.push_back(std::move(policy)); }
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
index ce48f2468d380..9ad4d4e40cecf 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
@@ -18,6 +18,7 @@
#include "lldb/API/SBCommandReturnObject.h"
#include "lldb/Interpreter/Interfaces/ScriptedInterface.h"
#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/Policy.h"
#include "../PythonDataObjects.h"
#include "../SWIGPythonBridge.h"
@@ -413,6 +414,9 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
return ErrorWithMessage<T>(caller_signature, "missing script class name",
error);
+ PolicyStack::Guard policy_guard =
+ PolicyStack::Get().PushScriptedExtensionCall();
+
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
Locker::FreeLock);
@@ -536,6 +540,9 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
return ErrorWithMessage<T>(caller_signature, "python object ill-formed",
error);
+ PolicyStack::Guard policy_guard =
+ PolicyStack::Get().PushScriptedExtensionCall();
+
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
Locker::FreeLock);
diff --git a/lldb/source/Target/TargetAPIMutex.cpp b/lldb/source/Target/TargetAPIMutex.cpp
index c7c89e17e57f1..6393678a16632 100644
--- a/lldb/source/Target/TargetAPIMutex.cpp
+++ b/lldb/source/Target/TargetAPIMutex.cpp
@@ -15,9 +15,13 @@ using namespace lldb_private;
void TargetAPIMutex::lock() {
if (m_target_sp) {
Policy policy = PolicyStack::Get().Current();
- m_mutex = policy.view == Policy::View::Private
- ? &m_target_sp->m_private_mutex
- : &m_target_sp->m_mutex;
+ if (policy.capabilities.can_bypass_target_api_mutex) {
+ m_mutex = nullptr;
+ } else {
+ m_mutex = policy.view == Policy::View::Private
+ ? &m_target_sp->m_private_mutex
+ : &m_target_sp->m_mutex;
+ }
}
if (m_mutex)
m_mutex->lock();
@@ -26,9 +30,13 @@ void TargetAPIMutex::lock() {
bool TargetAPIMutex::try_lock() {
if (m_target_sp) {
Policy policy = PolicyStack::Get().Current();
- m_mutex = policy.view == Policy::View::Private
- ? &m_target_sp->m_private_mutex
- : &m_target_sp->m_mutex;
+ if (policy.capabilities.can_bypass_target_api_mutex) {
+ m_mutex = nullptr;
+ } else {
+ m_mutex = policy.view == Policy::View::Private
+ ? &m_target_sp->m_private_mutex
+ : &m_target_sp->m_mutex;
+ }
}
return m_mutex ? m_mutex->try_lock() : true;
}
diff --git a/lldb/source/Utility/Policy.cpp b/lldb/source/Utility/Policy.cpp
index 4d1999aaf7b92..04293d7a03f85 100644
--- a/lldb/source/Utility/Policy.cpp
+++ b/lldb/source/Utility/Policy.cpp
@@ -64,6 +64,12 @@ Policy Policy::CreatePublicStateRunningExpression() {
return p;
}
+Policy Policy::CreateScriptedExtensionCall() {
+ Policy p = PolicyStack::Get().Current();
+ p.capabilities.can_bypass_target_api_mutex = true;
+ return p;
+}
+
PolicyStack::Guard::~Guard() {
if (!m_active)
return;
@@ -108,6 +114,7 @@ void Policy::Dump(Stream &s) const {
s << " bp_actions=" << capabilities.can_run_breakpoint_actions;
s << " frame_providers=" << capabilities.can_load_frame_providers;
s << " frame_recognizers=" << capabilities.can_run_frame_recognizers;
+ s << " bypass_api_mutex=" << capabilities.can_bypass_target_api_mutex;
s << '}';
}
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/Makefile b/lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/Makefile
new file mode 100644
index 0000000000000..c9319d6e6888a
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/Makefile
@@ -0,0 +1,2 @@
+C_SOURCES := main.c
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/TestFrameProviderRegisterCommandAPIMutexDeadlock.py b/lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/TestFrameProviderRegisterCommandAPIMutexDeadlock.py
new file mode 100644
index 0000000000000..4efb4baae9b0f
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/TestFrameProviderRegisterCommandAPIMutexDeadlock.py
@@ -0,0 +1,88 @@
+"""
+Test that a scripted frame provider whose get_frame_at_index touches SB
+API (self.input_frames) does not deadlock when running `bt` from the
+command interpreter.
+
+GetStoppedExecutionContext (used by SBFrame::IsValid, among others)
+unconditionally blocked acquiring the target's API mutex. The command
+thread running `bt` already holds that mutex (CommandObjectParsed's
+eCommandTryTargetAPILock) and can end up waiting on a StackFrameList
+lock held by the debugger's event-handler thread, which is itself
+blocked re-acquiring the API mutex from inside this provider's Python
+code -- an AB-BA deadlock between the command thread and the
+event-handler thread.
+
+The event-handler thread only runs when commands are driven through
+SBDebugger.RunCommandInterpreter (what the lldb driver itself uses),
+not through plain HandleCommand, so this test drives commands that way.
+
+Note: this is a genuine cross-thread race (the command thread vs. the
+debugger's event-handler thread), not a deterministic sequential
+deadlock, so this test is best-effort -- like the sibling
+runlock_reentrant_deadlock/was_hit_deadlock tests, it raises the odds of
+hitting the race within a single invocation but cannot guarantee it.
+"""
+
+import os
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class TestFrameProviderRegisterCommandAPIMutexDeadlock(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_register_command_then_bt_no_deadlock(self):
+ """
+ Register a scripted frame provider whose get_frame_at_index
+ touches SB API, then repeatedly run `bt` through
+ RunCommandInterpreter. Should complete without deadlocking.
+ """
+ self.build()
+
+ lldbutil.run_to_name_breakpoint(self, "frame3")
+
+ provider_path = os.path.join(self.getSourceDir(), "frame_provider.py")
+
+ commands = ["command script import " + provider_path]
+ commands.append(
+ "target frame-provider register -C frame_provider.DictFrameProvider"
+ )
+ # Run `bt` several times to raise the odds of hitting the race
+ # (see module docstring).
+ commands.extend(["bt"] * 20)
+ commands.append("quit")
+
+ stdin_path = self.getBuildArtifact("stdin.txt")
+ stdout_path = self.getBuildArtifact("stdout.txt")
+ with open(stdin_path, "w") as f:
+ f.write("\n".join(commands) + "\n")
+
+ with open(stdin_path, "r") as in_fileH, open(stdout_path, "w") as out_fileH:
+ in_sbf = lldb.SBFile(in_fileH.fileno(), "r", False)
+ out_sbf = lldb.SBFile(out_fileH.fileno(), "w", False)
+ self.assertSuccess(self.dbg.SetInputFile(in_sbf))
+ self.assertSuccess(self.dbg.SetOutputFile(out_sbf))
+ self.assertSuccess(self.dbg.SetErrorFile(out_sbf))
+
+ options = lldb.SBCommandInterpreterRunOptions()
+ options.SetEchoCommands(False)
+ options.SetPrintResults(True)
+ options.SetStopOnError(False)
+ options.SetStopOnCrash(False)
+
+ # If the API-mutex deadlock regresses, this call hangs forever
+ # (timing out the test run).
+ n_errors, quit_requested, has_crashed = self.dbg.RunCommandInterpreter(
+ True, False, options, 0, False, False
+ )
+
+ with open(stdout_path, "r") as out_fileH:
+ output = out_fileH.read()
+
+ self.assertFalse(has_crashed, "lldb should not have crashed")
+ self.assertTrue(quit_requested, "quit command should have been processed")
+ self.assertEqual(n_errors, 0, f"unexpected errors in output:\n{output}")
+
+ self.assertIn("successfully registered scripted frame provider", output)
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/frame_provider.py b/lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/frame_provider.py
new file mode 100644
index 0000000000000..2bfab64b1c4d8
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/frame_provider.py
@@ -0,0 +1,28 @@
+"""
+Frame provider that returns dict-based synthetic frames (never identity
+forwarding), while touching self.input_frames from get_frame_at_index.
+
+Returning a dict keeps this test isolated from the frame-aliasing bug:
+dict-based frames always go through ScriptedFrameProvider's
+create_frame_from_dict helper, which builds a brand new StackFrame and
+never reuses (or wraps via BorrowedStackFrame) the parent list's frame
+object. Only the API-mutex deadlock is reachable through this path.
+"""
+
+from lldb.plugins.scripted_frame_provider import ScriptedFrameProvider
+
+
+class DictFrameProvider(ScriptedFrameProvider):
+ @staticmethod
+ def get_description():
+ return "Provider that returns dict-based synthetic frames"
+
+ def get_frame_at_index(self, index):
+ if index >= len(self.input_frames):
+ return None
+ # __getitem__ calls SBFrame.IsValid() internally, which is what
+ # exercises GetStoppedExecutionContext.
+ frame = self.input_frames[index]
+ if frame is None:
+ return None
+ return {"idx": index, "pc": frame.GetPC()}
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/main.c b/lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/main.c
new file mode 100644
index 0000000000000..1aa56e3eddf7a
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/register_command_api_mutex_deadlock/main.c
@@ -0,0 +1,7 @@
+int frame3() { return 3; }
+
+int frame2() { return frame3(); }
+
+int frame1() { return frame2(); }
+
+int main() { return frame1(); }
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/Makefile b/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/Makefile
new file mode 100644
index 0000000000000..c9319d6e6888a
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/Makefile
@@ -0,0 +1,2 @@
+C_SOURCES := main.c
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/TestHoldMutexNoDeadlock.py b/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/TestHoldMutexNoDeadlock.py
new file mode 100644
index 0000000000000..6e46193bfd83b
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/TestHoldMutexNoDeadlock.py
@@ -0,0 +1,87 @@
+"""
+Test that a scripted frame provider can safely call a blocking
+SBMutex.lock() from inside get_frame_at_index without deadlocking.
+
+LLDB's private state thread can reach this callback without already
+holding the target's real API mutex, so a blocking lock() there is a
+genuinely new acquisition attempt, not a safe same-thread recursive
+re-lock. Target::APIMutexHandle re-resolves Target::GetAPIMutex() on
+every call rather than aliasing whatever mutex was current when the
+SBMutex was constructed, so this thread gets the same thread-local
+bypass mutex the internal machinery does, and lock() never contends
+with anyone.
+
+Like the sibling runlock_reentrant_deadlock/was_hit_deadlock/
+register_command_api_mutex_deadlock tests, this drives a genuine
+cross-thread race and is best-effort: it raises the odds of exercising
+the path within a single invocation but the important guarantee is that
+it cannot hang, not that it hits any particular thread ordering.
+"""
+
+import os
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class TestHoldMutexNoDeadlock(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_hold_mutex_no_deadlock(self):
+ """
+ Register a scripted frame provider that locks and holds
+ target.GetAPIMutex() from get_frame_at_index, then run `bt` and
+ `continue` through RunCommandInterpreter. Should complete
+ without deadlocking.
+ """
+ self.build()
+
+ lldbutil.run_to_name_breakpoint(self, "frame3")
+
+ provider_path = os.path.join(
+ self.getSourceDir(), "hold_mutex_frame_provider.py"
+ )
+ commands = ["command script import " + provider_path]
+ commands.append(
+ "target frame-provider register "
+ "-C hold_mutex_frame_provider.HoldMutexFrameProvider"
+ )
+ # Interleave `bt` with `continue` (hitting the same breakpoint
+ # again, via a loop in main.c) so get_frame_at_index runs
+ # repeatedly instead of once, raising the odds of hitting the
+ # race within a single test invocation.
+ commands.extend(["bt", "continue"] * 20)
+ commands.append("quit")
+
+ stdin_path = self.getBuildArtifact("stdin.txt")
+ stdout_path = self.getBuildArtifact("stdout.txt")
+ with open(stdin_path, "w") as f:
+ f.write("\n".join(commands) + "\n")
+
+ with open(stdin_path, "r") as in_fileH, open(stdout_path, "w") as out_fileH:
+ in_sbf = lldb.SBFile(in_fileH.fileno(), "r", False)
+ out_sbf = lldb.SBFile(out_fileH.fileno(), "w", False)
+ self.assertSuccess(self.dbg.SetInputFile(in_sbf))
+ self.assertSuccess(self.dbg.SetOutputFile(out_sbf))
+ self.assertSuccess(self.dbg.SetErrorFile(out_sbf))
+
+ options = lldb.SBCommandInterpreterRunOptions()
+ options.SetEchoCommands(False)
+ options.SetPrintResults(True)
+ options.SetStopOnError(False)
+ options.SetStopOnCrash(False)
+
+ # If the bypass regresses, this call hangs forever (timing
+ # out the test run).
+ n_errors, quit_requested, has_crashed = self.dbg.RunCommandInterpreter(
+ True, False, options, 0, False, False
+ )
+
+ with open(stdout_path, "r") as out_fileH:
+ output = out_fileH.read()
+
+ self.assertFalse(has_crashed, "lldb should not have crashed")
+ self.assertTrue(quit_requested, "quit command should have been processed")
+ self.assertEqual(n_errors, 0, f"unexpected errors in output:\n{output}")
+ self.assertIn("successfully registered scripted frame provider", output)
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/TestSBMutexReflectsTargetMutex.py b/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/TestSBMutexReflectsTargetMutex.py
new file mode 100644
index 0000000000000..781c8e3873406
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/TestSBMutexReflectsTargetMutex.py
@@ -0,0 +1,139 @@
+"""
+Test that a scripted frame provider calling SBTarget.GetAPIMutex() from
+get_frame_at_index gets a handle that reflects the state of the target's
+real, shared API mutex, even though the callback's own thread is exempt
+from having to serialize on it.
+
+SBMutex is meant to be obtainable inside a bypassed scripted callback and
+locked later, once that bypass no longer applies -- e.g. on a different
+thread with no scripted-extension call on its stack, as this test's own
+provider does (see sbmutex_frame_provider.py). So it must always alias
+the genuine target mutex rather than the thread-local mutex the bypass
+hands out for internal callers. This test drives the same
+kind of command-thread / internal-thread race as
+TestFrameProviderRegisterCommandAPIMutexDeadlock, interleaving `bt` with
+`continue` (hitting the same breakpoint again each time, via a loop in
+main.c) so get_frame_at_index runs many times instead of once.
+
+The provider obtains the mutex from inside get_frame_at_index (safe --
+obtaining a handle doesn't resolve or lock anything), but the actual
+try_lock() runs on a plain background thread it spawns for the check (see
+sbmutex_frame_provider.py for why: ScriptedPythonInterface::Dispatch pushes
+the API-mutex bypass policy for the callback's entire duration, so
+try_lock() on the callback's own thread would always resolve to the
+thread-local bypass mutex -- which no other thread could ever contend on,
+making the check meaningless regardless of what any other thread is doing
+to the real mutex). The background thread never had that policy pushed, so
+its try_lock() resolves to the real, shared mutex: if some other thread
+(e.g. the command thread running `bt`) happens to hold it at that moment,
+this correctly observes it as contended.
+
+Only try_lock() is used, which never blocks, so this cannot deadlock
+regardless of the outcome. An earlier version of this test tried to
+widen the race window by having the callback actually lock() and hold
+the mutex for a short duration, on the assumption that whichever thread
+reaches this callback already holds the real mutex first. That
+assumption is wrong -- LLDB's private state thread can reach this
+callback without already holding it -- so that held mutex.lock() call
+could genuinely block, and reproducibly deadlocked in practice. Do not
+reintroduce a blocking acquisition here.
+
+Observing contention is a genuine cross-thread race, so -- like the
+sibling runlock_reentrant_deadlock/was_hit_deadlock/
+register_command_api_mutex_deadlock tests -- this is best-effort: it
+raises the odds of witnessing it within a single invocation but cannot
+guarantee it, and the test does not require it to pass.
+"""
+
+import os
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class TestSBMutexReflectsTargetMutex(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_sbmutex_reflects_target_mutex(self):
+ """
+ Register a scripted frame provider that checks
+ target.GetAPIMutex().try_lock() from get_frame_at_index, then
+ repeatedly run `bt` and `continue` through RunCommandInterpreter.
+ Should complete without deadlocking, regardless of whether
+ contention is observed.
+ """
+ self.build()
+
+ lldbutil.run_to_name_breakpoint(self, "frame3")
+
+ provider_path = os.path.join(self.getSourceDir(), "sbmutex_frame_provider.py")
+ artifact_path = self.getBuildArtifact("contention.txt")
+ if os.path.exists(artifact_path):
+ os.remove(artifact_path)
+
+ commands = ["command script import " + provider_path]
+ commands.append(
+ "target frame-provider register "
+ "-C sbmutex_frame_provider.ContentionCheckFrameProvider "
+ "-k artifact_path -v " + artifact_path
+ )
+ # `bt` only re-invokes get_frame_at_index when the thread's stack
+ # frame list was invalidated by a new stop, so interleave `bt` with
+ # `continue` (hitting the same breakpoint again, in a loop in
+ # main.c) to get repeated fresh invocations, raising the odds of
+ # hitting the race within a single test invocation.
+ commands.extend(["bt", "continue"] * 20)
+ commands.append("quit")
+
+ stdin_path = self.getBuildArtifact("stdin.txt")
+ stdout_path = self.getBuildArtifact("stdout.txt")
+ with open(stdin_path, "w") as f:
+ f.write("\n".join(commands) + "\n")
+
+ with open(stdin_path, "r") as in_fileH, open(stdout_path, "w") as out_fileH:
+ in_sbf = lldb.SBFile(in_fileH.fileno(), "r", False)
+ out_sbf = lldb.SBFile(out_fileH.fileno(), "w", False)
+ self.assertSuccess(self.dbg.SetInputFile(in_sbf))
+ self.assertSuccess(self.dbg.SetOutputFile(out_sbf))
+ self.assertSuccess(self.dbg.SetErrorFile(out_sbf))
+
+ options = lldb.SBCommandInterpreterRunOptions()
+ options.SetEchoCommands(False)
+ options.SetPrintResults(True)
+ options.SetStopOnError(False)
+ options.SetStopOnCrash(False)
+
+ n_errors, quit_requested, has_crashed = self.dbg.RunCommandInterpreter(
+ True, False, options, 0, False, False
+ )
+
+ with open(stdout_path, "r") as out_fileH:
+ output = out_fileH.read()
+
+ self.assertFalse(has_crashed, "lldb should not have crashed")
+ self.assertTrue(quit_requested, "quit command should have been processed")
+ self.assertEqual(n_errors, 0, f"unexpected errors in output:\n{output}")
+ self.assertIn("successfully registered scripted frame provider", output)
+
+ self.assertTrue(
+ os.path.exists(artifact_path),
+ "get_frame_at_index should have run and recorded at least one outcome",
+ )
+ with open(artifact_path, "r") as f:
+ outcomes = [line.strip() for line in f if line.strip()]
+
+ self.assertTrue(outcomes, "expected at least one recorded outcome")
+ self.assertTrue(
+ all(
+ o
+ in (
+ "another thread held the real target API mutex",
+ "no other thread held the real target API mutex",
+ )
+ for o in outcomes
+ ),
+ f"unexpected outcome values: {outcomes}",
+ )
+ # Whether this specific outcome occurs is a race (see module
+ # docstring); not asserted on here.
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/hold_mutex_frame_provider.py b/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/hold_mutex_frame_provider.py
new file mode 100644
index 0000000000000..a7d2db5d567d0
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/hold_mutex_frame_provider.py
@@ -0,0 +1,35 @@
+"""
+Frame provider whose get_frame_at_index locks the target's real API
+mutex via SBMutex and holds it briefly, from inside the bypassed
+scripted-extension callback. See TestHoldMutexNoDeadlock.py for why this
+must not deadlock.
+"""
+
+import time
+
+from lldb.plugins.scripted_frame_provider import ScriptedFrameProvider
+
+HOLD_DURATION_SECONDS = 0.2
+
+
+class HoldMutexFrameProvider(ScriptedFrameProvider):
+ @staticmethod
+ def get_description():
+ return (
+ "Provider that holds the real API mutex via SBMutex from get_frame_at_index"
+ )
+
+ def get_frame_at_index(self, index):
+ if index >= len(self.input_frames):
+ return None
+
+ if index == 0:
+ mutex = self.target.GetAPIMutex()
+ mutex.lock()
+ time.sleep(HOLD_DURATION_SECONDS)
+ mutex.unlock()
+
+ frame = self.input_frames[index]
+ if frame is None:
+ return None
+ return {"idx": index, "pc": frame.GetPC()}
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/main.c b/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/main.c
new file mode 100644
index 0000000000000..ed95560986ac0
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/main.c
@@ -0,0 +1,12 @@
+int frame3() { return 3; }
+
+int frame2() { return frame3(); }
+
+int frame1() { return frame2(); }
+
+int main() {
+ int result = 0;
+ for (int i = 0; i < 25; ++i)
+ result += frame1();
+ return result;
+}
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/sbmutex_frame_provider.py b/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/sbmutex_frame_provider.py
new file mode 100644
index 0000000000000..bc35c717f3c17
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/sbmutex_reflects_target_mutex/sbmutex_frame_provider.py
@@ -0,0 +1,84 @@
+"""
+Frame provider whose get_frame_at_index checks whether the target's real
+API mutex is currently held by a different thread.
+
+Used by TestSBMutexReflectsTargetMutex.py to confirm that SBMutex
+(SBTarget::GetAPIMutex()) aliases the real, shared target mutex rather
+than the thread-local mutex the bypass hands out for internal callers.
+
+The check must NOT run directly on this thread while still inside
+get_frame_at_index: ScriptedPythonInterface::Dispatch pushes the
+can_bypass_target_api_mutex policy for its entire duration, which spans
+this whole Python call. Target::GetAPIMutex() re-checks that policy on
+every call, so try_lock() from here would always resolve to the
+thread-local bypass mutex -- which, being thread-local, can never be
+contended by another thread. That would make "another thread held it"
+unreachable and the check meaningless, regardless of what any other
+thread is actually doing to the real mutex.
+
+Instead, the check runs on a plain background thread, spawned fresh from
+here with no scripted-extension call on its stack. That thread never had
+the bypass policy pushed, so Target::GetAPIMutex() on it resolves to the
+real, shared mutex from the start -- exactly the mutex a concurrently
+running `bt` command (via CommandObjectParsed's eCommandTryTargetAPILock)
+may be holding at that moment.
+
+Only try_lock() is used, and it is never held beyond the immediate
+check: an earlier version of this provider held the mutex for a short
+duration to try to widen the race window, but that meant a genuinely
+blocking acquisition from whichever thread invoked this callback -- not
+every internal caller (e.g. the private state thread) already holds the
+real mutex by the time it gets here, so that held the mutex, which
+caused a real deadlock in practice. try_lock() never blocks, so this
+cannot deadlock regardless of the outcome.
+"""
+
+import threading
+
+from lldb.plugins.scripted_frame_provider import ScriptedFrameProvider
+
+
+class ContentionCheckFrameProvider(ScriptedFrameProvider):
+ @staticmethod
+ def get_description():
+ return "Provider that checks SBMutex contention from a background thread"
+
+ def __init__(self, input_frames, args):
+ super().__init__(input_frames, args)
+ self.artifact_path = None
+ if self.args is not None:
+ value = self.args.GetValueForKey("artifact_path")
+ if value.IsValid():
+ self.artifact_path = value.GetStringValue(4096)
+
+ def _check_contention(self, mutex):
+ # Runs on a fresh thread with no scripted-extension call (and so no
+ # can_bypass_target_api_mutex) on its stack -- see module docstring.
+ if mutex.try_lock():
+ # Uncontended: nobody else holds the real mutex right now.
+ # Undo the lock we just took.
+ mutex.unlock()
+ outcome = "no other thread held the real target API mutex"
+ else:
+ outcome = "another thread held the real target API mutex"
+ with open(self.artifact_path, "a") as f:
+ f.write(outcome + "\n")
+
+ def get_frame_at_index(self, index):
+ if index >= len(self.input_frames):
+ return None
+
+ if index == 0 and self.artifact_path:
+ # Obtaining the mutex handle itself doesn't lock anything --
+ # it's safe to do from inside the bypassed callback. Only the
+ # actual try_lock() call, on the background thread, needs to
+ # happen outside the bypass.
+ mutex = self.target.GetAPIMutex()
+ checker = threading.Thread(target=self._check_contention, args=(mutex,))
+ checker.start()
+ checker.join()
+
+ frame = self.input_frames[index]
+ if frame is None:
+ return None
+ return {"idx": index, "pc": frame.GetPC()}
diff --git a/lldb/unittests/Target/TargetAPIMutexTest.cpp b/lldb/unittests/Target/TargetAPIMutexTest.cpp
index 2650723ec6f4e..1d3036d5dde33 100644
--- a/lldb/unittests/Target/TargetAPIMutexTest.cpp
+++ b/lldb/unittests/Target/TargetAPIMutexTest.cpp
@@ -14,6 +14,7 @@
#include "lldb/Target/Platform.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Policy.h"
#include "gtest/gtest.h"
#include <thread>
@@ -193,3 +194,45 @@ TEST_F(TargetAPIMutexTargetTest, ResolvesFreshOnEachLockCall) {
contended.join();
lock.unlock();
}
+
+TEST_F(TargetAPIMutexTargetTest,
+ UnlockReplaysLockResolutionAcrossPolicyChange) {
+ // Regression test for the cross-thread bypass bug: lock() and unlock()
+ // must agree on which mutex they touch even if the calling thread's
+ // policy changes in between, because unlock() replays lock()'s
+ // resolution rather than re-resolving from the current policy.
+ TargetSP target_sp = CreateTarget();
+ ASSERT_TRUE(target_sp);
+
+ TargetAPIMutex lock(target_sp);
+ lock.lock();
+
+ // Simulate the calling thread now running inside a scripted-extension
+ // callback -- if unlock() re-resolved here, it would see the bypass
+ // and skip releasing the mutex it actually locked.
+ {
+ PolicyStack::Guard guard = PolicyStack::Get().PushScriptedExtensionCall();
+ lock.unlock();
+ }
+
+ // The real mutex must have actually been released: a fresh
+ // acquisition (outside the bypass policy) must succeed immediately.
+ TargetAPIMutex other_lock(target_sp);
+ EXPECT_TRUE(other_lock.try_lock());
+}
+
+TEST_F(TargetAPIMutexTargetTest, BypassPolicyMakesTryLockANoOp) {
+ TargetSP target_sp = CreateTarget();
+ ASSERT_TRUE(target_sp);
+
+ TargetAPIMutex outer_lock(target_sp);
+ outer_lock.lock();
+
+ // A handle resolved while the bypass policy is active never touches
+ // the real (already-held) mutex, so it succeeds even though the real
+ // mutex is contended.
+ PolicyStack::Guard guard = PolicyStack::Get().PushScriptedExtensionCall();
+ TargetAPIMutex lock(target_sp);
+ EXPECT_TRUE(lock.try_lock());
+ lock.unlock();
+}
diff --git a/lldb/unittests/Utility/PolicyTest.cpp b/lldb/unittests/Utility/PolicyTest.cpp
index 5ad045a03d30b..56edfb68f6855 100644
--- a/lldb/unittests/Utility/PolicyTest.cpp
+++ b/lldb/unittests/Utility/PolicyTest.cpp
@@ -70,6 +70,17 @@ TEST(PolicyTest, PublicStateRunningExpression) {
EXPECT_TRUE(p.capabilities.can_run_frame_recognizers);
}
+TEST(PolicyTest, ScriptedExtensionCall) {
+ Policy p = Policy::CreateScriptedExtensionCall();
+ EXPECT_TRUE(p.capabilities.can_bypass_target_api_mutex);
+
+ // Inherits the current view/capabilities rather than resetting them.
+ PolicyStack::Guard guard = PolicyStack::Get().PushPrivateState();
+ Policy nested = Policy::CreateScriptedExtensionCall();
+ EXPECT_EQ(nested.view, Policy::View::Private);
+ EXPECT_TRUE(nested.capabilities.can_bypass_target_api_mutex);
+}
+
TEST(PolicyTest, StackDefaultIsPublicState) {
Policy current = PolicyStack::Get().Current();
EXPECT_EQ(current.view, Policy::View::Public);
@@ -145,7 +156,8 @@ TEST(PolicyTest, DumpPublicState) {
EXPECT_EQ(s.GetString(),
"policy: view=public, capabilities={"
"eval_expr=true run_all=true try_all=true "
- "bp_actions=true frame_providers=true frame_recognizers=true}");
+ "bp_actions=true frame_providers=true frame_recognizers=true "
+ "bypass_api_mutex=false}");
}
TEST(PolicyTest, DumpPrivateState) {
@@ -154,7 +166,8 @@ TEST(PolicyTest, DumpPrivateState) {
EXPECT_EQ(s.GetString(),
"policy: view=private, capabilities={"
"eval_expr=true run_all=true try_all=true "
- "bp_actions=true frame_providers=true frame_recognizers=true}");
+ "bp_actions=true frame_providers=true frame_recognizers=true "
+ "bypass_api_mutex=false}");
}
TEST(PolicyTest, DumpStack) {
>From 9d74e0e48c4b9c0d75cc6fec502e366afe884a3f Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <ismail at bennani.ma>
Date: Sat, 11 Jul 2026 17:47:30 -0700
Subject: [PATCH 3/3] [lldb] Fix self-deadlock from frame identity aliasing in
scripted frame providers
ScriptedFrameProvider::GetFrameAtIndex reused the parent list's live
StackFrame object directly whenever a provider's get_frame_at_index
forwarded a frame under its own index (real_frame_index == idx), instead
of wrapping it in a BorrowedStackFrame like every other forwarding case.
SyntheticStackFrameList::FetchFramesUpTo unconditionally re-tags the
returned frame's m_frame_list_id to this (child) list before caching it.
Reusing the parent list's object means this re-tag corrupts the parent
list's own cached frame to claim it belongs to the child list instead.
Later, resolving that frame's identity back to a frame list (e.g. via
ExecutionContextRef::GetFrameSP, used by SBFrame validity checks) follows
the corrupted tag to the child list. If the resolving thread is the one
already fetching frames on that child list -- holding its writer lock
via GetFramesUpTo -- it self-deadlocks trying to take the (non-reentrant)
reader lock on the same list.
Always wrap in BorrowedStackFrame, even when the index is unchanged, so
the object added to the child list is never the same object cached in
the parent list.
Adds a regression test using an identity-forwarding provider (mirroring
the shape that surfaced this bug) to isolate it from the API-mutex
deadlock fixed in #208242. Like that test, this is a best-effort race
reproduction, not a guaranteed one.
Depends on #208242, which this branch is stacked on top of.
Signed-off-by: Med Ismail Bennani <ismail at bennani.ma>
---
.../ScriptedFrameProvider.cpp | 11 ++-
.../Makefile | 3 +
...oviderRegisterCommandFrameAliasDeadlock.py | 94 +++++++++++++++++++
.../frame_provider.py | 30 ++++++
.../main.c | 7 ++
5 files changed, 141 insertions(+), 4 deletions(-)
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/Makefile
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/TestFrameProviderRegisterCommandFrameAliasDeadlock.py
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/frame_provider.py
create mode 100644 lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/main.c
diff --git a/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp b/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp
index ab09d86b24d95..977ea5e0548e4 100644
--- a/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp
+++ b/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp
@@ -179,10 +179,13 @@ ScriptedFrameProvider::GetFrameAtIndex(uint32_t idx) {
if (real_frame_index < m_input_frames->GetNumFrames()) {
StackFrameSP real_frame_sp =
m_input_frames->GetFrameAtIndex(real_frame_index);
- synth_frame_sp =
- (real_frame_index == idx)
- ? real_frame_sp
- : std::make_shared<BorrowedStackFrame>(real_frame_sp, idx);
+ // Always wrap in a BorrowedStackFrame, even when the index is
+ // unchanged. FetchFramesUpTo below unconditionally overwrites
+ // frame_sp->m_frame_list_id to tag the frame as belonging to this
+ // synthetic list; reusing real_frame_sp directly would corrupt the
+ // parent list's cached frame (still m_input_frames' object) to claim
+ // it belongs to this list instead.
+ synth_frame_sp = std::make_shared<BorrowedStackFrame>(real_frame_sp, idx);
}
} else if (StructuredData::Dictionary *dict = obj_sp->GetAsDictionary()) {
// Check if it's a dictionary describing a frame.
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/Makefile b/lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/Makefile
new file mode 100644
index 0000000000000..0b710c6e298ae
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+CFLAGS_EXTRAS := -std=c99
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/TestFrameProviderRegisterCommandFrameAliasDeadlock.py b/lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/TestFrameProviderRegisterCommandFrameAliasDeadlock.py
new file mode 100644
index 0000000000000..dee8bf01c7195
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/TestFrameProviderRegisterCommandFrameAliasDeadlock.py
@@ -0,0 +1,94 @@
+"""
+Test that a scripted frame provider that forwards frames under their own
+index (identity forwarding) does not deadlock when running `bt` from the
+command interpreter.
+
+ScriptedFrameProvider::GetFrameAtIndex reused the parent list's live
+StackFrame object directly whenever a provider forwarded a frame under
+its own index, instead of wrapping it in a BorrowedStackFrame. Frame
+construction unconditionally re-tags the returned frame as belonging to
+the child list, corrupting the parent list's cached frame. Once that
+frame's corrupted list identity is later resolved, it points back at
+the (possibly still-being-built) child list, and a thread already
+holding that list's writer lock can self-deadlock taking the reader
+lock.
+
+The event-handler thread that can trigger this only runs when commands
+are driven through SBDebugger.RunCommandInterpreter (what the lldb
+driver itself uses), not plain HandleCommand, so this test drives
+commands that way.
+
+Note: this is a genuine cross-thread race (the command thread vs. the
+debugger's event-handler thread), not a deterministic sequential
+deadlock, so this test is best-effort -- like the sibling
+runlock_reentrant_deadlock/was_hit_deadlock tests, it raises the odds of
+hitting the race within a single invocation but cannot guarantee it.
+"""
+
+import os
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class TestFrameProviderRegisterCommandFrameAliasDeadlock(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_register_command_then_bt_no_deadlock(self):
+ """
+ Register a scripted frame provider that identity-forwards every
+ frame, then repeatedly run `bt` through RunCommandInterpreter.
+ Should complete without deadlocking.
+ """
+ self.build()
+
+ lldbutil.run_to_name_breakpoint(self, "frame3")
+
+ provider_path = os.path.join(self.getSourceDir(), "frame_provider.py")
+
+ commands = ["command script import " + provider_path]
+ commands.append(
+ "target frame-provider register -C frame_provider.IdentityProvider"
+ )
+ # Run `bt` several times to raise the odds of hitting the race
+ # between the command thread and the debugger's event-handler
+ # thread within a single test invocation.
+ commands.extend(["bt"] * 20)
+ commands.append("quit")
+
+ stdin_path = self.getBuildArtifact("stdin.txt")
+ stdout_path = self.getBuildArtifact("stdout.txt")
+ with open(stdin_path, "w") as f:
+ f.write("\n".join(commands) + "\n")
+
+ with open(stdin_path, "r") as in_fileH, open(stdout_path, "w") as out_fileH:
+ in_sbf = lldb.SBFile(in_fileH.fileno(), "r", False)
+ out_sbf = lldb.SBFile(out_fileH.fileno(), "w", False)
+ self.assertSuccess(self.dbg.SetInputFile(in_sbf))
+ self.assertSuccess(self.dbg.SetOutputFile(out_sbf))
+ self.assertSuccess(self.dbg.SetErrorFile(out_sbf))
+
+ options = lldb.SBCommandInterpreterRunOptions()
+ options.SetEchoCommands(False)
+ options.SetPrintResults(True)
+ options.SetStopOnError(False)
+ options.SetStopOnCrash(False)
+
+ # If the frame-aliasing self-deadlock regresses, this call
+ # hangs forever (timing out the test run).
+ n_errors, quit_requested, has_crashed = self.dbg.RunCommandInterpreter(
+ True, False, options, 0, False, False
+ )
+
+ with open(stdout_path, "r") as out_fileH:
+ output = out_fileH.read()
+
+ self.assertFalse(has_crashed, "lldb should not have crashed")
+ self.assertTrue(quit_requested, "quit command should have been processed")
+ self.assertEqual(n_errors, 0, f"unexpected errors in output:\n{output}")
+
+ self.assertIn("successfully registered scripted frame provider", output)
+ self.assertIn("frame3", output)
+ self.assertIn("frame2", output)
+ self.assertIn("frame1", output)
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/frame_provider.py b/lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/frame_provider.py
new file mode 100644
index 0000000000000..e8ed77dc5a985
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/frame_provider.py
@@ -0,0 +1,30 @@
+"""
+Frame provider that forwards every frame under its own index while also
+touching input_frames.
+
+Returning `index` (identity forwarding) means the provider reuses the
+same StackFrame object that is cached in the parent (input) frame list
+instead of wrapping it in a BorrowedStackFrame. Frame construction
+unconditionally re-tags that frame as belonging to this (child) list,
+corrupting the parent list's cached frame. When that corrupted frame is
+later resolved back to a frame list, it resolves to this list -- which,
+if the resolving thread is the one already fetching frames on this list,
+self-deadlocks trying to take a reader lock on the writer lock it
+already holds.
+"""
+
+from lldb.plugins.scripted_frame_provider import ScriptedFrameProvider
+
+
+class IdentityProvider(ScriptedFrameProvider):
+ @staticmethod
+ def get_description():
+ return "Provider that forwards each frame under its own index"
+
+ def get_frame_at_index(self, index):
+ if index < len(self.input_frames):
+ # __getitem__ calls SBFrame.IsValid() internally, which is what
+ # exercises GetStoppedExecutionContext.
+ self.input_frames[index]
+ return index
+ return None
diff --git a/lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/main.c b/lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/main.c
new file mode 100644
index 0000000000000..1aa56e3eddf7a
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_frame_provider/register_command_frame_alias_deadlock/main.c
@@ -0,0 +1,7 @@
+int frame3() { return 3; }
+
+int frame2() { return frame3(); }
+
+int frame1() { return frame2(); }
+
+int main() { return frame1(); }
More information about the lldb-commits
mailing list