From 4fc43c6da0e0be73e3f4dc75fbac8e6e2c543437 Mon Sep 17 00:00:00 2001 From: Haoyu Huang Date: Wed, 19 Nov 2025 22:56:36 +0000 Subject: [PATCH 1/2] c --- src/backend/utils/activity/pgstat.c | 4 ++++ src/backend/utils/activity/wait_event.c | 5 ++--- src/include/pgstat.h | 4 ++++ src/include/utils/wait_event.h | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c index c6783d11411..bc82d2b83a1 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -265,6 +265,8 @@ static bool pgstat_is_initialized = false; static bool pgstat_is_shutdown = false; #endif +pgstat_report_stat_hook_type pgstat_report_stat_hook = NULL; + /* * The different kinds of built-in statistics. @@ -802,6 +804,8 @@ pgstat_report_stat(bool force) pending_since = 0; pgstat_report_fixed = false; + if (pgstat_report_stat_hook) + pgstat_report_stat_hook(force); return 0; } diff --git a/src/backend/utils/activity/wait_event.c b/src/backend/utils/activity/wait_event.c index d9b8f34a355..425340a5f60 100644 --- a/src/backend/utils/activity/wait_event.c +++ b/src/backend/utils/activity/wait_event.c @@ -38,9 +38,8 @@ static const char *pgstat_get_wait_io(WaitEventIO w); static uint32 local_my_wait_event_info; uint32 *my_wait_event_info = &local_my_wait_event_info; - -#define WAIT_EVENT_CLASS_MASK 0xFF000000 -#define WAIT_EVENT_ID_MASK 0x0000FFFF +pgstat_report_wait_start_hook_type pgstat_report_wait_start_hook = NULL; +pgstat_report_wait_end_hook_type pgstat_report_wait_end_hook = NULL; /* * Hash tables for storing custom wait event ids and their names in diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 0a62fb39fe7..5c2a46c58c9 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -839,4 +839,8 @@ extern PGDLLIMPORT PgStat_Counter pgStatTransactionIdleTime; /* updated by the traffic cop and in errfinish() */ extern PGDLLIMPORT SessionEndType pgStatSessionEndCause; +typedef void (*pgstat_report_stat_hook_type)(bool force); +extern PGDLLIMPORT pgstat_report_stat_hook_type pgstat_report_stat_hook; + + #endif /* PGSTAT_H */ diff --git a/src/include/utils/wait_event.h b/src/include/utils/wait_event.h index f5815b4994a..688027b13fc 100644 --- a/src/include/utils/wait_event.h +++ b/src/include/utils/wait_event.h @@ -13,6 +13,9 @@ /* enums for wait events */ #include "utils/wait_event_types.h" +#define WAIT_EVENT_CLASS_MASK 0xFF000000 +#define WAIT_EVENT_ID_MASK 0x0000FFFF + extern const char *pgstat_get_wait_event(uint32 wait_event_info); extern const char *pgstat_get_wait_event_type(uint32 wait_event_info); static inline void pgstat_report_wait_start(uint32 wait_event_info); @@ -22,6 +25,11 @@ extern void pgstat_reset_wait_event_storage(void); extern PGDLLIMPORT uint32 *my_wait_event_info; +typedef void (*pgstat_report_wait_start_hook_type)(uint32 wait_event_info); +extern PGDLLIMPORT pgstat_report_wait_start_hook_type pgstat_report_wait_start_hook; +typedef void (*pgstat_report_wait_end_hook_type)(uint32 wait_event_info); +extern PGDLLIMPORT pgstat_report_wait_end_hook_type pgstat_report_wait_end_hook; + /* * Wait Events - Extension, InjectionPoint @@ -73,6 +81,9 @@ pgstat_report_wait_start(uint32 wait_event_info) * four-bytes, updates are atomic. */ *(volatile uint32 *) my_wait_event_info = wait_event_info; + + if (pgstat_report_wait_start_hook) + pgstat_report_wait_start_hook(wait_event_info); } /* ---------- @@ -84,6 +95,9 @@ pgstat_report_wait_start(uint32 wait_event_info) static inline void pgstat_report_wait_end(void) { + if (pgstat_report_wait_end_hook) + pgstat_report_wait_end_hook(*(volatile uint32 *) my_wait_event_info); + /* see pgstat_report_wait_start() */ *(volatile uint32 *) my_wait_event_info = 0; } From a4a54cfde02f3800f6065f30ebd9d43946c68b3a Mon Sep 17 00:00:00 2001 From: Haoyu Huang Date: Wed, 19 Nov 2025 23:22:28 +0000 Subject: [PATCH 2/2] c --- src/backend/Makefile | 2 +- src/backend/utils/activity/Makefile | 4 +- .../activity/generate-wait_event_types.pl | 257 +++++++++++++++++- src/include/utils/meson.build | 4 +- 4 files changed, 261 insertions(+), 6 deletions(-) diff --git a/src/backend/Makefile b/src/backend/Makefile index 7344c8c7f5c..685d7a0a77e 100644 --- a/src/backend/Makefile +++ b/src/backend/Makefile @@ -114,7 +114,7 @@ parser/gram.h: parser/gram.y storage/lmgr/lwlocknames.h: storage/lmgr/generate-lwlocknames.pl ../include/storage/lwlocklist.h utils/activity/wait_event_names.txt $(MAKE) -C storage/lmgr lwlocknames.h -utils/activity/wait_event_types.h: utils/activity/generate-wait_event_types.pl utils/activity/wait_event_names.txt +utils/activity/wait_event_types.h: utils/activity/generate-wait_event_types.pl utils/activity/wait_event_names.txt ../include/storage/lwlocklist.h ../include/utils/wait_classes.h $(MAKE) -C utils/activity wait_event_types.h pgstat_wait_event.c wait_event_funcs_data.c # run this unconditionally to avoid needing to know its dependencies here: diff --git a/src/backend/utils/activity/Makefile b/src/backend/utils/activity/Makefile index 9c2443e1ecd..f9849bebc98 100644 --- a/src/backend/utils/activity/Makefile +++ b/src/backend/utils/activity/Makefile @@ -45,8 +45,8 @@ wait_event.o: pgstat_wait_event.c pgstat_wait_event.c: wait_event_types.h touch $@ -wait_event_types.h: $(top_srcdir)/src/backend/utils/activity/wait_event_names.txt generate-wait_event_types.pl - $(PERL) $(srcdir)/generate-wait_event_types.pl --code $< +wait_event_types.h: $(top_srcdir)/src/backend/utils/activity/wait_event_names.txt $(top_srcdir)/src/include/storage/lwlocklist.h $(top_srcdir)/src/include/utils/wait_classes.h generate-wait_event_types.pl + $(PERL) $(srcdir)/generate-wait_event_types.pl --code $(wordlist 1,3,$^) clean: 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 424ad9f115d..623a7aa5e85 100644 --- a/src/backend/utils/activity/generate-wait_event_types.pl +++ b/src/backend/utils/activity/generate-wait_event_types.pl @@ -21,6 +21,12 @@ my $output_path = '.'; my $gen_docs = 0; my $gen_code = 0; +my $nb_waitclass_table_entries = 0; +my $nb_wait_events_with_null = 0; +my $nb_wait_events_per_class = 0; +my %waitclass_values; +my $wait_event_class_mask = 0xFF000000; +my $wait_event_id_mask = 0x0000FFFF; my $continue = "\n"; my %hashwe; @@ -38,11 +44,50 @@ open my $wait_event_names, '<', $ARGV[0] or die; +# When generating code, we need lwlocklist.h as the second argument +my $lwlocklist_file = $ARGV[1] if $gen_code; + +# When generating code, we need wait_classes.h as the third argument +my $wait_classes_file = $ARGV[2] if $gen_code; + my @abi_compatibility_lines; my @lines; my $abi_compatibility = 0; my $section_name; +# Function to parse wait_classes.h and extract wait class definitions +sub parse_wait_classes_header +{ + + open my $wait_classes_header, '<', $wait_classes_file + or die "Could not open $wait_classes_file: $!"; + + while (<$wait_classes_header>) + { + chomp; + if (/^\s*#define\s+(PG_WAIT_\w+)\s+(0x[0-9A-Fa-f]+)U?\s*$/) + { + my ($macro_name, $value) = ($1, $2); + + $waitclass_values{$macro_name} = $value; + } + } + + close $wait_classes_header; +} + +# Function to get the macro from the wait class name +sub waitclass_to_macro +{ + + my $waitclass = shift; + my $last = $waitclass; + $last =~ s/^WaitEvent//; + my $lastuc = uc $last; + + return "PG_WAIT_" . $lastuc; +} + # Remove comments and empty lines and add waitclassname based on the section while (<$wait_event_names>) { @@ -84,8 +129,39 @@ # Sort the lines based on the second column. # uc() is being used to force the comparison to be case-insensitive. -my @lines_sorted = - sort { uc((split(/\t/, $a))[1]) cmp uc((split(/\t/, $b))[1]) } @lines; + +my @lines_sorted; +if ($gen_code) +{ + my @lwlock_lines; + + # Separate LWLock lines from others + foreach my $line (@lines) + { + if ($line =~ /^WaitEventLWLock\t/) + { + push(@lwlock_lines, $line); + } + else + { + push(@lines_sorted, $line); + } + } + + # Sort only non-LWLock lines + @lines_sorted = + sort { uc((split(/\t/, $a))[1]) cmp uc((split(/\t/, $b))[1]) } + @lines_sorted; + + # Add LWLock lines back in their original order + push(@lines_sorted, @lwlock_lines); +} +else +{ + # For docs, use original alphabetical sorting for all + @lines_sorted = + sort { uc((split(/\t/, $a))[1]) cmp uc((split(/\t/, $b))[1]) } @lines; +} # If we are generating code, concat @lines_sorted and then # @abi_compatibility_lines. @@ -165,9 +241,22 @@ '; + my $wait_event_class_shift = 0; + my $temp_mask = $wait_event_class_mask; + while (($temp_mask & 1) == 0 && $temp_mask != 0) + { + $wait_event_class_shift++; + $temp_mask >>= 1; + } + printf $h $header_comment, 'wait_event_types.h'; printf $h "#ifndef WAIT_EVENT_TYPES_H\n"; printf $h "#define WAIT_EVENT_TYPES_H\n\n"; + printf $h "#define WAIT_EVENT_CLASS_MASK 0x%08X\n", + $wait_event_class_mask; + printf $h "#define WAIT_EVENT_ID_MASK 0x%08X\n", $wait_event_id_mask; + printf $h "#define WAIT_EVENT_CLASS_SHIFT %d\n\n", + $wait_event_class_shift; printf $h "#include \"utils/wait_classes.h\"\n\n"; printf $c $header_comment, 'pgstat_wait_event.c'; @@ -269,6 +358,170 @@ } } + printf $h " + +/* To represent wait_event_info as integers */ +typedef struct DecodedWaitInfo +{ + int classId; + int eventId; +} DecodedWaitInfo; + +/* To extract classId and eventId as integers from wait_event_info */ +#define WAIT_EVENT_INFO_DECODE(d, i) \\ + d.classId = ((i) & WAIT_EVENT_CLASS_MASK) / (WAIT_EVENT_CLASS_MASK & (-WAIT_EVENT_CLASS_MASK)), \\ + d.eventId = (i) & WAIT_EVENT_ID_MASK + +/* To encode wait_event_info from classId and eventId as integers */ +#define ENCODE_WAIT_EVENT_INFO(classId, eventId) \\ + (((classId) << WAIT_EVENT_CLASS_SHIFT) | ((eventId) & WAIT_EVENT_ID_MASK)) + +/* To map wait event classes into the WaitClassTable */ +typedef struct +{ + const int classId; + const int numberOfEvents; + const int offSet; + const char *className; + const char *const *eventNames; +} WaitClassTableEntry; + +extern WaitClassTableEntry WaitClassTable[];\n\n"; + + printf $c " +/* + * Lookup table that is used by the wait events statistics. + * Indexed by classId (derived from the PG_WAIT_* constants), handle gaps + * in the class ID numbering and provide metadata for wait events. + */ +WaitClassTableEntry WaitClassTable[] = {\n"; + + parse_wait_classes_header(); + my $next_index = 0; + my $class_divisor = $wait_event_class_mask & (-$wait_event_class_mask); + + foreach my $waitclass ( + sort { + my $macro_a = waitclass_to_macro($a); + my $macro_b = waitclass_to_macro($b); + hex($waitclass_values{$macro_a}) <=> + hex($waitclass_values{$macro_b}) + } keys %hashwe) + { + my $event_names_array; + my $array_size; + my $last = $waitclass; + $last =~ s/^WaitEvent//; + + $nb_waitclass_table_entries++; + + # The LWLocks need to be handled differently than the other classes when + # building the WaitClassTable. We need to take care of the prefedined + # LWLocks as well as the additional ones. + if ($waitclass eq 'WaitEventLWLock') + { + # Parse lwlocklist.h to get LWLock definitions + open my $lwlocklist, '<', $lwlocklist_file + or die "Could not open $lwlocklist_file: $!"; + + my %predefined_lwlock_indices; + my $max_lwlock_index = -1; + + while (<$lwlocklist>) + { + if (/^PG_LWLOCK\((\d+),\s+(\w+)\)$/) + { + my ($lockidx, $lockname) = ($1, $2); + $predefined_lwlock_indices{$lockname} = $lockidx; + $max_lwlock_index = $lockidx + if $lockidx > $max_lwlock_index; + } + } + + close $lwlocklist; + + # Iterates through wait_event_names.txt order + my @event_names_sparse; + my $next_additional_index = $max_lwlock_index + 1; + + foreach my $wev (@{ $hashwe{$waitclass} }) + { + my $lockname = $wev->[1]; + + if (exists $predefined_lwlock_indices{$lockname}) + { + # This is a predefined one, place it at its specific index + my $index = $predefined_lwlock_indices{$lockname}; + $event_names_sparse[$index] = "\"$lockname\""; + } + else + { + # This is an additional one, append it after predefined ones + $event_names_sparse[$next_additional_index] = + "\"$lockname\""; + $next_additional_index++; + } + } + + # Fill gaps with NULL for missing predefined locks + for my $i (0 .. $max_lwlock_index) + { + $event_names_sparse[$i] = "NULL" + unless defined $event_names_sparse[$i]; + } + + # Build the array literal + $event_names_array = "(const char *const []){" + . join(", ", @event_names_sparse) . "}"; + $array_size = scalar(@event_names_sparse); + } + else + { + # Construct a simple string array literal for this class + $event_names_array = "(const char *const []){"; + + # For each wait event in this class, add its name to the array + foreach my $wev (@{ $hashwe{$waitclass} }) + { + $event_names_array .= "\"$wev->[1]\", "; + } + + $event_names_array .= "}"; + $array_size = scalar(@{ $hashwe{$waitclass} }); + } + + my $lastuc = uc $last; + my $pg_wait_class = "PG_WAIT_" . $lastuc; + + my $index = hex($waitclass_values{$pg_wait_class}) / $class_divisor; + + # Fill any holes with {0, 0, 0, NULL, NULL} + while ($next_index < $index) + { + printf $c "{0, 0, 0, NULL, NULL},\n"; + $next_index++; + $nb_waitclass_table_entries++; + } + + my $offset = $nb_wait_events_with_null; + $nb_wait_events_with_null += $array_size; + + # Generate the entry + printf $c "{$pg_wait_class, $array_size, $offset, \"%s\", %s},\n", + $last, $event_names_array; + + $next_index = $index + 1; + } + + printf $c "};\n\n"; + + printf $h "#define NB_WAITCLASSTABLE_SIZE $nb_wait_events_with_null\n"; + printf $h + "#define NB_WAITCLASSTABLE_ENTRIES $nb_waitclass_table_entries\n\n"; + printf $h + "StaticAssertDecl(NB_WAITCLASSTABLE_SIZE > 0, \"Wait class table must have entries\");\n"; + printf $h + "StaticAssertDecl(NB_WAITCLASSTABLE_ENTRIES > 0, \"Must have at least one wait class\");\n"; printf $h "#endif /* WAIT_EVENT_TYPES_H */\n"; close $h; close $c; diff --git a/src/include/utils/meson.build b/src/include/utils/meson.build index 78c6b9b0a23..ff519242de7 100644 --- a/src/include/utils/meson.build +++ b/src/include/utils/meson.build @@ -2,7 +2,9 @@ wait_event_output = ['wait_event_types.h', 'pgstat_wait_event.c', 'wait_event_funcs_data.c'] wait_event_target = custom_target('wait_event_names', - input: files('../../backend/utils/activity/wait_event_names.txt'), + input: files('../../backend/utils/activity/wait_event_names.txt', + '../../include/storage/lwlocklist.h', + '../../include/utils/wait_classes.h'), output: wait_event_output, command: [ perl, files('../../backend/utils/activity/generate-wait_event_types.pl'),