Path: blob/main/contrib/llvm-project/libunwind/src/FrameHeaderCache.hpp
35148 views
//===-FrameHeaderCache.hpp ------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6// Cache the elf program headers necessary to unwind the stack more efficiently7// in the presence of many dsos.8//9//===----------------------------------------------------------------------===//1011#ifndef __FRAMEHEADER_CACHE_HPP__12#define __FRAMEHEADER_CACHE_HPP__1314#include "config.h"15#include <limits.h>1617#ifdef _LIBUNWIND_DEBUG_FRAMEHEADER_CACHE18#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE0(x) _LIBUNWIND_LOG0(x)19#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE(msg, ...) \20_LIBUNWIND_LOG(msg, __VA_ARGS__)21#else22#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE0(x)23#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE(msg, ...)24#endif2526// This cache should only be be used from within a dl_iterate_phdr callback.27// dl_iterate_phdr does the necessary synchronization to prevent problems28// with concurrent access via the libc load lock. Adding synchronization29// for other uses is possible, but not currently done.3031class _LIBUNWIND_HIDDEN FrameHeaderCache {32struct CacheEntry {33uintptr_t LowPC() { return Info.dso_base; }34uintptr_t HighPC() { return Info.dso_base + Info.text_segment_length; }35UnwindInfoSections Info;36CacheEntry *Next;37};3839static const size_t kCacheEntryCount = 8;4041// Can't depend on the C++ standard library in libunwind, so use an array to42// allocate the entries, and two linked lists for ordering unused and recently43// used entries. FIXME: Would the extra memory for a doubly-linked list44// be better than the runtime cost of traversing a very short singly-linked45// list on a cache miss? The entries themselves are all small and consecutive,46// so unlikely to cause page faults when following the pointers. The memory47// spent on additional pointers could also be spent on more entries.4849CacheEntry Entries[kCacheEntryCount];50CacheEntry *MostRecentlyUsed;51CacheEntry *Unused;5253void resetCache() {54_LIBUNWIND_FRAMEHEADERCACHE_TRACE0("FrameHeaderCache reset");55MostRecentlyUsed = nullptr;56Unused = &Entries[0];57for (size_t i = 0; i < kCacheEntryCount - 1; i++) {58Entries[i].Next = &Entries[i + 1];59}60Entries[kCacheEntryCount - 1].Next = nullptr;61}6263bool cacheNeedsReset(dl_phdr_info *PInfo) {64// C libraries increment dl_phdr_info.adds and dl_phdr_info.subs when65// loading and unloading shared libraries. If these values change between66// iterations of dl_iterate_phdr, then invalidate the cache.6768// These are static to avoid needing an initializer, and unsigned long long69// because that is their type within the extended dl_phdr_info. Initialize70// these to something extremely unlikely to be found upon the first call to71// dl_iterate_phdr.72static unsigned long long LastAdds = ULLONG_MAX;73static unsigned long long LastSubs = ULLONG_MAX;74if (PInfo->dlpi_adds != LastAdds || PInfo->dlpi_subs != LastSubs) {75// Resetting the entire cache is a big hammer, but this path is rare--76// usually just on the very first call, when the cache is empty anyway--so77// added complexity doesn't buy much.78LastAdds = PInfo->dlpi_adds;79LastSubs = PInfo->dlpi_subs;80resetCache();81return true;82}83return false;84}8586public:87bool find(dl_phdr_info *PInfo, size_t, void *data) {88if (cacheNeedsReset(PInfo) || MostRecentlyUsed == nullptr)89return false;9091auto *CBData = static_cast<dl_iterate_cb_data *>(data);92CacheEntry *Current = MostRecentlyUsed;93CacheEntry *Previous = nullptr;94while (Current != nullptr) {95_LIBUNWIND_FRAMEHEADERCACHE_TRACE(96"FrameHeaderCache check %lx in [%lx - %lx)", CBData->targetAddr,97Current->LowPC(), Current->HighPC());98if (Current->LowPC() <= CBData->targetAddr &&99CBData->targetAddr < Current->HighPC()) {100_LIBUNWIND_FRAMEHEADERCACHE_TRACE(101"FrameHeaderCache hit %lx in [%lx - %lx)", CBData->targetAddr,102Current->LowPC(), Current->HighPC());103if (Previous) {104// If there is no Previous, then Current is already the105// MostRecentlyUsed, and no need to move it up.106Previous->Next = Current->Next;107Current->Next = MostRecentlyUsed;108MostRecentlyUsed = Current;109}110*CBData->sects = Current->Info;111return true;112}113Previous = Current;114Current = Current->Next;115}116_LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache miss for address %lx",117CBData->targetAddr);118return false;119}120121void add(const UnwindInfoSections *UIS) {122CacheEntry *Current = nullptr;123124if (Unused != nullptr) {125Current = Unused;126Unused = Unused->Next;127} else {128Current = MostRecentlyUsed;129CacheEntry *Previous = nullptr;130while (Current->Next != nullptr) {131Previous = Current;132Current = Current->Next;133}134Previous->Next = nullptr;135_LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache evict [%lx - %lx)",136Current->LowPC(), Current->HighPC());137}138139Current->Info = *UIS;140Current->Next = MostRecentlyUsed;141MostRecentlyUsed = Current;142_LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache add [%lx - %lx)",143MostRecentlyUsed->LowPC(),144MostRecentlyUsed->HighPC());145}146};147148#endif // __FRAMEHEADER_CACHE_HPP__149150151