1 /*-------------------------------------------------------------------------
4 * Allocation set definitions.
6 * AllocSet is our standard implementation of the abstract MemoryContext
10 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
11 * Portions Copyright (c) 1994, Regents of the University of California
14 * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.31 2000/07/12 05:15:20 tgl Exp $
17 * This is a new (Feb. 05, 1999) implementation of the allocation set
18 * routines. AllocSet...() does not use OrderedSet...() any more.
19 * Instead it manages allocations in a block pool by itself, combining
20 * many small allocations in a few bigger blocks. AllocSetFree() normally
21 * doesn't free() memory really. It just add's the free'd area to some
22 * list for later reuse by AllocSetAlloc(). All memory blocks are free()'d
23 * at once on AllocSetReset(), which happens when the memory context gets
27 * Performance improvement from Tom Lane, 8/99: for extremely large request
28 * sizes, we do want to be able to give the memory back to free() as soon
29 * as it is pfree()'d. Otherwise we risk tying up a lot of memory in
30 * freelist entries that might never be usable. This is specially needed
31 * when the caller is repeatedly repalloc()'ing a block bigger and bigger;
32 * the previous instances of the block were guaranteed to be wasted until
33 * AllocSetReset() under the old way.
34 *-------------------------------------------------------------------------
39 #include "utils/memutils.h"
41 /* Define this to detail debug alloc information
43 /*#define HAVE_ALLOCINFO 1*/
46 * AllocSetContext is defined in nodes/memnodes.h.
48 typedef AllocSetContext *AllocSet;
52 * Aligned pointer which may be a member of an allocation set.
54 typedef void *AllocPointer;
58 * An AllocBlock is the unit of memory that is obtained by aset.c
59 * from malloc(). It contains one or more AllocChunks, which are
60 * the units requested by palloc() and freed by pfree(). AllocChunks
61 * cannot be returned to malloc() individually, instead they are put
62 * on freelists by pfree() and re-used by the next palloc() that has
63 * a matching request size.
65 * AllocBlockData is the header data for a block --- the usable space
66 * within the block begins at the next alignment boundary.
68 typedef struct AllocBlockData
70 AllocSet aset; /* aset that owns this block */
71 AllocBlock next; /* next block in aset's blocks list */
72 char *freeptr; /* start of free space in this block */
73 char *endptr; /* end of space in this block */
78 * The prefix of each piece of memory in an AllocBlock
80 * NB: this MUST match StandardChunkHeader as defined by utils/memutils.h.
82 typedef struct AllocChunkData
84 /* aset is the owning aset if allocated, or the freelist link if free */
86 /* size is always the size of the usable space in the chunk */
88 #ifdef MEMORY_CONTEXT_CHECKING
95 * True iff pointer is valid allocation pointer.
97 #define AllocPointerIsValid(pointer) PointerIsValid(pointer)
101 * True iff set is valid allocation set.
103 #define AllocSetIsValid(set) PointerIsValid(set)
105 /*--------------------
106 * Chunk freelist k holds chunks of size 1 << (k + ALLOC_MINBITS),
107 * for k = 0 .. ALLOCSET_NUM_FREELISTS-2.
108 * The last freelist holds all larger free chunks. Those chunks come in
109 * varying sizes depending on the request size, whereas smaller chunks are
110 * coerced to powers of 2 to improve their "recyclability".
112 * CAUTION: ALLOC_MINBITS must be large enough so that
113 * 1<<ALLOC_MINBITS is at least MAXALIGN,
114 * or we may fail to align the smallest chunks adequately.
115 * 16-byte alignment is enough on all currently known machines.
116 *--------------------
119 #define ALLOC_MINBITS 4 /* smallest chunk size is 16 bytes */
120 #define ALLOC_SMALLCHUNK_LIMIT (1 << (ALLOCSET_NUM_FREELISTS-2+ALLOC_MINBITS))
121 /* Size of largest chunk that we use a fixed size for */
123 /*--------------------
124 * The first block allocated for an allocset has size initBlockSize.
125 * Each time we have to allocate another block, we double the block size
126 * (if possible, and without exceeding maxBlockSize), so as to reduce
127 * the bookkeeping load on malloc().
129 * Blocks allocated to hold oversize chunks do not follow this rule, however;
130 * they are just however big they need to be to hold that single chunk.
131 * AllocSetAlloc has some freedom about whether to consider a chunk larger
132 * than ALLOC_SMALLCHUNK_LIMIT to be "oversize". We require all chunks
133 * >= ALLOC_BIGCHUNK_LIMIT to be allocated as single-chunk blocks; those
134 * chunks are treated specially by AllocSetFree and AllocSetRealloc. For
135 * request sizes between ALLOC_SMALLCHUNK_LIMIT and ALLOC_BIGCHUNK_LIMIT,
136 * AllocSetAlloc has discretion whether to put the request into an existing
137 * block or make a single-chunk block.
139 * We must have initBlockSize > ALLOC_SMALLCHUNK_LIMIT and
140 * ALLOC_BIGCHUNK_LIMIT > ALLOC_SMALLCHUNK_LIMIT.
141 *--------------------
144 #define ALLOC_BIGCHUNK_LIMIT (64 * 1024)
145 /* Chunks >= ALLOC_BIGCHUNK_LIMIT are immediately free()d by pfree() */
147 #define ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData))
148 #define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData))
150 /* Min safe value of allocation block size */
151 #define ALLOC_MIN_BLOCK_SIZE \
152 (ALLOC_SMALLCHUNK_LIMIT + ALLOC_CHUNKHDRSZ + ALLOC_BLOCKHDRSZ)
154 #define AllocPointerGetChunk(ptr) \
155 ((AllocChunk)(((char *)(ptr)) - ALLOC_CHUNKHDRSZ))
156 #define AllocChunkGetPointer(chk) \
157 ((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ))
158 #define AllocPointerGetAset(ptr) ((AllocSet)(AllocPointerGetChunk(ptr)->aset))
159 #define AllocPointerGetSize(ptr) (AllocPointerGetChunk(ptr)->size)
162 * These functions implement the MemoryContext API for AllocSet contexts.
164 static void *AllocSetAlloc(MemoryContext context, Size size);
165 static void AllocSetFree(MemoryContext context, void *pointer);
166 static void *AllocSetRealloc(MemoryContext context, void *pointer, Size size);
167 static void AllocSetInit(MemoryContext context);
168 static void AllocSetReset(MemoryContext context);
169 static void AllocSetDelete(MemoryContext context);
171 #ifdef MEMORY_CONTEXT_CHECKING
172 static void AllocSetCheck(MemoryContext context);
175 static void AllocSetStats(MemoryContext context);
178 * This is the virtual function table for AllocSet contexts.
180 static MemoryContextMethods AllocSetMethods = {
187 #ifdef MEMORY_CONTEXT_CHECKING
198 #ifdef HAVE_ALLOCINFO
199 #define AllocFreeInfo(_cxt, _chunk) \
200 fprintf(stderr, "AllocFree: %s: %p, %d\n", \
201 (_cxt)->header.name, (_chunk), (_chunk)->size)
202 #define AllocAllocInfo(_cxt, _chunk) \
203 fprintf(stderr, "AllocAlloc: %s: %p, %d\n", \
204 (_cxt)->header.name, (_chunk), (_chunk)->size)
206 #define AllocFreeInfo(_cxt, _chunk)
207 #define AllocAllocInfo(_cxt, _chunk)
211 * AllocSetFreeIndex -
213 * Depending on the size of an allocation compute which freechunk
214 * list of the alloc set it belongs to.
218 AllocSetFreeIndex(Size size)
224 size = (size - 1) >> ALLOC_MINBITS;
225 while (size != 0 && idx < ALLOCSET_NUM_FREELISTS - 1)
242 * AllocSetContextCreate
243 * Create a new AllocSet context.
245 * parent: parent context, or NULL if top-level context
246 * name: name of context (for debugging --- string will be copied)
247 * minContextSize: minimum context size
248 * initBlockSize: initial allocation block size
249 * maxBlockSize: maximum allocation block size
252 AllocSetContextCreate(MemoryContext parent,
260 /* Do the type-independent part of context creation */
261 context = (AllocSet) MemoryContextCreate(T_AllocSetContext,
262 sizeof(AllocSetContext),
267 * Make sure alloc parameters are safe, and save them
269 initBlockSize = MAXALIGN(initBlockSize);
270 if (initBlockSize < ALLOC_MIN_BLOCK_SIZE)
271 initBlockSize = ALLOC_MIN_BLOCK_SIZE;
272 maxBlockSize = MAXALIGN(maxBlockSize);
273 if (maxBlockSize < initBlockSize)
274 maxBlockSize = initBlockSize;
275 context->initBlockSize = initBlockSize;
276 context->maxBlockSize = maxBlockSize;
279 * Grab always-allocated space, if requested
281 if (minContextSize > ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)
283 Size blksize = MAXALIGN(minContextSize);
286 block = (AllocBlock) malloc(blksize);
288 elog(ERROR, "Memory exhausted in AllocSetContextCreate()");
289 block->aset = context;
290 block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
291 block->endptr = ((char *) block) + blksize;
292 block->next = context->blocks;
293 context->blocks = block;
294 /* Mark block as not to be released at reset time */
295 context->keeper = block;
297 #ifdef MEMORY_CONTEXT_CHECKING
298 /* mark memory for memory leak searching */
299 memset(block->freeptr, 0x7F, blksize - ALLOC_BLOCKHDRSZ);
303 return (MemoryContext) context;
308 * Context-type-specific initialization routine.
310 * This is called by MemoryContextCreate() after setting up the
311 * generic MemoryContext fields and before linking the new context
312 * into the context tree. We must do whatever is needed to make the
313 * new context minimally valid for deletion. We must *not* risk
314 * failure --- thus, for example, allocating more memory is not cool.
315 * (AllocSetContextCreate can allocate memory when it gets control
319 AllocSetInit(MemoryContext context)
322 * Since MemoryContextCreate already zeroed the context node,
323 * we don't have to do anything here: it's already OK.
329 * Frees all memory which is allocated in the given set.
331 * Actually, this routine has some discretion about what to do.
332 * It should mark all allocated chunks freed, but it need not
333 * necessarily give back all the resources the set owns. Our
334 * actual implementation is that we hang on to any "keeper"
335 * block specified for the set.
338 AllocSetReset(MemoryContext context)
340 AllocSet set = (AllocSet) context;
341 AllocBlock block = set->blocks;
343 AssertArg(AllocSetIsValid(set));
345 while (block != NULL)
347 AllocBlock next = block->next;
349 if (block == set->keeper)
351 /* Reset the block, but don't return it to malloc */
352 char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ;
354 #ifdef CLOBBER_FREED_MEMORY
355 /* Wipe freed memory for debugging purposes */
356 memset(datastart, 0x7F, ((char *) block->freeptr) - datastart);
358 block->freeptr = datastart;
363 /* Normal case, release the block */
364 #ifdef CLOBBER_FREED_MEMORY
365 /* Wipe freed memory for debugging purposes */
366 memset(block, 0x7F, ((char *) block->freeptr) - ((char *) block));
373 /* Now blocks list is either empty or just the keeper block */
374 set->blocks = set->keeper;
375 /* Clear chunk freelists in any case */
376 MemSet(set->freelist, 0, sizeof(set->freelist));
381 * Frees all memory which is allocated in the given set,
382 * in preparation for deletion of the set.
384 * Unlike AllocSetReset, this *must* free all resources of the set.
385 * But note we are not responsible for deleting the context node itself.
388 AllocSetDelete(MemoryContext context)
390 AllocSet set = (AllocSet) context;
391 AllocBlock block = set->blocks;
393 AssertArg(AllocSetIsValid(set));
395 while (block != NULL)
397 AllocBlock next = block->next;
399 #ifdef CLOBBER_FREED_MEMORY
400 /* Wipe freed memory for debugging purposes */
401 memset(block, 0x7F, ((char *) block->endptr) - ((char *) block));
407 /* Make it look empty, just in case... */
409 MemSet(set->freelist, 0, sizeof(set->freelist));
415 * Returns pointer to allocated memory of given size; memory is added
419 AllocSetAlloc(MemoryContext context, Size size)
421 AllocSet set = (AllocSet) context;
424 AllocChunk priorfree = NULL;
429 AssertArg(AllocSetIsValid(set));
432 * Small size can be in free list
434 if (size < ALLOC_BIGCHUNK_LIMIT)
437 * Lookup in the corresponding free list if there is a free chunk we
440 fidx = AllocSetFreeIndex(size);
441 for (chunk = set->freelist[fidx]; chunk; chunk = (AllocChunk) chunk->aset)
443 if (chunk->size >= size)
449 * If one is found, remove it from the free list, make it again a
450 * member of the alloc set and return its data address.
454 if (priorfree == NULL)
455 set->freelist[fidx] = (AllocChunk) chunk->aset;
457 priorfree->aset = chunk->aset;
459 chunk->aset = (void *) set;
461 #ifdef MEMORY_CONTEXT_CHECKING
462 chunk->data_size = size;
464 AllocAllocInfo(set, chunk);
465 return AllocChunkGetPointer(chunk);
471 fidx = ALLOCSET_NUM_FREELISTS - 1;
474 * Choose the actual chunk size to allocate.
476 if (size > ALLOC_SMALLCHUNK_LIMIT)
477 chunk_size = MAXALIGN(size);
479 chunk_size = 1 << (fidx + ALLOC_MINBITS);
480 Assert(chunk_size >= size);
483 * If there is enough room in the active allocation block, *and* the
484 * chunk is less than ALLOC_BIGCHUNK_LIMIT, put the chunk into the
485 * active allocation block.
487 if ((block = set->blocks) != NULL)
489 Size have_free = block->endptr - block->freeptr;
491 if (have_free < (chunk_size + ALLOC_CHUNKHDRSZ) ||
492 chunk_size >= ALLOC_BIGCHUNK_LIMIT)
497 * Otherwise, if requested size exceeds smallchunk limit, allocate an
498 * entire separate block for this allocation. In particular, we will
499 * always take this path if the requested size exceeds bigchunk limit.
501 if (block == NULL && size > ALLOC_SMALLCHUNK_LIMIT)
503 blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
504 block = (AllocBlock) malloc(blksize);
506 elog(ERROR, "Memory exhausted in AllocSetAlloc()");
508 block->freeptr = block->endptr = ((char *) block) + blksize;
510 chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ);
512 chunk->size = chunk_size;
515 * Try to stick the block underneath the active allocation block,
516 * so that we don't lose the use of the space remaining therein.
518 if (set->blocks != NULL)
520 block->next = set->blocks->next;
521 set->blocks->next = block;
529 #ifdef MEMORY_CONTEXT_CHECKING
530 chunk->data_size = size;
531 /* mark memory for memory leak searching */
532 memset(AllocChunkGetPointer(chunk), 0x7F, chunk->size);
534 AllocAllocInfo(set, chunk);
535 return AllocChunkGetPointer(chunk);
539 * Time to create a new regular (multi-chunk) block?
543 if (set->blocks == NULL)
545 blksize = set->initBlockSize;
546 block = (AllocBlock) malloc(blksize);
550 /* Get size of prior block */
551 blksize = set->blocks->endptr - ((char *) set->blocks);
554 * Special case: if very first allocation was for a large
555 * chunk (or we have a small "keeper" block), could have an
556 * undersized top block. Do something reasonable.
558 if (blksize < set->initBlockSize)
559 blksize = set->initBlockSize;
562 /* Crank it up, but not past max */
564 if (blksize > set->maxBlockSize)
565 blksize = set->maxBlockSize;
567 /* Try to allocate it */
568 block = (AllocBlock) malloc(blksize);
571 * We could be asking for pretty big blocks here, so cope if
572 * malloc fails. But give up if there's less than a meg or so
575 while (block == NULL && blksize > 1024 * 1024)
578 block = (AllocBlock) malloc(blksize);
583 elog(ERROR, "Memory exhausted in AllocSetAlloc()");
586 block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
587 block->endptr = ((char *) block) + blksize;
589 #ifdef MEMORY_CONTEXT_CHECKING
590 /* mark memory for memory leak searching */
591 memset(block->freeptr, 0x7F, blksize - ALLOC_BLOCKHDRSZ);
593 block->next = set->blocks;
599 * OK, do the allocation
601 chunk = (AllocChunk) (block->freeptr);
602 chunk->aset = (void *) set;
603 chunk->size = chunk_size;
605 #ifdef MEMORY_CONTEXT_CHECKING
606 chunk->data_size = size;
608 block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
609 Assert(block->freeptr <= block->endptr);
611 AllocAllocInfo(set, chunk);
612 return AllocChunkGetPointer(chunk);
617 * Frees allocated memory; memory is removed from the set.
620 AllocSetFree(MemoryContext context, void *pointer)
622 AllocSet set = (AllocSet) context;
623 AllocChunk chunk = AllocPointerGetChunk(pointer);
625 #if defined(CLOBBER_FREED_MEMORY) || defined(MEMORY_CONTEXT_CHECKING)
626 /* Wipe freed memory for debugging purposes or for memory leak
627 * searching (in freelist[] must be mark memory
629 memset(pointer, 0x7F, chunk->size);
632 AllocFreeInfo(set, chunk);
634 if (chunk->size >= ALLOC_BIGCHUNK_LIMIT)
637 * Big chunks are certain to have been allocated as single-chunk
638 * blocks. Find the containing block and return it to malloc().
640 AllocBlock block = set->blocks;
641 AllocBlock prevblock = NULL;
643 while (block != NULL)
645 if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ))
651 elog(ERROR, "AllocSetFree: cannot find block containing chunk");
652 /* let's just make sure chunk is the only one in the block */
653 Assert(block->freeptr == ((char *) block) +
654 (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ));
655 /* OK, remove block from aset's list and free it */
656 if (prevblock == NULL)
657 set->blocks = block->next;
659 prevblock->next = block->next;
660 #ifdef CLOBBER_FREED_MEMORY
661 /* Wipe freed memory for debugging purposes */
662 memset(block, 0x7F, ((char *) block->endptr) - ((char *) block));
668 /* Normal case, put the chunk into appropriate freelist */
669 int fidx = AllocSetFreeIndex(chunk->size);
671 chunk->aset = (void *) set->freelist[fidx];
673 #ifdef MEMORY_CONTEXT_CHECKING
674 chunk->data_size = 0;
676 set->freelist[fidx] = chunk;
682 * Returns new pointer to allocated memory of given size; this memory
683 * is added to the set. Memory associated with given pointer is copied
684 * into the new memory, and the old memory is freed.
687 AllocSetRealloc(MemoryContext context, void *pointer, Size size)
689 AllocSet set = (AllocSet) context;
693 * Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the
694 * allocated area already is >= the new size. (In particular, we
695 * always fall out here if the requested size is a decrease.)
697 oldsize = AllocPointerGetSize(pointer);
700 #ifdef MEMORY_CONTEXT_CHECKING
701 AllocChunk chunk = AllocPointerGetChunk(pointer);
703 /* mark memory for memory leak searching */
704 memset(((char *) chunk) + (ALLOC_CHUNKHDRSZ + size),
705 0x7F, chunk->size - size);
706 chunk->data_size = size;
711 if (oldsize >= ALLOC_BIGCHUNK_LIMIT)
715 * If the chunk is already >= bigchunk limit, then it must have
716 * been allocated as a single-chunk block. Find the containing
717 * block and use realloc() to make it bigger with minimum space
720 AllocChunk chunk = AllocPointerGetChunk(pointer);
721 AllocBlock block = set->blocks;
722 AllocBlock prevblock = NULL;
724 #ifdef MEMORY_CONTEXT_CHECKING
725 Size data_size = size;
728 while (block != NULL)
730 if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ))
736 elog(ERROR, "AllocSetRealloc: cannot find block containing chunk");
737 /* let's just make sure chunk is the only one in the block */
738 Assert(block->freeptr == ((char *) block) +
739 (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ));
742 size = MAXALIGN(size);
743 blksize = size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
744 block = (AllocBlock) realloc(block, blksize);
746 elog(ERROR, "Memory exhausted in AllocSetReAlloc()");
747 block->freeptr = block->endptr = ((char *) block) + blksize;
749 /* Update pointers since block has likely been moved */
750 chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ);
751 if (prevblock == NULL)
754 prevblock->next = block;
757 #ifdef MEMORY_CONTEXT_CHECKING
758 /* mark memory for memory leak searching */
759 memset(((char *) chunk) + (ALLOC_CHUNKHDRSZ + data_size),
760 0x7F, size - data_size);
761 chunk->data_size = data_size;
763 return AllocChunkGetPointer(chunk);
767 /* Normal small-chunk case: just do it by brute force. */
769 /* allocate new chunk */
770 AllocPointer newPointer = AllocSetAlloc((MemoryContext) set, size);
772 /* transfer existing data (certain to fit) */
773 memcpy(newPointer, pointer, oldsize);
776 AllocSetFree((MemoryContext) set, pointer);
784 * Displays stats about memory consumption of an allocset.
787 AllocSetStats(MemoryContext context)
789 AllocSet set = (AllocSet) context;
798 for (block = set->blocks; block != NULL; block = block->next)
801 totalspace += block->endptr - ((char *) block);
802 freespace += block->endptr - block->freeptr;
804 for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++)
806 for (chunk = set->freelist[fidx]; chunk != NULL;
807 chunk = (AllocChunk) chunk->aset)
810 freespace += chunk->size + ALLOC_CHUNKHDRSZ;
814 "%s: %ld total in %ld blocks; %ld free (%ld chunks); %ld used\n",
815 set->header.name, totalspace, nblocks, freespace, nchunks,
816 totalspace - freespace);
822 * Walk on chunks and check consistence of memory.
824 #ifdef MEMORY_CONTEXT_CHECKING
827 AllocSetCheck(MemoryContext context)
829 AllocSet set = (AllocSet) context;
830 AllocBlock block = NULL;
831 AllocChunk chunk = NULL;
832 char *name = set->header.name;
834 for (block = set->blocks; block != NULL; block = block->next)
836 char *bpoz = ((char *) block) + ALLOC_BLOCKHDRSZ;
837 /* long blk_size = block->endptr - ((char *) block);*/
838 long blk_free = block->endptr - block->freeptr;
839 long blk_used = block->freeptr - bpoz;
844 * Empty block - empty can be keeper-block only
848 if (set->keeper == block)
851 elog(ERROR, "AllocSetCheck(): %s: empty block %p",
864 chunk = (AllocChunk) bpoz;
866 chsize = chunk->size; /* align chunk size */
867 dsize = chunk->data_size; /* real data */
869 chdata_end = ((char *) chunk) + (ALLOC_CHUNKHDRSZ + dsize);
870 chend = ((char *) chunk) + (ALLOC_CHUNKHDRSZ + chsize);
872 if (!dsize && chsize < dsize)
873 elog(ERROR, "AllocSetCheck(): %s: internal error for chunk %p in block %p",
878 if (chsize < (1 << ALLOC_MINBITS))
879 elog(ERROR, "AllocSetCheck(): %s: bad size '%d' for chunk %p in block %p",
880 name, chsize, chunk, block);
882 /* single-chunk block */
883 if (chsize >= ALLOC_BIGCHUNK_LIMIT &&
884 chsize + ALLOC_CHUNKHDRSZ != blk_used)
885 elog(ERROR, "AllocSetCheck(): %s: bad singel-chunk %p in block %p",
889 * Check in-chunk leak
891 if (dsize < chsize && *chdata_end != 0x7F)
893 fprintf(stderr, "\n--- Leak %p ---\n", chdata_end);
894 fprintf(stderr, "Chunk dump size: %ld (chunk-header %ld + chunk-size: %d), data must be: %d\n--- dump begin ---\n",
895 chsize + ALLOC_CHUNKHDRSZ,
896 ALLOC_CHUNKHDRSZ, chsize, dsize);
898 fwrite((void *) chunk, chsize+ALLOC_CHUNKHDRSZ, sizeof(char), stderr);
899 fputs("\n--- dump end ---\n", stderr);
901 elog(ERROR, "AllocSetCheck(): %s: found in-chunk memory leak (block %p; chunk %p; leak at %p",
902 name, block, chunk, chdata_end);
906 * Check block-freeptr leak
908 if (chend == block->freeptr && blk_free &&
909 *chdata_end != 0x7F) {
911 fprintf(stderr, "\n--- Leak %p ---\n", chdata_end);
912 fprintf(stderr, "Dump size: %ld (chunk-header %ld + chunk-size: %d + block-freespace: %ld), data must be: %d\n--- dump begin ---\n",
913 chsize + ALLOC_CHUNKHDRSZ + blk_free,
914 ALLOC_CHUNKHDRSZ, chsize, blk_free, dsize);
916 fwrite((void *) chunk, chsize+ALLOC_CHUNKHDRSZ+blk_free, sizeof(char), stderr);
917 fputs("\n--- dump end ---\n", stderr);
919 elog(ERROR, "AllocSetCheck(): %s: found block-freeptr memory leak (block %p; chunk %p; leak at %p",
920 name, block, chunk, chdata_end);
926 if (chend < block->freeptr)
927 bpoz += ALLOC_CHUNKHDRSZ + chsize;
931 } while(block->freeptr > bpoz); /* chunk walker */
934 if ((blk_data + (nchunks * ALLOC_CHUNKHDRSZ)) != blk_used)
936 elog(ERROR, "AllocSetCheck(): %s: found non-consistent memory block %p",