llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Greg Clayton (clayborg) <details> <summary>Changes</summary> This patch extends the "target hook" to be able to lazily resolve addresses. It does so by allowing the python class that gets specified to the "target hook" to have a new 'def resolve_load_addr(self, load_addr, stream) -> lldb.SBAddress' method that is optionally implemented. JIT loader plug-ins can now be implemented by creating a python class and using the "target hook add" and specifying the python class. The script can find and load the corresponding module lazily and only when needed. As a backtrace is happening, the PC's are resolved through the target and if the PC fails to resolve to a section/offset address, the `resolve_load_address` method will be called and the address can be resolved. --- Patch is 31.11 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/212831.diff 19 Files Affected: - (modified) lldb/bindings/python/python-swigsafecast.swig (+4) - (modified) lldb/bindings/python/python-wrapper.swig (+12) - (modified) lldb/examples/python/templates/scripted_hook.py (+24) - (modified) lldb/include/lldb/API/SBAddress.h (+9) - (modified) lldb/include/lldb/Interpreter/Interfaces/ScriptedHookInterface.h (+25-2) - (modified) lldb/include/lldb/Interpreter/ScriptInterpreter.h (+2) - (modified) lldb/include/lldb/Target/Target.h (+13-1) - (modified) lldb/source/Commands/CommandObjectTarget.cpp (+4-1) - (modified) lldb/source/Interpreter/ScriptInterpreter.cpp (+9) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedHookPythonInterface.cpp (+27) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedHookPythonInterface.h (+3-2) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp (+14) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h (+8) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h (+2) - (modified) lldb/source/Target/Target.cpp (+46-2) - (added) lldb/test/API/commands/target/module-hook/resolve-addr/Makefile (+2) - (added) lldb/test/API/commands/target/module-hook/resolve-addr/TestModuleHookResolveAddr.py (+128) - (added) lldb/test/API/commands/target/module-hook/resolve-addr/addrhook.py (+94) - (added) lldb/test/API/commands/target/module-hook/resolve-addr/main.cpp (+4) ``````````diff diff --git a/lldb/bindings/python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig index a86dc44ce4106..ec3b36a7b1ec7 100644 --- a/lldb/bindings/python/python-swigsafecast.swig +++ b/lldb/bindings/python/python-swigsafecast.swig @@ -59,6 +59,10 @@ PythonObject SWIGBridge::ToSWIGWrapper(const StructuredDataImpl &data_impl) { return ToSWIGWrapper(std::unique_ptr<lldb::SBStructuredData>(new lldb::SBStructuredData(data_impl))); } +PythonObject SWIGBridge::ToSWIGWrapper(const Address &addr) { + return ToSWIGHelper(new lldb::SBAddress(addr), SWIGTYPE_p_lldb__SBAddress); +} + PythonObject SWIGBridge::ToSWIGWrapper(lldb::ThreadSP thread_sp) { return ToSWIGHelper(new lldb::SBThread(std::move(thread_sp)), SWIGTYPE_p_lldb__SBThread); diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig index 2392737402e20..513d04bd3daa2 100644 --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -533,6 +533,18 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBSymbolContext(PyObjec return sb_ptr; } +void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBAddress(PyObject * data) { + lldb::SBAddress *sb_ptr = nullptr; + + int valid_cast = + SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBAddress, 0); + + if (valid_cast == -1) + return NULL; + + return sb_ptr; +} + void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBValue(PyObject * data) { lldb::SBValue *sb_ptr = NULL; diff --git a/lldb/examples/python/templates/scripted_hook.py b/lldb/examples/python/templates/scripted_hook.py index 0725c2c9c5d91..a8468aea2ab3d 100644 --- a/lldb/examples/python/templates/scripted_hook.py +++ b/lldb/examples/python/templates/scripted_hook.py @@ -65,3 +65,27 @@ def handle_stop( returned to the user, `False` if the process should keep running. """ pass + + + def handle_resolve_addr( + self, load_addr: int, stream: lldb.SBStream) -> lldb.SBAddress: + """Called whenever the target is not able to resolve a load address to + a section offset address. + + Clients can implement a JIT loader plugin using this method. Anytime the + target fails to resolve an address, this method will be called. The + function can find the file for the module that contains the address, + load it into the target, and return the resolved address. If the + address cannot be resolved, return a default construct lldb.SBAddress. + + Args: + load_addr (int): The load address to attempt to resolve. + stream (lldb.SBStream): The stream to which the hook can write + output that will be reported to the user. + + Returns: + lldb.SBAddress: If the address was resolved, return a SBAddress + that has been resolved to a section offset address, or return a + default constructed SBAddress if the address could not be resolved. + """ + pass diff --git a/lldb/include/lldb/API/SBAddress.h b/lldb/include/lldb/API/SBAddress.h index 430dad4862dbf..bb490ee9dfedd 100644 --- a/lldb/include/lldb/API/SBAddress.h +++ b/lldb/include/lldb/API/SBAddress.h @@ -12,6 +12,12 @@ #include "lldb/API/SBDefines.h" #include "lldb/API/SBModule.h" +namespace lldb_private { +namespace python { +class SWIGBridge; +} +} // namespace lldb_private + namespace lldb { class LLDB_API SBAddress { @@ -104,6 +110,9 @@ class LLDB_API SBAddress { friend class SBValue; friend class SBQueueItem; + friend class lldb_private::ScriptInterpreter; + friend class lldb_private::python::SWIGBridge; + lldb_private::Address *operator->(); const lldb_private::Address *operator->() const; diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedHookInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedHookInterface.h index 54d335122300d..775fc65af87b2 100644 --- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedHookInterface.h +++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedHookInterface.h @@ -10,7 +10,7 @@ #define LLDB_INTERPRETER_INTERFACES_SCRIPTEDHOOKINTERFACE_H #include "lldb/lldb-private.h" - +#include "lldb/Core/Address.h" #include "ScriptedInterface.h" namespace lldb_private { @@ -21,9 +21,10 @@ class ScriptedHookInterface : public ScriptedInterface { bool handle_module_loaded = false; bool handle_module_unloaded = false; bool handle_stop = false; + bool handle_resolve_addr = false; bool any() const { - return handle_module_loaded || handle_module_unloaded || handle_stop; + return handle_module_loaded || handle_module_unloaded || handle_stop || handle_resolve_addr; } }; @@ -47,6 +48,28 @@ class ScriptedHookInterface : public ScriptedInterface { lldb::StreamSP &output_sp) { return true; } + + /// Called when the target tried to resolve an address but wasn't able to + /// resolve it to an object file section. This allows plug-ins to resolve + /// an address on demand. JIT plug-ins can be completely implemented using + /// ScriptedHookInterface plug-ins and can lazily load the JIT'ed information + /// as needed instead of setting breakpoints + /// + /// \param[in] load_addr + /// The load address to resolve. + /// + /// \param[out] addr + /// The section offset address that was resolved if \a true is returned. + /// + /// \param[in] output_sp + /// An output stream to use for logging. + /// + /// \return + /// True if the address was resolved, false otherwise. + virtual std::optional<Address> + HandleResolveAddress(lldb::addr_t load_addr, lldb::StreamSP &output_sp) { + return std::nullopt; + } }; } // namespace lldb_private diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index 0e65cb4b8ac4a..519155cffd360 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -652,6 +652,8 @@ class ScriptInterpreter : public PluginInterface { SymbolContext GetOpaqueTypeFromSBSymbolContext(const lldb::SBSymbolContext &sym_ctx) const; + Address GetOpaqueTypeFromSBAddress(const lldb::SBAddress &addr) const; + lldb::BreakpointSP GetOpaqueTypeFromSBBreakpoint(const lldb::SBBreakpoint &breakpoint) const; diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index fb43f432a08da..4062c123abb45 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -1769,6 +1769,7 @@ class Target : public std::enable_shared_from_this<Target>, kModulesLoaded = (1u << 0), kModulesUnloaded = (1u << 1), kProcessStop = (1u << 2), + kResolveAddress = (1u << 3) }; lldb::TargetSP &GetTarget() { return m_target_sp; } @@ -1826,6 +1827,11 @@ class Target : public std::enable_shared_from_this<Target>, return StopHook::StopHookResult::NoPreference; } + virtual std::optional<Address> HandleResolveAddress(lldb::addr_t load_addr, + lldb::StreamSP &output_sp) { + return std::nullopt; + } + virtual void GetDescription(Stream &s, lldb::DescriptionLevel level) const; protected: @@ -1896,7 +1902,9 @@ class Target : public std::enable_shared_from_this<Target>, void HandleModuleUnloaded(lldb::StreamSP output) override; StopHook::StopHookResult HandleStop(ExecutionContext &exe_ctx, lldb::StreamSP output) override; - + std::optional<Address> + HandleResolveAddress(lldb::addr_t load_addr, + lldb::StreamSP &output_sp) override; Status SetScriptCallback(const ScriptedMetadata &scripted_metadata); private: @@ -1948,6 +1956,10 @@ class Target : public std::enable_shared_from_this<Target>, // control over the process for the first time. bool RunStopHooks(bool at_initial_stop = false); + /// Runs the resolve address hooks that have been registered with this + /// target. Returns true if the address was resolved. + std::optional<Address> RunResolveAddressHooks(lldb::addr_t load_addr); + bool SetSuppresStopHooks(bool suppress) { bool old_value = m_suppress_stop_hooks; m_suppress_stop_hooks = suppress; diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index f77e87ad43aa2..cf06f3bb2c8ca 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -5557,7 +5557,8 @@ Python class hooks: pass def handle_stop(self, exe_ctx, stream): return True # True = should_stop, False = continue - + def handle_resolve_addr(self, load_addr, stream): + return lldb.SBAddress() # Invalid defailt constructed address. Filter options: --------------- Filters (-s, -f, -l, -e, -c, -n, -x, -t, -T, -q) restrict when the hook @@ -5913,6 +5914,8 @@ Valid trigger names: load, unload, stop. return Target::Hook::kModulesUnloaded; if (name == "stop") return Target::Hook::kProcessStop; + if (name == "resolve-addr") + return Target::Hook::kResolveAddress; return 0; } diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index 4f6095d097d10..2be78ad5f1511 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/API/SBAddress.h" +#include "lldb/Core/Address.h" #include "lldb/Core/Debugger.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Pipe.h" @@ -152,6 +154,13 @@ SymbolContext ScriptInterpreter::GetOpaqueTypeFromSBSymbolContext( return {}; } +Address ScriptInterpreter::GetOpaqueTypeFromSBAddress( + const lldb::SBAddress &sb_addr) const { + if (sb_addr.m_opaque_up) + return *sb_addr.m_opaque_up; + return {}; +} + std::optional<lldb_private::MemoryRegionInfo> ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo( const lldb::SBMemoryRegionInfo &mem_region) const { diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedHookPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedHookPythonInterface.cpp index df4146657c53e..3724f65a5ba25 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedHookPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedHookPythonInterface.cpp @@ -49,6 +49,8 @@ ScriptedHookPythonInterface::GetSupportedMethods() { methods.handle_module_unloaded = implementor.HasAttribute("handle_module_unloaded"); methods.handle_stop = implementor.HasAttribute("handle_stop"); + methods.handle_resolve_addr = + implementor.HasAttribute("handle_resolve_addr"); return methods; } @@ -64,12 +66,20 @@ void ScriptedHookPythonInterface::HandleModuleLoaded( lldb::StreamSP &output_sp) { Status error; Dispatch("handle_module_loaded", error, output_sp); + if (error.Fail()) { + LLDB_LOG(GetLog(LLDBLog::Script), "handle_module_loaded failed: {0}", + error.AsCString()); + } } void ScriptedHookPythonInterface::HandleModuleUnloaded( lldb::StreamSP &output_sp) { Status error; Dispatch("handle_module_unloaded", error, output_sp); + if (error.Fail()) { + LLDB_LOG(GetLog(LLDBLog::Script), "handle_module_unloaded failed: {0}", + error.AsCString()); + } } llvm::Expected<bool> @@ -83,6 +93,8 @@ ScriptedHookPythonInterface::HandleStop(ExecutionContext &exe_ctx, if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error)) { + LLDB_LOG(GetLog(LLDBLog::Script), "handle_stop failed: {0}", + error.AsCString()); if (!obj) return true; return error.ToError(); @@ -91,6 +103,21 @@ ScriptedHookPythonInterface::HandleStop(ExecutionContext &exe_ctx, return obj->GetBooleanValue(); } +std::optional<Address> +ScriptedHookPythonInterface::HandleResolveAddress(lldb::addr_t load_addr, + lldb::StreamSP &output_sp) { + Status error; + Address addr = + Dispatch<Address>("handle_resolve_addr", error, load_addr, output_sp); + if (error.Fail()) { + LLDB_LOG(GetLog(LLDBLog::Script), "handle_resolve_addr failed: {0}", + error.AsCString()); + } + if (addr.IsSectionOffset()) + return addr; + return std::nullopt; +} + void ScriptedHookPythonInterface::Initialize() { const std::vector<llvm::StringRef> ci_usages = { "target hook add -P <script-name> [-k key -v value ...]"}; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedHookPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedHookPythonInterface.h index da15a4bb287d9..6b6403e3fd1fc 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedHookPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedHookPythonInterface.h @@ -33,14 +33,15 @@ class ScriptedHookPythonInterface : public ScriptedHookInterface, return llvm::SmallVector<AbstractMethodRequirement>({{"handle_stop", 2}}); } - /// Check which of the three hook methods the Python class implements. + /// Check which of the hook methods the Python class implements. SupportedHookMethods GetSupportedMethods() override; void HandleModuleLoaded(lldb::StreamSP &output_sp) override; void HandleModuleUnloaded(lldb::StreamSP &output_sp) override; llvm::Expected<bool> HandleStop(ExecutionContext &exe_ctx, lldb::StreamSP &output_sp) override; - + std::optional<Address> HandleResolveAddress(lldb::addr_t load_addr, + lldb::StreamSP &output_sp) override; static void Initialize(); static void Terminate(); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index 61ceb40dd9d32..9a99cf73e3783 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -14,6 +14,8 @@ #include "../ScriptInterpreterPythonImpl.h" #include "ScriptedPythonInterface.h" +#include "lldb/API/SBAddress.h" +#include "lldb/Core/Address.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/ValueObject/ValueObjectList.h" #include <optional> @@ -118,6 +120,18 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<SymbolContext>( return {}; } +template <> +Address ScriptedPythonInterface::ExtractValueFromPythonObject<Address>( + python::PythonObject &p, Status &error) { + if (lldb::SBAddress *sb_addr = reinterpret_cast<lldb::SBAddress *>( + python::LLDBSWIGPython_CastPyObjectToSBAddress(p.get()))) + return m_interpreter.GetOpaqueTypeFromSBAddress(*sb_addr); + error = Status::FromErrorString( + "Couldn't cast lldb::SBAddress to lldb_private::Address."); + + return {}; +} + template <> lldb::DataExtractorSP ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>( diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h index aaa0b6a0f7a59..984fcaf6b8989 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h @@ -648,6 +648,10 @@ class ScriptedPythonInterface : virtual public ScriptedInterface { return python::SWIGBridge::ToSWIGWrapper(arg); } + python::PythonObject Transform(Address &arg) { + return python::SWIGBridge::ToSWIGWrapper(arg); + } + python::PythonObject Transform(lldb::StreamSP arg) { return python::SWIGBridge::ToSWIGWrapper(arg.get()); } @@ -768,6 +772,10 @@ SymbolContext ScriptedPythonInterface::ExtractValueFromPythonObject<SymbolContext>( python::PythonObject &p, Status &error); +template <> +Address ScriptedPythonInterface::ExtractValueFromPythonObject<Address>( + python::PythonObject &p, Status &error); + template <> lldb::StreamSP ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>( diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h index 07e0da1dcf70d..839d421b9e855 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -86,6 +86,7 @@ class SWIGBridge { static PythonObject ToSWIGWrapper(lldb::BreakpointSP breakpoint_sp); static PythonObject ToSWIGWrapper(Status &&status); static PythonObject ToSWIGWrapper(const StructuredDataImpl &data_impl); + static PythonObject ToSWIGWrapper(const Address &addr); static PythonObject ToSWIGWrapper(lldb::ThreadSP thread_sp); static PythonObject ToSWIGWrapper(lldb::StackFrameSP frame_sp); static PythonObject ToSWIGWrapper(lldb::StackFrameListSP frames_sp); @@ -252,6 +253,7 @@ void *LLDBSWIGPython_CastPyObjectToSBStream(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBThread(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBFrame(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBSymbolContext(PyObject *data); +void *LLDBSWIGPython_CastPyObjectToSBAddress(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBValue(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBValueList(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data); diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 148d7e0b30dbb..ddaba55deb203 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -3481,8 +3481,14 @@ Status Target::Install(ProcessLaunchInfo *launch_info) { bool Target::ResolveLoadAddress(addr_t load_addr, Address &so_addr, uint32_t stop_id, bool allow_section_end) { - return m_section_load_history.ResolveLoadAddress(stop_id, load_addr, so_addr, - allow_section_end); + if (m_section_load_history.ResolveLoadAddress(stop_id, load_addr, so_addr, + allow_section_end)) + return true; + if (std::optional<Address> opt_addr = RunResolveAddressHooks(load_addr)) { + so_addr = *opt_addr; + return true; + } + return false; } bool Target::ResolveFileAddress(lldb::addr_t file_addr, @@ -4668,6 +4674,8 @@ Status Target::HookScripted::SetScriptCallback( m_trigger_mask |= kModulesUnloaded; if (methods.handle_stop) m_trigger_mask |= kProcessStop; + if (methods.handle_resolve_addr) + m_trigger_mask |= kResolveAddress; return {}; } @@ -4709,6 +4717,14 @@ Target::HookScripted::HandleStop(ExecutionContext &exc_ctx, : StopHook::StopHookResult::RequestContinue; } +std::optional<Address> +Target::HookScripted::HandleResolveAddress(lldb::addr_t load_addr, + lldb::StreamSP &output_sp) { + if (!m_interface_sp) + return std::nullopt; + return m_interface_sp->HandleResolveAddress(load_addr, output_sp); +} + llvm::StringRef Target::HookScripted::GetScriptClassName() const { if (m_interface_sp && m_interface_sp->GetScriptedMetadata()) return m_interface_sp->GetScriptedMetadata()->GetClassName(); @@ -4812,6 +4828,34 @@ void Target::SetAllHooksEnabledState(bool enabled) { hook->SetIsEnabled(enabled); } +std::optional<Address> Target::RunResolveAddressHooks(lldb::addr_t load_addr) { + if (m_hooks.empty()) + return std::nullopt; + + // Copy active hooks into a local vector before iterating, in case a + // callback modifies m_hooks (same pattern as RunStopHooks). + std::vector<HookSP... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/212831 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
