aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/mcxtfuncs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/mcxtfuncs.c')
-rw-r--r--src/backend/utils/adt/mcxtfuncs.c180
1 files changed, 151 insertions, 29 deletions
diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c
index 10859414848..199e68c1ae5 100644
--- a/src/backend/utils/adt/mcxtfuncs.c
+++ b/src/backend/utils/adt/mcxtfuncs.c
@@ -19,7 +19,9 @@
#include "mb/pg_wchar.h"
#include "storage/proc.h"
#include "storage/procarray.h"
+#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/hsearch.h"
/* ----------
* The max bytes for showing identifiers of MemoryContext.
@@ -28,46 +30,106 @@
#define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE 1024
/*
+ * MemoryContextId
+ * Used for storage of transient identifiers for
+ * pg_get_backend_memory_contexts.
+ */
+typedef struct MemoryContextId
+{
+ MemoryContext context;
+ int context_id;
+} MemoryContextId;
+
+/*
+ * get_memory_context_name_and_ident
+ * Populate *name and *ident from the name and ident from 'context'.
+ */
+static void
+get_memory_context_name_and_ident(MemoryContext context, const char **const name,
+ const char **const ident)
+{
+ *name = context->name;
+ *ident = context->ident;
+
+ /*
+ * To be consistent with logging output, we label dynahash contexts with
+ * just the hash table name as with MemoryContextStatsPrint().
+ */
+ if (ident && strcmp(*name, "dynahash") == 0)
+ {
+ *name = *ident;
+ *ident = NULL;
+ }
+}
+
+/*
+ * int_list_to_array
+ * Convert an IntList to an array of INT4OIDs.
+ */
+static Datum
+int_list_to_array(const List *list)
+{
+ Datum *datum_array;
+ int length;
+ ArrayType *result_array;
+
+ length = list_length(list);
+ datum_array = (Datum *) palloc(length * sizeof(Datum));
+
+ foreach_int(i, list)
+ datum_array[foreach_current_index(i)] = Int32GetDatum(i);
+
+ result_array = construct_array_builtin(datum_array, length, INT4OID);
+
+ return PointerGetDatum(result_array);
+}
+
+/*
* PutMemoryContextsStatsTupleStore
- * One recursion level for pg_get_backend_memory_contexts.
+ * Add details for the given MemoryContext to 'tupstore'.
*/
static void
PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
TupleDesc tupdesc, MemoryContext context,
- const char *parent, int level)
+ HTAB *context_id_lookup)
{
-#define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS 10
+#define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS 11
Datum values[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
bool nulls[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
MemoryContextCounters stat;
- MemoryContext child;
+ List *path = NIL;
const char *name;
const char *ident;
const char *type;
Assert(MemoryContextIsValid(context));
- name = context->name;
- ident = context->ident;
-
/*
- * To be consistent with logging output, we label dynahash contexts with
- * just the hash table name as with MemoryContextStatsPrint().
+ * Figure out the transient context_id of this context and each of its
+ * ancestors.
*/
- if (ident && strcmp(name, "dynahash") == 0)
+ for (MemoryContext cur = context; cur != NULL; cur = cur->parent)
{
- name = ident;
- ident = NULL;
+ MemoryContextId *entry;
+ bool found;
+
+ entry = hash_search(context_id_lookup, &cur, HASH_FIND, &found);
+
+ if (!found)
+ elog(ERROR, "hash table corrupted");
+ path = lcons_int(entry->context_id, path);
}
/* Examine the context itself */
memset(&stat, 0, sizeof(stat));
- (*context->methods->stats) (context, NULL, (void *) &level, &stat, true);
+ (*context->methods->stats) (context, NULL, NULL, &stat, true);
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
+ get_memory_context_name_and_ident(context, &name, &ident);
+
if (name)
values[0] = CStringGetTextDatum(name);
else
@@ -92,8 +154,15 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
else
nulls[1] = true;
- if (parent)
- values[2] = CStringGetTextDatum(parent);
+ if (context->parent)
+ {
+ const char *parent_name,
+ *parent_ident;
+
+ get_memory_context_name_and_ident(context->parent, &parent_name,
+ &parent_ident);
+ values[2] = CStringGetTextDatum(parent_name);
+ }
else
nulls[2] = true;
@@ -117,19 +186,16 @@ PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
}
values[3] = CStringGetTextDatum(type);
- values[4] = Int32GetDatum(level);
- values[5] = Int64GetDatum(stat.totalspace);
- values[6] = Int64GetDatum(stat.nblocks);
- values[7] = Int64GetDatum(stat.freespace);
- values[8] = Int64GetDatum(stat.freechunks);
- values[9] = Int64GetDatum(stat.totalspace - stat.freespace);
- tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ values[4] = Int32GetDatum(list_length(path)); /* level */
+ values[5] = int_list_to_array(path);
+ values[6] = Int64GetDatum(stat.totalspace);
+ values[7] = Int64GetDatum(stat.nblocks);
+ values[8] = Int64GetDatum(stat.freespace);
+ values[9] = Int64GetDatum(stat.freechunks);
+ values[10] = Int64GetDatum(stat.totalspace - stat.freespace);
- for (child = context->firstchild; child != NULL; child = child->nextchild)
- {
- PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
- child, name, level + 1);
- }
+ tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ list_free(path);
}
/*
@@ -140,10 +206,66 @@ Datum
pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
{
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ int context_id;
+ List *contexts;
+ HASHCTL ctl;
+ HTAB *context_id_lookup;
+
+ ctl.keysize = sizeof(MemoryContext);
+ ctl.entrysize = sizeof(MemoryContextId);
+ ctl.hcxt = CurrentMemoryContext;
+
+ context_id_lookup = hash_create("pg_get_backend_memory_contexts",
+ 256,
+ &ctl,
+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
InitMaterializedSRF(fcinfo, 0);
- PutMemoryContextsStatsTupleStore(rsinfo->setResult, rsinfo->setDesc,
- TopMemoryContext, NULL, 0);
+
+ /*
+ * Here we use a non-recursive algorithm to visit all MemoryContexts
+ * starting with TopMemoryContext. The reason we avoid using a recursive
+ * algorithm is because we want to assign the context_id breadth-first.
+ * I.e. all contexts at level 1 are assigned IDs before contexts at level
+ * 2. Because contexts closer to TopMemoryContext are less likely to
+ * change, this makes the assigned context_id more stable. Otherwise, if
+ * the first child of TopMemoryContext obtained an additional grandchild,
+ * the context_id for the second child of TopMemoryContext would change.
+ */
+ contexts = list_make1(TopMemoryContext);
+
+ /* TopMemoryContext will always have a context_id of 1 */
+ context_id = 1;
+
+ foreach_ptr(MemoryContextData, cur, contexts)
+ {
+ MemoryContextId *entry;
+ bool found;
+
+ /*
+ * Record the context_id that we've assigned to each MemoryContext.
+ * PutMemoryContextsStatsTupleStore needs this to populate the "path"
+ * column with the parent context_ids.
+ */
+ entry = (MemoryContextId *) hash_search(context_id_lookup, &cur,
+ HASH_ENTER, &found);
+ entry->context_id = context_id++;
+ Assert(!found);
+
+ PutMemoryContextsStatsTupleStore(rsinfo->setResult,
+ rsinfo->setDesc,
+ cur,
+ context_id_lookup);
+
+ /*
+ * Append all children onto the contexts list so they're processed by
+ * subsequent iterations.
+ */
+ for (MemoryContext c = cur->firstchild; c != NULL; c = c->nextchild)
+ contexts = lappend(contexts, c);
+ }
+
+ hash_destroy(context_id_lookup);
return (Datum) 0;
}