diff options
author | Michael Paquier <michael@paquier.xyz> | 2023-08-20 15:35:02 +0900 |
---|---|---|
committer | Michael Paquier <michael@paquier.xyz> | 2023-08-20 15:35:02 +0900 |
commit | 1e68e43d3f0ff1dcf4a5926f9d6336b86bda034d (patch) | |
tree | f5ca7fce32380b095180dbdf147d5af176faccdf /src/backend/utils | |
parent | a2a6249cf1a4210caac534e8454a1614d0dd081a (diff) | |
download | postgresql-1e68e43d3f0ff1dcf4a5926f9d6336b86bda034d.tar.gz postgresql-1e68e43d3f0ff1dcf4a5926f9d6336b86bda034d.zip |
Add system view pg_wait_events
This new view, wrapped around a SRF, shows some information known about
wait events, as of:
- Name.
- Type (Activity, I/O, Extension, etc.).
- Description.
All the information retrieved comes from wait_event_names.txt, and the
description is the same as the documentation with filters applied to
remove any XML markups. This view is useful when joined with
pg_stat_activity to get the description of a wait event reported.
Custom wait events for extensions are included in the view.
Original idea by Yves Colin.
Author: Bertrand Drouvot
Reviewed-by: Kyotaro Horiguchi, Masahiro Ikeda, Tom Lane, Michael
Paquier
Discussion: https://postgr.es/m/0e2ae164-dc89-03c3-cf7f-de86378053ac@gmail.com
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/activity/.gitignore | 1 | ||||
-rw-r--r-- | src/backend/utils/activity/Makefile | 8 | ||||
-rw-r--r-- | src/backend/utils/activity/generate-wait_event_types.pl | 56 | ||||
-rw-r--r-- | src/backend/utils/activity/meson.build | 1 | ||||
-rw-r--r-- | src/backend/utils/activity/wait_event.c | 40 | ||||
-rw-r--r-- | src/backend/utils/activity/wait_event_funcs.c | 93 |
6 files changed, 193 insertions, 6 deletions
diff --git a/src/backend/utils/activity/.gitignore b/src/backend/utils/activity/.gitignore index d77079285b4..bd0c0c77729 100644 --- a/src/backend/utils/activity/.gitignore +++ b/src/backend/utils/activity/.gitignore @@ -1,2 +1,3 @@ /pgstat_wait_event.c /wait_event_types.h +/wait_event_funcs_data.c diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activity/Makefile index f1117745d4b..f57cf3958c1 100644 --- a/src/backend/utils/activity/Makefile +++ b/src/backend/utils/activity/Makefile @@ -32,10 +32,14 @@ OBJS = \ pgstat_subscription.o \ pgstat_wal.o \ pgstat_xact.o \ - wait_event.o + wait_event.o \ + wait_event_funcs.o include $(top_srcdir)/src/backend/common.mk +wait_event_funcs.o: wait_event_funcs_data.c +wait_event_funcs_data.c: wait_event_types.h + wait_event.o: pgstat_wait_event.c pgstat_wait_event.c: wait_event_types.h touch $@ @@ -44,4 +48,4 @@ wait_event_types.h: $(top_srcdir)/src/backend/utils/activity/wait_event_names.tx $(PERL) $(srcdir)/generate-wait_event_types.pl --code $< maintainer-clean: clean - rm -f wait_event_types.h pgstat_wait_event.c + rm -f wait_event_types.h pgstat_wait_event.c wait_event_funcs_data.c diff --git a/src/backend/utils/activity/generate-wait_event_types.pl b/src/backend/utils/activity/generate-wait_event_types.pl index 56335e87302..23eb3b1f572 100644 --- a/src/backend/utils/activity/generate-wait_event_types.pl +++ b/src/backend/utils/activity/generate-wait_event_types.pl @@ -4,6 +4,7 @@ # Generate wait events support files from wait_event_names.txt: # - wait_event_types.h (if --code is passed) # - pgstat_wait_event.c (if --code is passed) +# - wait_event_funcs_data.c (if --code is passed) # - wait_event_types.sgml (if --docs is passed) # # Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group @@ -98,8 +99,10 @@ if ($gen_code) # multiple times. my $htmp = "$output_path/wait_event_types.h.tmp$$"; my $ctmp = "$output_path/pgstat_wait_event.c.tmp$$"; + my $wctmp = "$output_path/wait_event_funcs_data.c.tmp$$"; open my $h, '>', $htmp or die "Could not open $htmp: $!"; open my $c, '>', $ctmp or die "Could not open $ctmp: $!"; + open my $wc, '>', $wctmp or die "Could not open $wctmp: $!"; my $header_comment = '/*------------------------------------------------------------------------- @@ -129,12 +132,14 @@ if ($gen_code) printf $c $header_comment, 'pgstat_wait_event.c'; + printf $wc $header_comment, 'wait_event_funcs_data.c'; + + # Generate the pgstat_wait_event.c and wait_event_types.h files # uc() is being used to force the comparison to be case-insensitive. foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe) { - - # Don't generate .c and .h files for Extension, LWLock and - # Lock, these are handled independently. + # Don't generate the pgstat_wait_event.c and wait_event_types.h files + # for Extension, LWLock and Lock, these are handled independently. next if ( $waitclass eq 'WaitEventExtension' || $waitclass eq 'WaitEventLWLock' @@ -183,14 +188,57 @@ if ($gen_code) printf $c "}\n\n"; } + # Generate wait_event_funcs_data.c, building the contents of a static + # C structure holding all the information about the wait events. + # uc() is being used to force the comparison to be case-insensitive, + # even though it is not required here. + foreach my $waitclass (sort { uc($a) cmp uc($b) } keys %hashwe) + { + my $last = $waitclass; + $last =~ s/^WaitEvent//; + + foreach my $wev (@{ $hashwe{$waitclass} }) + { + my $new_desc = substr $wev->[2], 1, -2; + # Escape single quotes. + $new_desc =~ s/'/\\'/g; + + # Replace the "quote" markups by real ones. + $new_desc =~ s/<quote>(.*?)<\/quote>/\\"$1\\"/g; + + # Remove SGML markups. + $new_desc =~ s/<.*?>(.*?)<.*?>/$1/g; + + # Tweak contents about links <xref linkend="text"/> + # on GUCs, + while (my ($capture) = + $new_desc =~ m/<xref linkend="guc-(.*?)"\/>/g) + { + $capture =~ s/-/_/g; + $new_desc =~ s/<xref linkend="guc-.*?"\/>/$capture/g; + } + # Then remove any reference to + # "see <xref linkend="text"/>". + $new_desc =~ s/; see.*$//; + + # Build one element of the C structure holding the + # wait event info, as of (type, name, description). + printf $wc "\t{\"%s\", \"%s\", \"%s\"},\n", $last, $wev->[1], + $new_desc; + } + } + printf $h "#endif /* WAIT_EVENT_TYPES_H */\n"; close $h; close $c; + close $wc; rename($htmp, "$output_path/wait_event_types.h") || die "rename: $htmp to $output_path/wait_event_types.h: $!"; rename($ctmp, "$output_path/pgstat_wait_event.c") || die "rename: $ctmp to $output_path/pgstat_wait_event.c: $!"; + rename($wctmp, "$output_path/wait_event_funcs_data.c") + || die "rename: $wctmp to $output_path/wait_event_funcs_data.c: $!"; } # Generate the .sgml file. elsif ($gen_docs) @@ -249,7 +297,7 @@ Usage: perl [--output <path>] [--code ] [ --sgml ] input_file Options: --outdir Output directory (default '.') - --code Generate wait_event_types.h and pgstat_wait_event.c. + --code Generate C and header files. --sgml Generate wait_event_types.sgml. generate-wait_event_types.pl generates the SGML documentation and code diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build index 9633f3623c4..46a27e75482 100644 --- a/src/backend/utils/activity/meson.build +++ b/src/backend/utils/activity/meson.build @@ -23,6 +23,7 @@ backend_sources += files( # seems nicer to not add that as an include path for the whole backend. waitevent_sources = files( 'wait_event.c', + 'wait_event_funcs.c', ) wait_event = static_library('wait_event_names', diff --git a/src/backend/utils/activity/wait_event.c b/src/backend/utils/activity/wait_event.c index 4b9b5c01cbe..4e112560da1 100644 --- a/src/backend/utils/activity/wait_event.c +++ b/src/backend/utils/activity/wait_event.c @@ -265,6 +265,46 @@ GetWaitEventExtensionIdentifier(uint16 eventId) /* + * Returns a list of currently defined custom wait event names for extensions. + * The result is a palloc'd array, with the number of elements saved in + * *nwaitevents. + */ +char ** +GetWaitEventExtensionNames(int *nwaitevents) +{ + char **waiteventnames; + WaitEventExtensionEntryByName *hentry; + HASH_SEQ_STATUS hash_seq; + int index; + int els; + + LWLockAcquire(WaitEventExtensionLock, LW_SHARED); + + /* Now we can safely count the number of entries */ + els = hash_get_num_entries(WaitEventExtensionHashByName); + + /* Allocate enough space for all entries */ + waiteventnames = palloc(els * sizeof(char *)); + + /* Now scan the hash table to copy the data */ + hash_seq_init(&hash_seq, WaitEventExtensionHashByName); + + index = 0; + while ((hentry = (WaitEventExtensionEntryByName *) hash_seq_search(&hash_seq)) != NULL) + { + waiteventnames[index] = pstrdup(hentry->wait_event_name); + index++; + } + + LWLockRelease(WaitEventExtensionLock); + + Assert(index == els); + + *nwaitevents = index; + return waiteventnames; +} + +/* * Configure wait event reporting to report wait events to *wait_event_info. * *wait_event_info needs to be valid until pgstat_reset_wait_event_storage() * is called. diff --git a/src/backend/utils/activity/wait_event_funcs.c b/src/backend/utils/activity/wait_event_funcs.c new file mode 100644 index 00000000000..6a65f527048 --- /dev/null +++ b/src/backend/utils/activity/wait_event_funcs.c @@ -0,0 +1,93 @@ +/*------------------------------------------------------------------------ + * + * wait_event_funcs.c + * Functions for accessing wait event data. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/utils/activity/wait_event_funcs.c + * + *------------------------------------------------------------------------ + */ +#include "postgres.h" + +#include "funcapi.h" +#include "utils/builtins.h" +#include "utils/wait_event.h" + +/* + * Each wait event has one corresponding entry in this structure, fed to + * the SQL function of this file. + */ +static const struct +{ + const char *type; + const char *name; + const char *description; +} + + waitEventData[] = +{ +#include "wait_event_funcs_data.c" + /* end of list */ + {NULL, NULL, NULL} +}; + + +/* + * pg_get_wait_events + * + * List information about wait events (type, name and description). + */ +Datum +pg_get_wait_events(PG_FUNCTION_ARGS) +{ +#define PG_GET_WAIT_EVENTS_COLS 3 + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + char **waiteventnames; + int nbextwaitevents; + + /* Build tuplestore to hold the result rows */ + InitMaterializedSRF(fcinfo, 0); + + /* Iterate over the list of wait events */ + for (int idx = 0; waitEventData[idx].type != NULL; idx++) + { + Datum values[PG_GET_WAIT_EVENTS_COLS] = {0}; + bool nulls[PG_GET_WAIT_EVENTS_COLS] = {0}; + + values[0] = CStringGetTextDatum(waitEventData[idx].type); + values[1] = CStringGetTextDatum(waitEventData[idx].name); + values[2] = CStringGetTextDatum(waitEventData[idx].description); + + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); + } + + /* Handle custom wait events for extensions */ + waiteventnames = GetWaitEventExtensionNames(&nbextwaitevents); + + for (int idx = 0; idx < nbextwaitevents; idx++) + { + StringInfoData buf; + Datum values[PG_GET_WAIT_EVENTS_COLS] = {0}; + bool nulls[PG_GET_WAIT_EVENTS_COLS] = {0}; + + + values[0] = CStringGetTextDatum("Extension"); + values[1] = CStringGetTextDatum(waiteventnames[idx]); + + initStringInfo(&buf); + appendStringInfo(&buf, + "Waiting for custom wait event \"%s\" defined by extension module", + waiteventnames[idx]); + + values[2] = CStringGetTextDatum(buf.data); + + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); + } + + return (Datum) 0; +} |