On Mon, Apr 14, 2025 at 06:15:07AM +0000, Hayato Kuroda (Fujitsu) wrote: > Apart from functions related with injection_points, this can be called even > when > postgres has been built with -Dinjection_points=false. This is intentional > because > this function does not have any side-effect and just refers the status. Is my > understanding correct?
Yes. The function could be changed to return an ERROR if the build does not support this option. It's more portable to return NULL if you have some queries that do joins. This could be used with pg_stat_activity and wait events for example, and some points are in positions strategic enough that they could be used across more than one library, like the restart point one or the autovacuum startup one. > I'm not sure it is directly related, but ISTM there are no direct ways to > check > whether the injection_points is enabled or not. How do you think adding the > function? Yeah, we could use something like that, not sure if that's worth it. > Regarding the internal of the patch, it could be crashed when two points are > attached and then first one is detached [1]. I think we should not use "idx" > for > the result array - PSA the fix. Oops. That's a brain fart from my side. Thanks. -- Michael
From ee033a0f883d52638cc9c90b9c0eff29b9dde5a5 Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@paquier.xyz> Date: Tue, 15 Apr 2025 08:39:36 +0900 Subject: [PATCH v2] 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 | 53 +++++++++++++++++ .../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, 216 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..ea7e6dba5211 100644 --- a/src/backend/utils/misc/injection_point.c +++ b/src/backend/utils/misc/injection_point.c @@ -584,3 +584,56 @@ 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; + int cur_pos = 0; + + 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[cur_pos].name = pstrdup(entry->name); + result[cur_pos].library = pstrdup(entry->library); + result[cur_pos].function = pstrdup(entry->function); + cur_pos++; + } + + LWLockRelease(InjectionPointLock); + + *num_points = cur_pos; + + 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