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 f2d35f6f1c76cefe184b04f1a0eb67f56c98513b 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 +++ .../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]) _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits