Directory: | cvmfs/ |
---|---|
File: | cvmfs/sqlitemem.cc |
Date: | 2025-06-22 02:36:02 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 154 | 159 | 96.9% |
Branches: | 80 | 122 | 65.6% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | */ | ||
4 | |||
5 | #define __STDC_FORMAT_MACROS | ||
6 | |||
7 | |||
8 | #include "sqlitemem.h" | ||
9 | |||
10 | #include <cassert> | ||
11 | #include <cstddef> | ||
12 | #include <cstring> | ||
13 | #include <new> | ||
14 | |||
15 | #include "malloc_arena.h" | ||
16 | #include "util/concurrency.h" | ||
17 | #include "util/exception.h" | ||
18 | #include "util/smalloc.h" | ||
19 | |||
20 | using namespace std; // NOLINT | ||
21 | |||
22 | |||
23 | 17112 | void *SqliteMemoryManager::LookasideBufferArena::GetBuffer() { | |
24 |
2/2✓ Branch 0 taken 40248 times.
✓ Branch 1 taken 144 times.
|
40392 | for (unsigned i = 0; i < kNoBitmaps; ++i) { |
25 | 40248 | const int bit_set = ffs(freemap_[i]); | |
26 |
2/2✓ Branch 0 taken 16968 times.
✓ Branch 1 taken 23280 times.
|
40248 | if (bit_set != 0) { |
27 | 16968 | freemap_[i] &= ~(1 << (bit_set - 1)); // set bit to zero | |
28 | 16968 | const int nbuffer = i * sizeof(int) * 8 + bit_set - 1; | |
29 | 16968 | return reinterpret_cast<char *>(arena_) + nbuffer * kBufferSize; | |
30 | } | ||
31 | } | ||
32 | 144 | return NULL; | |
33 | } | ||
34 | |||
35 | |||
36 | 6384 | bool SqliteMemoryManager::LookasideBufferArena::IsEmpty() { | |
37 |
2/2✓ Branch 0 taken 16176 times.
✓ Branch 1 taken 192 times.
|
16368 | for (unsigned i = 0; i < kNoBitmaps; ++i) { |
38 |
2/2✓ Branch 0 taken 6192 times.
✓ Branch 1 taken 9984 times.
|
16176 | if (~freemap_[i] != 0) |
39 | 6192 | return false; | |
40 | } | ||
41 | 192 | return true; | |
42 | } | ||
43 | |||
44 | |||
45 | 17112 | bool SqliteMemoryManager::LookasideBufferArena::Contains(void *buffer) { | |
46 |
3/4✓ Branch 0 taken 17112 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
✓ Branch 3 taken 17064 times.
|
17112 | if ((buffer == NULL) || (buffer < arena_)) |
47 | 48 | return false; | |
48 | 17064 | return (static_cast<uint64_t>((reinterpret_cast<char *>(buffer) | |
49 | 17064 | - reinterpret_cast<char *>(arena_))) | |
50 | 17064 | < kArenaSize); | |
51 | } | ||
52 | |||
53 | |||
54 | 3562 | SqliteMemoryManager::LookasideBufferArena::LookasideBufferArena() | |
55 | 3562 | : arena_(sxmmap(kArenaSize)) { | |
56 | // All buffers unused, i.e. all bits set | ||
57 | 3562 | memset(freemap_, 0xFF, kNoBitmaps * sizeof(int)); | |
58 | 3562 | } | |
59 | |||
60 | |||
61 | 3558 | SqliteMemoryManager::LookasideBufferArena::~LookasideBufferArena() { | |
62 | 3558 | sxunmap(arena_, kArenaSize); | |
63 | 3558 | } | |
64 | |||
65 | |||
66 | 16968 | void SqliteMemoryManager::LookasideBufferArena::PutBuffer(void *buffer) { | |
67 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16968 times.
|
16968 | assert(buffer >= arena_); |
68 | 16968 | const ptrdiff_t nbuffer = | |
69 | 16968 | (reinterpret_cast<char *>(buffer) - reinterpret_cast<char *>(arena_)) / | |
70 | kBufferSize; | ||
71 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16968 times.
|
16968 | assert(static_cast<uint64_t>(nbuffer) < kBuffersPerArena); |
72 | 16968 | const int nfreemap = nbuffer / (sizeof(int) * 8); | |
73 | 16968 | freemap_[nfreemap] |= 1 << (nbuffer % (sizeof(int) * 8)); | |
74 | 16968 | } | |
75 | |||
76 | |||
77 | //------------------------------------------------------------------------------ | ||
78 | |||
79 | |||
80 | SqliteMemoryManager *SqliteMemoryManager::instance_ = NULL; | ||
81 | |||
82 | |||
83 | /** | ||
84 | * Sqlite ensures that size > 0. | ||
85 | */ | ||
86 | 2246069 | void *SqliteMemoryManager::xMalloc(int size) { | |
87 | 2246069 | return instance_->GetMemory(size); | |
88 | } | ||
89 | |||
90 | |||
91 | /** | ||
92 | * Sqlite ensures that ptr != NULL. | ||
93 | */ | ||
94 | 2245976 | void SqliteMemoryManager::xFree(void *ptr) { instance_->PutMemory(ptr); } | |
95 | |||
96 | |||
97 | /** | ||
98 | * Sqlite ensures that ptr != NULL and new_size > 0. | ||
99 | */ | ||
100 | 1062629 | void *SqliteMemoryManager::xRealloc(void *ptr, int new_size) { | |
101 | 1062629 | const int old_size = xSize(ptr); | |
102 |
2/2✓ Branch 0 taken 26359 times.
✓ Branch 1 taken 1036270 times.
|
1062629 | if (old_size >= new_size) |
103 | 26359 | return ptr; | |
104 | |||
105 | 1036270 | void *new_ptr = xMalloc(new_size); | |
106 | 1036270 | memcpy(new_ptr, ptr, old_size); | |
107 | 1036270 | xFree(ptr); | |
108 | 1036270 | return new_ptr; | |
109 | } | ||
110 | |||
111 | |||
112 | /** | ||
113 | * Sqlite ensures that ptr != NULL. | ||
114 | */ | ||
115 | 3750323 | int SqliteMemoryManager::xSize(void *ptr) { | |
116 | 3750323 | return instance_->GetMemorySize(ptr); | |
117 | } | ||
118 | |||
119 | |||
120 | 1312380 | int SqliteMemoryManager::xRoundup(int size) { return RoundUp8(size); } | |
121 | |||
122 | |||
123 | 3226 | int SqliteMemoryManager::xInit(void *app_data __attribute__((unused))) { | |
124 | 3226 | return SQLITE_OK; | |
125 | } | ||
126 | |||
127 | |||
128 | 3222 | void SqliteMemoryManager::xShutdown(void *app_data __attribute__((unused))) { } | |
129 | |||
130 | |||
131 | 3226 | void SqliteMemoryManager::AssignGlobalArenas() { | |
132 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3226 times.
|
3226 | if (assigned_) |
133 | ✗ | return; | |
134 | int retval; | ||
135 | |||
136 | 3226 | retval = sqlite3_config(SQLITE_CONFIG_PAGECACHE, page_cache_memory_, | |
137 | kPageCacheSlotSize, kPageCacheNoSlots); | ||
138 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3226 times.
|
3226 | assert(retval == SQLITE_OK); |
139 | |||
140 | 3226 | retval = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &sqlite3_mem_vanilla_); | |
141 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3226 times.
|
3226 | assert(retval == SQLITE_OK); |
142 | 3226 | retval = sqlite3_config(SQLITE_CONFIG_MALLOC, &mem_methods_); | |
143 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3226 times.
|
3226 | assert(retval == SQLITE_OK); |
144 | |||
145 | 3226 | assigned_ = true; | |
146 | } | ||
147 | |||
148 | |||
149 | /** | ||
150 | * Needs to be the first operation on an opened sqlite database. Returns the | ||
151 | * location of the buffer. | ||
152 | */ | ||
153 | 1512 | void *SqliteMemoryManager::AssignLookasideBuffer(sqlite3 *db) { | |
154 | 1512 | const MutexLockGuard lock_guard(lock_); | |
155 | |||
156 |
1/2✓ Branch 1 taken 1512 times.
✗ Branch 2 not taken.
|
1512 | void *buffer = GetLookasideBuffer(); |
157 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1512 times.
|
1512 | assert(buffer != NULL); |
158 | const int retval = | ||
159 |
1/2✓ Branch 1 taken 1512 times.
✗ Branch 2 not taken.
|
1512 | sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, buffer, |
160 | kLookasideSlotSize, kLookasideSlotsPerDb); | ||
161 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1512 times.
|
1512 | assert(retval == SQLITE_OK); |
162 | 1512 | return buffer; | |
163 | 1512 | } | |
164 | |||
165 | |||
166 | 3462 | void SqliteMemoryManager::CleanupInstance() { | |
167 |
1/2✓ Branch 0 taken 3462 times.
✗ Branch 1 not taken.
|
3462 | delete instance_; |
168 | 3462 | instance_ = NULL; | |
169 | 3462 | } | |
170 | |||
171 | |||
172 | /** | ||
173 | * Opens a new arena if necessary. | ||
174 | */ | ||
175 | 7704 | void *SqliteMemoryManager::GetLookasideBuffer() { | |
176 | void *result; | ||
177 | vector<LookasideBufferArena *>::reverse_iterator | ||
178 | 7704 | reverse_iter = lookaside_buffer_arenas_.rbegin(); | |
179 | const vector<LookasideBufferArena *>::reverse_iterator i_rend = | ||
180 | 7704 | lookaside_buffer_arenas_.rend(); | |
181 |
3/4✓ Branch 2 taken 7752 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7704 times.
✓ Branch 5 taken 48 times.
|
7752 | for (; reverse_iter != i_rend; ++reverse_iter) { |
182 | 7704 | result = (*reverse_iter)->GetBuffer(); | |
183 |
2/2✓ Branch 0 taken 7656 times.
✓ Branch 1 taken 48 times.
|
7704 | if (result != NULL) |
184 | 7656 | return result; | |
185 | } | ||
186 | |||
187 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | LookasideBufferArena *new_arena = new LookasideBufferArena(); |
188 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | lookaside_buffer_arenas_.push_back(new_arena); |
189 | 48 | return new_arena->GetBuffer(); | |
190 | } | ||
191 | |||
192 | |||
193 | 3750515 | int SqliteMemoryManager::GetMemorySize(void *ptr) { | |
194 | 3750515 | return MallocArena::GetMallocArena(ptr, kArenaSize)->GetSize(ptr); | |
195 | } | ||
196 | |||
197 | |||
198 | /** | ||
199 | * Opens new arenas as necessary. | ||
200 | */ | ||
201 | 3206357 | void *SqliteMemoryManager::GetMemory(int size) { | |
202 |
1/2✓ Branch 2 taken 3206357 times.
✗ Branch 3 not taken.
|
3206357 | void *p = malloc_arenas_[idx_last_arena_]->Malloc(size); |
203 |
2/2✓ Branch 0 taken 3206309 times.
✓ Branch 1 taken 48 times.
|
3206357 | if (p != NULL) |
204 | 3206309 | return p; | |
205 | 48 | const unsigned N = malloc_arenas_.size(); | |
206 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 48 times.
|
96 | for (unsigned i = 0; i < N; ++i) { |
207 |
1/2✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
|
48 | p = malloc_arenas_[i]->Malloc(size); |
208 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (p != NULL) { |
209 | ✗ | idx_last_arena_ = i; | |
210 | ✗ | return p; | |
211 | } | ||
212 | } | ||
213 | 48 | idx_last_arena_ = N; | |
214 |
2/4✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
|
48 | MallocArena *M = new MallocArena(kArenaSize); |
215 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | malloc_arenas_.push_back(M); |
216 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | p = M->Malloc(size); |
217 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | assert(p != NULL); |
218 | 48 | return p; | |
219 | } | ||
220 | |||
221 | |||
222 | 3466 | SqliteMemoryManager::SqliteMemoryManager() | |
223 | 3466 | : assigned_(false) | |
224 | 3466 | , page_cache_memory_(sxmmap(kPageCacheSize)) | |
225 | 3466 | , idx_last_arena_(0) { | |
226 | 3466 | memset(&sqlite3_mem_vanilla_, 0, sizeof(sqlite3_mem_vanilla_)); | |
227 | 3466 | const int retval = pthread_mutex_init(&lock_, NULL); | |
228 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3466 times.
|
3466 | assert(retval == 0); |
229 | |||
230 |
2/4✓ Branch 1 taken 3466 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3466 times.
✗ Branch 6 not taken.
|
3466 | lookaside_buffer_arenas_.push_back(new LookasideBufferArena()); |
231 |
3/6✓ Branch 1 taken 3466 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3466 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3466 times.
✗ Branch 8 not taken.
|
3466 | malloc_arenas_.push_back(new MallocArena(kArenaSize)); |
232 | |||
233 | 3466 | memset(&mem_methods_, 0, sizeof(mem_methods_)); | |
234 | 3466 | mem_methods_.xMalloc = xMalloc; | |
235 | 3466 | mem_methods_.xFree = xFree; | |
236 | 3466 | mem_methods_.xRealloc = xRealloc; | |
237 | 3466 | mem_methods_.xSize = xSize; | |
238 | 3466 | mem_methods_.xRoundup = xRoundup; | |
239 | 3466 | mem_methods_.xInit = xInit; | |
240 | 3466 | mem_methods_.xShutdown = xShutdown; | |
241 | 3466 | mem_methods_.pAppData = NULL; | |
242 | 3466 | } | |
243 | |||
244 | |||
245 | /** | ||
246 | * Must be executed only after sqlite3_shutdown. | ||
247 | */ | ||
248 | 3462 | SqliteMemoryManager::~SqliteMemoryManager() { | |
249 |
2/2✓ Branch 0 taken 3222 times.
✓ Branch 1 taken 240 times.
|
3462 | if (assigned_) { |
250 | // Reset sqlite to default values | ||
251 | int retval; | ||
252 | 3222 | retval = sqlite3_config(SQLITE_CONFIG_PAGECACHE, NULL, 0, 0); | |
253 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3222 times.
|
3222 | assert(retval == SQLITE_OK); |
254 | 3222 | retval = sqlite3_config(SQLITE_CONFIG_MALLOC, &sqlite3_mem_vanilla_); | |
255 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3222 times.
|
3222 | assert(retval == SQLITE_OK); |
256 | } | ||
257 | |||
258 | 3462 | sxunmap(page_cache_memory_, kPageCacheSize); | |
259 |
2/2✓ Branch 1 taken 3462 times.
✓ Branch 2 taken 3462 times.
|
6924 | for (unsigned i = 0; i < lookaside_buffer_arenas_.size(); ++i) |
260 |
1/2✓ Branch 1 taken 3462 times.
✗ Branch 2 not taken.
|
3462 | delete lookaside_buffer_arenas_[i]; |
261 |
2/2✓ Branch 1 taken 3462 times.
✓ Branch 2 taken 3462 times.
|
6924 | for (unsigned i = 0; i < malloc_arenas_.size(); ++i) |
262 |
1/2✓ Branch 1 taken 3462 times.
✗ Branch 2 not taken.
|
3462 | delete malloc_arenas_[i]; |
263 | 3462 | pthread_mutex_destroy(&lock_); | |
264 | 3462 | } | |
265 | |||
266 | |||
267 | /** | ||
268 | * Only entirely empty arenas are freed to the system. In cvmfs, catalogs | ||
269 | * are gradually opened and sometimes close altogether when a new root catalog | ||
270 | * arrives. Hence there is no fragmentation. | ||
271 | */ | ||
272 | 7704 | void SqliteMemoryManager::PutLookasideBuffer(void *buffer) { | |
273 | 7704 | const unsigned N = lookaside_buffer_arenas_.size(); | |
274 |
1/2✓ Branch 0 taken 7704 times.
✗ Branch 1 not taken.
|
7704 | for (unsigned i = 0; i < N; ++i) { |
275 |
1/2✓ Branch 2 taken 7704 times.
✗ Branch 3 not taken.
|
7704 | if (lookaside_buffer_arenas_[i]->Contains(buffer)) { |
276 | 7704 | lookaside_buffer_arenas_[i]->PutBuffer(buffer); | |
277 |
6/6✓ Branch 0 taken 6144 times.
✓ Branch 1 taken 1560 times.
✓ Branch 4 taken 48 times.
✓ Branch 5 taken 6096 times.
✓ Branch 6 taken 48 times.
✓ Branch 7 taken 7656 times.
|
7704 | if ((N > 1) && lookaside_buffer_arenas_[i]->IsEmpty()) { |
278 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | delete lookaside_buffer_arenas_[i]; |
279 |
1/2✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
|
48 | lookaside_buffer_arenas_.erase(lookaside_buffer_arenas_.begin() + i); |
280 | } | ||
281 | 7704 | return; | |
282 | } | ||
283 | } | ||
284 | ✗ | PANIC(NULL); | |
285 | } | ||
286 | |||
287 | |||
288 | /** | ||
289 | * Closes empty areas. | ||
290 | */ | ||
291 | 2246168 | void SqliteMemoryManager::PutMemory(void *ptr) { | |
292 | 2246168 | MallocArena *M = MallocArena::GetMallocArena(ptr, kArenaSize); | |
293 | 2246168 | M->Free(ptr); | |
294 | 2246168 | const unsigned N = malloc_arenas_.size(); | |
295 |
5/6✓ Branch 0 taken 48 times.
✓ Branch 1 taken 2246120 times.
✓ Branch 3 taken 48 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 48 times.
✓ Branch 6 taken 2246120 times.
|
2246168 | if ((N > 1) && M->IsEmpty()) { |
296 |
1/2✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
|
96 | for (unsigned i = 0; i < N; ++i) { |
297 |
2/2✓ Branch 1 taken 48 times.
✓ Branch 2 taken 48 times.
|
96 | if (malloc_arenas_[i] == M) { |
298 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | delete malloc_arenas_[i]; |
299 |
1/2✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
|
48 | malloc_arenas_.erase(malloc_arenas_.begin() + i); |
300 | 48 | idx_last_arena_ = 0; | |
301 | 48 | return; | |
302 | } | ||
303 | } | ||
304 | ✗ | PANIC(NULL); | |
305 | } | ||
306 | } | ||
307 | |||
308 | |||
309 | /** | ||
310 | * To be used after an sqlite database has been closed. | ||
311 | */ | ||
312 | 1512 | void SqliteMemoryManager::ReleaseLookasideBuffer(void *buffer) { | |
313 | 1512 | const MutexLockGuard lock_guard(lock_); | |
314 |
1/2✓ Branch 1 taken 1512 times.
✗ Branch 2 not taken.
|
1512 | PutLookasideBuffer(buffer); |
315 | 1512 | } | |
316 |