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

via lldb-commits lldb-commits at lists.llvm.org
Tue Oct 8 12:11:00 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 d5820bd77da828e6a51180473a2df00c236cc6db 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 +
 lldb/source/Target/Statistics.cpp             |   7 +-
 .../python_api/debugger/TestDebuggerAPI.py    | 122 +++++++++++++-----
 14 files changed, 302 insertions(+), 47 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/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp
index d619f92122cb9d..0752dab30d80f1 100644
--- a/lldb/source/Target/Statistics.cpp
+++ b/lldb/source/Target/Statistics.cpp
@@ -261,14 +261,15 @@ llvm::json::Value DebuggerStats::ReportStatistics(
   std::vector<ModuleStats> modules;
   std::lock_guard<std::recursive_mutex> guard(
       Module::GetAllocationModuleCollectionMutex());
-  const uint64_t num_modules = Module::GetNumberAllocatedModules();
+  const ModuleList &target_modules = target->GetImages();
+  const uint64_t num_modules = target_modules.GetSize();
   uint32_t num_debug_info_enabled_modules = 0;
   uint32_t num_modules_has_debug_info = 0;
   uint32_t num_modules_with_variable_errors = 0;
   uint32_t num_modules_with_incomplete_types = 0;
   uint32_t num_stripped_modules = 0;
   for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
-    Module *module = Module::GetAllocatedModuleAtIndex(image_idx);
+    ModuleSP module = target_modules.GetModuleAtIndex(image_idx);
     ModuleStats module_stat;
     module_stat.symtab_parse_time = module->GetSymtabParseTime().get().count();
     module_stat.symtab_index_time = module->GetSymtabIndexTime().get().count();
@@ -333,7 +334,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
       ++num_modules_with_incomplete_types;
 
     if (include_modules) {
-      module_stat.identifier = (intptr_t)module;
+      module_stat.identifier = (intptr_t)module.get();
       module_stat.path = module->GetFileSpec().GetPath();
       if (ConstString object_name = module->GetObjectName()) {
         module_stat.path.append(1, '(');
diff --git a/lldb/test/API/python_api/debugger/TestDebuggerAPI.py b/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
index 646ccce36530d4..945b0c79b9a0fe 100644
--- a/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
+++ b/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
@@ -173,22 +173,25 @@ def test_AddDestroyCallback(self):
         def foo(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal called
-            called += [('foo', dbg_id)]
+            called += [("foo", dbg_id)]
 
         def bar(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal called
-            called += [('bar', dbg_id)]
+            called += [("bar", dbg_id)]
 
         token_foo = self.dbg.AddDestroyCallback(foo)
         token_bar = self.dbg.AddDestroyCallback(bar)
         self.dbg.Destroy(self.dbg)
 
         # Should call both `foo()` and `bar()`.
-        self.assertEqual(called, [
-            ('foo', original_dbg_id),
-            ('bar', original_dbg_id),
-        ])
+        self.assertEqual(
+            called,
+            [
+                ("foo", original_dbg_id),
+                ("bar", original_dbg_id),
+            ],
+        )
 
     def test_RemoveDestroyCallback(self):
         original_dbg_id = self.dbg.GetID()
@@ -197,12 +200,12 @@ def test_RemoveDestroyCallback(self):
         def foo(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal called
-            called += [('foo', dbg_id)]
+            called += [("foo", dbg_id)]
 
         def bar(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal called
-            called += [('bar', dbg_id)]
+            called += [("bar", dbg_id)]
 
         token_foo = self.dbg.AddDestroyCallback(foo)
         token_bar = self.dbg.AddDestroyCallback(bar)
@@ -212,7 +215,7 @@ def bar(dbg_id):
         # `Remove` should be successful
         self.assertTrue(ret)
         # Should only call `bar()`
-        self.assertEqual(called, [('bar', original_dbg_id)])
+        self.assertEqual(called, [("bar", original_dbg_id)])
 
     def test_RemoveDestroyCallback_invalid_token(self):
         original_dbg_id = self.dbg.GetID()
@@ -222,7 +225,7 @@ def test_RemoveDestroyCallback_invalid_token(self):
         def foo(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal called
-            called += [('foo', dbg_id)]
+            called += [("foo", dbg_id)]
 
         token_foo = self.dbg.AddDestroyCallback(foo)
         ret = self.dbg.RemoveDestroyCallback(magic_token_that_should_not_exist)
@@ -231,7 +234,7 @@ def foo(dbg_id):
         # `Remove` should be unsuccessful
         self.assertFalse(ret)
         # Should call `foo()`
-        self.assertEqual(called, [('foo', original_dbg_id)])
+        self.assertEqual(called, [("foo", original_dbg_id)])
 
     def test_HandleDestroyCallback(self):
         """
@@ -246,43 +249,96 @@ def test_HandleDestroyCallback(self):
         def foo(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal events
-            events.append(('foo called', dbg_id))
+            events.append(("foo called", dbg_id))
 
         def bar(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal events
-            events.append(('bar called', dbg_id))
+            events.append(("bar called", dbg_id))
 
         def add_foo(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal events
-            events.append(('add_foo called', dbg_id))
-            events.append(('foo token', self.dbg.AddDestroyCallback(foo)))
+            events.append(("add_foo called", dbg_id))
+            events.append(("foo token", self.dbg.AddDestroyCallback(foo)))
 
         def remove_bar(dbg_id):
             # Need nonlocal to modify closure variable.
             nonlocal events
-            events.append(('remove_bar called', dbg_id))
-            events.append(('remove bar ret', self.dbg.RemoveDestroyCallback(bar_token)))
+            events.append(("remove_bar called", dbg_id))
+            events.append(("remove bar ret", self.dbg.RemoveDestroyCallback(bar_token)))
 
         # Setup
-        events.append(('add_foo token', self.dbg.AddDestroyCallback(add_foo)))
+        events.append(("add_foo token", self.dbg.AddDestroyCallback(add_foo)))
         bar_token = self.dbg.AddDestroyCallback(bar)
-        events.append(('bar token', bar_token))
-        events.append(('remove_bar token', self.dbg.AddDestroyCallback(remove_bar)))
+        events.append(("bar token", bar_token))
+        events.append(("remove_bar token", self.dbg.AddDestroyCallback(remove_bar)))
         # Destroy
         self.dbg.Destroy(self.dbg)
 
-        self.assertEqual(events, [
-            # Setup
-            ('add_foo token', 0), # add_foo should be added
-            ('bar token', 1), # bar should be added
-            ('remove_bar token', 2), # remove_bar should be added
-            # Destroy
-            ('add_foo called', original_dbg_id), # add_foo should be called
-            ('foo token', 3), # foo should be added
-            ('bar called', original_dbg_id), # bar should be called
-            ('remove_bar called', original_dbg_id), # remove_bar should be called
-            ('remove bar ret', False), # remove_bar should fail, because it's already invoked and removed
-            ('foo called', original_dbg_id), # foo should be called
-        ])
+        self.assertEqual(
+            events,
+            [
+                # Setup
+                ("add_foo token", 0),  # add_foo should be added
+                ("bar token", 1),  # bar should be added
+                ("remove_bar token", 2),  # remove_bar should be added
+                # Destroy
+                ("add_foo called", original_dbg_id),  # add_foo should be called
+                ("foo token", 3),  # foo should be added
+                ("bar called", original_dbg_id),  # bar should be called
+                ("remove_bar called", original_dbg_id),  # remove_bar should be called
+                (
+                    "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