Thanks to visit codestin.com
Credit goes to doxygen.postgresql.org

PostgreSQL Source Code git master
bump.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * bump.c
4 * Bump allocator definitions.
5 *
6 * Bump is a MemoryContext implementation designed for memory usages which
7 * require allocating a large number of chunks, none of which ever need to be
8 * pfree'd or realloc'd. Chunks allocated by this context have no chunk header
9 * and operations which ordinarily require looking at the chunk header cannot
10 * be performed. For example, pfree, realloc, GetMemoryChunkSpace and
11 * GetMemoryChunkContext are all not possible with bump allocated chunks. The
12 * only way to release memory allocated by this context type is to reset or
13 * delete the context.
14 *
15 * Portions Copyright (c) 2024-2025, PostgreSQL Global Development Group
16 *
17 * IDENTIFICATION
18 * src/backend/utils/mmgr/bump.c
19 *
20 *
21 * Bump is best suited to cases which require a large number of short-lived
22 * chunks where performance matters. Because bump allocated chunks don't
23 * have a chunk header, it can fit more chunks on each block. This means we
24 * can do more with less memory and fewer cache lines. The reason it's best
25 * suited for short-lived usages of memory is that ideally, pointers to bump
26 * allocated chunks won't be visible to a large amount of code. The more
27 * code that operates on memory allocated by this allocator, the more chances
28 * that some code will try to perform a pfree or one of the other operations
29 * which are made impossible due to the lack of chunk header. In order to
30 * detect accidental usage of the various disallowed operations, we do add a
31 * MemoryChunk chunk header in MEMORY_CONTEXT_CHECKING builds and have the
32 * various disallowed functions raise an ERROR.
33 *
34 * Allocations are MAXALIGNed.
35 *
36 *-------------------------------------------------------------------------
37 */
38
39#include "postgres.h"
40
41#include "lib/ilist.h"
42#include "port/pg_bitutils.h"
43#include "utils/memdebug.h"
44#include "utils/memutils.h"
47
48#define Bump_BLOCKHDRSZ MAXALIGN(sizeof(BumpBlock))
49#define FIRST_BLOCKHDRSZ (MAXALIGN(sizeof(BumpContext)) + \
50 Bump_BLOCKHDRSZ)
51
52/* No chunk header unless built with MEMORY_CONTEXT_CHECKING */
53#ifdef MEMORY_CONTEXT_CHECKING
54#define Bump_CHUNKHDRSZ sizeof(MemoryChunk)
55#else
56#define Bump_CHUNKHDRSZ 0
57#endif
58
59#define Bump_CHUNK_FRACTION 8
60
61/* The keeper block is allocated in the same allocation as the set */
62#define KeeperBlock(set) ((BumpBlock *) ((char *) (set) + \
63 MAXALIGN(sizeof(BumpContext))))
64#define IsKeeperBlock(set, blk) (KeeperBlock(set) == (blk))
65
66typedef struct BumpBlock BumpBlock; /* forward reference */
67
68typedef struct BumpContext
69{
70 MemoryContextData header; /* Standard memory-context fields */
71
72 /* Bump context parameters */
73 uint32 initBlockSize; /* initial block size */
74 uint32 maxBlockSize; /* maximum block size */
75 uint32 nextBlockSize; /* next block size to allocate */
76 uint32 allocChunkLimit; /* effective chunk size limit */
77
78 dlist_head blocks; /* list of blocks with the block currently
79 * being filled at the head */
81
82/*
83 * BumpBlock
84 * BumpBlock is the unit of memory that is obtained by bump.c from
85 * malloc(). It contains zero or more allocations, which are the
86 * units requested by palloc().
87 */
89{
90 dlist_node node; /* doubly-linked list of blocks */
91#ifdef MEMORY_CONTEXT_CHECKING
92 BumpContext *context; /* pointer back to the owning context */
93#endif
94 char *freeptr; /* start of free space in this block */
95 char *endptr; /* end of space in this block */
96};
97
98/*
99 * BumpIsValid
100 * True iff set is valid bump context.
101 */
102#define BumpIsValid(set) \
103 ((set) && IsA(set, BumpContext))
104
105/*
106 * We always store external chunks on a dedicated block. This makes fetching
107 * the block from an external chunk easy since it's always the first and only
108 * chunk on the block.
109 */
110#define ExternalChunkGetBlock(chunk) \
111 (BumpBlock *) ((char *) chunk - Bump_BLOCKHDRSZ)
112
113/* Inlined helper functions */
114static inline void BumpBlockInit(BumpContext *context, BumpBlock *block,
115 Size blksize);
116static inline bool BumpBlockIsEmpty(BumpBlock *block);
117static inline void BumpBlockMarkEmpty(BumpBlock *block);
118static inline Size BumpBlockFreeBytes(BumpBlock *block);
119static inline void BumpBlockFree(BumpContext *set, BumpBlock *block);
120
121
122/*
123* BumpContextCreate
124* Create a new Bump context.
125*
126* parent: parent context, or NULL if top-level context
127* name: name of context (must be statically allocated)
128* minContextSize: minimum context size
129* initBlockSize: initial allocation block size
130* maxBlockSize: maximum allocation block size
131*/
133BumpContextCreate(MemoryContext parent, const char *name, Size minContextSize,
134 Size initBlockSize, Size maxBlockSize)
135{
136 Size firstBlockSize;
137 Size allocSize;
138 BumpContext *set;
139 BumpBlock *block;
140
141 /* ensure MemoryChunk's size is properly maxaligned */
143 "sizeof(MemoryChunk) is not maxaligned");
144
145 /*
146 * First, validate allocation parameters. Asserts seem sufficient because
147 * nobody varies their parameters at runtime. We somewhat arbitrarily
148 * enforce a minimum 1K block size. We restrict the maximum block size to
149 * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
150 * regards to addressing the offset between the chunk and the block that
151 * the chunk is stored on. We would be unable to store the offset between
152 * the chunk and block for any chunks that were beyond
153 * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
154 * larger than this.
155 */
156 Assert(initBlockSize == MAXALIGN(initBlockSize) &&
157 initBlockSize >= 1024);
158 Assert(maxBlockSize == MAXALIGN(maxBlockSize) &&
159 maxBlockSize >= initBlockSize &&
160 AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */
161 Assert(minContextSize == 0 ||
162 (minContextSize == MAXALIGN(minContextSize) &&
163 minContextSize >= 1024 &&
164 minContextSize <= maxBlockSize));
165 Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
166
167 /* Determine size of initial block */
168 allocSize = MAXALIGN(sizeof(BumpContext)) + Bump_BLOCKHDRSZ +
170 if (minContextSize != 0)
171 allocSize = Max(allocSize, minContextSize);
172 else
173 allocSize = Max(allocSize, initBlockSize);
174
175 /*
176 * Allocate the initial block. Unlike other bump.c blocks, it starts with
177 * the context header and its block header follows that.
178 */
179 set = (BumpContext *) malloc(allocSize);
180 if (set == NULL)
181 {
184 (errcode(ERRCODE_OUT_OF_MEMORY),
185 errmsg("out of memory"),
186 errdetail("Failed while creating memory context \"%s\".",
187 name)));
188 }
189
190 /*
191 * Avoid writing code that can fail between here and MemoryContextCreate;
192 * we'd leak the header and initial block if we ereport in this stretch.
193 */
194
195 /* See comments about Valgrind interactions in aset.c */
196 VALGRIND_CREATE_MEMPOOL(set, 0, false);
197 /* This vchunk covers the BumpContext and the keeper block header */
199
200 dlist_init(&set->blocks);
201
202 /* Fill in the initial block's block header */
203 block = KeeperBlock(set);
204 /* determine the block size and initialize it */
205 firstBlockSize = allocSize - MAXALIGN(sizeof(BumpContext));
206 BumpBlockInit(set, block, firstBlockSize);
207
208 /* add it to the doubly-linked list of blocks */
209 dlist_push_head(&set->blocks, &block->node);
210
211 /*
212 * Fill in BumpContext-specific header fields. The Asserts above should
213 * ensure that these all fit inside a uint32.
214 */
215 set->initBlockSize = (uint32) initBlockSize;
216 set->maxBlockSize = (uint32) maxBlockSize;
217 set->nextBlockSize = (uint32) initBlockSize;
218
219 /*
220 * Compute the allocation chunk size limit for this context.
221 *
222 * Limit the maximum size a non-dedicated chunk can be so that we can fit
223 * at least Bump_CHUNK_FRACTION of chunks this big onto the maximum sized
224 * block. We must further limit this value so that it's no more than
225 * MEMORYCHUNK_MAX_VALUE. We're unable to have non-external chunks larger
226 * than that value as we store the chunk size in the MemoryChunk 'value'
227 * field in the call to MemoryChunkSetHdrMask().
228 */
229 set->allocChunkLimit = Min(maxBlockSize, MEMORYCHUNK_MAX_VALUE);
230 while ((Size) (set->allocChunkLimit + Bump_CHUNKHDRSZ) >
231 (Size) ((Size) (maxBlockSize - Bump_BLOCKHDRSZ) / Bump_CHUNK_FRACTION))
232 set->allocChunkLimit >>= 1;
233
234 /* Finally, do the type-independent part of context creation */
235 MemoryContextCreate((MemoryContext) set, T_BumpContext, MCTX_BUMP_ID,
236 parent, name);
237
238 ((MemoryContext) set)->mem_allocated = allocSize;
239
240 return (MemoryContext) set;
241}
242
243/*
244 * BumpReset
245 * Frees all memory which is allocated in the given set.
246 *
247 * The code simply frees all the blocks in the context apart from the keeper
248 * block.
249 */
250void
252{
253 BumpContext *set = (BumpContext *) context;
254 dlist_mutable_iter miter;
255
256 Assert(BumpIsValid(set));
257
258#ifdef MEMORY_CONTEXT_CHECKING
259 /* Check for corruption and leaks before freeing */
260 BumpCheck(context);
261#endif
262
263 dlist_foreach_modify(miter, &set->blocks)
264 {
265 BumpBlock *block = dlist_container(BumpBlock, node, miter.cur);
266
267 if (IsKeeperBlock(set, block))
268 BumpBlockMarkEmpty(block);
269 else
270 BumpBlockFree(set, block);
271 }
272
273 /*
274 * Instruct Valgrind to throw away all the vchunks associated with this
275 * context, except for the one covering the BumpContext and keeper-block
276 * header. This gets rid of the vchunks for whatever user data is getting
277 * discarded by the context reset.
278 */
280
281 /* Reset block size allocation sequence, too */
282 set->nextBlockSize = set->initBlockSize;
283
284 /* Ensure there is only 1 item in the dlist */
287}
288
289/*
290 * BumpDelete
291 * Free all memory which is allocated in the given context.
292 */
293void
295{
296 /* Reset to release all releasable BumpBlocks */
297 BumpReset(context);
298
299 /* Destroy the vpool -- see notes in aset.c */
301
302 /* And free the context header and keeper block */
303 free(context);
304}
305
306/*
307 * Helper for BumpAlloc() that allocates an entire block for the chunk.
308 *
309 * BumpAlloc()'s comment explains why this is separate.
310 */
312static void *
313BumpAllocLarge(MemoryContext context, Size size, int flags)
314{
315 BumpContext *set = (BumpContext *) context;
316 BumpBlock *block;
317#ifdef MEMORY_CONTEXT_CHECKING
318 MemoryChunk *chunk;
319#endif
320 Size chunk_size;
321 Size required_size;
322 Size blksize;
323
324 /* validate 'size' is within the limits for the given 'flags' */
325 MemoryContextCheckSize(context, size, flags);
326
327#ifdef MEMORY_CONTEXT_CHECKING
328 /* ensure there's always space for the sentinel byte */
329 chunk_size = MAXALIGN(size + 1);
330#else
331 chunk_size = MAXALIGN(size);
332#endif
333
334 required_size = chunk_size + Bump_CHUNKHDRSZ;
335 blksize = required_size + Bump_BLOCKHDRSZ;
336
337 block = (BumpBlock *) malloc(blksize);
338 if (block == NULL)
339 return MemoryContextAllocationFailure(context, size, flags);
340
341 /* Make a vchunk covering the new block's header */
343
344 context->mem_allocated += blksize;
345
346 /* the block is completely full */
347 block->freeptr = block->endptr = ((char *) block) + blksize;
348
349#ifdef MEMORY_CONTEXT_CHECKING
350 /* block with a single (used) chunk */
351 block->context = set;
352
353 chunk = (MemoryChunk *) (((char *) block) + Bump_BLOCKHDRSZ);
354
355 /* mark the MemoryChunk as externally managed */
357
358 chunk->requested_size = size;
359 /* set mark to catch clobber of "unused" space */
360 Assert(size < chunk_size);
361 set_sentinel(MemoryChunkGetPointer(chunk), size);
362#endif
363#ifdef RANDOMIZE_ALLOCATED_MEMORY
364 /* fill the allocated space with junk */
365 randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
366#endif
367
368 /*
369 * Add the block to the tail of allocated blocks list. The current block
370 * is left at the head of the list as it may still have space for
371 * non-large allocations.
372 */
373 dlist_push_tail(&set->blocks, &block->node);
374
375#ifdef MEMORY_CONTEXT_CHECKING
376 /* Ensure any padding bytes are marked NOACCESS. */
378 chunk_size - size);
379
380 /* Disallow access to the chunk header. */
382
383 return MemoryChunkGetPointer(chunk);
384#else
385 return (void *) (((char *) block) + Bump_BLOCKHDRSZ);
386#endif
387}
388
389/*
390 * Small helper for allocating a new chunk from a chunk, to avoid duplicating
391 * the code between BumpAlloc() and BumpAllocFromNewBlock().
392 */
393static inline void *
395 Size chunk_size)
396{
397#ifdef MEMORY_CONTEXT_CHECKING
398 MemoryChunk *chunk;
399#else
400 void *ptr;
401#endif
402
403 /* validate we've been given a block with enough free space */
404 Assert(block != NULL);
405 Assert((block->endptr - block->freeptr) >= Bump_CHUNKHDRSZ + chunk_size);
406
407#ifdef MEMORY_CONTEXT_CHECKING
408 chunk = (MemoryChunk *) block->freeptr;
409#else
410 ptr = (void *) block->freeptr;
411#endif
412
413 /* point the freeptr beyond this chunk */
414 block->freeptr += (Bump_CHUNKHDRSZ + chunk_size);
415 Assert(block->freeptr <= block->endptr);
416
417#ifdef MEMORY_CONTEXT_CHECKING
418 /* Prepare to initialize the chunk header. */
420
421 MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_BUMP_ID);
422 chunk->requested_size = size;
423 /* set mark to catch clobber of "unused" space */
424 Assert(size < chunk_size);
425 set_sentinel(MemoryChunkGetPointer(chunk), size);
426
427#ifdef RANDOMIZE_ALLOCATED_MEMORY
428 /* fill the allocated space with junk */
429 randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
430#endif
431
432 /* Ensure any padding bytes are marked NOACCESS. */
434 chunk_size - size);
435
436 /* Disallow access to the chunk header. */
438
439 return MemoryChunkGetPointer(chunk);
440#else
441 return ptr;
442#endif /* MEMORY_CONTEXT_CHECKING */
443}
444
445/*
446 * Helper for BumpAlloc() that allocates a new block and returns a chunk
447 * allocated from it.
448 *
449 * BumpAlloc()'s comment explains why this is separate.
450 */
452static void *
453BumpAllocFromNewBlock(MemoryContext context, Size size, int flags,
454 Size chunk_size)
455{
456 BumpContext *set = (BumpContext *) context;
457 BumpBlock *block;
458 Size blksize;
459 Size required_size;
460
461 /*
462 * The first such block has size initBlockSize, and we double the space in
463 * each succeeding block, but not more than maxBlockSize.
464 */
465 blksize = set->nextBlockSize;
466 set->nextBlockSize <<= 1;
467 if (set->nextBlockSize > set->maxBlockSize)
468 set->nextBlockSize = set->maxBlockSize;
469
470 /* we'll need space for the chunk, chunk hdr and block hdr */
471 required_size = chunk_size + Bump_CHUNKHDRSZ + Bump_BLOCKHDRSZ;
472 /* round the size up to the next power of 2 */
473 if (blksize < required_size)
474 blksize = pg_nextpower2_size_t(required_size);
475
476 block = (BumpBlock *) malloc(blksize);
477
478 if (block == NULL)
479 return MemoryContextAllocationFailure(context, size, flags);
480
481 /* Make a vchunk covering the new block's header */
483
484 context->mem_allocated += blksize;
485
486 /* initialize the new block */
487 BumpBlockInit(set, block, blksize);
488
489 /* add it to the doubly-linked list of blocks */
490 dlist_push_head(&set->blocks, &block->node);
491
492 return BumpAllocChunkFromBlock(context, block, size, chunk_size);
493}
494
495/*
496 * BumpAlloc
497 * Returns a pointer to allocated memory of given size or raises an ERROR
498 * on allocation failure, or returns NULL when flags contains
499 * MCXT_ALLOC_NO_OOM.
500 *
501 * No request may exceed:
502 * MAXALIGN_DOWN(SIZE_MAX) - Bump_BLOCKHDRSZ - Bump_CHUNKHDRSZ
503 * All callers use a much-lower limit.
504 *
505 *
506 * Note: when using valgrind, it doesn't matter how the returned allocation
507 * is marked, as mcxt.c will set it to UNDEFINED.
508 * This function should only contain the most common code paths. Everything
509 * else should be in pg_noinline helper functions, thus avoiding the overhead
510 * of creating a stack frame for the common cases. Allocating memory is often
511 * a bottleneck in many workloads, so avoiding stack frame setup is
512 * worthwhile. Helper functions should always directly return the newly
513 * allocated memory so that we can just return that address directly as a tail
514 * call.
515 */
516void *
517BumpAlloc(MemoryContext context, Size size, int flags)
518{
519 BumpContext *set = (BumpContext *) context;
520 BumpBlock *block;
521 Size chunk_size;
522 Size required_size;
523
524 Assert(BumpIsValid(set));
525
526#ifdef MEMORY_CONTEXT_CHECKING
527 /* ensure there's always space for the sentinel byte */
528 chunk_size = MAXALIGN(size + 1);
529#else
530 chunk_size = MAXALIGN(size);
531#endif
532
533 /*
534 * If requested size exceeds maximum for chunks we hand the request off to
535 * BumpAllocLarge().
536 */
537 if (chunk_size > set->allocChunkLimit)
538 return BumpAllocLarge(context, size, flags);
539
540 required_size = chunk_size + Bump_CHUNKHDRSZ;
541
542 /*
543 * Not an oversized chunk. We try to first make use of the latest block,
544 * but if there's not enough space in it we must allocate a new block.
545 */
546 block = dlist_container(BumpBlock, node, dlist_head_node(&set->blocks));
547
548 if (BumpBlockFreeBytes(block) < required_size)
549 return BumpAllocFromNewBlock(context, size, flags, chunk_size);
550
551 /* The current block has space, so just allocate chunk there. */
552 return BumpAllocChunkFromBlock(context, block, size, chunk_size);
553}
554
555/*
556 * BumpBlockInit
557 * Initializes 'block' assuming 'blksize'. Does not update the context's
558 * mem_allocated field.
559 */
560static inline void
561BumpBlockInit(BumpContext *context, BumpBlock *block, Size blksize)
562{
563#ifdef MEMORY_CONTEXT_CHECKING
564 block->context = context;
565#endif
566 block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
567 block->endptr = ((char *) block) + blksize;
568
569 /* Mark unallocated space NOACCESS. */
571}
572
573/*
574 * BumpBlockIsEmpty
575 * Returns true iff 'block' contains no chunks
576 */
577static inline bool
579{
580 /* it's empty if the freeptr has not moved */
581 return (block->freeptr == ((char *) block + Bump_BLOCKHDRSZ));
582}
583
584/*
585 * BumpBlockMarkEmpty
586 * Set a block as empty. Does not free the block.
587 */
588static inline void
590{
591#if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
592 char *datastart = ((char *) block) + Bump_BLOCKHDRSZ;
593#endif
594
595#ifdef CLOBBER_FREED_MEMORY
596 wipe_mem(datastart, block->freeptr - datastart);
597#else
598 /* wipe_mem() would have done this */
599 VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
600#endif
601
602 /* Reset the block, but don't return it to malloc */
603 block->freeptr = ((char *) block) + Bump_BLOCKHDRSZ;
604}
605
606/*
607 * BumpBlockFreeBytes
608 * Returns the number of bytes free in 'block'
609 */
610static inline Size
612{
613 return (block->endptr - block->freeptr);
614}
615
616/*
617 * BumpBlockFree
618 * Remove 'block' from 'set' and release the memory consumed by it.
619 */
620static inline void
622{
623 /* Make sure nobody tries to free the keeper block */
624 Assert(!IsKeeperBlock(set, block));
625
626 /* release the block from the list of blocks */
627 dlist_delete(&block->node);
628
629 ((MemoryContext) set)->mem_allocated -= ((char *) block->endptr - (char *) block);
630
631#ifdef CLOBBER_FREED_MEMORY
632 wipe_mem(block, ((char *) block->endptr - (char *) block));
633#endif
634
635 /* As in aset.c, free block-header vchunks explicitly */
636 VALGRIND_MEMPOOL_FREE(set, block);
637
638 free(block);
639}
640
641/*
642 * BumpFree
643 * Unsupported.
644 */
645void
646BumpFree(void *pointer)
647{
648 elog(ERROR, "%s is not supported by the bump memory allocator", "pfree");
649}
650
651/*
652 * BumpRealloc
653 * Unsupported.
654 */
655void *
656BumpRealloc(void *pointer, Size size, int flags)
657{
658 elog(ERROR, "%s is not supported by the bump memory allocator", "realloc");
659 return NULL; /* keep compiler quiet */
660}
661
662/*
663 * BumpGetChunkContext
664 * Unsupported.
665 */
668{
669 elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkContext");
670 return NULL; /* keep compiler quiet */
671}
672
673/*
674 * BumpGetChunkSpace
675 * Unsupported.
676 */
677Size
678BumpGetChunkSpace(void *pointer)
679{
680 elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkSpace");
681 return 0; /* keep compiler quiet */
682}
683
684/*
685 * BumpIsEmpty
686 * Is a BumpContext empty of any allocated space?
687 */
688bool
690{
691 BumpContext *set = (BumpContext *) context;
692 dlist_iter iter;
693
694 Assert(BumpIsValid(set));
695
696 dlist_foreach(iter, &set->blocks)
697 {
698 BumpBlock *block = dlist_container(BumpBlock, node, iter.cur);
699
700 if (!BumpBlockIsEmpty(block))
701 return false;
702 }
703
704 return true;
705}
706
707/*
708 * BumpStats
709 * Compute stats about memory consumption of a Bump context.
710 *
711 * printfunc: if not NULL, pass a human-readable stats string to this.
712 * passthru: pass this pointer through to printfunc.
713 * totals: if not NULL, add stats about this context into *totals.
714 * print_to_stderr: print stats to stderr if true, elog otherwise.
715 */
716void
718 void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
719{
720 BumpContext *set = (BumpContext *) context;
721 Size nblocks = 0;
722 Size totalspace = 0;
723 Size freespace = 0;
724 dlist_iter iter;
725
726 Assert(BumpIsValid(set));
727
728 dlist_foreach(iter, &set->blocks)
729 {
730 BumpBlock *block = dlist_container(BumpBlock, node, iter.cur);
731
732 nblocks++;
733 totalspace += (block->endptr - (char *) block);
734 freespace += (block->endptr - block->freeptr);
735 }
736
737 if (printfunc)
738 {
739 char stats_string[200];
740
741 snprintf(stats_string, sizeof(stats_string),
742 "%zu total in %zu blocks; %zu free; %zu used",
743 totalspace, nblocks, freespace, totalspace - freespace);
744 printfunc(context, passthru, stats_string, print_to_stderr);
745 }
746
747 if (totals)
748 {
749 totals->nblocks += nblocks;
750 totals->totalspace += totalspace;
751 totals->freespace += freespace;
752 }
753}
754
755
756#ifdef MEMORY_CONTEXT_CHECKING
757
758/*
759 * BumpCheck
760 * Walk through chunks and check consistency of memory.
761 *
762 * NOTE: report errors as WARNING, *not* ERROR or FATAL. Otherwise you'll
763 * find yourself in an infinite loop when trouble occurs, because this
764 * routine will be entered again when elog cleanup tries to release memory!
765 */
766void
767BumpCheck(MemoryContext context)
768{
769 BumpContext *bump = (BumpContext *) context;
770 const char *name = context->name;
771 dlist_iter iter;
772 Size total_allocated = 0;
773
774 /* walk all blocks in this context */
775 dlist_foreach(iter, &bump->blocks)
776 {
777 BumpBlock *block = dlist_container(BumpBlock, node, iter.cur);
778 int nchunks;
779 char *ptr;
780 bool has_external_chunk = false;
781
782 if (IsKeeperBlock(bump, block))
783 total_allocated += block->endptr - (char *) bump;
784 else
785 total_allocated += block->endptr - (char *) block;
786
787 /* check block belongs to the correct context */
788 if (block->context != bump)
789 elog(WARNING, "problem in Bump %s: bogus context link in block %p",
790 name, block);
791
792 /* now walk through the chunks and count them */
793 nchunks = 0;
794 ptr = ((char *) block) + Bump_BLOCKHDRSZ;
795
796 while (ptr < block->freeptr)
797 {
798 MemoryChunk *chunk = (MemoryChunk *) ptr;
799 BumpBlock *chunkblock;
800 Size chunksize;
801
802 /* allow access to the chunk header */
804
805 if (MemoryChunkIsExternal(chunk))
806 {
807 chunkblock = ExternalChunkGetBlock(chunk);
808 chunksize = block->endptr - (char *) MemoryChunkGetPointer(chunk);
809 has_external_chunk = true;
810 }
811 else
812 {
813 chunkblock = MemoryChunkGetBlock(chunk);
814 chunksize = MemoryChunkGetValue(chunk);
815 }
816
817 /* move to the next chunk */
818 ptr += (chunksize + Bump_CHUNKHDRSZ);
819
820 nchunks += 1;
821
822 /* chunks have both block and context pointers, so check both */
823 if (chunkblock != block)
824 elog(WARNING, "problem in Bump %s: bogus block link in block %p, chunk %p",
825 name, block, chunk);
826 }
827
828 if (has_external_chunk && nchunks > 1)
829 elog(WARNING, "problem in Bump %s: external chunk on non-dedicated block %p",
830 name, block);
831
832 }
833
834 Assert(total_allocated == context->mem_allocated);
835}
836
837#endif /* MEMORY_CONTEXT_CHECKING */
static bool BumpBlockIsEmpty(BumpBlock *block)
Definition: bump.c:578
#define Bump_CHUNK_FRACTION
Definition: bump.c:59
void BumpFree(void *pointer)
Definition: bump.c:646
void BumpDelete(MemoryContext context)
Definition: bump.c:294
Size BumpGetChunkSpace(void *pointer)
Definition: bump.c:678
void BumpStats(MemoryContext context, MemoryStatsPrintFunc printfunc, void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
Definition: bump.c:717
static void BumpBlockFree(BumpContext *set, BumpBlock *block)
Definition: bump.c:621
#define KeeperBlock(set)
Definition: bump.c:62
static void BumpBlockMarkEmpty(BumpBlock *block)
Definition: bump.c:589
#define Bump_CHUNKHDRSZ
Definition: bump.c:56
static pg_noinline void * BumpAllocLarge(MemoryContext context, Size size, int flags)
Definition: bump.c:313
static void BumpBlockInit(BumpContext *context, BumpBlock *block, Size blksize)
Definition: bump.c:561
#define BumpIsValid(set)
Definition: bump.c:102
#define Bump_BLOCKHDRSZ
Definition: bump.c:48
MemoryContext BumpGetChunkContext(void *pointer)
Definition: bump.c:667
#define IsKeeperBlock(set, blk)
Definition: bump.c:64
struct BumpContext BumpContext
MemoryContext BumpContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: bump.c:133
void BumpReset(MemoryContext context)
Definition: bump.c:251
static pg_noinline void * BumpAllocFromNewBlock(MemoryContext context, Size size, int flags, Size chunk_size)
Definition: bump.c:453
#define FIRST_BLOCKHDRSZ
Definition: bump.c:49
bool BumpIsEmpty(MemoryContext context)
Definition: bump.c:689
void * BumpRealloc(void *pointer, Size size, int flags)
Definition: bump.c:656
static void * BumpAllocChunkFromBlock(MemoryContext context, BumpBlock *block, Size size, Size chunk_size)
Definition: bump.c:394
static Size BumpBlockFreeBytes(BumpBlock *block)
Definition: bump.c:611
#define ExternalChunkGetBlock(chunk)
Definition: bump.c:110
void * BumpAlloc(MemoryContext context, Size size, int flags)
Definition: bump.c:517
#define pg_noinline
Definition: c.h:285
#define Min(x, y)
Definition: c.h:1004
#define MAXALIGN(LEN)
Definition: c.h:811
#define Max(x, y)
Definition: c.h:998
uint32_t uint32
Definition: c.h:539
#define StaticAssertDecl(condition, errmessage)
Definition: c.h:936
size_t Size
Definition: c.h:611
int errdetail(const char *fmt,...)
Definition: elog.c:1207
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define WARNING
Definition: elog.h:36
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define ereport(elevel,...)
Definition: elog.h:150
Assert(PointerIsAligned(start, uint64))
#define free(a)
Definition: header.h:65
#define malloc(a)
Definition: header.h:50
#define dlist_foreach(iter, lhead)
Definition: ilist.h:623
static void dlist_init(dlist_head *head)
Definition: ilist.h:314
static bool dlist_has_next(const dlist_head *head, const dlist_node *node)
Definition: ilist.h:503
static void dlist_delete(dlist_node *node)
Definition: ilist.h:405
static void dlist_push_head(dlist_head *head, dlist_node *node)
Definition: ilist.h:347
static dlist_node * dlist_head_node(dlist_head *head)
Definition: ilist.h:565
#define dlist_foreach_modify(iter, lhead)
Definition: ilist.h:640
static bool dlist_is_empty(const dlist_head *head)
Definition: ilist.h:336
static void dlist_push_tail(dlist_head *head, dlist_node *node)
Definition: ilist.h:364
#define dlist_container(type, membername, ptr)
Definition: ilist.h:593
void MemoryContextCreate(MemoryContext node, NodeTag tag, MemoryContextMethodID method_id, MemoryContext parent, const char *name)
Definition: mcxt.c:1146
MemoryContext TopMemoryContext
Definition: mcxt.c:166
void MemoryContextStats(MemoryContext context)
Definition: mcxt.c:860
void * MemoryContextAllocationFailure(MemoryContext context, Size size, int flags)
Definition: mcxt.c:1195
#define VALGRIND_DESTROY_MEMPOOL(context)
Definition: memdebug.h:25
#define VALGRIND_MAKE_MEM_DEFINED(addr, size)
Definition: memdebug.h:26
#define VALGRIND_CREATE_MEMPOOL(context, redzones, zeroed)
Definition: memdebug.h:24
#define VALGRIND_MEMPOOL_ALLOC(context, addr, size)
Definition: memdebug.h:29
#define VALGRIND_MEMPOOL_TRIM(context, addr, size)
Definition: memdebug.h:32
#define VALGRIND_MEMPOOL_FREE(context, addr)
Definition: memdebug.h:30
#define VALGRIND_MAKE_MEM_NOACCESS(addr, size)
Definition: memdebug.h:27
#define VALGRIND_MAKE_MEM_UNDEFINED(addr, size)
Definition: memdebug.h:28
void(* MemoryStatsPrintFunc)(MemoryContext context, void *passthru, const char *stats_string, bool print_to_stderr)
Definition: memnodes.h:54
#define AllocHugeSizeIsValid(size)
Definition: memutils.h:49
static void MemoryContextCheckSize(MemoryContext context, Size size, int flags)
@ MCTX_BUMP_ID
#define MEMORYCHUNK_MAX_BLOCKOFFSET
#define MEMORYCHUNK_MAX_VALUE
static Size MemoryChunkGetValue(MemoryChunk *chunk)
#define MemoryChunkGetPointer(c)
static bool MemoryChunkIsExternal(MemoryChunk *chunk)
static void * MemoryChunkGetBlock(MemoryChunk *chunk)
static void MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk, MemoryContextMethodID methodid)
static void MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block, Size value, MemoryContextMethodID methodid)
struct MemoryContextData * MemoryContext
Definition: palloc.h:36
#define pg_nextpower2_size_t
Definition: pg_bitutils.h:441
#define snprintf
Definition: port.h:239
Definition: bump.c:89
char * endptr
Definition: bump.c:95
char * freeptr
Definition: bump.c:94
dlist_node node
Definition: bump.c:90
dlist_head blocks
Definition: bump.c:78
uint32 initBlockSize
Definition: bump.c:73
uint32 maxBlockSize
Definition: bump.c:74
uint32 nextBlockSize
Definition: bump.c:75
uint32 allocChunkLimit
Definition: bump.c:76
MemoryContextData header
Definition: bump.c:70
const char * name
Definition: memnodes.h:131
dlist_node * cur
Definition: ilist.h:179
dlist_node * cur
Definition: ilist.h:200
const char * name