Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * slab.c
4 : * SLAB allocator definitions.
5 : *
6 : * SLAB is a MemoryContext implementation designed for cases where large
7 : * numbers of equally-sized objects are allocated (and freed).
8 : *
9 : *
10 : * Portions Copyright (c) 2017, PostgreSQL Global Development Group
11 : *
12 : * IDENTIFICATION
13 : * src/backend/utils/mmgr/slab.c
14 : *
15 : *
16 : * NOTE:
17 : * The constant allocation size allows significant simplification and various
18 : * optimizations over more general purpose allocators. The blocks are carved
19 : * into chunks of exactly the right size (plus alignment), not wasting any
20 : * memory.
21 : *
22 : * The information about free chunks is maintained both at the block level and
23 : * global (context) level. This is possible as the chunk size (and thus also
24 : * the number of chunks per block) is fixed.
25 : *
26 : * On each block, free chunks are tracked in a simple linked list. Contents
27 : * of free chunks is replaced with an index of the next free chunk, forming
28 : * a very simple linked list. Each block also contains a counter of free
29 : * chunks. Combined with the local block-level freelist, it makes it trivial
30 : * to eventually free the whole block.
31 : *
32 : * At the context level, we use 'freelist' to track blocks ordered by number
33 : * of free chunks, starting with blocks having a single allocated chunk, and
34 : * with completely full blocks on the tail.
35 : *
36 : * This also allows various optimizations - for example when searching for
37 : * free chunk, the allocator reuses space from the fullest blocks first, in
38 : * the hope that some of the less full blocks will get completely empty (and
39 : * returned back to the OS).
40 : *
41 : * For each block, we maintain pointer to the first free chunk - this is quite
42 : * cheap and allows us to skip all the preceding used chunks, eliminating
43 : * a significant number of lookups in many common usage patters. In the worst
44 : * case this performs as if the pointer was not maintained.
45 : *
46 : * We cache the freelist index for the blocks with the fewest free chunks
47 : * (minFreeChunks), so that we don't have to search the freelist on every
48 : * SlabAlloc() call, which is quite expensive.
49 : *
50 : *-------------------------------------------------------------------------
51 : */
52 :
53 : #include "postgres.h"
54 :
55 : #include "utils/memdebug.h"
56 : #include "utils/memutils.h"
57 : #include "lib/ilist.h"
58 :
59 :
60 : /*
61 : * SlabContext is a specialized implementation of MemoryContext.
62 : */
63 : typedef struct SlabContext
64 : {
65 : MemoryContextData header; /* Standard memory-context fields */
66 : /* Allocation parameters for this context: */
67 : Size chunkSize; /* chunk size */
68 : Size fullChunkSize; /* chunk size including header and alignment */
69 : Size blockSize; /* block size */
70 : int chunksPerBlock; /* number of chunks per block */
71 : int minFreeChunks; /* min number of free chunks in any block */
72 : int nblocks; /* number of blocks allocated */
73 : /* blocks with free space, grouped by number of free chunks: */
74 : dlist_head freelist[FLEXIBLE_ARRAY_MEMBER];
75 : } SlabContext;
76 :
77 : /*
78 : * SlabBlock
79 : * Structure of a single block in SLAB allocator.
80 : *
81 : * node: doubly-linked list of blocks in global freelist
82 : * nfree: number of free chunks in this block
83 : * firstFreeChunk: index of the first free chunk
84 : */
85 : typedef struct SlabBlock
86 : {
87 : dlist_node node; /* doubly-linked list */
88 : int nfree; /* number of free chunks */
89 : int firstFreeChunk; /* index of the first free chunk in the block */
90 : } SlabBlock;
91 :
92 : /*
93 : * SlabChunk
94 : * The prefix of each piece of memory in an SlabBlock
95 : */
96 : typedef struct SlabChunk
97 : {
98 : /* block owning this chunk */
99 : void *block;
100 : SlabContext *slab; /* owning context */
101 : /* there must not be any padding to reach a MAXALIGN boundary here! */
102 : } SlabChunk;
103 :
104 :
105 : #define SlabPointerGetChunk(ptr) \
106 : ((SlabChunk *)(((char *)(ptr)) - sizeof(SlabChunk)))
107 : #define SlabChunkGetPointer(chk) \
108 : ((void *)(((char *)(chk)) + sizeof(SlabChunk)))
109 : #define SlabBlockGetChunk(slab, block, idx) \
110 : ((SlabChunk *) ((char *) (block) + sizeof(SlabBlock) \
111 : + (idx * slab->fullChunkSize)))
112 : #define SlabBlockStart(block) \
113 : ((char *) block + sizeof(SlabBlock))
114 : #define SlabChunkIndex(slab, block, chunk) \
115 : (((char *) chunk - SlabBlockStart(block)) / slab->fullChunkSize)
116 :
117 : /*
118 : * These functions implement the MemoryContext API for Slab contexts.
119 : */
120 : static void *SlabAlloc(MemoryContext context, Size size);
121 : static void SlabFree(MemoryContext context, void *pointer);
122 : static void *SlabRealloc(MemoryContext context, void *pointer, Size size);
123 : static void SlabInit(MemoryContext context);
124 : static void SlabReset(MemoryContext context);
125 : static void SlabDelete(MemoryContext context);
126 : static Size SlabGetChunkSpace(MemoryContext context, void *pointer);
127 : static bool SlabIsEmpty(MemoryContext context);
128 : static void SlabStats(MemoryContext context, int level, bool print,
129 : MemoryContextCounters *totals);
130 : #ifdef MEMORY_CONTEXT_CHECKING
131 : static void SlabCheck(MemoryContext context);
132 : #endif
133 :
134 : /*
135 : * This is the virtual function table for Slab contexts.
136 : */
137 : static MemoryContextMethods SlabMethods = {
138 : SlabAlloc,
139 : SlabFree,
140 : SlabRealloc,
141 : SlabInit,
142 : SlabReset,
143 : SlabDelete,
144 : SlabGetChunkSpace,
145 : SlabIsEmpty,
146 : SlabStats
147 : #ifdef MEMORY_CONTEXT_CHECKING
148 : ,SlabCheck
149 : #endif
150 : };
151 :
152 : /* ----------
153 : * Debug macros
154 : * ----------
155 : */
156 : #ifdef HAVE_ALLOCINFO
157 : #define SlabFreeInfo(_cxt, _chunk) \
158 : fprintf(stderr, "SlabFree: %s: %p, %zu\n", \
159 : (_cxt)->header.name, (_chunk), (_chunk)->header.size)
160 : #define SlabAllocInfo(_cxt, _chunk) \
161 : fprintf(stderr, "SlabAlloc: %s: %p, %zu\n", \
162 : (_cxt)->header.name, (_chunk), (_chunk)->header.size)
163 : #else
164 : #define SlabFreeInfo(_cxt, _chunk)
165 : #define SlabAllocInfo(_cxt, _chunk)
166 : #endif
167 :
168 :
169 : /*
170 : * SlabContextCreate
171 : * Create a new Slab context.
172 : *
173 : * parent: parent context, or NULL if top-level context
174 : * name: name of context (for debugging --- string will be copied)
175 : * blockSize: allocation block size
176 : * chunkSize: allocation chunk size
177 : *
178 : * The chunkSize may not exceed:
179 : * MAXALIGN_DOWN(SIZE_MAX) - MAXALIGN(sizeof(SlabBlock)) - SLAB_CHUNKHDRSZ
180 : *
181 : */
182 : MemoryContext
183 0 : SlabContextCreate(MemoryContext parent,
184 : const char *name,
185 : Size blockSize,
186 : Size chunkSize)
187 : {
188 : int chunksPerBlock;
189 : Size fullChunkSize;
190 : Size freelistSize;
191 : SlabContext *slab;
192 :
193 : StaticAssertStmt(offsetof(SlabChunk, slab) + sizeof(MemoryContext) ==
194 : MAXALIGN(sizeof(SlabChunk)),
195 : "padding calculation in SlabChunk is wrong");
196 :
197 : /* Make sure the linked list node fits inside a freed chunk */
198 0 : if (chunkSize < sizeof(int))
199 0 : chunkSize = sizeof(int);
200 :
201 : /* chunk, including SLAB header (both addresses nicely aligned) */
202 0 : fullChunkSize = MAXALIGN(sizeof(SlabChunk) + MAXALIGN(chunkSize));
203 :
204 : /* Make sure the block can store at least one chunk. */
205 0 : if (blockSize - sizeof(SlabBlock) < fullChunkSize)
206 0 : elog(ERROR, "block size %zu for slab is too small for %zu chunks",
207 : blockSize, chunkSize);
208 :
209 : /* Compute maximum number of chunks per block */
210 0 : chunksPerBlock = (blockSize - sizeof(SlabBlock)) / fullChunkSize;
211 :
212 : /* The freelist starts with 0, ends with chunksPerBlock. */
213 0 : freelistSize = sizeof(dlist_head) * (chunksPerBlock + 1);
214 :
215 : /* if we can't fit at least one chunk into the block, we're hosed */
216 0 : Assert(chunksPerBlock > 0);
217 :
218 : /* make sure the chunks actually fit on the block */
219 0 : Assert((fullChunkSize * chunksPerBlock) + sizeof(SlabBlock) <= blockSize);
220 :
221 : /* Do the type-independent part of context creation */
222 0 : slab = (SlabContext *)
223 0 : MemoryContextCreate(T_SlabContext,
224 : (offsetof(SlabContext, freelist) + freelistSize),
225 : &SlabMethods,
226 : parent,
227 : name);
228 :
229 0 : slab->blockSize = blockSize;
230 0 : slab->chunkSize = chunkSize;
231 0 : slab->fullChunkSize = fullChunkSize;
232 0 : slab->chunksPerBlock = chunksPerBlock;
233 0 : slab->nblocks = 0;
234 0 : slab->minFreeChunks = 0;
235 :
236 0 : return (MemoryContext) slab;
237 : }
238 :
239 : /*
240 : * SlabInit
241 : * Context-type-specific initialization routine.
242 : */
243 : static void
244 0 : SlabInit(MemoryContext context)
245 : {
246 : int i;
247 0 : SlabContext *slab = castNode(SlabContext, context);
248 :
249 0 : Assert(slab);
250 :
251 : /* initialize the freelist slots */
252 0 : for (i = 0; i < (slab->chunksPerBlock + 1); i++)
253 0 : dlist_init(&slab->freelist[i]);
254 0 : }
255 :
256 : /*
257 : * SlabReset
258 : * Frees all memory which is allocated in the given set.
259 : *
260 : * The code simply frees all the blocks in the context - we don't keep any
261 : * keeper blocks or anything like that.
262 : */
263 : static void
264 0 : SlabReset(MemoryContext context)
265 : {
266 : int i;
267 0 : SlabContext *slab = castNode(SlabContext, context);
268 :
269 0 : Assert(slab);
270 :
271 : #ifdef MEMORY_CONTEXT_CHECKING
272 : /* Check for corruption and leaks before freeing */
273 0 : SlabCheck(context);
274 : #endif
275 :
276 : /* walk over freelists and free the blocks */
277 0 : for (i = 0; i <= slab->chunksPerBlock; i++)
278 : {
279 : dlist_mutable_iter miter;
280 :
281 0 : dlist_foreach_modify(miter, &slab->freelist[i])
282 : {
283 0 : SlabBlock *block = dlist_container(SlabBlock, node, miter.cur);
284 :
285 0 : dlist_delete(miter.cur);
286 :
287 : #ifdef CLOBBER_FREED_MEMORY
288 0 : wipe_mem(block, slab->blockSize);
289 : #endif
290 0 : free(block);
291 0 : slab->nblocks--;
292 : }
293 : }
294 :
295 0 : slab->minFreeChunks = 0;
296 :
297 0 : Assert(slab->nblocks == 0);
298 0 : }
299 :
300 : /*
301 : * SlabDelete
302 : * Frees all memory which is allocated in the given slab, in preparation
303 : * for deletion of the slab. We simply call SlabReset().
304 : */
305 : static void
306 0 : SlabDelete(MemoryContext context)
307 : {
308 : /* just reset the context */
309 0 : SlabReset(context);
310 0 : }
311 :
312 : /*
313 : * SlabAlloc
314 : * Returns pointer to allocated memory of given size or NULL if
315 : * request could not be completed; memory is added to the slab.
316 : */
317 : static void *
318 0 : SlabAlloc(MemoryContext context, Size size)
319 : {
320 0 : SlabContext *slab = castNode(SlabContext, context);
321 : SlabBlock *block;
322 : SlabChunk *chunk;
323 : int idx;
324 :
325 0 : Assert(slab);
326 :
327 0 : Assert((slab->minFreeChunks >= 0) &&
328 : (slab->minFreeChunks < slab->chunksPerBlock));
329 :
330 : /* make sure we only allow correct request size */
331 0 : if (size != slab->chunkSize)
332 0 : elog(ERROR, "unexpected alloc chunk size %zu (expected %zu)",
333 : size, slab->chunkSize);
334 :
335 : /*
336 : * If there are no free chunks in any existing block, create a new block
337 : * and put it to the last freelist bucket.
338 : *
339 : * slab->minFreeChunks == 0 means there are no blocks with free chunks,
340 : * thanks to how minFreeChunks is updated at the end of SlabAlloc().
341 : */
342 0 : if (slab->minFreeChunks == 0)
343 : {
344 0 : block = (SlabBlock *) malloc(slab->blockSize);
345 :
346 0 : if (block == NULL)
347 0 : return NULL;
348 :
349 0 : block->nfree = slab->chunksPerBlock;
350 0 : block->firstFreeChunk = 0;
351 :
352 : /*
353 : * Put all the chunks on a freelist. Walk the chunks and point each
354 : * one to the next one.
355 : */
356 0 : for (idx = 0; idx < slab->chunksPerBlock; idx++)
357 : {
358 0 : chunk = SlabBlockGetChunk(slab, block, idx);
359 0 : *(int32 *) SlabChunkGetPointer(chunk) = (idx + 1);
360 : }
361 :
362 : /*
363 : * And add it to the last freelist with all chunks empty.
364 : *
365 : * We know there are no blocks in the freelist, otherwise we wouldn't
366 : * need a new block.
367 : */
368 0 : Assert(dlist_is_empty(&slab->freelist[slab->chunksPerBlock]));
369 :
370 0 : dlist_push_head(&slab->freelist[slab->chunksPerBlock], &block->node);
371 :
372 0 : slab->minFreeChunks = slab->chunksPerBlock;
373 0 : slab->nblocks += 1;
374 : }
375 :
376 : /* grab the block from the freelist (even the new block is there) */
377 0 : block = dlist_head_element(SlabBlock, node,
378 : &slab->freelist[slab->minFreeChunks]);
379 :
380 : /* make sure we actually got a valid block, with matching nfree */
381 0 : Assert(block != NULL);
382 0 : Assert(slab->minFreeChunks == block->nfree);
383 0 : Assert(block->nfree > 0);
384 :
385 : /* we know index of the first free chunk in the block */
386 0 : idx = block->firstFreeChunk;
387 :
388 : /* make sure the chunk index is valid, and that it's marked as empty */
389 0 : Assert((idx >= 0) && (idx < slab->chunksPerBlock));
390 :
391 : /* compute the chunk location block start (after the block header) */
392 0 : chunk = SlabBlockGetChunk(slab, block, idx);
393 :
394 : /*
395 : * Update the block nfree count, and also the minFreeChunks as we've
396 : * decreased nfree for a block with the minimum number of free chunks
397 : * (because that's how we chose the block).
398 : */
399 0 : block->nfree--;
400 0 : slab->minFreeChunks = block->nfree;
401 :
402 : /*
403 : * Remove the chunk from the freelist head. The index of the next free
404 : * chunk is stored in the chunk itself.
405 : */
406 : VALGRIND_MAKE_MEM_DEFINED(SlabChunkGetPointer(chunk), sizeof(int32));
407 0 : block->firstFreeChunk = *(int32 *) SlabChunkGetPointer(chunk);
408 :
409 0 : Assert(block->firstFreeChunk >= 0);
410 0 : Assert(block->firstFreeChunk <= slab->chunksPerBlock);
411 :
412 0 : Assert((block->nfree != 0 &&
413 : block->firstFreeChunk < slab->chunksPerBlock) ||
414 : (block->nfree == 0 &&
415 : block->firstFreeChunk == slab->chunksPerBlock));
416 :
417 : /* move the whole block to the right place in the freelist */
418 0 : dlist_delete(&block->node);
419 0 : dlist_push_head(&slab->freelist[block->nfree], &block->node);
420 :
421 : /*
422 : * And finally update minFreeChunks, i.e. the index to the block with the
423 : * lowest number of free chunks. We only need to do that when the block
424 : * got full (otherwise we know the current block is the right one). We'll
425 : * simply walk the freelist until we find a non-empty entry.
426 : */
427 0 : if (slab->minFreeChunks == 0)
428 : {
429 0 : for (idx = 1; idx <= slab->chunksPerBlock; idx++)
430 : {
431 0 : if (dlist_is_empty(&slab->freelist[idx]))
432 0 : continue;
433 :
434 : /* found a non-empty freelist */
435 0 : slab->minFreeChunks = idx;
436 0 : break;
437 : }
438 : }
439 :
440 0 : if (slab->minFreeChunks == slab->chunksPerBlock)
441 0 : slab->minFreeChunks = 0;
442 :
443 : /* Prepare to initialize the chunk header. */
444 : VALGRIND_MAKE_MEM_UNDEFINED(chunk, sizeof(SlabChunk));
445 :
446 0 : chunk->block = (void *) block;
447 0 : chunk->slab = slab;
448 :
449 : #ifdef MEMORY_CONTEXT_CHECKING
450 : /* slab mark to catch clobber of "unused" space */
451 0 : if (slab->chunkSize < (slab->fullChunkSize - sizeof(SlabChunk)))
452 : {
453 0 : set_sentinel(SlabChunkGetPointer(chunk), size);
454 : VALGRIND_MAKE_MEM_NOACCESS(((char *) chunk) +
455 : sizeof(SlabChunk) + slab->chunkSize,
456 : slab->fullChunkSize -
457 : (slab->chunkSize + sizeof(SlabChunk)));
458 : }
459 : #endif
460 : #ifdef RANDOMIZE_ALLOCATED_MEMORY
461 : /* fill the allocated space with junk */
462 : randomize_mem((char *) SlabChunkGetPointer(chunk), size);
463 : #endif
464 :
465 : SlabAllocInfo(slab, chunk);
466 0 : return SlabChunkGetPointer(chunk);
467 : }
468 :
469 : /*
470 : * SlabFree
471 : * Frees allocated memory; memory is removed from the slab.
472 : */
473 : static void
474 0 : SlabFree(MemoryContext context, void *pointer)
475 : {
476 : int idx;
477 0 : SlabContext *slab = castNode(SlabContext, context);
478 0 : SlabChunk *chunk = SlabPointerGetChunk(pointer);
479 0 : SlabBlock *block = chunk->block;
480 :
481 : SlabFreeInfo(slab, chunk);
482 :
483 : #ifdef MEMORY_CONTEXT_CHECKING
484 : /* Test for someone scribbling on unused space in chunk */
485 0 : if (slab->chunkSize < (slab->fullChunkSize - sizeof(SlabChunk)))
486 0 : if (!sentinel_ok(pointer, slab->chunkSize))
487 0 : elog(WARNING, "detected write past chunk end in %s %p",
488 : slab->header.name, chunk);
489 : #endif
490 :
491 : /* compute index of the chunk with respect to block start */
492 0 : idx = SlabChunkIndex(slab, block, chunk);
493 :
494 : /* add chunk to freelist, and update block nfree count */
495 0 : *(int32 *) pointer = block->firstFreeChunk;
496 0 : block->firstFreeChunk = idx;
497 0 : block->nfree++;
498 :
499 0 : Assert(block->nfree > 0);
500 0 : Assert(block->nfree <= slab->chunksPerBlock);
501 :
502 : #ifdef CLOBBER_FREED_MEMORY
503 : /* XXX don't wipe the int32 index, used for block-level freelist */
504 0 : wipe_mem((char *) pointer + sizeof(int32),
505 0 : slab->chunkSize - sizeof(int32));
506 : #endif
507 :
508 : /* remove the block from a freelist */
509 0 : dlist_delete(&block->node);
510 :
511 : /*
512 : * See if we need to update the minFreeChunks field for the slab - we only
513 : * need to do that if there the block had that number of free chunks
514 : * before we freed one. In that case, we check if there still are blocks
515 : * in the original freelist and we either keep the current value (if there
516 : * still are blocks) or increment it by one (the new block is still the
517 : * one with minimum free chunks).
518 : *
519 : * The one exception is when the block will get completely free - in that
520 : * case we will free it, se we can't use it for minFreeChunks. It however
521 : * means there are no more blocks with free chunks.
522 : */
523 0 : if (slab->minFreeChunks == (block->nfree - 1))
524 : {
525 : /* Have we removed the last chunk from the freelist? */
526 0 : if (dlist_is_empty(&slab->freelist[slab->minFreeChunks]))
527 : {
528 : /* but if we made the block entirely free, we'll free it */
529 0 : if (block->nfree == slab->chunksPerBlock)
530 0 : slab->minFreeChunks = 0;
531 : else
532 0 : slab->minFreeChunks++;
533 : }
534 : }
535 :
536 : /* If the block is now completely empty, free it. */
537 0 : if (block->nfree == slab->chunksPerBlock)
538 : {
539 0 : free(block);
540 0 : slab->nblocks--;
541 : }
542 : else
543 0 : dlist_push_head(&slab->freelist[block->nfree], &block->node);
544 :
545 0 : Assert(slab->nblocks >= 0);
546 0 : }
547 :
548 : /*
549 : * SlabRealloc
550 : * Change the allocated size of a chunk.
551 : *
552 : * As Slab is designed for allocating equally-sized chunks of memory, it can't
553 : * do an actual chunk size change. We try to be gentle and allow calls with
554 : * exactly the same size, as in that case we can simply return the same
555 : * chunk. When the size differs, we throw an error.
556 : *
557 : * We could also allow requests with size < chunkSize. That however seems
558 : * rather pointless - Slab is meant for chunks of constant size, and moreover
559 : * realloc is usually used to enlarge the chunk.
560 : */
561 : static void *
562 0 : SlabRealloc(MemoryContext context, void *pointer, Size size)
563 : {
564 0 : SlabContext *slab = castNode(SlabContext, context);
565 :
566 0 : Assert(slab);
567 :
568 : /* can't do actual realloc with slab, but let's try to be gentle */
569 0 : if (size == slab->chunkSize)
570 0 : return pointer;
571 :
572 0 : elog(ERROR, "slab allocator does not support realloc()");
573 : return NULL; /* keep compiler quiet */
574 : }
575 :
576 : /*
577 : * SlabGetChunkSpace
578 : * Given a currently-allocated chunk, determine the total space
579 : * it occupies (including all memory-allocation overhead).
580 : */
581 : static Size
582 0 : SlabGetChunkSpace(MemoryContext context, void *pointer)
583 : {
584 0 : SlabContext *slab = castNode(SlabContext, context);
585 :
586 0 : Assert(slab);
587 :
588 0 : return slab->fullChunkSize;
589 : }
590 :
591 : /*
592 : * SlabIsEmpty
593 : * Is an Slab empty of any allocated space?
594 : */
595 : static bool
596 0 : SlabIsEmpty(MemoryContext context)
597 : {
598 0 : SlabContext *slab = castNode(SlabContext, context);
599 :
600 0 : Assert(slab);
601 :
602 0 : return (slab->nblocks == 0);
603 : }
604 :
605 : /*
606 : * SlabStats
607 : * Compute stats about memory consumption of an Slab.
608 : *
609 : * level: recursion level (0 at top level); used for print indentation.
610 : * print: true to print stats to stderr.
611 : * totals: if not NULL, add stats about this Slab into *totals.
612 : */
613 : static void
614 0 : SlabStats(MemoryContext context, int level, bool print,
615 : MemoryContextCounters *totals)
616 : {
617 0 : SlabContext *slab = castNode(SlabContext, context);
618 0 : Size nblocks = 0;
619 0 : Size freechunks = 0;
620 0 : Size totalspace = 0;
621 0 : Size freespace = 0;
622 : int i;
623 :
624 0 : Assert(slab);
625 :
626 0 : for (i = 0; i <= slab->chunksPerBlock; i++)
627 : {
628 : dlist_iter iter;
629 :
630 0 : dlist_foreach(iter, &slab->freelist[i])
631 : {
632 0 : SlabBlock *block = dlist_container(SlabBlock, node, iter.cur);
633 :
634 0 : nblocks++;
635 0 : totalspace += slab->blockSize;
636 0 : freespace += slab->fullChunkSize * block->nfree;
637 0 : freechunks += block->nfree;
638 : }
639 : }
640 :
641 0 : if (print)
642 : {
643 0 : for (i = 0; i < level; i++)
644 0 : fprintf(stderr, " ");
645 0 : fprintf(stderr,
646 : "Slab: %s: %zu total in %zd blocks; %zu free (%zd chunks); %zu used\n",
647 : slab->header.name, totalspace, nblocks, freespace, freechunks,
648 : totalspace - freespace);
649 : }
650 :
651 0 : if (totals)
652 : {
653 0 : totals->nblocks += nblocks;
654 0 : totals->freechunks += freechunks;
655 0 : totals->totalspace += totalspace;
656 0 : totals->freespace += freespace;
657 : }
658 0 : }
659 :
660 :
661 : #ifdef MEMORY_CONTEXT_CHECKING
662 :
663 : /*
664 : * SlabCheck
665 : * Walk through chunks and check consistency of memory.
666 : *
667 : * NOTE: report errors as WARNING, *not* ERROR or FATAL. Otherwise you'll
668 : * find yourself in an infinite loop when trouble occurs, because this
669 : * routine will be entered again when elog cleanup tries to release memory!
670 : */
671 : static void
672 0 : SlabCheck(MemoryContext context)
673 : {
674 : int i;
675 0 : SlabContext *slab = castNode(SlabContext, context);
676 0 : char *name = slab->header.name;
677 : char *freechunks;
678 :
679 0 : Assert(slab);
680 0 : Assert(slab->chunksPerBlock > 0);
681 :
682 : /* bitmap of free chunks on a block */
683 0 : freechunks = palloc(slab->chunksPerBlock * sizeof(bool));
684 :
685 : /* walk all the freelists */
686 0 : for (i = 0; i <= slab->chunksPerBlock; i++)
687 : {
688 : int j,
689 : nfree;
690 : dlist_iter iter;
691 :
692 : /* walk all blocks on this freelist */
693 0 : dlist_foreach(iter, &slab->freelist[i])
694 : {
695 : int idx;
696 0 : SlabBlock *block = dlist_container(SlabBlock, node, iter.cur);
697 :
698 : /*
699 : * Make sure the number of free chunks (in the block header)
700 : * matches position in the freelist.
701 : */
702 0 : if (block->nfree != i)
703 0 : elog(WARNING, "problem in slab %s: number of free chunks %d in block %p does not match freelist %d",
704 : name, block->nfree, block, i);
705 :
706 : /* reset the bitmap of free chunks for this block */
707 0 : memset(freechunks, 0, (slab->chunksPerBlock * sizeof(bool)));
708 0 : idx = block->firstFreeChunk;
709 :
710 : /*
711 : * Now walk through the chunks, count the free ones and also
712 : * perform some additional checks for the used ones. As the chunk
713 : * freelist is stored within the chunks themselves, we have to
714 : * walk through the chunks and construct our own bitmap.
715 : */
716 :
717 0 : nfree = 0;
718 0 : while (idx < slab->chunksPerBlock)
719 : {
720 : SlabChunk *chunk;
721 :
722 : /* count the chunk as free, add it to the bitmap */
723 0 : nfree++;
724 0 : freechunks[idx] = true;
725 :
726 : /* read index of the next free chunk */
727 0 : chunk = SlabBlockGetChunk(slab, block, idx);
728 : VALGRIND_MAKE_MEM_DEFINED(SlabChunkGetPointer(chunk), sizeof(int32));
729 0 : idx = *(int32 *) SlabChunkGetPointer(chunk);
730 : }
731 :
732 0 : for (j = 0; j < slab->chunksPerBlock; j++)
733 : {
734 : /* non-zero bit in the bitmap means chunk the chunk is used */
735 0 : if (!freechunks[j])
736 : {
737 0 : SlabChunk *chunk = SlabBlockGetChunk(slab, block, j);
738 :
739 : /* chunks have both block and slab pointers, so check both */
740 0 : if (chunk->block != block)
741 0 : elog(WARNING, "problem in slab %s: bogus block link in block %p, chunk %p",
742 : name, block, chunk);
743 :
744 0 : if (chunk->slab != slab)
745 0 : elog(WARNING, "problem in slab %s: bogus slab link in block %p, chunk %p",
746 : name, block, chunk);
747 :
748 : /* there might be sentinel (thanks to alignment) */
749 0 : if (slab->chunkSize < (slab->fullChunkSize - sizeof(SlabChunk)))
750 0 : if (!sentinel_ok(chunk, slab->chunkSize))
751 0 : elog(WARNING, "problem in slab %s: detected write past chunk end in block %p, chunk %p",
752 : name, block, chunk);
753 : }
754 : }
755 :
756 : /*
757 : * Make sure we got the expected number of free chunks (as tracked
758 : * in the block header).
759 : */
760 0 : if (nfree != block->nfree)
761 0 : elog(WARNING, "problem in slab %s: number of free chunks %d in block %p does not match bitmap %d",
762 : name, block->nfree, block, nfree);
763 : }
764 : }
765 0 : }
766 :
767 : #endif /* MEMORY_CONTEXT_CHECKING */
|