TransactionAbortContext =
AllocSetContextCreateExtended(TopMemoryContext,
"TransactionAbortContext",
- 0,
32 * 1024,
32 * 1024,
32 * 1024);
}
/* Now build the actual relcache partition descriptor */
- rel->rd_pdcxt = AllocSetContextCreateExtended(CacheMemoryContext,
- RelationGetRelationName(rel),
- MEMCONTEXT_COPY_NAME,
- ALLOCSET_DEFAULT_SIZES);
+ rel->rd_pdcxt = AllocSetContextCreate(CacheMemoryContext,
+ "partition descriptor",
+ ALLOCSET_DEFAULT_SIZES);
+ MemoryContextCopySetIdentifier(rel->rd_pdcxt, RelationGetRelationName(rel));
+
oldcxt = MemoryContextSwitchTo(rel->rd_pdcxt);
result = (PartitionDescData *) palloc0(sizeof(PartitionDescData));
SysScanDesc sscan;
HeapTuple tuple;
+ MemoryContextCopySetIdentifier(rscxt,
+ RelationGetRelationName(relation));
+
rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
rsdesc->rscxt = rscxt;
* must be a child of whatever context holds the FmgrInfo.
*/
fcontext = AllocSetContextCreate(finfo->fn_mcxt,
- "SQL function data",
+ "SQL function",
ALLOCSET_DEFAULT_SIZES);
oldcontext = MemoryContextSwitchTo(fcontext);
procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
/*
- * copy function name immediately for use by error reporting callback
+ * copy function name immediately for use by error reporting callback, and
+ * for use as memory context identifier
*/
fcache->fname = pstrdup(NameStr(procedureStruct->proname));
+ MemoryContextSetIdentifier(fcontext, fcache->fname);
/*
* get the result type from the procedure tuple, and check for polymorphic
buffer->change_context = SlabContextCreate(new_ctx,
"Change",
- 0,
SLAB_DEFAULT_BLOCK_SIZE,
sizeof(ReorderBufferChange));
buffer->txn_context = SlabContextCreate(new_ctx,
"TXN",
- 0,
SLAB_DEFAULT_BLOCK_SIZE,
sizeof(ReorderBufferTXN));
buffer->tup_context = GenerationContextCreate(new_ctx,
"Tuples",
- 0,
SLAB_LARGE_BLOCK_SIZE);
hash_ctl.keysize = sizeof(TransactionId);
MemoryContext cxt;
MemoryContext oldcxt;
- cxt = AllocSetContextCreate(CurrentMemoryContext, "stats ext",
+ cxt = AllocSetContextCreate(CurrentMemoryContext,
+ "BuildRelationExtStatistics",
ALLOCSET_DEFAULT_SIZES);
oldcxt = MemoryContextSwitchTo(cxt);
plansource->magic = CACHEDPLANSOURCE_MAGIC;
plansource->raw_parse_tree = copyObject(raw_parse_tree);
plansource->query_string = pstrdup(query_string);
+ MemoryContextSetIdentifier(source_context, plansource->query_string);
plansource->commandTag = commandTag;
plansource->param_types = NULL;
plansource->num_params = 0;
plan_context = AllocSetContextCreate(CurrentMemoryContext,
"CachedPlan",
ALLOCSET_START_SMALL_SIZES);
+ MemoryContextCopySetIdentifier(plan_context, plansource->query_string);
/*
* Copy plan into the new context.
newsource->magic = CACHEDPLANSOURCE_MAGIC;
newsource->raw_parse_tree = copyObject(plansource->raw_parse_tree);
newsource->query_string = pstrdup(plansource->query_string);
+ MemoryContextSetIdentifier(source_context, newsource->query_string);
newsource->commandTag = plansource->commandTag;
if (plansource->num_params > 0)
{
/*
* Make the private context. Assume it'll not contain much data.
*/
- rulescxt = AllocSetContextCreateExtended(CacheMemoryContext,
- RelationGetRelationName(relation),
- MEMCONTEXT_COPY_NAME,
- ALLOCSET_SMALL_SIZES);
+ rulescxt = AllocSetContextCreate(CacheMemoryContext,
+ "relation rules",
+ ALLOCSET_SMALL_SIZES);
relation->rd_rulescxt = rulescxt;
+ MemoryContextCopySetIdentifier(rulescxt,
+ RelationGetRelationName(relation));
/*
* allocate an array to hold the rewrite rules (the array is extended if
if (!HeapTupleIsValid(tuple))
return;
- partkeycxt = AllocSetContextCreateExtended(CurTransactionContext,
- RelationGetRelationName(relation),
- MEMCONTEXT_COPY_NAME,
- ALLOCSET_SMALL_SIZES);
+ partkeycxt = AllocSetContextCreate(CurTransactionContext,
+ "partition key",
+ ALLOCSET_SMALL_SIZES);
+ MemoryContextCopySetIdentifier(partkeycxt,
+ RelationGetRelationName(relation));
key = (PartitionKey) MemoryContextAllocZero(partkeycxt,
sizeof(PartitionKeyData));
* a context, and not just a couple of pallocs, is so that we won't leak
* any subsidiary info attached to fmgr lookup records.
*/
- indexcxt = AllocSetContextCreateExtended(CacheMemoryContext,
- RelationGetRelationName(relation),
- MEMCONTEXT_COPY_NAME,
- ALLOCSET_SMALL_SIZES);
+ indexcxt = AllocSetContextCreate(CacheMemoryContext,
+ "index info",
+ ALLOCSET_SMALL_SIZES);
relation->rd_indexcxt = indexcxt;
+ MemoryContextCopySetIdentifier(indexcxt,
+ RelationGetRelationName(relation));
/*
* Now we can fetch the index AM's API struct
* prepare index info context --- parameters should match
* RelationInitIndexAccessInfo
*/
- indexcxt =
- AllocSetContextCreateExtended(CacheMemoryContext,
- RelationGetRelationName(rel),
- MEMCONTEXT_COPY_NAME,
- ALLOCSET_SMALL_SIZES);
+ indexcxt = AllocSetContextCreate(CacheMemoryContext,
+ "index info",
+ ALLOCSET_SMALL_SIZES);
rel->rd_indexcxt = indexcxt;
+ MemoryContextCopySetIdentifier(indexcxt,
+ RelationGetRelationName(rel));
/*
* Now we can fetch the index AM's API struct. (We can't store
Assert(!found); /* it wasn't there a moment ago */
/* Create private memory context the first time through */
- saveCtx = AllocSetContextCreateExtended(CacheMemoryContext,
- NameStr(dict->dictname),
- MEMCONTEXT_COPY_NAME,
- ALLOCSET_SMALL_SIZES);
+ saveCtx = AllocSetContextCreate(CacheMemoryContext,
+ "TS dictionary",
+ ALLOCSET_SMALL_SIZES);
+ MemoryContextCopySetIdentifier(saveCtx, NameStr(dict->dictname));
}
else
{
/* Clear the existing entry's private context */
saveCtx = entry->dictCtx;
- MemoryContextResetAndDeleteChildren(saveCtx);
+ /* Don't let context's ident pointer dangle while we reset it */
+ MemoryContextSetIdentifier(saveCtx, NULL);
+ MemoryContextReset(saveCtx);
+ MemoryContextCopySetIdentifier(saveCtx, NameStr(dict->dictname));
}
MemSet(entry, 0, sizeof(TSDictionaryCacheEntry));
CurrentDynaHashCxt = info->hcxt;
else
CurrentDynaHashCxt = TopMemoryContext;
- CurrentDynaHashCxt =
- AllocSetContextCreateExtended(CurrentDynaHashCxt,
- tabname,
- MEMCONTEXT_COPY_NAME,
- ALLOCSET_DEFAULT_SIZES);
+ CurrentDynaHashCxt = AllocSetContextCreate(CurrentDynaHashCxt,
+ "dynahash",
+ ALLOCSET_DEFAULT_SIZES);
}
/* Initialize the hash header, plus a copy of the table name */
hashp->tabname = (char *) (hashp + 1);
strcpy(hashp->tabname, tabname);
+ /* If we have a private context, label it with hashtable's name */
+ if (!(flags & HASH_SHARED_MEM))
+ MemoryContextSetIdentifier(CurrentDynaHashCxt, hashp->tabname);
+
/*
* Select the appropriate hash function (see comments at head of file).
*/
Size initBlockSize; /* initial block size */
Size maxBlockSize; /* maximum block size */
Size nextBlockSize; /* next block size to allocate */
- Size headerSize; /* allocated size of context header */
Size allocChunkLimit; /* effective chunk size limit */
AllocBlock keeper; /* keep this block over resets */
/* freelist this context could be put in, or -1 if not a candidate: */
* only its initial malloc chunk and no others. To be a candidate for a
* freelist, a context must have the same minContextSize/initBlockSize as
* other contexts in the list; but its maxBlockSize is irrelevant since that
- * doesn't affect the size of the initial chunk. Also, candidate contexts
- * *must not* use MEMCONTEXT_COPY_NAME since that would make their header size
- * variable. (We currently insist that all flags be zero, since other flags
- * would likely make the contexts less interchangeable, too.)
+ * doesn't affect the size of the initial chunk.
*
* We currently provide one freelist for ALLOCSET_DEFAULT_SIZES contexts
* and one for ALLOCSET_SMALL_SIZES contexts; the latter works for
static void AllocSetDelete(MemoryContext context);
static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer);
static bool AllocSetIsEmpty(MemoryContext context);
-static void AllocSetStats(MemoryContext context, int level, bool print,
+static void AllocSetStats(MemoryContext context,
+ MemoryStatsPrintFunc printfunc, void *passthru,
MemoryContextCounters *totals);
#ifdef MEMORY_CONTEXT_CHECKING
* Create a new AllocSet context.
*
* parent: parent context, or NULL if top-level context
- * name: name of context (for debugging only, need not be unique)
- * flags: bitmask of MEMCONTEXT_XXX option flags
+ * name: name of context (must be statically allocated)
* minContextSize: minimum context size
* initBlockSize: initial allocation block size
* maxBlockSize: maximum allocation block size
*
- * Notes: if flags & MEMCONTEXT_COPY_NAME, the name string will be copied into
- * context-lifespan storage; otherwise, it had better be statically allocated.
* Most callers should abstract the context size parameters using a macro
* such as ALLOCSET_DEFAULT_SIZES. (This is now *required* when going
* through the AllocSetContextCreate macro.)
MemoryContext
AllocSetContextCreateExtended(MemoryContext parent,
const char *name,
- int flags,
Size minContextSize,
Size initBlockSize,
Size maxBlockSize)
{
int freeListIndex;
- Size headerSize;
Size firstBlockSize;
AllocSet set;
AllocBlock block;
* Check whether the parameters match either available freelist. We do
* not need to demand a match of maxBlockSize.
*/
- if (flags == 0 &&
- minContextSize == ALLOCSET_DEFAULT_MINSIZE &&
+ if (minContextSize == ALLOCSET_DEFAULT_MINSIZE &&
initBlockSize == ALLOCSET_DEFAULT_INITSIZE)
freeListIndex = 0;
- else if (flags == 0 &&
- minContextSize == ALLOCSET_SMALL_MINSIZE &&
+ else if (minContextSize == ALLOCSET_SMALL_MINSIZE &&
initBlockSize == ALLOCSET_SMALL_INITSIZE)
freeListIndex = 1;
else
/* Reinitialize its header, installing correct name and parent */
MemoryContextCreate((MemoryContext) set,
T_AllocSetContext,
- set->headerSize,
- sizeof(AllocSetContext),
&AllocSetMethods,
parent,
- name,
- flags);
+ name);
return (MemoryContext) set;
}
}
- /* Size of the memory context header, including name storage if needed */
- if (flags & MEMCONTEXT_COPY_NAME)
- headerSize = MAXALIGN(sizeof(AllocSetContext) + strlen(name) + 1);
- else
- headerSize = MAXALIGN(sizeof(AllocSetContext));
-
/* Determine size of initial block */
- firstBlockSize = headerSize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
+ firstBlockSize = MAXALIGN(sizeof(AllocSetContext)) +
+ ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
if (minContextSize != 0)
firstBlockSize = Max(firstBlockSize, minContextSize);
else
*/
/* Fill in the initial block's block header */
- block = (AllocBlock) (((char *) set) + headerSize);
+ block = (AllocBlock) (((char *) set) + MAXALIGN(sizeof(AllocSetContext)));
block->aset = set;
block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
block->endptr = ((char *) set) + firstBlockSize;
set->initBlockSize = initBlockSize;
set->maxBlockSize = maxBlockSize;
set->nextBlockSize = initBlockSize;
- set->headerSize = headerSize;
set->freeListIndex = freeListIndex;
/*
/* Finally, do the type-independent part of context creation */
MemoryContextCreate((MemoryContext) set,
T_AllocSetContext,
- headerSize,
- sizeof(AllocSetContext),
&AllocSetMethods,
parent,
- name,
- flags);
+ name);
return (MemoryContext) set;
}
* AllocSetStats
* Compute stats about memory consumption of an allocset.
*
- * level: recursion level (0 at top level); used for print indentation.
- * print: true to print stats to stderr.
- * totals: if not NULL, add stats about this allocset into *totals.
+ * printfunc: if not NULL, pass a human-readable stats string to this.
+ * passthru: pass this pointer through to printfunc.
+ * totals: if not NULL, add stats about this context into *totals.
*/
static void
-AllocSetStats(MemoryContext context, int level, bool print,
+AllocSetStats(MemoryContext context,
+ MemoryStatsPrintFunc printfunc, void *passthru,
MemoryContextCounters *totals)
{
AllocSet set = (AllocSet) context;
int fidx;
/* Include context header in totalspace */
- totalspace = set->headerSize;
+ totalspace = MAXALIGN(sizeof(AllocSetContext));
for (block = set->blocks; block != NULL; block = block->next)
{
}
}
- if (print)
+ if (printfunc)
{
- int i;
-
- for (i = 0; i < level; i++)
- fprintf(stderr, " ");
- fprintf(stderr,
- "%s: %zu total in %zd blocks; %zu free (%zd chunks); %zu used\n",
- set->header.name, totalspace, nblocks, freespace, freechunks,
- totalspace - freespace);
+ char stats_string[200];
+
+ snprintf(stats_string, sizeof(stats_string),
+ "%zu total in %zd blocks; %zu free (%zd chunks); %zu used",
+ totalspace, nblocks, freespace, freechunks,
+ totalspace - freespace);
+ printfunc(context, passthru, stats_string);
}
if (totals)
/* Generational context parameters */
Size blockSize; /* standard block size */
- Size headerSize; /* allocated size of context header */
GenerationBlock *block; /* current (most recently allocated) block */
dlist_head blocks; /* list of blocks */
static void GenerationDelete(MemoryContext context);
static Size GenerationGetChunkSpace(MemoryContext context, void *pointer);
static bool GenerationIsEmpty(MemoryContext context);
-static void GenerationStats(MemoryContext context, int level, bool print,
+static void GenerationStats(MemoryContext context,
+ MemoryStatsPrintFunc printfunc, void *passthru,
MemoryContextCounters *totals);
#ifdef MEMORY_CONTEXT_CHECKING
/*
* GenerationContextCreate
* Create a new Generation context.
+ *
+ * parent: parent context, or NULL if top-level context
+ * name: name of context (must be statically allocated)
+ * blockSize: generation block size
*/
MemoryContext
GenerationContextCreate(MemoryContext parent,
const char *name,
- int flags,
Size blockSize)
{
- Size headerSize;
GenerationContext *set;
/* Assert we padded GenerationChunk properly */
* freeing the first generation of allocations.
*/
- /* Size of the memory context header, including name storage if needed */
- if (flags & MEMCONTEXT_COPY_NAME)
- headerSize = MAXALIGN(sizeof(GenerationContext) + strlen(name) + 1);
- else
- headerSize = MAXALIGN(sizeof(GenerationContext));
-
- set = (GenerationContext *) malloc(headerSize);
+ set = (GenerationContext *) malloc(MAXALIGN(sizeof(GenerationContext)));
if (set == NULL)
{
MemoryContextStats(TopMemoryContext);
/* Fill in GenerationContext-specific header fields */
set->blockSize = blockSize;
- set->headerSize = headerSize;
set->block = NULL;
dlist_init(&set->blocks);
/* Finally, do the type-independent part of context creation */
MemoryContextCreate((MemoryContext) set,
T_GenerationContext,
- headerSize,
- sizeof(GenerationContext),
&GenerationMethods,
parent,
- name,
- flags);
+ name);
return (MemoryContext) set;
}
* GenerationStats
* Compute stats about memory consumption of a Generation context.
*
- * level: recursion level (0 at top level); used for print indentation.
- * print: true to print stats to stderr.
- * totals: if not NULL, add stats about this Generation into *totals.
+ * printfunc: if not NULL, pass a human-readable stats string to this.
+ * passthru: pass this pointer through to printfunc.
+ * totals: if not NULL, add stats about this context into *totals.
*
* XXX freespace only accounts for empty space at the end of the block, not
* space of freed chunks (which is unknown).
*/
static void
-GenerationStats(MemoryContext context, int level, bool print,
+GenerationStats(MemoryContext context,
+ MemoryStatsPrintFunc printfunc, void *passthru,
MemoryContextCounters *totals)
{
GenerationContext *set = (GenerationContext *) context;
dlist_iter iter;
/* Include context header in totalspace */
- totalspace = set->headerSize;
+ totalspace = MAXALIGN(sizeof(GenerationContext));
dlist_foreach(iter, &set->blocks)
{
freespace += (block->endptr - block->freeptr);
}
- if (print)
+ if (printfunc)
{
- int i;
-
- for (i = 0; i < level; i++)
- fprintf(stderr, " ");
- fprintf(stderr,
- "Generation: %s: %zu total in %zd blocks (%zd chunks); %zu free (%zd chunks); %zu used\n",
- ((MemoryContext) set)->name, totalspace, nblocks, nchunks, freespace,
- nfreechunks, totalspace - freespace);
+ char stats_string[200];
+
+ snprintf(stats_string, sizeof(stats_string),
+ "%zu total in %zd blocks (%zd chunks); %zu free (%zd chunks); %zu used",
+ totalspace, nblocks, nchunks, freespace,
+ nfreechunks, totalspace - freespace);
+ printfunc(context, passthru, stats_string);
}
if (totals)
#include "postgres.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "utils/memdebug.h"
#include "utils/memutils.h"
static void MemoryContextStatsInternal(MemoryContext context, int level,
bool print, int max_children,
MemoryContextCounters *totals);
+static void MemoryContextStatsPrint(MemoryContext context, void *passthru,
+ const char *stats_string);
/*
* You should not do memory allocations within a critical section, because
*/
ErrorContext = AllocSetContextCreateExtended(TopMemoryContext,
"ErrorContext",
- 0,
8 * 1024,
8 * 1024,
8 * 1024);
if (!context->isReset)
{
MemoryContextCallResetCallbacks(context);
+
+ /*
+ * If context->ident points into the context's memory, it will become
+ * a dangling pointer. We could prevent that by setting it to NULL
+ * here, but that would break valid coding patterns that keep the
+ * ident elsewhere, e.g. in a parent context. Another idea is to use
+ * MemoryContextContains(), but we don't require ident strings to be
+ * in separately-palloc'd chunks, so that risks false positives. So
+ * for now we assume the programmer got it right.
+ */
+
context->methods->reset(context);
context->isReset = true;
VALGRIND_DESTROY_MEMPOOL(context);
*/
MemoryContextSetParent(context, NULL);
+ /*
+ * Also reset the context's ident pointer, in case it points into the
+ * context. This would only matter if someone tries to get stats on the
+ * (already unlinked) context, which is unlikely, but let's be safe.
+ */
+ context->ident = NULL;
+
context->methods->delete_context(context);
VALGRIND_DESTROY_MEMPOOL(context);
}
}
+/*
+ * MemoryContextSetIdentifier
+ * Set the identifier string for a memory context.
+ *
+ * An identifier can be provided to help distinguish among different contexts
+ * of the same kind in memory context stats dumps. The identifier string
+ * must live at least as long as the context it is for; typically it is
+ * allocated inside that context, so that it automatically goes away on
+ * context deletion. Pass id = NULL to forget any old identifier.
+ */
+void
+MemoryContextSetIdentifier(MemoryContext context, const char *id)
+{
+ AssertArg(MemoryContextIsValid(context));
+ context->ident = id;
+}
+
/*
* MemoryContextSetParent
* Change a context to belong to a new parent (or no parent).
AssertArg(MemoryContextIsValid(context));
/* Examine the context itself */
- context->methods->stats(context, level, print, totals);
+ context->methods->stats(context,
+ print ? MemoryContextStatsPrint : NULL,
+ (void *) &level,
+ totals);
/*
* Examine children. If there are more than max_children of them, we do
}
}
+/*
+ * MemoryContextStatsPrint
+ * Print callback used by MemoryContextStatsInternal
+ *
+ * For now, the passthru pointer just points to "int level"; later we might
+ * make that more complicated.
+ */
+static void
+MemoryContextStatsPrint(MemoryContext context, void *passthru,
+ const char *stats_string)
+{
+ int level = *(int *) passthru;
+ const char *name = context->name;
+ const char *ident = context->ident;
+ int i;
+
+ /*
+ * It seems preferable to label dynahash contexts with just the hash table
+ * name. Those are already unique enough, so the "dynahash" part isn't
+ * very helpful, and this way is more consistent with pre-v11 practice.
+ */
+ if (ident && strcmp(name, "dynahash") == 0)
+ {
+ name = ident;
+ ident = NULL;
+ }
+
+ for (i = 0; i < level; i++)
+ fprintf(stderr, " ");
+ fprintf(stderr, "%s: %s", name, stats_string);
+ if (ident)
+ {
+ /*
+ * Some contexts may have very long identifiers (e.g., SQL queries).
+ * Arbitrarily truncate at 100 bytes, but be careful not to break
+ * multibyte characters. Also, replace ASCII control characters, such
+ * as newlines, with spaces.
+ */
+ int idlen = strlen(ident);
+ bool truncated = false;
+
+ if (idlen > 100)
+ {
+ idlen = pg_mbcliplen(ident, idlen, 100);
+ truncated = true;
+ }
+ fprintf(stderr, ": ");
+ while (idlen-- > 0)
+ {
+ unsigned char c = *ident++;
+
+ if (c < ' ')
+ c = ' ';
+ fputc(c, stderr);
+ }
+ if (truncated)
+ fprintf(stderr, "...");
+ }
+ fputc('\n', stderr);
+}
+
/*
* MemoryContextCheck
* Check all chunks in the named context.
*
* node: the as-yet-uninitialized common part of the context header node.
* tag: NodeTag code identifying the memory context type.
- * size: total size of context header including context-type-specific fields,
- * as well as space for the context name if MEMCONTEXT_COPY_NAME is set.
- * nameoffset: where within the "size" space to insert the context name.
* methods: context-type-specific methods (usually statically allocated).
* parent: parent context, or NULL if this will be a top-level context.
- * name: name of context (for debugging only, need not be unique).
- * flags: bitmask of MEMCONTEXT_XXX option flags.
+ * name: name of context (must be statically allocated).
*
* Context routines generally assume that MemoryContextCreate can't fail,
* so this can contain Assert but not elog/ereport.
*/
void
MemoryContextCreate(MemoryContext node,
- NodeTag tag, Size size, Size nameoffset,
+ NodeTag tag,
const MemoryContextMethods *methods,
MemoryContext parent,
- const char *name,
- int flags)
+ const char *name)
{
/* Creating new memory contexts is not allowed in a critical section */
Assert(CritSectionCount == 0);
- /* Check size is sane */
- Assert(nameoffset >= sizeof(MemoryContextData));
- Assert((flags & MEMCONTEXT_COPY_NAME) ?
- size >= nameoffset + strlen(name) + 1 :
- size >= nameoffset);
-
/* Initialize all standard fields of memory context header */
node->type = tag;
node->isReset = true;
node->parent = parent;
node->firstchild = NULL;
node->prevchild = NULL;
+ node->name = name;
+ node->ident = NULL;
node->reset_cbs = NULL;
- if (flags & MEMCONTEXT_COPY_NAME)
- {
- /* Insert context name into space reserved for it */
- char *namecopy = ((char *) node) + nameoffset;
-
- node->name = namecopy;
- strcpy(namecopy, name);
- }
- else
- {
- /* Assume the passed-in name is statically allocated */
- node->name = name;
- }
-
/* OK to link node into context tree */
if (parent)
{
static void SlabDelete(MemoryContext context);
static Size SlabGetChunkSpace(MemoryContext context, void *pointer);
static bool SlabIsEmpty(MemoryContext context);
-static void SlabStats(MemoryContext context, int level, bool print,
+static void SlabStats(MemoryContext context,
+ MemoryStatsPrintFunc printfunc, void *passthru,
MemoryContextCounters *totals);
#ifdef MEMORY_CONTEXT_CHECKING
static void SlabCheck(MemoryContext context);
* Create a new Slab context.
*
* parent: parent context, or NULL if top-level context
- * name: name of context (for debugging only, need not be unique)
- * flags: bitmask of MEMCONTEXT_XXX option flags
+ * name: name of context (must be statically allocated)
* blockSize: allocation block size
* chunkSize: allocation chunk size
*
- * Notes: if flags & MEMCONTEXT_COPY_NAME, the name string will be copied into
- * context-lifespan storage; otherwise, it had better be statically allocated.
* The chunkSize may not exceed:
* MAXALIGN_DOWN(SIZE_MAX) - MAXALIGN(sizeof(SlabBlock)) - SLAB_CHUNKHDRSZ
*/
MemoryContext
SlabContextCreate(MemoryContext parent,
const char *name,
- int flags,
Size blockSize,
Size chunkSize)
{
int chunksPerBlock;
Size fullChunkSize;
Size freelistSize;
- Size nameOffset;
Size headerSize;
SlabContext *slab;
int i;
* this with the first regular block; not worth the extra complication.
*/
- /* Size of the memory context header, including name storage if needed */
- nameOffset = offsetof(SlabContext, freelist) + freelistSize;
- if (flags & MEMCONTEXT_COPY_NAME)
- headerSize = nameOffset + strlen(name) + 1;
- else
- headerSize = nameOffset;
+ /* Size of the memory context header */
+ headerSize = offsetof(SlabContext, freelist) + freelistSize;
slab = (SlabContext *) malloc(headerSize);
if (slab == NULL)
/* Finally, do the type-independent part of context creation */
MemoryContextCreate((MemoryContext) slab,
T_SlabContext,
- headerSize,
- nameOffset,
&SlabMethods,
parent,
- name,
- flags);
+ name);
return (MemoryContext) slab;
}
* SlabStats
* Compute stats about memory consumption of a Slab context.
*
- * level: recursion level (0 at top level); used for print indentation.
- * print: true to print stats to stderr.
- * totals: if not NULL, add stats about this Slab into *totals.
+ * printfunc: if not NULL, pass a human-readable stats string to this.
+ * passthru: pass this pointer through to printfunc.
+ * totals: if not NULL, add stats about this context into *totals.
*/
static void
-SlabStats(MemoryContext context, int level, bool print,
+SlabStats(MemoryContext context,
+ MemoryStatsPrintFunc printfunc, void *passthru,
MemoryContextCounters *totals)
{
SlabContext *slab = castNode(SlabContext, context);
}
}
- if (print)
+ if (printfunc)
{
- for (i = 0; i < level; i++)
- fprintf(stderr, " ");
- fprintf(stderr,
- "Slab: %s: %zu total in %zd blocks; %zu free (%zd chunks); %zu used\n",
- slab->header.name, totalspace, nblocks, freespace, freechunks,
- totalspace - freespace);
+ char stats_string[200];
+
+ snprintf(stats_string, sizeof(stats_string),
+ "%zu total in %zd blocks; %zu free (%zd chunks); %zu used",
+ totalspace, nblocks, freespace, freechunks,
+ totalspace - freespace);
+ printfunc(context, passthru, stats_string);
}
if (totals)
* A logical context in which memory allocations occur.
*
* MemoryContext itself is an abstract type that can have multiple
- * implementations, though for now we have only AllocSetContext.
+ * implementations.
* The function pointers in MemoryContextMethods define one specific
* implementation of MemoryContext --- they are a virtual function table
* in C++ terms.
*
* Node types that are actual implementations of memory contexts must
- * begin with the same fields as MemoryContext.
+ * begin with the same fields as MemoryContextData.
*
* Note: for largely historical reasons, typedef MemoryContext is a pointer
* to the context struct rather than the struct type itself.
*/
+typedef void (*MemoryStatsPrintFunc) (MemoryContext context, void *passthru,
+ const char *stats_string);
+
typedef struct MemoryContextMethods
{
void *(*alloc) (MemoryContext context, Size size);
void (*delete_context) (MemoryContext context);
Size (*get_chunk_space) (MemoryContext context, void *pointer);
bool (*is_empty) (MemoryContext context);
- void (*stats) (MemoryContext context, int level, bool print,
+ void (*stats) (MemoryContext context,
+ MemoryStatsPrintFunc printfunc, void *passthru,
MemoryContextCounters *totals);
#ifdef MEMORY_CONTEXT_CHECKING
void (*check) (MemoryContext context);
MemoryContext prevchild; /* previous child of same parent */
MemoryContext nextchild; /* next child of same parent */
const char *name; /* context name (just for debugging) */
+ const char *ident; /* context ID if any (just for debugging) */
MemoryContextCallback *reset_cbs; /* list of reset/delete callbacks */
} MemoryContextData;
extern void MemoryContextResetOnly(MemoryContext context);
extern void MemoryContextResetChildren(MemoryContext context);
extern void MemoryContextDeleteChildren(MemoryContext context);
+extern void MemoryContextSetIdentifier(MemoryContext context, const char *id);
extern void MemoryContextSetParent(MemoryContext context,
MemoryContext new_parent);
extern Size GetMemoryChunkSpace(void *pointer);
#endif
extern bool MemoryContextContains(MemoryContext context, void *pointer);
+/* Handy macro for copying and assigning context ID ... but note double eval */
+#define MemoryContextCopySetIdentifier(cxt, id) \
+ MemoryContextSetIdentifier(cxt, MemoryContextStrdup(cxt, id))
+
/*
* GetMemoryChunkContext
* Given a currently-allocated chunk, determine the context
* specific creation routines, and noplace else.
*/
extern void MemoryContextCreate(MemoryContext node,
- NodeTag tag, Size size, Size nameoffset,
+ NodeTag tag,
const MemoryContextMethods *methods,
MemoryContext parent,
- const char *name,
- int flags);
+ const char *name);
/*
/* aset.c */
extern MemoryContext AllocSetContextCreateExtended(MemoryContext parent,
const char *name,
- int flags,
Size minContextSize,
Size initBlockSize,
Size maxBlockSize);
/*
- * This backwards compatibility macro only works for constant context names,
- * and you must specify block sizes with one of the abstraction macros below.
+ * This wrapper macro exists to check for non-constant strings used as context
+ * names; that's no longer supported. (Use MemoryContextSetIdentifier if you
+ * want to provide a variable identifier.) Note you must specify block sizes
+ * with one of the abstraction macros below.
*/
#ifdef HAVE__BUILTIN_CONSTANT_P
#define AllocSetContextCreate(parent, name, allocparams) \
(StaticAssertExpr(__builtin_constant_p(name), \
- "Use AllocSetContextCreateExtended with MEMCONTEXT_COPY_NAME for non-constant context names"), \
- AllocSetContextCreateExtended(parent, name, 0, allocparams))
+ "memory context names must be constant strings"), \
+ AllocSetContextCreateExtended(parent, name, allocparams))
#else
#define AllocSetContextCreate(parent, name, allocparams) \
- AllocSetContextCreateExtended(parent, name, 0, allocparams)
+ AllocSetContextCreateExtended(parent, name, allocparams)
#endif
/* slab.c */
extern MemoryContext SlabContextCreate(MemoryContext parent,
const char *name,
- int flags,
Size blockSize,
Size chunkSize);
/* generation.c */
extern MemoryContext GenerationContextCreate(MemoryContext parent,
const char *name,
- int flags,
Size blockSize);
-/*
- * Flag option bits for FooContextCreate functions.
- * In future, some of these might be relevant to only some context types.
- *
- * COPY_NAME: FooContextCreate's name argument is not a constant string
- */
-#define MEMCONTEXT_COPY_NAME 0x0001 /* is passed name transient? */
-
/*
* Recommended default alloc parameters, suitable for "ordinary" contexts
* that might hold quite a lot of data.
/************************************************************
* Allocate a context that will hold all PG data for the procedure.
************************************************************/
- proc_cxt = AllocSetContextCreateExtended(TopMemoryContext,
- NameStr(procStruct->proname),
- MEMCONTEXT_COPY_NAME,
- ALLOCSET_SMALL_SIZES);
+ proc_cxt = AllocSetContextCreate(TopMemoryContext,
+ "PL/Perl function",
+ ALLOCSET_SMALL_SIZES);
/************************************************************
* Allocate and fill a new procedure description block.
oldcontext = MemoryContextSwitchTo(proc_cxt);
prodesc = (plperl_proc_desc *) palloc0(sizeof(plperl_proc_desc));
prodesc->proname = pstrdup(NameStr(procStruct->proname));
+ MemoryContextSetIdentifier(proc_cxt, prodesc->proname);
prodesc->fn_cxt = proc_cxt;
prodesc->fn_refcount = 0;
prodesc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
* per-function memory context, so it can be reclaimed easily.
*/
func_cxt = AllocSetContextCreate(TopMemoryContext,
- "PL/pgSQL function context",
+ "PL/pgSQL function",
ALLOCSET_DEFAULT_SIZES);
plpgsql_compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
function->fn_signature = format_procedure(fcinfo->flinfo->fn_oid);
+ MemoryContextSetIdentifier(func_cxt, function->fn_signature);
function->fn_oid = fcinfo->flinfo->fn_oid;
function->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
function->fn_tid = procTup->t_self;
memset(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(PLpgSQL_func_hashkey);
ctl.entrysize = sizeof(plpgsql_HashEnt);
- plpgsql_HashTable = hash_create("PLpgSQL function cache",
+ plpgsql_HashTable = hash_create("PLpgSQL function hash",
FUNCS_PER_USER,
&ctl,
HASH_ELEM | HASH_BLOBS);
}
/* Create long-lived context that all procedure info will live in */
- cxt = AllocSetContextCreateExtended(TopMemoryContext,
- procName,
- MEMCONTEXT_COPY_NAME,
- ALLOCSET_DEFAULT_SIZES);
+ cxt = AllocSetContextCreate(TopMemoryContext,
+ "PL/Python function",
+ ALLOCSET_DEFAULT_SIZES);
oldcxt = MemoryContextSwitchTo(cxt);
int i;
proc->proname = pstrdup(NameStr(procStruct->proname));
+ MemoryContextSetIdentifier(cxt, proc->proname);
proc->pyname = pstrdup(procName);
proc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
proc->fn_tid = procTup->t_self;
/************************************************************
* Allocate a context that will hold all PG data for the procedure.
- * We use the internal proc name as the context name.
************************************************************/
- proc_cxt = AllocSetContextCreateExtended(TopMemoryContext,
- internal_proname,
- MEMCONTEXT_COPY_NAME,
- ALLOCSET_SMALL_SIZES);
+ proc_cxt = AllocSetContextCreate(TopMemoryContext,
+ "PL/Tcl function",
+ ALLOCSET_SMALL_SIZES);
/************************************************************
* Allocate and fill a new procedure description block.
oldcontext = MemoryContextSwitchTo(proc_cxt);
prodesc = (pltcl_proc_desc *) palloc0(sizeof(pltcl_proc_desc));
prodesc->user_proname = pstrdup(NameStr(procStruct->proname));
+ MemoryContextSetIdentifier(proc_cxt, prodesc->user_proname);
prodesc->internal_proname = pstrdup(internal_proname);
prodesc->fn_cxt = proc_cxt;
prodesc->fn_refcount = 0;