diff options
| author | Julian Lettner <yln@users.noreply.github.com> | 2025-10-24 17:08:25 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-24 17:08:25 -0700 |
| commit | 10a975be0f4c6337fe981c4086d90c582a970010 (patch) | |
| tree | 7ae4eea5bebdd1f90e49292a6df6fbd743c5498d /lldb/source/Target/Target.cpp | |
| parent | 409c6544435395ac24d3efb92fd51841e9223315 (diff) | |
[lldb] Introduce internal stop hooks (#164506)
Introduce the concept of internal stop hooks.
These are similar to LLDB's internal breakpoints:
LLDB itself will add them and users of LLDB will
not be able to add or remove them.
This change adds the following 3
independently-useful concepts:
* Maintain a list of internal stop hooks that will be populated by LLDB
and cannot be added to or removed from by users. They are managed in a
separate list in `Target::m_internal_stop_hooks`.
* `StopHookKind:CodeBased` and `StopHookCoded` represent a stop hook
defined by a C++ code callback (instead of command line expressions or a
Python class).
* Stop hooks that do not print any output can now also suppress the
printing of their header and description when they are hit via
`StopHook::GetSuppressOutput`.
Combining these 3 concepts we can model "internal
stop hooks" which serve the same function as
LLDB's internal breakpoints: executing built-in,
LLDB-defined behavior, leveraging the existing
mechanism of stop hooks.
This change also simplifies
`Target::RunStopHooks`. We already have to
materialize a new list for combining internal and
user stop hooks. Filter and only add active hooks to this list to avoid
the need for "isActive?"
checks later on.
Diffstat (limited to 'lldb/source/Target/Target.cpp')
| -rw-r--r-- | lldb/source/Target/Target.cpp | 77 |
1 files changed, 52 insertions, 25 deletions
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index e224a12e3346..d070c3d953d4 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -183,8 +183,8 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch, m_watchpoint_list(), m_process_sp(), m_search_filter_sp(), m_image_search_paths(ImageSearchPathsChanged, this), m_source_manager_up(), m_stop_hooks(), m_stop_hook_next_id(0), - m_latest_stop_hook_id(0), m_valid(true), m_suppress_stop_hooks(false), - m_is_dummy_target(is_dummy_target), + m_internal_stop_hooks(), m_latest_stop_hook_id(0), m_valid(true), + m_suppress_stop_hooks(false), m_is_dummy_target(is_dummy_target), m_target_unique_id(g_target_unique_id++), m_frame_recognizer_manager_up( std::make_unique<StackFrameRecognizerManager>()) { @@ -217,6 +217,7 @@ Target::~Target() { void Target::PrimeFromDummyTarget(Target &target) { m_stop_hooks = target.m_stop_hooks; m_stop_hook_next_id = target.m_stop_hook_next_id; + m_internal_stop_hooks = target.m_internal_stop_hooks; for (const auto &breakpoint_sp : target.m_breakpoint_list.Breakpoints()) { if (breakpoint_sp->IsInternal()) @@ -383,6 +384,7 @@ void Target::Destroy() { m_image_search_paths.Clear(notify); m_stop_hooks.clear(); m_stop_hook_next_id = 0; + m_internal_stop_hooks.clear(); m_suppress_stop_hooks = false; m_repl_map.clear(); Args signal_args; @@ -3041,8 +3043,9 @@ SourceManager &Target::GetSourceManager() { return *m_source_manager_up; } -Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind) { - lldb::user_id_t new_uid = ++m_stop_hook_next_id; +Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind, + bool internal) { + user_id_t new_uid = (internal ? LLDB_INVALID_UID : ++m_stop_hook_next_id); Target::StopHookSP stop_hook_sp; switch (kind) { case StopHook::StopHookKind::CommandBased: @@ -3051,8 +3054,14 @@ Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind) { case StopHook::StopHookKind::ScriptBased: stop_hook_sp.reset(new StopHookScripted(shared_from_this(), new_uid)); break; + case StopHook::StopHookKind::CodeBased: + stop_hook_sp.reset(new StopHookCoded(shared_from_this(), new_uid)); + break; } - m_stop_hooks[new_uid] = stop_hook_sp; + if (internal) + m_internal_stop_hooks.push_back(stop_hook_sp); + else + m_stop_hooks[new_uid] = stop_hook_sp; return stop_hook_sp; } @@ -3098,6 +3107,23 @@ void Target::SetAllStopHooksActiveState(bool active_state) { } } +// FIXME: Ideally we would like to return a `const &` (const reference) instead +// of creating copy here, but that is not possible due to different container +// types. In C++20, we should be able to use `std::ranges::views::values` to +// adapt the key-pair entries in the `std::map` (behind `StopHookCollection`) +// to avoid creating the copy. +const std::vector<Target::StopHookSP> +Target::GetStopHooks(bool internal) const { + if (internal) + return m_internal_stop_hooks; + + std::vector<StopHookSP> stop_hooks; + for (auto &[_, hook] : m_stop_hooks) + stop_hooks.push_back(hook); + + return stop_hooks; +} + bool Target::RunStopHooks(bool at_initial_stop) { if (m_suppress_stop_hooks) return false; @@ -3111,16 +3137,20 @@ bool Target::RunStopHooks(bool at_initial_stop) { if (!(state == eStateStopped || state == eStateAttaching)) return false; - if (m_stop_hooks.empty()) - return false; + auto is_active = [at_initial_stop](StopHookSP hook) { + bool should_run_now = (!at_initial_stop || hook->GetRunAtInitialStop()); + return hook->IsActive() && should_run_now; + }; - bool no_active_hooks = - llvm::none_of(m_stop_hooks, [at_initial_stop](auto &p) { - bool should_run_now = - !at_initial_stop || p.second->GetRunAtInitialStop(); - return p.second->IsActive() && should_run_now; - }); - if (no_active_hooks) + // Create list of active internal and user stop hooks. + std::vector<StopHookSP> active_hooks; + llvm::copy_if(m_internal_stop_hooks, std::back_inserter(active_hooks), + is_active); + for (auto &[_, hook] : m_stop_hooks) { + if (is_active(hook)) + active_hooks.push_back(hook); + } + if (active_hooks.empty()) return false; // Make sure we check that we are not stopped because of us running a user @@ -3169,24 +3199,21 @@ bool Target::RunStopHooks(bool at_initial_stop) { StreamSP output_sp = m_debugger.GetAsyncOutputStream(); auto on_exit = llvm::make_scope_exit([output_sp] { output_sp->Flush(); }); - bool print_hook_header = (m_stop_hooks.size() != 1); - bool print_thread_header = (num_exe_ctx != 1); + size_t num_hooks_with_output = llvm::count_if( + active_hooks, [](auto h) { return !h->GetSuppressOutput(); }); + bool print_hook_header = (num_hooks_with_output > 1); + bool print_thread_header = (num_exe_ctx > 1); bool should_stop = false; bool requested_continue = false; - for (auto stop_entry : m_stop_hooks) { - StopHookSP cur_hook_sp = stop_entry.second; - if (!cur_hook_sp->IsActive()) - continue; - if (at_initial_stop && !cur_hook_sp->GetRunAtInitialStop()) - continue; - + for (auto cur_hook_sp : active_hooks) { bool any_thread_matched = false; for (auto exc_ctx : exc_ctx_with_reasons) { if (!cur_hook_sp->ExecutionContextPasses(exc_ctx)) continue; - if (print_hook_header && !any_thread_matched) { + bool suppress_output = cur_hook_sp->GetSuppressOutput(); + if (print_hook_header && !any_thread_matched && !suppress_output) { StreamString s; cur_hook_sp->GetDescription(s, eDescriptionLevelBrief); if (s.GetSize() != 0) @@ -3197,7 +3224,7 @@ bool Target::RunStopHooks(bool at_initial_stop) { any_thread_matched = true; } - if (print_thread_header) + if (print_thread_header && !suppress_output) output_sp->Printf("-- Thread %d\n", exc_ctx.GetThreadPtr()->GetIndexID()); |
