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);