From bc76b4a250384fcafab4892c84d35b6121dea7e3 Mon Sep 17 00:00:00 2001 From: Florents Tselai Date: Sat, 15 Mar 2025 10:21:34 +0200 Subject: [PATCH 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 08f780a2e638..7c2246999ec0 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 1d4fd31ffedb..549cbe07a285 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 d3d28a263fa9..aa3fac2afb8f 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 8ffbd343a05a..229abf926b60 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 b3351be0a16b..aad402b5e648 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 6cf828ca8d0d..ec257f933984 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, From fa43c8d511c22993bd5d0cb12ea1af9696685f76 Mon Sep 17 00:00:00 2001 From: Florents Tselai Date: Tue, 3 Jun 2025 22:30:33 +0300 Subject: [PATCH 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 7c2246999ec0..64e62732f18c 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 549cbe07a285..33e3d9ba3f02 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 aa3fac2afb8f..bef92be82506 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 229abf926b60..3c279378b5ba 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 aad402b5e648..d27ee2e6a1a8 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 ec257f933984..893c5b2b9331 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, From 4be74168424ef99efe36381470ff4465d47f993b Mon Sep 17 00:00:00 2001 From: Florents Tselai Date: Tue, 3 Jun 2025 22:37:51 +0300 Subject: [PATCH 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 b58c52ea50f5..2d55671f5fa1 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 + + <structname>pg_dsm_registry_allocations</structname> + + + pg_dsm_registry_allocations + + + + The pg_dsm_registry_allocations view shows current + allocations registered in the dynamic shared memory (DSM) registry. This + includes all DSM segments that have been allocated and tracked by + PostgreSQL during the server's operation. + + + + This view is useful for monitoring DSM usage by extensions or internal + components using dynamic shared memory segments. + + + + <structname>pg_dsm_registry_allocations</structname> Columns + + + + + Column Type + + + Description + + + + + + + + name text + + + The name of the DSM allocation as registered in the DSM registry. + + + + + + size int8 + + + The size of the allocated dynamic shared memory segment in bytes. + + + + +
+ + + By default, access to the pg_dsm_registry_allocations + view is restricted to superusers or roles granted the + pg_read_all_stats role due to the sensitive nature + of dynamic shared memory usage information. + +
+ <structname>pg_stats</structname> From 07c1e42344fe77f56382fd390e8975e7d0a0de08 Mon Sep 17 00:00:00 2001 From: Florents Tselai Date: Tue, 3 Jun 2025 23:06:15 +0300 Subject: [PATCH 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 2d55671f5fa1..e41a7b5745ff 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -186,6 +186,11 @@ NUMA node mappings for shared memory allocations + + pg_dsm_registry_allocations + DSM segment allocations registered via the DSM registry + + pg_stats planner statistics diff --git a/src/backend/storage/ipc/dsm_registry.c b/src/backend/storage/ipc/dsm_registry.c index 33e3d9ba3f02..0650c70e1736 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 */