#define GetFreeListLink(chkptr) \
(AllocFreeListLink *) ((char *) (chkptr) + ALLOC_CHUNKHDRSZ)
+/* Validate a freelist index retrieved from a chunk header */
+#define FreeListIdxIsValid(fidx) \
+ ((fidx) >= 0 && (fidx) < ALLOCSET_NUM_FREELISTS)
+
/* Determine the size of the chunk based on the freelist index */
#define GetChunkSizeFromFreeListIdx(fidx) \
((((Size) 1) << ALLOC_MINBITS) << (fidx))
* AllocSetIsValid
* True iff set is valid allocation set.
*/
-#define AllocSetIsValid(set) PointerIsValid(set)
+#define AllocSetIsValid(set) \
+ (PointerIsValid(set) && IsA(set, AllocSetContext))
+
+/*
+ * AllocBlockIsValid
+ * True iff block is valid block of allocation set.
+ */
+#define AllocBlockIsValid(block) \
+ (PointerIsValid(block) && AllocSetIsValid((block)->aset))
/*
* We always store external chunks on a dedicated block. This makes fetching
{
AllocSet set = (AllocSet) context;
AllocBlock block;
- Size keepersize PG_USED_FOR_ASSERTS_ONLY
- = set->keeper->endptr - ((char *) set);
+ Size keepersize PG_USED_FOR_ASSERTS_ONLY;
AssertArg(AllocSetIsValid(set));
AllocSetCheck(context);
#endif
+ /* Remember keeper block size for Assert below */
+ keepersize = set->keeper->endptr - ((char *) set);
+
/* Clear chunk freelists */
MemSetAligned(set->freelist, 0, sizeof(set->freelist));
{
AllocSet set = (AllocSet) context;
AllocBlock block = set->blocks;
- Size keepersize PG_USED_FOR_ASSERTS_ONLY
- = set->keeper->endptr - ((char *) set);
+ Size keepersize PG_USED_FOR_ASSERTS_ONLY;
AssertArg(AllocSetIsValid(set));
AllocSetCheck(context);
#endif
+ /* Remember keeper block size for Assert below */
+ keepersize = set->keeper->endptr - ((char *) set);
+
/*
* If the context is a candidate for a freelist, put it into that freelist
* instead of destroying it.
if (MemoryChunkIsExternal(chunk))
{
-
+ /* Release single-chunk block. */
AllocBlock block = ExternalChunkGetBlock(chunk);
+ /*
+ * Try to verify that we have a sane block pointer: the block header
+ * should reference an aset and the freeptr should match the endptr.
+ */
+ if (!AllocBlockIsValid(block) || block->freeptr != block->endptr)
+ elog(ERROR, "could not find block containing chunk %p", chunk);
+
set = block->aset;
#ifdef MEMORY_CONTEXT_CHECKING
}
#endif
-
- /*
- * Try to verify that we have a sane block pointer, the freeptr should
- * match the endptr.
- */
- if (block->freeptr != block->endptr)
- elog(ERROR, "could not find block containing chunk %p", chunk);
-
/* OK, remove block from aset's list and free it */
if (block->prev)
block->prev->next = block->next;
}
else
{
- int fidx = MemoryChunkGetValue(chunk);
AllocBlock block = MemoryChunkGetBlock(chunk);
- AllocFreeListLink *link = GetFreeListLink(chunk);
+ int fidx;
+ AllocFreeListLink *link;
+ /*
+ * In this path, for speed reasons we just Assert that the referenced
+ * block is good. We can also Assert that the value field is sane.
+ * Future field experience may show that these Asserts had better
+ * become regular runtime test-and-elog checks.
+ */
+ AssertArg(AllocBlockIsValid(block));
set = block->aset;
+ fidx = MemoryChunkGetValue(chunk);
+ Assert(FreeListIdxIsValid(fidx));
+ link = GetFreeListLink(chunk);
+
#ifdef MEMORY_CONTEXT_CHECKING
/* Test for someone scribbling on unused space in chunk */
if (chunk->requested_size < GetChunkSizeFromFreeListIdx(fidx))
AllocSet set;
MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
Size oldsize;
+ int fidx;
/* Allow access to private part of chunk header. */
VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOCCHUNK_PRIVATE_LEN);
Size oldblksize;
block = ExternalChunkGetBlock(chunk);
- oldsize = block->endptr - (char *) pointer;
+
+ /*
+ * Try to verify that we have a sane block pointer: the block header
+ * should reference an aset and the freeptr should match the endptr.
+ */
+ if (!AllocBlockIsValid(block) || block->freeptr != block->endptr)
+ elog(ERROR, "could not find block containing chunk %p", chunk);
+
set = block->aset;
+ oldsize = block->endptr - (char *) pointer;
+
#ifdef MEMORY_CONTEXT_CHECKING
/* Test for someone scribbling on unused space in chunk */
Assert(chunk->requested_size < oldsize);
set->header.name, chunk);
#endif
- /*
- * Try to verify that we have a sane block pointer, the freeptr should
- * match the endptr.
- */
- if (block->freeptr != block->endptr)
- elog(ERROR, "could not find block containing chunk %p", chunk);
-
#ifdef MEMORY_CONTEXT_CHECKING
/* ensure there's always space for the sentinel byte */
chksize = MAXALIGN(size + 1);
}
block = MemoryChunkGetBlock(chunk);
- oldsize = GetChunkSizeFromFreeListIdx(MemoryChunkGetValue(chunk));
+
+ /*
+ * In this path, for speed reasons we just Assert that the referenced
+ * block is good. We can also Assert that the value field is sane. Future
+ * field experience may show that these Asserts had better become regular
+ * runtime test-and-elog checks.
+ */
+ AssertArg(AllocBlockIsValid(block));
set = block->aset;
+ fidx = MemoryChunkGetValue(chunk);
+ Assert(FreeListIdxIsValid(fidx));
+ oldsize = GetChunkSizeFromFreeListIdx(fidx);
+
#ifdef MEMORY_CONTEXT_CHECKING
/* Test for someone scribbling on unused space in chunk */
if (chunk->requested_size < oldsize)
else
block = (AllocBlock) MemoryChunkGetBlock(chunk);
+ AssertArg(AllocBlockIsValid(block));
set = block->aset;
return &set->header;
AllocSetGetChunkSpace(void *pointer)
{
MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
+ int fidx;
if (MemoryChunkIsExternal(chunk))
{
AllocBlock block = ExternalChunkGetBlock(chunk);
+ AssertArg(AllocBlockIsValid(block));
return block->endptr - (char *) chunk;
}
- return GetChunkSizeFromFreeListIdx(MemoryChunkGetValue(chunk)) +
- ALLOC_CHUNKHDRSZ;
+ fidx = MemoryChunkGetValue(chunk);
+ Assert(FreeListIdxIsValid(fidx));
+ return GetChunkSizeFromFreeListIdx(fidx) + ALLOC_CHUNKHDRSZ;
}
/*
bool
AllocSetIsEmpty(MemoryContext context)
{
+ AssertArg(AllocSetIsValid(context));
+
/*
* For now, we say "empty" only if the context is new or just reset. We
* could examine the freelists to determine if all space has been freed,
AllocBlock block;
int fidx;
+ AssertArg(AllocSetIsValid(set));
+
/* Include context header in totalspace */
totalspace = MAXALIGN(sizeof(AllocSetContext));
}
for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++)
{
+ Size chksz = GetChunkSizeFromFreeListIdx(fidx);
MemoryChunk *chunk = set->freelist[fidx];
while (chunk != NULL)
{
- Size chksz = GetChunkSizeFromFreeListIdx(MemoryChunkGetValue(chunk));
AllocFreeListLink *link = GetFreeListLink(chunk);
- Assert(GetChunkSizeFromFreeListIdx(fidx) == chksz);
+ Assert(MemoryChunkGetValue(chunk) == fidx);
freechunks++;
freespace += chksz + ALLOC_CHUNKHDRSZ;
}
else
{
- chsize = GetChunkSizeFromFreeListIdx(MemoryChunkGetValue(chunk)); /* aligned chunk size */
+ int fidx = MemoryChunkGetValue(chunk);
+
+ if (!FreeListIdxIsValid(fidx))
+ elog(WARNING, "problem in alloc set %s: bad chunk size for chunk %p in block %p",
+ name, chunk, block);
+
+ chsize = GetChunkSizeFromFreeListIdx(fidx); /* aligned chunk size */
/*
* Check the stored block offset correctly references this
* simplicity.
*/
#define GENERATIONCHUNK_PRIVATE_LEN offsetof(MemoryChunk, hdrmask)
+
/*
* GenerationIsValid
- * True iff set is valid allocation set.
+ * True iff set is valid generation set.
+ */
+#define GenerationIsValid(set) \
+ (PointerIsValid(set) && IsA(set, GenerationContext))
+
+/*
+ * GenerationBlockIsValid
+ * True iff block is valid block of generation set.
*/
-#define GenerationIsValid(set) PointerIsValid(set)
+#define GenerationBlockIsValid(block) \
+ (PointerIsValid(block) && GenerationIsValid((block)->context))
/*
* We always store external chunks on a dedicated block. This makes fetching
Size chunk_size;
Size required_size;
+ AssertArg(GenerationIsValid(set));
+
#ifdef MEMORY_CONTEXT_CHECKING
/* ensure there's always space for the sentinel byte */
chunk_size = MAXALIGN(size + 1);
if (MemoryChunkIsExternal(chunk))
{
block = ExternalChunkGetBlock(chunk);
+
+ /*
+ * Try to verify that we have a sane block pointer: the block header
+ * should reference a generation context.
+ */
+ if (!GenerationBlockIsValid(block))
+ elog(ERROR, "could not find block containing chunk %p", chunk);
+
#if defined(MEMORY_CONTEXT_CHECKING) || defined(CLOBBER_FREED_MEMORY)
chunksize = block->endptr - (char *) pointer;
#endif
else
{
block = MemoryChunkGetBlock(chunk);
+
+ /*
+ * In this path, for speed reasons we just Assert that the referenced
+ * block is good. Future field experience may show that this Assert
+ * had better become a regular runtime test-and-elog check.
+ */
+ AssertArg(GenerationBlockIsValid(block));
+
#if defined(MEMORY_CONTEXT_CHECKING) || defined(CLOBBER_FREED_MEMORY)
chunksize = MemoryChunkGetValue(chunk);
#endif
if (MemoryChunkIsExternal(chunk))
{
block = ExternalChunkGetBlock(chunk);
+
+ /*
+ * Try to verify that we have a sane block pointer: the block header
+ * should reference a generation context.
+ */
+ if (!GenerationBlockIsValid(block))
+ elog(ERROR, "could not find block containing chunk %p", chunk);
+
oldsize = block->endptr - (char *) pointer;
}
else
{
block = MemoryChunkGetBlock(chunk);
+
+ /*
+ * In this path, for speed reasons we just Assert that the referenced
+ * block is good. Future field experience may show that this Assert
+ * had better become a regular runtime test-and-elog check.
+ */
+ AssertArg(GenerationBlockIsValid(block));
+
oldsize = MemoryChunkGetValue(chunk);
}
else
block = (GenerationBlock *) MemoryChunkGetBlock(chunk);
+ AssertArg(GenerationBlockIsValid(block));
return &block->context->header;
}
{
GenerationBlock *block = ExternalChunkGetBlock(chunk);
+ AssertArg(GenerationBlockIsValid(block));
chunksize = block->endptr - (char *) pointer;
}
else
GenerationContext *set = (GenerationContext *) context;
dlist_iter iter;
+ AssertArg(GenerationIsValid(set));
+
dlist_foreach(iter, &set->blocks)
{
GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur);
Size freespace = 0;
dlist_iter iter;
+ AssertArg(GenerationIsValid(set));
+
/* Include context header in totalspace */
totalspace = MAXALIGN(sizeof(GenerationContext));
#define SlabChunkIndex(slab, block, chunk) \
(((char *) chunk - SlabBlockStart(block)) / slab->fullChunkSize)
+/*
+ * SlabIsValid
+ * True iff set is valid slab allocation set.
+ */
+#define SlabIsValid(set) \
+ (PointerIsValid(set) && IsA(set, SlabContext))
+
+/*
+ * SlabBlockIsValid
+ * True iff block is valid block of slab allocation set.
+ */
+#define SlabBlockIsValid(block) \
+ (PointerIsValid(block) && SlabIsValid((block)->slab))
+
+
/*
* SlabContextCreate
* Create a new Slab context.
void
SlabReset(MemoryContext context)
{
+ SlabContext *slab = (SlabContext *) context;
int i;
- SlabContext *slab = castNode(SlabContext, context);
- Assert(slab);
+ AssertArg(SlabIsValid(slab));
#ifdef MEMORY_CONTEXT_CHECKING
/* Check for corruption and leaks before freeing */
void *
SlabAlloc(MemoryContext context, Size size)
{
- SlabContext *slab = castNode(SlabContext, context);
+ SlabContext *slab = (SlabContext *) context;
SlabBlock *block;
MemoryChunk *chunk;
int idx;
- Assert(slab);
+ AssertArg(SlabIsValid(slab));
Assert((slab->minFreeChunks >= 0) &&
(slab->minFreeChunks < slab->chunksPerBlock));
void
SlabFree(void *pointer)
{
- int idx;
MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
SlabBlock *block = MemoryChunkGetBlock(chunk);
- SlabContext *slab = block->slab;
+ SlabContext *slab;
+ int idx;
+
+ /*
+ * For speed reasons we just Assert that the referenced block is good.
+ * Future field experience may show that this Assert had better become a
+ * regular runtime test-and-elog check.
+ */
+ AssertArg(SlabBlockIsValid(block));
+ slab = block->slab;
#ifdef MEMORY_CONTEXT_CHECKING
/* Test for someone scribbling on unused space in chunk */
{
MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
SlabBlock *block = MemoryChunkGetBlock(chunk);
- SlabContext *slab = block->slab;
+ SlabContext *slab;
+
+ /*
+ * Try to verify that we have a sane block pointer: the block header
+ * should reference a slab context. (We use a test-and-elog, not just
+ * Assert, because it seems highly likely that we're here in error in the
+ * first place.)
+ */
+ if (!SlabBlockIsValid(block))
+ elog(ERROR, "could not find block containing chunk %p", chunk);
+ slab = block->slab;
- Assert(slab);
/* can't do actual realloc with slab, but let's try to be gentle */
if (size == slab->chunkSize)
return pointer;
{
MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
SlabBlock *block = MemoryChunkGetBlock(chunk);
- SlabContext *slab = block->slab;
-
- Assert(slab != NULL);
- return &slab->header;
+ AssertArg(SlabBlockIsValid(block));
+ return &block->slab->header;
}
/*
{
MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
SlabBlock *block = MemoryChunkGetBlock(chunk);
- SlabContext *slab = block->slab;
+ SlabContext *slab;
- Assert(slab);
+ AssertArg(SlabBlockIsValid(block));
+ slab = block->slab;
return slab->fullChunkSize;
}
bool
SlabIsEmpty(MemoryContext context)
{
- SlabContext *slab = castNode(SlabContext, context);
+ SlabContext *slab = (SlabContext *) context;
- Assert(slab);
+ AssertArg(SlabIsValid(slab));
return (slab->nblocks == 0);
}
MemoryContextCounters *totals,
bool print_to_stderr)
{
- SlabContext *slab = castNode(SlabContext, context);
+ SlabContext *slab = (SlabContext *) context;
Size nblocks = 0;
Size freechunks = 0;
Size totalspace;
Size freespace = 0;
int i;
+ AssertArg(SlabIsValid(slab));
+
/* Include context header in totalspace */
totalspace = slab->headerSize;
void
SlabCheck(MemoryContext context)
{
+ SlabContext *slab = (SlabContext *) context;
int i;
- SlabContext *slab = castNode(SlabContext, context);
const char *name = slab->header.name;
- Assert(slab);
+ AssertArg(SlabIsValid(slab));
Assert(slab->chunksPerBlock > 0);
/* walk all the freelists */