On Fri, Mar 14, 2025 at 4:16 PM Nathan Bossart <[email protected]>
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
From 2f475e8a05142991feac2f62b57bc25b9c5ceebc Mon Sep 17 00:00:00 2001
From: Florents Tselai <[email protected]>
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