diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile index 7b9cd12a2a..ed28cd13a8 100644 --- a/src/test/modules/injection_points/Makefile +++ b/src/test/modules/injection_points/Makefile @@ -4,7 +4,8 @@ MODULE_big = injection_points OBJS = \ $(WIN32RES) \ injection_points.o \ - injection_stats.o + injection_stats.o \ + injection_stats_fixed.o EXTENSION = injection_points DATA = injection_points--1.0.sql PGFILEDESC = "injection_points - facility for injection points" diff --git a/src/test/modules/injection_points/injection_points--1.0.sql b/src/test/modules/injection_points/injection_points--1.0.sql index b98d571889..1b2a4938a9 100644 --- a/src/test/modules/injection_points/injection_points--1.0.sql +++ b/src/test/modules/injection_points/injection_points--1.0.sql @@ -84,3 +84,14 @@ CREATE FUNCTION injection_points_stats_numcalls(IN point_name TEXT) RETURNS bigint AS 'MODULE_PATHNAME', 'injection_points_stats_numcalls' LANGUAGE C STRICT; + +-- +-- injection_points_stats_fixed() +-- +-- Reports fixed-numbered statistics for injection points. +CREATE FUNCTION injection_points_stats_fixed(OUT numattach int8, + OUT numdetach int8, + OUT numrun int8) +RETURNS record +AS 'MODULE_PATHNAME', 'injection_points_stats_fixed' +LANGUAGE C STRICT; diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c index acec4e95b0..dc02be1bbf 100644 --- a/src/test/modules/injection_points/injection_points.c +++ b/src/test/modules/injection_points/injection_points.c @@ -297,6 +297,7 @@ injection_points_attach(PG_FUNCTION_ARGS) condition.pid = MyProcPid; } + pgstat_report_inj_fixed(1, 0, 0); InjectionPointAttach(name, "injection_points", function, &condition, sizeof(InjectionPointCondition)); @@ -342,6 +343,7 @@ injection_points_run(PG_FUNCTION_ARGS) { char *name = text_to_cstring(PG_GETARG_TEXT_PP(0)); + pgstat_report_inj_fixed(0, 0, 1); INJECTION_POINT(name); PG_RETURN_VOID(); @@ -432,6 +434,7 @@ injection_points_detach(PG_FUNCTION_ARGS) { char *name = text_to_cstring(PG_GETARG_TEXT_PP(0)); + pgstat_report_inj_fixed(0, 1, 0); if (!InjectionPointDetach(name)) elog(ERROR, "could not detach injection point \"%s\"", name); @@ -459,4 +462,5 @@ _PG_init(void) return; pgstat_register_inj(); + pgstat_register_inj_fixed(); } diff --git a/src/test/modules/injection_points/injection_stats.h b/src/test/modules/injection_points/injection_stats.h index 3e99705483..d519f29f83 100644 --- a/src/test/modules/injection_points/injection_stats.h +++ b/src/test/modules/injection_points/injection_stats.h @@ -15,9 +15,16 @@ #ifndef INJECTION_STATS #define INJECTION_STATS +/* injection_stats.c */ extern void pgstat_register_inj(void); extern void pgstat_create_inj(const char *name); extern void pgstat_drop_inj(const char *name); extern void pgstat_report_inj(const char *name); +/* injection_stats_fixed.c */ +extern void pgstat_register_inj_fixed(void); +extern void pgstat_report_inj_fixed(uint32 numattach, + uint32 numdetach, + uint32 numrun); + #endif diff --git a/src/test/modules/injection_points/injection_stats_fixed.c b/src/test/modules/injection_points/injection_stats_fixed.c new file mode 100644 index 0000000000..72a5f9decb --- /dev/null +++ b/src/test/modules/injection_points/injection_stats_fixed.c @@ -0,0 +1,192 @@ +/*-------------------------------------------------------------------------- + * + * injection_stats_fixed.c + * Code for fixed-numbered statistics of injection points. + * + * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/test/modules/injection_points/injection_stats_fixed.c + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "fmgr.h" + +#include "common/hashfn.h" +#include "funcapi.h" +#include "injection_stats.h" +#include "pgstat.h" +#include "utils/builtins.h" +#include "utils/pgstat_internal.h" + +/* Structures for statistics of injection points, fixed-size */ +typedef struct PgStat_StatInjFixedEntry +{ + PgStat_Counter numattach; /* number of points attached */ + PgStat_Counter numdetach; /* number of points detached */ + PgStat_Counter numrun; /* number of points run */ + TimestampTz stat_reset_timestamp; +} PgStat_StatInjFixedEntry; + +typedef struct PgStatShared_InjectionPointFixed +{ + LWLock lock; /* protects all the counters */ + uint32 changecount; + PgStat_StatInjFixedEntry stats; + PgStat_StatInjFixedEntry reset_offset; +} PgStatShared_InjectionPointFixed; + +/* Callbacks for fixed-numbered stats */ +static void injection_stats_fixed_init_shmem_cb(void *stats); +static void injection_stats_fixed_reset_all_cb(TimestampTz ts); +static void injection_stats_fixed_snapshot_cb(void); + +static const PgStat_KindInfo injection_stats_fixed = { + .name = "injection_points_fixed", + .fixed_amount = true, + + .shared_size = sizeof(PgStat_StatInjFixedEntry), + .shared_data_off = offsetof(PgStatShared_InjectionPointFixed, stats), + .shared_data_len = sizeof(((PgStatShared_InjectionPointFixed *) 0)->stats), + + .init_shmem_cb = injection_stats_fixed_init_shmem_cb, + .reset_all_cb = injection_stats_fixed_reset_all_cb, + .snapshot_cb = injection_stats_fixed_snapshot_cb, +}; + +/* + * Kind ID reserved for statistics of injection points. + */ +#define PGSTAT_KIND_INJECTION_FIXED 130 + +/* Track if fixed-numbered stats are loaded */ +static bool inj_fixed_loaded = false; + +static void +injection_stats_fixed_init_shmem_cb(void *stats) +{ + PgStatShared_InjectionPointFixed *stats_shmem = + (PgStatShared_InjectionPointFixed *) stats; + + LWLockInitialize(&stats_shmem->lock, LWTRANCHE_PGSTATS_DATA); +} + +static void +injection_stats_fixed_reset_all_cb(TimestampTz ts) +{ + PgStatShared_InjectionPointFixed *stats_shmem = + pgstat_get_custom_shmem_data(PGSTAT_KIND_INJECTION_FIXED); + + LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE); + pgstat_copy_changecounted_stats(&stats_shmem->reset_offset, + &stats_shmem->stats, + sizeof(stats_shmem->stats), + &stats_shmem->changecount); + stats_shmem->stats.stat_reset_timestamp = ts; + LWLockRelease(&stats_shmem->lock); +} + +static void +injection_stats_fixed_snapshot_cb(void) +{ + PgStatShared_InjectionPointFixed *stats_shmem = + pgstat_get_custom_shmem_data(PGSTAT_KIND_INJECTION_FIXED); + PgStat_StatInjFixedEntry *stat_snap = + pgstat_get_custom_snapshot_data(PGSTAT_KIND_INJECTION_FIXED); + PgStat_StatInjFixedEntry *reset_offset = &stats_shmem->reset_offset; + PgStat_StatInjFixedEntry reset; + + pgstat_copy_changecounted_stats(stat_snap, + &stats_shmem->stats, + sizeof(stats_shmem->stats), + &stats_shmem->changecount); + + LWLockAcquire(&stats_shmem->lock, LW_SHARED); + memcpy(&reset, reset_offset, sizeof(stats_shmem->stats)); + LWLockRelease(&stats_shmem->lock); + + /* compensate by reset offsets */ +#define FIXED_COMP(fld) stat_snap->fld -= reset.fld; + FIXED_COMP(numattach); + FIXED_COMP(numdetach); + FIXED_COMP(numrun); +#undef FIXED_COMP +} + +/* + * Workhorse to do the registration work, called in _PG_init(). + */ +void +pgstat_register_inj_fixed(void) +{ + pgstat_register_kind(PGSTAT_KIND_INJECTION_FIXED, &injection_stats_fixed); + + /* mark stats as loaded */ + inj_fixed_loaded = true; +} + +/* + * Report fixed number of statistics for an injection point. + */ +void +pgstat_report_inj_fixed(uint32 numattach, + uint32 numdetach, + uint32 numrun) +{ + PgStatShared_InjectionPointFixed *stats_shmem; + + /* leave if disabled */ + if (!inj_fixed_loaded) + return; + + stats_shmem = pgstat_get_custom_shmem_data(PGSTAT_KIND_INJECTION_FIXED); + + pgstat_begin_changecount_write(&stats_shmem->changecount); + stats_shmem->stats.numattach += numattach; + stats_shmem->stats.numdetach += numdetach; + stats_shmem->stats.numrun += numrun; + pgstat_end_changecount_write(&stats_shmem->changecount); +} + +/* + * SQL function returning fixed-numbered statistics for injection points. + */ +PG_FUNCTION_INFO_V1(injection_points_stats_fixed); +Datum +injection_points_stats_fixed(PG_FUNCTION_ARGS) +{ + TupleDesc tupdesc; + Datum values[3] = {0}; + bool nulls[3] = {0}; + PgStat_StatInjFixedEntry *stats; + + if (!inj_fixed_loaded) + PG_RETURN_NULL(); + + pgstat_snapshot_fixed(PGSTAT_KIND_INJECTION_FIXED); + stats = pgstat_get_custom_snapshot_data(PGSTAT_KIND_INJECTION_FIXED); + + /* Initialise attributes information in the tuple descriptor */ + tupdesc = CreateTemplateTupleDesc(3); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "numattach", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "numdetach", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "numrun", + INT8OID, -1, 0); + BlessTupleDesc(tupdesc); + + values[0] = Int64GetDatum(stats->numattach); + values[1] = Int64GetDatum(stats->numdetach); + values[2] = Int64GetDatum(stats->numrun); + nulls[0] = false; + nulls[1] = false; + nulls[2] = false; + + /* Returns the record as Datum */ + PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls))); +} diff --git a/src/test/modules/injection_points/meson.build b/src/test/modules/injection_points/meson.build index a52fe5121e..c9e357f644 100644 --- a/src/test/modules/injection_points/meson.build +++ b/src/test/modules/injection_points/meson.build @@ -7,6 +7,7 @@ endif injection_points_sources = files( 'injection_points.c', 'injection_stats.c', + 'injection_stats_fixed.c', ) if host_system == 'windows' diff --git a/src/test/modules/injection_points/t/001_stats.pl b/src/test/modules/injection_points/t/001_stats.pl index fff805b46a..7897691f95 100644 --- a/src/test/modules/injection_points/t/001_stats.pl +++ b/src/test/modules/injection_points/t/001_stats.pl @@ -33,18 +33,27 @@ $node->safe_psql('postgres', "SELECT injection_points_run('stats-notice');"); my $numcalls = $node->safe_psql('postgres', "SELECT injection_points_stats_numcalls('stats-notice');"); is($numcalls, '2', 'number of stats calls'); +my $fixedstats = $node->safe_psql('postgres', + "SELECT * FROM injection_points_stats_fixed();"); +is($fixedstats, '1|0|2', 'number of fixed stats'); # Restart the node cleanly, stats should still be around. $node->restart; $numcalls = $node->safe_psql('postgres', "SELECT injection_points_stats_numcalls('stats-notice');"); is($numcalls, '2', 'number of stats after clean restart'); +$fixedstats = $node->safe_psql('postgres', + "SELECT * FROM injection_points_stats_fixed();"); +is($fixedstats, '1|0|2', 'number of fixed stats after clean restart'); # On crash the stats are gone. $node->stop('immediate'); $node->start; $numcalls = $node->safe_psql('postgres', "SELECT injection_points_stats_numcalls('stats-notice');"); -is($numcalls, '', 'number of stats after clean restart'); +is($numcalls, '', 'number of stats after crash'); +$fixedstats = $node->safe_psql('postgres', + "SELECT * FROM injection_points_stats_fixed();"); +is($fixedstats, '0|0|0', 'number of fixed stats after crash'); done_testing(); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 6cabdb02d6..6e6b7c2711 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2120,6 +2120,7 @@ PgStatShared_Database PgStatShared_Function PgStatShared_HashEntry PgStatShared_InjectionPoint +PgStatShared_InjectionPointFixed PgStatShared_IO PgStatShared_Relation PgStatShared_ReplSlot @@ -2152,6 +2153,7 @@ PgStat_SnapshotEntry PgStat_StatDBEntry PgStat_StatFuncEntry PgStat_StatInjEntry +PgStat_StatInjFixedEntry PgStat_StatReplSlotEntry PgStat_StatSubEntry PgStat_StatTabEntry