https://github.com/jeffreytan81 updated https://github.com/llvm/llvm-project/pull/111206
>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 d5820bd77da828e6a51180473a2df00c236cc6db Mon Sep 17 00:00:00 2001 From: jeffreytan81 <jeffrey...@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]) _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits