like pg_shmem_allocations, but fine-grained for DSM registry ?

Started by Florents Tselai10 months ago23 messages
#1Florents Tselai
florents.tselai@gmail.com

I’ve been working with the DSM registry API.
I was wondering if it is possible (it doesn’t look like it) or if it has been discussed:
can we expose a view like pg_shmem_allocations, but fine-grained for every named segment (i.e. created by GetNamedDSMSegment )?

Currently, there is a "DSM Registry Data” entry in that view,
but iiuc, it’s only about the top-level hash table the registry uses.

That’s probably related to /messages/by-id/CAH2L28vHzRankszhqz7deXURxKncxfirnuW68zD7+hVAqaS5GQ@mail.gmail.com

#2Nathan Bossart
nathandbossart@gmail.com
In reply to: Florents Tselai (#1)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On Thu, Mar 13, 2025 at 06:54:09PM +0200, Florents Tselai wrote:

I�ve been working with the DSM registry API.
I was wondering if it is possible (it doesn�t look like it) or if it has been discussed:
can we expose a view like pg_shmem_allocations, but fine-grained for every named segment (i.e. created by GetNamedDSMSegment )?

Currently, there is a "DSM Registry Data" entry in that view,
but iiuc, it�s only about the top-level hash table the registry uses.

This seems like a generally reasonable idea to me. In theory, it should be
easy enough to build something that walks through the DSM registry hash
table.

--
nathan

#3Florents Tselai
florents.tselai@gmail.com
In reply to: Nathan Bossart (#2)
1 attachment(s)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On Fri, Mar 14, 2025 at 4:16 PM Nathan Bossart <nathandbossart@gmail.com>
wrote:

On Thu, Mar 13, 2025 at 06:54:09PM +0200, Florents Tselai wrote:

I扉e been working with the DSM registry API.
I was wondering if it is possible (it doesn愒 look like it) or if it has

been discussed:

can we expose a view like pg_shmem_allocations, but fine-grained for

every named segment (i.e. created by GetNamedDSMSegment )?

Currently, there is a "DSM Registry Data" entry in that view,
but iiuc, it愀 only about the top-level hash table the registry uses.

This seems like a generally reasonable idea to me. In theory, it should be
easy enough to build something that walks through the DSM registry hash
table.

Here's a first attempt towards a view pg_dsm_registry(name, size) that
does just that
So, using the test_dsm_registry module as a test bed,

it would look like this.

CREATE EXTENSION test_dsm_registry;
SELECT set_val_in_shmem(1236);
set_val_in_shmem
------------------

(1 row)

-- 20 bytes = int (4 bytes) + LWLock (16bytes)
SELECT * FROM pg_dsm_registry;
name | size
-------------------+------
test_dsm_registry | 20
(1 row)

I'll create a cf entry to keep track of this

Attachments:

v1-pg_dsm_registry-view.patchapplication/x-patch; name=v1-pg_dsm_registry-view.patchDownload
From 2f475e8a05142991feac2f62b57bc25b9c5ceebc Mon Sep 17 00:00:00 2001
From: Florents Tselai <florents.tselai@gmail.com>
Date: Fri, 14 Mar 2025 23:34:13 +0200
Subject: [PATCH v1] First attempt towards a pg_dsm_registry view. Iterates
 over the registry hash table entries and reports the name and size.

---
 src/backend/catalog/system_views.sql          |  8 ++
 src/backend/storage/ipc/dsm_registry.c        | 86 +++++++++++++++++++
 src/include/catalog/pg_proc.dat               |  6 ++
 .../expected/test_dsm_registry.out            |  7 ++
 .../sql/test_dsm_registry.sql                 |  3 +
 src/test/regress/expected/rules.out           |  3 +
 6 files changed, 113 insertions(+)

diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index a4d2cfdcaf5..63800af0769 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -658,6 +658,14 @@ GRANT SELECT ON pg_shmem_allocations TO pg_read_all_stats;
 REVOKE EXECUTE ON FUNCTION pg_get_shmem_allocations() FROM PUBLIC;
 GRANT EXECUTE ON FUNCTION pg_get_shmem_allocations() TO pg_read_all_stats;
 
+CREATE VIEW pg_dsm_registry AS
+SELECT * FROM pg_get_dsm_registry();
+
+REVOKE ALL ON pg_dsm_registry FROM PUBLIC;
+GRANT SELECT ON pg_dsm_registry TO pg_read_all_stats;
+REVOKE EXECUTE ON FUNCTION pg_get_dsm_registry() FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION pg_get_dsm_registry() TO pg_read_all_stats;
+
 CREATE VIEW pg_backend_memory_contexts AS
	 SELECT * FROM pg_get_backend_memory_contexts();
 
diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 1d4fd31ffed..362452ab090 100644
--- a/src/backend/storage/ipc/dsm_registry.c
+++ b/src/backend/storage/ipc/dsm_registry.c
@@ -31,6 +31,10 @@
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
 #include "utils/memutils.h"
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
 
 typedef struct DSMRegistryCtxStruct
 {
@@ -198,3 +202,85 @@ GetNamedDSMSegment(const char *name, size_t size,
 
 	return ret;
 }
+
+void
+iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg);
+void
+iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg)
+{
+    DSMRegistryEntry *entry;
+    dshash_seq_status status;
+
+    /* Ensure DSM registry is initialized */
+    init_dsm_registry();
+
+    /* Use non-exclusive access to avoid blocking other backends */
+    dshash_seq_init(&status, dsm_registry_table, false);
+
+    while ((entry = dshash_seq_next(&status)) != NULL)
+        callback(entry, arg);
+
+    dshash_seq_term(&status);
+}
+
+/* SQL SRF showing DSM registry allocated memory */
+PG_FUNCTION_INFO_V1(pg_get_dsm_registry);
+
+typedef struct
+{
+	Tuplestorestate *tupstore;
+	TupleDesc        tupdesc;
+} DSMRegistrySRFContext;
+
+static void
+collect_dsm_registry(DSMRegistryEntry *entry, void *arg)
+{
+	DSMRegistrySRFContext *ctx = (DSMRegistrySRFContext *) arg;
+	Datum values[2];
+	bool nulls[2] = {false, false};
+
+	values[0] = CStringGetTextDatum(entry->name);
+	values[1] = Int64GetDatum(entry->size);
+
+	tuplestore_putvalues(ctx->tupstore, ctx->tupdesc, values, nulls);
+}
+
+Datum
+pg_get_dsm_registry(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+	TupleDesc tupdesc;
+	Tuplestorestate *tupstore;
+	DSMRegistrySRFContext ctx;
+
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR, (errmsg("pg_get_dsm_registry must be used in a SRF context")));
+
+	/* Set up tuple descriptor */
+	tupdesc = CreateTemplateTupleDesc(2);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "size", INT8OID, -1, 0);
+
+	/* Switch to per-query memory context */
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	/* Initialize tuplestore */
+	tupstore = tuplestore_begin_heap(false, false, work_mem);
+
+	ctx.tupstore = tupstore;
+	ctx.tupdesc = tupdesc;
+
+	/* Collect registry data */
+	iterate_dsm_registry(collect_dsm_registry, &ctx);
+
+	/* Switch back and return results */
+	MemoryContextSwitchTo(oldcontext);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 890822eaf79..3c012f52d95 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8491,6 +8491,12 @@
   proallargtypes => '{text,int8,int8,int8}', proargmodes => '{o,o,o,o}',
   proargnames => '{name,off,size,allocated_size}',
   prosrc => 'pg_get_shmem_allocations' },
+{ oid => '6062', descr => 'DSM registry allocations',
+  proname => 'pg_get_dsm_registry', prorows => '50', proretset => 't',
+  provolatile => 'v', prorettype => 'record', proargtypes => '',
+  proallargtypes => '{text,int8}', proargmodes => '{o,o}',
+  proargnames => '{name,size}',
+  prosrc => 'pg_get_dsm_registry' },
 
 # memory context of local backend
 { oid => '2282',
diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
index 8ffbd343a05..229abf926b6 100644
--- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
+++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
@@ -12,3 +12,10 @@ SELECT get_val_in_shmem();
	          1236
 (1 row)
 
+-- 20 bytes = int (4 bytes) + LWLock (16bytes)
+SELECT * FROM pg_dsm_registry;
+       name        | size 
+-------------------+------
+ test_dsm_registry |   20
+(1 row)
+
diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
index b3351be0a16..aad402b5e64 100644
--- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
+++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
@@ -2,3 +2,6 @@ CREATE EXTENSION test_dsm_registry;
 SELECT set_val_in_shmem(1236);
 \c
 SELECT get_val_in_shmem();
+
+-- 20 bytes = int (4 bytes) + LWLock (16bytes)
+SELECT * FROM pg_dsm_registry;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 62f69ac20b2..fc35de68dae 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1324,6 +1324,9 @@ pg_cursors| SELECT name,
	 is_scrollable,
	 creation_time
	FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
+pg_dsm_registry| SELECT name,
+    size
+   FROM pg_get_dsm_registry() pg_get_dsm_registry(name, size);
 pg_file_settings| SELECT sourcefile,
	 sourceline,
	 seqno,
-- 
2.48.1

#4Florents Tselai
florents.tselai@gmail.com
In reply to: Florents Tselai (#3)
1 attachment(s)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On Fri, Mar 14, 2025 at 11:44 PM Florents Tselai <florents.tselai@gmail.com>
wrote:

On Fri, Mar 14, 2025 at 4:16 PM Nathan Bossart <nathandbossart@gmail.com>
wrote:

On Thu, Mar 13, 2025 at 06:54:09PM +0200, Florents Tselai wrote:

I扉e been working with the DSM registry API.
I was wondering if it is possible (it doesn愒 look like it) or if it has

been discussed:

can we expose a view like pg_shmem_allocations, but fine-grained for

every named segment (i.e. created by GetNamedDSMSegment )?

Currently, there is a "DSM Registry Data" entry in that view,
but iiuc, it愀 only about the top-level hash table the registry uses.

This seems like a generally reasonable idea to me. In theory, it should
be
easy enough to build something that walks through the DSM registry hash
table.

Here's a first attempt towards a view pg_dsm_registry(name, size) that
does just that
So, using the test_dsm_registry module as a test bed,

it would look like this.

CREATE EXTENSION test_dsm_registry;
SELECT set_val_in_shmem(1236);
set_val_in_shmem
------------------

(1 row)

-- 20 bytes = int (4 bytes) + LWLock (16bytes)
SELECT * FROM pg_dsm_registry;
name | size
-------------------+------
test_dsm_registry | 20
(1 row)

I'll create a cf entry to keep track of this

Here's an updated v3 that fixes some white spaces v2 had—no other changes.

I'm wondering though if it also makes sense to expose:
- backend_pid (who created the segment)
- is_pinned bool
- created_at

https://commitfest.postgresql.org/patch/5652/

Attachments:

v3-0001-pg_dsm_registry-view.patchapplication/octet-stream; name=v3-0001-pg_dsm_registry-view.patchDownload
From c168a1544199505b0f9d1e9ea9a5183b7affee34 Mon Sep 17 00:00:00 2001
From: Florents Tselai <florents.tselai@gmail.com>
Date: Sat, 15 Mar 2025 10:21:34 +0200
Subject: [PATCH v3] First attempt towards a pg_dsm_registry view. Iterates
 over the registry hash table entries and reports the name and size.

---
 src/backend/catalog/system_views.sql          |  8 ++
 src/backend/storage/ipc/dsm_registry.c        | 83 +++++++++++++++++++
 src/include/catalog/pg_proc.dat               |  6 ++
 .../expected/test_dsm_registry.out            |  7 ++
 .../sql/test_dsm_registry.sql                 |  3 +
 src/test/regress/expected/rules.out           |  3 +
 6 files changed, 110 insertions(+)

diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index a4d2cfdcaf5..63800af0769 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -658,6 +658,14 @@ GRANT SELECT ON pg_shmem_allocations TO pg_read_all_stats;
 REVOKE EXECUTE ON FUNCTION pg_get_shmem_allocations() FROM PUBLIC;
 GRANT EXECUTE ON FUNCTION pg_get_shmem_allocations() TO pg_read_all_stats;
 
+CREATE VIEW pg_dsm_registry AS
+SELECT * FROM pg_get_dsm_registry();
+
+REVOKE ALL ON pg_dsm_registry FROM PUBLIC;
+GRANT SELECT ON pg_dsm_registry TO pg_read_all_stats;
+REVOKE EXECUTE ON FUNCTION pg_get_dsm_registry() FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION pg_get_dsm_registry() TO pg_read_all_stats;
+
 CREATE VIEW pg_backend_memory_contexts AS
     SELECT * FROM pg_get_backend_memory_contexts();
 
diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 1d4fd31ffed..549cbe07a28 100644
--- a/src/backend/storage/ipc/dsm_registry.c
+++ b/src/backend/storage/ipc/dsm_registry.c
@@ -31,6 +31,10 @@
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
 #include "utils/memutils.h"
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
 
 typedef struct DSMRegistryCtxStruct
 {
@@ -198,3 +202,82 @@ GetNamedDSMSegment(const char *name, size_t size,
 
 	return ret;
 }
+
+void
+iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg);
+void
+iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg)
+{
+	DSMRegistryEntry *entry;
+	dshash_seq_status status;
+	/* Ensure DSM registry is initialized */
+	init_dsm_registry();
+
+	/* Use non-exclusive access to avoid blocking other backends */
+	dshash_seq_init(&status, dsm_registry_table, false);
+	while ((entry = dshash_seq_next(&status)) != NULL)
+		callback(entry, arg);
+	dshash_seq_term(&status);
+}
+
+/* SQL SRF showing DSM registry allocated memory */
+PG_FUNCTION_INFO_V1(pg_get_dsm_registry);
+
+typedef struct
+{
+	Tuplestorestate *tupstore;
+	TupleDesc        tupdesc;
+} DSMRegistrySRFContext;
+
+static void
+collect_dsm_registry(DSMRegistryEntry *entry, void *arg)
+{
+	DSMRegistrySRFContext *ctx = (DSMRegistrySRFContext *) arg;
+	Datum values[2];
+	bool nulls[2] = {false, false};
+
+	values[0] = CStringGetTextDatum(entry->name);
+	values[1] = Int64GetDatum(entry->size);
+
+	tuplestore_putvalues(ctx->tupstore, ctx->tupdesc, values, nulls);
+}
+
+Datum
+pg_get_dsm_registry(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+	TupleDesc tupdesc;
+	Tuplestorestate *tupstore;
+	DSMRegistrySRFContext ctx;
+
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR, (errmsg("pg_get_dsm_registry must be used in a SRF context")));
+
+	/* Set up tuple descriptor */
+	tupdesc = CreateTemplateTupleDesc(2);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "size", INT8OID, -1, 0);
+
+	/* Switch to per-query memory context */
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	/* Initialize tuplestore */
+	tupstore = tuplestore_begin_heap(false, false, work_mem);
+
+	ctx.tupstore = tupstore;
+	ctx.tupdesc = tupdesc;
+
+	/* Collect registry data */
+	iterate_dsm_registry(collect_dsm_registry, &ctx);
+
+	/* Switch back and return results */
+	MemoryContextSwitchTo(oldcontext);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 890822eaf79..4b91855f08c 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8491,6 +8491,12 @@
   proallargtypes => '{text,int8,int8,int8}', proargmodes => '{o,o,o,o}',
   proargnames => '{name,off,size,allocated_size}',
   prosrc => 'pg_get_shmem_allocations' },
+{ oid => '6062', descr => 'dsm registry allocations',
+  proname => 'pg_get_dsm_registry', prorows => '50', proretset => 't',
+  provolatile => 'v', prorettype => 'record', proargtypes => '',
+  proallargtypes => '{text,int8}', proargmodes => '{o,o}',
+  proargnames => '{name,size}',
+  prosrc => 'pg_get_dsm_registry' },
 
 # memory context of local backend
 { oid => '2282',
diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
index 8ffbd343a05..229abf926b6 100644
--- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
+++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
@@ -12,3 +12,10 @@ SELECT get_val_in_shmem();
              1236
 (1 row)
 
+-- 20 bytes = int (4 bytes) + LWLock (16bytes)
+SELECT * FROM pg_dsm_registry;
+       name        | size 
+-------------------+------
+ test_dsm_registry |   20
+(1 row)
+
diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
index b3351be0a16..aad402b5e64 100644
--- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
+++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
@@ -2,3 +2,6 @@ CREATE EXTENSION test_dsm_registry;
 SELECT set_val_in_shmem(1236);
 \c
 SELECT get_val_in_shmem();
+
+-- 20 bytes = int (4 bytes) + LWLock (16bytes)
+SELECT * FROM pg_dsm_registry;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 62f69ac20b2..fc35de68dae 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1324,6 +1324,9 @@ pg_cursors| SELECT name,
     is_scrollable,
     creation_time
    FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
+pg_dsm_registry| SELECT name,
+    size
+   FROM pg_get_dsm_registry() pg_get_dsm_registry(name, size);
 pg_file_settings| SELECT sourcefile,
     sourceline,
     seqno,
-- 
2.48.1

#5Sungwoo Chang
swchangdev@gmail.com
In reply to: Florents Tselai (#4)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

Hi,

I made a patch that adds Detach and Destroy functions for dsm registry.

https://commitfest.postgresql.org/patch/5654

I was thinking, given the forementioned patch is reviewed and merged, it
would be nice to add SQL command that allows manually detach or destroy the
dsm registry entry.

On that note, the attributes you mentioned (backend_pid, is_pinned, and
created_at) will be nice information to have for users to decide which
entry to detach or destroy. Especially, is_pinned will be crucial because
you cannot unpin the dsm segment twice.

Best regards,
Sungwoo Chang

2025년 3월 15일 (토) 17:42, Florents Tselai <florents.tselai@gmail.com>님이 작성:

Show quoted text

On Fri, Mar 14, 2025 at 11:44 PM Florents Tselai <
florents.tselai@gmail.com> wrote:

On Fri, Mar 14, 2025 at 4:16 PM Nathan Bossart <nathandbossart@gmail.com>
wrote:

On Thu, Mar 13, 2025 at 06:54:09PM +0200, Florents Tselai wrote:

I扉e been working with the DSM registry API.
I was wondering if it is possible (it doesn愒 look like it) or if it

has been discussed:

can we expose a view like pg_shmem_allocations, but fine-grained for

every named segment (i.e. created by GetNamedDSMSegment )?

Currently, there is a "DSM Registry Data" entry in that view,
but iiuc, it愀 only about the top-level hash table the registry uses.

This seems like a generally reasonable idea to me. In theory, it should
be
easy enough to build something that walks through the DSM registry hash
table.

Here's a first attempt towards a view pg_dsm_registry(name, size) that
does just that
So, using the test_dsm_registry module as a test bed,

it would look like this.

CREATE EXTENSION test_dsm_registry;
SELECT set_val_in_shmem(1236);
set_val_in_shmem
------------------

(1 row)

-- 20 bytes = int (4 bytes) + LWLock (16bytes)
SELECT * FROM pg_dsm_registry;
name | size
-------------------+------
test_dsm_registry | 20
(1 row)

I'll create a cf entry to keep track of this

Here's an updated v3 that fixes some white spaces v2 had—no other changes.

I'm wondering though if it also makes sense to expose:
- backend_pid (who created the segment)
- is_pinned bool
- created_at

https://commitfest.postgresql.org/patch/5652/

#6Florents Tselai
florents.tselai@gmail.com
In reply to: Sungwoo Chang (#5)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On Wed, Mar 19, 2025, 05:46 Sungwoo Chang <swchangdev@gmail.com> wrote:

Hi,

I made a patch that adds Detach and Destroy functions for dsm registry.

https://commitfest.postgresql.org/patch/5654

Yes, I've been following that thread.
I find it useful too

I was thinking, given the forementioned patch is reviewed and merged, it
would be nice to add SQL command that allows manually detach or destroy the
dsm registry entry.

Not sure I agree with this.
Having some insight into what's going on in shmem it's useful I think;
But exposing detach / destroy at the query level... I don't think so.
Unless there's a compelling story I can't think of .

Show quoted text

On that note, the attributes you mentioned (backend_pid, is_pinned, and
created_at) will be nice information to have for users to decide which
entry to detach or destroy. Especially, is_pinned will be crucial because
you cannot unpin the dsm segment twice.

Best regards,
Sungwoo Chang

2025년 3월 15일 (토) 17:42, Florents Tselai <florents.tselai@gmail.com>님이 작성:

On Fri, Mar 14, 2025 at 11:44 PM Florents Tselai <
florents.tselai@gmail.com> wrote:

On Fri, Mar 14, 2025 at 4:16 PM Nathan Bossart <nathandbossart@gmail.com>
wrote:

On Thu, Mar 13, 2025 at 06:54:09PM +0200, Florents Tselai wrote:

I扉e been working with the DSM registry API.
I was wondering if it is possible (it doesn愒 look like it) or if it

has been discussed:

can we expose a view like pg_shmem_allocations, but fine-grained for

every named segment (i.e. created by GetNamedDSMSegment )?

Currently, there is a "DSM Registry Data" entry in that view,
but iiuc, it愀 only about the top-level hash table the registry uses.

This seems like a generally reasonable idea to me. In theory, it
should be
easy enough to build something that walks through the DSM registry hash
table.

Here's a first attempt towards a view pg_dsm_registry(name, size) that
does just that
So, using the test_dsm_registry module as a test bed,

it would look like this.

CREATE EXTENSION test_dsm_registry;
SELECT set_val_in_shmem(1236);
set_val_in_shmem
------------------

(1 row)

-- 20 bytes = int (4 bytes) + LWLock (16bytes)
SELECT * FROM pg_dsm_registry;
name | size
-------------------+------
test_dsm_registry | 20
(1 row)

I'll create a cf entry to keep track of this

Here's an updated v3 that fixes some white spaces v2 had—no other changes.

I'm wondering though if it also makes sense to expose:
- backend_pid (who created the segment)
- is_pinned bool
- created_at

https://commitfest.postgresql.org/patch/5652/

#7Sungwoo Chang
swchangdev@gmail.com
In reply to: Florents Tselai (#6)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

Well, by exposing detach/destory functions at the query level, users will
be able to manage dangling shmem in dsm registry if for some reason you
cannot access the segment anymore. Otherwise, if a shmem segment becomes
orphaned, the only thing you can do to clear the shmem resource is
restarting the whole instance. It will have the same functionality as
pg_backend_shutdown in a sense that you won't need to call it unless
something goes wrong.

Having said that, I admit it's not urgent to implement t the commands yet.
Let's wait till the patches are all reviewed and merged, and we can
continue the discussion from that point on.

Best regards,

Sungwoo Chang

2025년 3월 19일 (수) 14:43, Florents Tselai <florents.tselai@gmail.com>님이 작성:

Show quoted text

On Wed, Mar 19, 2025, 05:46 Sungwoo Chang <swchangdev@gmail.com> wrote:

Hi,

I made a patch that adds Detach and Destroy functions for dsm registry.

https://commitfest.postgresql.org/patch/5654

Yes, I've been following that thread.
I find it useful too

I was thinking, given the forementioned patch is reviewed and merged, it
would be nice to add SQL command that allows manually detach or destroy the
dsm registry entry.

Not sure I agree with this.
Having some insight into what's going on in shmem it's useful I think;
But exposing detach / destroy at the query level... I don't think so.
Unless there's a compelling story I can't think of .

On that note, the attributes you mentioned (backend_pid, is_pinned, and
created_at) will be nice information to have for users to decide which
entry to detach or destroy. Especially, is_pinned will be crucial because
you cannot unpin the dsm segment twice.

Best regards,
Sungwoo Chang

2025년 3월 15일 (토) 17:42, Florents Tselai <florents.tselai@gmail.com>님이 작성:

On Fri, Mar 14, 2025 at 11:44 PM Florents Tselai <
florents.tselai@gmail.com> wrote:

On Fri, Mar 14, 2025 at 4:16 PM Nathan Bossart <
nathandbossart@gmail.com> wrote:

On Thu, Mar 13, 2025 at 06:54:09PM +0200, Florents Tselai wrote:

I扉e been working with the DSM registry API.
I was wondering if it is possible (it doesn愒 look like it) or if it

has been discussed:

can we expose a view like pg_shmem_allocations, but fine-grained for

every named segment (i.e. created by GetNamedDSMSegment )?

Currently, there is a "DSM Registry Data" entry in that view,
but iiuc, it愀 only about the top-level hash table the registry uses.

This seems like a generally reasonable idea to me. In theory, it
should be
easy enough to build something that walks through the DSM registry hash
table.

Here's a first attempt towards a view pg_dsm_registry(name, size) that
does just that
So, using the test_dsm_registry module as a test bed,

it would look like this.

CREATE EXTENSION test_dsm_registry;
SELECT set_val_in_shmem(1236);
set_val_in_shmem
------------------

(1 row)

-- 20 bytes = int (4 bytes) + LWLock (16bytes)
SELECT * FROM pg_dsm_registry;
name | size
-------------------+------
test_dsm_registry | 20
(1 row)

I'll create a cf entry to keep track of this

Here's an updated v3 that fixes some white spaces v2 had—no other
changes.

I'm wondering though if it also makes sense to expose:
- backend_pid (who created the segment)
- is_pinned bool
- created_at

https://commitfest.postgresql.org/patch/5652/

#8Nathan Bossart
nathandbossart@gmail.com
In reply to: Florents Tselai (#4)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On Sat, Mar 15, 2025 at 10:41:15AM +0200, Florents Tselai wrote:

Here's an updated v3 that fixes some white spaces v2 had-no other changes.

Thanks for the patch.

I'm wondering though if it also makes sense to expose:
- backend_pid (who created the segment)
- is_pinned bool
- created_at

created_at might be interesting. I'm not sure the others have much use.
It would be cool to surface which library/function created the segment
IMHO. But for now, I'd keep the view simple and just show the contents of
the DSM registry's hash table.

+CREATE VIEW pg_dsm_registry AS
+SELECT * FROM pg_get_dsm_registry();

I'd suggest pg_dsm_registry_allocations and
pg_get_dsm_registry_allocations() to match pg_shmem_allocations.

+void
+iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg);
+void
+iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg)
+{

I'm not sure what's going on here. Presumably we could just mark
iterate_dsm_registry() as static.

Taking a step back, I think the loop is quite overengineered. You have a
function for calling dshash_seq_init/next/term, a callback function for
storing the tuple, and a special struct for some SRF context. I don't see
any need to abstract things to this extent. I think we should instead
open-code the loop in pg_get_dsm_registry().

+/* SQL SRF showing DSM registry allocated memory */
+PG_FUNCTION_INFO_V1(pg_get_dsm_registry);

There should be no need to do this. Its pg_proc.dat entry will
automatically generate the required prototype.

+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR, (errmsg("pg_get_dsm_registry must be used in a SRF context")));
+
+	/* Set up tuple descriptor */
+	tupdesc = CreateTemplateTupleDesc(2);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "size", INT8OID, -1, 0);

This SRF-related code can be simplified by using InitMaterializedSRF() and
friends. Check out pg_ls_dir() in genfile.c for an example.

+-- 20 bytes = int (4 bytes) + LWLock (16bytes)
+SELECT * FROM pg_dsm_registry;
+       name        | size 
+-------------------+------
+ test_dsm_registry |   20
+(1 row)

I'm a little worried about the portability of this test. I would suggest
changing the query to something like

SELECT size > 0 FROM pg_dsm_registry WHERE name = 'test_dsm_registry';

--
nathan

#9Florents Tselai
florents.tselai@gmail.com
In reply to: Nathan Bossart (#8)
3 attachment(s)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

Thanks for the detailed review Nathan

On 3 Jun 2025, at 4:52 PM, Nathan Bossart <nathandbossart@gmail.com> wrote:

On Sat, Mar 15, 2025 at 10:41:15AM +0200, Florents Tselai wrote:

Here's an updated v3 that fixes some white spaces v2 had-no other changes.

Thanks for the patch.

I'm wondering though if it also makes sense to expose:
- backend_pid (who created the segment)
- is_pinned bool
- created_at

created_at might be interesting. I'm not sure the others have much use.
It would be cool to surface which library/function created the segment
IMHO. But for now, I'd keep the view simple and just show the contents of
the DSM registry's hash table.

+CREATE VIEW pg_dsm_registry AS
+SELECT * FROM pg_get_dsm_registry();

I'd suggest pg_dsm_registry_allocations and
pg_get_dsm_registry_allocations() to match pg_shmem_allocations.

+void
+iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg);
+void
+iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg)
+{

I'm not sure what's going on here. Presumably we could just mark
iterate_dsm_registry() as static.

Taking a step back, I think the loop is quite overengineered. You have a
function for calling dshash_seq_init/next/term, a callback function for
storing the tuple, and a special struct for some SRF context. I don't see
any need to abstract things to this extent. I think we should instead
open-code the loop in pg_get_dsm_registry().

+/* SQL SRF showing DSM registry allocated memory */
+PG_FUNCTION_INFO_V1(pg_get_dsm_registry);

There should be no need to do this. Its pg_proc.dat entry will
automatically generate the required prototype.

+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR, (errmsg("pg_get_dsm_registry must be used in a SRF context")));
+
+	/* Set up tuple descriptor */
+	tupdesc = CreateTemplateTupleDesc(2);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "size", INT8OID, -1, 0);

