https://github.com/jeffreytan81 created https://github.com/llvm/llvm-project/pull/111206
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. >From 6e84ab9a14e63c58e1facdbf9a695c093882b37b Mon Sep 17 00:00:00 2001 From: jeffreytan81 <jeffrey...@fb.com> Date: Mon, 19 Aug 2024 10:57:35 -0700 Subject: [PATCH 1/2] Fix StartDebuggingRequestHandler/ReplModeRequestHandler in lldb-dap --- lldb/tools/lldb-dap/DAP.h | 2 -- lldb/tools/lldb-dap/lldb-dap.cpp | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 57562a14983519..7828272aa15a7d 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -192,8 +192,6 @@ struct DAP { std::mutex call_mutex; std::map<int /* request_seq */, ResponseCallback /* reply handler */> inflight_reverse_requests; - StartDebuggingRequestHandler start_debugging_request_handler; - ReplModeRequestHandler repl_mode_request_handler; ReplMode repl_mode; std::string command_escape_prefix = "`"; lldb::SBFormat frame_format; diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index ea84f31aec3a6c..f50a6c17310739 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -1627,12 +1627,12 @@ void request_initialize(const llvm::json::Object &request) { "lldb-dap", "Commands for managing lldb-dap."); if (GetBoolean(arguments, "supportsStartDebuggingRequest", false)) { cmd.AddCommand( - "startDebugging", &g_dap.start_debugging_request_handler, + "startDebugging", new StartDebuggingRequestHandler(), "Sends a startDebugging request from the debug adapter to the client " "to start a child debug session of the same type as the caller."); } cmd.AddCommand( - "repl-mode", &g_dap.repl_mode_request_handler, + "repl-mode", new ReplModeRequestHandler(), "Get or set the repl behavior of lldb-dap evaluation requests."); g_dap.progress_event_thread = std::thread(ProgressEventThreadFunction); >From 4f119961f3ae2d7bc40a2c00e18ee5774254c773 Mon Sep 17 00:00:00 2001 From: jeffreytan81 <jeffrey...@fb.com> Date: Fri, 4 Oct 2024 09:31:28 -0700 Subject: [PATCH 2/2] Add SBDebugger::AddCreateCallback API --- lldb/bindings/python/python-swigsafecast.swig | 5 +++ lldb/bindings/python/python-typemaps.swig | 19 +++++++++ lldb/bindings/python/python-wrapper.swig | 20 +++++++++ lldb/include/lldb/API/SBDebugger.h | 10 +++++ lldb/include/lldb/API/SBDefines.h | 2 + lldb/include/lldb/Core/Debugger.h | 41 +++++++++++++++---- lldb/include/lldb/lldb-private-types.h | 2 + lldb/source/API/SBDebugger.cpp | 20 +++++++++ lldb/source/Core/Debugger.cpp | 40 +++++++++++++++++- .../Python/SWIGPythonBridge.h | 3 ++ .../python_api/debugger/TestDebuggerAPI.py | 22 ++++++++++ 11 files changed, 174 insertions(+), 10 deletions(-) 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()) _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits