#include "postgres.h"
#include "port/pg_bitutils.h"
#include "utils/memdebug.h"
#include "utils/memutils.h"
#include "utils/memutils_internal.h"
#include "utils/memutils_memorychunk.h"
#define ALLOC_MINBITS 3
#define ALLOCSET_NUM_FREELISTS 11
#define ALLOC_CHUNK_LIMIT (1 << (ALLOCSET_NUM_FREELISTS-1+ALLOC_MINBITS))
#define ALLOC_CHUNK_FRACTION 4
#define ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData))
#define ALLOC_CHUNKHDRSZ sizeof(MemoryChunk)
typedef struct AllocBlockData *AllocBlock;
typedef void *AllocPointer;
typedef struct AllocFreeListLink
{
MemoryChunk *next;
} AllocFreeListLink;
#define GetFreeListLink(chkptr) \
(AllocFreeListLink *) ((char *) (chkptr) + ALLOC_CHUNKHDRSZ)
#define FreeListIdxIsValid(fidx) \
((fidx) >= 0 && (fidx) < ALLOCSET_NUM_FREELISTS)
#define GetChunkSizeFromFreeListIdx(fidx) \
((((Size) 1) << ALLOC_MINBITS) << (fidx))
typedef struct AllocSetContext
{
MemoryContextData header;
AllocBlock blocks;
MemoryChunk *freelist[ALLOCSET_NUM_FREELISTS];
uint32 initBlockSize;
uint32 maxBlockSize;
uint32 nextBlockSize;
uint32 allocChunkLimit;
int freeListIndex;
} AllocSetContext;
typedef AllocSetContext *AllocSet;
typedef struct AllocBlockData
{
AllocSet aset;
AllocBlock prev;
AllocBlock next;
char *freeptr;
char *endptr;
} AllocBlockData;
#define AllocPointerIsValid(pointer) PointerIsValid(pointer)
#define AllocSetIsValid(set) \
(PointerIsValid(set) && IsA(set, AllocSetContext))
#define AllocBlockIsValid(block) \
(PointerIsValid(block) && AllocSetIsValid((block)->aset))
#define ExternalChunkGetBlock(chunk) \
(AllocBlock) ((char *) chunk - ALLOC_BLOCKHDRSZ)
#define MAX_FREE_CONTEXTS 100
#define KeeperBlock(set) \
((AllocBlock) (((char *) set) + MAXALIGN(sizeof(AllocSetContext))))
#define IsKeeperBlock(set, block) ((block) == (KeeperBlock(set)))
typedef struct AllocSetFreeList
{
int num_free;
AllocSetContext *first_free;
} AllocSetFreeList;
static __thread AllocSetFreeList context_freelists[2] =
{
{
0, NULL
},
{
0, NULL
}
};
static inline int
AllocSetFreeIndex(Size size)
{
int idx;
if (size > (1 << ALLOC_MINBITS))
{
#ifdef HAVE_BITSCAN_REVERSE
idx = pg_leftmost_one_pos32((uint32) size - 1) - ALLOC_MINBITS + 1;
#else
uint32 t,
tsize;
StaticAssertDecl(ALLOC_CHUNK_LIMIT < (1 << 16),
"ALLOC_CHUNK_LIMIT must be less than 64kB");
tsize = size - 1;
t = tsize >> 8;
idx = t ? pg_leftmost_one_pos[t] + 8 : pg_leftmost_one_pos[tsize];
idx -= ALLOC_MINBITS - 1;
#endif
Assert(idx < ALLOCSET_NUM_FREELISTS);
}
else
idx = 0;
return idx;
}
MemoryContext
AllocSetContextCreateInternal(MemoryContext parent,
const char *name,
Size minContextSize,
Size initBlockSize,
Size maxBlockSize)
{
int freeListIndex;
Size firstBlockSize;
AllocSet set;
AllocBlock block;
StaticAssertDecl(ALLOC_CHUNKHDRSZ == MAXALIGN(ALLOC_CHUNKHDRSZ),
"sizeof(MemoryChunk) is not maxaligned");
StaticAssertDecl(sizeof(AllocFreeListLink) <= (1 << ALLOC_MINBITS),
"sizeof(AllocFreeListLink) larger than minimum allocation size");
Assert(initBlockSize == MAXALIGN(initBlockSize) &&
initBlockSize >= 1024);
Assert(maxBlockSize == MAXALIGN(maxBlockSize) &&
maxBlockSize >= initBlockSize &&
AllocHugeSizeIsValid(maxBlockSize));
Assert(minContextSize == 0 ||
(minContextSize == MAXALIGN(minContextSize) &&
minContextSize >= 1024 &&
minContextSize <= maxBlockSize));
Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
if (minContextSize == ALLOCSET_DEFAULT_MINSIZE &&
initBlockSize == ALLOCSET_DEFAULT_INITSIZE)
freeListIndex = 0;
else if (minContextSize == ALLOCSET_SMALL_MINSIZE &&
initBlockSize == ALLOCSET_SMALL_INITSIZE)
freeListIndex = 1;
else
freeListIndex = -1;
if (freeListIndex >= 0)
{
AllocSetFreeList *freelist = &context_freelists[freeListIndex];
if (freelist->first_free != NULL)
{
set = freelist->first_free;
freelist->first_free = (AllocSet) set->header.nextchild;
freelist->num_free--;
set->maxBlockSize = maxBlockSize;
MemoryContextCreate((MemoryContext) set,
T_AllocSetContext,
MCTX_ASET_ID,
parent,
name);
((MemoryContext) set)->mem_allocated =
KeeperBlock(set)->endptr - ((char *) set);
return (MemoryContext) set;
}
}
firstBlockSize = MAXALIGN(sizeof(AllocSetContext)) +
ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
if (minContextSize != 0)
firstBlockSize = Max(firstBlockSize, minContextSize);
else
firstBlockSize = Max(firstBlockSize, initBlockSize);
set = (AllocSet) malloc(firstBlockSize);
if (set == NULL)
{
if (TopMemoryContext)
MemoryContextStats(TopMemoryContext);
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory"),
errdetail("Failed while creating memory context \"%s\".",
name)));
}
block = KeeperBlock(set);
block->aset = set;
block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
block->endptr = ((char *) set) + firstBlockSize;
block->prev = NULL;
block->next = NULL;
VALGRIND_MAKE_MEM_NOACCESS(block->freeptr, block->endptr - block->freeptr);
set->blocks = block;
MemSetAligned(set->freelist, 0, sizeof(set->freelist));
set->initBlockSize = (uint32) initBlockSize;
set->maxBlockSize = (uint32) maxBlockSize;
set->nextBlockSize = (uint32) initBlockSize;
set->freeListIndex = freeListIndex;
StaticAssertStmt(ALLOC_CHUNK_LIMIT == ALLOCSET_SEPARATE_THRESHOLD,
"ALLOC_CHUNK_LIMIT != ALLOCSET_SEPARATE_THRESHOLD");
set->allocChunkLimit = ALLOC_CHUNK_LIMIT;
while ((Size) (set->allocChunkLimit + ALLOC_CHUNKHDRSZ) >
(Size) ((maxBlockSize - ALLOC_BLOCKHDRSZ) / ALLOC_CHUNK_FRACTION))
set->allocChunkLimit >>= 1;
MemoryContextCreate((MemoryContext) set,
T_AllocSetContext,
MCTX_ASET_ID,
parent,
name);
((MemoryContext) set)->mem_allocated = firstBlockSize;
return (MemoryContext) set;
}
void
AllocSetReset(MemoryContext context)
{
AllocSet set = (AllocSet) context;
AllocBlock block;
Size keepersize PG_USED_FOR_ASSERTS_ONLY;
Assert(AllocSetIsValid(set));
#ifdef MEMORY_CONTEXT_CHECKING
AllocSetCheck(context);
#endif
keepersize = KeeperBlock(set)->endptr - ((char *) set);
MemSetAligned(set->freelist, 0, sizeof(set->freelist));
block = set->blocks;
set->blocks = KeeperBlock(set);
while (block != NULL)
{
AllocBlock next = block->next;
if (IsKeeperBlock(set, block))
{
char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ;
#ifdef CLOBBER_FREED_MEMORY
wipe_mem(datastart, block->freeptr - datastart);
#else
VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
#endif
block->freeptr = datastart;
block->prev = NULL;
block->next = NULL;
}
else
{
context->mem_allocated -= block->endptr - ((char *) block);
#ifdef CLOBBER_FREED_MEMORY
wipe_mem(block, block->freeptr - ((char *) block));
#endif
free(block);
}
block = next;
}
Assert(context->mem_allocated == keepersize);
set->nextBlockSize = set->initBlockSize;
}
void
AllocSetDelete(MemoryContext context)
{
AllocSet set = (AllocSet) context;
AllocBlock block = set->blocks;
Size keepersize PG_USED_FOR_ASSERTS_ONLY;
Assert(AllocSetIsValid(set));
#ifdef MEMORY_CONTEXT_CHECKING
AllocSetCheck(context);
#endif
keepersize = KeeperBlock(set)->endptr - ((char *) set);
if (set->freeListIndex >= 0)
{
AllocSetFreeList *freelist = &context_freelists[set->freeListIndex];
if (!context->isReset)
MemoryContextResetOnly(context);
if (freelist->num_free >= MAX_FREE_CONTEXTS)
{
while (freelist->first_free != NULL)
{
AllocSetContext *oldset = freelist->first_free;
freelist->first_free = (AllocSetContext *) oldset->header.nextchild;
freelist->num_free--;
free(oldset);
}
Assert(freelist->num_free == 0);
}
set->header.nextchild = (MemoryContext) freelist->first_free;
freelist->first_free = set;
freelist->num_free++;
return;
}
while (block != NULL)
{
AllocBlock next = block->next;
if (!IsKeeperBlock(set, block))
context->mem_allocated -= block->endptr - ((char *) block);
#ifdef CLOBBER_FREED_MEMORY
wipe_mem(block, block->freeptr - ((char *) block));
#endif
if (!IsKeeperBlock(set, block))
free(block);
block = next;
}
Assert(context->mem_allocated == keepersize);
free(set);
}
pg_noinline
static void *
AllocSetAllocLarge(MemoryContext context, Size size, int flags)
{
AllocSet set = (AllocSet) context;
AllocBlock block;
MemoryChunk *chunk;
Size chunk_size;
Size blksize;
MemoryContextCheckSize(context, size, flags);
#ifdef MEMORY_CONTEXT_CHECKING
chunk_size = MAXALIGN(size + 1);
#else
chunk_size = MAXALIGN(size);
#endif
blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
block = (AllocBlock) malloc(blksize);
if (block == NULL)
return MemoryContextAllocationFailure(context, size, flags);
context->mem_allocated += blksize;
block->aset = set;
block->freeptr = block->endptr = ((char *) block) + blksize;
chunk = (MemoryChunk *) (((char *) block) + ALLOC_BLOCKHDRSZ);
MemoryChunkSetHdrMaskExternal(chunk, MCTX_ASET_ID);
#ifdef MEMORY_CONTEXT_CHECKING
chunk->requested_size = size;
Assert(size < chunk_size);
set_sentinel(MemoryChunkGetPointer(chunk), size);
#endif
#ifdef RANDOMIZE_ALLOCATED_MEMORY
randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
#endif
if (set->blocks != NULL)
{
block->prev = set->blocks;
block->next = set->blocks->next;
if (block->next)
block->next->prev = block;
set->blocks->next = block;
}
else
{
block->prev = NULL;
block->next = NULL;
set->blocks = block;
}
VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
chunk_size - size);
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
return MemoryChunkGetPointer(chunk);
}
static inline void *
AllocSetAllocChunkFromBlock(MemoryContext context, AllocBlock block,
Size size, Size chunk_size, int fidx)
{
MemoryChunk *chunk;
chunk = (MemoryChunk *) (block->freeptr);
VALGRIND_MAKE_MEM_UNDEFINED(chunk, ALLOC_CHUNKHDRSZ);
block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
Assert(block->freeptr <= block->endptr);
MemoryChunkSetHdrMask(chunk, block, fidx, MCTX_ASET_ID);
#ifdef MEMORY_CONTEXT_CHECKING
chunk->requested_size = size;
if (size < chunk_size)
set_sentinel(MemoryChunkGetPointer(chunk), size);
#endif
#ifdef RANDOMIZE_ALLOCATED_MEMORY
randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
#endif
VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
chunk_size - size);
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
return MemoryChunkGetPointer(chunk);
}
pg_noinline
static void *
AllocSetAllocFromNewBlock(MemoryContext context, Size size, int flags,
int fidx)
{
AllocSet set = (AllocSet) context;
AllocBlock block;
Size availspace;
Size blksize;
Size required_size;
Size chunk_size;
Assert(set->blocks != NULL);
block = set->blocks;
availspace = block->endptr - block->freeptr;
while (availspace >= ((1 << ALLOC_MINBITS) + ALLOC_CHUNKHDRSZ))
{
AllocFreeListLink *link;
MemoryChunk *chunk;
Size availchunk = availspace - ALLOC_CHUNKHDRSZ;
int a_fidx = AllocSetFreeIndex(availchunk);
if (availchunk != GetChunkSizeFromFreeListIdx(a_fidx))
{
a_fidx--;
Assert(a_fidx >= 0);
availchunk = GetChunkSizeFromFreeListIdx(a_fidx);
}
chunk = (MemoryChunk *) (block->freeptr);
VALGRIND_MAKE_MEM_UNDEFINED(chunk, ALLOC_CHUNKHDRSZ);
block->freeptr += (availchunk + ALLOC_CHUNKHDRSZ);
availspace -= (availchunk + ALLOC_CHUNKHDRSZ);
MemoryChunkSetHdrMask(chunk, block, a_fidx, MCTX_ASET_ID);
#ifdef MEMORY_CONTEXT_CHECKING
chunk->requested_size = InvalidAllocSize;
#endif
link = GetFreeListLink(chunk);
VALGRIND_MAKE_MEM_DEFINED(link, sizeof(AllocFreeListLink));
link->next = set->freelist[a_fidx];
VALGRIND_MAKE_MEM_NOACCESS(link, sizeof(AllocFreeListLink));
set->freelist[a_fidx] = chunk;
}
blksize = set->nextBlockSize;
set->nextBlockSize <<= 1;
if (set->nextBlockSize > set->maxBlockSize)
set->nextBlockSize = set->maxBlockSize;
chunk_size = GetChunkSizeFromFreeListIdx(fidx);
Assert(chunk_size >= size);
required_size = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
while (blksize < required_size)
blksize <<= 1;
block = (AllocBlock) malloc(blksize);
while (block == NULL && blksize > 1024 * 1024)
{
blksize >>= 1;
if (blksize < required_size)
break;
block = (AllocBlock) malloc(blksize);
}
if (block == NULL)
return MemoryContextAllocationFailure(context, size, flags);
context->mem_allocated += blksize;
block->aset = set;
block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
block->endptr = ((char *) block) + blksize;
VALGRIND_MAKE_MEM_NOACCESS(block->freeptr,
blksize - ALLOC_BLOCKHDRSZ);
block->prev = NULL;
block->next = set->blocks;
if (block->next)
block->next->prev = block;
set->blocks = block;
return AllocSetAllocChunkFromBlock(context, block, size, chunk_size, fidx);
}
void *
AllocSetAlloc(MemoryContext context, Size size, int flags)
{
AllocSet set = (AllocSet) context;
AllocBlock block;
MemoryChunk *chunk;
int fidx;
Size chunk_size;
Size availspace;
Assert(AllocSetIsValid(set));
Assert(set->blocks != NULL);
if (size > set->allocChunkLimit)
return AllocSetAllocLarge(context, size, flags);
fidx = AllocSetFreeIndex(size);
chunk = set->freelist[fidx];
if (chunk != NULL)
{
AllocFreeListLink *link = GetFreeListLink(chunk);
VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOC_CHUNKHDRSZ);
Assert(fidx == MemoryChunkGetValue(chunk));
VALGRIND_MAKE_MEM_DEFINED(link, sizeof(AllocFreeListLink));
set->freelist[fidx] = link->next;
VALGRIND_MAKE_MEM_NOACCESS(link, sizeof(AllocFreeListLink));
#ifdef MEMORY_CONTEXT_CHECKING
chunk->requested_size = size;
if (size < GetChunkSizeFromFreeListIdx(fidx))
set_sentinel(MemoryChunkGetPointer(chunk), size);
#endif
#ifdef RANDOMIZE_ALLOCATED_MEMORY
randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
#endif
VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
GetChunkSizeFromFreeListIdx(fidx) - size);
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
return MemoryChunkGetPointer(chunk);
}
chunk_size = GetChunkSizeFromFreeListIdx(fidx);
Assert(chunk_size >= size);
block = set->blocks;
availspace = block->endptr - block->freeptr;
if (unlikely(availspace < (chunk_size + ALLOC_CHUNKHDRSZ)))
return AllocSetAllocFromNewBlock(context, size, flags, fidx);
return AllocSetAllocChunkFromBlock(context, block, size, chunk_size, fidx);
}
void
AllocSetFree(void *pointer)
{
AllocSet set;
MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOC_CHUNKHDRSZ);
if (MemoryChunkIsExternal(chunk))
{
AllocBlock block = ExternalChunkGetBlock(chunk);
if (!AllocBlockIsValid(block) || block->freeptr != block->endptr)
elog(ERROR, "could not find block containing chunk %p", chunk);
set = block->aset;
#ifdef MEMORY_CONTEXT_CHECKING
{
Assert(chunk->requested_size < (block->endptr - (char *) pointer));
if (!sentinel_ok(pointer, chunk->requested_size))
elog(WARNING, "detected write past chunk end in %s %p",
set->header.name, chunk);
}
#endif
if (block->prev)
block->prev->next = block->next;
else
set->blocks = block->next;
if (block->next)
block->next->prev = block->prev;
set->header.mem_allocated -= block->endptr - ((char *) block);
#ifdef CLOBBER_FREED_MEMORY
wipe_mem(block, block->freeptr - ((char *) block));
#endif
free(block);
}
else
{
AllocBlock block = MemoryChunkGetBlock(chunk);
int fidx;
AllocFreeListLink *link;
Assert(AllocBlockIsValid(block));
set = block->aset;
fidx = MemoryChunkGetValue(chunk);
Assert(FreeListIdxIsValid(fidx));
link = GetFreeListLink(chunk);
#ifdef MEMORY_CONTEXT_CHECKING
if (chunk->requested_size < GetChunkSizeFromFreeListIdx(fidx))
if (!sentinel_ok(pointer, chunk->requested_size))
elog(WARNING, "detected write past chunk end in %s %p",
set->header.name, chunk);
#endif
#ifdef CLOBBER_FREED_MEMORY
wipe_mem(pointer, GetChunkSizeFromFreeListIdx(fidx));
#endif
VALGRIND_MAKE_MEM_DEFINED(link, sizeof(AllocFreeListLink));
link->next = set->freelist[fidx];
VALGRIND_MAKE_MEM_NOACCESS(link, sizeof(AllocFreeListLink));
set->freelist[fidx] = chunk;
#ifdef MEMORY_CONTEXT_CHECKING
chunk->requested_size = InvalidAllocSize;
#endif
}
}
void *
AllocSetRealloc(void *pointer, Size size, int flags)
{
AllocBlock block;
AllocSet set;
MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
Size oldchksize;
int fidx;
VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOC_CHUNKHDRSZ);
if (MemoryChunkIsExternal(chunk))
{
Size chksize;
Size blksize;
Size oldblksize;
block = ExternalChunkGetBlock(chunk);
if (!AllocBlockIsValid(block) || block->freeptr != block->endptr)
elog(ERROR, "could not find block containing chunk %p", chunk);
set = block->aset;
MemoryContextCheckSize((MemoryContext) set, size, flags);
oldchksize = block->endptr - (char *) pointer;
#ifdef MEMORY_CONTEXT_CHECKING
Assert(chunk->requested_size < oldchksize);
if (!sentinel_ok(pointer, chunk->requested_size))
elog(WARNING, "detected write past chunk end in %s %p",
set->header.name, chunk);
#endif
#ifdef MEMORY_CONTEXT_CHECKING
chksize = MAXALIGN(size + 1);
#else
chksize = MAXALIGN(size);
#endif
blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
oldblksize = block->endptr - ((char *) block);
block = (AllocBlock) realloc(block, blksize);
if (block == NULL)
{
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
return MemoryContextAllocationFailure(&set->header, size, flags);
}
set->header.mem_allocated -= oldblksize;
set->header.mem_allocated += blksize;
block->freeptr = block->endptr = ((char *) block) + blksize;
chunk = (MemoryChunk *) (((char *) block) + ALLOC_BLOCKHDRSZ);
pointer = MemoryChunkGetPointer(chunk);
if (block->prev)
block->prev->next = block;
else
set->blocks = block;
if (block->next)
block->next->prev = block;
#ifdef MEMORY_CONTEXT_CHECKING
#ifdef RANDOMIZE_ALLOCATED_MEMORY
if (size > chunk->requested_size)
randomize_mem((char *) pointer + chunk->requested_size,
size - chunk->requested_size);
#else
#ifdef USE_VALGRIND
if (Min(size, oldchksize) > chunk->requested_size)
VALGRIND_MAKE_MEM_UNDEFINED((char *) pointer + chunk->requested_size,
Min(size, oldchksize) - chunk->requested_size);
#endif
#endif
chunk->requested_size = size;
Assert(size < chksize);
set_sentinel(pointer, size);
#else
VALGRIND_MAKE_MEM_DEFINED(pointer, Min(size, oldchksize));
#endif
VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size, chksize - size);
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
return pointer;
}
block = MemoryChunkGetBlock(chunk);
Assert(AllocBlockIsValid(block));
set = block->aset;
fidx = MemoryChunkGetValue(chunk);
Assert(FreeListIdxIsValid(fidx));
oldchksize = GetChunkSizeFromFreeListIdx(fidx);
#ifdef MEMORY_CONTEXT_CHECKING
if (chunk->requested_size < oldchksize)
if (!sentinel_ok(pointer, chunk->requested_size))
elog(WARNING, "detected write past chunk end in %s %p",
set->header.name, chunk);
#endif
if (oldchksize >= size)
{
#ifdef MEMORY_CONTEXT_CHECKING
Size oldrequest = chunk->requested_size;
#ifdef RANDOMIZE_ALLOCATED_MEMORY
if (size > oldrequest)
randomize_mem((char *) pointer + oldrequest,
size - oldrequest);
#endif
chunk->requested_size = size;
if (size > oldrequest)
VALGRIND_MAKE_MEM_UNDEFINED((char *) pointer + oldrequest,
size - oldrequest);
else
VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size,
oldchksize - size);
if (size < oldchksize)
set_sentinel(pointer, size);
#else
VALGRIND_MAKE_MEM_NOACCESS(pointer, oldchksize);
VALGRIND_MAKE_MEM_DEFINED(pointer, size);
#endif
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
return pointer;
}
else
{
AllocPointer newPointer;
Size oldsize;
newPointer = AllocSetAlloc((MemoryContext) set, size, flags);
if (newPointer == NULL)
{
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
return MemoryContextAllocationFailure((MemoryContext) set, size, flags);
}
VALGRIND_MAKE_MEM_UNDEFINED(newPointer, size);
#ifdef MEMORY_CONTEXT_CHECKING
oldsize = chunk->requested_size;
#else
oldsize = oldchksize;
VALGRIND_MAKE_MEM_DEFINED(pointer, oldsize);
#endif
memcpy(newPointer, pointer, oldsize);
AllocSetFree(pointer);
return newPointer;
}
}
MemoryContext
AllocSetGetChunkContext(void *pointer)
{
MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
AllocBlock block;
AllocSet set;
VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOC_CHUNKHDRSZ);
if (MemoryChunkIsExternal(chunk))
block = ExternalChunkGetBlock(chunk);
else
block = (AllocBlock) MemoryChunkGetBlock(chunk);
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
Assert(AllocBlockIsValid(block));
set = block->aset;
return &set->header;
}
Size
AllocSetGetChunkSpace(void *pointer)
{
MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
int fidx;
VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOC_CHUNKHDRSZ);
if (MemoryChunkIsExternal(chunk))
{
AllocBlock block = ExternalChunkGetBlock(chunk);
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
Assert(AllocBlockIsValid(block));
return block->endptr - (char *) chunk;
}
fidx = MemoryChunkGetValue(chunk);
Assert(FreeListIdxIsValid(fidx));
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
return GetChunkSizeFromFreeListIdx(fidx) + ALLOC_CHUNKHDRSZ;
}
bool
AllocSetIsEmpty(MemoryContext context)
{
Assert(AllocSetIsValid(context));
if (context->isReset)
return true;
return false;
}
void
AllocSetStats(MemoryContext context,
MemoryStatsPrintFunc printfunc, void *passthru,
MemoryContextCounters *totals, bool print_to_stderr)
{
AllocSet set = (AllocSet) context;
Size nblocks = 0;
Size freechunks = 0;
Size totalspace;
Size freespace = 0;
AllocBlock block;
int fidx;
Assert(AllocSetIsValid(set));
totalspace = MAXALIGN(sizeof(AllocSetContext));
for (block = set->blocks; block != NULL; block = block->next)
{
nblocks++;
totalspace += block->endptr - ((char *) block);
freespace += block->endptr - block->freeptr;
}
for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++)
{
Size chksz = GetChunkSizeFromFreeListIdx(fidx);
MemoryChunk *chunk = set->freelist[fidx];
while (chunk != NULL)
{
AllocFreeListLink *link = GetFreeListLink(chunk);
VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOC_CHUNKHDRSZ);
Assert(MemoryChunkGetValue(chunk) == fidx);
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
freechunks++;
freespace += chksz + ALLOC_CHUNKHDRSZ;
VALGRIND_MAKE_MEM_DEFINED(link, sizeof(AllocFreeListLink));
chunk = link->next;
VALGRIND_MAKE_MEM_NOACCESS(link, sizeof(AllocFreeListLink));
}
}
if (printfunc)
{
char stats_string[200];
snprintf(stats_string, sizeof(stats_string),
"%zu total in %zu blocks; %zu free (%zu chunks); %zu used",
totalspace, nblocks, freespace, freechunks,
totalspace - freespace);
printfunc(context, passthru, stats_string, print_to_stderr);
}
if (totals)
{
totals->nblocks += nblocks;
totals->freechunks += freechunks;
totals->totalspace += totalspace;
totals->freespace += freespace;
}
}
#ifdef MEMORY_CONTEXT_CHECKING
void
AllocSetCheck(MemoryContext context)
{
AllocSet set = (AllocSet) context;
const char *name = set->header.name;
AllocBlock prevblock;
AllocBlock block;
Size total_allocated = 0;
for (prevblock = NULL, block = set->blocks;
block != NULL;
prevblock = block, block = block->next)
{
char *bpoz = ((char *) block) + ALLOC_BLOCKHDRSZ;
long blk_used = block->freeptr - bpoz;
long blk_data = 0;
long nchunks = 0;
bool has_external_chunk = false;
if (IsKeeperBlock(set, block))
total_allocated += block->endptr - ((char *) set);
else
total_allocated += block->endptr - ((char *) block);
if (!blk_used)
{
if (!IsKeeperBlock(set, block))
elog(WARNING, "problem in alloc set %s: empty block %p",
name, block);
}
if (block->aset != set ||
block->prev != prevblock ||
block->freeptr < bpoz ||
block->freeptr > block->endptr)
elog(WARNING, "problem in alloc set %s: corrupt header in block %p",
name, block);
while (bpoz < block->freeptr)
{
MemoryChunk *chunk = (MemoryChunk *) bpoz;
Size chsize,
dsize;
VALGRIND_MAKE_MEM_DEFINED(chunk, ALLOC_CHUNKHDRSZ);
if (MemoryChunkIsExternal(chunk))
{
chsize = block->endptr - (char *) MemoryChunkGetPointer(chunk);
has_external_chunk = true;
if (chsize + ALLOC_CHUNKHDRSZ != blk_used)
elog(WARNING, "problem in alloc set %s: bad single-chunk %p in block %p",
name, chunk, block);
}
else
{
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);
if (block != MemoryChunkGetBlock(chunk))
elog(WARNING, "problem in alloc set %s: bad block offset for chunk %p in block %p",
name, chunk, block);
}
dsize = chunk->requested_size;
if (dsize != InvalidAllocSize && dsize > chsize)
elog(WARNING, "problem in alloc set %s: req size > alloc size for chunk %p in block %p",
name, chunk, block);
if (chsize < (1 << ALLOC_MINBITS))
elog(WARNING, "problem in alloc set %s: bad size %zu for chunk %p in block %p",
name, chsize, chunk, block);
if (dsize != InvalidAllocSize && dsize < chsize &&
!sentinel_ok(chunk, ALLOC_CHUNKHDRSZ + dsize))
elog(WARNING, "problem in alloc set %s: detected write past chunk end in block %p, chunk %p",
name, block, chunk);
if (dsize != InvalidAllocSize)
VALGRIND_MAKE_MEM_NOACCESS(chunk, ALLOC_CHUNKHDRSZ);
blk_data += chsize;
nchunks++;
bpoz += ALLOC_CHUNKHDRSZ + chsize;
}
if ((blk_data + (nchunks * ALLOC_CHUNKHDRSZ)) != blk_used)
elog(WARNING, "problem in alloc set %s: found inconsistent memory block %p",
name, block);
if (has_external_chunk && nchunks > 1)
elog(WARNING, "problem in alloc set %s: external chunk on non-dedicated block %p",
name, block);
}
Assert(total_allocated == context->mem_allocated);
}
#endif
void
AllocSetDeleteFreeList(MemoryContext context)
{
AllocSet set = (AllocSet) context;
if (set->freeListIndex >= 0)
{
AllocSetFreeList *freelist = &context_freelists[set->freeListIndex];
while (freelist->first_free != NULL)
{
AllocSetContext *oldset = freelist->first_free;
freelist->first_free = (AllocSetContext *) oldset->header.nextchild;
freelist->num_free--;
free(oldset);
}
Assert(freelist->num_free == 0);
}
}