This SRF-related code can be simplified by using InitMaterializedSRF() and
friends. Check out pg_ls_dir() in genfile.c for an example.

+-- 20 bytes = int (4 bytes) + LWLock (16bytes)
+SELECT * FROM pg_dsm_registry;
+       name        | size 
+-------------------+------
+ test_dsm_registry |   20
+(1 row)

I'm a little worried about the portability of this test. I would suggest
changing the query to something like

SELECT size > 0 FROM pg_dsm_registry WHERE name = 'test_dsm_registry';

--
nathan

Attaching a v4 which resolves these and also adds a doc entry.

Attachments:

v4-0001-First-attempt-towards-a-pg_dsm_registry-view.-Ite.patchapplication/octet-stream; name=v4-0001-First-attempt-towards-a-pg_dsm_registry-view.-Ite.patch; x-unix-mode=0644Download
From 48d57b5608899d894e9a3385ac99682357bee6e6 Mon Sep 17 00:00:00 2001
From: Florents Tselai <florents.tselai@gmail.com>
Date: Sat, 15 Mar 2025 10:21:34 +0200
Subject: [PATCH v4 1/3] First attempt towards a pg_dsm_registry view. Iterates
 over the registry hash table entries and reports the name and size.

# Conflicts:
#	src/backend/catalog/system_views.sql
---
 src/backend/catalog/system_views.sql          |  8 ++
 src/backend/storage/ipc/dsm_registry.c        | 83 +++++++++++++++++++
 src/include/catalog/pg_proc.dat               |  6 ++
 .../expected/test_dsm_registry.out            |  7 ++
 .../sql/test_dsm_registry.sql                 |  3 +
 src/test/regress/expected/rules.out           |  3 +
 6 files changed, 110 insertions(+)

diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 08f780a2e63..7c2246999ec 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -666,6 +666,14 @@ GRANT SELECT ON pg_shmem_allocations_numa TO pg_read_all_stats;
 REVOKE EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() FROM PUBLIC;
 GRANT EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() TO pg_read_all_stats;
 
+CREATE VIEW pg_dsm_registry AS
+SELECT * FROM pg_get_dsm_registry();
+
+REVOKE ALL ON pg_dsm_registry FROM PUBLIC;
+GRANT SELECT ON pg_dsm_registry TO pg_read_all_stats;
+REVOKE EXECUTE ON FUNCTION pg_get_dsm_registry() FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION pg_get_dsm_registry() TO pg_read_all_stats;
+
 CREATE VIEW pg_backend_memory_contexts AS
     SELECT * FROM pg_get_backend_memory_contexts();
 
diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 1d4fd31ffed..549cbe07a28 100644
--- a/src/backend/storage/ipc/dsm_registry.c
+++ b/src/backend/storage/ipc/dsm_registry.c
@@ -31,6 +31,10 @@
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
 #include "utils/memutils.h"
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
 
 typedef struct DSMRegistryCtxStruct
 {
@@ -198,3 +202,82 @@ GetNamedDSMSegment(const char *name, size_t size,
 
 	return ret;
 }
+
+void
+iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg);
+void
+iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg)
+{
+	DSMRegistryEntry *entry;
+	dshash_seq_status status;
+	/* Ensure DSM registry is initialized */
+	init_dsm_registry();
+
+	/* Use non-exclusive access to avoid blocking other backends */
+	dshash_seq_init(&status, dsm_registry_table, false);
+	while ((entry = dshash_seq_next(&status)) != NULL)
+		callback(entry, arg);
+	dshash_seq_term(&status);
+}
+
+/* SQL SRF showing DSM registry allocated memory */
+PG_FUNCTION_INFO_V1(pg_get_dsm_registry);
+
+typedef struct
+{
+	Tuplestorestate *tupstore;
+	TupleDesc        tupdesc;
+} DSMRegistrySRFContext;
+
+static void
+collect_dsm_registry(DSMRegistryEntry *entry, void *arg)
+{
+	DSMRegistrySRFContext *ctx = (DSMRegistrySRFContext *) arg;
+	Datum values[2];
+	bool nulls[2] = {false, false};
+
+	values[0] = CStringGetTextDatum(entry->name);
+	values[1] = Int64GetDatum(entry->size);
+
+	tuplestore_putvalues(ctx->tupstore, ctx->tupdesc, values, nulls);
+}
+
+Datum
+pg_get_dsm_registry(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+	TupleDesc tupdesc;
+	Tuplestorestate *tupstore;
+	DSMRegistrySRFContext ctx;
+
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR, (errmsg("pg_get_dsm_registry must be used in a SRF context")));
+
+	/* Set up tuple descriptor */
+	tupdesc = CreateTemplateTupleDesc(2);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "size", INT8OID, -1, 0);
+
+	/* Switch to per-query memory context */
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	/* Initialize tuplestore */
+	tupstore = tuplestore_begin_heap(false, false, work_mem);
+
+	ctx.tupstore = tupstore;
+	ctx.tupdesc = tupdesc;
+
+	/* Collect registry data */
+	iterate_dsm_registry(collect_dsm_registry, &ctx);
+
+	/* Switch back and return results */
+	MemoryContextSwitchTo(oldcontext);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d3d28a263fa..aa3fac2afb8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8541,6 +8541,12 @@
   proallargtypes => '{text,int8,int8,int8}', proargmodes => '{o,o,o,o}',
   proargnames => '{name,off,size,allocated_size}',
   prosrc => 'pg_get_shmem_allocations' },
+{ oid => '6062', descr => 'dsm registry allocations',
+  proname => 'pg_get_dsm_registry', prorows => '50', proretset => 't',
+  provolatile => 'v', prorettype => 'record', proargtypes => '',
+  proallargtypes => '{text,int8}', proargmodes => '{o,o}',
+  proargnames => '{name,size}',
+  prosrc => 'pg_get_dsm_registry' },
 
 { oid => '4099', descr => 'Is NUMA support available?',
   proname => 'pg_numa_available', provolatile => 's', prorettype => 'bool',
diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
index 8ffbd343a05..229abf926b6 100644
--- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
+++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
@@ -12,3 +12,10 @@ SELECT get_val_in_shmem();
              1236
 (1 row)
 
+-- 20 bytes = int (4 bytes) + LWLock (16bytes)
+SELECT * FROM pg_dsm_registry;
+       name        | size 
+-------------------+------
+ test_dsm_registry |   20
+(1 row)
+
diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
index b3351be0a16..aad402b5e64 100644
--- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
+++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
@@ -2,3 +2,6 @@ CREATE EXTENSION test_dsm_registry;
 SELECT set_val_in_shmem(1236);
 \c
 SELECT get_val_in_shmem();
+
+-- 20 bytes = int (4 bytes) + LWLock (16bytes)
+SELECT * FROM pg_dsm_registry;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 6cf828ca8d0..ec257f93398 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1340,6 +1340,9 @@ pg_cursors| SELECT name,
     is_scrollable,
     creation_time
    FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
+pg_dsm_registry| SELECT name,
+    size
+   FROM pg_get_dsm_registry() pg_get_dsm_registry(name, size);
 pg_file_settings| SELECT sourcefile,
     sourceline,
     seqno,
-- 
2.49.0

v4-0002-Rename-view-to-pg_dsm_registry_allocations-and-fu.patchapplication/octet-stream; name=v4-0002-Rename-view-to-pg_dsm_registry_allocations-and-fu.patch; x-unix-mode=0644Download
From 59ade2d443da696d1a7dc6a25f73ad4a0357e35b Mon Sep 17 00:00:00 2001
From: Florents Tselai <florents.tselai@gmail.com>
Date: Tue, 3 Jun 2025 22:30:33 +0300
Subject: [PATCH v4 2/3] * Rename view to pg_dsm_registry_allocations and
 function to pg_get_dsm_registry_allocations * Simplify implementation for
 pg_get_dsm_registry_allocations * Simplify test

---
 src/backend/catalog/system_views.sql          | 12 +--
 src/backend/storage/ipc/dsm_registry.c        | 78 +++++--------------
 src/include/catalog/pg_proc.dat               |  4 +-
 .../expected/test_dsm_registry.out            |  9 +--
 .../sql/test_dsm_registry.sql                 |  3 +-
 src/test/regress/expected/rules.out           |  4 +-
 6 files changed, 35 insertions(+), 75 deletions(-)

diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 7c2246999ec..64e62732f18 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -666,13 +666,13 @@ GRANT SELECT ON pg_shmem_allocations_numa TO pg_read_all_stats;
 REVOKE EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() FROM PUBLIC;
 GRANT EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() TO pg_read_all_stats;
 
-CREATE VIEW pg_dsm_registry AS
-SELECT * FROM pg_get_dsm_registry();
+CREATE VIEW pg_dsm_registry_allocations AS
+SELECT * FROM pg_get_dsm_registry_allocations();
 
-REVOKE ALL ON pg_dsm_registry FROM PUBLIC;
-GRANT SELECT ON pg_dsm_registry TO pg_read_all_stats;
-REVOKE EXECUTE ON FUNCTION pg_get_dsm_registry() FROM PUBLIC;
-GRANT EXECUTE ON FUNCTION pg_get_dsm_registry() TO pg_read_all_stats;
+REVOKE ALL ON pg_dsm_registry_allocations FROM PUBLIC;
+GRANT SELECT ON pg_dsm_registry_allocations TO pg_read_all_stats;
+REVOKE EXECUTE ON FUNCTION pg_get_dsm_registry_allocations() FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION pg_get_dsm_registry_allocations() TO pg_read_all_stats;
 
 CREATE VIEW pg_backend_memory_contexts AS
     SELECT * FROM pg_get_backend_memory_contexts();
diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 549cbe07a28..33e3d9ba3f0 100644
--- a/src/backend/storage/ipc/dsm_registry.c
+++ b/src/backend/storage/ipc/dsm_registry.c
@@ -203,81 +203,43 @@ GetNamedDSMSegment(const char *name, size_t size,
 	return ret;
 }
 
-void
-iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg);
-void
-iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg)
-{
-	DSMRegistryEntry *entry;
-	dshash_seq_status status;
-	/* Ensure DSM registry is initialized */
-	init_dsm_registry();
-
-	/* Use non-exclusive access to avoid blocking other backends */
-	dshash_seq_init(&status, dsm_registry_table, false);
-	while ((entry = dshash_seq_next(&status)) != NULL)
-		callback(entry, arg);
-	dshash_seq_term(&status);
-}
-
-/* SQL SRF showing DSM registry allocated memory */
-PG_FUNCTION_INFO_V1(pg_get_dsm_registry);
-
 typedef struct
 {
 	Tuplestorestate *tupstore;
 	TupleDesc        tupdesc;
 } DSMRegistrySRFContext;
 
