https://github.com/medismailben updated https://github.com/llvm/llvm-project/pull/195765
>From 4a2a333834be9984f5c5d156819ec75ae2324226 Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <[email protected]> Date: Mon, 4 May 2026 17:30:02 -0700 Subject: [PATCH 1/2] [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 | 78 +++++++++++++++++++ .../lldb/Utility/ThreadLocalPolicyStack.h | 57 ++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 lldb/include/lldb/Target/Policy.h create mode 100644 lldb/include/lldb/Utility/ThreadLocalPolicyStack.h diff --git a/lldb/include/lldb/Target/Policy.h b/lldb/include/lldb/Target/Policy.h new file mode 100644 index 0000000000000..93bd5c39380f3 --- /dev/null +++ b/lldb/include/lldb/Target/Policy.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_POLICY_H +#define LLDB_TARGET_POLICY_H + +#include "lldb/Utility/ThreadLocalPolicyStack.h" + +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 : 1; + bool stop_others_only : 1; + bool can_try_all_threads : 1; + bool can_run_breakpoint_actions : 1; + bool can_load_frame_providers : 1; + bool can_run_frame_recognizers : 1; + }; + + View view = View::Public; + Capabilities capabilities = { + true, // can_evaluate_expressions + false, // stop_others_only + true, // can_try_all_threads + true, // can_run_breakpoint_actions + true, // can_load_frame_providers + true, // can_run_frame_recognizers + }; + + 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; + } +}; + +using PolicyStack = ThreadLocalPolicyStack<Policy>; + +} // namespace lldb_private + +#endif // LLDB_TARGET_POLICY_H diff --git a/lldb/include/lldb/Utility/ThreadLocalPolicyStack.h b/lldb/include/lldb/Utility/ThreadLocalPolicyStack.h new file mode 100644 index 0000000000000..d24b23a34a583 --- /dev/null +++ b/lldb/include/lldb/Utility/ThreadLocalPolicyStack.h @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// 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_UTILITY_THREADLOCALPOLICYSTACK_H +#define LLDB_UTILITY_THREADLOCALPOLICYSTACK_H + +#include <vector> + +namespace lldb_private { + +/// Generic 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. +template <typename Policy> class ThreadLocalPolicyStack { +public: + static ThreadLocalPolicyStack &GetForCurrentThread() { + static thread_local ThreadLocalPolicyStack s_stack; + return s_stack; + } + + const Policy &Current() const { return m_stack.back(); } + + void Push(Policy policy) { m_stack.push_back(policy); } + + void Pop() { + if (m_stack.size() > 1) + m_stack.pop_back(); + } + + /// RAII guard that pushes a policy on construction and pops on destruction. + class Guard { + public: + Guard(Policy policy) { GetForCurrentThread().Push(policy); } + ~Guard() { GetForCurrentThread().Pop(); } + + Guard(const Guard &) = delete; + Guard &operator=(const Guard &) = delete; + }; + +private: + std::vector<Policy> m_stack = {Policy{}}; +}; + +} // namespace lldb_private + +#endif // LLDB_UTILITY_THREADLOCALPOLICYSTACK_H >From d564deb8dc56de71a3e4337a7f1668de6968ce3f Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani <[email protected]> Date: Mon, 4 May 2026 18:16:18 -0700 Subject: [PATCH 2/2] [lldb] Adopt Policy for private state thread identification Replace the ad-hoc PrivateStateThreadGuard (a thread_local bool with RAII wrapper) with the new Policy infrastructure. RunThreadPlan and RunPrivateStateThread now push Policy::PrivateState() onto the per-thread policy stack. GetStackFrameList checks the policy's view field instead of PrivateStateThreadGuard::IsPrivateStateThread(). This is a strict NFC refactor -- the same decisions are made, the same code paths are taken. The PrivateStateThreadGuard struct is removed from Process.h. rdar://176223894 Signed-off-by: Med Ismail Bennani <[email protected]> --- lldb/include/lldb/Target/Process.h | 22 ---------------------- lldb/source/Target/Process.cpp | 13 ++++++------- lldb/source/Target/Thread.cpp | 16 +++++++++------- 3 files changed, 15 insertions(+), 36 deletions(-) diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 67a9a883cd36c..eb6de7876ed42 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -3719,28 +3719,6 @@ class UtilityFunctionScope { } }; -/// RAII guard that marks the current thread as a private state thread. -/// -/// When RunThreadPlan detects it is running on the private state thread, it -/// spins up an override thread and reassigns m_current_private_state_thread_sp -/// to it. The original PST continues processing events via DoOnRemoval -/// callbacks, but CurrentThreadIsPrivateStateThread() no longer recognizes it. -/// This guard sets a thread_local flag so that GetStackFrameList can identify -/// the original PST and return parent frames instead of provider-augmented -/// frames. -struct PrivateStateThreadGuard { - PrivateStateThreadGuard() { g_is_private_state_thread = true; } - ~PrivateStateThreadGuard() { g_is_private_state_thread = false; } - static bool IsPrivateStateThread() { return g_is_private_state_thread; } - - // Non-copyable, non-movable. - PrivateStateThreadGuard(const PrivateStateThreadGuard &) = delete; - PrivateStateThreadGuard &operator=(const PrivateStateThreadGuard &) = delete; - -private: - static thread_local bool g_is_private_state_thread; -}; - } // namespace lldb_private #endif // LLDB_TARGET_PROCESS_H diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 114bbd7355f0c..8e12a0db321ba 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -52,6 +52,7 @@ #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/OperatingSystem.h" #include "lldb/Target/Platform.h" +#include "lldb/Target/Policy.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" @@ -4320,14 +4321,12 @@ Status Process::HaltPrivate() { return error; } -thread_local bool PrivateStateThreadGuard::g_is_private_state_thread = false; - thread_result_t Process::RunPrivateStateThread(bool is_override) { // Override PSTs exist solely to service RunThreadPlan expression evaluation. // They must see parent frames, not provider-augmented frames. - std::optional<PrivateStateThreadGuard> pst_guard; + std::optional<PolicyStack::Guard> policy_guard; if (is_override) - pst_guard.emplace(); + policy_guard.emplace(Policy::PrivateState()); bool control_only = true; @@ -5525,9 +5524,9 @@ Process::RunThreadPlan(ExecutionContext &exe_ctx, // If we spawned an override PST, mark the current (original) PST so // GetStackFrameList returns parent frames during event processing. - std::optional<PrivateStateThreadGuard> private_state_thread_guard; + std::optional<PolicyStack::Guard> policy_guard; if (backup_private_state_thread) - private_state_thread_guard.emplace(); + policy_guard.emplace(Policy::PrivateState()); while (true) { // We usually want to resume the process if we get to the top of the @@ -5929,7 +5928,7 @@ Process::RunThreadPlan(ExecutionContext &exe_ctx, } } // END WAIT LOOP - private_state_thread_guard.reset(); + policy_guard.reset(); // If we had to start up a temporary private state thread to run this // thread plan, shut it down now. diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index 70053b8822bdc..b37c5c9313ef2 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -24,6 +24,7 @@ #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Policy.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/ScriptedThreadPlan.h" @@ -1533,7 +1534,7 @@ StackFrameListSP Thread::GetStackFrameList() { // thread that is already mid-construction. // // 2. Current thread is a private state thread that should see the - // private reality (PrivateStateThreadGuard::IsPrivateStateThread). + // private reality (Policy::View::Private). // // For case 1, if a provider is active we return its input (parent) // frames. For case (2), we return/create the unwinder frame list @@ -1559,12 +1560,13 @@ StackFrameListSP Thread::GetStackFrameList() { return m_curr_frames_sp; // For case 2, PST must see the private reality, not the public illusion. - // PrivateStateThreadGuard is a thread_local flag set by RunThreadPlan - // (for the original PST) and RunPrivateStateThread (for override PSTs). - // We cannot use CurrentThreadIsPrivateStateThread() here because - // RunThreadPlan reassigns m_current_private_state_thread_sp to the - // override, so the original PST is no longer recognized. - if (PrivateStateThreadGuard::IsPrivateStateThread()) { + // Policy is pushed by RunThreadPlan (for the original PST) + // and RunPrivateStateThread (for override PSTs). We cannot use + // CurrentThreadIsPrivateStateThread() here because RunThreadPlan reassigns + // m_current_private_state_thread_sp to the override, so the original PST + // is no longer recognized. + auto &policy = PolicyStack::GetForCurrentThread().Current(); + if (policy.view == Policy::View::Private) { if (!m_unwinder_frames_sp) m_unwinder_frames_sp = std::make_shared<StackFrameList>( *this, m_prev_frames_sp, true, /*provider_id=*/0); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
