diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index febeb6eaf8eb966f7c50e4368b4af5c67ec205d2..70759abd31f1f2a4b51be562e58c05edfa5df6b8 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -253,7 +253,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); +static void AllocSetStats(MemoryContext context, int level, bool print, + MemoryContextCounters *totals); #ifdef MEMORY_CONTEXT_CHECKING static void AllocSetCheck(MemoryContext context); @@ -1228,20 +1229,23 @@ AllocSetIsEmpty(MemoryContext context) /* * AllocSetStats - * Displays stats about memory consumption of an allocset. + * 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. */ static void -AllocSetStats(MemoryContext context, int level) +AllocSetStats(MemoryContext context, int level, bool print, + MemoryContextCounters *totals) { AllocSet set = (AllocSet) context; Size nblocks = 0; - Size nchunks = 0; + Size freechunks = 0; Size totalspace = 0; Size freespace = 0; AllocBlock block; - AllocChunk chunk; int fidx; - int i; for (block = set->blocks; block != NULL; block = block->next) { @@ -1251,21 +1255,35 @@ AllocSetStats(MemoryContext context, int level) } for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++) { + AllocChunk chunk; + for (chunk = set->freelist[fidx]; chunk != NULL; chunk = (AllocChunk) chunk->aset) { - nchunks++; + freechunks++; freespace += chunk->size + ALLOC_CHUNKHDRSZ; } } - for (i = 0; i < level; i++) - fprintf(stderr, " "); + if (print) + { + int i; - fprintf(stderr, + 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, nchunks, - totalspace - freespace); + set->header.name, totalspace, nblocks, freespace, freechunks, + totalspace - freespace); + } + + if (totals) + { + totals->nblocks += nblocks; + totals->freechunks += freechunks; + totals->totalspace += totalspace; + totals->freespace += freespace; + } } diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c index 12d29f7440a7c217e7abd27ee39176c66885b597..705f3ef279130cc49ef0ccd152a6ddd3cd9ad4a9 100644 --- a/src/backend/utils/mmgr/mcxt.c +++ b/src/backend/utils/mmgr/mcxt.c @@ -52,7 +52,9 @@ MemoryContext CurTransactionContext = NULL; MemoryContext PortalContext = NULL; static void MemoryContextCallResetCallbacks(MemoryContext context); -static void MemoryContextStatsInternal(MemoryContext context, int level); +static void MemoryContextStatsInternal(MemoryContext context, int level, + bool print, int max_children, + MemoryContextCounters *totals); /* * You should not do memory allocations within a critical section, because @@ -477,25 +479,106 @@ MemoryContextIsEmpty(MemoryContext context) * MemoryContextStats * Print statistics about the named context and all its descendants. * - * This is just a debugging utility, so it's not fancy. The statistics - * are merely sent to stderr. + * This is just a debugging utility, so it's not very fancy. However, we do + * make some effort to summarize when the output would otherwise be very long. + * The statistics are sent to stderr. */ void MemoryContextStats(MemoryContext context) { - MemoryContextStatsInternal(context, 0); + /* A hard-wired limit on the number of children is usually good enough */ + MemoryContextStatsDetail(context, 100); } +/* + * MemoryContextStatsDetail + * + * Entry point for use if you want to vary the number of child contexts shown. + */ +void +MemoryContextStatsDetail(MemoryContext context, int max_children) +{ + MemoryContextCounters grand_totals; + + memset(&grand_totals, 0, sizeof(grand_totals)); + + MemoryContextStatsInternal(context, 0, true, max_children, &grand_totals); + + fprintf(stderr, + "Grand total: %zu bytes in %zd blocks; %zu free (%zd chunks); %zu used\n", + grand_totals.totalspace, grand_totals.nblocks, + grand_totals.freespace, grand_totals.freechunks, + grand_totals.totalspace - grand_totals.freespace); +} + +/* + * MemoryContextStatsInternal + * One recursion level for MemoryContextStats + * + * Print this context if print is true, but in any case accumulate counts into + * *totals (if given). + */ static void -MemoryContextStatsInternal(MemoryContext context, int level) +MemoryContextStatsInternal(MemoryContext context, int level, + bool print, int max_children, + MemoryContextCounters *totals) { + MemoryContextCounters local_totals; MemoryContext child; + int ichild; AssertArg(MemoryContextIsValid(context)); - (*context->methods->stats) (context, level); - for (child = context->firstchild; child != NULL; child = child->nextchild) - MemoryContextStatsInternal(child, level + 1); + /* Examine the context itself */ + (*context->methods->stats) (context, level, print, totals); + + /* + * Examine children. If there are more than max_children of them, we do + * not print the rest explicitly, but just summarize them. + */ + memset(&local_totals, 0, sizeof(local_totals)); + + for (child = context->firstchild, ichild = 0; + child != NULL; + child = child->nextchild, ichild++) + { + if (ichild < max_children) + MemoryContextStatsInternal(child, level + 1, + print, max_children, + totals); + else + MemoryContextStatsInternal(child, level + 1, + false, max_children, + &local_totals); + } + + /* Deal with excess children */ + if (ichild > max_children) + { + if (print) + { + int i; + + for (i = 0; i <= level; i++) + fprintf(stderr, " "); + fprintf(stderr, + "%d more child contexts containing %zu total in %zd blocks; %zu free (%zd chunks); %zu used\n", + ichild - max_children, + local_totals.totalspace, + local_totals.nblocks, + local_totals.freespace, + local_totals.freechunks, + local_totals.totalspace - local_totals.freespace); + } + + if (totals) + { + totals->nblocks += local_totals.nblocks; + totals->freechunks += local_totals.freechunks; + totals->totalspace += local_totals.totalspace; + totals->freespace += local_totals.freespace; + } + } } /* diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h index 5e036b9b6f5dc892b8947caef2c91cc32bcc4874..577ab9cb1f9560ebc0364d7f390a52588fa16fea 100644 --- a/src/include/nodes/memnodes.h +++ b/src/include/nodes/memnodes.h @@ -16,6 +16,24 @@ #include "nodes/nodes.h" +/* + * MemoryContextCounters + * Summarization state for MemoryContextStats collection. + * + * The set of counters in this struct is biased towards AllocSet; if we ever + * add any context types that are based on fundamentally different approaches, + * we might need more or different counters here. A possible API spec then + * would be to print only nonzero counters, but for now we just summarize in + * the format historically used by AllocSet. + */ +typedef struct MemoryContextCounters +{ + Size nblocks; /* Total number of malloc blocks */ + Size freechunks; /* Total number of free chunks */ + Size totalspace; /* Total bytes requested from malloc */ + Size freespace; /* The unused portion of totalspace */ +} MemoryContextCounters; + /* * MemoryContext * A logical context in which memory allocations occur. @@ -44,7 +62,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); + void (*stats) (MemoryContext context, int level, bool print, + MemoryContextCounters *totals); #ifdef MEMORY_CONTEXT_CHECKING void (*check) (MemoryContext context); #endif diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h index f0fe0f449cac4b8e96ac4b6d0927ec593b6830bb..38106d79e21da6ad79fd6090da638f23afddb724 100644 --- a/src/include/utils/memutils.h +++ b/src/include/utils/memutils.h @@ -104,6 +104,7 @@ extern MemoryContext GetMemoryChunkContext(void *pointer); extern MemoryContext MemoryContextGetParent(MemoryContext context); extern bool MemoryContextIsEmpty(MemoryContext context); extern void MemoryContextStats(MemoryContext context); +extern void MemoryContextStatsDetail(MemoryContext context, int max_children); extern void MemoryContextAllowInCriticalSection(MemoryContext context, bool allow);