Warning: That file was not part of the compilation database. It may have many parsing errors.

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
32class _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
87public:
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

Warning: That file was not part of the compilation database. It may have many parsing errors.

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of libunwind/src/FrameHeaderCache.hpp