From 72cea20702c26d55b333cafb6bc1706296c9bdb6 Mon Sep 17 00:00:00 2001 From: Dmitry Fomin Date: Sun, 7 Jun 2026 20:31:49 +0000 Subject: [PATCH v2 1/7] wait_event_timing: add --enable-wait-event-timing flag and wait_event_capture GUC Introduce the compile-time option --enable-wait-event-timing (meson: -Dwait_event_timing=true) defining USE_WAIT_EVENT_TIMING, and the runtime GUC wait_event_capture (PGC_SUSET, enum off|stats, default off). This commit is scaffolding only: it wires the flag through both build systems and registers the GUC with check/assign hooks, but adds no instrumentation yet -- later commits in this series add the recording hot path, the SQL surface, and the trace level. In builds compiled without --enable-wait-event-timing, the GUC's check hook rejects any value other than off (downgrading to off with a warning for non-interactive sources), so the variable exists uniformly for tooling but cannot be enabled. --- configure | 32 +++++ configure.ac | 8 ++ meson.build | 1 + meson_options.txt | 3 + src/backend/utils/activity/Makefile | 3 +- src/backend/utils/activity/meson.build | 1 + .../utils/activity/wait_event_timing.c | 109 ++++++++++++++++++ src/backend/utils/misc/guc_parameters.dat | 9 ++ src/backend/utils/misc/guc_tables.c | 1 + src/backend/utils/misc/postgresql.conf.sample | 2 + src/include/pg_config.h.in | 3 + src/include/utils/guc.h | 1 + src/include/utils/guc_hooks.h | 2 + src/include/utils/wait_event_timing.h | 53 +++++++++ src/tools/pgindent/typedefs.list | 1 + 15 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 src/backend/utils/activity/wait_event_timing.c create mode 100644 src/include/utils/wait_event_timing.h diff --git a/configure b/configure index 35b0b72f0a7..a20dc7cd69e 100755 --- a/configure +++ b/configure @@ -850,6 +850,7 @@ enable_debug enable_profiling enable_coverage enable_dtrace +enable_wait_event_timing enable_tap_tests enable_injection_points with_blocksize @@ -1551,6 +1552,8 @@ Optional Features: --enable-profiling build with profiling enabled --enable-coverage build with coverage testing instrumentation --enable-dtrace build with DTrace support + --enable-wait-event-timing + build with wait event timing instrumentation --enable-tap-tests enable TAP tests (requires Perl and IPC::Run) --enable-injection-points enable injection points (for testing) @@ -3633,6 +3636,35 @@ fi +# +# --enable-wait-event-timing adds wait event timing instrumentation +# + + +# Check whether --enable-wait-event-timing was given. +if test "${enable_wait_event_timing+set}" = set; then : + enableval=$enable_wait_event_timing; + case $enableval in + yes) + +$as_echo "#define USE_WAIT_EVENT_TIMING 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --enable-wait-event-timing option" "$LINENO" 5 + ;; + esac + +else + enable_wait_event_timing=no + +fi + + + # # TAP tests # diff --git a/configure.ac b/configure.ac index 0e624fe36b9..bafe6cc1fce 100644 --- a/configure.ac +++ b/configure.ac @@ -225,6 +225,14 @@ fi AC_SUBST(DTRACEFLAGS)]) AC_SUBST(enable_dtrace) +# +# --enable-wait-event-timing adds wait event timing instrumentation +# +PGAC_ARG_BOOL(enable, wait-event-timing, no, + [build with wait event timing instrumentation], + [AC_DEFINE([USE_WAIT_EVENT_TIMING], 1, + [Define to 1 to build with wait event timing. (--enable-wait-event-timing)])]) + # # TAP tests # diff --git a/meson.build b/meson.build index d88a7a70308..93264e83230 100644 --- a/meson.build +++ b/meson.build @@ -505,6 +505,7 @@ meson_bin = find_program(meson_binpath, native: true) cdata.set('USE_ASSERT_CHECKING', get_option('cassert') ? 1 : false) cdata.set('USE_INJECTION_POINTS', get_option('injection_points') ? 1 : false) +cdata.set('USE_WAIT_EVENT_TIMING', get_option('wait_event_timing') ? 1 : false) blocksize = get_option('blocksize').to_int() * 1024 diff --git a/meson_options.txt b/meson_options.txt index 6a793f3e479..1f191d3a9d6 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -40,6 +40,9 @@ option('pgport', type: 'integer', value: 5432, option('cassert', type: 'boolean', value: false, description: 'Enable assertion checks (for debugging)') +option('wait_event_timing', type: 'boolean', value: false, + description: 'Enable wait event timing instrumentation') + option('tap_tests', type: 'feature', value: 'auto', description: 'Enable TAP tests') diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activity/Makefile index 5fed953c28a..1c824e9b788 100644 --- a/src/backend/utils/activity/Makefile +++ b/src/backend/utils/activity/Makefile @@ -36,7 +36,8 @@ OBJS = \ pgstat_wal.o \ pgstat_xact.o \ wait_event.o \ - wait_event_funcs.o + wait_event_funcs.o \ + wait_event_timing.o # Force these dependencies to be known even without dependency info built: wait_event.o: wait_event.c $(top_builddir)/src/backend/utils/pgstat_wait_event.c diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build index 470b5dac402..13a85bb0d6a 100644 --- a/src/backend/utils/activity/meson.build +++ b/src/backend/utils/activity/meson.build @@ -20,6 +20,7 @@ backend_sources += files( 'pgstat_subscription.c', 'pgstat_wal.c', 'pgstat_xact.c', + 'wait_event_timing.c', ) # this includes a .c file with contents generated in ../../../include/activity, diff --git a/src/backend/utils/activity/wait_event_timing.c b/src/backend/utils/activity/wait_event_timing.c new file mode 100644 index 00000000000..d11823adc57 --- /dev/null +++ b/src/backend/utils/activity/wait_event_timing.c @@ -0,0 +1,109 @@ +/*------------------------------------------------------------------------- + * + * wait_event_timing.c + * Per-backend wait event timing instrumentation. + * + * Controlled by the wait_event_capture GUC (off | stats, default off) + * and the compile-time option --enable-wait-event-timing. + * + * This commit provides only the GUC scaffolding: the backing variable, + * the enum-value table consumed by guc.c, and the check/assign hooks. + * No instrumentation is performed yet -- later commits in the series add + * the recording hot path and the SQL surface. The file compiles in both + * build configurations; in builds without --enable-wait-event-timing the + * check hook rejects any value other than off. + * + * Copyright (c) 2026, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/utils/activity/wait_event_timing.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "utils/guc.h" +#include "utils/guc_hooks.h" +#include "utils/wait_event_timing.h" + +/* + * GUC variable -- always defined so the GUC system has a backing variable + * even when compiled without --enable-wait-event-timing. In stub builds + * the check hook below rejects any value other than OFF. + */ +int wait_event_capture = WAIT_EVENT_CAPTURE_OFF; + +/* + * Enum value table consumed by guc.c. Order matches the + * WaitEventCaptureLevel enum and the documented "off < stats" ordering. + */ +const struct config_enum_entry wait_event_capture_options[] = { + {"off", WAIT_EVENT_CAPTURE_OFF, false}, + {"stats", WAIT_EVENT_CAPTURE_STATS, false}, + {NULL, 0, false} +}; + +#ifdef USE_WAIT_EVENT_TIMING + +/* + * GUC check hook for wait_event_capture (timing build). + * + * All enum values are accepted at this level. Side effects (attaching + * storage, etc.) are introduced by later commits; for now there is + * nothing to validate beyond the enum mapping that guc.c already did. + */ +bool +check_wait_event_capture(int *newval, void **extra, GucSource source) +{ + return true; +} + +/* + * GUC assign hook for wait_event_capture (timing build). + * + * No-op for now. Later commits use this hook to drop in-flight wait + * state and manage per-session resources when the capture level changes. + */ +void +assign_wait_event_capture(int newval, void *extra) +{ +} + +#else /* !USE_WAIT_EVENT_TIMING */ + +/* + * GUC check hook for the stub build. Any value other than 'off' is + * meaningless without --enable-wait-event-timing, so reject it -- or + * downgrade to 'off' with a warning when the value comes from a + * non-interactive source (config file at startup), so a leftover setting + * does not prevent the server from starting. + */ +bool +check_wait_event_capture(int *newval, void **extra, GucSource source) +{ + if (*newval != WAIT_EVENT_CAPTURE_OFF) + { + if (source < PGC_S_INTERACTIVE) + { + ereport(WARNING, + (errmsg("wait_event_capture is not supported by this build, " + "forcing to \"off\""), + errhint("Compile PostgreSQL with " + "--enable-wait-event-timing."))); + *newval = WAIT_EVENT_CAPTURE_OFF; + return true; + } + GUC_check_errdetail("This build does not support wait event capture."); + GUC_check_errhint("Compile PostgreSQL with --enable-wait-event-timing."); + return false; + } + return true; +} + +/* Stub assign hook -- nothing to do without compile-time support. */ +void +assign_wait_event_capture(int newval, void *extra) +{ +} + +#endif /* USE_WAIT_EVENT_TIMING */ diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat index c9118e71988..2e2701cb0a2 100644 --- a/src/backend/utils/misc/guc_parameters.dat +++ b/src/backend/utils/misc/guc_parameters.dat @@ -3433,6 +3433,15 @@ boot_val => 'true', }, +{ name => 'wait_event_capture', type => 'enum', context => 'PGC_SUSET', group => 'STATS_CUMULATIVE', + short_desc => 'Controls collection of per-wait-event timing statistics.', + variable => 'wait_event_capture', + boot_val => 'WAIT_EVENT_CAPTURE_OFF', + options => 'wait_event_capture_options', + check_hook => 'check_wait_event_capture', + assign_hook => 'assign_wait_event_capture', +}, + { name => 'wal_block_size', type => 'int', context => 'PGC_INTERNAL', group => 'PRESET_OPTIONS', short_desc => 'Shows the block size in the write ahead log.', flags => 'GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE', diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 90aa374b3ec..1a99e14bab2 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -103,6 +103,7 @@ #include "utils/plancache.h" #include "utils/ps_status.h" #include "utils/rls.h" +#include "utils/wait_event_timing.h" #include "utils/xml.h" #ifdef TRACE_SYNCSCAN diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index d7942f50a70..ea0391c4952 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -703,6 +703,8 @@ #track_cost_delay_timing = off #track_io_timing = off #track_wal_io_timing = off +#wait_event_capture = off # off, stats + # (requires --enable-wait-event-timing) #track_functions = none # none, pl, all #stats_fetch_consistency = cache # cache, none, snapshot diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 4f8113c144b..ed0b7f26f9f 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -762,6 +762,9 @@ /* Define to select unnamed POSIX semaphores. */ #undef USE_UNNAMED_POSIX_SEMAPHORES +/* Define to 1 to build with wait event timing. (--enable-wait-event-timing) */ +#undef USE_WAIT_EVENT_TIMING + /* Define to select Win32-style semaphores. */ #undef USE_WIN32_SEMAPHORES diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 8057d7870ad..285be02ef58 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -347,6 +347,7 @@ extern PGDLLIMPORT const struct config_enum_entry dynamic_shared_memory_options[ extern PGDLLIMPORT const struct config_enum_entry io_method_options[]; extern PGDLLIMPORT const struct config_enum_entry recovery_target_action_options[]; extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[]; +extern PGDLLIMPORT const struct config_enum_entry wait_event_capture_options[]; extern PGDLLIMPORT const struct config_enum_entry wal_level_options[]; extern PGDLLIMPORT const struct config_enum_entry wal_sync_method_options[]; diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h index 307f4fbaefe..7896e9c7239 100644 --- a/src/include/utils/guc_hooks.h +++ b/src/include/utils/guc_hooks.h @@ -172,6 +172,8 @@ extern bool check_transaction_isolation(int *newval, void **extra, GucSource sou extern bool check_transaction_read_only(bool *newval, void **extra, GucSource source); extern void assign_transaction_timeout(int newval, void *extra); extern const char *show_unix_socket_permissions(void); +extern bool check_wait_event_capture(int *newval, void **extra, GucSource source); +extern void assign_wait_event_capture(int newval, void *extra); extern bool check_wal_buffers(int *newval, void **extra, GucSource source); extern bool check_wal_consistency_checking(char **newval, void **extra, GucSource source); diff --git a/src/include/utils/wait_event_timing.h b/src/include/utils/wait_event_timing.h new file mode 100644 index 00000000000..b44d19ecf35 --- /dev/null +++ b/src/include/utils/wait_event_timing.h @@ -0,0 +1,53 @@ +/*------------------------------------------------------------------------- + * + * wait_event_timing.h + * Per-backend wait event timing instrumentation. + * + * This header declares the public surface of the wait-event-timing + * feature, gated by the compile-time option --enable-wait-event-timing + * (USE_WAIT_EVENT_TIMING) and the runtime GUC wait_event_capture. + * + * This commit introduces only the scaffolding: the capture-level enum + * and the GUC backing variable. Later commits in the series add the + * recording hot path, the SQL-visible statistics, and the per-session + * trace ring. + * + * Copyright (c) 2026, PostgreSQL Global Development Group + * + * src/include/utils/wait_event_timing.h + *------------------------------------------------------------------------- + */ +#ifndef WAIT_EVENT_TIMING_H +#define WAIT_EVENT_TIMING_H + +#include "c.h" + +/* + * Capture levels for the wait_event_capture GUC. Order is significant: + * higher values are strict supersets of lower ones, so code paths can + * test for activation with "level >= WAIT_EVENT_CAPTURE_STATS". + * + * OFF - No instrumentation, no hot-path cost. + * STATS - Aggregated per-event statistics (added by a later commit). + * + * A further TRACE level is added later in the series. + */ +typedef enum WaitEventCaptureLevel +{ + WAIT_EVENT_CAPTURE_OFF = 0, + WAIT_EVENT_CAPTURE_STATS, +} WaitEventCaptureLevel; + +/* + * Pin the enum ordering at compile time so future code that compares with + * >= against WAIT_EVENT_CAPTURE_STATS keeps working, and so reordering is + * caught at build time rather than via mysterious runtime mode switches. + */ +StaticAssertDecl(WAIT_EVENT_CAPTURE_OFF == 0 && + WAIT_EVENT_CAPTURE_STATS == 1, + "WaitEventCaptureLevel values must be 0=OFF < 1=STATS"); + +/* GUC variable (defined in wait_event_timing.c, even in stub builds). */ +extern PGDLLIMPORT int wait_event_capture; + +#endif /* WAIT_EVENT_TIMING_H */ diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 117e7379f10..67eb7ea00a9 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -3418,6 +3418,7 @@ WaitEvent WaitEventActivity WaitEventBuffer WaitEventClient +WaitEventCaptureLevel WaitEventCustomCounterData WaitEventCustomEntryByInfo WaitEventCustomEntryByName -- 2.43.0