https://github.com/medismailben updated https://github.com/llvm/llvm-project/pull/195762
>From 53a4804a64912627bb46f9d0cbdce5362100ff11 Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <[email protected]> Date: Tue, 5 May 2026 13:01:03 -0700 Subject: [PATCH] [lldb] Add Policy infrastructure Add a generic thread-local policy stack and a Policy struct that describes what view of the process a thread should see (private reality vs public illusion) and what operations it is allowed to perform. This is the infrastructure for replacing ad-hoc host thread identity checks (CurrentThreadIsPrivateStateThread, IsOnThread, etc.) with a unified, composable mechanism. No behavioral changes yet -- adoption will follow in subsequent patches. rdar://176223894 Signed-off-by: Med Ismail Bennani <[email protected]> --- lldb/include/lldb/Target/Policy.h | 114 +++++++++++++++++++++++++ lldb/unittests/Target/CMakeLists.txt | 1 + lldb/unittests/Target/PolicyTest.cpp | 122 +++++++++++++++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 lldb/include/lldb/Target/Policy.h create mode 100644 lldb/unittests/Target/PolicyTest.cpp diff --git a/lldb/include/lldb/Target/Policy.h b/lldb/include/lldb/Target/Policy.h new file mode 100644 index 0000000000000..36084a1fc2cb7 --- /dev/null +++ b/lldb/include/lldb/Target/Policy.h @@ -0,0 +1,114 @@ +//===----------------------------------------------------------------------===// +// +// 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_POLICY_H +#define LLDB_TARGET_POLICY_H + +#include "llvm/ADT/SmallVector.h" + +#include <cassert> + +namespace lldb_private { + +/// Describes what view of the process a thread should see and what +/// operations it is allowed to perform. +/// +/// Frame providers are a public illusion layered on top of the private +/// reality (the unwinder stack). The private state thread manages the +/// stop of that private reality, so the correct view for its logic IS the +/// private reality. The public illusion is only applied once the process +/// has settled and clients query the stopped state. +/// +/// This struct is a pure data store -- no business logic. It is pushed +/// onto a per-thread stack (PolicyStack) so that code +/// can consult the current policy instead of comparing host thread +/// identities. +struct Policy { + /// What view of the process this thread sees. + enum class View { + Public, ///< Provider-augmented frames, public state, public run lock. + Private, ///< Parent (unwinder) frames, private state, private run lock. + }; + + /// What operations this thread is allowed to perform. + /// Enforced at specific callsites, not by the policy itself. + struct Capabilities { + bool can_evaluate_expressions = true; + /// Whether expression evaluation may resume all threads to avoid + /// deadlocks (e.g. when a lock is held by another thread). + bool can_run_all_threads = true; + /// Whether the expression runner may fall back to running all threads + /// after a single-thread attempt times out. + bool can_try_all_threads = true; + bool can_run_breakpoint_actions = true; + bool can_load_frame_providers = true; + bool can_run_frame_recognizers = true; + }; + + View view = View::Public; + Capabilities capabilities; + + static Policy PublicState() { return {}; } + + static Policy PrivateState() { + Policy p; + p.view = View::Private; + p.capabilities.can_load_frame_providers = false; + p.capabilities.can_run_frame_recognizers = false; + return p; + } + + static Policy PublicStateRunningExpression() { + Policy p; + p.capabilities.can_run_breakpoint_actions = false; + return p; + } +}; + +/// Per-thread policy stack. +/// +/// The stack lives in thread_local storage. Each thread has its own stack, +/// initialized with a default-constructed base entry that is never popped. +/// RAII guards (Guard) push and pop policies. +/// +/// For thread pool workers that don't inherit thread_local storage, the +/// policy must be passed into the lambda and pushed onto the worker +/// thread's stack when the task starts. +class PolicyStack { +public: + static PolicyStack &GetForCurrentThread() { + static thread_local PolicyStack s_stack; + return s_stack; + } + + Policy Current() const { return m_stack.back(); } + + void Push(Policy policy) { m_stack.push_back(std::move(policy)); } + + void Pop() { + assert(!m_stack.empty() && "can't pop the last policy"); + m_stack.pop_back(); + } + + /// RAII guard that pushes a policy on construction and pops on destruction. + class Guard { + public: + explicit Guard(Policy policy) { GetForCurrentThread().Push(policy); } + ~Guard() { GetForCurrentThread().Pop(); } + + Guard(const Guard &) = delete; + Guard &operator=(const Guard &) = delete; + }; + +private: + llvm::SmallVector<Policy> m_stack = {Policy{}}; +}; + +} // namespace lldb_private + +#endif // LLDB_TARGET_POLICY_H diff --git a/lldb/unittests/Target/CMakeLists.txt b/lldb/unittests/Target/CMakeLists.txt index bf08a8f015ba0..c4578e2fbfc77 100644 --- a/lldb/unittests/Target/CMakeLists.txt +++ b/lldb/unittests/Target/CMakeLists.txt @@ -9,6 +9,7 @@ add_lldb_unittest(TargetTests MemoryTagMapTest.cpp ModuleCacheTest.cpp PathMappingListTest.cpp + PolicyTest.cpp RegisterFlagsTest.cpp RemoteAwarePlatformTest.cpp ScratchTypeSystemTest.cpp diff --git a/lldb/unittests/Target/PolicyTest.cpp b/lldb/unittests/Target/PolicyTest.cpp new file mode 100644 index 0000000000000..e1291d185eb21 --- /dev/null +++ b/lldb/unittests/Target/PolicyTest.cpp @@ -0,0 +1,122 @@ +//===-- PolicyTest.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/Policy.h" +#include "gtest/gtest.h" + +#include <thread> + +using namespace lldb_private; + +TEST(PolicyTest, DefaultIsPublicWithAllCapabilities) { + Policy p; + EXPECT_EQ(p.view, Policy::View::Public); + EXPECT_TRUE(p.capabilities.can_evaluate_expressions); + EXPECT_TRUE(p.capabilities.can_run_all_threads); + EXPECT_TRUE(p.capabilities.can_try_all_threads); + EXPECT_TRUE(p.capabilities.can_run_breakpoint_actions); + EXPECT_TRUE(p.capabilities.can_load_frame_providers); + EXPECT_TRUE(p.capabilities.can_run_frame_recognizers); +} + +TEST(PolicyTest, PublicState) { + Policy p = Policy::PublicState(); + EXPECT_EQ(p.view, Policy::View::Public); + EXPECT_TRUE(p.capabilities.can_evaluate_expressions); + EXPECT_TRUE(p.capabilities.can_run_all_threads); + EXPECT_TRUE(p.capabilities.can_try_all_threads); + EXPECT_TRUE(p.capabilities.can_run_breakpoint_actions); + EXPECT_TRUE(p.capabilities.can_load_frame_providers); + EXPECT_TRUE(p.capabilities.can_run_frame_recognizers); +} + +TEST(PolicyTest, PrivateState) { + Policy p = Policy::PrivateState(); + EXPECT_EQ(p.view, Policy::View::Private); + EXPECT_TRUE(p.capabilities.can_evaluate_expressions); + EXPECT_TRUE(p.capabilities.can_run_all_threads); + EXPECT_TRUE(p.capabilities.can_try_all_threads); + EXPECT_TRUE(p.capabilities.can_run_breakpoint_actions); + EXPECT_FALSE(p.capabilities.can_load_frame_providers); + EXPECT_FALSE(p.capabilities.can_run_frame_recognizers); +} + +TEST(PolicyTest, PublicStateRunningExpression) { + Policy p = Policy::PublicStateRunningExpression(); + EXPECT_EQ(p.view, Policy::View::Public); + EXPECT_TRUE(p.capabilities.can_evaluate_expressions); + EXPECT_TRUE(p.capabilities.can_run_all_threads); + EXPECT_TRUE(p.capabilities.can_try_all_threads); + EXPECT_FALSE(p.capabilities.can_run_breakpoint_actions); + EXPECT_TRUE(p.capabilities.can_load_frame_providers); + EXPECT_TRUE(p.capabilities.can_run_frame_recognizers); +} + +TEST(PolicyTest, StackDefaultIsPublicState) { + PolicyStack &stack = PolicyStack::GetForCurrentThread(); + Policy current = stack.Current(); + EXPECT_EQ(current.view, Policy::View::Public); + EXPECT_TRUE(current.capabilities.can_evaluate_expressions); + EXPECT_TRUE(current.capabilities.can_load_frame_providers); +} + +TEST(PolicyTest, StackPushPop) { + PolicyStack &stack = PolicyStack::GetForCurrentThread(); + + stack.Push(Policy::PrivateState()); + EXPECT_EQ(stack.Current().view, Policy::View::Private); + EXPECT_FALSE(stack.Current().capabilities.can_load_frame_providers); + + stack.Push(Policy::PublicStateRunningExpression()); + EXPECT_EQ(stack.Current().view, Policy::View::Public); + EXPECT_FALSE(stack.Current().capabilities.can_run_breakpoint_actions); + + stack.Pop(); + EXPECT_EQ(stack.Current().view, Policy::View::Private); + + stack.Pop(); + EXPECT_EQ(stack.Current().view, Policy::View::Public); +} + +TEST(PolicyTest, GuardRAII) { + PolicyStack &stack = PolicyStack::GetForCurrentThread(); + EXPECT_EQ(stack.Current().view, Policy::View::Public); + + { + PolicyStack::Guard guard(Policy::PrivateState()); + EXPECT_EQ(stack.Current().view, Policy::View::Private); + EXPECT_FALSE(stack.Current().capabilities.can_load_frame_providers); + + { + PolicyStack::Guard inner(Policy::PublicStateRunningExpression()); + EXPECT_EQ(stack.Current().view, Policy::View::Public); + EXPECT_FALSE(stack.Current().capabilities.can_run_breakpoint_actions); + } + + EXPECT_EQ(stack.Current().view, Policy::View::Private); + } + + EXPECT_EQ(stack.Current().view, Policy::View::Public); +} + +TEST(PolicyTest, StackIsPerThread) { + PolicyStack &main_stack = PolicyStack::GetForCurrentThread(); + main_stack.Push(Policy::PrivateState()); + + Policy::View other_thread_view; + std::thread t([&other_thread_view]() { + PolicyStack &stack = PolicyStack::GetForCurrentThread(); + other_thread_view = stack.Current().view; + }); + t.join(); + + EXPECT_EQ(main_stack.Current().view, Policy::View::Private); + EXPECT_EQ(other_thread_view, Policy::View::Public); + + main_stack.Pop(); +} _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
