Skip to content

Commit 5f6caec

Browse files
committed
[lldb] Introduce internal stop hooks
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.
1 parent c5b53b1 commit 5f6caec

File tree

2 files changed

+91
-32
lines changed

2 files changed

+91
-32
lines changed

lldb/include/lldb/Target/Target.h

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,7 +1356,11 @@ class Target : public std::enable_shared_from_this<Target>,
13561356
StopHook(const StopHook &rhs);
13571357
virtual ~StopHook() = default;
13581358

1359-
enum class StopHookKind : uint32_t { CommandBased = 0, ScriptBased };
1359+
enum class StopHookKind : uint32_t {
1360+
CommandBased = 0,
1361+
ScriptBased,
1362+
CodeBased,
1363+
};
13601364
enum class StopHookResult : uint32_t {
13611365
KeepStopped = 0,
13621366
RequestContinue,
@@ -1403,6 +1407,12 @@ class Target : public std::enable_shared_from_this<Target>,
14031407

14041408
bool GetRunAtInitialStop() const { return m_at_initial_stop; }
14051409

1410+
void SetSuppressOutput(bool suppress_output) {
1411+
m_suppress_output = suppress_output;
1412+
}
1413+
1414+
bool GetSuppressOutput() const { return m_suppress_output; }
1415+
14061416
void GetDescription(Stream &s, lldb::DescriptionLevel level) const;
14071417
virtual void GetSubclassDescription(Stream &s,
14081418
lldb::DescriptionLevel level) const = 0;
@@ -1414,6 +1424,7 @@ class Target : public std::enable_shared_from_this<Target>,
14141424
bool m_active = true;
14151425
bool m_auto_continue = false;
14161426
bool m_at_initial_stop = true;
1427+
bool m_suppress_output = false;
14171428

14181429
StopHook(lldb::TargetSP target_sp, lldb::user_id_t uid);
14191430
};
@@ -1433,7 +1444,7 @@ class Target : public std::enable_shared_from_this<Target>,
14331444

14341445
private:
14351446
StringList m_commands;
1436-
// Use CreateStopHook to make a new empty stop hook. The GetCommandPointer
1447+
// Use CreateStopHook to make a new empty stop hook. The SetActionFromString
14371448
// and fill it with commands, and SetSpecifier to set the specifier shared
14381449
// pointer (can be null, that will match anything.)
14391450
StopHookCommandLine(lldb::TargetSP target_sp, lldb::user_id_t uid)
@@ -1460,19 +1471,56 @@ class Target : public std::enable_shared_from_this<Target>,
14601471
StructuredDataImpl m_extra_args;
14611472
lldb::ScriptedStopHookInterfaceSP m_interface_sp;
14621473

1463-
/// Use CreateStopHook to make a new empty stop hook. The GetCommandPointer
1464-
/// and fill it with commands, and SetSpecifier to set the specifier shared
1465-
/// pointer (can be null, that will match anything.)
1474+
/// Use CreateStopHook to make a new empty stop hook. Use SetScriptCallback
1475+
/// to set the script to execute, and SetSpecifier to set the specifier
1476+
/// shared pointer (can be null, that will match anything.)
14661477
StopHookScripted(lldb::TargetSP target_sp, lldb::user_id_t uid)
14671478
: StopHook(target_sp, uid) {}
14681479
friend class Target;
14691480
};
14701481

1482+
class StopHookCoded : public StopHook {
1483+
public:
1484+
~StopHookCoded() override = default;
1485+
1486+
using HandleStopCallback = StopHookResult(ExecutionContext &exc_ctx,
1487+
lldb::StreamSP output);
1488+
1489+
void SetCallback(llvm::StringRef name, HandleStopCallback *callback) {
1490+
m_name = name;
1491+
m_callback = callback;
1492+
}
1493+
1494+
StopHookResult HandleStop(ExecutionContext &exc_ctx,
1495+
lldb::StreamSP output) override {
1496+
return m_callback(exc_ctx, output);
1497+
}
1498+
1499+
void GetSubclassDescription(Stream &s,
1500+
lldb::DescriptionLevel level) const override {
1501+
s.Indent();
1502+
s.Printf("%s (built-in)\n", m_name.c_str());
1503+
}
1504+
1505+
private:
1506+
std::string m_name;
1507+
HandleStopCallback *m_callback;
1508+
1509+
/// Use CreateStopHook to make a new empty stop hook. Use SetCallback to set
1510+
/// the callback to execute, and SetSpecifier to set the specifier shared
1511+
/// pointer (can be null, that will match anything.)
1512+
StopHookCoded(lldb::TargetSP target_sp, lldb::user_id_t uid)
1513+
: StopHook(target_sp, uid) {}
1514+
friend class Target;
1515+
};
1516+
1517+
void RegisterInternalStopHooks();
1518+
14711519
typedef std::shared_ptr<StopHook> StopHookSP;
14721520

