[Lldb-commits] [lldb] Add SBDebugger::AddCreateCallback API (PR #111206)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Oct 4 13:34:44 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: None (jeffreytan81)
<details>
<summary>Changes</summary>
This PR adds new SBDebugger::AddCreateCallback()/RemoveCreateCallback() APIs.
This API enables any lldb client to subscribe to debugger creation and perform action on it. For example, we have telemetry code that uses `SBDebugger::AddDestroyCallback()` to register debugger termination callback to collect `statistics dump`. We would like to use `SBDebugger::AddCreateCallback()` so that we can register telemetry reporting for all debugger instances not only the first one.
---
Full diff: https://github.com/llvm/llvm-project/pull/111206.diff
11 Files Affected:
- (modified) lldb/bindings/python/python-swigsafecast.swig (+5)
- (modified) lldb/bindings/python/python-typemaps.swig (+19)
- (modified) lldb/bindings/python/python-wrapper.swig (+20)
- (modified) lldb/include/lldb/API/SBDebugger.h (+10)
- (modified) lldb/include/lldb/API/SBDefines.h (+2)
- (modified) lldb/include/lldb/Core/Debugger.h (+32-9)
- (modified) lldb/include/lldb/lldb-private-types.h (+2)
- (modified) lldb/source/API/SBDebugger.cpp (+20)
- (modified) lldb/source/Core/Debugger.cpp (+39-1)
- (modified) lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h (+3)
- (modified) lldb/test/API/python_api/debugger/TestDebuggerAPI.py (+22)
``````````diff
diff --git a/lldb/bindings/python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig
index 0127ac6bfa4681..dc2b9c0ca69693 100644
--- a/lldb/bindings/python/python-swigsafecast.swig
+++ b/lldb/bindings/python/python-swigsafecast.swig
@@ -133,5 +133,10 @@ PythonObject SWIGBridge::ToSWIGWrapper(
return ToSWIGHelper(module_spec_sb.release(), SWIGTYPE_p_lldb__SBModuleSpec);
}
+PythonObject SWIGBridge::ToSWIGWrapper(
+ std::unique_ptr<lldb::SBDebugger> debugger_sb) {
+ return ToSWIGHelper(debugger_sb.release(), SWIGTYPE_p_lldb__SBDebugger);
+}
+
} // namespace python
} // namespace lldb_private
diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig
index f8c33e15c03e66..c61edc31bad26f 100644
--- a/lldb/bindings/python/python-typemaps.swig
+++ b/lldb/bindings/python/python-typemaps.swig
@@ -452,6 +452,25 @@ template <> bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
$1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
}
+// For lldb::SBDebuggerCreateCallback
+%typemap(in) (lldb::SBDebuggerCreateCallback create_callback, void *baton) {
+ if (!($input == Py_None ||
+ PyCallable_Check(reinterpret_cast<PyObject *>($input)))) {
+ PyErr_SetString(PyExc_TypeError, "Need a callable object or None!");
+ SWIG_fail;
+ }
+
+ // Don't lose the callback reference
+ Py_INCREF($input);
+ $1 = LLDBSwigPythonCallPythonSBDebuggerCreateCallback;
+ $2 = $input;
+}
+
+%typemap(typecheck) (lldb::SBDebuggerCreateCallback create_callback, void *baton) {
+ $1 = $input == Py_None;
+ $1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
+}
+
// For lldb::SBDebuggerDestroyCallback
%typemap(in) (lldb::SBDebuggerDestroyCallback destroy_callback, void *baton) {
if (!($input == Py_None ||
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index 810673aaec5d19..9eef0479290073 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -1024,6 +1024,26 @@ static void LLDBSwigPythonCallPythonLogOutputCallback(const char *str,
}
}
+// For DebuggerCreateCallback functions
+static void LLDBSwigPythonCallPythonSBDebuggerCreateCallback(lldb::SBDebugger &debugger,
+ void *baton) {
+ if (baton != Py_None) {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ PythonObject debugger_arg = SWIGBridge::ToSWIGWrapper(
+ std::make_unique<SBDebugger>(debugger));
+
+ // Create a tuple of arguments
+ PyObject *args = PyTuple_New(1);
+ PyTuple_SetItem(args, 0, debugger_arg.get());
+
+ // Call the Python function
+ PyObject *result = PyObject_CallFunction(
+ reinterpret_cast<PyObject *>(baton), const_cast<char *>("O"), args);
+ Py_XDECREF(result);
+ SWIG_PYTHON_THREAD_END_BLOCK;
+ }
+}
+
// For DebuggerTerminateCallback functions
static void LLDBSwigPythonCallPythonSBDebuggerTerminateCallback(lldb::user_id_t debugger_id,
void *baton) {
diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h
index 84ea9c0f772e16..cef5307418fa2d 100644
--- a/lldb/include/lldb/API/SBDebugger.h
+++ b/lldb/include/lldb/API/SBDebugger.h
@@ -336,6 +336,16 @@ class LLDB_API SBDebugger {
void SetDestroyCallback(lldb::SBDebuggerDestroyCallback destroy_callback,
void *baton);
+ /// Add a callback for when the debugger is created. 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.
+ static lldb::callback_token_t
+ AddCreateCallback(lldb::SBDebuggerCreateCallback create_callback,
+ void *baton);
+
+ /// Remove the specified callback. Return true if successful.
+ static bool RemoveCreateCallback(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.
diff --git a/lldb/include/lldb/API/SBDefines.h b/lldb/include/lldb/API/SBDefines.h
index 9543ebc08a320f..104bdc8c79f44c 100644
--- a/lldb/include/lldb/API/SBDefines.h
+++ b/lldb/include/lldb/API/SBDefines.h
@@ -139,6 +139,8 @@ typedef bool (*SBBreakpointHitCallback)(void *baton, lldb::SBProcess &process,
lldb::SBThread &thread,
lldb::SBBreakpointLocation &location);
+typedef void (*SBDebuggerCreateCallback)(lldb::SBDebugger &debugger,
+ void *baton);
typedef void (*SBDebuggerDestroyCallback)(lldb::user_id_t debugger_id,
void *baton);
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index a72c2596cc2c5e..16e3298f7b1ed5 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -569,6 +569,16 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
SetDestroyCallback(lldb_private::DebuggerDestroyCallback destroy_callback,
void *baton);
+ /// Add a callback for when the debugger is created. 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.
+ static lldb::callback_token_t
+ AddCreateCallback(lldb_private::DebuggerCreateCallback create_callback,
+ void *baton, void *original_callback);
+
+ /// Remove the specified callback. Return true if successful.
+ static bool RemoveCreateCallback(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.
@@ -737,19 +747,32 @@ 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 {
+ 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, typename ExtraT>
+ struct CallbackInfoExtraData : public CallbackInfo<T> {
+ CallbackInfoExtraData() {}
+ CallbackInfoExtraData(lldb::callback_token_t token, T callback, void *baton,
+ ExtraT extra_data)
+ : CallbackInfo<T>(token, callback, baton), extra_data(extra_data) {}
+ ExtraT extra_data;
+ };
+ static std::mutex s_create_callback_mutex;
+ static lldb::callback_token_t s_create_callback_next_token;
+ static llvm::SmallVector<
+ CallbackInfoExtraData<lldb_private::DebuggerCreateCallback, void *>, 2>
+ s_create_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;
diff --git a/lldb/include/lldb/lldb-private-types.h b/lldb/include/lldb/lldb-private-types.h
index b82a2b8aa05744..c997538f62827a 100644
--- a/lldb/include/lldb/lldb-private-types.h
+++ b/lldb/include/lldb/lldb-private-types.h
@@ -143,6 +143,8 @@ typedef struct type256 { uint64_t x[4]; } type256;
using ValueObjectProviderTy =
std::function<lldb::ValueObjectSP(ConstString, StackFrame *)>;
+typedef void (*DebuggerCreateCallback)(lldb::DebuggerSP debugger, void *baton,
+ void *original_callback);
typedef void (*DebuggerDestroyCallback)(lldb::user_id_t debugger_id,
void *baton);
typedef bool (*CommandOverrideCallbackWithResult)(
diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index 6b72994fc96afb..806415aa7b3de0 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -1703,6 +1703,26 @@ void SBDebugger::SetDestroyCallback(
}
}
+lldb::callback_token_t
+SBDebugger::AddCreateCallback(lldb::SBDebuggerCreateCallback create_callback,
+ void *baton) {
+ LLDB_INSTRUMENT_VA(create_callback, baton);
+
+ DebuggerCreateCallback callback = [](lldb::DebuggerSP debugger, void *baton,
+ void *original_callback) {
+ SBDebugger sb_debugger(debugger);
+ lldb::SBDebuggerCreateCallback original_callback_func =
+ (lldb::SBDebuggerCreateCallback)original_callback;
+ original_callback_func(sb_debugger, baton);
+ };
+ return Debugger::AddCreateCallback(callback, baton, (void *)create_callback);
+}
+
+bool SBDebugger::RemoveCreateCallback(lldb::callback_token_t token) {
+ LLDB_INSTRUMENT_VA(token);
+ return Debugger::RemoveCreateCallback(token);
+}
+
lldb::callback_token_t
SBDebugger::AddDestroyCallback(lldb::SBDebuggerDestroyCallback destroy_callback,
void *baton) {
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 9bdc5a3949751d..7fee7eee78f7c0 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -106,6 +106,13 @@ 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_create_callback_mutex;
+lldb::callback_token_t Debugger::s_create_callback_next_token = 0;
+llvm::SmallVector<Debugger::CallbackInfoExtraData<
+ lldb_private::DebuggerCreateCallback, void *>,
+ 2>
+ Debugger::s_create_callbacks;
+
static constexpr OptionEnumValueElement g_show_disassembly_enum_values[] = {
{
Debugger::eStopDisassemblyTypeNever,
@@ -739,6 +746,15 @@ DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
g_debugger_list_ptr->push_back(debugger_sp);
}
debugger_sp->InstanceInitialize();
+
+ // Invoke all debugger create callbacks.
+ {
+ std::lock_guard<std::mutex> guard(s_create_callback_mutex);
+ for (const auto &callback_info : s_create_callbacks) {
+ callback_info.callback(debugger_sp, callback_info.baton,
+ callback_info.extra_data);
+ }
+ }
return debugger_sp;
}
@@ -748,7 +764,7 @@ void Debugger::HandleDestroyCallback() {
// 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;
{
std::lock_guard<std::mutex> guard(m_destroy_callback_mutex);
if (m_destroy_callbacks.empty())
@@ -1447,6 +1463,28 @@ void Debugger::SetDestroyCallback(
m_destroy_callbacks.emplace_back(token, destroy_callback, baton);
}
+lldb::callback_token_t Debugger::AddCreateCallback(
+ lldb_private::DebuggerCreateCallback create_callback, void *baton,
+ void *original_callback) {
+ std::lock_guard<std::mutex> guard(s_create_callback_mutex);
+ const lldb::callback_token_t token = s_create_callback_next_token++;
+ s_create_callbacks.emplace_back(token, create_callback, baton,
+ original_callback);
+ return token;
+}
+
+bool Debugger::RemoveCreateCallback(lldb::callback_token_t token) {
+ std::lock_guard<std::mutex> guard(s_create_callback_mutex);
+ for (auto it = s_create_callbacks.begin(); it != s_create_callbacks.end();
+ ++it) {
+ if (it->token == token) {
+ s_create_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);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index 97a3837fd7aa62..f050fb3a214a6c 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -33,6 +33,7 @@ class SBStructuredData;
class SBFileSpec;
class SBModuleSpec;
class SBStringList;
+class SBDebugger;
} // namespace lldb
namespace lldb_private {
@@ -111,6 +112,8 @@ class SWIGBridge {
ToSWIGWrapper(std::unique_ptr<lldb::SBFileSpec> file_spec_sb);
static PythonObject
ToSWIGWrapper(std::unique_ptr<lldb::SBModuleSpec> module_spec_sb);
+ static PythonObject
+ ToSWIGWrapper(std::unique_ptr<lldb::SBDebugger> debugger_sb);
static python::ScopedPythonObject<lldb::SBCommandReturnObject>
ToSWIGWrapper(CommandReturnObject &cmd_retobj);
diff --git a/lldb/test/API/python_api/debugger/TestDebuggerAPI.py b/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
index 646ccce36530d4..6c7433f2c93163 100644
--- a/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
+++ b/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
@@ -286,3 +286,25 @@ def remove_bar(dbg_id):
('remove bar ret', False), # remove_bar should fail, because it's already invoked and removed
('foo called', original_dbg_id), # foo should be called
])
+
+ def test_AddRemoveDebuggerCreateCallback(self):
+ """
+ Test SBDebugger::AddCreateCallback and SBDebugger::RemoveCreateCallback
+ """
+ created_debuggers = []
+ def debugger_create_callback(debugger):
+ created_debuggers.append(debugger)
+
+ create_callback_token = lldb.SBDebugger.AddCreateCallback(debugger_create_callback)
+ debugger1 = lldb.SBDebugger.Create()
+ debugger2 = lldb.SBDebugger.Create()
+
+ lldb.SBDebugger.RemoveCreateCallback(create_callback_token)
+ debugger3 = lldb.SBDebugger.Create()
+
+ self.assertNotEqual(debugger1.GetID(), debugger2.GetID())
+ self.assertNotEqual(debugger1.GetID(), debugger3.GetID())
+
+ self.assertEqual(len(created_debuggers), 2)
+ self.assertEqual(debugger1.GetID(), created_debuggers[0].GetID())
+ self.assertEqual(debugger2.GetID(), created_debuggers[1].GetID())
``````````
</details>
https://github.com/llvm/llvm-project/pull/111206
More information about the lldb-commits
mailing list