diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/access/transam/xact.c | 1 | ||||
-rw-r--r-- | src/backend/catalog/partition.c | 9 | ||||
-rw-r--r-- | src/backend/commands/policy.c | 3 | ||||
-rw-r--r-- | src/backend/executor/functions.c | 6 | ||||
-rw-r--r-- | src/backend/replication/logical/reorderbuffer.c | 3 | ||||
-rw-r--r-- | src/backend/statistics/extended_stats.c | 3 | ||||
-rw-r--r-- | src/backend/utils/cache/plancache.c | 3 | ||||
-rw-r--r-- | src/backend/utils/cache/relcache.c | 37 | ||||
-rw-r--r-- | src/backend/utils/cache/ts_cache.c | 13 | ||||
-rw-r--r-- | src/backend/utils/hash/dynahash.c | 12 | ||||
-rw-r--r-- | src/backend/utils/mmgr/aset.c | 72 | ||||
-rw-r--r-- | src/backend/utils/mmgr/generation.c | 52 | ||||
-rw-r--r-- | src/backend/utils/mmgr/mcxt.c | 138 | ||||
-rw-r--r-- | src/backend/utils/mmgr/slab.c | 47 | ||||
-rw-r--r-- | src/include/nodes/memnodes.h | 11 | ||||
-rw-r--r-- | src/include/utils/memutils.h | 33 | ||||
-rw-r--r-- | src/pl/plperl/plperl.c | 8 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_comp.c | 5 | ||||
-rw-r--r-- | src/pl/plpython/plpy_procedure.c | 8 | ||||
-rw-r--r-- | src/pl/tcl/pltcl.c | 9 |
20 files changed, 263 insertions, 210 deletions
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 5d1b9027cf5..cfc62011b50 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -999,7 +999,6 @@ AtStart_Memory(void) TransactionAbortContext = AllocSetContextCreateExtended(TopMemoryContext, "TransactionAbortContext", - 0, 32 * 1024, 32 * 1024, 32 * 1024); diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index b00a986432e..39ee773d934 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -525,10 +525,11 @@ RelationBuildPartitionDesc(Relation rel) } /* 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)); diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c index 280a14a101b..cfaf32ccbd7 100644 --- a/src/backend/commands/policy.c +++ b/src/backend/commands/policy.c @@ -214,6 +214,9 @@ RelationBuildRowSecurity(Relation relation) SysScanDesc sscan; HeapTuple tuple; + MemoryContextCopySetIdentifier(rscxt, + RelationGetRelationName(relation)); + rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc)); rsdesc->rscxt = rscxt; diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 1c00ac9588f..23545896d4d 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -612,7 +612,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK) * 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); @@ -635,9 +635,11 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK) 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 diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index 5ffe638b19c..b4016ed52b0 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -243,19 +243,16 @@ ReorderBufferAllocate(void) 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); diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index d34d5c3d12e..6c836f68a98 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -74,7 +74,8 @@ BuildRelationExtStatistics(Relation onerel, double totalrows, MemoryContext cxt; MemoryContext oldcxt; - cxt = AllocSetContextCreate(CurrentMemoryContext, "stats ext", + cxt = AllocSetContextCreate(CurrentMemoryContext, + "BuildRelationExtStatistics", ALLOCSET_DEFAULT_SIZES); oldcxt = MemoryContextSwitchTo(cxt); diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index 8d7d8e04c9f..85bb7b914a0 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -180,6 +180,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree, 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; @@ -951,6 +952,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist, plan_context = AllocSetContextCreate(CurrentMemoryContext, "CachedPlan", ALLOCSET_START_SMALL_SIZES); + MemoryContextCopySetIdentifier(plan_context, plansource->query_string); /* * Copy plan into the new context. @@ -1346,6 +1348,7 @@ CopyCachedPlan(CachedPlanSource *plansource) 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) { diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 46513024405..de502f9bc9e 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -675,11 +675,12 @@ RelationBuildRuleLock(Relation relation) /* * 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 @@ -852,10 +853,11 @@ RelationBuildPartitionKey(Relation relation) 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)); @@ -1533,11 +1535,12 @@ RelationInitIndexAccessInfo(Relation relation) * 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 @@ -5603,12 +5606,12 @@ load_relcache_init_file(bool shared) * 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 diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c index 3d5c1941488..97347780d3b 100644 --- a/src/backend/utils/cache/ts_cache.c +++ b/src/backend/utils/cache/ts_cache.c @@ -294,16 +294,19 @@ lookup_ts_dictionary_cache(Oid dictId) 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)); diff --git a/src/backend/utils/hash/dynahash.c b/src/backend/utils/hash/dynahash.c index 5281cd54103..785e0faffb5 100644 --- a/src/backend/utils/hash/dynahash.c +++ b/src/backend/utils/hash/dynahash.c @@ -340,11 +340,9 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags) 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 */ @@ -354,6 +352,10 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags) 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). */ diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index 3f9b18844fa..e3d2c4e2faa 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -130,7 +130,6 @@ typedef struct AllocSetContext 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: */ @@ -228,10 +227,7 @@ typedef struct AllocChunkData * 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 @@ -276,7 +272,8 @@ static void AllocSetReset(MemoryContext context); 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 @@ -378,14 +375,11 @@ AllocSetFreeIndex(Size size) * 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.) @@ -393,13 +387,11 @@ AllocSetFreeIndex(Size size) 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; @@ -431,12 +423,10 @@ AllocSetContextCreateExtended(MemoryContext parent, * 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 @@ -462,25 +452,17 @@ AllocSetContextCreateExtended(MemoryContext parent, /* 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 @@ -508,7 +490,7 @@ AllocSetContextCreateExtended(MemoryContext parent, */ /* 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; @@ -529,7 +511,6 @@ AllocSetContextCreateExtended(MemoryContext parent, set->initBlockSize = initBlockSize; set->maxBlockSize = maxBlockSize; set->nextBlockSize = initBlockSize; - set->headerSize = headerSize; set->freeListIndex = freeListIndex; /* @@ -559,12 +540,9 @@ AllocSetContextCreateExtended(MemoryContext parent, /* 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; } @@ -1327,12 +1305,13 @@ AllocSetIsEmpty(MemoryContext context) * 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; @@ -1344,7 +1323,7 @@ AllocSetStats(MemoryContext context, int level, bool print, int fidx; /* Include context header in totalspace */ - totalspace = set->headerSize; + totalspace = MAXALIGN(sizeof(AllocSetContext)); for (block = set->blocks; block != NULL; block = block->next) { @@ -1364,16 +1343,15 @@ AllocSetStats(MemoryContext context, int level, bool print, } } - 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) diff --git a/src/backend/utils/mmgr/generation.c b/src/backend/utils/mmgr/generation.c index 338386a5d19..7ee3c481b34 100644 --- a/src/backend/utils/mmgr/generation.c +++ b/src/backend/utils/mmgr/generation.c @@ -61,7 +61,6 @@ typedef struct GenerationContext /* 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 */ @@ -154,7 +153,8 @@ static void GenerationReset(MemoryContext context); 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 @@ -203,14 +203,16 @@ static const MemoryContextMethods GenerationMethods = { /* * 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 */ @@ -238,13 +240,7 @@ GenerationContextCreate(MemoryContext parent, * 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); @@ -262,19 +258,15 @@ GenerationContextCreate(MemoryContext parent, /* 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; } @@ -683,15 +675,16 @@ GenerationIsEmpty(MemoryContext context) * 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; @@ -703,7 +696,7 @@ GenerationStats(MemoryContext context, int level, bool print, dlist_iter iter; /* Include context header in totalspace */ - totalspace = set->headerSize; + totalspace = MAXALIGN(sizeof(GenerationContext)); dlist_foreach(iter, &set->blocks) { @@ -716,16 +709,15 @@ GenerationStats(MemoryContext context, int level, bool print, 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) diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c index d7baa54808f..6cda41481fb 100644 --- a/src/backend/utils/mmgr/mcxt.c +++ b/src/backend/utils/mmgr/mcxt.c @@ -21,6 +21,7 @@ #include "postgres.h" +#include "mb/pg_wchar.h" #include "miscadmin.h" #include "utils/memdebug.h" #include "utils/memutils.h" @@ -55,6 +56,8 @@ static void MemoryContextCallResetCallbacks(MemoryContext context); 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 @@ -118,7 +121,6 @@ MemoryContextInit(void) */ ErrorContext = AllocSetContextCreateExtended(TopMemoryContext, "ErrorContext", - 0, 8 * 1024, 8 * 1024, 8 * 1024); @@ -158,6 +160,17 @@ MemoryContextResetOnly(MemoryContext context) 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); @@ -222,6 +235,13 @@ MemoryContextDelete(MemoryContext 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); @@ -296,6 +316,23 @@ MemoryContextCallResetCallbacks(MemoryContext 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). * @@ -480,7 +517,10 @@ MemoryContextStatsInternal(MemoryContext context, int level, 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 @@ -532,6 +572,67 @@ MemoryContextStatsInternal(MemoryContext context, int level, } /* + * 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. * @@ -612,34 +713,23 @@ MemoryContextContains(MemoryContext context, void *pointer) * * 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; @@ -647,22 +737,10 @@ MemoryContextCreate(MemoryContext node, 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) { diff --git a/src/backend/utils/mmgr/slab.c b/src/backend/utils/mmgr/slab.c index 58beb64ab9c..26b0a156b6b 100644 --- a/src/backend/utils/mmgr/slab.c +++ b/src/backend/utils/mmgr/slab.c @@ -131,7 +131,8 @@ static void SlabReset(MemoryContext context); 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); @@ -176,27 +177,22 @@ static const MemoryContextMethods SlabMethods = { * 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; @@ -231,12 +227,8 @@ SlabContextCreate(MemoryContext parent, * 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) @@ -270,12 +262,9 @@ SlabContextCreate(MemoryContext parent, /* Finally, do the type-independent part of context creation */ MemoryContextCreate((MemoryContext) slab, T_SlabContext, - headerSize, - nameOffset, &SlabMethods, parent, - name, - flags); + name); return (MemoryContext) slab; } @@ -634,12 +623,13 @@ SlabIsEmpty(MemoryContext context) * 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); @@ -667,14 +657,15 @@ SlabStats(MemoryContext context, int level, bool print, } } - 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) diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h index ccb64f01a72..2a8d83f3d08 100644 --- a/src/include/nodes/memnodes.h +++ b/src/include/nodes/memnodes.h @@ -39,18 +39,21 @@ typedef struct MemoryContextCounters * 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); @@ -61,7 +64,8 @@ typedef struct MemoryContextMethods 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); @@ -81,6 +85,7 @@ typedef struct MemoryContextData 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; diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h index 6e6c6f2e4cd..4f2ca26caf0 100644 --- a/src/include/utils/memutils.h +++ b/src/include/utils/memutils.h @@ -76,6 +76,7 @@ extern void MemoryContextDelete(MemoryContext context); 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); @@ -91,6 +92,10 @@ extern void MemoryContextCheck(MemoryContext context); #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 @@ -133,11 +138,10 @@ GetMemoryChunkContext(void *pointer) * 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); /* @@ -147,47 +151,38 @@ extern void MemoryContextCreate(MemoryContext node, /* 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. */ diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index d44089aedcc..7c0d6655498 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -2782,10 +2782,9 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger) /************************************************************ * 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. @@ -2794,6 +2793,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger) 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); diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index b1a0c1cc4f3..96c1622d66c 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -342,11 +342,12 @@ do_compile(FunctionCallInfo fcinfo, * 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; @@ -2453,7 +2454,7 @@ plpgsql_HashTableInit(void) 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); diff --git a/src/pl/plpython/plpy_procedure.c b/src/pl/plpython/plpy_procedure.c index b4c4dcdb6c2..16f1278b372 100644 --- a/src/pl/plpython/plpy_procedure.c +++ b/src/pl/plpython/plpy_procedure.c @@ -164,10 +164,9 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger) } /* 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); @@ -183,6 +182,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger) 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; diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index 865071bc3bd..558cabc9493 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -1479,12 +1479,10 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, /************************************************************ * 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. @@ -1493,6 +1491,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, 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; |