Hi all, One thing that's been lacking in injection points is the possibility to look at the state of the injection points in shared memory through some SQL. This was on my tablets for some time, but I have not taken the time to do the actual legwork.
The attached patch adds a SRF that returns a set of tuples made of the name, library and function for all the injection points attached to the system. This implementation relies on a new function added in injection_point.c, called InjectionPointList(), that retrieves a palloc()'d array of the injection points, hiding from the callers the internals of what this stuff does with the shmem array lookup. This is useful for monitoring or in tests, to make sure for example that nothing is left around at the end of a script. I have a different proposal planned for this area of the code, where this function would be good to have, but I am sending an independent patch as this stuff is useful on its own. The patch includes a couple of tests and some documentation. Thanks, -- Michael
From 8865e2ca3b1b6291e94d2a5476926095245a4e2f Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@paquier.xyz> Date: Mon, 14 Apr 2025 09:26:52 +0900 Subject: [PATCH] Add pg_get_injection_points() This is a system function that displays the information about the injection points currently attached to the system, feeding from the states of things in shared memory. --- src/include/catalog/pg_proc.dat | 8 +++ src/include/utils/injection_point.h | 14 +++++ src/backend/utils/misc/Makefile | 1 + src/backend/utils/misc/injection_point.c | 50 ++++++++++++++++ .../utils/misc/injection_point_funcs.c | 59 +++++++++++++++++++ src/backend/utils/misc/meson.build | 1 + .../expected/injection_points.out | 16 +++++ .../injection_points/sql/injection_points.sql | 7 +++ src/test/regress/expected/misc_functions.out | 7 +++ src/test/regress/sql/misc_functions.sql | 3 + doc/src/sgml/func.sgml | 46 +++++++++++++++ src/tools/pgindent/typedefs.list | 1 + 12 files changed, 213 insertions(+) create mode 100644 src/backend/utils/misc/injection_point_funcs.c diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 62beb71da288..d087d9fda1e8 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12566,4 +12566,12 @@ proargnames => '{pid,io_id,io_generation,state,operation,off,length,target,handle_data_len,raw_result,result,target_desc,f_sync,f_localmem,f_buffered}', prosrc => 'pg_get_aios' }, +# Injection point functions +{ oid => '8490', descr => 'information about injection points attached', + proname => 'pg_get_injection_points', prorows => '3', proretset => 't', + provolatile => 'v', proparallel => 'r', prorettype => 'record', + proargtypes => '', proallargtypes => '{text,text,text}', + proargmodes => '{o,o,o}', proargnames => '{name,library,function}', + prosrc => 'pg_get_injection_points' }, + ] diff --git a/src/include/utils/injection_point.h b/src/include/utils/injection_point.h index 6ba64cd1ebc6..c5c78914b848 100644 --- a/src/include/utils/injection_point.h +++ b/src/include/utils/injection_point.h @@ -11,6 +11,17 @@ #ifndef INJECTION_POINT_H #define INJECTION_POINT_H +/* + * Injection point data, used when retrieving a list of all the attached + * injection points. + */ +typedef struct InjectionPointData +{ + const char *name; + const char *library; + const char *function; +} InjectionPointData; + /* * Injection points require --enable-injection-points. */ @@ -46,6 +57,9 @@ extern void InjectionPointCached(const char *name); extern bool IsInjectionPointAttached(const char *name); extern bool InjectionPointDetach(const char *name); +/* Get the current set of injection points attached */ +extern InjectionPointData *InjectionPointList(uint32 *num_points); + #ifdef EXEC_BACKEND extern PGDLLIMPORT struct InjectionPointsCtl *ActiveInjectionPoints; #endif diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile index b362ae437710..93703633f69a 100644 --- a/src/backend/utils/misc/Makefile +++ b/src/backend/utils/misc/Makefile @@ -22,6 +22,7 @@ OBJS = \ guc_tables.o \ help_config.o \ injection_point.o \ + injection_point_funcs.o \ pg_config.o \ pg_controldata.o \ pg_rusage.o \ diff --git a/src/backend/utils/misc/injection_point.c b/src/backend/utils/misc/injection_point.c index 97ab851f0a63..bf1a49f7472a 100644 --- a/src/backend/utils/misc/injection_point.c +++ b/src/backend/utils/misc/injection_point.c @@ -584,3 +584,53 @@ IsInjectionPointAttached(const char *name) return false; /* silence compiler */ #endif } + +/* + * Retrieve a list of all the injection points currently attached. + * + * This list is palloc'd in the current memory context. + */ +InjectionPointData * +InjectionPointList(uint32 *num_points) +{ +#ifdef USE_INJECTION_POINTS + InjectionPointData *result; + uint32 max_inuse; + + LWLockAcquire(InjectionPointLock, LW_SHARED); + + max_inuse = pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse); + + /* + * This overestimates the allocation size, including slots that may be + * free, for simplicity. + */ + result = palloc0(sizeof(InjectionPointData) * max_inuse); + + for (int idx = 0; idx < max_inuse; idx++) + { + InjectionPointEntry *entry; + uint64 generation; + + entry = &ActiveInjectionPoints->entries[idx]; + generation = pg_atomic_read_u64(&entry->generation); + + /* skip free slots */ + if (generation % 2 == 0) + continue; + + result[idx].name = pstrdup(entry->name); + result[idx].library = pstrdup(entry->library); + result[idx].function = pstrdup(entry->function); + (*num_points)++; + } + + LWLockRelease(InjectionPointLock); + + return result; + +#else + *num_points = 0; + return NULL; +#endif +} diff --git a/src/backend/utils/misc/injection_point_funcs.c b/src/backend/utils/misc/injection_point_funcs.c new file mode 100644 index 000000000000..750ac4abf67d --- /dev/null +++ b/src/backend/utils/misc/injection_point_funcs.c @@ -0,0 +1,59 @@ +/*------------------------------------------------------------------------- + * + * injection_point_funcs.c + * + * SQL commands and SQL-accessible functions related to injection points. + * + * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/utils/misc/injection_point_funcs.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "funcapi.h" +#include "utils/builtins.h" +#include "utils/injection_point.h" + +/* + * pg_get_injection_points + * + * Return a table of all the injection points currently attached to the + * system. + */ +Datum +pg_get_injection_points(PG_FUNCTION_ARGS) +{ +#define NUM_PG_GET_INJECTION_POINTS 3 + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + uint32 num_points = 0; + InjectionPointData *injps = NULL; + + /* Build a tuplestore to return our results in */ + InitMaterializedSRF(fcinfo, 0); + + injps = InjectionPointList(&num_points); + + for (uint32 idx = 0; idx < num_points; idx++) + { + Datum values[NUM_PG_GET_INJECTION_POINTS]; + bool nulls[NUM_PG_GET_INJECTION_POINTS]; + + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + + values[0] = PointerGetDatum(cstring_to_text(injps[idx].name)); + values[1] = PointerGetDatum(cstring_to_text(injps[idx].library)); + values[2] = PointerGetDatum(cstring_to_text(injps[idx].function)); + + /* shove row into tuplestore */ + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); + } + + return (Datum) 0; +#undef NUM_PG_GET_INJECTION_POINTS +} diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build index 9e389a00d057..4a89f1e016ea 100644 --- a/src/backend/utils/misc/meson.build +++ b/src/backend/utils/misc/meson.build @@ -7,6 +7,7 @@ backend_sources += files( 'guc_tables.c', 'help_config.c', 'injection_point.c', + 'injection_point_funcs.c', 'pg_config.c', 'pg_controldata.c', 'pg_rusage.c', diff --git a/src/test/modules/injection_points/expected/injection_points.out b/src/test/modules/injection_points/expected/injection_points.out index f25bbe4966ee..4b1436a3ef69 100644 --- a/src/test/modules/injection_points/expected/injection_points.out +++ b/src/test/modules/injection_points/expected/injection_points.out @@ -26,6 +26,15 @@ SELECT injection_points_attach('TestInjectionLog2', 'notice'); (1 row) +SELECT name, library, function FROM pg_get_injection_points() + ORDER BY name COLLATE "C"; + name | library | function +--------------------+------------------+------------------ + TestInjectionError | injection_points | injection_error + TestInjectionLog | injection_points | injection_notice + TestInjectionLog2 | injection_points | injection_notice +(3 rows) + SELECT injection_points_run('TestInjectionBooh'); -- nothing injection_points_run ---------------------- @@ -253,5 +262,12 @@ SELECT injection_points_detach('TestConditionLocal1'); (1 row) +-- No points should be left around. +SELECT name, library, function FROM pg_get_injection_points() + ORDER BY name COLLATE "C"; + name | library | function +------+---------+---------- +(0 rows) + DROP EXTENSION injection_points; DROP FUNCTION wait_pid; diff --git a/src/test/modules/injection_points/sql/injection_points.sql b/src/test/modules/injection_points/sql/injection_points.sql index e3a481d60449..b12c40551954 100644 --- a/src/test/modules/injection_points/sql/injection_points.sql +++ b/src/test/modules/injection_points/sql/injection_points.sql @@ -14,6 +14,9 @@ SELECT injection_points_attach('TestInjectionError', 'error'); SELECT injection_points_attach('TestInjectionLog', 'notice'); SELECT injection_points_attach('TestInjectionLog2', 'notice'); +SELECT name, library, function FROM pg_get_injection_points() + ORDER BY name COLLATE "C"; + SELECT injection_points_run('TestInjectionBooh'); -- nothing SELECT injection_points_run('TestInjectionLog2'); -- notice SELECT injection_points_run('TestInjectionLog'); -- notice @@ -75,5 +78,9 @@ SELECT injection_points_detach('TestConditionError'); SELECT injection_points_attach('TestConditionLocal1', 'error'); SELECT injection_points_detach('TestConditionLocal1'); +-- No points should be left around. +SELECT name, library, function FROM pg_get_injection_points() + ORDER BY name COLLATE "C"; + DROP EXTENSION injection_points; DROP FUNCTION wait_pid; diff --git a/src/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out index d3f5d16a6725..c09bff495045 100644 --- a/src/test/regress/expected/misc_functions.out +++ b/src/test/regress/expected/misc_functions.out @@ -802,6 +802,13 @@ SELECT count(*) > 0 AS ok FROM pg_control_system(); t (1 row) +-- Test function for injection point +SELECT count(*) >= 0 AS ok FROM pg_get_injection_points(); + ok +---- + t +(1 row) + -- pg_split_walfile_name, pg_walfile_name & pg_walfile_name_offset SELECT * FROM pg_split_walfile_name(NULL); segment_number | timeline_id diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql index aaebb298330b..e351a19a8744 100644 --- a/src/test/regress/sql/misc_functions.sql +++ b/src/test/regress/sql/misc_functions.sql @@ -355,6 +355,9 @@ SELECT count(*) > 0 AS ok FROM pg_control_init(); SELECT count(*) > 0 AS ok FROM pg_control_recovery(); SELECT count(*) > 0 AS ok FROM pg_control_system(); +-- Test function for injection point +SELECT count(*) >= 0 AS ok FROM pg_get_injection_points(); + -- pg_split_walfile_name, pg_walfile_name & pg_walfile_name_offset SELECT * FROM pg_split_walfile_name(NULL); SELECT * FROM pg_split_walfile_name('invalid'); diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 1c5cfee25d12..6c0e0b45fa83 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -28496,6 +28496,52 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres} </sect2> + <sect2 id="functions-info-injection-points"> + <title>Injection Points Information Functions</title> + + <para> + The functions shown in <xref linkend="functions-info-injection-points"/> + print information about the injection points. + See <xref linkend="xfunc-addin-injection-points" />. + </para> + + <table id="functions-injection-points"> + <title>WAL Summarization Information Functions</title> + <tgroup cols="1"> + <thead> + <row> + <entry role="func_table_entry"><para role="func_signature"> + Function + </para> + <para> + Description + </para></entry> + </row> + </thead> + + <tbody> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>pg_get_injection_points</primary> + </indexterm> + <function>pg_get_injection_points</function> () + <returnvalue>setof record</returnvalue> + ( <parameter>name</parameter> <type>text</type>, + <parameter>library</parameter> <type>text</type>, + <parameter>function</parameter> <type>text</type> ) + </para> + <para> + Returns information about the injection points currently attached + to the cluster. + </para></entry> + </row> + </tbody> + + </tgroup> + </table> + </sect2> + </sect1> <sect1 id="functions-admin"> diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index d16bc208654b..e7df2dc10684 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1281,6 +1281,7 @@ InjectionPointCacheEntry InjectionPointCallback InjectionPointCondition InjectionPointConditionType +InjectionPointData InjectionPointEntry InjectionPointsCtl InjectionPointSharedState -- 2.49.0
signature.asc
Description: PGP signature