From d387f9c1e63580dc8f21bcc86e002dfd2f828a12 Mon Sep 17 00:00:00 2001 From: jca Date: Tue, 4 Nov 2025 13:09:23 +0000 Subject: [PATCH] take 2: re-apply two commits that were lost while merging the 19.1.7 update Reintroduce a tweaked version of the IP-based caching implementation. Implementing a custom "new" operator has the two following desirable properties: - make the code more standalone, not depending on "new" from libcxx. - teach this allocator to return nullptr on memory shortage ("noexcept") so it can fail gracefully. If we can't allocate an item, we just don't cache it. That should be more resilient to memory shortages and thus more usable from libexecinfo. ok rsadowski@ robert@ --- gnu/llvm/libunwind/src/AddressSpace.hpp | 67 +++++++++++++++++++++++++ gnu/llvm/libunwind/src/UnwindCursor.hpp | 11 +++- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/gnu/llvm/libunwind/src/AddressSpace.hpp b/gnu/llvm/libunwind/src/AddressSpace.hpp index 5551c7d4bef..ab6bec9bc3c 100644 --- a/gnu/llvm/libunwind/src/AddressSpace.hpp +++ b/gnu/llvm/libunwind/src/AddressSpace.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "libunwind.h" #include "config.h" @@ -152,6 +153,72 @@ struct UnwindInfoSections { #endif }; +class UnwindInfoSectionsCache { +public: + + struct CacheItem { + /* + * Implement new operator to avoid depending on libcxx. By declaring + * it noexcept we can let it return nullptr under memory shortage. + */ + void *operator new(size_t sz) noexcept { + return malloc(sz); + } + + CacheItem(UnwindInfoSections &uis, uintptr_t pc) + : m_uis(uis), m_pc(pc) { + } + CacheItem(uintptr_t pc) + : m_pc(pc) { + } + + UnwindInfoSections m_uis; + uintptr_t m_pc; + + RB_ENTRY(CacheItem) entry; + }; + + typedef uintptr_t CacheItemKey; + + int CacheCmp(struct CacheItem *c1, struct CacheItem *c2) { + return (c1->m_pc < c2->m_pc ? -1 : c1->m_pc > c2->m_pc); + } + + UnwindInfoSectionsCache() { + m_head = RB_INITIALIZER(&head); + } + + bool getUnwindInfoSectionsForPC(CacheItemKey key, UnwindInfoSections &uis) { + UnwindInfoSections *result = nullptr; + if (m_prev_req_item && m_prev_req_item->m_pc == key) + result = &m_prev_req_item->m_uis; + else { + struct CacheItem find(key), *res; + res = RB_FIND(CacheTree, &m_head, &find); + if (res) { + m_prev_req_item = res; + result = &res->m_uis; + } + } + if (result) { + uis = *result; + return true; + } + return false; + } + + void setUnwindInfoSectionsForPC(CacheItemKey key, UnwindInfoSections &uis) { + CacheItem *p_item = new CacheItem(uis, key); + if (p_item == nullptr) + return; + RB_INSERT(CacheTree, &m_head, p_item); + } + +private: + CacheItem *m_prev_req_item = nullptr; + RB_HEAD(CacheTree, CacheItem) m_head; + RB_GENERATE(CacheTree, CacheItem, entry, CacheCmp); +}; /// LocalAddressSpace is used as a template parameter to UnwindCursor when /// unwinding a thread in the same process. The wrappers compile away, diff --git a/gnu/llvm/libunwind/src/UnwindCursor.hpp b/gnu/llvm/libunwind/src/UnwindCursor.hpp index 06e65419735..6f6165fdd2b 100644 --- a/gnu/llvm/libunwind/src/UnwindCursor.hpp +++ b/gnu/llvm/libunwind/src/UnwindCursor.hpp @@ -90,6 +90,8 @@ extern "C" _Unwind_Reason_Code __libunwind_seh_personality( namespace libunwind { +static thread_local UnwindInfoSectionsCache uwis_cache; + #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) /// Cache of recently found FDEs. template @@ -2600,7 +2602,14 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { // Ask address space object to find unwind sections for this pc. UnwindInfoSections sects; - if (_addressSpace.findUnwindSections(pc, sects)) { + bool have_sects = false; + if (uwis_cache.getUnwindInfoSectionsForPC(pc, sects)) + have_sects = true; + else if (_addressSpace.findUnwindSections(pc, sects)) { + uwis_cache.setUnwindInfoSectionsForPC(pc, sects); + have_sects = true; + } + if (have_sects) { #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) // If there is a compact unwind encoding table, look there first. if (sects.compact_unwind_section != 0) {