-
Notifications
You must be signed in to change notification settings - Fork 14.3k
Add SBDebugger:: AddNotificationCallback API #111206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
6e84ab9
b23f3ea
b8ec0fb
1a350e4
f2d35f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -138,7 +138,10 @@ class LLDB_API SBUnixSignals; | |
typedef bool (*SBBreakpointHitCallback)(void *baton, lldb::SBProcess &process, | ||
lldb::SBThread &thread, | ||
lldb::SBBreakpointLocation &location); | ||
|
||
typedef void (*SBNotificationCallback)(lldb::NotificationType type, | ||
lldb::SBDebugger &, | ||
lldb::SBExecutionContext &exe_ctx, | ||
Comment on lines
+142
to
+143
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add accessors to the SBExecutionContext for accessing a debugger? We can modify the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's always been a little weird that ExecutionContext didn't have a debugger - though TTTT that only mattered for default constructed ones: so long as any of the other entities are filled in you can get to the debugger. |
||
void *baton); | ||
typedef void (*SBDebuggerDestroyCallback)(lldb::user_id_t debugger_id, | ||
void *baton); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -569,6 +569,18 @@ class Debugger : public std::enable_shared_from_this<Debugger>, | |
SetDestroyCallback(lldb_private::DebuggerDestroyCallback destroy_callback, | ||
void *baton); | ||
|
||
/// Add a notification callback when notification type event happens. Return a | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Read literally this says you are adding the notification callback when the event happens. Maybe: Add a notification callback that will trigger when an event of the given NotificationType occurs. You should also state when the creation and destruction callbacks trigger w.r.t. the action the event is reporting. I.e. Creation callbacks trigger synchronously after the given object is fully set up, and Destruction callbacks trigger right before the object is destroyed. |
||
/// token, which can be used to remove said callback. Multiple callbacks can | ||
/// be added by calling this function multiple times, and will be invoked in | ||
/// FIFO order. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is original_callback for? You should document this if it's needed. |
||
static lldb::callback_token_t AddNotificationCallback( | ||
lldb::NotificationType type, | ||
lldb_private::NotificationCallback notification_callback, void *baton, | ||
void *original_callback); | ||
|
||
/// Remove the specified callback. Return true if successful. | ||
static bool RemoveNotificationCallback(lldb::callback_token_t token); | ||
|
||
/// Add a callback for when the debugger is destroyed. Return a token, which | ||
/// can be used to remove said callback. Multiple callbacks can be added by | ||
/// calling this function multiple times, and will be invoked in FIFO order. | ||
|
@@ -683,6 +695,9 @@ class Debugger : public std::enable_shared_from_this<Debugger>, | |
|
||
void InstanceInitialize(); | ||
|
||
static void InvokeNotificationCallbacks(lldb::DebuggerSP debugger_sp, | ||
lldb::NotificationType notify_type); | ||
|
||
// these should never be NULL | ||
lldb::FileSP m_input_file_sp; | ||
lldb::StreamFileSP m_output_stream_sp; | ||
|
@@ -737,19 +752,35 @@ class Debugger : public std::enable_shared_from_this<Debugger>, | |
lldb::TargetSP m_dummy_target_sp; | ||
Diagnostics::CallbackID m_diagnostics_callback_id; | ||
|
||
std::mutex m_destroy_callback_mutex; | ||
lldb::callback_token_t m_destroy_callback_next_token = 0; | ||
struct DestroyCallbackInfo { | ||
DestroyCallbackInfo() {} | ||
DestroyCallbackInfo(lldb::callback_token_t token, | ||
lldb_private::DebuggerDestroyCallback callback, | ||
void *baton) | ||
template <typename T> struct CallbackInfo { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC, |
||
CallbackInfo() {} | ||
CallbackInfo(lldb::callback_token_t token, T callback, void *baton) | ||
: token(token), callback(callback), baton(baton) {} | ||
lldb::callback_token_t token; | ||
lldb_private::DebuggerDestroyCallback callback; | ||
T callback; | ||
void *baton; | ||
}; | ||
llvm::SmallVector<DestroyCallbackInfo, 2> m_destroy_callbacks; | ||
template <typename T> | ||
struct NotificationCallbackInfo : public CallbackInfo<T> { | ||
NotificationCallbackInfo() {} | ||
NotificationCallbackInfo(lldb::callback_token_t token, | ||
lldb::NotificationType type, T callback, | ||
void *baton, void *original_callback) | ||
: CallbackInfo<T>(token, callback, baton), type(type), | ||
original_callback(original_callback) {} | ||
lldb::NotificationType type; | ||
void *original_callback; | ||
}; | ||
static std::mutex s_notification_callback_mutex; | ||
static lldb::callback_token_t s_notification_callback_next_token; | ||
static llvm::SmallVector< | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Every action we perform with the notifications except for "deleting by token" knows the type of the notification that it's adding/looking up. We could even encode the type in the token and we'd always know. So it seems awkward to store them in a flat list. When we get around to listing these and other management tasks, those will very likely also be requests by type. |
||
NotificationCallbackInfo<lldb_private::NotificationCallback>, 2> | ||
s_notification_callbacks; | ||
|
||
std::mutex m_destroy_callback_mutex; | ||
lldb::callback_token_t m_destroy_callback_next_token = 0; | ||
llvm::SmallVector<CallbackInfo<lldb_private::DebuggerDestroyCallback>, 2> | ||
m_destroy_callbacks; | ||
|
||
uint32_t m_interrupt_requested = 0; ///< Tracks interrupt requests | ||
std::mutex m_interrupt_mutex; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1362,6 +1362,12 @@ enum Severity { | |
eSeverityInfo, // Equivalent to Remark used in clang. | ||
}; | ||
|
||
enum NotificationType { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be good to put a comment here saying (a) this is used for SBNotificationCallbacks and (b) this is where you should add any other notification callbacks (i.e. target creation and destruction...) That should be made clear so we don't invent another mechanism for this elsewhere... |
||
eDebuggerWillBeCreated = (1 << 0), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it's not useful to call this before the new Debugger is fully initialized. |
||
eDebuggerWillBeDestroyed = | ||
(1 << 1), // Call before debugger object is destroyed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we actually want to treat this as a bitfield? If not, maybe they should just get the usual sequential numbers? |
||
}; | ||
|
||
} // namespace lldb | ||
|
||
#endif // LLDB_LLDB_ENUMERATIONS_H |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1703,6 +1703,30 @@ void SBDebugger::SetDestroyCallback( | |||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
lldb::callback_token_t SBDebugger::AddNotificationCallback( | ||||||||||
lldb::NotificationType type, | ||||||||||
lldb::SBNotificationCallback notification_callback, void *baton) { | ||||||||||
LLDB_INSTRUMENT_VA(type, notification_callback, baton); | ||||||||||
|
||||||||||
NotificationCallback callback = [](lldb::NotificationType type, | ||||||||||
lldb::DebuggerSP debugger, | ||||||||||
lldb::ExecutionContextRefSP exe_ctx, | ||||||||||
void *baton, void *original_callback) { | ||||||||||
SBDebugger sb_debugger(debugger); | ||||||||||
lldb::SBNotificationCallback original_callback_func = | ||||||||||
(lldb::SBNotificationCallback)original_callback; | ||||||||||
Comment on lines
+1716
to
+1717
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
(use c++ cast, and avoid repeating the type) |
||||||||||
SBExecutionContext sb_exe_ctx(exe_ctx); | ||||||||||
original_callback_func(type, sb_debugger, sb_exe_ctx, baton); | ||||||||||
}; | ||||||||||
return Debugger::AddNotificationCallback(type, callback, baton, | ||||||||||
(void *)notification_callback); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||||||||||
} | ||||||||||
|
||||||||||
bool SBDebugger::RemoveNotificationCallback(lldb::callback_token_t token) { | ||||||||||
LLDB_INSTRUMENT_VA(token); | ||||||||||
return Debugger::RemoveNotificationCallback(token); | ||||||||||
} | ||||||||||
|
||||||||||
lldb::callback_token_t | ||||||||||
SBDebugger::AddDestroyCallback(lldb::SBDebuggerDestroyCallback destroy_callback, | ||||||||||
void *baton) { | ||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,6 +106,12 @@ static Debugger::DebuggerList *g_debugger_list_ptr = | |
nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain | ||
static llvm::DefaultThreadPool *g_thread_pool = nullptr; | ||
|
||
std::mutex Debugger::s_notification_callback_mutex; | ||
lldb::callback_token_t Debugger::s_notification_callback_next_token = 0; | ||
llvm::SmallVector< | ||
Debugger::NotificationCallbackInfo<lldb_private::NotificationCallback>, 2> | ||
Debugger::s_notification_callbacks; | ||
|
||
static constexpr OptionEnumValueElement g_show_disassembly_enum_values[] = { | ||
{ | ||
Debugger::eStopDisassemblyTypeNever, | ||
|
@@ -739,16 +745,29 @@ DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback, | |
g_debugger_list_ptr->push_back(debugger_sp); | ||
} | ||
debugger_sp->InstanceInitialize(); | ||
|
||
InvokeNotificationCallbacks(debugger_sp, lldb::eDebuggerWillBeCreated); | ||
return debugger_sp; | ||
} | ||
|
||
void Debugger::InvokeNotificationCallbacks(DebuggerSP debugger_sp, | ||
lldb::NotificationType notify_type) { | ||
std::lock_guard<std::mutex> guard(s_notification_callback_mutex); | ||
for (const auto &callback_info : s_notification_callbacks) { | ||
if ((callback_info.type & notify_type) == notify_type) | ||
callback_info.callback(notify_type, debugger_sp, nullptr, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note this will deadlock if any of the callbacks try to register another callback. You may want to consider making a copy of the list and iterating over that (without holding the mutex). |
||
callback_info.baton, | ||
callback_info.original_callback); | ||
} | ||
} | ||
|
||
void Debugger::HandleDestroyCallback() { | ||
const lldb::user_id_t user_id = GetID(); | ||
// Invoke and remove all the callbacks in an FIFO order. Callbacks which are | ||
// added during this loop will be appended, invoked and then removed last. | ||
// Callbacks which are removed during this loop will not be invoked. | ||
while (true) { | ||
DestroyCallbackInfo callback_info; | ||
CallbackInfo<DebuggerDestroyCallback> callback_info; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about treating the destroy callbacks as (legacy) synonyms for NotificationCallbacks of the "destroy" kind? Basically, have Then we could delete all of this code... |
||
{ | ||
std::lock_guard<std::mutex> guard(m_destroy_callback_mutex); | ||
if (m_destroy_callbacks.empty()) | ||
|
@@ -766,6 +785,7 @@ void Debugger::Destroy(DebuggerSP &debugger_sp) { | |
if (!debugger_sp) | ||
return; | ||
|
||
InvokeNotificationCallbacks(debugger_sp, lldb::eDebuggerWillBeDestroyed); | ||
debugger_sp->HandleDestroyCallback(); | ||
CommandInterpreter &cmd_interpreter = debugger_sp->GetCommandInterpreter(); | ||
|
||
|
@@ -1447,6 +1467,29 @@ void Debugger::SetDestroyCallback( | |
m_destroy_callbacks.emplace_back(token, destroy_callback, baton); | ||
} | ||
|
||
lldb::callback_token_t Debugger::AddNotificationCallback( | ||
lldb::NotificationType type, | ||
lldb_private::NotificationCallback notification_callback, void *baton, | ||
void *original_callback) { | ||
std::lock_guard<std::mutex> guard(s_notification_callback_mutex); | ||
const lldb::callback_token_t token = s_notification_callback_next_token++; | ||
s_notification_callbacks.emplace_back(token, type, notification_callback, | ||
baton, original_callback); | ||
return token; | ||
} | ||
|
||
bool Debugger::RemoveNotificationCallback(lldb::callback_token_t token) { | ||
std::lock_guard<std::mutex> guard(s_notification_callback_mutex); | ||
for (auto it = s_notification_callbacks.begin(); | ||
it != s_notification_callbacks.end(); ++it) { | ||
if (it->token == token) { | ||
s_notification_callbacks.erase(it); | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
lldb::callback_token_t Debugger::AddDestroyCallback( | ||
lldb_private::DebuggerDestroyCallback destroy_callback, void *baton) { | ||
std::lock_guard<std::mutex> guard(m_destroy_callback_mutex); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this use PythonObject wrappers instead?
Something like
PythonCallable(baton)(PythonInteger(type), debugger_arg, exe_ctx_arg)
?