#ifndef __FRAMEHEADER_CACHE_HPP__
#define __FRAMEHEADER_CACHE_HPP__
#include "config.h"
#include <limits.h>
#ifdef _LIBUNWIND_DEBUG_FRAMEHEADER_CACHE
#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE0(x) _LIBUNWIND_LOG0(x)
#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE(msg, ...) \
_LIBUNWIND_LOG(msg, __VA_ARGS__)
#else
#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE0(x)
#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE(msg, ...)
#endif
class _LIBUNWIND_HIDDEN FrameHeaderCache {
struct CacheEntry {
uintptr_t LowPC() { return Info.dso_base; }
uintptr_t HighPC() { return Info.dso_base + Info.text_segment_length; }
UnwindInfoSections Info;
CacheEntry *Next;
};
static const size_t kCacheEntryCount = 8;
CacheEntry Entries[kCacheEntryCount];
CacheEntry *MostRecentlyUsed;
CacheEntry *Unused;
void resetCache() {
_LIBUNWIND_FRAMEHEADERCACHE_TRACE0("FrameHeaderCache reset");
MostRecentlyUsed = nullptr;
Unused = &Entries[0];
for (size_t i = 0; i < kCacheEntryCount - 1; i++) {
Entries[i].Next = &Entries[i + 1];
}
Entries[kCacheEntryCount - 1].Next = nullptr;
}
bool cacheNeedsReset(dl_phdr_info *PInfo) {
static unsigned long long LastAdds = ULLONG_MAX;
static unsigned long long LastSubs = ULLONG_MAX;
if (PInfo->dlpi_adds != LastAdds || PInfo->dlpi_subs != LastSubs) {
LastAdds = PInfo->dlpi_adds;
LastSubs = PInfo->dlpi_subs;
resetCache();
return true;
}
return false;
}
public:
bool find(dl_phdr_info *PInfo, size_t, void *data) {
if (cacheNeedsReset(PInfo) || MostRecentlyUsed == nullptr)
return false;
auto *CBData = static_cast<dl_iterate_cb_data *>(data);
CacheEntry *Current = MostRecentlyUsed;
CacheEntry *Previous = nullptr;
while (Current != nullptr) {
_LIBUNWIND_FRAMEHEADERCACHE_TRACE(
"FrameHeaderCache check %lx in [%lx - %lx)", CBData->targetAddr,
Current->LowPC(), Current->HighPC());
if (Current->LowPC() <= CBData->targetAddr &&
CBData->targetAddr < Current->HighPC()) {
_LIBUNWIND_FRAMEHEADERCACHE_TRACE(
"FrameHeaderCache hit %lx in [%lx - %lx)", CBData->targetAddr,
Current->LowPC(), Current->HighPC());
if (Previous) {
Previous->Next = Current->Next;
Current->Next = MostRecentlyUsed;
MostRecentlyUsed = Current;
}
*CBData->sects = Current->Info;
return true;
}
Previous = Current;
Current = Current->Next;
}
_LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache miss for address %lx",
CBData->targetAddr);
return false;
}
void add(const UnwindInfoSections *UIS) {
CacheEntry *Current = nullptr;
if (Unused != nullptr) {
Current = Unused;
Unused = Unused->Next;
} else {
Current = MostRecentlyUsed;
CacheEntry *Previous = nullptr;
while (Current->Next != nullptr) {
Previous = Current;
Current = Current->Next;
}
Previous->Next = nullptr;
_LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache evict [%lx - %lx)",
Current->LowPC(), Current->HighPC());
}
Current->Info = *UIS;
Current->Next = MostRecentlyUsed;
MostRecentlyUsed = Current;
_LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache add [%lx - %lx)",
MostRecentlyUsed->LowPC(),
MostRecentlyUsed->HighPC());
}
};
#endif