[Lldb-commits] [lldb] Add SBDebugger::AddCreateCallback API (PR #111206)

via lldb-commits lldb-commits at lists.llvm.org
Tue Oct 8 12:22:03 PDT 2024


https://github.com/jeffreytan81 updated https://github.com/llvm/llvm-project/pull/111206

>From 6e84ab9a14e63c58e1facdbf9a695c093882b37b Mon Sep 17 00:00:00 2001
From: jeffreytan81 <jeffreytan at 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 f2d35f6f1c76cefe184b04f1a0eb67f56c98513b Mon Sep 17 00:00:00 2001
From: jeffreytan81 <jeffreytan at fb.com>
Date: Tue, 8 Oct 2024 12:10:29 -0700
Subject: [PATCH 2/2] Add SBDebugger::AddNotificationCallback

---
 lldb/bindings/python/python-swigsafecast.swig | 10 ++++
 lldb/bindings/python/python-typemaps.swig     | 19 +++++++
 lldb/bindings/python/python-wrapper.swig      | 40 +++++++++++++++
 lldb/include/lldb/API/SBDebugger.h            | 11 +++++
 lldb/include/lldb/API/SBDefines.h             |  5 +-
 lldb/include/lldb/API/SBExecutionContext.h    |  1 +
 lldb/include/lldb/Core/Debugger.h             | 49 +++++++++++++++----
 lldb/include/lldb/lldb-enumerations.h         |  6 +++
 lldb/include/lldb/lldb-private-types.h        |  4 ++
 lldb/source/API/SBDebugger.cpp                | 24 +++++++++
 lldb/source/Core/Debugger.cpp                 | 45 ++++++++++++++++-
 .../Python/SWIGPythonBridge.h                 |  6 +++
 .../python_api/debugger/TestDebuggerAPI.py    | 47 ++++++++++++++++++
 13 files changed, 256 insertions(+), 11 deletions(-)

diff --git a/lldb/bindings/python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig
index 0127ac6bfa4681..2dd03d5f2a4b50 100644
--- a/lldb/bindings/python/python-swigsafecast.swig
+++ b/lldb/bindings/python/python-swigsafecast.swig
@@ -133,5 +133,15 @@ 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);
+}
+
+PythonObject SWIGBridge::ToSWIGWrapper(
+    std::unique_ptr<lldb::SBExecutionContext> exe_ctx_sb) {
+  return ToSWIGHelper(exe_ctx_sb.release(), SWIGTYPE_p_lldb__SBExecutionContext);
+}
+
 } // namespace python
 } // namespace lldb_private
diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig
index f8c33e15c03e66..d67172155cee21 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::SBNotificationCallback
+%typemap(in) (lldb::SBNotificationCallback notification_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 = LLDBSwigPythonCallPythonSBNotificationCallback;
+  $2 = $input;
+}
+
+%typemap(typecheck) (lldb::SBNotificationCallback notification_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..8fbca0f4161676 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -1024,6 +1024,46 @@ static void LLDBSwigPythonCallPythonLogOutputCallback(const char *str,
   }
 }
 
