From 34c0d1959e763b44f2802dd8f41669d102369c3d Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Tue, 8 Jul 2025 14:52:36 -0500 Subject: [PATCH] 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 Reviewed-by: Sungwoo Chang 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 e1ac544ee407..d3ff8c357385 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -81,6 +81,11 @@ open cursors + + pg_dsm_registry_allocations + shared memory allocations tracked in the DSM registry + + pg_file_settings summary of configuration file contents @@ -1086,6 +1091,75 @@ AND c1.path[c2.level] = c2.path[c2.level]; + + <structname>pg_dsm_registry_allocations</structname> + + + pg_dsm_registry_allocations + + + + The pg_dsm_registry_allocations view shows shared + memory allocations tracked in the dynamic shared memory (DSM) registry. + This includes memory allocated by extensions using the mechanisms detailed + in . + + + + <structname>pg_dsm_registry_allocations</structname> Columns + + + + + Column Type + + + Description + + + + + + + + name text + + + The name of the allocation in the DSM registry. + + + + + + type text + + + The type of allocation. Possible values are segment, + area, and hash, which correspond + to dynamic shared memory segments, areas, and hash tables, respectively. + + + + + + size int8 + + + Size of the allocation in bytes. NULL for entries of type + area and hash. + + + + +
+ + + By default, the pg_dsm_registry_allocations view + can be read only by superusers or roles with privileges of the + pg_read_all_stats role. + +
+ <structname>pg_file_settings</structname> diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index e5dbbe61b811..b2d5332effc1 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 828c2ff0c7f5..1682cc6d34c7 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 d4650947c63a..6e3b3ddd67b7 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 8ded82e59d6f..ca8abbb377e6 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 c2e25cddaae1..965a3f1ebb63 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 c25062c288f3..aadc328589d2 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 6cf828ca8d0d..dce8c672b40f 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 f337aa67c13f..47bd4011dc99 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;