Add pg_shmem_allocations view.
authorRobert Haas <rhaas@postgresql.org>
Thu, 9 Jan 2020 15:59:07 +0000 (10:59 -0500)
committerRobert Haas <rhaas@postgresql.org>
Thu, 9 Jan 2020 15:59:07 +0000 (10:59 -0500)
This tells you about allocations that have been made from the main
shared memory segment. The original patch also tried to show information
about dynamic shared memory allocation as well, but I decided to
leave that problem for another time.

Andres Freund and Robert Haas, reviewed by Michael Paquier, Marti
Raudsepp, Tom Lane, Álvaro Herrera, and Kyotaro Horiguchi.

Discussion: http://postgr.es/m/20140504114417.GM12715@awork2.anarazel.de

doc/src/sgml/catalogs.sgml
doc/src/sgml/xfunc.sgml
src/backend/catalog/system_views.sql
src/backend/storage/ipc/shmem.c
src/include/catalog/pg_proc.dat
src/include/storage/shmem.h
src/test/regress/expected/rules.out

index 55694c43689efd28efa7c3127d6d82814a3b94e0..85ac79f07ec058713fd8bae5f0a82ee34d7574fb 100644 (file)
@@ -8362,6 +8362,11 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       <entry>planner statistics</entry>
      </row>
 
+     <row>
+      <entry><link linkend="view-pg-shmem-allocations"><structname>pg_shmem_allocations</structname></link></entry>
+      <entry>shared memory allocations</entry>
+     </row>
+
      <row>
       <entry><link linkend="view-pg-stats-ext"><structname>pg_stats_ext</structname></link></entry>
       <entry>extended planner statistics</entry>
@@ -10748,6 +10753,86 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
 
  </sect1>
 
+ <sect1 id="view-pg-shmem-allocations">
+  <title><structname>pg_shmem_allocations</structname></title>
+
+  <indexterm zone="view-pg-shmem-allocations">
+   <primary>pg_shmem_allocations</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_shmem_allocations</structname> view shows allocations
+   made from the server's main shared memory segment.  This includes both
+   memory allocated by <productname>postgres</productname> itself and memory
+   allocated by extensions using the mechanisms detailed in
+   <xref linkend="xfunc-shared-addin" />.
+  </para>
+
+  <para>
+   Note that this view does not include memory allocated using the dynamic
+   shared memory infrastructure.
+  </para>
+
+  <table>
+   <title><structname>pg_shmem_allocations</structname> Columns</title>
+
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>name</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry>The name of the shared memory allocation. NULL for unused memory
+      and <literal>&lt;anonymous&gt;</literal> for anonymous
+      allocations.</entry>
+     </row>
+
+     <row>
+      <entry><structfield>off</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry>The offset at which the allocation starts. NULL for anonymous
+      allocations and unused memory.</entry>
+     </row>
+
+     <row>
+      <entry><structfield>size</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Size of the allocation</entry>
+     </row>
+
+     <row>
+      <entry><structfield>allocated_size</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry>Size of the allocation including padding. For anonymous
+      allocations, no information about padding is available, so the
+      <literal>size</literal> and <literal>allocated_size</literal> columns
+      will always be equal. Padding is not meaningful for free memory, so
+      the columns will be equal in that case also.</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   Anonymous allocations are allocations that have been made
+   with <literal>ShmemAlloc()</literal> directly, rather than via
+   <literal>ShmemInitStruct()</literal> or
+   <literal>ShmemInitHash()</literal>.
+  </para>
+
+  <para>
+   By default, the <structname>pg_shmem_allocations</structname> view can be
+   read only by superusers.
+  </para>
+ </sect1>
+
  <sect1 id="view-pg-stats">
   <title><structname>pg_stats</structname></title>
 
index 5616524cfd318362853ea0e21e5033310cf9f815..ca5e6efd7e46b5bcd470a89c94e7378b46a2d7b4 100644 (file)
@@ -3239,7 +3239,7 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray
     </para>
    </sect2>
 
-   <sect2>
+   <sect2 id="xfunc-shared-addin">
     <title>Shared Memory and LWLocks</title>
 
     <para>
index 2fc3e3ff90af2ae82614750a7997129f7b7863f3..773edf85e78c7a9322e3e222d930d1364d0b3445 100644 (file)
@@ -547,6 +547,12 @@ CREATE VIEW pg_config AS
 REVOKE ALL on pg_config FROM PUBLIC;
 REVOKE EXECUTE ON FUNCTION pg_config() FROM PUBLIC;
 
+CREATE VIEW pg_shmem_allocations AS
+    SELECT * FROM pg_get_shmem_allocations();
+
+REVOKE ALL ON pg_shmem_allocations FROM PUBLIC;
+REVOKE EXECUTE ON FUNCTION pg_get_shmem_allocations() FROM PUBLIC;
+
 -- Statistics views
 
 CREATE VIEW pg_stat_all_tables AS
index a4f0ec1a673bde13726702f9236c30760d0382d7..2892a573e4ae0b4670e141f80a9f733eff922006 100644 (file)
 #include "postgres.h"
 
 #include "access/transam.h"
+#include "fmgr.h"
+#include "funcapi.h"
 #include "miscadmin.h"
 #include "storage/lwlock.h"
 #include "storage/pg_shmem.h"
 #include "storage/shmem.h"
 #include "storage/spin.h"
+#include "utils/builtins.h"
 
+static void *ShmemAllocRaw(Size size, Size *allocated_size);
 
 /* shared memory global variables */
 