-static void
-collect_dsm_registry(DSMRegistryEntry *entry, void *arg)
-{
-	DSMRegistrySRFContext *ctx = (DSMRegistrySRFContext *) arg;
-	Datum values[2];
-	bool nulls[2] = {false, false};
-
-	values[0] = CStringGetTextDatum(entry->name);
-	values[1] = Int64GetDatum(entry->size);
-
-	tuplestore_putvalues(ctx->tupstore, ctx->tupdesc, values, nulls);
-}
-
 Datum
-pg_get_dsm_registry(PG_FUNCTION_ARGS)
+pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS)
 {
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
-	MemoryContext per_query_ctx;
-	MemoryContext oldcontext;
-	TupleDesc tupdesc;
-	Tuplestorestate *tupstore;
-	DSMRegistrySRFContext ctx;
+	DSMRegistryEntry *entry;
+	dshash_seq_status status;
 
 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-		ereport(ERROR, (errmsg("pg_get_dsm_registry must be used in a SRF context")));
+		ereport(ERROR, (errmsg("pg_get_dsm_registry_allocations must be used in a SRF context")));
+
+	InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
 
-	/* Set up tuple descriptor */
-	tupdesc = CreateTemplateTupleDesc(2);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "size", INT8OID, -1, 0);
+	/* Ensure DSM registry initialized */
+	init_dsm_registry();
 
-	/* Switch to per-query memory context */
-	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
-	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+	/* Use non-exclusive access to avoid blocking other backends */
+	dshash_seq_init(&status, dsm_registry_table, false);
 
-	/* Initialize tuplestore */
-	tupstore = tuplestore_begin_heap(false, false, work_mem);
+	while ((entry = dshash_seq_next(&status)) != NULL)
+	{
+		Datum values[2];
+		bool nulls[2] = {false, false};
 
-	ctx.tupstore = tupstore;
-	ctx.tupdesc = tupdesc;
+		values[0] = CStringGetTextDatum(entry->name);
+		values[1] = Int64GetDatum(entry->size);
 
-	/* Collect registry data */
-	iterate_dsm_registry(collect_dsm_registry, &ctx);
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+							 values, nulls);
+	}
 
-	/* Switch back and return results */
-	MemoryContextSwitchTo(oldcontext);
-	rsinfo->returnMode = SFRM_Materialize;
-	rsinfo->setResult = tupstore;
-	rsinfo->setDesc = tupdesc;
+	dshash_seq_term(&status);
 
 	return (Datum) 0;
 }
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index aa3fac2afb8..bef92be8250 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8542,11 +8542,11 @@
   proargnames => '{name,off,size,allocated_size}',
   prosrc => 'pg_get_shmem_allocations' },
 { oid => '6062', descr => 'dsm registry allocations',
-  proname => 'pg_get_dsm_registry', prorows => '50', proretset => 't',
+  proname => 'pg_get_dsm_registry_allocations', prorows => '50', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
   proallargtypes => '{text,int8}', proargmodes => '{o,o}',
   proargnames => '{name,size}',
-  prosrc => 'pg_get_dsm_registry' },
+  prosrc => 'pg_get_dsm_registry_allocations' },
 
 { oid => '4099', descr => 'Is NUMA support available?',
   proname => 'pg_numa_available', provolatile => 's', prorettype => 'bool',
diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
index 229abf926b6..3c279378b5b 100644
--- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
+++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
@@ -12,10 +12,9 @@ SELECT get_val_in_shmem();
              1236
 (1 row)
 
--- 20 bytes = int (4 bytes) + LWLock (16bytes)
-SELECT * FROM pg_dsm_registry;
-       name        | size 
--------------------+------
- test_dsm_registry |   20
+SELECT size > 0 FROM pg_dsm_registry_allocations WHERE name = 'test_dsm_registry';
+ ?column? 
+----------
+ t
 (1 row)
 
diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
index aad402b5e64..d27ee2e6a1a 100644
--- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
+++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
@@ -3,5 +3,4 @@ SELECT set_val_in_shmem(1236);
 \c
 SELECT get_val_in_shmem();
 
--- 20 bytes = int (4 bytes) + LWLock (16bytes)
-SELECT * FROM pg_dsm_registry;
+SELECT size > 0 FROM pg_dsm_registry_allocations WHERE name = 'test_dsm_registry';
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index ec257f93398..893c5b2b933 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1340,9 +1340,9 @@ pg_cursors| SELECT name,
     is_scrollable,
     creation_time
    FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
-pg_dsm_registry| SELECT name,
+pg_dsm_registry_allocations| SELECT name,
     size
-   FROM pg_get_dsm_registry() pg_get_dsm_registry(name, size);
+   FROM pg_get_dsm_registry_allocations() pg_get_dsm_registry_allocations(name, size);
 pg_file_settings| SELECT sourcefile,
     sourceline,
     seqno,
-- 
2.49.0

v4-0003-Add-doc-entry-for-pg_shmem_allocations_numa.patchapplication/octet-stream; name=v4-0003-Add-doc-entry-for-pg_shmem_allocations_numa.patch; x-unix-mode=0644Download
From ee9d5aa8a77bdc9a23ffa5cab0cf8f8e74f6e40c Mon Sep 17 00:00:00 2001
From: Florents Tselai <florents.tselai@gmail.com>
Date: Tue, 3 Jun 2025 22:37:51 +0300
Subject: [PATCH v4 3/3] Add doc entry for pg_shmem_allocations_numa

---
 doc/src/sgml/system-views.sgml | 63 ++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index b58c52ea50f..2d55671f5fa 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -4146,6 +4146,69 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
   </para>
  </sect1>
 
+ <sect1 id="view-pg-dsm-registry-allocations">
+  <title><structname>pg_dsm_registry_allocations</structname></title>
+
+  <indexterm zone="view-pg-dsm-registry-allocations">
+   <primary>pg_dsm_registry_allocations</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_dsm_registry_allocations</structname> view shows current
+   allocations registered in the dynamic shared memory (DSM) registry. This
+   includes all DSM segments that have been allocated and tracked by
+   <productname>PostgreSQL</productname> during the server's operation.
+  </para>
+
+  <para>
+   This view is useful for monitoring DSM usage by extensions or internal
+   components using dynamic shared memory segments.
+  </para>
+
+  <table>
+   <title><structname>pg_dsm_registry_allocations</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>
+        The name of the DSM allocation as registered in the DSM registry.
+       </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>size</structfield> <type>int8</type>
+      </para>
+       <para>
+        The size of the allocated dynamic shared memory segment in bytes.
+       </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   By default, access to the <structname>pg_dsm_registry_allocations</structname>
+   view is restricted to superusers or roles granted the
+   <literal>pg_read_all_stats</literal> role due to the sensitive nature
+   of dynamic shared memory usage information.
+  </para>
+ </sect1>
+
  <sect1 id="view-pg-stats">
   <title><structname>pg_stats</structname></title>
 
-- 
2.49.0

#10Nathan Bossart
nathandbossart@gmail.com
In reply to: Florents Tselai (#9)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On Tue, Jun 03, 2025 at 10:39:25PM +0300, Florents Tselai wrote:

Thanks for the detailed review Nathan

Thanks for the updated patch!

+    if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+        ereport(ERROR, (errmsg("pg_get_dsm_registry_allocations must be used in a SRF context")));

InitMaterializedSRF() takes care of this for you.

+typedef struct
+{
+    Tuplestorestate *tupstore;
+    TupleDesc        tupdesc;
+} DSMRegistrySRFContext;

This appears to be unused.

+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"

Do we need fmgr.h and miscadmin.h? Also, please alphabetize these into the
existing list of #includes.

+ values[1] = Int64GetDatum(entry->size);

I think there's a sign mismatch problem here, but it seems implausible in
practice. pg_get_shmem_allocations() does the same thing.

+ <sect1 id="view-pg-dsm-registry-allocations">
+  <title><structname>pg_dsm_registry_allocations</structname></title>

We need to add an entry into the System Views table in the Overview page,
too.

--
nathan

#11Florents Tselai
florents.tselai@gmail.com
In reply to: Nathan Bossart (#10)
4 attachment(s)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?
Show quoted text

On 3 Jun 2025, at 10:53 PM, Nathan Bossart <nathandbossart@gmail.com> wrote:

On Tue, Jun 03, 2025 at 10:39:25PM +0300, Florents Tselai wrote:

Thanks for the detailed review Nathan

Thanks for the updated patch!

+    if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+        ereport(ERROR, (errmsg("pg_get_dsm_registry_allocations must be used in a SRF context")));

InitMaterializedSRF() takes care of this for you.

+typedef struct
+{
+    Tuplestorestate *tupstore;
+    TupleDesc        tupdesc;
+} DSMRegistrySRFContext;

This appears to be unused.

+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"

Do we need fmgr.h and miscadmin.h? Also, please alphabetize these into the
existing list of #includes.

+ values[1] = Int64GetDatum(entry->size);

I think there's a sign mismatch problem here, but it seems implausible in
practice. pg_get_shmem_allocations() does the same thing.

+ <sect1 id="view-pg-dsm-registry-allocations">
+  <title><structname>pg_dsm_registry_allocations</structname></title>

We need to add an entry into the System Views table in the Overview page,
too.

--
nathan

Attachments:

v4-0001-First-attempt-towards-a-pg_dsm_registry-view.-Ite.patchapplication/octet-stream; name=v4-0001-First-attempt-towards-a-pg_dsm_registry-view.-Ite.patch; x-unix-mode=0644Download
From 48d57b5608899d894e9a3385ac99682357bee6e6 Mon Sep 17 00:00:00 2001
From: Florents Tselai <florents.tselai@gmail.com>
Date: Sat, 15 Mar 2025 10:21:34 +0200
Subject: [PATCH v4 1/4] First attempt towards a pg_dsm_registry view. Iterates
 over the registry hash table entries and reports the name and size.

# Conflicts:
#	src/backend/catalog/system_views.sql
---
 src/backend/catalog/system_views.sql          |  8 ++
 src/backend/storage/ipc/dsm_registry.c        | 83 +++++++++++++++++++
 src/include/catalog/pg_proc.dat               |  6 ++
 .../expected/test_dsm_registry.out            |  7 ++
 .../sql/test_dsm_registry.sql                 |  3 +
 src/test/regress/expected/rules.out           |  3 +
 6 files changed, 110 insertions(+)

diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 08f780a2e63..7c2246999ec 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -666,6 +666,14 @@ GRANT SELECT ON pg_shmem_allocations_numa TO pg_read_all_stats;
 REVOKE EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() FROM PUBLIC;
 GRANT EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() TO pg_read_all_stats;
 
+CREATE VIEW pg_dsm_registry AS
+SELECT * FROM pg_get_dsm_registry();
+
+REVOKE ALL ON pg_dsm_registry FROM PUBLIC;
+GRANT SELECT ON pg_dsm_registry TO pg_read_all_stats;
+REVOKE EXECUTE ON FUNCTION pg_get_dsm_registry() FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION pg_get_dsm_registry() TO pg_read_all_stats;
+
 CREATE VIEW pg_backend_memory_contexts AS
     SELECT * FROM pg_get_backend_memory_contexts();
 
diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 1d4fd31ffed..549cbe07a28 100644
--- a/src/backend/storage/ipc/dsm_registry.c
+++ b/src/backend/storage/ipc/dsm_registry.c
@@ -31,6 +31,10 @@
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
 #include "utils/memutils.h"
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
 
 typedef struct DSMRegistryCtxStruct
 {
@@ -198,3 +202,82 @@ GetNamedDSMSegment(const char *name, size_t size,
 
 	return ret;
 }
+
+void
+iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg);
+void
+iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg)
+{
+	DSMRegistryEntry *entry;
+	dshash_seq_status status;
+	/* Ensure DSM registry is initialized */
+	init_dsm_registry();
+
+	/* Use non-exclusive access to avoid blocking other backends */
+	dshash_seq_init(&status, dsm_registry_table, false);
+	while ((entry = dshash_seq_next(&status)) != NULL)
+		callback(entry, arg);
+	dshash_seq_term(&status);
+}
+
+/* SQL SRF showing DSM registry allocated memory */
+PG_FUNCTION_INFO_V1(pg_get_dsm_registry);
+
+typedef struct
+{
+	Tuplestorestate *tupstore;
+	TupleDesc        tupdesc;
+} DSMRegistrySRFContext;
+
+static void
+collect_dsm_registry(DSMRegistryEntry *entry, void *arg)
+{
+	DSMRegistrySRFContext *ctx = (DSMRegistrySRFContext *) arg;
+	Datum values[2];
+	bool nulls[2] = {false, false};
+
+	values[0] = CStringGetTextDatum(entry->name);
+	values[1] = Int64GetDatum(entry->size);
+
+	tuplestore_putvalues(ctx->tupstore, ctx->tupdesc, values, nulls);
+}
+
+Datum
+pg_get_dsm_registry(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	MemoryContext per_query_ctx;
+	MemoryContext oldcontext;
+	TupleDesc tupdesc;
+	Tuplestorestate *tupstore;
+	DSMRegistrySRFContext ctx;
+
+	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+		ereport(ERROR, (errmsg("pg_get_dsm_registry must be used in a SRF context")));
+
+	/* Set up tuple descriptor */
+	tupdesc = CreateTemplateTupleDesc(2);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "size", INT8OID, -1, 0);
+
+	/* Switch to per-query memory context */
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	/* Initialize tuplestore */
+	tupstore = tuplestore_begin_heap(false, false, work_mem);
+
+	ctx.tupstore = tupstore;
+	ctx.tupdesc = tupdesc;
+
+	/* Collect registry data */
+	iterate_dsm_registry(collect_dsm_registry, &ctx);
+
+	/* Switch back and return results */
+	MemoryContextSwitchTo(oldcontext);
+	rsinfo->returnMode = SFRM_Materialize;
+	rsinfo->setResult = tupstore;
+	rsinfo->setDesc = tupdesc;
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d3d28a263fa..aa3fac2afb8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8541,6 +8541,12 @@
   proallargtypes => '{text,int8,int8,int8}', proargmodes => '{o,o,o,o}',
   proargnames => '{name,off,size,allocated_size}',
   prosrc => 'pg_get_shmem_allocations' },
+{ oid => '6062', descr => 'dsm registry allocations',
+  proname => 'pg_get_dsm_registry', prorows => '50', proretset => 't',
+  provolatile => 'v', prorettype => 'record', proargtypes => '',
+  proallargtypes => '{text,int8}', proargmodes => '{o,o}',
+  proargnames => '{name,size}',
+  prosrc => 'pg_get_dsm_registry' },
 
 { oid => '4099', descr => 'Is NUMA support available?',
   proname => 'pg_numa_available', provolatile => 's', prorettype => 'bool',
diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
index 8ffbd343a05..229abf926b6 100644
--- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
+++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
@@ -12,3 +12,10 @@ SELECT get_val_in_shmem();
              1236
 (1 row)
 
+-- 20 bytes = int (4 bytes) + LWLock (16bytes)
+SELECT * FROM pg_dsm_registry;
+       name        | size 
+-------------------+------
+ test_dsm_registry |   20
+(1 row)
+
diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
index b3351be0a16..aad402b5e64 100644
--- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
+++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
@@ -2,3 +2,6 @@ CREATE EXTENSION test_dsm_registry;
 SELECT set_val_in_shmem(1236);
 \c
 SELECT get_val_in_shmem();
+
+-- 20 bytes = int (4 bytes) + LWLock (16bytes)
+SELECT * FROM pg_dsm_registry;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 6cf828ca8d0..ec257f93398 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1340,6 +1340,9 @@ pg_cursors| SELECT name,
     is_scrollable,
     creation_time
    FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
+pg_dsm_registry| SELECT name,
+    size
+   FROM pg_get_dsm_registry() pg_get_dsm_registry(name, size);
 pg_file_settings| SELECT sourcefile,
     sourceline,
     seqno,
-- 
2.49.0

v4-0002-Rename-view-to-pg_dsm_registry_allocations-and-fu.patchapplication/octet-stream; name=v4-0002-Rename-view-to-pg_dsm_registry_allocations-and-fu.patch; x-unix-mode=0644Download
From 59ade2d443da696d1a7dc6a25f73ad4a0357e35b Mon Sep 17 00:00:00 2001
From: Florents Tselai <florents.tselai@gmail.com>
Date: Tue, 3 Jun 2025 22:30:33 +0300
Subject: [PATCH v4 2/4] * Rename view to pg_dsm_registry_allocations and
 function to pg_get_dsm_registry_allocations * Simplify implementation for
 pg_get_dsm_registry_allocations * Simplify test

---
 src/backend/catalog/system_views.sql          | 12 +--
 src/backend/storage/ipc/dsm_registry.c        | 78 +++++--------------
 src/include/catalog/pg_proc.dat               |  4 +-
 .../expected/test_dsm_registry.out            |  9 +--
 .../sql/test_dsm_registry.sql                 |  3 +-
 src/test/regress/expected/rules.out           |  4 +-
 6 files changed, 35 insertions(+), 75 deletions(-)

diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 7c2246999ec..64e62732f18 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -666,13 +666,13 @@ GRANT SELECT ON pg_shmem_allocations_numa TO pg_read_all_stats;
 REVOKE EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() FROM PUBLIC;
 GRANT EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() TO pg_read_all_stats;
 
-CREATE VIEW pg_dsm_registry AS
-SELECT * FROM pg_get_dsm_registry();
+CREATE VIEW pg_dsm_registry_allocations AS
+SELECT * FROM pg_get_dsm_registry_allocations();
 
-REVOKE ALL ON pg_dsm_registry FROM PUBLIC;
-GRANT SELECT ON pg_dsm_registry TO pg_read_all_stats;
-REVOKE EXECUTE ON FUNCTION pg_get_dsm_registry() FROM PUBLIC;
-GRANT EXECUTE ON FUNCTION pg_get_dsm_registry() TO pg_read_all_stats;
+REVOKE ALL ON pg_dsm_registry_allocations FROM PUBLIC;
+GRANT SELECT ON pg_dsm_registry_allocations TO pg_read_all_stats;
+REVOKE EXECUTE ON FUNCTION pg_get_dsm_registry_allocations() FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION pg_get_dsm_registry_allocations() TO pg_read_all_stats;
 
 CREATE VIEW pg_backend_memory_contexts AS
     SELECT * FROM pg_get_backend_memory_contexts();
diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 549cbe07a28..33e3d9ba3f0 100644
--- a/src/backend/storage/ipc/dsm_registry.c
+++ b/src/backend/storage/ipc/dsm_registry.c
@@ -203,81 +203,43 @@ GetNamedDSMSegment(const char *name, size_t size,
 	return ret;
 }
 
-void
-iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg);
-void
-iterate_dsm_registry(void (*callback)(DSMRegistryEntry *, void *), void *arg)
-{
-	DSMRegistryEntry *entry;
-	dshash_seq_status status;
-	/* Ensure DSM registry is initialized */
-	init_dsm_registry();
-
-	/* Use non-exclusive access to avoid blocking other backends */
-	dshash_seq_init(&status, dsm_registry_table, false);
-	while ((entry = dshash_seq_next(&status)) != NULL)
-		callback(entry, arg);
-	dshash_seq_term(&status);
-}
-
-/* SQL SRF showing DSM registry allocated memory */
-PG_FUNCTION_INFO_V1(pg_get_dsm_registry);
-
 typedef struct
 {
 	Tuplestorestate *tupstore;
 	TupleDesc        tupdesc;
 } DSMRegistrySRFContext;
 
-static void
-collect_dsm_registry(DSMRegistryEntry *entry, void *arg)
-{
-	DSMRegistrySRFContext *ctx = (DSMRegistrySRFContext *) arg;
-	Datum values[2];
-	bool nulls[2] = {false, false};
-
-	values[0] = CStringGetTextDatum(entry->name);
-	values[1] = Int64GetDatum(entry->size);
-
-	tuplestore_putvalues(ctx->tupstore, ctx->tupdesc, values, nulls);
-}
-
 Datum
-pg_get_dsm_registry(PG_FUNCTION_ARGS)
+pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS)
 {
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
-	MemoryContext per_query_ctx;
-	MemoryContext oldcontext;
-	TupleDesc tupdesc;
-	Tuplestorestate *tupstore;
-	DSMRegistrySRFContext ctx;
+	DSMRegistryEntry *entry;
+	dshash_seq_status status;
 
 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-		ereport(ERROR, (errmsg("pg_get_dsm_registry must be used in a SRF context")));
+		ereport(ERROR, (errmsg("pg_get_dsm_registry_allocations must be used in a SRF context")));
+
+	InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
 
-	/* Set up tuple descriptor */
-	tupdesc = CreateTemplateTupleDesc(2);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "size", INT8OID, -1, 0);
+	/* Ensure DSM registry initialized */
+	init_dsm_registry();
 
