aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/transam/xact.c1
-rw-r--r--src/backend/catalog/partition.c9
-rw-r--r--src/backend/commands/policy.c3
-rw-r--r--src/backend/executor/functions.c6
-rw-r--r--src/backend/replication/logical/reorderbuffer.c3
-rw-r--r--src/backend/statistics/extended_stats.c3
-rw-r--r--src/backend/utils/cache/plancache.c3
-rw-r--r--src/backend/utils/cache/relcache.c37
-rw-r--r--src/backend/utils/cache/ts_cache.c13
-rw-r--r--src/backend/utils/hash/dynahash.c12
-rw-r--r--src/backend/utils/mmgr/aset.c72
-rw-r--r--src/backend/utils/mmgr/generation.c52
-rw-r--r--src/backend/utils/mmgr/mcxt.c138
-rw-r--r--src/backend/utils/mmgr/slab.c47
-rw-r--r--src/include/nodes/memnodes.h11
-rw-r--r--src/include/utils/memutils.h33
-rw-r--r--src/pl/plperl/plperl.c8
-rw-r--r--src/pl/plpgsql/src/pl_comp.c5
-rw-r--r--src/pl/plpython/plpy_procedure.c8
-rw-r--r--src/pl/tcl/pltcl.c9
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;