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