-	/* Switch to per-query memory context */
-	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
-	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+	/* Use non-exclusive access to avoid blocking other backends */
+	dshash_seq_init(&status, dsm_registry_table, false);
 
-	/* Initialize tuplestore */
-	tupstore = tuplestore_begin_heap(false, false, work_mem);
+	while ((entry = dshash_seq_next(&status)) != NULL)
+	{
+		Datum values[2];
+		bool nulls[2] = {false, false};
 
-	ctx.tupstore = tupstore;
-	ctx.tupdesc = tupdesc;
+		values[0] = CStringGetTextDatum(entry->name);
+		values[1] = Int64GetDatum(entry->size);
 
-	/* Collect registry data */
-	iterate_dsm_registry(collect_dsm_registry, &ctx);
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+							 values, nulls);
+	}
 
-	/* Switch back and return results */
-	MemoryContextSwitchTo(oldcontext);
-	rsinfo->returnMode = SFRM_Materialize;
-	rsinfo->setResult = tupstore;
-	rsinfo->setDesc = tupdesc;
+	dshash_seq_term(&status);
 
 	return (Datum) 0;
 }
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index aa3fac2afb8..bef92be8250 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8542,11 +8542,11 @@
   proargnames => '{name,off,size,allocated_size}',
   prosrc => 'pg_get_shmem_allocations' },
 { oid => '6062', descr => 'dsm registry allocations',
-  proname => 'pg_get_dsm_registry', prorows => '50', proretset => 't',
+  proname => 'pg_get_dsm_registry_allocations', prorows => '50', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
   proallargtypes => '{text,int8}', proargmodes => '{o,o}',
   proargnames => '{name,size}',
-  prosrc => 'pg_get_dsm_registry' },
+  prosrc => 'pg_get_dsm_registry_allocations' },
 
 { oid => '4099', descr => 'Is NUMA support available?',
   proname => 'pg_numa_available', provolatile => 's', prorettype => 'bool',
diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
index 229abf926b6..3c279378b5b 100644
--- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
+++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
@@ -12,10 +12,9 @@ SELECT get_val_in_shmem();
              1236
 (1 row)
 
--- 20 bytes = int (4 bytes) + LWLock (16bytes)
-SELECT * FROM pg_dsm_registry;
-       name        | size 
--------------------+------
- test_dsm_registry |   20
+SELECT size > 0 FROM pg_dsm_registry_allocations WHERE name = 'test_dsm_registry';
+ ?column? 
+----------
+ t
 (1 row)
 
diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
index aad402b5e64..d27ee2e6a1a 100644
--- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
+++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
@@ -3,5 +3,4 @@ SELECT set_val_in_shmem(1236);
 \c
 SELECT get_val_in_shmem();
 
--- 20 bytes = int (4 bytes) + LWLock (16bytes)
-SELECT * FROM pg_dsm_registry;
+SELECT size > 0 FROM pg_dsm_registry_allocations WHERE name = 'test_dsm_registry';
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index ec257f93398..893c5b2b933 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1340,9 +1340,9 @@ pg_cursors| SELECT name,
     is_scrollable,
     creation_time
    FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
-pg_dsm_registry| SELECT name,
+pg_dsm_registry_allocations| SELECT name,
     size
-   FROM pg_get_dsm_registry() pg_get_dsm_registry(name, size);
+   FROM pg_get_dsm_registry_allocations() pg_get_dsm_registry_allocations(name, size);
 pg_file_settings| SELECT sourcefile,
     sourceline,
     seqno,
-- 
2.49.0

v4-0003-Add-doc-entry-for-pg_shmem_allocations_numa.patchapplication/octet-stream; name=v4-0003-Add-doc-entry-for-pg_shmem_allocations_numa.patch; x-unix-mode=0644Download
From ee9d5aa8a77bdc9a23ffa5cab0cf8f8e74f6e40c Mon Sep 17 00:00:00 2001
From: Florents Tselai <florents.tselai@gmail.com>
Date: Tue, 3 Jun 2025 22:37:51 +0300
Subject: [PATCH v4 3/4] Add doc entry for pg_shmem_allocations_numa

---
 doc/src/sgml/system-views.sgml | 63 ++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index b58c52ea50f..2d55671f5fa 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -4146,6 +4146,69 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
   </para>
  </sect1>
 
+ <sect1 id="view-pg-dsm-registry-allocations">
+  <title><structname>pg_dsm_registry_allocations</structname></title>
+
+  <indexterm zone="view-pg-dsm-registry-allocations">
+   <primary>pg_dsm_registry_allocations</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_dsm_registry_allocations</structname> view shows current
+   allocations registered in the dynamic shared memory (DSM) registry. This
+   includes all DSM segments that have been allocated and tracked by
+   <productname>PostgreSQL</productname> during the server's operation.
+  </para>
+
+  <para>
+   This view is useful for monitoring DSM usage by extensions or internal
+   components using dynamic shared memory segments.
+  </para>
+
+  <table>
+   <title><structname>pg_dsm_registry_allocations</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>
+        The name of the DSM allocation as registered in the DSM registry.
+       </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>size</structfield> <type>int8</type>
+      </para>
+       <para>
+        The size of the allocated dynamic shared memory segment in bytes.
+       </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   By default, access to the <structname>pg_dsm_registry_allocations</structname>
+   view is restricted to superusers or roles granted the
+   <literal>pg_read_all_stats</literal> role due to the sensitive nature
+   of dynamic shared memory usage information.
+  </para>
+ </sect1>
+
  <sect1 id="view-pg-stats">
   <title><structname>pg_stats</structname></title>
 
-- 
2.49.0

v4-0004-Cleanup-includes-in-pg_get_dsm_registry_allocatio.patchapplication/octet-stream; name=v4-0004-Cleanup-includes-in-pg_get_dsm_registry_allocatio.patch; x-unix-mode=0644Download
From 502455e799005dea5f9fab28bcc3f0f33e776b65 Mon Sep 17 00:00:00 2001
From: Florents Tselai <florents.tselai@gmail.com>
Date: Tue, 3 Jun 2025 23:06:15 +0300
Subject: [PATCH v4 4/4] Cleanup includes in pg_get_dsm_registry_allocations;
 remove unused headers and alphabetize. Add entry in System View overview

---
 doc/src/sgml/system-views.sgml         |  5 +++++
 src/backend/storage/ipc/dsm_registry.c | 15 ++-------------
 2 files changed, 7 insertions(+), 13 deletions(-)

diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 2d55671f5fa..e41a7b5745f 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -186,6 +186,11 @@
       <entry>NUMA node mappings for shared memory allocations</entry>
      </row>
 
+     <row>
+      <entry><link linkend="view-pg-dsm-registry-allocations"><structname>pg_dsm_registry_allocations</structname></link></entry>
+      <entry>DSM segment allocations registered via the DSM registry</entry>
+     </row>
+
      <row>
       <entry><link linkend="view-pg-stats"><structname>pg_stats</structname></link></entry>
       <entry>planner statistics</entry>
diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 33e3d9ba3f0..0650c70e173 100644
--- a/src/backend/storage/ipc/dsm_registry.c
+++ b/src/backend/storage/ipc/dsm_registry.c
@@ -26,15 +26,13 @@
 
 #include "postgres.h"
 
+#include "funcapi.h"
 #include "lib/dshash.h"
 #include "storage/dsm_registry.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
-#include "utils/memutils.h"
-#include "fmgr.h"
-#include "funcapi.h"
-#include "miscadmin.h"
 #include "utils/builtins.h"
+#include "utils/memutils.h"
 
 typedef struct DSMRegistryCtxStruct
 {
@@ -203,12 +201,6 @@ GetNamedDSMSegment(const char *name, size_t size,
 	return ret;
 }
 
-typedef struct
-{
-	Tuplestorestate *tupstore;
-	TupleDesc        tupdesc;
-} DSMRegistrySRFContext;
-
 Datum
 pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS)
 {
@@ -216,9 +208,6 @@ pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS)
 	DSMRegistryEntry *entry;
 	dshash_seq_status status;
 
-	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
-		ereport(ERROR, (errmsg("pg_get_dsm_registry_allocations must be used in a SRF context")));
-
 	InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
 
 	/* Ensure DSM registry initialized */
-- 
2.49.0

#12Nathan Bossart
nathandbossart@gmail.com
In reply to: Florents Tselai (#11)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

This latest patch set looks pretty good to me.

--
nathan

#13Florents Tselai
florents.tselai@gmail.com
In reply to: Nathan Bossart (#12)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On Tue, Jun 3, 2025 at 11:23 PM Nathan Bossart <nathandbossart@gmail.com>
wrote:

This latest patch set looks pretty good to me.

Thanks; let's wait a bit to see if there're any objections.
If not, I've taken note of

created_at might be interesting.

For a separate patch .

#14Nathan Bossart
nathandbossart@gmail.com
In reply to: Florents Tselai (#13)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On Wed, Jun 04, 2025 at 11:17:42AM +0300, Florents Tselai wrote:

Thanks; let's wait a bit to see if there're any objections.

Would you mind combining the patches into just one patch? That's how I
reviewed it, and that's how it would be committed.

--
nathan

#15Florents Tselai
florents.tselai@gmail.com
In reply to: Nathan Bossart (#14)
1 attachment(s)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On 4 Jun 2025, at 7:19 PM, Nathan Bossart <nathandbossart@gmail.com> wrote:

On Wed, Jun 04, 2025 at 11:17:42AM +0300, Florents Tselai wrote:

Thanks; let's wait a bit to see if there're any objections.

Would you mind combining the patches into just one patch? That's how I
reviewed it, and that's how it would be committed.

This works ?

Attachments:

v4-0001-Add-pg_dsm_registry_allocations-system-view.patchapplication/octet-stream; name=v4-0001-Add-pg_dsm_registry_allocations-system-view.patch; x-unix-mode=0644Download
From f66d63613d8db0974c79a83a67184efa1dddfd93 Mon Sep 17 00:00:00 2001
From: Florents Tselai <florents.tselai@gmail.com>
Date: Wed, 4 Jun 2025 21:02:01 +0300
Subject: [PATCH v4] Add pg_dsm_registry_allocations system view

---
 doc/src/sgml/system-views.sgml                | 68 +++++++++++++++++++
 src/backend/catalog/system_views.sql          |  8 +++
 src/backend/storage/ipc/dsm_registry.c        | 34 ++++++++++
 src/include/catalog/pg_proc.dat               |  6 ++
 .../expected/test_dsm_registry.out            |  6 ++
 .../sql/test_dsm_registry.sql                 |  2 +
 src/test/regress/expected/rules.out           |  3 +
 7 files changed, 127 insertions(+)

diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index b58c52ea50f..e41a7b5745f 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -186,6 +186,11 @@
       <entry>NUMA node mappings for shared memory allocations</entry>
      </row>
 
+     <row>
+      <entry><link linkend="view-pg-dsm-registry-allocations"><structname>pg_dsm_registry_allocations</structname></link></entry>
+      <entry>DSM segment allocations registered via the DSM registry</entry>
+     </row>
+
      <row>
       <entry><link linkend="view-pg-stats"><structname>pg_stats</structname></link></entry>
       <entry>planner statistics</entry>
@@ -4146,6 +4151,69 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
   </para>
  </sect1>
 
+ <sect1 id="view-pg-dsm-registry-allocations">
+  <title><structname>pg_dsm_registry_allocations</structname></title>
+
+  <indexterm zone="view-pg-dsm-registry-allocations">
+   <primary>pg_dsm_registry_allocations</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_dsm_registry_allocations</structname> view shows current
+   allocations registered in the dynamic shared memory (DSM) registry. This
+   includes all DSM segments that have been allocated and tracked by
+   <productname>PostgreSQL</productname> during the server's operation.
+  </para>
+
+  <para>
+   This view is useful for monitoring DSM usage by extensions or internal
+   components using dynamic shared memory segments.
+  </para>
+
+  <table>
+   <title><structname>pg_dsm_registry_allocations</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>
+        The name of the DSM allocation as registered in the DSM registry.
+       </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>size</structfield> <type>int8</type>
+      </para>
+       <para>
+        The size of the allocated dynamic shared memory segment in bytes.
+       </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   By default, access to the <structname>pg_dsm_registry_allocations</structname>
+   view is restricted to superusers or roles granted the
+   <literal>pg_read_all_stats</literal> role due to the sensitive nature
+   of dynamic shared memory usage information.
+  </para>
+ </sect1>
+
  <sect1 id="view-pg-stats">
   <title><structname>pg_stats</structname></title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 08f780a2e63..64e62732f18 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -666,6 +666,14 @@ GRANT SELECT ON pg_shmem_allocations_numa TO pg_read_all_stats;
 REVOKE EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() FROM PUBLIC;
 GRANT EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() TO pg_read_all_stats;
 
+CREATE VIEW pg_dsm_registry_allocations AS
+SELECT * FROM pg_get_dsm_registry_allocations();
+
+REVOKE ALL ON pg_dsm_registry_allocations FROM PUBLIC;
+GRANT SELECT ON pg_dsm_registry_allocations TO pg_read_all_stats;
+REVOKE EXECUTE ON FUNCTION pg_get_dsm_registry_allocations() FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION pg_get_dsm_registry_allocations() TO pg_read_all_stats;
+
 CREATE VIEW pg_backend_memory_contexts AS
     SELECT * FROM pg_get_backend_memory_contexts();
 
diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 1d4fd31ffed..0650c70e173 100644
--- a/src/backend/storage/ipc/dsm_registry.c
+++ b/src/backend/storage/ipc/dsm_registry.c
@@ -26,10 +26,12 @@
 
 #include "postgres.h"
 
+#include "funcapi.h"
 #include "lib/dshash.h"
 #include "storage/dsm_registry.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "utils/builtins.h"
 #include "utils/memutils.h"
 
 typedef struct DSMRegistryCtxStruct
@@ -198,3 +200,35 @@ GetNamedDSMSegment(const char *name, size_t size,
 
 	return ret;
 }
+
+Datum
+pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	DSMRegistryEntry *entry;
+	dshash_seq_status status;
+
+	InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
+
+	/* Ensure DSM registry initialized */
+	init_dsm_registry();
+
+	/* Use non-exclusive access to avoid blocking other backends */
+	dshash_seq_init(&status, dsm_registry_table, false);
+
+	while ((entry = dshash_seq_next(&status)) != NULL)
+	{
+		Datum values[2];
+		bool nulls[2] = {false, false};
+
+		values[0] = CStringGetTextDatum(entry->name);
+		values[1] = Int64GetDatum(entry->size);
+
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+							 values, nulls);
+	}
+
+	dshash_seq_term(&status);
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d3d28a263fa..bef92be8250 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8541,6 +8541,12 @@
   proallargtypes => '{text,int8,int8,int8}', proargmodes => '{o,o,o,o}',
   proargnames => '{name,off,size,allocated_size}',
   prosrc => 'pg_get_shmem_allocations' },
+{ oid => '6062', descr => 'dsm registry allocations',
+  proname => 'pg_get_dsm_registry_allocations', prorows => '50', proretset => 't',
+  provolatile => 'v', prorettype => 'record', proargtypes => '',
+  proallargtypes => '{text,int8}', proargmodes => '{o,o}',
+  proargnames => '{name,size}',
+  prosrc => 'pg_get_dsm_registry_allocations' },
 
 { oid => '4099', descr => 'Is NUMA support available?',
   proname => 'pg_numa_available', provolatile => 's', prorettype => 'bool',
diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
index 8ffbd343a05..3c279378b5b 100644
--- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
+++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
@@ -12,3 +12,9 @@ SELECT get_val_in_shmem();
              1236
 (1 row)
 
+SELECT size > 0 FROM pg_dsm_registry_allocations WHERE name = 'test_dsm_registry';
+ ?column? 
+----------
+ t
+(1 row)
+
diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
index b3351be0a16..d27ee2e6a1a 100644
--- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
+++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
@@ -2,3 +2,5 @@ CREATE EXTENSION test_dsm_registry;
 SELECT set_val_in_shmem(1236);
 \c
 SELECT get_val_in_shmem();
+
+SELECT size > 0 FROM pg_dsm_registry_allocations WHERE name = 'test_dsm_registry';
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 6cf828ca8d0..893c5b2b933 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1340,6 +1340,9 @@ pg_cursors| SELECT name,
     is_scrollable,
     creation_time
    FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
+pg_dsm_registry_allocations| SELECT name,
+    size
+   FROM pg_get_dsm_registry_allocations() pg_get_dsm_registry_allocations(name, size);
 pg_file_settings| SELECT sourcefile,
     sourceline,
     seqno,
-- 
2.49.0

#16Nathan Bossart
nathandbossart@gmail.com
In reply to: Florents Tselai (#15)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On Wed, Jun 04, 2025 at 09:03:03PM +0300, Florents Tselai wrote:

On 4 Jun 2025, at 7:19 PM, Nathan Bossart <nathandbossart@gmail.com> wrote:

Would you mind combining the patches into just one patch? That's how I
reviewed it, and that's how it would be committed.

This works ?

Yes, thanks.

--
nathan

#17Nathan Bossart
nathandbossart@gmail.com
In reply to: Nathan Bossart (#16)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

I think this will need some rework to deal with the addition of
GetNamedDSA() and GetNamedDSHash() [0]/messages/by-id/flat/aEC8HGy2tRQjZg_8@nathan. We probably want to add a "type"
column to show whether the entry is for a DSM, DSA, or dshash table. And
for DSAs and dshash tables, we probably want to use dsa_get_total_size()
for the "size" column.

[0]: /messages/by-id/flat/aEC8HGy2tRQjZg_8@nathan

--
nathan

#18Florents Tselai
florents.tselai@gmail.com
In reply to: Nathan Bossart (#17)
1 attachment(s)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On 25 Jun 2025, at 4:33 AM, Nathan Bossart <nathandbossart@gmail.com> wrote:

for DSAs and dshash tables, we probably want to use dsa_get_total_size()
for the "size" column.

The thing is that dsa_get_total_size expects an already attached dsa.

I can't see how it's possible to get the actual size for the dsa and dsh case,
without attaching and then using, dsa_get_total_size on the attached dsa.
And I don't think we wanna do that.

Instead maybe we can just report NULL for the dsa and dsh cases?

Here’s a v5 that does it like that

Attachments:

v5-0001-Update-pg_dsm_registry_allocations-to-include-stu.patchapplication/octet-stream; name=v5-0001-Update-pg_dsm_registry_allocations-to-include-stu.patch; x-unix-mode=0644Download
From 56deeba7514f9e3e8d91c9d0e5f56d206f1ec663 Mon Sep 17 00:00:00 2001
From: Florents Tselai <florents.tselai@gmail.com>
Date: Tue, 8 Jul 2025 14:06:57 +0300
Subject: [PATCH v5] Update pg_dsm_registry_allocations to include stuff added
 in fe07100

---
 doc/src/sgml/system-views.sgml                | 84 +++++++++++++++++++
 src/backend/catalog/system_views.sql          |  8 ++
 src/backend/storage/ipc/dsm_registry.c        | 66 +++++++++++++++
 src/include/catalog/pg_proc.dat               |  6 ++
 .../expected/test_dsm_registry.out            |  9 ++
 .../sql/test_dsm_registry.sql                 |  2 +
 src/test/regress/expected/rules.out           |  4 +
 7 files changed, 179 insertions(+)

diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index e1ac544ee40..c61c6f6b2a7 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -186,6 +186,11 @@
       <entry>NUMA node mappings for shared memory allocations</entry>
      </row>
 
+     <row>
+      <entry><link linkend="view-pg-dsm-registry-allocations"><structname>pg_dsm_registry_allocations</structname></link></entry>
+      <entry>DSM segment allocations registered via the DSM registry</entry>
+     </row>
+
      <row>
       <entry><link linkend="view-pg-stats"><structname>pg_stats</structname></link></entry>
       <entry>planner statistics</entry>
@@ -4143,6 +4148,85 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
   </para>
  </sect1>
 
+ <sect1 id="view-pg-dsm-registry-allocations">
+  <title><structname>pg_dsm_registry_allocations</structname></title>
+
+  <indexterm zone="view-pg-dsm-registry-allocations">
+   <primary>pg_dsm_registry_allocations</primary>
+  </indexterm>
+
+  <para>
+     The <structname>pg_dsm_registry_allocations</structname> view shows current
+     allocations registered in the dynamic shared memory (DSM) registry. This
+     includes all entries that have been registered and tracked by
+     <productname>PostgreSQL</productname> during the server's operation. Entries
+     may represent a raw dynamic shared memory (DSM) segment, a dynamic shared
+     memory area (DSA), or a shared hash table (dshash) built on top of a DSA.
+  </para>
+
+  <para>
+   This view is useful for monitoring DSM usage by extensions or internal
+   components using dynamic shared memory segments.
+  </para>
+
+  <table>
+     <title><structname>pg_dsm_registry_allocations</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>
+          The name of the allocation as registered in the DSM registry.
+         </para></entry>
+       </row>
+
+       <row>
+        <entry role="catalog_table_entry"><para role="column_definition">
+         <structfield>type</structfield> <type>text</type>
+        </para>
+         <para>
+          The type of allocation. Possible values are <literal>dsm</literal> for a
+          raw dynamic shared memory segment, <literal>dsa</literal> for a dynamic
+          shared memory area, and <literal>dsh</literal> for a dynamic shared hash table.
+         </para></entry>
+       </row>
+
+       <row>
+        <entry role="catalog_table_entry"><para role="column_definition">
+         <structfield>size</structfield> <type>int8</type>
+        </para>
+         <para>
+          The size of the allocation in bytes. For entries of type <literal>dsm</literal>,
+          this reflects the actual size of the allocated DSM segment. For <literal>dsa</literal>
+          and <literal>dsh</literal> entries, the size is reported as <literal>NULL</literal>
+          because the server does not attach to these areas in order to retrieve their size.
+         </para></entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+
+  <para>
+   By default, access to the <structname>pg_dsm_registry_allocations</structname>
+   view is restricted to superusers or roles granted the
+   <literal>pg_read_all_stats</literal> role due to the sensitive nature
+   of dynamic shared memory usage information.
+  </para>
+ </sect1>
+
  <sect1 id="view-pg-stats">
   <title><structname>pg_stats</structname></title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e5dbbe61b81..e637f080a37 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -666,6 +666,14 @@ GRANT SELECT ON pg_shmem_allocations_numa TO pg_read_all_stats;
 REVOKE EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() FROM PUBLIC;
 GRANT EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() TO pg_read_all_stats;
 
+CREATE VIEW pg_dsm_registry_allocations AS
+SELECT * FROM pg_get_dsm_registry_allocations();
+
+REVOKE ALL ON pg_dsm_registry_allocations FROM PUBLIC;
+GRANT SELECT ON pg_dsm_registry_allocations TO pg_read_all_stats;
+REVOKE EXECUTE ON FUNCTION pg_get_dsm_registry_allocations() FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION pg_get_dsm_registry_allocations() TO pg_read_all_stats;
+
 CREATE VIEW pg_backend_memory_contexts AS
     SELECT * FROM pg_get_backend_memory_contexts();
 
diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 828c2ff0c7f..1934e086b0a 100644
--- a/src/backend/storage/ipc/dsm_registry.c
+++ b/src/backend/storage/ipc/dsm_registry.c
@@ -40,10 +40,12 @@
 
 #include "postgres.h"
 
+#include "funcapi.h"
 #include "lib/dshash.h"
 #include "storage/dsm_registry.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "utils/builtins.h"
 #include "utils/memutils.h"
 
 #define DSMR_NAME_LEN				128
@@ -435,3 +437,67 @@ GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found)
 
 	return ret;
 }
+
+Datum
+pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	DSMRegistryEntry *entry;
+	dshash_seq_status status;
+
+	InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
+
+	/* Ensure DSM registry initialized */
+	init_dsm_registry();
+
+	/* Use non-exclusive access to avoid blocking other backends */
+	dshash_seq_init(&status, dsm_registry_table, false);
+
+	while ((entry = dshash_seq_next(&status)) != NULL)
+	{
+		/* name, type, size */
+		Datum values[3];
+		bool nulls[3] = {false, false, false};
+
+		/* 0: name */
+		values[0] = CStringGetTextDatum(entry->name);
+
+		/* 1: type */
+		switch (entry->type) {
+			case DSMR_ENTRY_TYPE_DSM:
+				values[1] = CStringGetTextDatum("dsm");
+				break;
+			case DSMR_ENTRY_TYPE_DSA:
+				values[1] = CStringGetTextDatum("dsa");
+				break;
+			case DSMR_ENTRY_TYPE_DSH:
+				values[1] = CStringGetTextDatum("dsh");
+				break;
+			default:
+				values[1] = CStringGetTextDatum("unknown");
+				break;
+		}
+
+		/* 2: size */
+		switch (entry->type)
+		{
+			case DSMR_ENTRY_TYPE_DSM:
+				values[2] = Int64GetDatum(entry->data.dsm.size);
+				break;
+
+			/* in case of dsa or dsh we return NULL */
+			case DSMR_ENTRY_TYPE_DSA:
+			case DSMR_ENTRY_TYPE_DSH:
+			default:
+				nulls[2] = true;
+				break;
+		}
+
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+							 values, nulls);
+	}
+
+	dshash_seq_term(&status);
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d4650947c63..2f5df3affae 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8559,6 +8559,12 @@
   proallargtypes => '{text,int8,int8,int8}', proargmodes => '{o,o,o,o}',
   proargnames => '{name,off,size,allocated_size}',
   prosrc => 'pg_get_shmem_allocations' },
+{ oid => '6062', descr => 'dsm registry allocations',
+  proname => 'pg_get_dsm_registry_allocations', prorows => '50', proretset => 't',
+  provolatile => 'v', prorettype => 'record', proargtypes => '',
+  proallargtypes => '{text,text,int8}', proargmodes => '{o,o,o}',
+  proargnames => '{name,type,size}',
+  prosrc => 'pg_get_dsm_registry_allocations' },
 
 { oid => '4099', descr => 'Is NUMA support available?',
   proname => 'pg_numa_available', provolatile => 's', prorettype => 'bool',
diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
index 8ded82e59d6..90f0a888a16 100644
--- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
+++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
@@ -24,3 +24,12 @@ SELECT get_val_in_hash('test');
  1414
 (1 row)
 
+\c
+SELECT name, type, size > 0 OR size ISNULL as size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%';
+          name          | type | size 
+------------------------+------+------
+ test_dsm_registry_dsa  | dsa  | t
+ test_dsm_registry_hash | dsh  | t
+ test_dsm_registry_dsm  | dsm  | t
+(3 rows)
+
diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
index c2e25cddaae..a84beaec6c5 100644
--- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
+++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
@@ -4,3 +4,5 @@ SELECT set_val_in_hash('test', '1414');
 \c
 SELECT get_val_in_shmem();
 SELECT get_val_in_hash('test');
+\c
+SELECT name, type, size > 0 OR size ISNULL as size FROM pg_dsm_registry_allocations WHERE name like 'test_dsm_registry%';
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 6cf828ca8d0..dce8c672b40 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1340,6 +1340,10 @@ pg_cursors| SELECT name,
     is_scrollable,
     creation_time
    FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
+pg_dsm_registry_allocations| SELECT name,
+    type,
+    size
+   FROM pg_get_dsm_registry_allocations() pg_get_dsm_registry_allocations(name, type, size);
 pg_file_settings| SELECT sourcefile,
     sourceline,
     seqno,
-- 
2.49.0

#19Nathan Bossart
nathandbossart@gmail.com
In reply to: Florents Tselai (#18)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On Tue, Jul 08, 2025 at 07:17:54PM +0800, Florents Tselai wrote:

I can't see how it's possible to get the actual size for the dsa and dsh case,
without attaching and then using, dsa_get_total_size on the attached dsa.
And I don't think we wanna do that.

Instead maybe we can just report NULL for the dsa and dsh cases?

Yeah, I'm not sure what else we can do about that without a bunch of
refactoring work on the DSA/dshash side, and it's probably not worth the
effort, anyway.

--
nathan

#20Florents Tselai
florents.tselai@gmail.com
In reply to: Nathan Bossart (#19)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On 8 Jul 2025, at 11:21 PM, Nathan Bossart <nathandbossart@gmail.com> wrote:

On Tue, Jul 08, 2025 at 07:17:54PM +0800, Florents Tselai wrote:

I can't see how it's possible to get the actual size for the dsa and dsh case,
without attaching and then using, dsa_get_total_size on the attached dsa.
And I don't think we wanna do that.

Instead maybe we can just report NULL for the dsa and dsh cases?

Yeah, I'm not sure what else we can do about that without a bunch of
refactoring work on the DSA/dshash side, and it's probably not worth the
effort, anyway.

I agree. In that case I think v5 should be enough.

#21Nathan Bossart
nathandbossart@gmail.com
In reply to: Florents Tselai (#20)
1 attachment(s)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

Here is what I have staged for commit, which I'm planning to do tomorrow.

--
nathan

Attachments:

v6-0001-Introduce-pg_dsm_registry_allocations-view.patchtext/plain; charset=us-asciiDownload
From eae91e016509605630616a314a8ee2eaa1113b15 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Tue, 8 Jul 2025 14:52:36 -0500
Subject: [PATCH v6 1/1] Introduce pg_dsm_registry_allocations view.

This commit adds a new system view that provides information about
entries in the dynamic shared memory (DSM) registry.  Specifically,
it returns the name, type, and size of each entry.  Note that since
we cannot discover the size of dynamic shared memory areas (DSAs)
and hash tables backed by DSAs (dshashes) without first attaching
to them, the size column is left as NULL for those.

XXX: NEEDS CATVERSION BUMP

Author: Florents Tselai <florents.tselai@gmail.com>
Reviewed-by: Sungwoo Chang <swchangdev@gmail.com>
Discussion: https://postgr.es/m/4D445D3E-81C5-4135-95BB-D414204A0AB4%40gmail.com
---
 doc/src/sgml/system-views.sgml                | 74 +++++++++++++++++++
 src/backend/catalog/system_views.sql          |  8 ++
 src/backend/storage/ipc/dsm_registry.c        | 49 ++++++++++++
 src/include/catalog/pg_proc.dat               |  7 ++
 .../expected/test_dsm_registry.out            | 18 +++++
 .../sql/test_dsm_registry.sql                 |  7 ++
 src/test/regress/expected/privileges.out      | 15 +++-
 src/test/regress/expected/rules.out           |  4 +
 src/test/regress/sql/privileges.sql           |  5 +-
 9 files changed, 185 insertions(+), 2 deletions(-)

diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index e1ac544ee40..d3ff8c35738 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -81,6 +81,11 @@
       <entry>open cursors</entry>
      </row>
 
+     <row>
+      <entry><link linkend="view-pg-dsm-registry-allocations"><structname>pg_dsm_registry_allocations</structname></link></entry>
+      <entry>shared memory allocations tracked in the DSM registry</entry>
+     </row>
+
      <row>
       <entry><link linkend="view-pg-file-settings"><structname>pg_file_settings</structname></link></entry>
       <entry>summary of configuration file contents</entry>
@@ -1086,6 +1091,75 @@ AND c1.path[c2.level] = c2.path[c2.level];
 
  </sect1>
 
+ <sect1 id="view-pg-dsm-registry-allocations">
+  <title><structname>pg_dsm_registry_allocations</structname></title>
+
+  <indexterm zone="view-pg-dsm-registry-allocations">
+   <primary>pg_dsm_registry_allocations</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_dsm_registry_allocations</structname> view shows shared
+   memory allocations tracked in the dynamic shared memory (DSM) registry.
+   This includes memory allocated by extensions using the mechanisms detailed
+   in <xref linkend="xfunc-shared-addin-after-startup" />.
+  </para>
+
+  <table>
+   <title><structname>pg_dsm_registry_allocations</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>
+       The name of the allocation in the DSM registry.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>type</structfield> <type>text</type>
+      </para>
+      <para>
+       The type of allocation.  Possible values are <literal>segment</literal>,
+       <literal>area</literal>, and <literal>hash</literal>, which correspond
+       to dynamic shared memory segments, areas, and hash tables, respectively.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>size</structfield> <type>int8</type>
+      </para>
+      <para>
+       Size of the allocation in bytes.  NULL for entries of type
+       <literal>area</literal> and <literal>hash</literal>.
+      </para></entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   By default, the <structname>pg_dsm_registry_allocations</structname> view
+   can be read only by superusers or roles with privileges of the
+   <literal>pg_read_all_stats</literal> role.
+  </para>
+ </sect1>
+
  <sect1 id="view-pg-file-settings">
   <title><structname>pg_file_settings</structname></title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e5dbbe61b81..b2d5332effc 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -666,6 +666,14 @@ GRANT SELECT ON pg_shmem_allocations_numa TO pg_read_all_stats;
 REVOKE EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() FROM PUBLIC;
 GRANT EXECUTE ON FUNCTION pg_get_shmem_allocations_numa() TO pg_read_all_stats;
 
+CREATE VIEW pg_dsm_registry_allocations AS
+    SELECT * FROM pg_get_dsm_registry_allocations();
+
+REVOKE ALL ON pg_dsm_registry_allocations FROM PUBLIC;
+GRANT SELECT ON pg_dsm_registry_allocations TO pg_read_all_stats;
+REVOKE EXECUTE ON FUNCTION pg_get_dsm_registry_allocations() FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION pg_get_dsm_registry_allocations() TO pg_read_all_stats;
+
 CREATE VIEW pg_backend_memory_contexts AS
     SELECT * FROM pg_get_backend_memory_contexts();
 
diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c
index 828c2ff0c7f..1682cc6d34c 100644
--- a/src/backend/storage/ipc/dsm_registry.c
+++ b/src/backend/storage/ipc/dsm_registry.c
@@ -40,10 +40,12 @@
 
 #include "postgres.h"
 
+#include "funcapi.h"
 #include "lib/dshash.h"
 #include "storage/dsm_registry.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "utils/builtins.h"
 #include "utils/memutils.h"
 
 #define DSMR_NAME_LEN				128
@@ -88,6 +90,13 @@ typedef enum DSMREntryType
 	DSMR_ENTRY_TYPE_DSH,
 } DSMREntryType;
 
+static const char *const DSMREntryTypeNames[] =
+{
+	[DSMR_ENTRY_TYPE_DSM] = "segment",
+	[DSMR_ENTRY_TYPE_DSA] = "area",
+	[DSMR_ENTRY_TYPE_DSH] = "hash",
+};
+
 typedef struct DSMRegistryEntry
 {
 	char		name[DSMR_NAME_LEN];
@@ -435,3 +444,43 @@ GetNamedDSHash(const char *name, const dshash_parameters *params, bool *found)
 
 	return ret;
 }
+
+Datum
+pg_get_dsm_registry_allocations(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	DSMRegistryEntry *entry;
+	MemoryContext oldcontext;
+	dshash_seq_status status;
+
+	InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
+
+	/* Be sure any local memory allocated by DSM/DSA routines is persistent. */
+	oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+	init_dsm_registry();
+	MemoryContextSwitchTo(oldcontext);
+
+	dshash_seq_init(&status, dsm_registry_table, false);
+	while ((entry = dshash_seq_next(&status)) != NULL)
+	{
+		Datum		vals[3];
+		bool		nulls[3] = {0};
+
+		vals[0] = CStringGetTextDatum(entry->name);
+		vals[1] = CStringGetTextDatum(DSMREntryTypeNames[entry->type]);
+
+		/*
+		 * Since we can't know the size of DSA/dshash entries without first
+		 * attaching to them, return NULL for those.
+		 */
+		if (entry->type == DSMR_ENTRY_TYPE_DSM)
+			vals[2] = Int64GetDatum(entry->data.dsm.size);
+		else
+			nulls[2] = true;
+
+		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, vals, nulls);
+	}
+	dshash_seq_term(&status);
+
+	return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d4650947c63..6e3b3ddd67b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8572,6 +8572,13 @@
   proargnames => '{name,numa_node,size}',
   prosrc => 'pg_get_shmem_allocations_numa' },
 
+{ oid => '9314', descr => 'allocations tracked in the DSM registry',
+  proname => 'pg_get_dsm_registry_allocations', prorows => '50',
+  proretset => 't', provolatile => 'v', prorettype => 'record',
+  proargtypes => '', proallargtypes => '{text,text,int8}',
+  proargmodes => '{o,o,o}', proargnames => '{name,type,size}',
+  prosrc => 'pg_get_dsm_registry_allocations' },
+
 # memory context of local backend
 { oid => '2282',
   descr => 'information about all memory contexts of local backend',
diff --git a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
index 8ded82e59d6..ca8abbb377e 100644
--- a/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
+++ b/src/test/modules/test_dsm_registry/expected/test_dsm_registry.out
@@ -1,3 +1,10 @@
+SELECT name, type, size IS DISTINCT FROM 0 AS size
+FROM pg_dsm_registry_allocations
+WHERE name like 'test_dsm_registry%' ORDER BY name;
+ name | type | size 
+------+------+------
+(0 rows)
+
 CREATE EXTENSION test_dsm_registry;
 SELECT set_val_in_shmem(1236);
  set_val_in_shmem 
@@ -24,3 +31,14 @@ SELECT get_val_in_hash('test');
  1414
 (1 row)
 
+\c
+SELECT name, type, size IS DISTINCT FROM 0 AS size
+FROM pg_dsm_registry_allocations
+WHERE name like 'test_dsm_registry%' ORDER BY name;
+          name          |  type   | size 
+------------------------+---------+------
+ test_dsm_registry_dsa  | area    | t
+ test_dsm_registry_dsm  | segment | t
+ test_dsm_registry_hash | hash    | t
+(3 rows)
+
diff --git a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
index c2e25cddaae..965a3f1ebb6 100644
--- a/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
+++ b/src/test/modules/test_dsm_registry/sql/test_dsm_registry.sql
@@ -1,6 +1,13 @@
+SELECT name, type, size IS DISTINCT FROM 0 AS size
+FROM pg_dsm_registry_allocations
+WHERE name like 'test_dsm_registry%' ORDER BY name;
 CREATE EXTENSION test_dsm_registry;
 SELECT set_val_in_shmem(1236);
 SELECT set_val_in_hash('test', '1414');
 \c
 SELECT get_val_in_shmem();
 SELECT get_val_in_hash('test');
+\c
+SELECT name, type, size IS DISTINCT FROM 0 AS size
+FROM pg_dsm_registry_allocations
+WHERE name like 'test_dsm_registry%' ORDER BY name;
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index c25062c288f..aadc328589d 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -3220,7 +3220,8 @@ REVOKE MAINTAIN ON lock_table FROM regress_locktable_user;
 DROP TABLE lock_table;
 DROP USER regress_locktable_user;
 -- test to check privileges of system views pg_shmem_allocations,
--- pg_shmem_allocations_numa and pg_backend_memory_contexts.
+-- pg_shmem_allocations_numa, pg_dsm_registry_allocations, and
+-- pg_backend_memory_contexts.
 -- switch to superuser
 \c -
 CREATE ROLE regress_readallstats;
@@ -3248,6 +3249,12 @@ SELECT has_table_privilege('regress_readallstats','pg_shmem_allocations_numa','S
  f
 (1 row)
 
+SELECT has_table_privilege('regress_readallstats','pg_dsm_registry_allocations','SELECT'); -- no
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
 GRANT pg_read_all_stats TO regress_readallstats;
 SELECT has_table_privilege('regress_readallstats','pg_aios','SELECT'); -- yes
  has_table_privilege 
@@ -3273,6 +3280,12 @@ SELECT has_table_privilege('regress_readallstats','pg_shmem_allocations_numa','S
  t
 (1 row)
 
+SELECT has_table_privilege('regress_readallstats','pg_dsm_registry_allocations','SELECT'); -- yes
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
 -- run query to ensure that functions within views can be executed
 SET ROLE regress_readallstats;
 SELECT COUNT(*) >= 0 AS ok FROM pg_aios;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 6cf828ca8d0..dce8c672b40 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1340,6 +1340,10 @@ pg_cursors| SELECT name,
     is_scrollable,
     creation_time
    FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
+pg_dsm_registry_allocations| SELECT name,
+    type,
+    size
+   FROM pg_get_dsm_registry_allocations() pg_get_dsm_registry_allocations(name, type, size);
 pg_file_settings| SELECT sourcefile,
     sourceline,
     seqno,
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index f337aa67c13..47bd4011dc9 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -1948,7 +1948,8 @@ DROP TABLE lock_table;
 DROP USER regress_locktable_user;
 
 -- test to check privileges of system views pg_shmem_allocations,
--- pg_shmem_allocations_numa and pg_backend_memory_contexts.
+-- pg_shmem_allocations_numa, pg_dsm_registry_allocations, and
+-- pg_backend_memory_contexts.
 
 -- switch to superuser
 \c -
@@ -1959,6 +1960,7 @@ SELECT has_table_privilege('regress_readallstats','pg_aios','SELECT'); -- no
 SELECT has_table_privilege('regress_readallstats','pg_backend_memory_contexts','SELECT'); -- no
 SELECT has_table_privilege('regress_readallstats','pg_shmem_allocations','SELECT'); -- no
 SELECT has_table_privilege('regress_readallstats','pg_shmem_allocations_numa','SELECT'); -- no
+SELECT has_table_privilege('regress_readallstats','pg_dsm_registry_allocations','SELECT'); -- no
 
 GRANT pg_read_all_stats TO regress_readallstats;
 
@@ -1966,6 +1968,7 @@ SELECT has_table_privilege('regress_readallstats','pg_aios','SELECT'); -- yes
 SELECT has_table_privilege('regress_readallstats','pg_backend_memory_contexts','SELECT'); -- yes
 SELECT has_table_privilege('regress_readallstats','pg_shmem_allocations','SELECT'); -- yes
 SELECT has_table_privilege('regress_readallstats','pg_shmem_allocations_numa','SELECT'); -- yes
+SELECT has_table_privilege('regress_readallstats','pg_dsm_registry_allocations','SELECT'); -- yes
 
 -- run query to ensure that functions within views can be executed
 SET ROLE regress_readallstats;
-- 
2.39.5 (Apple Git-154)

#22Florents Tselai
florents.tselai@gmail.com
In reply to: Nathan Bossart (#21)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

On Tue, Jul 8, 2025 at 11:11 PM Nathan Bossart <nathandbossart@gmail.com>
wrote:

Show quoted text

Here is what I have staged for commit, which I'm planning to do tomorrow.

I agree with your improvements as well; thanks.

#23Nathan Bossart
nathandbossart@gmail.com
In reply to: Florents Tselai (#22)
Re: like pg_shmem_allocations, but fine-grained for DSM registry ?

Committed.

--
nathan