diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 55694c43689..85ac79f07ec 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -8362,6 +8362,11 @@ SCRAM-SHA-256$<iteration count>:&l
planner statistics
+
+ pg_shmem_allocations
+ shared memory allocations
+
+
pg_stats_extextended planner statistics
@@ -10748,6 +10753,86 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
+
+ pg_shmem_allocations
+
+
+ pg_shmem_allocations
+
+
+
+ The pg_shmem_allocations view shows allocations
+ made from the server's main shared memory segment. This includes both
+ memory allocated by postgres itself and memory
+ allocated by extensions using the mechanisms detailed in
+ .
+
+
+
+ Note that this view does not include memory allocated using the dynamic
+ shared memory infrastructure.
+
+
+
+ pg_shmem_allocations Columns
+
+
+
+
+ Name
+ Type
+ Description
+
+
+
+
+
+ name
+ text
+ The name of the shared memory allocation. NULL for unused memory
+ and <anonymous> for anonymous
+ allocations.
+
+
+
+ off
+ bigint
+ The offset at which the allocation starts. NULL for anonymous
+ allocations and unused memory.
+
+
+
+ size
+ bigint
+ Size of the allocation
+
+
+
+ allocated_size
+ bigint
+ Size of the allocation including padding. For anonymous
+ allocations, no information about padding is available, so the
+ size and allocated_size columns
+ will always be equal. Padding is not meaningful for free memory, so
+ the columns will be equal in that case also.
+
+
+
+
+
+
+ Anonymous allocations are allocations that have been made
+ with ShmemAlloc() directly, rather than via
+ ShmemInitStruct() or
+ ShmemInitHash().
+
+
+
+ By default, the pg_shmem_allocations view can be
+ read only by superusers.
+
+
+
pg_stats
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 5616524cfd3..ca5e6efd7e4 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -3239,7 +3239,7 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray
-
+ Shared Memory and LWLocks
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 2fc3e3ff90a..773edf85e78 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -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
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index a4f0ec1a673..2892a573e4a 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -66,12 +66,16 @@
#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("");
+ 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;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 59f1ff01ab8..427faa3c3b6 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7663,6 +7663,15 @@
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',
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 243b73f8bbd..0c1af892062 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -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;
/*
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 80a07825b95..62eaf90a0f2 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -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,