+// For NotificationCallback functions
+static void LLDBSwigPythonCallPythonSBNotificationCallback(
+  lldb::NotificationType type, lldb::SBDebugger &debugger, 
+  lldb::SBExecutionContext &exe_ctx, void *baton) {
+  
+  if (baton != Py_None) {
+    SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+
+    // Convert debugger and exe_ctx to Python objects
+    PythonObject debugger_arg = SWIGBridge::ToSWIGWrapper(
+      std::make_unique<SBDebugger>(debugger));  // Wrap debugger reference
+
+    PythonObject exe_ctx_arg = SWIGBridge::ToSWIGWrapper(
+      std::make_unique<SBExecutionContext>(exe_ctx));  // Wrap ExecutionContext
+
+    // Create a tuple of arguments (type, debugger, exe_ctx)
+    PyObject *args = PyTuple_New(3);
+    
+    // Add NotificationType as an integer to the tuple
+    PyTuple_SetItem(args, 0, PyLong_FromLong(static_cast<long>(type)));
+
+    // Add debugger and exe_ctx to the tuple
+    Py_INCREF(debugger_arg.get());
+    PyTuple_SetItem(args, 1, debugger_arg.get());
+
+    Py_INCREF(exe_ctx_arg.get());
+    PyTuple_SetItem(args, 2, exe_ctx_arg.get());
+
+    // Call the Python function with the tuple of arguments (type, debugger, exe_ctx)
+    PyObject *result = PyObject_CallFunction(
+            reinterpret_cast<PyObject *>(baton), const_cast<char *>("O"), args);
+
+    // Clean up
+    Py_XDECREF(result);
+    Py_DECREF(args);  // Decrement reference count for args
+
+    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..dbaafeddfc192a 100644
--- a/lldb/include/lldb/API/SBDebugger.h
+++ b/lldb/include/lldb/API/SBDebugger.h
@@ -336,6 +336,17 @@ class LLDB_API SBDebugger {
   void SetDestroyCallback(lldb::SBDebuggerDestroyCallback destroy_callback,
                           void *baton);
 
+  /// Add a notification callback when notification type event happens. 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
+  AddNotificationCallback(lldb::NotificationType notification_type,
+                          lldb::SBNotificationCallback notification_callback,
+                          void *baton);
+  /// 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.
diff --git a/lldb/include/lldb/API/SBDefines.h b/lldb/include/lldb/API/SBDefines.h
index 9543ebc08a320f..f208f2a2b6f317 100644
--- a/lldb/include/lldb/API/SBDefines.h
+++ b/lldb/include/lldb/API/SBDefines.h
@@ -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,
+                                       void *baton);
 typedef void (*SBDebuggerDestroyCallback)(lldb::user_id_t debugger_id,
                                           void *baton);
 
diff --git a/lldb/include/lldb/API/SBExecutionContext.h b/lldb/include/lldb/API/SBExecutionContext.h
index e8de2ebe58785e..62fd637da93ff6 100644
--- a/lldb/include/lldb/API/SBExecutionContext.h
+++ b/lldb/include/lldb/API/SBExecutionContext.h
@@ -58,6 +58,7 @@ class LLDB_API SBExecutionContext {
 
   lldb_private::ExecutionContextRef *get() const;
 
+  friend class SBDebugger;
   SBExecutionContext(lldb::ExecutionContextRefSP exe_ctx_ref_sp);
 
 private:
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index a72c2596cc2c5e..fef87a0c4726ff 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -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
+  /// 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 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 {
+    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<
+      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;
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 938f6e3abe8f2a..7dd65b07c1dc3f 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -1362,6 +1362,12 @@ enum Severity {
   eSeverityInfo, // Equivalent to Remark used in clang.
 };
 
+enum NotificationType {
+  eDebuggerWillBeCreated = (1 << 0),
+  eDebuggerWillBeDestroyed =
+      (1 << 1), // Call before debugger object is destroyed
+};
+
 } // namespace lldb
 
 #endif // LLDB_LLDB_ENUMERATIONS_H
diff --git a/lldb/include/lldb/lldb-private-types.h b/lldb/include/lldb/lldb-private-types.h
index b82a2b8aa05744..b96d4981f6fe3d 100644
--- a/lldb/include/lldb/lldb-private-types.h
+++ b/lldb/include/lldb/lldb-private-types.h
@@ -143,6 +143,10 @@ typedef struct type256 { uint64_t x[4]; } type256;
 using ValueObjectProviderTy =
     std::function<lldb::ValueObjectSP(ConstString, StackFrame *)>;
 
+typedef void (*NotificationCallback)(lldb::NotificationType type,
+                                     lldb::DebuggerSP debugger,
+                                     lldb::ExecutionContextRefSP exe_ctx,
+                                     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..560c929760705f 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -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;
+    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);
+}
+
+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) {
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 9bdc5a3949751d..651ae3f639ebdd 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -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,
+                             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;
     {
       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);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index 97a3837fd7aa62..9ea42465361088 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -33,6 +33,8 @@ class SBStructuredData;
 class SBFileSpec;
 class SBModuleSpec;
 class SBStringList;
+class SBDebugger;
+class SBExecutionContext;
 } // namespace lldb
 
 namespace lldb_private {
@@ -111,6 +113,10 @@ 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 PythonObject
+  ToSWIGWrapper(std::unique_ptr<lldb::SBExecutionContext> exe_ctx_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..92b0cee691e091 100644
--- a/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
+++ b/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
@@ -286,3 +286,50 @@ 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_AddRemoveNotificationCallback(self):
+        """
+        Test SBDebugger::AddNotificationCallback and SBDebugger::RemoveNotificationCallback
+        """
+        created_debuggers = []
+
+        def debugger_create_notification_callback(type, debugger, exe_ctx):
+            created_debuggers.append(debugger)
+
+        destroyed_debugger_ids = []
+
+        def debugger_destroy_notification_callback(type, debugger, exe_ctx):
+            destroyed_debugger_ids.append(debugger.GetID())
+
+        create_callback_token = lldb.SBDebugger.AddNotificationCallback(
+            lldb.eDebuggerWillBeCreated, debugger_create_notification_callback
+        )
+        debugger1 = lldb.SBDebugger.Create()
+        debugger2 = lldb.SBDebugger.Create()
+
+        # Remove create callback before creating 3rd debugger
+        lldb.SBDebugger.RemoveNotificationCallback(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.SBDebugger.Destroy(debugger1)
+        lldb.SBDebugger.Destroy(debugger2)
+
+        # Add destroy callback after destroying the first two debuggers but
+        # before creating 3rd debugger
+        lldb.SBDebugger.AddNotificationCallback(
+            lldb.eDebuggerWillBeDestroyed, debugger_destroy_notification_callback
+        )
+
+        self.assertEqual(len(destroyed_debugger_ids), 0)
+        debugger3_id = debugger3.GetID()
+        lldb.SBDebugger.Destroy(debugger3)
+        self.assertEqual(len(destroyed_debugger_ids), 1)
+        self.assertEqual(debugger3_id, destroyed_debugger_ids[0])



More information about the lldb-commits mailing list