From 3e98c0bafb28de87ae095b341687dc082371af54 Mon Sep 17 00:00:00 2001 From: Fujii Masao Date: Wed, 19 Aug 2020 15:34:43 +0900 Subject: [PATCH] Add pg_backend_memory_contexts system view. This view displays the usages of all the memory contexts of the server process attached to the current session. This information is useful to investigate the cause of backend-local memory bloat. This information can be also collected by calling MemoryContextStats(TopMemoryContext) via a debugger. But this technique cannot be uesd in some environments because no debugger is available there. And it outputs lots of text messages and it's not easy to analyze them. So, pg_backend_memory_contexts view allows us to access to backend-local memory contexts information more easily. Bump catalog version. Author: Atsushi Torikoshi, Fujii Masao Reviewed-by: Tatsuhito Kasahara, Andres Freund, Daniel Gustafsson, Robert Haas, Michael Paquier Discussion: https://postgr.es/m/72a656e0f71d0860161e0b3f67e4d771@oss.nttdata.com --- doc/src/sgml/catalogs.sgml | 122 +++++++++++++++++++++++ src/backend/catalog/system_views.sql | 3 + src/backend/utils/mmgr/mcxt.c | 138 +++++++++++++++++++++++++++ src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.dat | 9 ++ src/test/regress/expected/rules.out | 10 ++ 6 files changed, 283 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index fc329c5cff..1232b24e74 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -9226,6 +9226,11 @@ SCRAM-SHA-256$<iteration count>:&l available versions of extensions + + pg_backend_memory_contexts + backend memory contexts + + pg_config compile-time configuration parameters @@ -9577,6 +9582,123 @@ SCRAM-SHA-256$<iteration count>:&l + + <structname>pg_backend_memory_contexts</structname> + + + pg_backend_memory_contexts + + + + The view pg_backend_memory_contexts displays all + the memory contexts of the server process attached to the current session. + + + pg_backend_memory_contexts contains one row + for each memory context. + + + + <structname>pg_backend_memory_contexts</structname> Columns + + + + + Column Type + + + Description + + + + + + + + name text + + + Name of the memory context + + + + + + ident text + + + Identification information of the memory context. This field is truncated at 1024 bytes + + + + + + parent text + + + Name of the parent of this memory context + + + + + + level int4 + + + Distance from TopMemoryContext in context tree + + + + + + total_bytes int8 + + + Total bytes allocated for this memory context + + + + + + total_nblocks int8 + + + Total number of blocks allocated for this memory context + + + + + + free_bytes int8 + + + Free space in bytes + + + + + + free_chunks int8 + + + Total number of free chunks + + + + + + used_bytes int8 + + + Used space in bytes + + + + +
+ +
+ <structname>pg_config</structname> diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 8625cbeab6..ba5a23ac25 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -554,6 +554,9 @@ CREATE VIEW pg_shmem_allocations AS REVOKE ALL ON pg_shmem_allocations FROM PUBLIC; REVOKE EXECUTE ON FUNCTION pg_get_shmem_allocations() FROM PUBLIC; +CREATE VIEW pg_backend_memory_contexts AS + SELECT * FROM pg_get_backend_memory_contexts(); + -- Statistics views CREATE VIEW pg_stat_all_tables AS diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c index abda22fa57..d9bb2499db 100644 --- a/src/backend/utils/mmgr/mcxt.c +++ b/src/backend/utils/mmgr/mcxt.c @@ -21,8 +21,10 @@ #include "postgres.h" +#include "funcapi.h" #include "mb/pg_wchar.h" #include "miscadmin.h" +#include "utils/builtins.h" #include "utils/memdebug.h" #include "utils/memutils.h" @@ -67,6 +69,12 @@ static void MemoryContextStatsPrint(MemoryContext context, void *passthru, #define AssertNotInCriticalSection(context) \ Assert(CritSectionCount == 0 || (context)->allowInCritSection) +/* ---------- + * The max bytes for showing identifiers of MemoryContext. + * ---------- + */ +#define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE 1024 + /***************************************************************************** * EXPORTED ROUTINES * *****************************************************************************/ @@ -1220,3 +1228,133 @@ pchomp(const char *in) n--; return pnstrdup(in, n); } + +/* + * PutMemoryContextsStatsTupleStore + * One recursion level for pg_get_backend_memory_contexts. + */ +static void +PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore, + TupleDesc tupdesc, MemoryContext context, + const char *parent, int level) +{ +#define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS 9 + + Datum values[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS]; + bool nulls[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS]; + MemoryContextCounters stat; + MemoryContext child; + const char *name; + const char *ident; + + AssertArg(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(). + */ + if (ident && strcmp(name, "dynahash") == 0) + { + name = ident; + ident = NULL; + } + + /* Examine the context itself */ + memset(&stat, 0, sizeof(stat)); + (*context->methods->stats) (context, NULL, (void *) &level, &stat); + + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + + if (name) + values[0] = CStringGetTextDatum(name); + else + nulls[0] = true; + + if (ident) + { + int idlen = strlen(ident); + char clipped_ident[MEMORY_CONTEXT_IDENT_DISPLAY_SIZE]; + + /* + * Some identifiers such as SQL query string can be very long, + * truncate oversize identifiers. + */ + if (idlen >= MEMORY_CONTEXT_IDENT_DISPLAY_SIZE) + idlen = pg_mbcliplen(ident, idlen, MEMORY_CONTEXT_IDENT_DISPLAY_SIZE - 1); + + memcpy(clipped_ident, ident, idlen); + clipped_ident[idlen] = '\0'; + values[1] = CStringGetTextDatum(clipped_ident); + } + else + nulls[1] = true; + + if (parent) + values[2] = CStringGetTextDatum(parent); + else + nulls[2] = true; + + values[3] = Int32GetDatum(level); + values[4] = Int64GetDatum(stat.totalspace); + values[5] = Int64GetDatum(stat.nblocks); + values[6] = Int64GetDatum(stat.freespace); + values[7] = Int64GetDatum(stat.freechunks); + values[8] = Int64GetDatum(stat.totalspace - stat.freespace); + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + + for (child = context->firstchild; child != NULL; child = child->nextchild) + { + PutMemoryContextsStatsTupleStore(tupstore, tupdesc, + child, name, level + 1); + } +} + +/* + * pg_get_backend_memory_contexts + * SQL SRF showing backend memory context. + */ +Datum +pg_get_backend_memory_contexts(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not allowed in this context"))); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + MemoryContextSwitchTo(oldcontext); + + PutMemoryContextsStatsTupleStore(tupstore, tupdesc, + TopMemoryContext, NULL, 0); + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + return (Datum) 0; +} diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 9284951121..3e67797630 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202007251 +#define CATALOG_VERSION_NO 202008191 #endif diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 082a11f270..27989971db 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7807,6 +7807,15 @@ proargnames => '{name,off,size,allocated_size}', prosrc => 'pg_get_shmem_allocations' }, +# memory context of local backend +{ oid => '2282', descr => 'information about all memory contexts of local backend', + proname => 'pg_get_backend_memory_contexts', prorows => '100', proretset => 't', + provolatile => 'v', proparallel => 'r', prorettype => 'record', proargtypes => '', + proallargtypes => '{text,text,text,int4,int8,int8,int8,int8,int8}', + proargmodes => '{o,o,o,o,o,o,o,o,o}', + proargnames => '{name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes}', + prosrc => 'pg_get_backend_memory_contexts' }, + # non-persistent series generator { oid => '1066', descr => 'non-persistent series generator', proname => 'generate_series', prorows => '1000', diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 601734a6f1..2a18dc423e 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1324,6 +1324,16 @@ pg_available_extensions| SELECT e.name, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); +pg_backend_memory_contexts| SELECT pg_get_backend_memory_contexts.name, + pg_get_backend_memory_contexts.ident, + pg_get_backend_memory_contexts.parent, + pg_get_backend_memory_contexts.level, + pg_get_backend_memory_contexts.total_bytes, + pg_get_backend_memory_contexts.total_nblocks, + pg_get_backend_memory_contexts.free_bytes, + pg_get_backend_memory_contexts.free_chunks, + pg_get_backend_memory_contexts.used_bytes + FROM pg_get_backend_memory_contexts() pg_get_backend_memory_contexts(name, ident, parent, level, total_bytes, total_nblocks, free_bytes, free_chunks, used_bytes); pg_config| SELECT pg_config.name, pg_config.setting FROM pg_config() pg_config(name, setting); -- 2.39.5