@@ -157,8 +161,9 @@ void *
 ShmemAlloc(Size size)
 {
    void       *newSpace;
+   Size        allocated_size;
 
-   newSpace = ShmemAllocNoError(size);
+   newSpace = ShmemAllocRaw(size, &allocated_size);
    if (!newSpace)
        ereport(ERROR,
                (errcode(ERRCODE_OUT_OF_MEMORY),
@@ -174,6 +179,20 @@ ShmemAlloc(Size size)
  */
 void *
 ShmemAllocNoError(Size size)
+{
+   Size        allocated_size;
+
+   return ShmemAllocRaw(size, &allocated_size);
+}
+
+/*
+ * ShmemAllocRaw -- allocate align chunk and return allocated size
+ *
+ * Also sets *allocated_size to the number of bytes allocated, which will
+ * be equal to the number requested plus any padding we choose to add.
+ */
+static void *
+ShmemAllocRaw(Size size, Size *allocated_size)
 {
    Size        newStart;
    Size        newFree;
@@ -191,6 +210,7 @@ ShmemAllocNoError(Size size)
     * won't be sufficient.
     */
    size = CACHELINEALIGN(size);
+   *allocated_size = size;
 
    Assert(ShmemSegHdr != NULL);
 
@@ -441,8 +461,10 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
    }
    else
    {
+       Size    allocated_size;
+
        /* It isn't in the table yet. allocate and initialize it */
-       structPtr = ShmemAllocNoError(size);
+       structPtr = ShmemAllocRaw(size, &allocated_size);
        if (structPtr == NULL)
        {
            /* out of memory; remove the failed ShmemIndex entry */
@@ -455,6 +477,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
                            name, size)));
        }
        result->size = size;
+       result->allocated_size = allocated_size;
        result->location = structPtr;
    }
 
@@ -503,3 +526,82 @@ mul_size(Size s1, Size s2)
                 errmsg("requested shared memory size overflows size_t")));
    return result;
 }
+
+/* SQL SRF showing allocated shared memory */
+Datum
+pg_get_shmem_allocations(PG_FUNCTION_ARGS)
+{
+#define PG_GET_SHMEM_SIZES_COLS 4
+   ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+   TupleDesc   tupdesc;
+   Tuplestorestate *tupstore;
+   MemoryContext per_query_ctx;
+   MemoryContext oldcontext;
+   HASH_SEQ_STATUS hstat;
+   ShmemIndexEnt *ent;
+   Size    named_allocated = 0;
+   Datum       values[PG_GET_SHMEM_SIZES_COLS];
+   bool        nulls[PG_GET_SHMEM_SIZES_COLS];
+
+   /* 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);
+
+   LWLockAcquire(ShmemIndexLock, LW_SHARED);
+
+   hash_seq_init(&hstat, ShmemIndex);
+
+   /* output all allocated entries */
+   memset(nulls, 0, sizeof(nulls));
+   while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
+   {
+       values[0] = CStringGetTextDatum(ent->key);
+       values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+       values[2] = Int64GetDatum(ent->size);
+       values[3] = Int64GetDatum(ent->allocated_size);
+       named_allocated += ent->allocated_size;
+
+       tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+   }
+
+   /* output shared memory allocated but not counted via the shmem index */
+   values[0] = CStringGetTextDatum("<anonymous>");
+   nulls[1] = true;
+   values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+   values[3] = values[2];
+   tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+
+   /* output as-of-yet unused shared memory */
+   nulls[0] = true;
+   values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+   nulls[1] = false;
+   values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+   values[3] = values[2];
+   tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+
+   LWLockRelease(ShmemIndexLock);
+
+   tuplestore_donestoring(tupstore);
+
+   return (Datum) 0;
+}
index 59f1ff01ab804f8b03fbb7bbe352a215763e0806..427faa3c3b66fbf48d611a8f5d1d036545fe197b 100644 (file)
   proparallel => 'r', prorettype => 'float8', proargtypes => '',
   prosrc => 'pg_notification_queue_usage' },
 
+# shared memory usage
+{ oid => '8613',
+  descr => 'allocations from the main shared memory segment',
+  proname => 'pg_get_shmem_allocations', 'prorows' => 50, 'proretset' => 't',
+  provolatile => 'v', 'prorettype' => 'record', 'proargtypes' => '',
+  proallargtypes => '{text,int8,int8,int8}', proargmodes => '{o,o,o,o}',
+  proargnames => '{name,off,size,allocated_size}',
+  prosrc => 'pg_get_shmem_allocations' },
+
 # non-persistent series generator
 { oid => '1066', descr => 'non-persistent series generator',
   proname => 'generate_series', prorows => '1000',
index 243b73f8bbdc34916e31507f6f9fd30a3ad9245f..0c1af892062db37dd4eb65aac3a91c42f825fadf 100644 (file)
@@ -59,7 +59,8 @@ typedef struct
 {
    char        key[SHMEM_INDEX_KEYSIZE];   /* string name */
    void       *location;       /* location in shared mem */
-   Size        size;           /* # bytes allocated for the structure */
+   Size        size;           /* # bytes requested for the structure */
+   Size        allocated_size; /* # bytes actually allocated */
 } ShmemIndexEnt;
 
 /*
index 80a07825b953b902b837ab88dfe93219fc77a056..62eaf90a0f29ea1f5ad561bbdd0bfa7709402a41 100644 (file)
@@ -1721,6 +1721,11 @@ pg_shadow| SELECT pg_authid.rolname AS usename,
    FROM (pg_authid
      LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid))))
   WHERE pg_authid.rolcanlogin;
+pg_shmem_allocations| SELECT pg_get_shmem_allocations.name,
+    pg_get_shmem_allocations.off,
+    pg_get_shmem_allocations.size,
+    pg_get_shmem_allocations.allocated_size
+   FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size);
 pg_stat_activity| SELECT s.datid,
     d.datname,
     s.pid,