Clean up MEMORY_CONTEXT_CHECKING code, and apply it more thoroughly. Also,

apply Karel Zak's patch to recycle residual space in an exhausted allocation
block.  (Bet you thought I'd forgot about that, Karel?)
This commit is contained in:
Tom Lane 2000-12-01 05:16:45 +00:00
parent b7c3784417
commit dd2b56d623
2 changed files with 208 additions and 175 deletions

View File

@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.32 2000/11/16 05:51:02 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.33 2000/12/01 05:16:45 tgl Exp $
* *
* NOTE: * NOTE:
* This is a new (Feb. 05, 1999) implementation of the allocation set * This is a new (Feb. 05, 1999) implementation of the allocation set
@ -31,6 +31,24 @@
* when the caller is repeatedly repalloc()'ing a block bigger and bigger; * when the caller is repeatedly repalloc()'ing a block bigger and bigger;
* the previous instances of the block were guaranteed to be wasted until * the previous instances of the block were guaranteed to be wasted until
* AllocSetReset() under the old way. * AllocSetReset() under the old way.
*
* About CLOBBER_FREED_MEMORY:
*
* If this symbol is defined, all freed memory is overwritten with 0x7F's.
* This is useful for catching places that reference already-freed memory.
*
* About MEMORY_CONTEXT_CHECKING:
*
* Since we usually round request sizes up to the next power of 2, there
* is often some unused space immediately after a requested data area.
* Thus, if someone makes the common error of writing past what they've
* requested, the problem is likely to go unnoticed ... until the day when
* there *isn't* any wasted space, perhaps because of different memory
* alignment on a new platform, or some other effect. To catch this sort
* of problem, the MEMORY_CONTEXT_CHECKING option stores 0x7E just beyond
* the requested space whenever the request is less than the actual chunk
* size, and verifies that the byte is undamaged when the chunk is freed.
*
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -38,9 +56,8 @@
#include "utils/memutils.h" #include "utils/memutils.h"
/* Define this to detail debug alloc information /* Define this to detail debug alloc information */
*/ /* #define HAVE_ALLOCINFO */
/*#define HAVE_ALLOCINFO 1*/
/* /*
* AllocSetContext is defined in nodes/memnodes.h. * AllocSetContext is defined in nodes/memnodes.h.
@ -82,11 +99,12 @@ typedef struct AllocBlockData
typedef struct AllocChunkData typedef struct AllocChunkData
{ {
/* aset is the owning aset if allocated, or the freelist link if free */ /* aset is the owning aset if allocated, or the freelist link if free */
void *aset; void *aset;
/* size is always the size of the usable space in the chunk */ /* size is always the size of the usable space in the chunk */
Size size; Size size;
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
Size data_size; /* when debugging memory usage, also store actual requested size */
Size requested_size;
#endif #endif
} AllocChunkData; } AllocChunkData;
@ -155,8 +173,6 @@ typedef struct AllocChunkData
((AllocChunk)(((char *)(ptr)) - ALLOC_CHUNKHDRSZ)) ((AllocChunk)(((char *)(ptr)) - ALLOC_CHUNKHDRSZ))
#define AllocChunkGetPointer(chk) \ #define AllocChunkGetPointer(chk) \
((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ)) ((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ))
#define AllocPointerGetAset(ptr) ((AllocSet)(AllocPointerGetChunk(ptr)->aset))
#define AllocPointerGetSize(ptr) (AllocPointerGetChunk(ptr)->size)
/* /*
* These functions implement the MemoryContext API for AllocSet contexts. * These functions implement the MemoryContext API for AllocSet contexts.
@ -167,11 +183,9 @@ static void *AllocSetRealloc(MemoryContext context, void *pointer, Size size);
static void AllocSetInit(MemoryContext context); static void AllocSetInit(MemoryContext context);
static void AllocSetReset(MemoryContext context); static void AllocSetReset(MemoryContext context);
static void AllocSetDelete(MemoryContext context); static void AllocSetDelete(MemoryContext context);
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
static void AllocSetCheck(MemoryContext context); static void AllocSetCheck(MemoryContext context);
#endif #endif
static void AllocSetStats(MemoryContext context); static void AllocSetStats(MemoryContext context);
/* /*
@ -293,11 +307,6 @@ AllocSetContextCreate(MemoryContext parent,
context->blocks = block; context->blocks = block;
/* Mark block as not to be released at reset time */ /* Mark block as not to be released at reset time */
context->keeper = block; context->keeper = block;
#ifdef MEMORY_CONTEXT_CHECKING
/* mark memory for memory leak searching */
memset(block->freeptr, 0x7F, blksize - ALLOC_BLOCKHDRSZ);
#endif
} }
return (MemoryContext) context; return (MemoryContext) context;
@ -342,6 +351,11 @@ AllocSetReset(MemoryContext context)
AssertArg(AllocSetIsValid(set)); AssertArg(AllocSetIsValid(set));
#ifdef MEMORY_CONTEXT_CHECKING
/* Check for corruption and leaks before freeing */
AllocSetCheck(context);
#endif
while (block != NULL) while (block != NULL)
{ {
AllocBlock next = block->next; AllocBlock next = block->next;
@ -353,7 +367,7 @@ AllocSetReset(MemoryContext context)
#ifdef CLOBBER_FREED_MEMORY #ifdef CLOBBER_FREED_MEMORY
/* Wipe freed memory for debugging purposes */ /* Wipe freed memory for debugging purposes */
memset(datastart, 0x7F, ((char *) block->freeptr) - datastart); memset(datastart, 0x7F, block->freeptr - datastart);
#endif #endif
block->freeptr = datastart; block->freeptr = datastart;
block->next = NULL; block->next = NULL;
@ -363,7 +377,7 @@ AllocSetReset(MemoryContext context)
/* Normal case, release the block */ /* Normal case, release the block */
#ifdef CLOBBER_FREED_MEMORY #ifdef CLOBBER_FREED_MEMORY
/* Wipe freed memory for debugging purposes */ /* Wipe freed memory for debugging purposes */
memset(block, 0x7F, ((char *) block->freeptr) - ((char *) block)); memset(block, 0x7F, block->freeptr - ((char *) block));
#endif #endif
free(block); free(block);
} }
@ -392,13 +406,18 @@ AllocSetDelete(MemoryContext context)
AssertArg(AllocSetIsValid(set)); AssertArg(AllocSetIsValid(set));
#ifdef MEMORY_CONTEXT_CHECKING
/* Check for corruption and leaks before freeing */
AllocSetCheck(context);
#endif
while (block != NULL) while (block != NULL)
{ {
AllocBlock next = block->next; AllocBlock next = block->next;
#ifdef CLOBBER_FREED_MEMORY #ifdef CLOBBER_FREED_MEMORY
/* Wipe freed memory for debugging purposes */ /* Wipe freed memory for debugging purposes */
memset(block, 0x7F, ((char *) block->endptr) - ((char *) block)); memset(block, 0x7F, block->freeptr - ((char *) block));
#endif #endif
free(block); free(block);
block = next; block = next;
@ -422,53 +441,47 @@ AllocSetAlloc(MemoryContext context, Size size)
AllocBlock block; AllocBlock block;
AllocChunk chunk; AllocChunk chunk;
AllocChunk priorfree = NULL; AllocChunk priorfree = NULL;
int fidx; int fidx;
Size chunk_size; Size chunk_size;
Size blksize; Size blksize;
AssertArg(AllocSetIsValid(set)); AssertArg(AllocSetIsValid(set));
/* /*
* Small size can be in free list * Lookup in the corresponding free list if there is a free chunk we
* could reuse
*/ */
if (size < ALLOC_BIGCHUNK_LIMIT) fidx = AllocSetFreeIndex(size);
for (chunk = set->freelist[fidx]; chunk; chunk = (AllocChunk) chunk->aset)
{ {
/* if (chunk->size >= size)
* Lookup in the corresponding free list if there is a free chunk we break;
* could reuse priorfree = chunk;
*/ }
fidx = AllocSetFreeIndex(size);
for (chunk = set->freelist[fidx]; chunk; chunk = (AllocChunk) chunk->aset)
{
if (chunk->size >= size)
break;
priorfree = chunk;
}
/* /*
* If one is found, remove it from the free list, make it again a * If one is found, remove it from the free list, make it again a
* member of the alloc set and return its data address. * member of the alloc set and return its data address.
*/ */
if (chunk != NULL) if (chunk != NULL)
{ {
if (priorfree == NULL) if (priorfree == NULL)
set->freelist[fidx] = (AllocChunk) chunk->aset; set->freelist[fidx] = (AllocChunk) chunk->aset;
else else
priorfree->aset = chunk->aset; priorfree->aset = chunk->aset;
chunk->aset = (void *) set; chunk->aset = (void *) set;
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
chunk->data_size = size; chunk->requested_size = size;
/* set mark to catch clobber of "unused" space */
if (size < chunk->size)
((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
#endif #endif
AllocAllocInfo(set, chunk);
return AllocChunkGetPointer(chunk); AllocAllocInfo(set, chunk);
} return AllocChunkGetPointer(chunk);
} }
else
/* Big chunk
*/
fidx = ALLOCSET_NUM_FREELISTS - 1;
/* /*
* Choose the actual chunk size to allocate. * Choose the actual chunk size to allocate.
@ -510,6 +523,12 @@ AllocSetAlloc(MemoryContext context, Size size)
chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ); chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ);
chunk->aset = set; chunk->aset = set;
chunk->size = chunk_size; chunk->size = chunk_size;
#ifdef MEMORY_CONTEXT_CHECKING
chunk->requested_size = size;
/* set mark to catch clobber of "unused" space */
if (size < chunk->size)
((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
#endif
/* /*
* Try to stick the block underneath the active allocation block, * Try to stick the block underneath the active allocation block,
@ -526,11 +545,6 @@ AllocSetAlloc(MemoryContext context, Size size)
set->blocks = block; set->blocks = block;
} }
#ifdef MEMORY_CONTEXT_CHECKING
chunk->data_size = size;
/* mark memory for memory leak searching */
memset(AllocChunkGetPointer(chunk), 0x7F, chunk->size);
#endif
AllocAllocInfo(set, chunk); AllocAllocInfo(set, chunk);
return AllocChunkGetPointer(chunk); return AllocChunkGetPointer(chunk);
} }
@ -547,6 +561,47 @@ AllocSetAlloc(MemoryContext context, Size size)
} }
else else
{ {
/*
* The existing active (top) block does not have enough room
* for the requested allocation, but it might still have a
* useful amount of space in it. Once we push it down in the
* block list, we'll never try to allocate more space from it.
* So, before we do that, carve up its free space into chunks
* that we can put on the set's freelists.
*/
Size availspace = set->blocks->endptr - set->blocks->freeptr;
while (availspace >= ((1 << ALLOC_MINBITS) + ALLOC_CHUNKHDRSZ))
{
Size availchunk = availspace - ALLOC_CHUNKHDRSZ;
int x_fidx = AllocSetFreeIndex(availchunk);
/*
* In most cases, we'll get back the index of the next larger
* freelist than the one we need to put this chunk on. The
* exceptions are when availchunk is exactly a power of 2,
* or is large enough that we're dealing with the last
* (variable-chunk-size) freelist. This test detects both:
*/
if (availchunk < (1 << (x_fidx + ALLOC_MINBITS)))
{
x_fidx--;
Assert(x_fidx >= 0);
availchunk = (1 << (x_fidx + ALLOC_MINBITS));
}
chunk = (AllocChunk) (set->blocks->freeptr);
chunk->size = availchunk;
#ifdef MEMORY_CONTEXT_CHECKING
chunk->requested_size = 0; /* mark it free */
#endif
chunk->aset = (void *) set->freelist[x_fidx];
set->freelist[x_fidx] = chunk;
set->blocks->freeptr += (availchunk + ALLOC_CHUNKHDRSZ);
availspace -= (availchunk + ALLOC_CHUNKHDRSZ);
}
/* Get size of prior block */ /* Get size of prior block */
blksize = set->blocks->endptr - ((char *) set->blocks); blksize = set->blocks->endptr - ((char *) set->blocks);
@ -586,12 +641,7 @@ AllocSetAlloc(MemoryContext context, Size size)
block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
block->endptr = ((char *) block) + blksize; block->endptr = ((char *) block) + blksize;
#ifdef MEMORY_CONTEXT_CHECKING
/* mark memory for memory leak searching */
memset(block->freeptr, 0x7F, blksize - ALLOC_BLOCKHDRSZ);
#endif
block->next = set->blocks; block->next = set->blocks;
set->blocks = block; set->blocks = block;
} }
@ -601,10 +651,13 @@ AllocSetAlloc(MemoryContext context, Size size)
chunk = (AllocChunk) (block->freeptr); chunk = (AllocChunk) (block->freeptr);
chunk->aset = (void *) set; chunk->aset = (void *) set;
chunk->size = chunk_size; chunk->size = chunk_size;
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
chunk->data_size = size; chunk->requested_size = size;
/* set mark to catch clobber of "unused" space */
if (size < chunk->size)
((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
#endif #endif
block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ); block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
Assert(block->freeptr <= block->endptr); Assert(block->freeptr <= block->endptr);
@ -622,15 +675,16 @@ AllocSetFree(MemoryContext context, void *pointer)
AllocSet set = (AllocSet) context; AllocSet set = (AllocSet) context;
AllocChunk chunk = AllocPointerGetChunk(pointer); AllocChunk chunk = AllocPointerGetChunk(pointer);
#if defined(CLOBBER_FREED_MEMORY) || defined(MEMORY_CONTEXT_CHECKING)
/* Wipe freed memory for debugging purposes or for memory leak
* searching (in freelist[] must be mark memory
*/
memset(pointer, 0x7F, chunk->size);
#endif
AllocFreeInfo(set, chunk); AllocFreeInfo(set, chunk);
#ifdef MEMORY_CONTEXT_CHECKING
/* Test for someone scribbling on unused space in chunk */
if (chunk->requested_size < chunk->size)
if (((char *) pointer)[chunk->requested_size] != 0x7E)
elog(ERROR, "AllocSetFree: detected write past chunk end in %p",
chunk);
#endif
if (chunk->size >= ALLOC_BIGCHUNK_LIMIT) if (chunk->size >= ALLOC_BIGCHUNK_LIMIT)
{ {
/* /*
@ -659,7 +713,7 @@ AllocSetFree(MemoryContext context, void *pointer)
prevblock->next = block->next; prevblock->next = block->next;
#ifdef CLOBBER_FREED_MEMORY #ifdef CLOBBER_FREED_MEMORY
/* Wipe freed memory for debugging purposes */ /* Wipe freed memory for debugging purposes */
memset(block, 0x7F, ((char *) block->endptr) - ((char *) block)); memset(block, 0x7F, block->freeptr - ((char *) block));
#endif #endif
free(block); free(block);
} }
@ -670,8 +724,14 @@ AllocSetFree(MemoryContext context, void *pointer)
chunk->aset = (void *) set->freelist[fidx]; chunk->aset = (void *) set->freelist[fidx];
#ifdef CLOBBER_FREED_MEMORY
/* Wipe freed memory for debugging purposes */
memset(pointer, 0x7F, chunk->size);
#endif
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
chunk->data_size = 0; /* Reset requested_size to 0 in chunks that are on freelist */
chunk->requested_size = 0;
#endif #endif
set->freelist[fidx] = chunk; set->freelist[fidx] = chunk;
} }
@ -687,23 +747,29 @@ static void *
AllocSetRealloc(MemoryContext context, void *pointer, Size size) AllocSetRealloc(MemoryContext context, void *pointer, Size size)
{ {
AllocSet set = (AllocSet) context; AllocSet set = (AllocSet) context;
Size oldsize; AllocChunk chunk = AllocPointerGetChunk(pointer);
Size oldsize = chunk->size;
#ifdef MEMORY_CONTEXT_CHECKING
/* Test for someone scribbling on unused space in chunk */
if (chunk->requested_size < oldsize)
if (((char *) pointer)[chunk->requested_size] != 0x7E)
elog(ERROR, "AllocSetRealloc: detected write past chunk end in %p",
chunk);
#endif
/* /*
* Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the * Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the
* allocated area already is >= the new size. (In particular, we * allocated area already is >= the new size. (In particular, we
* always fall out here if the requested size is a decrease.) * always fall out here if the requested size is a decrease.)
*/ */
oldsize = AllocPointerGetSize(pointer);
if (oldsize >= size) if (oldsize >= size)
{ {
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
AllocChunk chunk = AllocPointerGetChunk(pointer); chunk->requested_size = size;
/* set mark to catch clobber of "unused" space */
/* mark memory for memory leak searching */ if (size < chunk->size)
memset(((char *) chunk) + (ALLOC_CHUNKHDRSZ + size), ((char *) pointer)[size] = 0x7E;
0x7F, chunk->size - size);
chunk->data_size = size;
#endif #endif
return pointer; return pointer;
} }
@ -717,13 +783,10 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
* block and use realloc() to make it bigger with minimum space * block and use realloc() to make it bigger with minimum space
* wastage. * wastage.
*/ */
AllocChunk chunk = AllocPointerGetChunk(pointer);
AllocBlock block = set->blocks; AllocBlock block = set->blocks;
AllocBlock prevblock = NULL; AllocBlock prevblock = NULL;
Size chksize;
Size blksize; Size blksize;
#ifdef MEMORY_CONTEXT_CHECKING
Size data_size = size;
#endif
while (block != NULL) while (block != NULL)
{ {
@ -739,8 +802,8 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
(chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)); (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ));
/* Do the realloc */ /* Do the realloc */
size = MAXALIGN(size); chksize = MAXALIGN(size);
blksize = size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
block = (AllocBlock) realloc(block, blksize); block = (AllocBlock) realloc(block, blksize);
if (block == NULL) if (block == NULL)
elog(ERROR, "Memory exhausted in AllocSetReAlloc()"); elog(ERROR, "Memory exhausted in AllocSetReAlloc()");
@ -752,14 +815,15 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
set->blocks = block; set->blocks = block;
else else
prevblock->next = block; prevblock->next = block;
chunk->size = size; chunk->size = chksize;
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
/* mark memory for memory leak searching */ chunk->requested_size = size;
memset(((char *) chunk) + (ALLOC_CHUNKHDRSZ + data_size), /* set mark to catch clobber of "unused" space */
0x7F, size - data_size); if (size < chunk->size)
chunk->data_size = data_size; ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
#endif #endif
return AllocChunkGetPointer(chunk); return AllocChunkGetPointer(chunk);
} }
else else
@ -817,25 +881,22 @@ AllocSetStats(MemoryContext context)
} }
/*
* AllocSetCheck
* Walk on chunks and check consistence of memory.
*/
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
/*
* AllocSetCheck
* Walk through chunks and check consistency of memory.
*/
static void static void
AllocSetCheck(MemoryContext context) AllocSetCheck(MemoryContext context)
{ {
AllocSet set = (AllocSet) context; AllocSet set = (AllocSet) context;
AllocBlock block = NULL;
AllocChunk chunk = NULL;
char *name = set->header.name; char *name = set->header.name;
AllocBlock block;
for (block = set->blocks; block != NULL; block = block->next) for (block = set->blocks; block != NULL; block = block->next)
{ {
char *bpoz = ((char *) block) + ALLOC_BLOCKHDRSZ; char *bpoz = ((char *) block) + ALLOC_BLOCKHDRSZ;
/* long blk_size = block->endptr - ((char *) block);*/
long blk_free = block->endptr - block->freeptr;
long blk_used = block->freeptr - bpoz; long blk_used = block->freeptr - bpoz;
long blk_data = 0; long blk_data = 0;
long nchunks = 0; long nchunks = 0;
@ -845,96 +906,67 @@ AllocSetCheck(MemoryContext context)
*/ */
if (!blk_used) if (!blk_used)
{ {
if (set->keeper == block) if (set->keeper != block)
continue; elog(NOTICE, "AllocSetCheck(): %s: empty block %p",
else name, block);
elog(ERROR, "AllocSetCheck(): %s: empty block %p",
name, block);
} }
/* /*
* Chunk walker * Chunk walker
*/ */
do { while (bpoz < block->freeptr)
Size chsize, {
dsize; AllocChunk chunk = (AllocChunk) bpoz;
char *chdata_end, Size chsize,
*chend; dsize;
char *chdata_end;
chunk = (AllocChunk) bpoz;
chsize = chunk->size; /* align chunk size */
dsize = chunk->data_size; /* real data */
chsize = chunk->size; /* aligned chunk size */
dsize = chunk->requested_size; /* real data */
chdata_end = ((char *) chunk) + (ALLOC_CHUNKHDRSZ + dsize); chdata_end = ((char *) chunk) + (ALLOC_CHUNKHDRSZ + dsize);
chend = ((char *) chunk) + (ALLOC_CHUNKHDRSZ + chsize);
if (!dsize && chsize < dsize)
elog(ERROR, "AllocSetCheck(): %s: internal error for chunk %p in block %p",
name, chunk, block);
/* /*
* Check chunk size * Check chunk size
*/ */
if (dsize > chsize)
elog(ERROR, "AllocSetCheck(): %s: req size > alloc size for chunk %p in block %p",
name, chunk, block);
if (chsize < (1 << ALLOC_MINBITS)) if (chsize < (1 << ALLOC_MINBITS))
elog(ERROR, "AllocSetCheck(): %s: bad size '%lu' for chunk %p in block %p", elog(ERROR, "AllocSetCheck(): %s: bad size %lu for chunk %p in block %p",
name, (unsigned long)chsize, chunk, block); name, (unsigned long) chsize, chunk, block);
/* single-chunk block */ /* single-chunk block? */
if (chsize >= ALLOC_BIGCHUNK_LIMIT && if (chsize >= ALLOC_BIGCHUNK_LIMIT &&
chsize + ALLOC_CHUNKHDRSZ != blk_used) chsize + ALLOC_CHUNKHDRSZ != blk_used)
elog(ERROR, "AllocSetCheck(): %s: bad singel-chunk %p in block %p", elog(ERROR, "AllocSetCheck(): %s: bad single-chunk %p in block %p",
name, chunk, block); name, chunk, block);
/* /*
* Check in-chunk leak * If chunk is allocated, check for correct aset pointer.
* (If it's free, the aset is the freelist pointer, which we
* can't check as easily...)
*/ */
if (dsize < chsize && *chdata_end != 0x7F) if (dsize > 0 && chunk->aset != (void *) set)
{ elog(ERROR, "AllocSetCheck(): %s: bogus aset link in block %p, chunk %p",
fprintf(stderr, "\n--- Leak %p ---\n", chdata_end); name, block, chunk);
fprintf(stderr, "Chunk dump size: %ld (chunk-header %ld + chunk-size: %lu), data must be: %lu\n--- dump begin ---\n",
chsize + ALLOC_CHUNKHDRSZ,
ALLOC_CHUNKHDRSZ, (unsigned long)chsize, (unsigned long)dsize);
fwrite((void *) chunk, chsize+ALLOC_CHUNKHDRSZ, sizeof(char), stderr);
fputs("\n--- dump end ---\n", stderr);
elog(ERROR, "AllocSetCheck(): %s: found in-chunk memory leak (block %p; chunk %p; leak at %p",
name, block, chunk, chdata_end);
}
/* /*
* Check block-freeptr leak * Check for overwrite of "unallocated" space in chunk
*/ */
if (chend == block->freeptr && blk_free && if (dsize > 0 && dsize < chsize && *chdata_end != 0x7E)
*chdata_end != 0x7F) { elog(ERROR, "AllocSetCheck(): %s: detected write past chunk end in block %p, chunk %p",
name, block, chunk);
fprintf(stderr, "\n--- Leak %p ---\n", chdata_end);
fprintf(stderr, "Dump size: %ld (chunk-header %ld + chunk-size: %lu + block-freespace: %ld), data must be: %lu\n--- dump begin ---\n",
chsize + ALLOC_CHUNKHDRSZ + blk_free,
ALLOC_CHUNKHDRSZ, (unsigned long)chsize, blk_free, (unsigned long)dsize);
fwrite((void *) chunk, chsize+ALLOC_CHUNKHDRSZ+blk_free, sizeof(char), stderr);
fputs("\n--- dump end ---\n", stderr);
elog(ERROR, "AllocSetCheck(): %s: found block-freeptr memory leak (block %p; chunk %p; leak at %p",
name, block, chunk, chdata_end);
}
blk_data += chsize; blk_data += chsize;
nchunks++; nchunks++;
if (chend < block->freeptr) bpoz += ALLOC_CHUNKHDRSZ + chsize;
bpoz += ALLOC_CHUNKHDRSZ + chsize; }
else
break;
} while(block->freeptr > bpoz); /* chunk walker */
if ((blk_data + (nchunks * ALLOC_CHUNKHDRSZ)) != blk_used) if ((blk_data + (nchunks * ALLOC_CHUNKHDRSZ)) != blk_used)
elog(ERROR, "AllocSetCheck(): %s: found inconsistent memory block %p",
elog(ERROR, "AllocSetCheck(): %s: found non-consistent memory block %p", name, block);
name, block);
} }
} }
#endif
#endif /* MEMORY_CONTEXT_CHECKING */

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: memutils.h,v 1.37 2000/07/11 14:30:37 momjian Exp $ * $Id: memutils.h,v 1.38 2000/12/01 05:16:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -46,10 +46,11 @@
*/ */
typedef struct StandardChunkHeader typedef struct StandardChunkHeader
{ {
MemoryContext context; /* owning context */ MemoryContext context; /* owning context */
Size size; /* size of data space allocated in chunk */ Size size; /* size of data space allocated in chunk */
#ifdef MEMORY_CONTEXT_CHECKING #ifdef MEMORY_CONTEXT_CHECKING
Size data_size; /* real data size (without align) */ /* when debugging memory usage, also store actual requested size */
Size requested_size;
#endif #endif
} StandardChunkHeader; } StandardChunkHeader;