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 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits