Add pg_backend_memory_contexts system view.
authorFujii Masao <fujii@postgresql.org>
Wed, 19 Aug 2020 06:34:43 +0000 (15:34 +0900)
committerFujii Masao <fujii@postgresql.org>
Wed, 19 Aug 2020 06:34:43 +0000 (15:34 +0900)
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
src/backend/catalog/system_views.sql
src/backend/utils/mmgr/mcxt.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat
src/test/regress/expected/rules.out

index fc329c5cff968487281949155de6e05df733da15..1232b24e74cff1ddd01ac7f98fe3e42838dedf71 100644 (file)
@@ -9226,6 +9226,11 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       <entry>available versions of extensions</entry>
      </row>
 
+     <row>
+      <entry><link linkend="view-pg-backend-memory-contexts"><structname>pg_backend_memory_contexts</structname></link></entry>
+      <entry>backend memory contexts</entry>
+     </row>
+
      <row>
       <entry><link linkend="view-pg-config"><structname>pg_config</structname></link></entry>
       <entry>compile-time configuration parameters</entry>
@@ -9577,6 +9582,123 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
   </para>
  </sect1>
 
+ <sect1 id="view-pg-backend-memory-contexts">
+  <title><structname>pg_backend_memory_contexts</structname></title>
+
+  <indexterm zone="view-pg-backend-memory-contexts">
+   <primary>pg_backend_memory_contexts</primary>
+  </indexterm>
+
+  <para>
+   The view <structname>pg_backend_memory_contexts</structname> displays all
+   the memory contexts of the server process attached to the current session.
+  </para>
+  <para>
+   <structname>pg_backend_memory_contexts</structname> contains one row
+   for each memory context.
+  </para>
+
+  <table>
+   <title><structname>pg_backend_memory_contexts</structname> Columns</title>
+   <tgroup cols="1">
+    <thead>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       Column Type
+      </para>
+      <para>
+       Description
+      </para></entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>name</structfield> <type>text</type>
+      </para>
+      <para>
+       Name of the memory context
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>ident</structfield> <type>text</type>
+      </para>
+      <para>
+       Identification information of the memory context. This field is truncated at 1024 bytes
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>parent</structfield> <type>text</type>
+      </para>
+      <para>
+       Name of the parent of this memory context
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>level</structfield> <type>int4</type>
+      </para>
+      <para>
+       Distance from TopMemoryContext in context tree
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>total_bytes</structfield> <type>int8</type>
+      </para>
+      <para>
+       Total bytes allocated for this memory context
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>total_nblocks</structfield> <type>int8</type>
+      </para>
+      <para>
+       Total number of blocks allocated for this memory context
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>free_bytes</structfield> <type>int8</type>
+      </para>
+      <para>
+       Free space in bytes
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>free_chunks</structfield> <type>int8</type>
+      </para>
+      <para>
+       Total number of free chunks
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>used_bytes</structfield> <type>int8</type>
+      </para>
+      <para>
+       Used space in bytes
+      </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect1>
+
  <sect1 id="view-pg-config">
   <title><structname>pg_config</structname></title>
 
index 8625cbeab6e470da28c9a85fb74b76b45125d432..ba5a23ac2524f305944e23c0d6b1670986db55c6 100644 (file)
@@ -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
index abda22fa570a3b760782257b599b0a84c10c4708..d9bb2499db7522bdf62d2336c04a4c41c561c0af 100644 (file)
 
 #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;
+}
index 928495112196a721a0631721fc416b0fff0b9277..3e6779763000f2fe5dbad682e13b5fe256de6041 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202007251
+#define CATALOG_VERSION_NO 202008191
 
 #endif
index 082a11f2708c64557c011114042d182c0952a54d..27989971db74d5303a1c1379a73caf3a75b2fec3 100644 (file)
   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',
index 601734a6f1ec1f6f97f7d303017c4cb79452aa28..2a18dc423e2bfd2ca6705e7c4b20a9a334c421c9 100644 (file)
@@ -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);