LCOV - code coverage report
Current view: top level - src/backend/utils/activity - pgstat.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 85.0 % 555 472
Test Date: 2026-08-17 21:15:38 Functions: 100.0 % 35 35
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 66.2 % 420 278

             Branch data     Line data    Source code
       1                 :             : /* ----------
       2                 :             :  * pgstat.c
       3                 :             :  *    Infrastructure for the cumulative statistics system.
       4                 :             :  *
       5                 :             :  * The cumulative statistics system accumulates statistics for different kinds
       6                 :             :  * of objects. Some kinds of statistics are collected for a fixed number of
       7                 :             :  * objects (most commonly 1), e.g., checkpointer statistics. Other kinds of
       8                 :             :  * statistics are collected for a varying number of objects
       9                 :             :  * (e.g. relations). See PgStat_KindInfo for a list of currently handled
      10                 :             :  * statistics.
      11                 :             :  *
      12                 :             :  * Statistics are loaded from the filesystem during startup (by the startup
      13                 :             :  * process), unless preceded by a crash, in which case all stats are
      14                 :             :  * discarded. They are written out by the checkpointer process just before
      15                 :             :  * shutting down (if the stats kind allows it), except when shutting down in
      16                 :             :  * immediate mode.
      17                 :             :  *
      18                 :             :  * Fixed-numbered stats are stored in plain (non-dynamic) shared memory.
      19                 :             :  *
      20                 :             :  * Statistics for variable-numbered objects are stored in dynamic shared
      21                 :             :  * memory and can be found via a dshash hashtable. The statistics counters are
      22                 :             :  * not part of the dshash entry (PgStatShared_HashEntry) directly, but are
      23                 :             :  * separately allocated (PgStatShared_HashEntry->body). The separate
      24                 :             :  * allocation allows different kinds of statistics to be stored in the same
      25                 :             :  * hashtable without wasting space in PgStatShared_HashEntry.
      26                 :             :  *
      27                 :             :  * Variable-numbered stats are addressed by PgStat_HashKey while running.  It
      28                 :             :  * is not possible to have statistics for an object that cannot be addressed
      29                 :             :  * that way at runtime. A wider identifier can be used when serializing to
      30                 :             :  * disk (used for replication slot stats).
      31                 :             :  *
      32                 :             :  * To avoid contention on the shared hashtable, each backend has a
      33                 :             :  * backend-local hashtable (pgStatEntryRefHash) in front of the shared
      34                 :             :  * hashtable, containing references (PgStat_EntryRef) to shared hashtable
      35                 :             :  * entries. The shared hashtable only needs to be accessed when no prior
      36                 :             :  * reference is found in the local hashtable. Besides pointing to the
      37                 :             :  * shared hashtable entry (PgStatShared_HashEntry) PgStat_EntryRef also
      38                 :             :  * contains a pointer to the shared statistics data, as a process-local
      39                 :             :  * address, to reduce access costs.
      40                 :             :  *
      41                 :             :  * The names for structs stored in shared memory are prefixed with
      42                 :             :  * PgStatShared instead of PgStat. Each stats entry in shared memory is
      43                 :             :  * protected by a dedicated lwlock.
      44                 :             :  *
      45                 :             :  * Most stats updates are first accumulated locally in each process as pending
      46                 :             :  * entries, then later flushed to shared memory (just after commit, or by
      47                 :             :  * idle-timeout). This practically eliminates contention on individual stats
      48                 :             :  * entries. For most kinds of variable-numbered pending stats data is stored
      49                 :             :  * in PgStat_EntryRef->pending. All entries with pending data are in the
      50                 :             :  * pgStatPending list. Pending statistics updates are flushed out by
      51                 :             :  * pgstat_report_stat().
      52                 :             :  *
      53                 :             :  * It is possible for external modules to define custom statistics kinds,
      54                 :             :  * that can use the same properties as any built-in stats kinds.  Each custom
      55                 :             :  * stats kind needs to assign a unique ID to ensure that it does not overlap
      56                 :             :  * with other extensions.  In order to reserve a unique stats kind ID, refer
      57                 :             :  * to https://wiki.postgresql.org/wiki/CustomCumulativeStats.
      58                 :             :  *
      59                 :             :  * The behavior of different kinds of statistics is determined by the kind's
      60                 :             :  * entry in pgstat_kind_builtin_infos for all the built-in statistics kinds
      61                 :             :  * defined, and pgstat_kind_custom_infos for custom kinds registered at
      62                 :             :  * startup by pgstat_register_kind().  See PgStat_KindInfo for details.
      63                 :             :  *
      64                 :             :  * The consistency of read accesses to statistics can be configured using the
      65                 :             :  * stats_fetch_consistency GUC (see config.sgml and monitoring.sgml for the
      66                 :             :  * settings). When using PGSTAT_FETCH_CONSISTENCY_CACHE or
      67                 :             :  * PGSTAT_FETCH_CONSISTENCY_SNAPSHOT statistics are stored in
      68                 :             :  * pgStatLocal.snapshot.
      69                 :             :  *
      70                 :             :  * To keep things manageable, stats handling is split across several
      71                 :             :  * files. Infrastructure pieces are in:
      72                 :             :  * - pgstat.c - this file, to tie it all together
      73                 :             :  * - pgstat_shmem.c - nearly everything dealing with shared memory, including
      74                 :             :  *   the maintenance of hashtable entries
      75                 :             :  * - pgstat_xact.c - transactional integration, including the transactional
      76                 :             :  *   creation and dropping of stats entries
      77                 :             :  *
      78                 :             :  * Each statistics kind is handled in a dedicated file:
      79                 :             :  * - pgstat_archiver.c
      80                 :             :  * - pgstat_backend.c
      81                 :             :  * - pgstat_bgwriter.c
      82                 :             :  * - pgstat_checkpointer.c
      83                 :             :  * - pgstat_database.c
      84                 :             :  * - pgstat_function.c
      85                 :             :  * - pgstat_io.c
      86                 :             :  * - pgstat_lock.c
      87                 :             :  * - pgstat_relation.c
      88                 :             :  * - pgstat_replslot.c
      89                 :             :  * - pgstat_slru.c
      90                 :             :  * - pgstat_subscription.c
      91                 :             :  * - pgstat_wal.c
      92                 :             :  *
      93                 :             :  * Whenever possible infrastructure files should not contain code related to
      94                 :             :  * specific kinds of stats.
      95                 :             :  *
      96                 :             :  *
      97                 :             :  * Copyright (c) 2001-2026, PostgreSQL Global Development Group
      98                 :             :  *
      99                 :             :  * IDENTIFICATION
     100                 :             :  *    src/backend/utils/activity/pgstat.c
     101                 :             :  * ----------
     102                 :             :  */
     103                 :             : #include "postgres.h"
     104                 :             : 
     105                 :             : #include <unistd.h>
     106                 :             : 
     107                 :             : #include "access/xact.h"
     108                 :             : #include "lib/dshash.h"
     109                 :             : #include "pgstat.h"
     110                 :             : #include "storage/fd.h"
     111                 :             : #include "storage/ipc.h"
     112                 :             : #include "storage/lwlock.h"
     113                 :             : #include "utils/guc_hooks.h"
     114                 :             : #include "utils/memutils.h"
     115                 :             : #include "utils/pgstat_internal.h"
     116                 :             : #include "utils/timestamp.h"
     117                 :             : 
     118                 :             : 
     119                 :             : /* ----------
     120                 :             :  * Timer definitions.
     121                 :             :  *
     122                 :             :  * In milliseconds.
     123                 :             :  * ----------
     124                 :             :  */
     125                 :             : 
     126                 :             : /* minimum interval non-forced stats flushes.*/
     127                 :             : #define PGSTAT_MIN_INTERVAL         1000
     128                 :             : /* how long until to block flushing pending stats updates */
     129                 :             : #define PGSTAT_MAX_INTERVAL         60000
     130                 :             : /* when to call pgstat_report_stat() again, even when idle */
     131                 :             : #define PGSTAT_IDLE_INTERVAL        10000
     132                 :             : 
     133                 :             : /* ----------
     134                 :             :  * Initial size hints for the hash tables used in statistics.
     135                 :             :  * ----------
     136                 :             :  */
     137                 :             : 
     138                 :             : #define PGSTAT_SNAPSHOT_HASH_SIZE   512
     139                 :             : 
     140                 :             : /* ---------
     141                 :             :  * Identifiers in stats file.
     142                 :             :  * ---------
     143                 :             :  */
     144                 :             : #define PGSTAT_FILE_ENTRY_END   'E' /* end of file */
     145                 :             : #define PGSTAT_FILE_ENTRY_FIXED 'F' /* fixed-numbered stats entry */
     146                 :             : #define PGSTAT_FILE_ENTRY_NAME  'N' /* stats entry identified by name */
     147                 :             : #define PGSTAT_FILE_ENTRY_HASH  'S' /* stats entry identified by
     148                 :             :                                      * PgStat_HashKey */
     149                 :             : 
     150                 :             : /* hash table for statistics snapshots entry */
     151                 :             : typedef struct PgStat_SnapshotEntry
     152                 :             : {
     153                 :             :     PgStat_HashKey key;
     154                 :             :     char        status;         /* for simplehash use */
     155                 :             :     void       *data;           /* the stats data itself */
     156                 :             : } PgStat_SnapshotEntry;
     157                 :             : 
     158                 :             : 
     159                 :             : /* ----------
     160                 :             :  * Backend-local Hash Table Definitions
     161                 :             :  * ----------
     162                 :             :  */
     163                 :             : 
     164                 :             : /* for stats snapshot entries */
     165                 :             : #define SH_PREFIX pgstat_snapshot
     166                 :             : #define SH_ELEMENT_TYPE PgStat_SnapshotEntry
     167                 :             : #define SH_KEY_TYPE PgStat_HashKey
     168                 :             : #define SH_KEY key
     169                 :             : #define SH_HASH_KEY(tb, key) \
     170                 :             :     pgstat_hash_hash_key(&key, sizeof(PgStat_HashKey), NULL)
     171                 :             : #define SH_EQUAL(tb, a, b) \
     172                 :             :     pgstat_cmp_hash_key(&a, &b, sizeof(PgStat_HashKey), NULL) == 0
     173                 :             : #define SH_SCOPE static inline
     174                 :             : #define SH_DEFINE
     175                 :             : #define SH_DECLARE
     176                 :             : #include "lib/simplehash.h"
     177                 :             : 
     178                 :             : 
     179                 :             : /* ----------
     180                 :             :  * Local function forward declarations
     181                 :             :  * ----------
     182                 :             :  */
     183                 :             : 
     184                 :             : static void pgstat_write_statsfile(void);
     185                 :             : static void pgstat_read_statsfile(void);
     186                 :             : 
     187                 :             : static void pgstat_init_snapshot_fixed(void);
     188                 :             : 
     189                 :             : static void pgstat_reset_after_failure(void);
     190                 :             : 
     191                 :             : static bool pgstat_flush_pending_entries(bool nowait);
     192                 :             : 
     193                 :             : static void pgstat_prep_snapshot(void);
     194                 :             : static void pgstat_build_snapshot(void);
     195                 :             : static void pgstat_build_snapshot_fixed(PgStat_Kind kind);
     196                 :             : 
     197                 :             : static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
     198                 :             : 
     199                 :             : 
     200                 :             : /* ----------
     201                 :             :  * GUC parameters
     202                 :             :  * ----------
     203                 :             :  */
     204                 :             : 
     205                 :             : bool        pgstat_track_counts = false;
     206                 :             : int         pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
     207                 :             : 
     208                 :             : 
     209                 :             : /* ----------
     210                 :             :  * state shared with pgstat_*.c
     211                 :             :  * ----------
     212                 :             :  */
     213                 :             : 
     214                 :             : PgStat_LocalState pgStatLocal;
     215                 :             : 
     216                 :             : /*
     217                 :             :  * Track pending reports for fixed-numbered stats, used by
     218                 :             :  * pgstat_report_stat().
     219                 :             :  */
     220                 :             : bool        pgstat_report_fixed = false;
     221                 :             : 
     222                 :             : /* ----------
     223                 :             :  * Local data
     224                 :             :  *
     225                 :             :  * NB: There should be only variables related to stats infrastructure here,
     226                 :             :  * not for specific kinds of stats.
     227                 :             :  * ----------
     228                 :             :  */
     229                 :             : 
     230                 :             : /*
     231                 :             :  * Memory contexts containing the pgStatEntryRefHash table, the
     232                 :             :  * pgStatSharedRef entries, and pending data respectively. Mostly to make it
     233                 :             :  * easier to track / attribute memory usage.
     234                 :             :  */
     235                 :             : 
     236                 :             : static MemoryContext pgStatPendingContext = NULL;
     237                 :             : 
     238                 :             : /*
     239                 :             :  * Backend local list of PgStat_EntryRef with unflushed pending stats.
     240                 :             :  *
     241                 :             :  * Newly pending entries should only ever be added to the end of the list,
     242                 :             :  * otherwise pgstat_flush_pending_entries() might not see them immediately.
     243                 :             :  */
     244                 :             : static dlist_head pgStatPending = DLIST_STATIC_INIT(pgStatPending);
     245                 :             : 
     246                 :             : 
     247                 :             : /*
     248                 :             :  * Force the next stats flush to happen regardless of
     249                 :             :  * PGSTAT_MIN_INTERVAL. Useful in test scripts.
     250                 :             :  */
     251                 :             : static bool pgStatForceNextFlush = false;
     252                 :             : 
     253                 :             : /*
     254                 :             :  * Force-clear existing snapshot before next use when stats_fetch_consistency
     255                 :             :  * is changed.
     256                 :             :  */
     257                 :             : static bool force_stats_snapshot_clear = false;
     258                 :             : 
     259                 :             : 
     260                 :             : /*
     261                 :             :  * For assertions that check pgstat is not used before initialization / after
     262                 :             :  * shutdown.
     263                 :             :  */
     264                 :             : #ifdef USE_ASSERT_CHECKING
     265                 :             : static bool pgstat_is_initialized = false;
     266                 :             : static bool pgstat_is_shutdown = false;
     267                 :             : #endif
     268                 :             : 
     269                 :             : 
     270                 :             : /*
     271                 :             :  * The different kinds of built-in statistics.
     272                 :             :  *
     273                 :             :  * If reasonably possible, handling specific to one kind of stats should go
     274                 :             :  * through this abstraction, rather than making more of pgstat.c aware.
     275                 :             :  *
     276                 :             :  * See comments for struct PgStat_KindInfo for details about the individual
     277                 :             :  * fields.
     278                 :             :  *
     279                 :             :  * XXX: It'd be nicer to define this outside of this file. But there doesn't
     280                 :             :  * seem to be a great way of doing that, given the split across multiple
     281                 :             :  * files.
     282                 :             :  */
     283                 :             : static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE] = {
     284                 :             : 
     285                 :             :     /* stats kinds for variable-numbered objects */
     286                 :             : 
     287                 :             :     [PGSTAT_KIND_DATABASE] = {
     288                 :             :         .name = "database",
     289                 :             : 
     290                 :             :         .fixed_amount = false,
     291                 :             :         .write_to_file = true,
     292                 :             :         /* so pg_stat_database entries can be seen in all databases */
     293                 :             :         .accessed_across_databases = true,
     294                 :             : 
     295                 :             :         .shared_size = sizeof(PgStatShared_Database),
     296                 :             :         .shared_data_off = offsetof(PgStatShared_Database, stats),
     297                 :             :         .shared_data_len = sizeof(((PgStatShared_Database *) 0)->stats),
     298                 :             :         .pending_size = sizeof(PgStat_StatDBEntry),
     299                 :             : 
     300                 :             :         .flush_pending_cb = pgstat_database_flush_cb,
     301                 :             :         .reset_timestamp_cb = pgstat_database_reset_timestamp_cb,
     302                 :             :     },
     303                 :             : 
     304                 :             :     [PGSTAT_KIND_RELATION] = {
     305                 :             :         .name = "relation",
     306                 :             : 
     307                 :             :         .fixed_amount = false,
     308                 :             :         .write_to_file = true,
     309                 :             : 
     310                 :             :         .shared_size = sizeof(PgStatShared_Relation),
     311                 :             :         .shared_data_off = offsetof(PgStatShared_Relation, stats),
     312                 :             :         .shared_data_len = sizeof(((PgStatShared_Relation *) 0)->stats),
     313                 :             :         .pending_size = sizeof(PgStat_RelationStatus),
     314                 :             : 
     315                 :             :         .flush_pending_cb = pgstat_relation_flush_cb,
     316                 :             :         .delete_pending_cb = pgstat_relation_delete_pending_cb,
     317                 :             :         .reset_timestamp_cb = pgstat_relation_reset_timestamp_cb,
     318                 :             :     },
     319                 :             : 
     320                 :             :     [PGSTAT_KIND_INDEX] = {
     321                 :             :         .name = "index",
     322                 :             : 
     323                 :             :         .fixed_amount = false,
     324                 :             :         .write_to_file = true,
     325                 :             : 
     326                 :             :         .shared_size = sizeof(PgStatShared_Index),
     327                 :             :         .shared_data_off = offsetof(PgStatShared_Index, stats),
     328                 :             :         .shared_data_len = sizeof(((PgStatShared_Index *) 0)->stats),
     329                 :             :         .pending_size = sizeof(PgStat_RelationStatus),
     330                 :             : 
     331                 :             :         .flush_pending_cb = pgstat_index_flush_cb,
     332                 :             :         .delete_pending_cb = pgstat_index_delete_pending_cb,
     333                 :             :         .reset_timestamp_cb = pgstat_index_reset_timestamp_cb,
     334                 :             :     },
     335                 :             : 
     336                 :             :     [PGSTAT_KIND_FUNCTION] = {
     337                 :             :         .name = "function",
     338                 :             : 
     339                 :             :         .fixed_amount = false,
     340                 :             :         .write_to_file = true,
     341                 :             : 
     342                 :             :         .shared_size = sizeof(PgStatShared_Function),
     343                 :             :         .shared_data_off = offsetof(PgStatShared_Function, stats),
     344                 :             :         .shared_data_len = sizeof(((PgStatShared_Function *) 0)->stats),
     345                 :             :         .pending_size = sizeof(PgStat_FunctionCounts),
     346                 :             : 
     347                 :             :         .flush_pending_cb = pgstat_function_flush_cb,
     348                 :             :         .reset_timestamp_cb = pgstat_function_reset_timestamp_cb,
     349                 :             :     },
     350                 :             : 
     351                 :             :     [PGSTAT_KIND_REPLSLOT] = {
     352                 :             :         .name = "replslot",
     353                 :             : 
     354                 :             :         .fixed_amount = false,
     355                 :             :         .write_to_file = true,
     356                 :             : 
     357                 :             :         .accessed_across_databases = true,
     358                 :             : 
     359                 :             :         .shared_size = sizeof(PgStatShared_ReplSlot),
     360                 :             :         .shared_data_off = offsetof(PgStatShared_ReplSlot, stats),
     361                 :             :         .shared_data_len = sizeof(((PgStatShared_ReplSlot *) 0)->stats),
     362                 :             : 
     363                 :             :         .reset_timestamp_cb = pgstat_replslot_reset_timestamp_cb,
     364                 :             :         .to_serialized_name = pgstat_replslot_to_serialized_name_cb,
     365                 :             :         .from_serialized_name = pgstat_replslot_from_serialized_name_cb,
     366                 :             :     },
     367                 :             : 
     368                 :             :     [PGSTAT_KIND_SUBSCRIPTION] = {
     369                 :             :         .name = "subscription",
     370                 :             : 
     371                 :             :         .fixed_amount = false,
     372                 :             :         .write_to_file = true,
     373                 :             :         /* so pg_stat_subscription_stats entries can be seen in all databases */
     374                 :             :         .accessed_across_databases = true,
     375                 :             : 
     376                 :             :         .shared_size = sizeof(PgStatShared_Subscription),
     377                 :             :         .shared_data_off = offsetof(PgStatShared_Subscription, stats),
     378                 :             :         .shared_data_len = sizeof(((PgStatShared_Subscription *) 0)->stats),
     379                 :             :         .pending_size = sizeof(PgStat_BackendSubEntry),
     380                 :             : 
     381                 :             :         .flush_pending_cb = pgstat_subscription_flush_cb,
     382                 :             :         .reset_timestamp_cb = pgstat_subscription_reset_timestamp_cb,
     383                 :             :     },
     384                 :             : 
     385                 :             :     [PGSTAT_KIND_BACKEND] = {
     386                 :             :         .name = "backend",
     387                 :             : 
     388                 :             :         .fixed_amount = false,
     389                 :             :         .write_to_file = false,
     390                 :             : 
     391                 :             :         .accessed_across_databases = true,
     392                 :             : 
     393                 :             :         .shared_size = sizeof(PgStatShared_Backend),
     394                 :             :         .shared_data_off = offsetof(PgStatShared_Backend, stats),
     395                 :             :         .shared_data_len = sizeof(((PgStatShared_Backend *) 0)->stats),
     396                 :             : 
     397                 :             :         .flush_static_cb = pgstat_backend_flush_cb,
     398                 :             :         .reset_timestamp_cb = pgstat_backend_reset_timestamp_cb,
     399                 :             :     },
     400                 :             : 
     401                 :             :     /* stats for fixed-numbered (mostly 1) objects */
     402                 :             : 
     403                 :             :     [PGSTAT_KIND_ARCHIVER] = {
     404                 :             :         .name = "archiver",
     405                 :             : 
     406                 :             :         .fixed_amount = true,
     407                 :             :         .write_to_file = true,
     408                 :             : 
     409                 :             :         .snapshot_ctl_off = offsetof(PgStat_Snapshot, archiver),
     410                 :             :         .shared_ctl_off = offsetof(PgStat_ShmemControl, archiver),
     411                 :             :         .shared_data_off = offsetof(PgStatShared_Archiver, stats),
     412                 :             :         .shared_data_len = sizeof(((PgStatShared_Archiver *) 0)->stats),
     413                 :             : 
     414                 :             :         .init_shmem_cb = pgstat_archiver_init_shmem_cb,
     415                 :             :         .reset_all_cb = pgstat_archiver_reset_all_cb,
     416                 :             :         .snapshot_cb = pgstat_archiver_snapshot_cb,
     417                 :             :     },
     418                 :             : 
     419                 :             :     [PGSTAT_KIND_BGWRITER] = {
     420                 :             :         .name = "bgwriter",
     421                 :             : 
     422                 :             :         .fixed_amount = true,
     423                 :             :         .write_to_file = true,
     424                 :             : 
     425                 :             :         .snapshot_ctl_off = offsetof(PgStat_Snapshot, bgwriter),
     426                 :             :         .shared_ctl_off = offsetof(PgStat_ShmemControl, bgwriter),
     427                 :             :         .shared_data_off = offsetof(PgStatShared_BgWriter, stats),
     428                 :             :         .shared_data_len = sizeof(((PgStatShared_BgWriter *) 0)->stats),
     429                 :             : 
     430                 :             :         .init_shmem_cb = pgstat_bgwriter_init_shmem_cb,
     431                 :             :         .reset_all_cb = pgstat_bgwriter_reset_all_cb,
     432                 :             :         .snapshot_cb = pgstat_bgwriter_snapshot_cb,
     433                 :             :     },
     434                 :             : 
     435                 :             :     [PGSTAT_KIND_CHECKPOINTER] = {
     436                 :             :         .name = "checkpointer",
     437                 :             : 
     438                 :             :         .fixed_amount = true,
     439                 :             :         .write_to_file = true,
     440                 :             : 
     441                 :             :         .snapshot_ctl_off = offsetof(PgStat_Snapshot, checkpointer),
     442                 :             :         .shared_ctl_off = offsetof(PgStat_ShmemControl, checkpointer),
     443                 :             :         .shared_data_off = offsetof(PgStatShared_Checkpointer, stats),
     444                 :             :         .shared_data_len = sizeof(((PgStatShared_Checkpointer *) 0)->stats),
     445                 :             : 
     446                 :             :         .init_shmem_cb = pgstat_checkpointer_init_shmem_cb,
     447                 :             :         .reset_all_cb = pgstat_checkpointer_reset_all_cb,
     448                 :             :         .snapshot_cb = pgstat_checkpointer_snapshot_cb,
     449                 :             :     },
     450                 :             : 
     451                 :             :     [PGSTAT_KIND_IO] = {
     452                 :             :         .name = "io",
     453                 :             : 
     454                 :             :         .fixed_amount = true,
     455                 :             :         .write_to_file = true,
     456                 :             : 
     457                 :             :         .snapshot_ctl_off = offsetof(PgStat_Snapshot, io),
     458                 :             :         .shared_ctl_off = offsetof(PgStat_ShmemControl, io),
     459                 :             :         .shared_data_off = offsetof(PgStatShared_IO, stats),
     460                 :             :         .shared_data_len = sizeof(((PgStatShared_IO *) 0)->stats),
     461                 :             : 
     462                 :             :         .flush_static_cb = pgstat_io_flush_cb,
     463                 :             :         .init_shmem_cb = pgstat_io_init_shmem_cb,
     464                 :             :         .reset_all_cb = pgstat_io_reset_all_cb,
     465                 :             :         .snapshot_cb = pgstat_io_snapshot_cb,
     466                 :             :     },
     467                 :             : 
     468                 :             :     [PGSTAT_KIND_LOCK] = {
     469                 :             :         .name = "lock",
     470                 :             : 
     471                 :             :         .fixed_amount = true,
     472                 :             :         .write_to_file = true,
     473                 :             : 
     474                 :             :         .snapshot_ctl_off = offsetof(PgStat_Snapshot, lock),
     475                 :             :         .shared_ctl_off = offsetof(PgStat_ShmemControl, lock),
     476                 :             :         .shared_data_off = offsetof(PgStatShared_Lock, stats),
     477                 :             :         .shared_data_len = sizeof(((PgStatShared_Lock *) 0)->stats),
     478                 :             : 
     479                 :             :         .flush_static_cb = pgstat_lock_flush_cb,
     480                 :             :         .init_shmem_cb = pgstat_lock_init_shmem_cb,
     481                 :             :         .reset_all_cb = pgstat_lock_reset_all_cb,
     482                 :             :         .snapshot_cb = pgstat_lock_snapshot_cb,
     483                 :             :     },
     484                 :             : 
     485                 :             :     [PGSTAT_KIND_SLRU] = {
     486                 :             :         .name = "slru",
     487                 :             : 
     488                 :             :         .fixed_amount = true,
     489                 :             :         .write_to_file = true,
     490                 :             : 
     491                 :             :         .snapshot_ctl_off = offsetof(PgStat_Snapshot, slru),
     492                 :             :         .shared_ctl_off = offsetof(PgStat_ShmemControl, slru),
     493                 :             :         .shared_data_off = offsetof(PgStatShared_SLRU, stats),
     494                 :             :         .shared_data_len = sizeof(((PgStatShared_SLRU *) 0)->stats),
     495                 :             : 
     496                 :             :         .flush_static_cb = pgstat_slru_flush_cb,
     497                 :             :         .init_shmem_cb = pgstat_slru_init_shmem_cb,
     498                 :             :         .reset_all_cb = pgstat_slru_reset_all_cb,
     499                 :             :         .snapshot_cb = pgstat_slru_snapshot_cb,
     500                 :             :     },
     501                 :             : 
     502                 :             :     [PGSTAT_KIND_WAL] = {
     503                 :             :         .name = "wal",
     504                 :             : 
     505                 :             :         .fixed_amount = true,
     506                 :             :         .write_to_file = true,
     507                 :             : 
     508                 :             :         .snapshot_ctl_off = offsetof(PgStat_Snapshot, wal),
     509                 :             :         .shared_ctl_off = offsetof(PgStat_ShmemControl, wal),
     510                 :             :         .shared_data_off = offsetof(PgStatShared_Wal, stats),
     511                 :             :         .shared_data_len = sizeof(((PgStatShared_Wal *) 0)->stats),
     512                 :             : 
     513                 :             :         .init_backend_cb = pgstat_wal_init_backend_cb,
     514                 :             :         .flush_static_cb = pgstat_wal_flush_cb,
     515                 :             :         .init_shmem_cb = pgstat_wal_init_shmem_cb,
     516                 :             :         .reset_all_cb = pgstat_wal_reset_all_cb,
     517                 :             :         .snapshot_cb = pgstat_wal_snapshot_cb,
     518                 :             :     },
     519                 :             : };
     520                 :             : 
     521                 :             : /*
     522                 :             :  * Information about custom statistics kinds.
     523                 :             :  *
     524                 :             :  * These are saved in a different array than the built-in kinds to save
     525                 :             :  * in clarity with the initializations.
     526                 :             :  *
     527                 :             :  * Indexed by PGSTAT_KIND_CUSTOM_MIN, of size PGSTAT_KIND_CUSTOM_SIZE.
     528                 :             :  */
     529                 :             : static const PgStat_KindInfo **pgstat_kind_custom_infos = NULL;
     530                 :             : 
     531                 :             : /* ------------------------------------------------------------
     532                 :             :  * Functions managing the state of the stats system for all backends.
     533                 :             :  * ------------------------------------------------------------
     534                 :             :  */
     535                 :             : 
     536                 :             : /*
     537                 :             :  * Read on-disk stats into memory at server start.
     538                 :             :  *
     539                 :             :  * Should only be called by the startup process or in single user mode.
     540                 :             :  */
     541                 :             : void
     542                 :         899 : pgstat_restore_stats(void)
     543                 :             : {
     544                 :         899 :     pgstat_read_statsfile();
     545                 :         899 : }
     546                 :             : 
     547                 :             : /*
     548                 :             :  * Remove the stats file.  This is currently used only if WAL recovery is
     549                 :             :  * needed after a crash.
     550                 :             :  *
     551                 :             :  * Should only be called by the startup process or in single user mode.
     552                 :             :  */
     553                 :             : void
     554                 :         198 : pgstat_discard_stats(void)
     555                 :             : {
     556                 :             :     int         ret;
     557                 :             : 
     558                 :             :     /* NB: this needs to be done even in single user mode */
     559                 :             : 
     560                 :             :     /* First, cleanup the main pgstats file */
     561                 :         198 :     ret = unlink(PGSTAT_STAT_PERMANENT_FILENAME);
     562         [ +  + ]:         198 :     if (ret != 0)
     563                 :             :     {
     564         [ +  - ]:         197 :         if (errno == ENOENT)
     565         [ +  + ]:         197 :             elog(DEBUG2,
     566                 :             :                  "didn't need to unlink permanent stats file \"%s\" - didn't exist",
     567                 :             :                  PGSTAT_STAT_PERMANENT_FILENAME);
     568                 :             :         else
     569         [ #  # ]:           0 :             ereport(LOG,
     570                 :             :                     (errcode_for_file_access(),
     571                 :             :                      errmsg("could not unlink permanent statistics file \"%s\": %m",
     572                 :             :                             PGSTAT_STAT_PERMANENT_FILENAME)));
     573                 :             :     }
     574                 :             :     else
     575                 :             :     {
     576         [ -  + ]:           1 :         ereport(DEBUG2,
     577                 :             :                 (errcode_for_file_access(),
     578                 :             :                  errmsg_internal("unlinked permanent statistics file \"%s\"",
     579                 :             :                                  PGSTAT_STAT_PERMANENT_FILENAME)));
     580                 :             :     }
     581                 :             : 
     582                 :             :     /* Finish callbacks, if required */
     583         [ +  + ]:        6534 :     for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
     584                 :             :     {
     585                 :        6336 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
     586                 :             : 
     587   [ +  +  +  + ]:        6336 :         if (kind_info && kind_info->finish)
     588                 :           1 :             kind_info->finish(STATS_DISCARD);
     589                 :             :     }
     590                 :             : 
     591                 :             :     /*
     592                 :             :      * Reset stats contents. This will set reset timestamps of fixed-numbered
     593                 :             :      * stats to the current time (no variable stats exist).
     594                 :             :      */
     595                 :         198 :     pgstat_reset_after_failure();
     596                 :         198 : }
     597                 :             : 
     598                 :             : /*
     599                 :             :  * pgstat_before_server_shutdown() needs to be called by exactly one process
     600                 :             :  * during regular server shutdowns. Otherwise all stats will be lost.
     601                 :             :  *
     602                 :             :  * We currently only write out stats for proc_exit(0). We might want to change
     603                 :             :  * that at some point... But right now pgstat_discard_stats() would be called
     604                 :             :  * during the start after a disorderly shutdown, anyway.
     605                 :             :  */
     606                 :             : void
     607                 :         779 : pgstat_before_server_shutdown(int code, Datum arg)
     608                 :             : {
     609                 :             :     Assert(pgStatLocal.shmem != NULL);
     610                 :             :     Assert(!pgStatLocal.shmem->is_shutdown);
     611                 :             : 
     612                 :             :     /*
     613                 :             :      * Stats should only be reported after pgstat_initialize() and before
     614                 :             :      * pgstat_shutdown(). This is a convenient point to catch most violations
     615                 :             :      * of this rule.
     616                 :             :      */
     617                 :             :     Assert(pgstat_is_initialized && !pgstat_is_shutdown);
     618                 :             : 
     619                 :             :     /* flush out our own pending changes before writing out */
     620                 :         779 :     pgstat_report_stat(true);
     621                 :             : 
     622                 :             :     /*
     623                 :             :      * Only write out file during normal shutdown. Don't even signal that
     624                 :             :      * we've shutdown during irregular shutdowns, because the shutdown
     625                 :             :      * sequence isn't coordinated to ensure this backend shuts down last.
     626                 :             :      */
     627         [ +  + ]:         779 :     if (code == 0)
     628                 :             :     {
     629                 :         774 :         pgStatLocal.shmem->is_shutdown = true;
     630                 :         774 :         pgstat_write_statsfile();
     631                 :             :     }
     632                 :         779 : }
     633                 :             : 
     634                 :             : 
     635                 :             : /* ------------------------------------------------------------
     636                 :             :  * Backend initialization / shutdown functions
     637                 :             :  * ------------------------------------------------------------
     638                 :             :  */
     639                 :             : 
     640                 :             : /*
     641                 :             :  * Shut down a single backend's statistics reporting at process exit.
     642                 :             :  *
     643                 :             :  * Flush out any remaining statistics counts.  Without this, operations
     644                 :             :  * triggered during backend exit (such as temp table deletions) won't be
     645                 :             :  * counted.
     646                 :             :  */
     647                 :             : static void
     648                 :       25124 : pgstat_shutdown_hook(int code, Datum arg)
     649                 :             : {
     650                 :             :     Assert(!pgstat_is_shutdown);
     651                 :             :     Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
     652                 :             : 
     653                 :             :     /*
     654                 :             :      * If we got as far as discovering our own database ID, we can flush out
     655                 :             :      * what we did so far.  Otherwise, we'd be reporting an invalid database
     656                 :             :      * ID, so forget it.  (This means that accesses to pg_database during
     657                 :             :      * failed backend starts might never get counted.)
     658                 :             :      */
     659         [ +  + ]:       25124 :     if (OidIsValid(MyDatabaseId))
     660                 :       18991 :         pgstat_report_disconnect(MyDatabaseId);
     661                 :             : 
     662                 :       25124 :     pgstat_report_stat(true);
     663                 :             : 
     664                 :             :     /* there shouldn't be any pending changes left */
     665                 :             :     Assert(dlist_is_empty(&pgStatPending));
     666                 :       25124 :     dlist_init(&pgStatPending);
     667                 :             : 
     668                 :             :     /* drop the backend stats entry */
     669         [ -  + ]:       25124 :     if (!pgstat_drop_entry(PGSTAT_KIND_BACKEND, InvalidOid, MyProcNumber, false))
     670                 :           0 :         pgstat_request_entry_refs_gc();
     671                 :             : 
     672                 :       25124 :     pgstat_detach_shmem();
     673                 :             : 
     674                 :             : #ifdef USE_ASSERT_CHECKING
     675                 :             :     pgstat_is_shutdown = true;
     676                 :             : #endif
     677                 :       25124 : }
     678                 :             : 
     679                 :             : /*
     680                 :             :  * Initialize pgstats state, and set up our on-proc-exit hook. Called from
     681                 :             :  * BaseInit().
     682                 :             :  *
     683                 :             :  * NOTE: MyDatabaseId isn't set yet; so the shutdown hook has to be careful.
     684                 :             :  */
     685                 :             : void
     686                 :       25124 : pgstat_initialize(void)
     687                 :             : {
     688                 :             :     Assert(!pgstat_is_initialized);
     689                 :             : 
     690                 :       25124 :     pgstat_attach_shmem();
     691                 :             : 
     692                 :       25124 :     pgstat_init_snapshot_fixed();
     693                 :             : 
     694                 :             :     /* Backend initialization callbacks */
     695         [ +  + ]:      829092 :     for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
     696                 :             :     {
     697                 :      803968 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
     698                 :             : 
     699   [ +  +  +  + ]:      803968 :         if (kind_info == NULL || kind_info->init_backend_cb == NULL)
     700                 :      778844 :             continue;
     701                 :             : 
     702                 :       25124 :         kind_info->init_backend_cb();
     703                 :             :     }
     704                 :             : 
     705                 :             :     /* Set up a process-exit hook to clean up */
     706                 :       25124 :     before_shmem_exit(pgstat_shutdown_hook, 0);
     707                 :             : 
     708                 :             : #ifdef USE_ASSERT_CHECKING
     709                 :             :     pgstat_is_initialized = true;
     710                 :             : #endif
     711                 :       25124 : }
     712                 :             : 
     713                 :             : 
     714                 :             : /* ------------------------------------------------------------
     715                 :             :  * Public functions used by backends follow
     716                 :             :  * ------------------------------------------------------------
     717                 :             :  */
     718                 :             : 
     719                 :             : /*
     720                 :             :  * Must be called by processes that performs DML: tcop/postgres.c, logical
     721                 :             :  * receiver processes, SPI worker, etc. to flush pending statistics updates to
     722                 :             :  * shared memory.
     723                 :             :  *
     724                 :             :  * Unless called with 'force', pending stats updates are flushed happen once
     725                 :             :  * per PGSTAT_MIN_INTERVAL (1000ms). When not forced, stats flushes do not
     726                 :             :  * block on lock acquisition, except if stats updates have been pending for
     727                 :             :  * longer than PGSTAT_MAX_INTERVAL (60000ms).
     728                 :             :  *
     729                 :             :  * Whenever pending stats updates remain at the end of pgstat_report_stat() a
     730                 :             :  * suggested idle timeout is returned. Currently this is always
     731                 :             :  * PGSTAT_IDLE_INTERVAL (10000ms). Callers can use the returned time to set up
     732                 :             :  * a timeout after which to call pgstat_report_stat(true), but are not
     733                 :             :  * required to do so.
     734                 :             :  *
     735                 :             :  * Note that this is called only when not within a transaction, so it is fair
     736                 :             :  * to use transaction stop time as an approximation of current time.
     737                 :             :  */
     738                 :             : long
     739                 :      400318 : pgstat_report_stat(bool force)
     740                 :             : {
     741                 :             :     static TimestampTz pending_since = 0;
     742                 :             :     static TimestampTz last_flush = 0;
     743                 :             :     bool        partial_flush;
     744                 :             :     TimestampTz now;
     745                 :             :     bool        nowait;
     746                 :             : 
     747                 :             :     pgstat_assert_is_up();
     748                 :             :     Assert(!IsTransactionOrTransactionBlock());
     749                 :             : 
     750                 :             :     /* "absorb" the forced flush even if there's nothing to flush */
     751         [ +  + ]:      400318 :     if (pgStatForceNextFlush)
     752                 :             :     {
     753                 :         341 :         force = true;
     754                 :         341 :         pgStatForceNextFlush = false;
     755                 :             :     }
     756                 :             : 
     757                 :             :     /* Don't expend a clock check if nothing to do */
     758         [ +  + ]:      400318 :     if (dlist_is_empty(&pgStatPending) &&
     759         [ +  + ]:       10862 :         !pgstat_report_fixed)
     760                 :             :     {
     761                 :        7757 :         return 0;
     762                 :             :     }
     763                 :             : 
     764                 :             :     /*
     765                 :             :      * There should never be stats to report once stats are shut down. Can't
     766                 :             :      * assert that before the checks above, as there is an unconditional
     767                 :             :      * pgstat_report_stat() call in pgstat_shutdown_hook() - which at least
     768                 :             :      * the process that ran pgstat_before_server_shutdown() will still call.
     769                 :             :      */
     770                 :             :     Assert(!pgStatLocal.shmem->is_shutdown);
     771                 :             : 
     772         [ +  + ]:      392561 :     if (force)
     773                 :             :     {
     774                 :             :         /*
     775                 :             :          * Stats reports are forced either when it's been too long since stats
     776                 :             :          * have been reported or in processes that force stats reporting to
     777                 :             :          * happen at specific points (including shutdown). In the former case
     778                 :             :          * the transaction stop time might be quite old, in the latter it
     779                 :             :          * would never get cleared.
     780                 :             :          */
     781                 :       24951 :         now = GetCurrentTimestamp();
     782                 :             :     }
     783                 :             :     else
     784                 :             :     {
     785                 :      367610 :         now = GetCurrentTransactionStopTimestamp();
     786                 :             : 
     787   [ +  +  -  + ]:      702940 :         if (pending_since > 0 &&
     788                 :      335330 :             TimestampDifferenceExceeds(pending_since, now, PGSTAT_MAX_INTERVAL))
     789                 :             :         {
     790                 :             :             /* don't keep pending updates longer than PGSTAT_MAX_INTERVAL */
     791                 :           0 :             force = true;
     792                 :             :         }
     793         [ +  + ]:      367610 :         else if (last_flush > 0 &&
     794         [ +  + ]:      352384 :                  !TimestampDifferenceExceeds(last_flush, now, PGSTAT_MIN_INTERVAL))
     795                 :             :         {
     796                 :             :             /* don't flush too frequently */
     797         [ +  + ]:      350508 :             if (pending_since == 0)
     798                 :       16840 :                 pending_since = now;
     799                 :             : 
     800                 :      350508 :             return PGSTAT_IDLE_INTERVAL;
     801                 :             :         }
     802                 :             :     }
     803                 :             : 
     804                 :       42053 :     pgstat_update_dbstats(now);
     805                 :             : 
     806                 :             :     /* don't wait for lock acquisition when !force */
     807                 :       42053 :     nowait = !force;
     808                 :             : 
     809                 :       42053 :     partial_flush = false;
     810                 :             : 
     811                 :             :     /* flush of variable-numbered stats tracked in pending entries list */
     812                 :       42053 :     partial_flush |= pgstat_flush_pending_entries(nowait);
     813                 :             : 
     814                 :             :     /* flush of other stats kinds */
     815         [ +  + ]:       42053 :     if (pgstat_report_fixed)
     816                 :             :     {
     817         [ +  + ]:     1347060 :         for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
     818                 :             :         {
     819                 :     1306240 :             const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
     820                 :             : 
     821         [ +  + ]:     1306240 :             if (!kind_info)
     822                 :      734586 :                 continue;
     823         [ +  + ]:      571654 :             if (!kind_info->flush_static_cb)
     824                 :      367554 :                 continue;
     825                 :             : 
     826                 :      204100 :             partial_flush |= kind_info->flush_static_cb(nowait);
     827                 :             :         }
     828                 :             :     }
     829                 :             : 
     830                 :       42053 :     last_flush = now;
     831                 :             : 
     832                 :             :     /*
     833                 :             :      * If some of the pending stats could not be flushed due to lock
     834                 :             :      * contention, let the caller know when to retry.
     835                 :             :      */
     836         [ +  + ]:       42053 :     if (partial_flush)
     837                 :             :     {
     838                 :             :         /* force should have prevented us from getting here */
     839                 :             :         Assert(!force);
     840                 :             : 
     841                 :             :         /* remember since when stats have been pending */
     842         [ +  + ]:          10 :         if (pending_since == 0)
     843                 :           8 :             pending_since = now;
     844                 :             : 
     845                 :          10 :         return PGSTAT_IDLE_INTERVAL;
     846                 :             :     }
     847                 :             : 
     848                 :       42043 :     pending_since = 0;
     849                 :       42043 :     pgstat_report_fixed = false;
     850                 :             : 
     851                 :       42043 :     return 0;
     852                 :             : }
     853                 :             : 
     854                 :             : /*
     855                 :             :  * Force locally pending stats to be flushed during the next
     856                 :             :  * pgstat_report_stat() call. This is useful for writing tests.
     857                 :             :  */
     858                 :             : void
     859                 :         341 : pgstat_force_next_flush(void)
     860                 :             : {
     861                 :         341 :     pgStatForceNextFlush = true;
     862                 :         341 : }
     863                 :             : 
     864                 :             : /*
     865                 :             :  * Only for use by pgstat_reset_counters()
     866                 :             :  */
     867                 :             : static bool
     868                 :       16215 : match_db_entries(PgStatShared_HashEntry *entry, Datum match_data)
     869                 :             : {
     870                 :       16215 :     return entry->key.dboid == MyDatabaseId;
     871                 :             : }
     872                 :             : 
     873                 :             : /*
     874                 :             :  * Reset counters for our database.
     875                 :             :  *
     876                 :             :  * Permission checking for this function is managed through the normal
     877                 :             :  * GRANT system.
     878                 :             :  */
     879                 :             : void
     880                 :          15 : pgstat_reset_counters(void)
     881                 :             : {
     882                 :          15 :     TimestampTz ts = GetCurrentTimestamp();
     883                 :             : 
     884                 :          15 :     pgstat_reset_matching_entries(match_db_entries,
     885                 :             :                                   ObjectIdGetDatum(MyDatabaseId),
     886                 :             :                                   ts);
     887                 :          15 : }
     888                 :             : 
     889                 :             : /*
     890                 :             :  * Reset a single variable-numbered entry.
     891                 :             :  *
     892                 :             :  * If the stats kind is within a database, also reset the database's
     893                 :             :  * stat_reset_timestamp.
     894                 :             :  *
     895                 :             :  * Permission checking for this function is managed through the normal
     896                 :             :  * GRANT system.
     897                 :             :  */
     898                 :             : void
     899                 :          45 : pgstat_reset(PgStat_Kind kind, Oid dboid, uint64 objid)
     900                 :             : {
     901                 :          45 :     const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
     902                 :          45 :     TimestampTz ts = GetCurrentTimestamp();
     903                 :             : 
     904                 :             :     /* not needed atm, and doesn't make sense with the current signature */
     905                 :             :     Assert(!pgstat_get_kind_info(kind)->fixed_amount);
     906                 :             : 
     907                 :             :     /* reset the "single counter" */
     908                 :          45 :     pgstat_reset_entry(kind, dboid, objid, ts);
     909                 :             : 
     910         [ +  + ]:          45 :     if (!kind_info->accessed_across_databases)
     911                 :          28 :         pgstat_reset_database_timestamp(dboid, ts);
     912                 :          45 : }
     913                 :             : 
     914                 :             : /*
     915                 :             :  * Reset stats for all entries of a kind.
     916                 :             :  *
     917                 :             :  * Permission checking for this function is managed through the normal
     918                 :             :  * GRANT system.
     919                 :             :  */
     920                 :             : void
     921                 :          46 : pgstat_reset_of_kind(PgStat_Kind kind)
     922                 :             : {
     923                 :          46 :     const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
     924                 :          46 :     TimestampTz ts = GetCurrentTimestamp();
     925                 :             : 
     926         [ +  + ]:          46 :     if (kind_info->fixed_amount)
     927                 :          42 :         kind_info->reset_all_cb(ts);
     928                 :             :     else
     929                 :           4 :         pgstat_reset_entries_of_kind(kind, ts);
     930                 :          46 : }
     931                 :             : 
     932                 :             : 
     933                 :             : /* ------------------------------------------------------------
     934                 :             :  * Fetching of stats
     935                 :             :  * ------------------------------------------------------------
     936                 :             :  */
     937                 :             : 
     938                 :             : /*
     939                 :             :  * Discard any data collected in the current transaction.  Any subsequent
     940                 :             :  * request will cause new snapshots to be read.
     941                 :             :  *
     942                 :             :  * This is also invoked during transaction commit or abort to discard
     943                 :             :  * the no-longer-wanted snapshot.  Updates of stats_fetch_consistency can
     944                 :             :  * cause this routine to be called.
     945                 :             :  */
     946                 :             : void
     947                 :      661975 : pgstat_clear_snapshot(void)
     948                 :             : {
     949                 :             :     pgstat_assert_is_up();
     950                 :             : 
     951                 :      661975 :     memset(&pgStatLocal.snapshot.fixed_valid, 0,
     952                 :             :            sizeof(pgStatLocal.snapshot.fixed_valid));
     953                 :      661975 :     memset(&pgStatLocal.snapshot.custom_valid, 0,
     954                 :             :            sizeof(pgStatLocal.snapshot.custom_valid));
     955                 :      661975 :     pgStatLocal.snapshot.stats = NULL;
     956                 :      661975 :     pgStatLocal.snapshot.mode = PGSTAT_FETCH_CONSISTENCY_NONE;
     957                 :             : 
     958                 :             :     /* Release memory, if any was allocated */
     959         [ +  + ]:      661975 :     if (pgStatLocal.snapshot.context)
     960                 :             :     {
     961                 :         818 :         MemoryContextDelete(pgStatLocal.snapshot.context);
     962                 :             : 
     963                 :             :         /* Reset variables */
     964                 :         818 :         pgStatLocal.snapshot.context = NULL;
     965                 :             :     }
     966                 :             : 
     967                 :             :     /*
     968                 :             :      * Historically the backend_status.c facilities lived in this file, and
     969                 :             :      * were reset with the same function. For now keep it that way, and
     970                 :             :      * forward the reset request.
     971                 :             :      */
     972                 :      661975 :     pgstat_clear_backend_activity_snapshot();
     973                 :             : 
     974                 :             :     /* Reset this flag, as it may be possible that a cleanup was forced. */
     975                 :      661975 :     force_stats_snapshot_clear = false;
     976                 :      661975 : }
     977                 :             : 
     978                 :             : void *
     979                 :      304650 : pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid, bool *may_free)
     980                 :             : {
     981                 :      304650 :     PgStat_HashKey key = {0};
     982                 :             :     PgStat_EntryRef *entry_ref;
     983                 :             :     void       *stats_data;
     984                 :      304650 :     const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
     985                 :             : 
     986                 :             :     /* should be called from backends */
     987                 :             :     Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
     988                 :             :     Assert(!kind_info->fixed_amount);
     989                 :             : 
     990                 :             :     /*
     991                 :             :      * Initialize *may_free to false.  We'll change it to true later if we end
     992                 :             :      * up allocating the result in the caller's context and not caching it.
     993                 :             :      */
     994         [ +  + ]:      304650 :     if (may_free)
     995                 :      291879 :         *may_free = false;
     996                 :             : 
     997                 :      304650 :     pgstat_prep_snapshot();
     998                 :             : 
     999                 :      304650 :     key.kind = kind;
    1000                 :      304650 :     key.dboid = dboid;
    1001                 :      304650 :     key.objid = objid;
    1002                 :             : 
    1003                 :             :     /* if we need to build a full snapshot, do so */
    1004         [ +  + ]:      304650 :     if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
    1005                 :         299 :         pgstat_build_snapshot();
    1006                 :             : 
    1007                 :             :     /* if caching is desired, look up in cache */
    1008         [ +  + ]:      304650 :     if (pgstat_fetch_consistency > PGSTAT_FETCH_CONSISTENCY_NONE)
    1009                 :             :     {
    1010                 :        7293 :         PgStat_SnapshotEntry *entry = NULL;
    1011                 :             : 
    1012                 :        7293 :         entry = pgstat_snapshot_lookup(pgStatLocal.snapshot.stats, key);
    1013                 :             : 
    1014         [ +  + ]:        7293 :         if (entry)
    1015                 :         616 :             return entry->data;
    1016                 :             : 
    1017                 :             :         /*
    1018                 :             :          * If we built a full snapshot and the key is not in
    1019                 :             :          * pgStatLocal.snapshot.stats, there are no matching stats.
    1020                 :             :          */
    1021         [ +  + ]:        6677 :         if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
    1022                 :          16 :             return NULL;
    1023                 :             :     }
    1024                 :             : 
    1025                 :      304018 :     pgStatLocal.snapshot.mode = pgstat_fetch_consistency;
    1026                 :             : 
    1027                 :      304018 :     entry_ref = pgstat_get_entry_ref(kind, dboid, objid, false, NULL);
    1028                 :             : 
    1029   [ +  +  +  + ]:      304018 :     if (entry_ref == NULL || entry_ref->shared_entry->dropped)
    1030                 :             :     {
    1031                 :             :         /* create empty entry when using PGSTAT_FETCH_CONSISTENCY_CACHE */
    1032         [ +  + ]:        7486 :         if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_CACHE)
    1033                 :             :         {
    1034                 :        1358 :             PgStat_SnapshotEntry *entry = NULL;
    1035                 :             :             bool        found;
    1036                 :             : 
    1037                 :        1358 :             entry = pgstat_snapshot_insert(pgStatLocal.snapshot.stats, key, &found);
    1038                 :             :             Assert(!found);
    1039                 :        1358 :             entry->data = NULL;
    1040                 :             :         }
    1041                 :        7486 :         return NULL;
    1042                 :             :     }
    1043                 :             : 
    1044                 :             :     /*
    1045                 :             :      * Allocate in caller's context for PGSTAT_FETCH_CONSISTENCY_NONE,
    1046                 :             :      * otherwise we could quickly end up with a fair bit of memory used due to
    1047                 :             :      * repeated accesses.
    1048                 :             :      */
    1049         [ +  + ]:      296532 :     if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE)
    1050                 :             :     {
    1051                 :      291229 :         stats_data = palloc(kind_info->shared_data_len);
    1052                 :             : 
    1053                 :             :         /*
    1054                 :             :          * Since we allocated the result in the caller's context and aren't
    1055                 :             :          * caching it, the caller can safely pfree() it.
    1056                 :             :          */
    1057         [ +  + ]:      291229 :         if (may_free)
    1058                 :      287059 :             *may_free = true;
    1059                 :             :     }
    1060                 :             :     else
    1061                 :        5303 :         stats_data = MemoryContextAlloc(pgStatLocal.snapshot.context,
    1062                 :        5303 :                                         kind_info->shared_data_len);
    1063                 :             : 
    1064                 :      296532 :     (void) pgstat_lock_entry_shared(entry_ref, false);
    1065                 :      593064 :     memcpy(stats_data,
    1066                 :      296532 :            pgstat_get_entry_data(kind, entry_ref->shared_stats),
    1067                 :      296532 :            kind_info->shared_data_len);
    1068                 :      296532 :     pgstat_unlock_entry(entry_ref);
    1069                 :             : 
    1070         [ +  + ]:      296532 :     if (pgstat_fetch_consistency > PGSTAT_FETCH_CONSISTENCY_NONE)
    1071                 :             :     {
    1072                 :        5303 :         PgStat_SnapshotEntry *entry = NULL;
    1073                 :             :         bool        found;
    1074                 :             : 
    1075                 :        5303 :         entry = pgstat_snapshot_insert(pgStatLocal.snapshot.stats, key, &found);
    1076                 :        5303 :         entry->data = stats_data;
    1077                 :             :     }
    1078                 :             : 
    1079                 :      296532 :     return stats_data;
    1080                 :             : }
    1081                 :             : 
    1082                 :             : /*
    1083                 :             :  * If a stats snapshot has been taken, return the timestamp at which that was
    1084                 :             :  * done, and set *have_snapshot to true. Otherwise *have_snapshot is set to
    1085                 :             :  * false.
    1086                 :             :  */
    1087                 :             : TimestampTz
    1088                 :          40 : pgstat_get_stat_snapshot_timestamp(bool *have_snapshot)
    1089                 :             : {
    1090         [ +  + ]:          40 :     if (force_stats_snapshot_clear)
    1091                 :          12 :         pgstat_clear_snapshot();
    1092                 :             : 
    1093         [ +  + ]:          40 :     if (pgStatLocal.snapshot.mode == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
    1094                 :             :     {
    1095                 :          16 :         *have_snapshot = true;
    1096                 :          16 :         return pgStatLocal.snapshot.snapshot_timestamp;
    1097                 :             :     }
    1098                 :             : 
    1099                 :          24 :     *have_snapshot = false;
    1100                 :             : 
    1101                 :          24 :     return 0;
    1102                 :             : }
    1103                 :             : 
    1104                 :             : bool
    1105                 :          96 : pgstat_have_entry(PgStat_Kind kind, Oid dboid, uint64 objid)
    1106                 :             : {
    1107                 :             :     /* fixed-numbered stats always exist */
    1108         [ +  + ]:          96 :     if (pgstat_get_kind_info(kind)->fixed_amount)
    1109                 :           8 :         return true;
    1110                 :             : 
    1111                 :          88 :     return pgstat_get_entry_ref(kind, dboid, objid, false, NULL) != NULL;
    1112                 :             : }
    1113                 :             : 
    1114                 :             : /*
    1115                 :             :  * Ensure snapshot for fixed-numbered 'kind' exists.
    1116                 :             :  *
    1117                 :             :  * Typically used by the pgstat_fetch_* functions for a kind of stats, before
    1118                 :             :  * massaging the data into the desired format.
    1119                 :             :  */
    1120                 :             : void
    1121                 :         284 : pgstat_snapshot_fixed(PgStat_Kind kind)
    1122                 :             : {
    1123                 :             :     Assert(pgstat_is_kind_valid(kind));
    1124                 :             :     Assert(pgstat_get_kind_info(kind)->fixed_amount);
    1125                 :             : 
    1126         [ -  + ]:         284 :     if (force_stats_snapshot_clear)
    1127                 :           0 :         pgstat_clear_snapshot();
    1128                 :             : 
    1129         [ +  + ]:         284 :     if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
    1130                 :          12 :         pgstat_build_snapshot();
    1131                 :             :     else
    1132                 :         272 :         pgstat_build_snapshot_fixed(kind);
    1133                 :             : 
    1134         [ +  + ]:         284 :     if (pgstat_is_kind_builtin(kind))
    1135                 :             :         Assert(pgStatLocal.snapshot.fixed_valid[kind]);
    1136                 :           5 :     else if (pgstat_is_kind_custom(kind))
    1137                 :             :         Assert(pgStatLocal.snapshot.custom_valid[kind - PGSTAT_KIND_CUSTOM_MIN]);
    1138                 :         284 : }
    1139                 :             : 
    1140                 :             : static void
    1141                 :       25124 : pgstat_init_snapshot_fixed(void)
    1142                 :             : {
    1143                 :             :     /*
    1144                 :             :      * Initialize fixed-numbered statistics data in snapshots, only for custom
    1145                 :             :      * stats kinds.
    1146                 :             :      */
    1147         [ +  + ]:      251240 :     for (PgStat_Kind kind = PGSTAT_KIND_CUSTOM_MIN; kind <= PGSTAT_KIND_CUSTOM_MAX; kind++)
    1148                 :             :     {
    1149                 :      226116 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    1150                 :             : 
    1151   [ +  +  +  + ]:      226116 :         if (!kind_info || !kind_info->fixed_amount)
    1152                 :      226065 :             continue;
    1153                 :             : 
    1154                 :          51 :         pgStatLocal.snapshot.custom_data[kind - PGSTAT_KIND_CUSTOM_MIN] =
    1155                 :          51 :             MemoryContextAlloc(TopMemoryContext, kind_info->shared_data_len);
    1156                 :             :     }
    1157                 :       25124 : }
    1158                 :             : 
    1159                 :             : static void
    1160                 :      304680 : pgstat_prep_snapshot(void)
    1161                 :             : {
    1162         [ +  + ]:      304680 :     if (force_stats_snapshot_clear)
    1163                 :          12 :         pgstat_clear_snapshot();
    1164                 :             : 
    1165         [ +  + ]:      304680 :     if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE ||
    1166         [ +  + ]:        7323 :         pgStatLocal.snapshot.stats != NULL)
    1167                 :      303862 :         return;
    1168                 :             : 
    1169         [ +  - ]:         818 :     if (!pgStatLocal.snapshot.context)
    1170                 :         818 :         pgStatLocal.snapshot.context = AllocSetContextCreate(TopMemoryContext,
    1171                 :             :                                                              "PgStat Snapshot",
    1172                 :             :                                                              ALLOCSET_SMALL_SIZES);
    1173                 :             : 
    1174                 :         818 :     pgStatLocal.snapshot.stats =
    1175                 :         818 :         pgstat_snapshot_create(pgStatLocal.snapshot.context,
    1176                 :             :                                PGSTAT_SNAPSHOT_HASH_SIZE,
    1177                 :             :                                NULL);
    1178                 :             : }
    1179                 :             : 
    1180                 :             : static void
    1181                 :         311 : pgstat_build_snapshot(void)
    1182                 :             : {
    1183                 :             :     dshash_seq_status hstat;
    1184                 :             :     PgStatShared_HashEntry *p;
    1185                 :             : 
    1186                 :             :     /* should only be called when we need a snapshot */
    1187                 :             :     Assert(pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT);
    1188                 :             : 
    1189                 :             :     /* snapshot already built */
    1190         [ +  + ]:         311 :     if (pgStatLocal.snapshot.mode == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
    1191                 :         281 :         return;
    1192                 :             : 
    1193                 :          30 :     pgstat_prep_snapshot();
    1194                 :             : 
    1195                 :             :     Assert(pgStatLocal.snapshot.stats->members == 0);
    1196                 :             : 
    1197                 :          30 :     pgStatLocal.snapshot.snapshot_timestamp = GetCurrentTimestamp();
    1198                 :             : 
    1199                 :             :     /*
    1200                 :             :      * Snapshot all variable stats.
    1201                 :             :      */
    1202                 :          30 :     dshash_seq_init(&hstat, pgStatLocal.shared_hash, false);
    1203         [ +  + ]:       36627 :     while ((p = dshash_seq_next(&hstat)) != NULL)
    1204                 :             :     {
    1205                 :       36597 :         PgStat_Kind kind = p->key.kind;
    1206                 :       36597 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    1207                 :             :         bool        found;
    1208                 :             :         PgStat_SnapshotEntry *entry;
    1209                 :             :         PgStatShared_Common *stats_data;
    1210                 :             : 
    1211                 :             :         /*
    1212                 :             :          * Check if the stats object should be included in the snapshot.
    1213                 :             :          * Unless the stats kind can be accessed from all databases (e.g.,
    1214                 :             :          * database stats themselves), we only include stats for the current
    1215                 :             :          * database or objects not associated with a database (e.g. shared
    1216                 :             :          * relations).
    1217                 :             :          */
    1218         [ +  + ]:       36597 :         if (p->key.dboid != MyDatabaseId &&
    1219         [ +  + ]:        9999 :             p->key.dboid != InvalidOid &&
    1220         [ +  + ]:        8292 :             !kind_info->accessed_across_databases)
    1221                 :        8304 :             continue;
    1222                 :             : 
    1223         [ +  + ]:       28395 :         if (p->dropped)
    1224                 :         102 :             continue;
    1225                 :             : 
    1226                 :             :         Assert(pg_atomic_read_u32(&p->refcount) > 0);
    1227                 :             : 
    1228                 :       28293 :         stats_data = dsa_get_address(pgStatLocal.dsa, p->body);
    1229                 :             :         Assert(stats_data);
    1230                 :             : 
    1231                 :       28293 :         entry = pgstat_snapshot_insert(pgStatLocal.snapshot.stats, p->key, &found);
    1232                 :             :         Assert(!found);
    1233                 :             : 
    1234                 :       28293 :         entry->data = MemoryContextAlloc(pgStatLocal.snapshot.context,
    1235                 :             :                                          pgstat_get_entry_len(kind));
    1236                 :             : 
    1237                 :             :         /*
    1238                 :             :          * Acquire the LWLock directly instead of using
    1239                 :             :          * pg_stat_lock_entry_shared() which requires a reference.
    1240                 :             :          */
    1241                 :       28293 :         LWLockAcquire(&stats_data->lock, LW_SHARED);
    1242                 :       28293 :         memcpy(entry->data,
    1243                 :       28293 :                pgstat_get_entry_data(kind, stats_data),
    1244                 :             :                pgstat_get_entry_len(kind));
    1245                 :       28293 :         LWLockRelease(&stats_data->lock);
    1246                 :             :     }
    1247                 :          30 :     dshash_seq_term(&hstat);
    1248                 :             : 
    1249                 :             :     /*
    1250                 :             :      * Build snapshot of all fixed-numbered stats.
    1251                 :             :      */
    1252         [ +  + ]:         990 :     for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
    1253                 :             :     {
    1254                 :         960 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    1255                 :             : 
    1256         [ +  + ]:         960 :         if (!kind_info)
    1257                 :         540 :             continue;
    1258         [ +  + ]:         420 :         if (!kind_info->fixed_amount)
    1259                 :             :         {
    1260                 :             :             Assert(kind_info->snapshot_cb == NULL);
    1261                 :         210 :             continue;
    1262                 :             :         }
    1263                 :             : 
    1264                 :         210 :         pgstat_build_snapshot_fixed(kind);
    1265                 :             :     }
    1266                 :             : 
    1267                 :          30 :     pgStatLocal.snapshot.mode = PGSTAT_FETCH_CONSISTENCY_SNAPSHOT;
    1268                 :             : }
    1269                 :             : 
    1270                 :             : static void
    1271                 :        5901 : pgstat_build_snapshot_fixed(PgStat_Kind kind)
    1272                 :             : {
    1273                 :        5901 :     const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    1274                 :             :     int         idx;
    1275                 :             :     bool       *valid;
    1276                 :             : 
    1277                 :             :     /* Position in fixed_valid or custom_valid */
    1278         [ +  + ]:        5901 :     if (pgstat_is_kind_builtin(kind))
    1279                 :             :     {
    1280                 :        5895 :         idx = kind;
    1281                 :        5895 :         valid = pgStatLocal.snapshot.fixed_valid;
    1282                 :             :     }
    1283                 :             :     else
    1284                 :             :     {
    1285                 :           6 :         idx = kind - PGSTAT_KIND_CUSTOM_MIN;
    1286                 :           6 :         valid = pgStatLocal.snapshot.custom_valid;
    1287                 :             :     }
    1288                 :             : 
    1289                 :             :     Assert(kind_info->fixed_amount);
    1290                 :             :     Assert(kind_info->snapshot_cb != NULL);
    1291                 :             : 
    1292         [ +  + ]:        5901 :     if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE)
    1293                 :             :     {
    1294                 :             :         /* rebuild every time */
    1295                 :        5434 :         valid[idx] = false;
    1296                 :             :     }
    1297         [ +  + ]:         467 :     else if (valid[idx])
    1298                 :             :     {
    1299                 :             :         /* in snapshot mode we shouldn't get called again */
    1300                 :             :         Assert(pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_CACHE);
    1301                 :           6 :         return;
    1302                 :             :     }
    1303                 :             : 
    1304                 :             :     Assert(!valid[idx]);
    1305                 :             : 
    1306                 :        5895 :     kind_info->snapshot_cb();
    1307                 :             : 
    1308                 :             :     Assert(!valid[idx]);
    1309                 :        5895 :     valid[idx] = true;
    1310                 :             : }
    1311                 :             : 
    1312                 :             : 
    1313                 :             : /* ------------------------------------------------------------
    1314                 :             :  * Backend-local pending stats infrastructure
    1315                 :             :  * ------------------------------------------------------------
    1316                 :             :  */
    1317                 :             : 
    1318                 :             : /*
    1319                 :             :  * Returns the appropriate PgStat_EntryRef, preparing it to receive pending
    1320                 :             :  * stats if not already done.
    1321                 :             :  *
    1322                 :             :  * If created_entry is non-NULL, it'll be set to true if the entry is newly
    1323                 :             :  * created, false otherwise.
    1324                 :             :  */
    1325                 :             : PgStat_EntryRef *
    1326                 :     2435778 : pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, uint64 objid, bool *created_entry)
    1327                 :             : {
    1328                 :             :     PgStat_EntryRef *entry_ref;
    1329                 :             : 
    1330                 :     2435778 :     entry_ref = pgstat_get_entry_ref(kind, dboid, objid,
    1331                 :             :                                      true, created_entry);
    1332                 :             : 
    1333                 :     2435778 :     pgstat_prep_pending_from_entry_ref(entry_ref);
    1334                 :             : 
    1335                 :     2435778 :     return entry_ref;
    1336                 :             : }
    1337                 :             : 
    1338                 :             : /*
    1339                 :             :  * Return an existing stats entry, or NULL.
    1340                 :             :  *
    1341                 :             :  * This should only be used for helper function for pgstatfuncs.c - outside of
    1342                 :             :  * that it shouldn't be needed.
    1343                 :             :  */
    1344                 :             : PgStat_EntryRef *
    1345                 :          56 : pgstat_fetch_pending_entry(PgStat_Kind kind, Oid dboid, uint64 objid)
    1346                 :             : {
    1347                 :             :     PgStat_EntryRef *entry_ref;
    1348                 :             : 
    1349                 :          56 :     entry_ref = pgstat_get_entry_ref(kind, dboid, objid, false, NULL);
    1350                 :             : 
    1351   [ +  +  +  + ]:          56 :     if (entry_ref == NULL || entry_ref->pending == NULL)
    1352                 :          20 :         return NULL;
    1353                 :             : 
    1354                 :          36 :     return entry_ref;
    1355                 :             : }
    1356                 :             : 
    1357                 :             : void
    1358                 :     1243495 : pgstat_delete_pending_entry(PgStat_EntryRef *entry_ref)
    1359                 :             : {
    1360                 :     1243495 :     PgStat_Kind kind = entry_ref->shared_entry->key.kind;
    1361                 :     1243495 :     const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    1362                 :     1243495 :     void       *pending_data = entry_ref->pending;
    1363                 :             : 
    1364                 :             :     Assert(pending_data != NULL);
    1365                 :             :     /* !fixed_amount stats should be handled explicitly */
    1366                 :             :     Assert(!pgstat_get_kind_info(kind)->fixed_amount);
    1367                 :             : 
    1368         [ +  + ]:     1243495 :     if (kind_info->delete_pending_cb)
    1369                 :     1175368 :         kind_info->delete_pending_cb(entry_ref);
    1370                 :             : 
    1371                 :     1243495 :     pfree(pending_data);
    1372                 :     1243495 :     entry_ref->pending = NULL;
    1373                 :             : 
    1374                 :     1243495 :     dlist_delete(&entry_ref->pending_node);
    1375                 :     1243495 : }
    1376                 :             : 
    1377                 :             : /*
    1378                 :             :  * Prepare the given entry to receive pending stats, if not already done.
    1379                 :             :  */
    1380                 :             : void
    1381                 :     2435778 : pgstat_prep_pending_from_entry_ref(PgStat_EntryRef *entry_ref)
    1382                 :             : {
    1383                 :             :     PgStat_Kind kind;
    1384                 :             : 
    1385                 :             :     Assert(entry_ref != NULL);
    1386                 :             : 
    1387                 :     2435778 :     kind = entry_ref->shared_entry->key.kind;
    1388                 :             : 
    1389                 :             :     /* need to be able to flush out */
    1390                 :             :     Assert(pgstat_get_kind_info(kind)->flush_pending_cb != NULL);
    1391                 :             : 
    1392         [ +  + ]:     2435778 :     if (entry_ref->pending == NULL)
    1393                 :             :     {
    1394                 :     1243495 :         size_t      entrysize = pgstat_get_kind_info(kind)->pending_size;
    1395                 :             : 
    1396                 :             :         Assert(entrysize != (size_t) -1);
    1397                 :             : 
    1398         [ +  + ]:     1243495 :         if (unlikely(!pgStatPendingContext))
    1399                 :             :         {
    1400                 :       20636 :             pgStatPendingContext =
    1401                 :       20636 :                 AllocSetContextCreate(TopMemoryContext,
    1402                 :             :                                       "PgStat Pending",
    1403                 :             :                                       ALLOCSET_SMALL_SIZES);
    1404                 :             :         }
    1405                 :             : 
    1406                 :     1243495 :         entry_ref->pending = MemoryContextAllocZero(pgStatPendingContext, entrysize);
    1407                 :     1243495 :         dlist_push_tail(&pgStatPending, &entry_ref->pending_node);
    1408                 :             :     }
    1409                 :     2435778 : }
    1410                 :             : 
    1411                 :             : /*
    1412                 :             :  * Flush out pending variable-numbered stats.
    1413                 :             :  */
    1414                 :             : static bool
    1415                 :       42053 : pgstat_flush_pending_entries(bool nowait)
    1416                 :             : {
    1417                 :       42053 :     bool        have_pending = false;
    1418                 :       42053 :     dlist_node *cur = NULL;
    1419                 :             : 
    1420                 :             :     /*
    1421                 :             :      * Need to be a bit careful iterating over the list of pending entries.
    1422                 :             :      * Processing a pending entry may queue further pending entries to the end
    1423                 :             :      * of the list that we want to process, so a simple iteration won't do.
    1424                 :             :      * Further complicating matters is that we want to delete the current
    1425                 :             :      * entry in each iteration from the list if we flushed successfully.
    1426                 :             :      *
    1427                 :             :      * So we just keep track of the next pointer in each loop iteration.
    1428                 :             :      */
    1429         [ +  + ]:       42053 :     if (!dlist_is_empty(&pgStatPending))
    1430                 :       39176 :         cur = dlist_head_node(&pgStatPending);
    1431                 :             : 
    1432         [ +  + ]:     1239027 :     while (cur)
    1433                 :             :     {
    1434                 :     1196974 :         PgStat_EntryRef *entry_ref =
    1435                 :             :             dlist_container(PgStat_EntryRef, pending_node, cur);
    1436                 :     1196974 :         PgStat_HashKey key = entry_ref->shared_entry->key;
    1437                 :     1196974 :         PgStat_Kind kind = key.kind;
    1438                 :     1196974 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    1439                 :             :         bool        did_flush;
    1440                 :             :         dlist_node *next;
    1441                 :             : 
    1442                 :             :         Assert(!kind_info->fixed_amount);
    1443                 :             :         Assert(kind_info->flush_pending_cb != NULL);
    1444                 :             : 
    1445                 :             :         /* flush the stats, if possible */
    1446                 :     1196974 :         did_flush = kind_info->flush_pending_cb(entry_ref, nowait);
    1447                 :             : 
    1448                 :             :         Assert(did_flush || nowait);
    1449                 :             : 
    1450                 :             :         /* determine next entry, before deleting the pending entry */
    1451         [ +  + ]:     1196974 :         if (dlist_has_next(&pgStatPending, cur))
    1452                 :     1157798 :             next = dlist_next_node(&pgStatPending, cur);
    1453                 :             :         else
    1454                 :       39176 :             next = NULL;
    1455                 :             : 
    1456                 :             :         /* if successfully flushed, remove entry */
    1457         [ +  + ]:     1196974 :         if (did_flush)
    1458                 :     1196962 :             pgstat_delete_pending_entry(entry_ref);
    1459                 :             :         else
    1460                 :          12 :             have_pending = true;
    1461                 :             : 
    1462                 :     1196974 :         cur = next;
    1463                 :             :     }
    1464                 :             : 
    1465                 :             :     Assert(dlist_is_empty(&pgStatPending) == !have_pending);
    1466                 :             : 
    1467                 :       42053 :     return have_pending;
    1468                 :             : }
    1469                 :             : 
    1470                 :             : 
    1471                 :             : /* ------------------------------------------------------------
    1472                 :             :  * Helper / infrastructure functions
    1473                 :             :  * ------------------------------------------------------------
    1474                 :             :  */
    1475                 :             : 
    1476                 :             : PgStat_Kind
    1477                 :         100 : pgstat_get_kind_from_str(char *kind_str)
    1478                 :             : {
    1479         [ +  + ]:         378 :     for (PgStat_Kind kind = PGSTAT_KIND_BUILTIN_MIN; kind <= PGSTAT_KIND_BUILTIN_MAX; kind++)
    1480                 :             :     {
    1481         [ +  + ]:         374 :         if (pg_strcasecmp(kind_str, pgstat_kind_builtin_infos[kind].name) == 0)
    1482                 :          96 :             return kind;
    1483                 :             :     }
    1484                 :             : 
    1485                 :             :     /* Check the custom set of cumulative stats */
    1486         [ -  + ]:           4 :     if (pgstat_kind_custom_infos)
    1487                 :             :     {
    1488         [ #  # ]:           0 :         for (PgStat_Kind kind = PGSTAT_KIND_CUSTOM_MIN; kind <= PGSTAT_KIND_CUSTOM_MAX; kind++)
    1489                 :             :         {
    1490                 :           0 :             uint32      idx = kind - PGSTAT_KIND_CUSTOM_MIN;
    1491                 :             : 
    1492   [ #  #  #  # ]:           0 :             if (pgstat_kind_custom_infos[idx] &&
    1493                 :           0 :                 pg_strcasecmp(kind_str, pgstat_kind_custom_infos[idx]->name) == 0)
    1494                 :           0 :                 return kind;
    1495                 :             :         }
    1496                 :             :     }
    1497                 :             : 
    1498         [ +  - ]:           4 :     ereport(ERROR,
    1499                 :             :             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1500                 :             :              errmsg("invalid statistics kind: \"%s\"", kind_str)));
    1501                 :             :     return PGSTAT_KIND_INVALID; /* avoid compiler warnings */
    1502                 :             : }
    1503                 :             : 
    1504                 :             : static inline bool
    1505                 :      500530 : pgstat_is_kind_valid(PgStat_Kind kind)
    1506                 :             : {
    1507   [ +  +  +  - ]:      500530 :     return pgstat_is_kind_builtin(kind) || pgstat_is_kind_custom(kind);
    1508                 :             : }
    1509                 :             : 
    1510                 :             : const PgStat_KindInfo *
    1511                 :     8898567 : pgstat_get_kind_info(PgStat_Kind kind)
    1512                 :             : {
    1513         [ +  + ]:     8898567 :     if (pgstat_is_kind_builtin(kind))
    1514                 :     7398238 :         return &pgstat_kind_builtin_infos[kind];
    1515                 :             : 
    1516         [ +  + ]:     1500329 :     if (pgstat_is_kind_custom(kind))
    1517                 :             :     {
    1518                 :      868979 :         uint32      idx = kind - PGSTAT_KIND_CUSTOM_MIN;
    1519                 :             : 
    1520         [ +  + ]:      868979 :         if (pgstat_kind_custom_infos == NULL ||
    1521         [ +  + ]:        1892 :             pgstat_kind_custom_infos[idx] == NULL)
    1522                 :      868501 :             return NULL;
    1523                 :         478 :         return pgstat_kind_custom_infos[idx];
    1524                 :             :     }
    1525                 :             : 
    1526                 :      631350 :     return NULL;
    1527                 :             : }
    1528                 :             : 
    1529                 :             : /*
    1530                 :             :  * Register a new stats kind.
    1531                 :             :  *
    1532                 :             :  * PgStat_Kinds must be globally unique across all extensions. Refer
    1533                 :             :  * to https://wiki.postgresql.org/wiki/CustomCumulativeStats to reserve a
    1534                 :             :  * unique ID for your extension, to avoid conflicts with other extension
    1535                 :             :  * developers. During development, use PGSTAT_KIND_EXPERIMENTAL to avoid
    1536                 :             :  * needlessly reserving a new ID.
    1537                 :             :  */
    1538                 :             : void
    1539                 :           6 : pgstat_register_kind(PgStat_Kind kind, const PgStat_KindInfo *kind_info)
    1540                 :             : {
    1541                 :           6 :     uint32      idx = kind - PGSTAT_KIND_CUSTOM_MIN;
    1542                 :             : 
    1543   [ +  -  -  + ]:           6 :     if (kind_info->name == NULL || strlen(kind_info->name) == 0)
    1544         [ #  # ]:           0 :         ereport(ERROR,
    1545                 :             :                 (errmsg("failed to register custom cumulative statistics with ID %u", kind),
    1546                 :             :                  errhint("Provide a non-empty name for the custom cumulative statistics.")));
    1547                 :             : 
    1548         [ -  + ]:           6 :     if (!pgstat_is_kind_custom(kind))
    1549         [ #  # ]:           0 :         ereport(ERROR, (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
    1550                 :             :                         errhint("Provide a custom cumulative statistics ID between %u and %u.",
    1551                 :             :                                 PGSTAT_KIND_CUSTOM_MIN, PGSTAT_KIND_CUSTOM_MAX)));
    1552                 :             : 
    1553         [ -  + ]:           6 :     if (!process_shared_preload_libraries_in_progress)
    1554         [ #  # ]:           0 :         ereport(ERROR,
    1555                 :             :                 (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
    1556                 :             :                  errdetail("Custom cumulative statistics must be registered while initializing modules in \"%s\".",
    1557                 :             :                            "shared_preload_libraries")));
    1558                 :             : 
    1559                 :             :     /*
    1560                 :             :      * Check some data for fixed-numbered stats.
    1561                 :             :      */
    1562         [ +  + ]:           6 :     if (kind_info->fixed_amount)
    1563                 :             :     {
    1564         [ -  + ]:           3 :         if (kind_info->shared_size == 0)
    1565         [ #  # ]:           0 :             ereport(ERROR,
    1566                 :             :                     (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
    1567                 :             :                      errhint("Custom cumulative statistics require a shared memory size for fixed-numbered objects.")));
    1568         [ -  + ]:           3 :         if (kind_info->init_shmem_cb == NULL)
    1569         [ #  # ]:           0 :             ereport(ERROR,
    1570                 :             :                     (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
    1571                 :             :                      errhint("Custom cumulative statistics require a \"%s\" callback for fixed-numbered objects.",
    1572                 :             :                              "init_shmem_cb")));
    1573         [ -  + ]:           3 :         if (kind_info->reset_all_cb == NULL)
    1574         [ #  # ]:           0 :             ereport(ERROR,
    1575                 :             :                     (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
    1576                 :             :                      errhint("Custom cumulative statistics require a \"%s\" callback for fixed-numbered objects.",
    1577                 :             :                              "reset_all_cb")));
    1578         [ -  + ]:           3 :         if (kind_info->snapshot_cb == NULL)
    1579         [ #  # ]:           0 :             ereport(ERROR,
    1580                 :             :                     (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
    1581                 :             :                      errhint("Custom cumulative statistics require a \"%s\" callback for fixed-numbered objects.",
    1582                 :             :                              "snapshot_cb")));
    1583         [ -  + ]:           3 :         if (kind_info->track_entry_count)
    1584         [ #  # ]:           0 :             ereport(ERROR,
    1585                 :             :                     (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
    1586                 :             :                      errhint("Custom cumulative statistics cannot use entry count tracking for fixed-numbered objects.")));
    1587                 :             :     }
    1588                 :             :     else
    1589                 :             :     {
    1590   [ +  -  -  + ]:           3 :         if (kind_info->pending_size > 0 && kind_info->flush_pending_cb == NULL)
    1591         [ #  # ]:           0 :             ereport(ERROR,
    1592                 :             :                     (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
    1593                 :             :                      errhint("Custom cumulative statistics require a \"%s\" callback when pending size is set.",
    1594                 :             :                              "flush_pending_cb")));
    1595                 :             :     }
    1596                 :             : 
    1597                 :             :     /*
    1598                 :             :      * If pgstat_kind_custom_infos is not available yet, allocate it.
    1599                 :             :      */
    1600         [ +  + ]:           6 :     if (pgstat_kind_custom_infos == NULL)
    1601                 :             :     {
    1602                 :           3 :         pgstat_kind_custom_infos = (const PgStat_KindInfo **)
    1603                 :           3 :             MemoryContextAllocZero(TopMemoryContext,
    1604                 :             :                                    sizeof(PgStat_KindInfo *) * PGSTAT_KIND_CUSTOM_SIZE);
    1605                 :             :     }
    1606                 :             : 
    1607         [ -  + ]:           6 :     if (pgstat_kind_custom_infos[idx] != NULL &&
    1608         [ #  # ]:           0 :         pgstat_kind_custom_infos[idx]->name != NULL)
    1609         [ #  # ]:           0 :         ereport(ERROR,
    1610                 :             :                 (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
    1611                 :             :                  errdetail("Custom cumulative statistics \"%s\" already registered with the same ID.",
    1612                 :             :                            pgstat_kind_custom_infos[idx]->name)));
    1613                 :             : 
    1614                 :             :     /* check for existing custom stats with the same name */
    1615         [ +  + ]:          60 :     for (PgStat_Kind existing_kind = PGSTAT_KIND_CUSTOM_MIN; existing_kind <= PGSTAT_KIND_CUSTOM_MAX; existing_kind++)
    1616                 :             :     {
    1617                 :          54 :         uint32      existing_idx = existing_kind - PGSTAT_KIND_CUSTOM_MIN;
    1618                 :             : 
    1619         [ +  + ]:          54 :         if (pgstat_kind_custom_infos[existing_idx] == NULL)
    1620                 :          51 :             continue;
    1621         [ -  + ]:           3 :         if (!pg_strcasecmp(pgstat_kind_custom_infos[existing_idx]->name, kind_info->name))
    1622         [ #  # ]:           0 :             ereport(ERROR,
    1623                 :             :                     (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
    1624                 :             :                      errdetail("Existing cumulative statistics with ID %u has the same name.", existing_kind)));
    1625                 :             :     }
    1626                 :             : 
    1627                 :             :     /* Register it */
    1628                 :           6 :     pgstat_kind_custom_infos[idx] = kind_info;
    1629         [ +  - ]:           6 :     ereport(LOG,
    1630                 :             :             (errmsg("registered custom cumulative statistics \"%s\" with ID %u",
    1631                 :             :                     kind_info->name, kind)));
    1632                 :           6 : }
    1633                 :             : 
    1634                 :             : /*
    1635                 :             :  * Stats should only be reported after pgstat_initialize() and before
    1636                 :             :  * pgstat_shutdown(). This check is put in a few central places to catch
    1637                 :             :  * violations of this rule more easily.
    1638                 :             :  */
    1639                 :             : #ifdef USE_ASSERT_CHECKING
    1640                 :             : void
    1641                 :             : pgstat_assert_is_up(void)
    1642                 :             : {
    1643                 :             :     Assert(pgstat_is_initialized && !pgstat_is_shutdown);
    1644                 :             : }
    1645                 :             : #endif
    1646                 :             : 
    1647                 :             : 
    1648                 :             : /* ------------------------------------------------------------
    1649                 :             :  * reading and writing of on-disk stats file
    1650                 :             :  * ------------------------------------------------------------
    1651                 :             :  */
    1652                 :             : 
    1653                 :             : #define write_chunk_s(fpout, ptr) write_chunk(fpout, ptr, sizeof(*ptr))
    1654                 :             : #define read_chunk_s(fpin, ptr) read_chunk(fpin, ptr, sizeof(*ptr))
    1655                 :             : 
    1656                 :             : /* helpers for pgstat_write_statsfile() */
    1657                 :             : static void
    1658                 :      497680 : write_chunk(FILE *fpout, void *ptr, size_t len)
    1659                 :             : {
    1660                 :             :     int         rc;
    1661                 :             : 
    1662                 :      497680 :     rc = fwrite(ptr, len, 1, fpout);
    1663                 :             : 
    1664                 :             :     /* we'll check for errors with ferror once at the end */
    1665                 :             :     (void) rc;
    1666                 :      497680 : }
    1667                 :             : 
    1668                 :             : /*
    1669                 :             :  * This function is called in the last process that is accessing the shared
    1670                 :             :  * stats so locking is not required.
    1671                 :             :  */
    1672                 :             : static void
    1673                 :         774 : pgstat_write_statsfile(void)
    1674                 :             : {
    1675                 :             :     FILE       *fpout;
    1676                 :             :     int32       format_id;
    1677                 :         774 :     const char *tmpfile = PGSTAT_STAT_PERMANENT_TMPFILE;
    1678                 :         774 :     const char *statfile = PGSTAT_STAT_PERMANENT_FILENAME;
    1679                 :             :     dshash_seq_status hstat;
    1680                 :             :     PgStatShared_HashEntry *ps;
    1681                 :         774 :     PgStat_StatsFileOp status = STATS_WRITE;
    1682                 :             : 
    1683                 :             :     pgstat_assert_is_up();
    1684                 :             : 
    1685                 :             :     /* should be called only by the checkpointer or single user mode */
    1686                 :             :     Assert(!IsUnderPostmaster || MyBackendType == B_CHECKPOINTER);
    1687                 :             : 
    1688                 :             :     /* we're shutting down, so it's ok to just override this */
    1689                 :         774 :     pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_NONE;
    1690                 :             : 
    1691         [ +  + ]:         774 :     elog(DEBUG2, "writing stats file \"%s\"", statfile);
    1692                 :             : 
    1693                 :             :     /*
    1694                 :             :      * Open the statistics temp file to write out the current values.
    1695                 :             :      */
    1696                 :         774 :     fpout = AllocateFile(tmpfile, PG_BINARY_W);
    1697         [ -  + ]:         774 :     if (fpout == NULL)
    1698                 :             :     {
    1699         [ #  # ]:           0 :         ereport(LOG,
    1700                 :             :                 (errcode_for_file_access(),
    1701                 :             :                  errmsg("could not open temporary statistics file \"%s\": %m",
    1702                 :             :                         tmpfile)));
    1703                 :           0 :         return;
    1704                 :             :     }
    1705                 :             : 
    1706                 :             :     /*
    1707                 :             :      * Write the file header --- currently just a format ID.
    1708                 :             :      */
    1709                 :         774 :     format_id = PGSTAT_FILE_FORMAT_ID;
    1710                 :         774 :     write_chunk_s(fpout, &format_id);
    1711                 :             : 
    1712                 :             :     /* Write various stats structs for fixed number of objects */
    1713         [ +  + ]:       25542 :     for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
    1714                 :             :     {
    1715                 :             :         char       *ptr;
    1716                 :       24768 :         const PgStat_KindInfo *info = pgstat_get_kind_info(kind);
    1717                 :             : 
    1718   [ +  +  +  + ]:       24768 :         if (!info || !info->fixed_amount)
    1719                 :       19349 :             continue;
    1720                 :             : 
    1721                 :        5419 :         if (pgstat_is_kind_builtin(kind))
    1722                 :             :             Assert(info->snapshot_ctl_off != 0);
    1723                 :             : 
    1724                 :             :         /* skip if no need to write to file */
    1725         [ -  + ]:        5419 :         if (!info->write_to_file)
    1726                 :           0 :             continue;
    1727                 :             : 
    1728                 :        5419 :         pgstat_build_snapshot_fixed(kind);
    1729         [ +  + ]:        5419 :         if (pgstat_is_kind_builtin(kind))
    1730                 :        5418 :             ptr = ((char *) &pgStatLocal.snapshot) + info->snapshot_ctl_off;
    1731                 :             :         else
    1732                 :           1 :             ptr = pgStatLocal.snapshot.custom_data[kind - PGSTAT_KIND_CUSTOM_MIN];
    1733                 :             : 
    1734                 :        5419 :         fputc(PGSTAT_FILE_ENTRY_FIXED, fpout);
    1735                 :        5419 :         write_chunk_s(fpout, &kind);
    1736                 :        5419 :         write_chunk(fpout, ptr, info->shared_data_len);
    1737                 :             :     }
    1738                 :             : 
    1739                 :             :     /*
    1740                 :             :      * Walk through the stats entries
    1741                 :             :      */
    1742                 :         774 :     dshash_seq_init(&hstat, pgStatLocal.shared_hash, false);
    1743         [ +  + ]:      243879 :     while ((ps = dshash_seq_next(&hstat)) != NULL)
    1744                 :             :     {
    1745                 :             :         PgStatShared_Common *shstats;
    1746                 :      243105 :         const PgStat_KindInfo *kind_info = NULL;
    1747                 :             : 
    1748         [ -  + ]:      243105 :         CHECK_FOR_INTERRUPTS();
    1749                 :             : 
    1750                 :             :         /*
    1751                 :             :          * We should not see any "dropped" entries when writing the stats
    1752                 :             :          * file, as all backends and auxiliary processes should have cleaned
    1753                 :             :          * up their references before they terminated.
    1754                 :             :          *
    1755                 :             :          * However, since we are already shutting down, it is not worth
    1756                 :             :          * crashing the server over any potential cleanup issues, so we simply
    1757                 :             :          * skip such entries if encountered.
    1758                 :             :          */
    1759                 :             :         Assert(!ps->dropped);
    1760         [ -  + ]:      243105 :         if (ps->dropped)
    1761                 :           0 :             continue;
    1762                 :             : 
    1763                 :             :         /*
    1764                 :             :          * This discards data related to custom stats kinds that are unknown
    1765                 :             :          * to this process.
    1766                 :             :          */
    1767         [ -  + ]:      243105 :         if (!pgstat_is_kind_valid(ps->key.kind))
    1768                 :             :         {
    1769         [ #  # ]:           0 :             elog(WARNING, "found unknown stats entry %u/%u/%" PRIu64,
    1770                 :             :                  ps->key.kind, ps->key.dboid,
    1771                 :             :                  ps->key.objid);
    1772                 :           0 :             continue;
    1773                 :             :         }
    1774                 :             : 
    1775                 :      243105 :         shstats = (PgStatShared_Common *) dsa_get_address(pgStatLocal.dsa, ps->body);
    1776                 :             : 
    1777                 :      243105 :         kind_info = pgstat_get_kind_info(ps->key.kind);
    1778                 :             : 
    1779                 :             :         /* if not dropped the valid-entry refcount should exist */
    1780                 :             :         Assert(pg_atomic_read_u32(&ps->refcount) > 0);
    1781                 :             : 
    1782                 :             :         /* skip if no need to write to file */
    1783         [ +  + ]:      243105 :         if (!kind_info->write_to_file)
    1784                 :         131 :             continue;
    1785                 :             : 
    1786         [ +  + ]:      242974 :         if (!kind_info->to_serialized_name)
    1787                 :             :         {
    1788                 :             :             /* normal stats entry, identified by PgStat_HashKey */
    1789                 :      242854 :             fputc(PGSTAT_FILE_ENTRY_HASH, fpout);
    1790                 :      242854 :             write_chunk_s(fpout, &ps->key);
    1791                 :             :         }
    1792                 :             :         else
    1793                 :             :         {
    1794                 :             :             /* stats entry identified by name on disk (e.g. slots) */
    1795                 :             :             NameData    name;
    1796                 :             : 
    1797                 :         120 :             kind_info->to_serialized_name(&ps->key, shstats, &name);
    1798                 :             : 
    1799                 :         120 :             fputc(PGSTAT_FILE_ENTRY_NAME, fpout);
    1800                 :         120 :             write_chunk_s(fpout, &ps->key.kind);
    1801                 :         120 :             write_chunk_s(fpout, &name);
    1802                 :             :         }
    1803                 :             : 
    1804                 :             :         /* Write except the header part of the entry */
    1805                 :      242974 :         write_chunk(fpout,
    1806                 :             :                     pgstat_get_entry_data(ps->key.kind, shstats),
    1807                 :             :                     pgstat_get_entry_len(ps->key.kind));
    1808                 :             : 
    1809                 :             :         /* Write more data for the entry, if required */
    1810         [ +  + ]:      242974 :         if (kind_info->to_serialized_data &&
    1811         [ -  + ]:           2 :             !kind_info->to_serialized_data(&ps->key, shstats, fpout))
    1812                 :             :         {
    1813                 :           0 :             status = STATS_DISCARD;
    1814                 :           0 :             break;
    1815                 :             :         }
    1816                 :             :     }
    1817                 :         774 :     dshash_seq_term(&hstat);
    1818                 :             : 
    1819                 :             :     /*
    1820                 :             :      * No more output to be done. Close the temp file and replace the old
    1821                 :             :      * pgstat.stat with it.  The ferror() check replaces testing for error
    1822                 :             :      * after each individual fputc or fwrite (in write_chunk()) above.
    1823                 :             :      */
    1824                 :         774 :     fputc(PGSTAT_FILE_ENTRY_END, fpout);
    1825                 :             : 
    1826         [ -  + ]:         774 :     if (status == STATS_DISCARD)
    1827                 :             :     {
    1828                 :             :         /*
    1829                 :             :          * A to_serialized_data callback failed.  DEBUG2 because the callback
    1830                 :             :          * already logged the reason.
    1831                 :             :          */
    1832         [ #  # ]:           0 :         elog(DEBUG2, "discarding temporary statistics file \"%s\"", tmpfile);
    1833                 :           0 :         FreeFile(fpout);
    1834                 :           0 :         unlink(tmpfile);
    1835                 :             :     }
    1836         [ -  + ]:         774 :     else if (ferror(fpout))
    1837                 :             :     {
    1838         [ #  # ]:           0 :         ereport(LOG,
    1839                 :             :                 (errcode_for_file_access(),
    1840                 :             :                  errmsg("could not write temporary statistics file \"%s\": %m",
    1841                 :             :                         tmpfile)));
    1842                 :           0 :         FreeFile(fpout);
    1843                 :           0 :         unlink(tmpfile);
    1844                 :           0 :         status = STATS_DISCARD;
    1845                 :             :     }
    1846         [ -  + ]:         774 :     else if (FreeFile(fpout) < 0)
    1847                 :             :     {
    1848         [ #  # ]:           0 :         ereport(LOG,
    1849                 :             :                 (errcode_for_file_access(),
    1850                 :             :                  errmsg("could not close temporary statistics file \"%s\": %m",
    1851                 :             :                         tmpfile)));
    1852                 :           0 :         unlink(tmpfile);
    1853                 :           0 :         status = STATS_DISCARD;
    1854                 :             :     }
    1855         [ -  + ]:         774 :     else if (durable_rename(tmpfile, statfile, LOG) < 0)
    1856                 :             :     {
    1857                 :             :         /* durable_rename already emitted log message */
    1858                 :           0 :         unlink(tmpfile);
    1859                 :           0 :         status = STATS_DISCARD;
    1860                 :             :     }
    1861                 :             : 
    1862                 :             :     /* Finish callbacks, if required */
    1863         [ +  + ]:       25542 :     for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
    1864                 :             :     {
    1865                 :       24768 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    1866                 :             : 
    1867   [ +  +  +  + ]:       24768 :         if (kind_info && kind_info->finish)
    1868                 :           1 :             kind_info->finish(status);
    1869                 :             :     }
    1870                 :             : }
    1871                 :             : 
    1872                 :             : /* helpers for pgstat_read_statsfile() */
    1873                 :             : static bool
    1874                 :      515776 : read_chunk(FILE *fpin, void *ptr, size_t len)
    1875                 :             : {
    1876                 :      515776 :     return fread(ptr, 1, len, fpin) == len;
    1877                 :             : }
    1878                 :             : 
    1879                 :             : /*
    1880                 :             :  * Reads in existing statistics file into memory.
    1881                 :             :  *
    1882                 :             :  * This function is called in the only process that is accessing the shared
    1883                 :             :  * stats so locking is not required.
    1884                 :             :  */
    1885                 :             : static void
    1886                 :         899 : pgstat_read_statsfile(void)
    1887                 :             : {
    1888                 :             :     FILE       *fpin;
    1889                 :             :     int32       format_id;
    1890                 :             :     bool        found;
    1891                 :         899 :     PgStat_StatsFileOp status = STATS_READ;
    1892                 :         899 :     const char *statfile = PGSTAT_STAT_PERMANENT_FILENAME;
    1893                 :         899 :     PgStat_ShmemControl *shmem = pgStatLocal.shmem;
    1894                 :             : 
    1895                 :             :     /* shouldn't be called from postmaster */
    1896                 :             :     Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
    1897                 :             : 
    1898         [ +  + ]:         899 :     elog(DEBUG2, "reading stats file \"%s\"", statfile);
    1899                 :             : 
    1900                 :             :     /*
    1901                 :             :      * Try to open the stats file. If it doesn't exist, the backends simply
    1902                 :             :      * returns zero for anything and statistics simply starts from scratch
    1903                 :             :      * with empty counters.
    1904                 :             :      *
    1905                 :             :      * ENOENT is a possibility if stats collection was previously disabled or
    1906                 :             :      * has not yet written the stats file for the first time.  Any other
    1907                 :             :      * failure condition is suspicious.
    1908                 :             :      */
    1909         [ +  + ]:         899 :     if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
    1910                 :             :     {
    1911         [ -  + ]:          58 :         if (errno != ENOENT)
    1912         [ #  # ]:           0 :             ereport(LOG,
    1913                 :             :                     (errcode_for_file_access(),
    1914                 :             :                      errmsg("could not open statistics file \"%s\": %m",
    1915                 :             :                             statfile)));
    1916                 :          58 :         pgstat_reset_after_failure();
    1917                 :          58 :         status = STATS_DISCARD;
    1918                 :          58 :         goto finish;
    1919                 :             :     }
    1920                 :             : 
    1921                 :             :     /*
    1922                 :             :      * Verify it's of the expected format.
    1923                 :             :      */
    1924         [ -  + ]:         841 :     if (!read_chunk_s(fpin, &format_id))
    1925                 :             :     {
    1926         [ #  # ]:           0 :         elog(WARNING, "could not read format ID");
    1927                 :           0 :         goto error;
    1928                 :             :     }
    1929                 :             : 
    1930         [ +  + ]:         841 :     if (format_id != PGSTAT_FILE_FORMAT_ID)
    1931                 :             :     {
    1932         [ +  - ]:           1 :         elog(WARNING, "found incorrect format ID %d (expected %d)",
    1933                 :             :              format_id, PGSTAT_FILE_FORMAT_ID);
    1934                 :           1 :         goto error;
    1935                 :             :     }
    1936                 :             : 
    1937                 :             :     /*
    1938                 :             :      * We found an existing statistics file. Read it and put all the stats
    1939                 :             :      * data into place.
    1940                 :             :      */
    1941                 :             :     for (;;)
    1942                 :      257425 :     {
    1943                 :      258265 :         int         t = fgetc(fpin);
    1944                 :             : 
    1945   [ +  +  +  - ]:      258265 :         switch (t)
    1946                 :             :         {
    1947                 :        5881 :             case PGSTAT_FILE_ENTRY_FIXED:
    1948                 :             :                 {
    1949                 :             :                     PgStat_Kind kind;
    1950                 :             :                     const PgStat_KindInfo *info;
    1951                 :             :                     char       *ptr;
    1952                 :             : 
    1953                 :             :                     /* entry for fixed-numbered stats */
    1954         [ -  + ]:        5881 :                     if (!read_chunk_s(fpin, &kind))
    1955                 :             :                     {
    1956         [ #  # ]:           0 :                         elog(WARNING, "could not read stats kind for entry of type %c", t);
    1957                 :           0 :                         goto error;
    1958                 :             :                     }
    1959                 :             : 
    1960         [ -  + ]:        5881 :                     if (!pgstat_is_kind_valid(kind))
    1961                 :             :                     {
    1962         [ #  # ]:           0 :                         elog(WARNING, "invalid stats kind %u for entry of type %c",
    1963                 :             :                              kind, t);
    1964                 :           0 :                         goto error;
    1965                 :             :                     }
    1966                 :             : 
    1967                 :        5881 :                     info = pgstat_get_kind_info(kind);
    1968         [ -  + ]:        5881 :                     if (!info)
    1969                 :             :                     {
    1970         [ #  # ]:           0 :                         elog(WARNING, "could not find information of kind %u for entry of type %c",
    1971                 :             :                              kind, t);
    1972                 :           0 :                         goto error;
    1973                 :             :                     }
    1974                 :             : 
    1975         [ -  + ]:        5881 :                     if (!info->fixed_amount)
    1976                 :             :                     {
    1977         [ #  # ]:           0 :                         elog(WARNING, "invalid fixed_amount in stats kind %u for entry of type %c",
    1978                 :             :                              kind, t);
    1979                 :           0 :                         goto error;
    1980                 :             :                     }
    1981                 :             : 
    1982                 :             :                     /* Load back stats into shared memory */
    1983         [ +  + ]:        5881 :                     if (pgstat_is_kind_builtin(kind))
    1984                 :        5880 :                         ptr = ((char *) shmem) + info->shared_ctl_off +
    1985                 :        5880 :                             info->shared_data_off;
    1986                 :             :                     else
    1987                 :             :                     {
    1988                 :           1 :                         int         idx = kind - PGSTAT_KIND_CUSTOM_MIN;
    1989                 :             : 
    1990                 :           1 :                         ptr = ((char *) shmem->custom_data[idx]) +
    1991                 :           1 :                             info->shared_data_off;
    1992                 :             :                     }
    1993                 :             : 
    1994         [ -  + ]:        5881 :                     if (!read_chunk(fpin, ptr, info->shared_data_len))
    1995                 :             :                     {
    1996         [ #  # ]:           0 :                         elog(WARNING, "could not read data of stats kind %u for entry of type %c with size %u",
    1997                 :             :                              kind, t, info->shared_data_len);
    1998                 :           0 :                         goto error;
    1999                 :             :                     }
    2000                 :             : 
    2001                 :        5881 :                     break;
    2002                 :             :                 }
    2003                 :      251544 :             case PGSTAT_FILE_ENTRY_HASH:
    2004                 :             :             case PGSTAT_FILE_ENTRY_NAME:
    2005                 :             :                 {
    2006                 :             :                     PgStat_HashKey key;
    2007                 :             :                     PgStatShared_HashEntry *p;
    2008                 :             :                     PgStatShared_Common *header;
    2009                 :      251544 :                     const PgStat_KindInfo *kind_info = NULL;
    2010                 :             : 
    2011         [ -  + ]:      251544 :                     CHECK_FOR_INTERRUPTS();
    2012                 :             : 
    2013         [ +  + ]:      251544 :                     if (t == PGSTAT_FILE_ENTRY_HASH)
    2014                 :             :                     {
    2015                 :             :                         /* normal stats entry, identified by PgStat_HashKey */
    2016         [ -  + ]:      251458 :                         if (!read_chunk_s(fpin, &key))
    2017                 :             :                         {
    2018         [ #  # ]:           0 :                             elog(WARNING, "could not read key for entry of type %c", t);
    2019                 :           0 :                             goto error;
    2020                 :             :                         }
    2021                 :             : 
    2022         [ -  + ]:      251458 :                         if (!pgstat_is_kind_valid(key.kind))
    2023                 :             :                         {
    2024         [ #  # ]:           0 :                             elog(WARNING, "invalid stats kind for entry %u/%u/%" PRIu64 " of type %c",
    2025                 :             :                                  key.kind, key.dboid,
    2026                 :             :                                  key.objid, t);
    2027                 :           0 :                             goto error;
    2028                 :             :                         }
    2029                 :             : 
    2030                 :      251458 :                         kind_info = pgstat_get_kind_info(key.kind);
    2031         [ -  + ]:      251458 :                         if (!kind_info)
    2032                 :             :                         {
    2033         [ #  # ]:           0 :                             elog(WARNING, "could not find information of kind for entry %u/%u/%" PRIu64 " of type %c",
    2034                 :             :                                  key.kind, key.dboid,
    2035                 :             :                                  key.objid, t);
    2036                 :           0 :                             goto error;
    2037                 :             :                         }
    2038                 :             :                     }
    2039                 :             :                     else
    2040                 :             :                     {
    2041                 :             :                         /* stats entry identified by name on disk (e.g. slots) */
    2042                 :             :                         PgStat_Kind kind;
    2043                 :             :                         NameData    name;
    2044                 :             : 
    2045         [ -  + ]:          86 :                         if (!read_chunk_s(fpin, &kind))
    2046                 :             :                         {
    2047         [ #  # ]:           0 :                             elog(WARNING, "could not read stats kind for entry of type %c", t);
    2048                 :           0 :                             goto error;
    2049                 :             :                         }
    2050         [ -  + ]:          86 :                         if (!read_chunk_s(fpin, &name))
    2051                 :             :                         {
    2052         [ #  # ]:           0 :                             elog(WARNING, "could not read name of stats kind %u for entry of type %c",
    2053                 :             :                                  kind, t);
    2054                 :           0 :                             goto error;
    2055                 :             :                         }
    2056         [ -  + ]:          86 :                         if (!pgstat_is_kind_valid(kind))
    2057                 :             :                         {
    2058         [ #  # ]:           0 :                             elog(WARNING, "invalid stats kind %u for entry of type %c",
    2059                 :             :                                  kind, t);
    2060                 :           0 :                             goto error;
    2061                 :             :                         }
    2062                 :             : 
    2063                 :          86 :                         kind_info = pgstat_get_kind_info(kind);
    2064         [ -  + ]:          86 :                         if (!kind_info)
    2065                 :             :                         {
    2066         [ #  # ]:           0 :                             elog(WARNING, "could not find information of kind %u for entry of type %c",
    2067                 :             :                                  kind, t);
    2068                 :           0 :                             goto error;
    2069                 :             :                         }
    2070                 :             : 
    2071         [ -  + ]:          86 :                         if (!kind_info->from_serialized_name)
    2072                 :             :                         {
    2073         [ #  # ]:           0 :                             elog(WARNING, "invalid from_serialized_name in stats kind %u for entry of type %c",
    2074                 :             :                                  kind, t);
    2075                 :           0 :                             goto error;
    2076                 :             :                         }
    2077                 :             : 
    2078         [ +  + ]:          86 :                         if (!kind_info->from_serialized_name(&name, &key))
    2079                 :             :                         {
    2080                 :             :                             /* skip over data for entry we don't care about */
    2081         [ -  + ]:           1 :                             if (fseek(fpin, pgstat_get_entry_len(kind), SEEK_CUR) != 0)
    2082                 :             :                             {
    2083         [ #  # ]:           0 :                                 elog(WARNING, "could not seek \"%s\" of stats kind %u for entry of type %c",
    2084                 :             :                                      NameStr(name), kind, t);
    2085                 :           0 :                                 goto error;
    2086                 :             :                             }
    2087                 :             : 
    2088                 :           1 :                             continue;
    2089                 :             :                         }
    2090                 :             : 
    2091                 :             :                         Assert(key.kind == kind);
    2092                 :             :                     }
    2093                 :             : 
    2094                 :             :                     /*
    2095                 :             :                      * This intentionally doesn't use pgstat_get_entry_ref() -
    2096                 :             :                      * putting all stats into checkpointer's
    2097                 :             :                      * pgStatEntryRefHash would be wasted effort and memory.
    2098                 :             :                      */
    2099                 :      251543 :                     p = dshash_find_or_insert(pgStatLocal.shared_hash, &key, &found);
    2100                 :             : 
    2101                 :             :                     /* don't allow duplicate entries */
    2102         [ -  + ]:      251543 :                     if (found)
    2103                 :             :                     {
    2104                 :           0 :                         dshash_release_lock(pgStatLocal.shared_hash, p);
    2105         [ #  # ]:           0 :                         elog(WARNING, "found duplicate stats entry %u/%u/%" PRIu64 " of type %c",
    2106                 :             :                              key.kind, key.dboid,
    2107                 :             :                              key.objid, t);
    2108                 :           0 :                         goto error;
    2109                 :             :                     }
    2110                 :             : 
    2111                 :      251543 :                     header = pgstat_init_entry(key.kind, p);
    2112                 :      251543 :                     dshash_release_lock(pgStatLocal.shared_hash, p);
    2113         [ -  + ]:      251543 :                     if (header == NULL)
    2114                 :             :                     {
    2115                 :             :                         /*
    2116                 :             :                          * It would be tempting to switch this ERROR to a
    2117                 :             :                          * WARNING, but it would mean that all the statistics
    2118                 :             :                          * are discarded when the environment fails on OOM.
    2119                 :             :                          */
    2120         [ #  # ]:           0 :                         elog(ERROR, "could not allocate entry %u/%u/%" PRIu64 " of type %c",
    2121                 :             :                              key.kind, key.dboid,
    2122                 :             :                              key.objid, t);
    2123                 :             :                     }
    2124                 :             : 
    2125         [ -  + ]:      251543 :                     if (!read_chunk(fpin,
    2126                 :             :                                     pgstat_get_entry_data(key.kind, header),
    2127                 :             :                                     pgstat_get_entry_len(key.kind)))
    2128                 :             :                     {
    2129         [ #  # ]:           0 :                         elog(WARNING, "could not read data for entry %u/%u/%" PRIu64 " of type %c",
    2130                 :             :                              key.kind, key.dboid,
    2131                 :             :                              key.objid, t);
    2132                 :           0 :                         goto error;
    2133                 :             :                     }
    2134                 :             : 
    2135                 :             :                     /* read more data for the entry, if required */
    2136         [ +  + ]:      251543 :                     if (kind_info->from_serialized_data)
    2137                 :             :                     {
    2138         [ -  + ]:           2 :                         if (!kind_info->from_serialized_data(&key, header, fpin))
    2139                 :             :                         {
    2140         [ #  # ]:           0 :                             elog(WARNING, "could not read auxiliary data for entry %u/%u/%" PRIu64 " of type %c",
    2141                 :             :                                  key.kind, key.dboid,
    2142                 :             :                                  key.objid, t);
    2143                 :           0 :                             goto error;
    2144                 :             :                         }
    2145                 :             :                     }
    2146                 :             : 
    2147                 :      251543 :                     break;
    2148                 :             :                 }
    2149                 :         840 :             case PGSTAT_FILE_ENTRY_END:
    2150                 :             : 
    2151                 :             :                 /*
    2152                 :             :                  * check that PGSTAT_FILE_ENTRY_END actually signals end of
    2153                 :             :                  * file
    2154                 :             :                  */
    2155         [ +  + ]:         840 :                 if (fgetc(fpin) != EOF)
    2156                 :             :                 {
    2157         [ +  - ]:           1 :                     elog(WARNING, "could not read end-of-file");
    2158                 :           1 :                     goto error;
    2159                 :             :                 }
    2160                 :             : 
    2161                 :         839 :                 goto done;
    2162                 :             : 
    2163                 :           0 :             default:
    2164         [ #  # ]:           0 :                 elog(WARNING, "could not read entry of type %c", t);
    2165                 :           0 :                 goto error;
    2166                 :             :         }
    2167                 :             :     }
    2168                 :             : 
    2169                 :         841 : done:
    2170                 :             :     /* First, cleanup the main stats file */
    2171                 :         841 :     FreeFile(fpin);
    2172                 :             : 
    2173         [ +  + ]:         841 :     elog(DEBUG2, "removing permanent stats file \"%s\"", statfile);
    2174                 :         841 :     unlink(statfile);
    2175                 :             : 
    2176                 :         899 : finish:
    2177                 :             :     /* Finish callbacks, if required */
    2178         [ +  + ]:       29667 :     for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
    2179                 :             :     {
    2180                 :       28768 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    2181                 :             : 
    2182   [ +  +  +  + ]:       28768 :         if (kind_info && kind_info->finish)
    2183                 :           2 :             kind_info->finish(status);
    2184                 :             :     }
    2185                 :             : 
    2186                 :         899 :     return;
    2187                 :             : 
    2188                 :           2 : error:
    2189         [ +  - ]:           2 :     ereport(LOG,
    2190                 :             :             (errmsg("corrupted statistics file \"%s\"", statfile)));
    2191                 :             : 
    2192                 :           2 :     pgstat_reset_after_failure();
    2193                 :           2 :     status = STATS_DISCARD;
    2194                 :             : 
    2195                 :           2 :     goto done;
    2196                 :             : }
    2197                 :             : 
    2198                 :             : /*
    2199                 :             :  * Helper to reset / drop stats after a crash or after restoring stats from
    2200                 :             :  * disk failed, potentially after already loading parts.
    2201                 :             :  */
    2202                 :             : static void
    2203                 :         258 : pgstat_reset_after_failure(void)
    2204                 :             : {
    2205                 :         258 :     TimestampTz ts = GetCurrentTimestamp();
    2206                 :             : 
    2207                 :             :     /* reset fixed-numbered stats */
    2208         [ +  + ]:        8514 :     for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
    2209                 :             :     {
    2210                 :        8256 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    2211                 :             : 
    2212   [ +  +  +  + ]:        8256 :         if (!kind_info || !kind_info->fixed_amount)
    2213                 :        6449 :             continue;
    2214                 :             : 
    2215                 :        1807 :         kind_info->reset_all_cb(ts);
    2216                 :             :     }
    2217                 :             : 
    2218                 :             :     /* and drop variable-numbered ones */
    2219                 :         258 :     pgstat_drop_all_entries();
    2220                 :         258 : }
    2221                 :             : 
    2222                 :             : /*
    2223                 :             :  * GUC assign_hook for stats_fetch_consistency.
    2224                 :             :  */
    2225                 :             : void
    2226                 :        3521 : assign_stats_fetch_consistency(int newval, void *extra)
    2227                 :             : {
    2228                 :             :     /*
    2229                 :             :      * Changing this value in a transaction may cause snapshot state
    2230                 :             :      * inconsistencies, so force a clear of the current snapshot on the next
    2231                 :             :      * snapshot build attempt.
    2232                 :             :      */
    2233         [ +  + ]:        3521 :     if (pgstat_fetch_consistency != newval)
    2234                 :        2102 :         force_stats_snapshot_clear = true;
    2235                 :        3521 : }
        

Generated by: LCOV version 2.0-1