[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