14731521
/// Add an empty stop hook to the Target's stop hook list, and returns a
1474-
/// shared pointer to it in new_hook. Returns the id of the new hook.
1475-
StopHookSP CreateStopHook(StopHook::StopHookKind kind);
1522+
/// shared pointer to it in new_hook.
1523+
StopHookSP CreateStopHook(StopHook::StopHookKind kind, bool internal = false);
14761524

14771525
/// If you tried to create a stop hook, and that failed, call this to
14781526
/// remove the stop hook, as it will also reset the stop hook counter.
@@ -1656,6 +1704,7 @@ class Target : public std::enable_shared_from_this<Target>,
16561704
typedef std::map<lldb::user_id_t, StopHookSP> StopHookCollection;
16571705
StopHookCollection m_stop_hooks;
16581706
lldb::user_id_t m_stop_hook_next_id;
1707+
std::vector<StopHookSP> m_internal_stop_hooks;
16591708
uint32_t m_latest_stop_hook_id; /// This records the last natural stop at
16601709
/// which we ran a stop-hook.
16611710
bool m_valid;

lldb/source/Target/Target.cpp

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch,
183183
m_watchpoint_list(), m_process_sp(), m_search_filter_sp(),
184184
m_image_search_paths(ImageSearchPathsChanged, this),
185185
m_source_manager_up(), m_stop_hooks(), m_stop_hook_next_id(0),
186-
m_latest_stop_hook_id(0), m_valid(true), m_suppress_stop_hooks(false),
187-
m_is_dummy_target(is_dummy_target),
186+
m_internal_stop_hooks(), m_latest_stop_hook_id(0), m_valid(true),
187+
m_suppress_stop_hooks(false), m_is_dummy_target(is_dummy_target),
188188
m_target_unique_id(g_target_unique_id++),
189189
m_frame_recognizer_manager_up(
190190
std::make_unique<StackFrameRecognizerManager>()) {
@@ -217,6 +217,7 @@ Target::~Target() {
217217
void Target::PrimeFromDummyTarget(Target &target) {
218218
m_stop_hooks = target.m_stop_hooks;
219219
m_stop_hook_next_id = target.m_stop_hook_next_id;
220+
m_internal_stop_hooks = target.m_internal_stop_hooks;
220221

221222
for (const auto &breakpoint_sp : target.m_breakpoint_list.Breakpoints()) {
222223
if (breakpoint_sp->IsInternal())
@@ -383,6 +384,7 @@ void Target::Destroy() {
383384
m_image_search_paths.Clear(notify);
384385
m_stop_hooks.clear();
385386
m_stop_hook_next_id = 0;
387+
m_internal_stop_hooks.clear();
386388
m_suppress_stop_hooks = false;
387389
m_repl_map.clear();
388390
Args signal_args;
@@ -3041,8 +3043,9 @@ SourceManager &Target::GetSourceManager() {
30413043
return *m_source_manager_up;
30423044
}
30433045

3044-
Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind) {
3045-
lldb::user_id_t new_uid = ++m_stop_hook_next_id;
3046+
Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind,
3047+
bool internal) {
3048+
user_id_t new_uid = (internal ? LLDB_INVALID_UID : ++m_stop_hook_next_id);
30463049
Target::StopHookSP stop_hook_sp;
30473050
switch (kind) {
30483051
case StopHook::StopHookKind::CommandBased:
@@ -3051,8 +3054,14 @@ Target::StopHookSP Target::CreateStopHook(StopHook::StopHookKind kind) {
30513054
case StopHook::StopHookKind::ScriptBased:
30523055
stop_hook_sp.reset(new StopHookScripted(shared_from_this(), new_uid));
30533056
break;
3057+
case StopHook::StopHookKind::CodeBased:
3058+
stop_hook_sp.reset(new StopHookCoded(shared_from_this(), new_uid));
3059+
break;
30543060
}
3055-
m_stop_hooks[new_uid] = stop_hook_sp;
3061+
if (internal)
3062+
m_internal_stop_hooks.push_back(stop_hook_sp);
3063+
else
3064+
m_stop_hooks[new_uid] = stop_hook_sp;
30563065
return stop_hook_sp;
30573066
}
30583067

@@ -3111,16 +3120,20 @@ bool Target::RunStopHooks(bool at_initial_stop) {
31113120
if (!(state == eStateStopped || state == eStateAttaching))
31123121
return false;
31133122

3114-
if (m_stop_hooks.empty())
3115-
return false;
3123+
auto is_active = [at_initial_stop](StopHookSP hook) {
3124+
bool should_run_now = (!at_initial_stop || hook->GetRunAtInitialStop());
3125+
return hook->IsActive() && should_run_now;
3126+
};
31163127

3117-
bool no_active_hooks =
3118-
llvm::none_of(m_stop_hooks, [at_initial_stop](auto &p) {
3119-
bool should_run_now =
3120-
!at_initial_stop || p.second->GetRunAtInitialStop();
3121-
return p.second->IsActive() && should_run_now;
3122-
});
3123-
if (no_active_hooks)
3128+
// Create list of active internal and user stop hooks.
3129+
std::vector<StopHookSP> active_hooks;
3130+
llvm::copy_if(m_internal_stop_hooks, std::back_inserter(active_hooks),
3131+
is_active);
3132+
for (auto &[_, hook] : m_stop_hooks) {
3133+
if (is_active(hook))
3134+
active_hooks.push_back(hook);
3135+
}
3136+
if (active_hooks.empty())
31243137
return false;
31253138

31263139
// Make sure we check that we are not stopped because of us running a user
@@ -3169,24 +3182,21 @@ bool Target::RunStopHooks(bool at_initial_stop) {
31693182
StreamSP output_sp = m_debugger.GetAsyncOutputStream();
31703183
auto on_exit = llvm::make_scope_exit([output_sp] { output_sp->Flush(); });
31713184

3172-
bool print_hook_header = (m_stop_hooks.size() != 1);
3173-
bool print_thread_header = (num_exe_ctx != 1);
3185+
size_t num_hooks_with_output = llvm::count_if(
3186+
active_hooks, [](auto h) { return !h->GetSuppressOutput(); });
3187+
bool print_hook_header = (num_hooks_with_output > 1);
3188+
bool print_thread_header = (num_exe_ctx > 1);
31743189
bool should_stop = false;
31753190
bool requested_continue = false;
31763191

3177-
for (auto stop_entry : m_stop_hooks) {
3178-
StopHookSP cur_hook_sp = stop_entry.second;
3179-
if (!cur_hook_sp->IsActive())
3180-
continue;
3181-
if (at_initial_stop && !cur_hook_sp->GetRunAtInitialStop())
3182-
continue;
3183-
3192+
for (auto cur_hook_sp : active_hooks) {
31843193
bool any_thread_matched = false;
31853194
for (auto exc_ctx : exc_ctx_with_reasons) {
31863195
if (!cur_hook_sp->ExecutionContextPasses(exc_ctx))
31873196
continue;
31883197

3189-
if (print_hook_header && !any_thread_matched) {
3198+
bool suppress_output = cur_hook_sp->GetSuppressOutput();
3199+
if (print_hook_header && !any_thread_matched && !suppress_output) {
31903200
StreamString s;
31913201
cur_hook_sp->GetDescription(s, eDescriptionLevelBrief);
31923202
if (s.GetSize() != 0)
@@ -3197,7 +3207,7 @@ bool Target::RunStopHooks(bool at_initial_stop) {
31973207
any_thread_matched = true;
31983208
}
31993209

3200-
if (print_thread_header)
3210+
if (print_thread_header && !suppress_output)
32013211
output_sp->Printf("-- Thread %d\n",
32023212
exc_ctx.GetThreadPtr()->GetIndexID());
32033213

0 commit comments

Comments
 (0)