Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/libunwind/src/FrameHeaderCache.hpp
35148 views
1
//===-FrameHeaderCache.hpp ------------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
// Cache the elf program headers necessary to unwind the stack more efficiently
8
// in the presence of many dsos.
9
//
10
//===----------------------------------------------------------------------===//
11
12
#ifndef __FRAMEHEADER_CACHE_HPP__
13
#define __FRAMEHEADER_CACHE_HPP__
14
15
#include "config.h"
16
#include <limits.h>
17
18
#ifdef _LIBUNWIND_DEBUG_FRAMEHEADER_CACHE
19
#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE0(x) _LIBUNWIND_LOG0(x)
20
#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE(msg, ...) \
21
_LIBUNWIND_LOG(msg, __VA_ARGS__)
22
#else
23
#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE0(x)
24
#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE(msg, ...)
25
#endif
26
27
// This cache should only be be used from within a dl_iterate_phdr callback.
28
// dl_iterate_phdr does the necessary synchronization to prevent problems
29
// with concurrent access via the libc load lock. Adding synchronization
30
// for other uses is possible, but not currently done.
31
32
class _LIBUNWIND_HIDDEN FrameHeaderCache {
33
struct CacheEntry {
34
uintptr_t LowPC() { return Info.dso_base; }
35
uintptr_t HighPC() { return Info.dso_base + Info.text_segment_length; }
36
UnwindInfoSections Info;
37
CacheEntry *Next;
38
};
39
40
static const size_t kCacheEntryCount = 8;
41
42
// Can't depend on the C++ standard library in libunwind, so use an array to
43
// allocate the entries, and two linked lists for ordering unused and recently
44
// used entries. FIXME: Would the extra memory for a doubly-linked list
45
// be better than the runtime cost of traversing a very short singly-linked
46
// list on a cache miss? The entries themselves are all small and consecutive,
47
// so unlikely to cause page faults when following the pointers. The memory
48
// spent on additional pointers could also be spent on more entries.
49
50
CacheEntry Entries[kCacheEntryCount];
51
CacheEntry *MostRecentlyUsed;
52
CacheEntry *Unused;
53
54
void resetCache() {
55
_LIBUNWIND_FRAMEHEADERCACHE_TRACE0("FrameHeaderCache reset");
56
MostRecentlyUsed = nullptr;
57
Unused = &Entries[0];
58
for (size_t i = 0; i < kCacheEntryCount - 1; i++) {
59
Entries[i].Next = &Entries[i + 1];
60
}
61
Entries[kCacheEntryCount - 1].Next = nullptr;
62
}
63
64
bool cacheNeedsReset(dl_phdr_info *PInfo) {
65
// C libraries increment dl_phdr_info.adds and dl_phdr_info.subs when
66
// loading and unloading shared libraries. If these values change between
67
// iterations of dl_iterate_phdr, then invalidate the cache.
68
69
// These are static to avoid needing an initializer, and unsigned long long
70
// because that is their type within the extended dl_phdr_info. Initialize
71
// these to something extremely unlikely to be found upon the first call to
72
// dl_iterate_phdr.
73
static unsigned long long LastAdds = ULLONG_MAX;
74
static unsigned long long LastSubs = ULLONG_MAX;
75
if (PInfo->dlpi_adds != LastAdds || PInfo->dlpi_subs != LastSubs) {
76
// Resetting the entire cache is a big hammer, but this path is rare--
77
// usually just on the very first call, when the cache is empty anyway--so
78
// added complexity doesn't buy much.
79
LastAdds = PInfo->dlpi_adds;
80
LastSubs = PInfo->dlpi_subs;
81
resetCache();
82
return true;
83
}
84
return false;
85
}
86
87
public:
88
bool find(dl_phdr_info *PInfo, size_t, void *data) {
89
if (cacheNeedsReset(PInfo) || MostRecentlyUsed == nullptr)
90
return false;
91
92
auto *CBData = static_cast<dl_iterate_cb_data *>(data);
93
CacheEntry *Current = MostRecentlyUsed;
94
CacheEntry *Previous = nullptr;
95
while (Current != nullptr) {
96
_LIBUNWIND_FRAMEHEADERCACHE_TRACE(
97
"FrameHeaderCache check %lx in [%lx - %lx)", CBData->targetAddr,
98
Current->LowPC(), Current->HighPC());
99
if (Current->LowPC() <= CBData->targetAddr &&
100
CBData->targetAddr < Current->HighPC()) {
101
_LIBUNWIND_FRAMEHEADERCACHE_TRACE(
102
"FrameHeaderCache hit %lx in [%lx - %lx)", CBData->targetAddr,
103
Current->LowPC(), Current->HighPC());
104
if (Previous) {
105
// If there is no Previous, then Current is already the
106
// MostRecentlyUsed, and no need to move it up.
107
Previous->Next = Current->Next;
108
Current->Next = MostRecentlyUsed;
109
MostRecentlyUsed = Current;
110
}
111
*CBData->sects = Current->Info;
112
return true;
113
}
114
Previous = Current;
115
Current = Current->Next;
116
}
117
_LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache miss for address %lx",
118
CBData->targetAddr);
119
return false;
120
}
121
122
void add(const UnwindInfoSections *UIS) {
123
CacheEntry *Current = nullptr;
124
125
if (Unused != nullptr) {
126
Current = Unused;
127
Unused = Unused->Next;
128
} else {
129
Current = MostRecentlyUsed;
130
CacheEntry *Previous = nullptr;
131
while (Current->Next != nullptr) {
132
Previous = Current;
133
Current = Current->Next;
134
}
135
Previous->Next = nullptr;
136
_LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache evict [%lx - %lx)",
137
Current->LowPC(), Current->HighPC());
138
}
139
140
Current->Info = *UIS;
141
Current->Next = MostRecentlyUsed;
142
MostRecentlyUsed = Current;
143
_LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache add [%lx - %lx)",
144
MostRecentlyUsed->LowPC(),
145
MostRecentlyUsed->HighPC());
146
}
147
};
148
149
#endif // __FRAMEHEADER_CACHE_HPP__
150
151