LCOV - differential code coverage report
Current view: top level - src/backend/utils/activity - pgstat.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 92.7 % 436 404 1 4 25 2 8 262 9 125 21 273 1 2
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 32 32 32 32
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  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, except when shutting down in immediate mode.
      16                 :  *
      17                 :  * Fixed-numbered stats are stored in plain (non-dynamic) shared memory.
      18                 :  *
      19                 :  * Statistics for variable-numbered objects are stored in dynamic shared
      20                 :  * memory and can be found via a dshash hashtable. The statistics counters are
      21                 :  * not part of the dshash entry (PgStatShared_HashEntry) directly, but are
      22                 :  * separately allocated (PgStatShared_HashEntry->body). The separate
      23                 :  * allocation allows different kinds of statistics to be stored in the same
      24                 :  * hashtable without wasting space in PgStatShared_HashEntry.
      25                 :  *
      26                 :  * Variable-numbered stats are addressed by PgStat_HashKey while running.  It
      27                 :  * is not possible to have statistics for an object that cannot be addressed
      28                 :  * that way at runtime. A wider identifier can be used when serializing to
      29                 :  * disk (used for replication slot stats).
      30                 :  *
      31                 :  * To avoid contention on the shared hashtable, each backend has a
      32                 :  * backend-local hashtable (pgStatEntryRefHash) in front of the shared
      33                 :  * hashtable, containing references (PgStat_EntryRef) to shared hashtable
      34                 :  * entries. The shared hashtable only needs to be accessed when no prior
      35                 :  * reference is found in the local hashtable. Besides pointing to the
      36                 :  * shared hashtable entry (PgStatShared_HashEntry) PgStat_EntryRef also
      37                 :  * contains a pointer to the shared statistics data, as a process-local
      38                 :  * address, to reduce access costs.
      39                 :  *
      40                 :  * The names for structs stored in shared memory are prefixed with
      41                 :  * PgStatShared instead of PgStat. Each stats entry in shared memory is
      42                 :  * protected by a dedicated lwlock.
      43                 :  *
      44                 :  * Most stats updates are first accumulated locally in each process as pending
      45                 :  * entries, then later flushed to shared memory (just after commit, or by
      46                 :  * idle-timeout). This practically eliminates contention on individual stats
      47                 :  * entries. For most kinds of variable-numbered pending stats data is stored
      48                 :  * in PgStat_EntryRef->pending. All entries with pending data are in the
      49                 :  * pgStatPending list. Pending statistics updates are flushed out by
      50                 :  * pgstat_report_stat().
      51                 :  *
      52                 :  * The behavior of different kinds of statistics is determined by the kind's
      53                 :  * entry in pgstat_kind_infos, see PgStat_KindInfo for details.
      54                 :  *
      55                 :  * The consistency of read accesses to statistics can be configured using the
      56                 :  * stats_fetch_consistency GUC (see config.sgml and monitoring.sgml for the
      57                 :  * settings). When using PGSTAT_FETCH_CONSISTENCY_CACHE or
      58                 :  * PGSTAT_FETCH_CONSISTENCY_SNAPSHOT statistics are stored in
      59                 :  * pgStatLocal.snapshot.
      60                 :  *
      61                 :  * To keep things manageable, stats handling is split across several
      62                 :  * files. Infrastructure pieces are in:
      63                 :  * - pgstat.c - this file, to tie it all together
      64                 :  * - pgstat_shmem.c - nearly everything dealing with shared memory, including
      65                 :  *   the maintenance of hashtable entries
      66                 :  * - pgstat_xact.c - transactional integration, including the transactional
      67                 :  *   creation and dropping of stats entries
      68                 :  *
      69                 :  * Each statistics kind is handled in a dedicated file:
      70                 :  * - pgstat_archiver.c
      71                 :  * - pgstat_bgwriter.c
      72                 :  * - pgstat_checkpointer.c
      73                 :  * - pgstat_database.c
      74                 :  * - pgstat_function.c
      75                 :  * - pgstat_io.c
      76                 :  * - pgstat_relation.c
      77                 :  * - pgstat_replslot.c
      78                 :  * - pgstat_slru.c
      79                 :  * - pgstat_subscription.c
      80                 :  * - pgstat_wal.c
      81                 :  *
      82                 :  * Whenever possible infrastructure files should not contain code related to
      83                 :  * specific kinds of stats.
      84                 :  *
      85                 :  *
      86                 :  * Copyright (c) 2001-2023, PostgreSQL Global Development Group
      87                 :  *
      88                 :  * IDENTIFICATION
      89                 :  *    src/backend/utils/activity/pgstat.c
      90                 :  * ----------
      91                 :  */
      92                 : #include "postgres.h"
      93                 : 
      94                 : #include <unistd.h>
      95                 : 
      96                 : #include "access/transam.h"
      97                 : #include "access/xact.h"
      98                 : #include "lib/dshash.h"
      99                 : #include "pgstat.h"
     100                 : #include "port/atomics.h"
     101                 : #include "storage/fd.h"
     102                 : #include "storage/ipc.h"
     103                 : #include "storage/lwlock.h"
     104                 : #include "storage/pg_shmem.h"
     105                 : #include "storage/shmem.h"
     106                 : #include "utils/guc.h"
     107                 : #include "utils/memutils.h"
     108                 : #include "utils/pgstat_internal.h"
     109                 : #include "utils/timestamp.h"
     110                 : 
     111                 : 
     112                 : /* ----------
     113                 :  * Timer definitions.
     114                 :  *
     115                 :  * In milliseconds.
     116                 :  * ----------
     117                 :  */
     118                 : 
     119                 : /* minimum interval non-forced stats flushes.*/
     120                 : #define PGSTAT_MIN_INTERVAL         1000
     121                 : /* how long until to block flushing pending stats updates */
     122                 : #define PGSTAT_MAX_INTERVAL         60000
     123                 : /* when to call pgstat_report_stat() again, even when idle */
     124                 : #define PGSTAT_IDLE_INTERVAL        10000
     125                 : 
     126                 : /* ----------
     127                 :  * Initial size hints for the hash tables used in statistics.
     128                 :  * ----------
     129                 :  */
     130                 : 
     131                 : #define PGSTAT_SNAPSHOT_HASH_SIZE   512
     132                 : 
     133                 : 
     134                 : /* hash table for statistics snapshots entry */
     135                 : typedef struct PgStat_SnapshotEntry
     136                 : {
     137                 :     PgStat_HashKey key;
     138                 :     char        status;         /* for simplehash use */
     139                 :     void       *data;           /* the stats data itself */
     140                 : } PgStat_SnapshotEntry;
     141                 : 
     142                 : 
     143                 : /* ----------
     144                 :  * Backend-local Hash Table Definitions
     145                 :  * ----------
     146                 :  */
     147                 : 
     148                 : /* for stats snapshot entries */
     149                 : #define SH_PREFIX pgstat_snapshot
     150                 : #define SH_ELEMENT_TYPE PgStat_SnapshotEntry
     151                 : #define SH_KEY_TYPE PgStat_HashKey
     152                 : #define SH_KEY key
     153                 : #define SH_HASH_KEY(tb, key) \
     154                 :     pgstat_hash_hash_key(&key, sizeof(PgStat_HashKey), NULL)
     155                 : #define SH_EQUAL(tb, a, b) \
     156                 :     pgstat_cmp_hash_key(&a, &b, sizeof(PgStat_HashKey), NULL) == 0
     157                 : #define SH_SCOPE static inline
     158                 : #define SH_DEFINE
     159                 : #define SH_DECLARE
     160                 : #include "lib/simplehash.h"
     161                 : 
     162                 : 
     163                 : /* ----------
     164                 :  * Local function forward declarations
     165                 :  * ----------
     166                 :  */
     167                 : 
     168                 : static void pgstat_write_statsfile(void);
     169                 : static void pgstat_read_statsfile(void);
     170                 : 
     171                 : static void pgstat_reset_after_failure(void);
     172                 : 
     173                 : static bool pgstat_flush_pending_entries(bool nowait);
     174                 : 
     175                 : static void pgstat_prep_snapshot(void);
     176                 : static void pgstat_build_snapshot(void);
     177                 : static void pgstat_build_snapshot_fixed(PgStat_Kind kind);
     178                 : 
     179                 : static inline bool pgstat_is_kind_valid(int ikind);
     180                 : 
     181                 : 
     182                 : /* ----------
     183                 :  * GUC parameters
     184                 :  * ----------
     185                 :  */
     186                 : 
     187                 : bool        pgstat_track_counts = false;
     188                 : int         pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
     189                 : 
     190                 : 
     191                 : /* ----------
     192                 :  * state shared with pgstat_*.c
     193                 :  * ----------
     194                 :  */
     195                 : 
     196                 : PgStat_LocalState pgStatLocal;
     197                 : 
     198                 : 
     199                 : /* ----------
     200                 :  * Local data
     201                 :  *
     202                 :  * NB: There should be only variables related to stats infrastructure here,
     203                 :  * not for specific kinds of stats.
     204                 :  * ----------
     205                 :  */
     206                 : 
     207                 : /*
     208                 :  * Memory contexts containing the pgStatEntryRefHash table, the
     209                 :  * pgStatSharedRef entries, and pending data respectively. Mostly to make it
     210                 :  * easier to track / attribute memory usage.
     211                 :  */
     212                 : 
     213                 : static MemoryContext pgStatPendingContext = NULL;
     214                 : 
     215                 : /*
     216                 :  * Backend local list of PgStat_EntryRef with unflushed pending stats.
     217                 :  *
     218                 :  * Newly pending entries should only ever be added to the end of the list,
     219                 :  * otherwise pgstat_flush_pending_entries() might not see them immediately.
     220                 :  */
     221                 : static dlist_head pgStatPending = DLIST_STATIC_INIT(pgStatPending);
     222                 : 
     223                 : 
     224                 : /*
     225                 :  * Force the next stats flush to happen regardless of
     226                 :  * PGSTAT_MIN_INTERVAL. Useful in test scripts.
     227                 :  */
     228                 : static bool pgStatForceNextFlush = false;
     229                 : 
     230                 : /*
     231                 :  * For assertions that check pgstat is not used before initialization / after
     232                 :  * shutdown.
     233                 :  */
     234                 : #ifdef USE_ASSERT_CHECKING
     235                 : static bool pgstat_is_initialized = false;
     236                 : static bool pgstat_is_shutdown = false;
     237                 : #endif
     238                 : 
     239                 : 
     240                 : /*
     241                 :  * The different kinds of statistics.
     242                 :  *
     243                 :  * If reasonably possible, handling specific to one kind of stats should go
     244                 :  * through this abstraction, rather than making more of pgstat.c aware.
     245                 :  *
     246                 :  * See comments for struct PgStat_KindInfo for details about the individual
     247                 :  * fields.
     248                 :  *
     249                 :  * XXX: It'd be nicer to define this outside of this file. But there doesn't
     250                 :  * seem to be a great way of doing that, given the split across multiple
     251                 :  * files.
     252                 :  */
     253                 : static const PgStat_KindInfo pgstat_kind_infos[PGSTAT_NUM_KINDS] = {
     254                 : 
     255                 :     /* stats kinds for variable-numbered objects */
     256                 : 
     257                 :     [PGSTAT_KIND_DATABASE] = {
     258                 :         .name = "database",
     259                 : 
     260                 :         .fixed_amount = false,
     261                 :         /* so pg_stat_database entries can be seen in all databases */
     262                 :         .accessed_across_databases = true,
     263                 : 
     264                 :         .shared_size = sizeof(PgStatShared_Database),
     265                 :         .shared_data_off = offsetof(PgStatShared_Database, stats),
     266                 :         .shared_data_len = sizeof(((PgStatShared_Database *) 0)->stats),
     267                 :         .pending_size = sizeof(PgStat_StatDBEntry),
     268                 : 
     269                 :         .flush_pending_cb = pgstat_database_flush_cb,
     270                 :         .reset_timestamp_cb = pgstat_database_reset_timestamp_cb,
     271                 :     },
     272                 : 
     273                 :     [PGSTAT_KIND_RELATION] = {
     274                 :         .name = "relation",
     275                 : 
     276                 :         .fixed_amount = false,
     277                 : 
     278                 :         .shared_size = sizeof(PgStatShared_Relation),
     279                 :         .shared_data_off = offsetof(PgStatShared_Relation, stats),
     280                 :         .shared_data_len = sizeof(((PgStatShared_Relation *) 0)->stats),
     281                 :         .pending_size = sizeof(PgStat_TableStatus),
     282                 : 
     283                 :         .flush_pending_cb = pgstat_relation_flush_cb,
     284                 :         .delete_pending_cb = pgstat_relation_delete_pending_cb,
     285                 :     },
     286                 : 
     287                 :     [PGSTAT_KIND_FUNCTION] = {
     288                 :         .name = "function",
     289                 : 
     290                 :         .fixed_amount = false,
     291                 : 
     292                 :         .shared_size = sizeof(PgStatShared_Function),
     293                 :         .shared_data_off = offsetof(PgStatShared_Function, stats),
     294                 :         .shared_data_len = sizeof(((PgStatShared_Function *) 0)->stats),
     295                 :         .pending_size = sizeof(PgStat_FunctionCounts),
     296                 : 
     297                 :         .flush_pending_cb = pgstat_function_flush_cb,
     298                 :     },
     299                 : 
     300                 :     [PGSTAT_KIND_REPLSLOT] = {
     301                 :         .name = "replslot",
     302                 : 
     303                 :         .fixed_amount = false,
     304                 : 
     305                 :         .accessed_across_databases = true,
     306                 :         .named_on_disk = true,
     307                 : 
     308                 :         .shared_size = sizeof(PgStatShared_ReplSlot),
     309                 :         .shared_data_off = offsetof(PgStatShared_ReplSlot, stats),
     310                 :         .shared_data_len = sizeof(((PgStatShared_ReplSlot *) 0)->stats),
     311                 : 
     312                 :         .reset_timestamp_cb = pgstat_replslot_reset_timestamp_cb,
     313                 :         .to_serialized_name = pgstat_replslot_to_serialized_name_cb,
     314                 :         .from_serialized_name = pgstat_replslot_from_serialized_name_cb,
     315                 :     },
     316                 : 
     317                 :     [PGSTAT_KIND_SUBSCRIPTION] = {
     318                 :         .name = "subscription",
     319                 : 
     320                 :         .fixed_amount = false,
     321                 :         /* so pg_stat_subscription_stats entries can be seen in all databases */
     322                 :         .accessed_across_databases = true,
     323                 : 
     324                 :         .shared_size = sizeof(PgStatShared_Subscription),
     325                 :         .shared_data_off = offsetof(PgStatShared_Subscription, stats),
     326                 :         .shared_data_len = sizeof(((PgStatShared_Subscription *) 0)->stats),
     327                 :         .pending_size = sizeof(PgStat_BackendSubEntry),
     328                 : 
     329                 :         .flush_pending_cb = pgstat_subscription_flush_cb,
     330                 :         .reset_timestamp_cb = pgstat_subscription_reset_timestamp_cb,
     331                 :     },
     332                 : 
     333                 : 
     334                 :     /* stats for fixed-numbered (mostly 1) objects */
     335                 : 
     336                 :     [PGSTAT_KIND_ARCHIVER] = {
     337                 :         .name = "archiver",
     338                 : 
     339                 :         .fixed_amount = true,
     340                 : 
     341                 :         .reset_all_cb = pgstat_archiver_reset_all_cb,
     342                 :         .snapshot_cb = pgstat_archiver_snapshot_cb,
     343                 :     },
     344                 : 
     345                 :     [PGSTAT_KIND_BGWRITER] = {
     346                 :         .name = "bgwriter",
     347                 : 
     348                 :         .fixed_amount = true,
     349                 : 
     350                 :         .reset_all_cb = pgstat_bgwriter_reset_all_cb,
     351                 :         .snapshot_cb = pgstat_bgwriter_snapshot_cb,
     352                 :     },
     353                 : 
     354                 :     [PGSTAT_KIND_CHECKPOINTER] = {
     355                 :         .name = "checkpointer",
     356                 : 
     357                 :         .fixed_amount = true,
     358                 : 
     359                 :         .reset_all_cb = pgstat_checkpointer_reset_all_cb,
     360                 :         .snapshot_cb = pgstat_checkpointer_snapshot_cb,
     361                 :     },
     362                 : 
     363                 :     [PGSTAT_KIND_IO] = {
     364                 :         .name = "io",
     365                 : 
     366                 :         .fixed_amount = true,
     367                 : 
     368                 :         .reset_all_cb = pgstat_io_reset_all_cb,
     369                 :         .snapshot_cb = pgstat_io_snapshot_cb,
     370                 :     },
     371                 : 
     372                 :     [PGSTAT_KIND_SLRU] = {
     373                 :         .name = "slru",
     374                 : 
     375                 :         .fixed_amount = true,
     376                 : 
     377                 :         .reset_all_cb = pgstat_slru_reset_all_cb,
     378                 :         .snapshot_cb = pgstat_slru_snapshot_cb,
     379                 :     },
     380                 : 
     381                 :     [PGSTAT_KIND_WAL] = {
     382                 :         .name = "wal",
     383                 : 
     384                 :         .fixed_amount = true,
     385                 : 
     386                 :         .reset_all_cb = pgstat_wal_reset_all_cb,
     387                 :         .snapshot_cb = pgstat_wal_snapshot_cb,
     388                 :     },
     389                 : };
     390                 : 
     391                 : 
     392                 : /* ------------------------------------------------------------
     393                 :  * Functions managing the state of the stats system for all backends.
     394                 :  * ------------------------------------------------------------
     395                 :  */
     396                 : 
     397                 : /*
     398                 :  * Read on-disk stats into memory at server start.
     399                 :  *
     400                 :  * Should only be called by the startup process or in single user mode.
     401                 :  */
     402                 : void
     403 GIC        1045 : pgstat_restore_stats(void)
     404                 : {
     405            1045 :     pgstat_read_statsfile();
     406            1045 : }
     407                 : 
     408                 : /*
     409                 :  * Remove the stats file.  This is currently used only if WAL recovery is
     410                 :  * needed after a crash.
     411                 :  *
     412                 :  * Should only be called by the startup process or in single user mode.
     413 ECB             :  */
     414                 : void
     415 CBC         131 : pgstat_discard_stats(void)
     416 ECB             : {
     417                 :     int         ret;
     418                 : 
     419                 :     /* NB: this needs to be done even in single user mode */
     420                 : 
     421 GIC         131 :     ret = unlink(PGSTAT_STAT_PERMANENT_FILENAME);
     422             131 :     if (ret != 0)
     423                 :     {
     424             130 :         if (errno == ENOENT)
     425 CBC         130 :             elog(DEBUG2,
     426                 :                  "didn't need to unlink permanent stats file \"%s\" - didn't exist",
     427                 :                  PGSTAT_STAT_PERMANENT_FILENAME);
     428                 :         else
     429 UIC           0 :             ereport(LOG,
     430                 :                     (errcode_for_file_access(),
     431 ECB             :                      errmsg("could not unlink permanent statistics file \"%s\": %m",
     432                 :                             PGSTAT_STAT_PERMANENT_FILENAME)));
     433                 :     }
     434                 :     else
     435                 :     {
     436 GIC           1 :         ereport(DEBUG2,
     437                 :                 (errcode_for_file_access(),
     438                 :                  errmsg_internal("unlinked permanent statistics file \"%s\"",
     439                 :                                  PGSTAT_STAT_PERMANENT_FILENAME)));
     440                 :     }
     441                 : 
     442                 :     /*
     443                 :      * Reset stats contents. This will set reset timestamps of fixed-numbered
     444                 :      * stats to the current time (no variable stats exist).
     445                 :      */
     446 CBC         131 :     pgstat_reset_after_failure();
     447 GIC         131 : }
     448                 : 
     449                 : /*
     450                 :  * pgstat_before_server_shutdown() needs to be called by exactly one process
     451                 :  * during regular server shutdowns. Otherwise all stats will be lost.
     452                 :  *
     453                 :  * We currently only write out stats for proc_exit(0). We might want to change
     454                 :  * that at some point... But right now pgstat_discard_stats() would be called
     455                 :  * during the start after a disorderly shutdown, anyway.
     456 ECB             :  */
     457                 : void
     458 GIC         974 : pgstat_before_server_shutdown(int code, Datum arg)
     459                 : {
     460             974 :     Assert(pgStatLocal.shmem != NULL);
     461             974 :     Assert(!pgStatLocal.shmem->is_shutdown);
     462                 : 
     463                 :     /*
     464                 :      * Stats should only be reported after pgstat_initialize() and before
     465                 :      * pgstat_shutdown(). This is a convenient point to catch most violations
     466                 :      * of this rule.
     467                 :      */
     468 CBC         974 :     Assert(pgstat_is_initialized && !pgstat_is_shutdown);
     469                 : 
     470 ECB             :     /* flush out our own pending changes before writing out */
     471 CBC         974 :     pgstat_report_stat(true);
     472                 : 
     473                 :     /*
     474                 :      * Only write out file during normal shutdown. Don't even signal that
     475                 :      * we've shutdown during irregular shutdowns, because the shutdown
     476                 :      * sequence isn't coordinated to ensure this backend shuts down last.
     477                 :      */
     478             974 :     if (code == 0)
     479                 :     {
     480 GIC         969 :         pgStatLocal.shmem->is_shutdown = true;
     481 CBC         969 :         pgstat_write_statsfile();
     482                 :     }
     483 GIC         974 : }
     484                 : 
     485                 : 
     486                 : /* ------------------------------------------------------------
     487                 :  * Backend initialization / shutdown functions
     488 ECB             :  * ------------------------------------------------------------
     489                 :  */
     490                 : 
     491                 : /*
     492                 :  * Shut down a single backend's statistics reporting at process exit.
     493                 :  *
     494                 :  * Flush out any remaining statistics counts.  Without this, operations
     495                 :  * triggered during backend exit (such as temp table deletions) won't be
     496                 :  * counted.
     497                 :  */
     498                 : static void
     499 GIC       13300 : pgstat_shutdown_hook(int code, Datum arg)
     500                 : {
     501           13300 :     Assert(!pgstat_is_shutdown);
     502           13300 :     Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
     503                 : 
     504                 :     /*
     505                 :      * If we got as far as discovering our own database ID, we can flush out
     506                 :      * what we did so far.  Otherwise, we'd be reporting an invalid database
     507                 :      * ID, so forget it.  (This means that accesses to pg_database during
     508                 :      * failed backend starts might never get counted.)
     509 ECB             :      */
     510 GIC       13300 :     if (OidIsValid(MyDatabaseId))
     511 CBC       10456 :         pgstat_report_disconnect(MyDatabaseId);
     512 ECB             : 
     513 GIC       13300 :     pgstat_report_stat(true);
     514                 : 
     515                 :     /* there shouldn't be any pending changes left */
     516           13300 :     Assert(dlist_is_empty(&pgStatPending));
     517           13300 :     dlist_init(&pgStatPending);
     518                 : 
     519           13300 :     pgstat_detach_shmem();
     520 ECB             : 
     521                 : #ifdef USE_ASSERT_CHECKING
     522 GIC       13300 :     pgstat_is_shutdown = true;
     523 ECB             : #endif
     524 GIC       13300 : }
     525                 : 
     526 ECB             : /*
     527                 :  * Initialize pgstats state, and set up our on-proc-exit hook. Called from
     528                 :  * BaseInit().
     529                 :  *
     530                 :  * NOTE: MyDatabaseId isn't set yet; so the shutdown hook has to be careful.
     531                 :  */
     532                 : void
     533 GIC       13300 : pgstat_initialize(void)
     534 ECB             : {
     535 GIC       13300 :     Assert(!pgstat_is_initialized);
     536                 : 
     537           13300 :     pgstat_attach_shmem();
     538                 : 
     539           13300 :     pgstat_init_wal();
     540                 : 
     541                 :     /* Set up a process-exit hook to clean up */
     542           13300 :     before_shmem_exit(pgstat_shutdown_hook, 0);
     543 ECB             : 
     544                 : #ifdef USE_ASSERT_CHECKING
     545 CBC       13300 :     pgstat_is_initialized = true;
     546                 : #endif
     547           13300 : }
     548                 : 
     549 ECB             : 
     550                 : /* ------------------------------------------------------------
     551                 :  * Public functions used by backends follow
     552                 :  * ------------------------------------------------------------
     553                 :  */
     554                 : 
     555                 : /*
     556                 :  * Must be called by processes that performs DML: tcop/postgres.c, logical
     557                 :  * receiver processes, SPI worker, etc. to flush pending statistics updates to
     558                 :  * shared memory.
     559                 :  *
     560                 :  * Unless called with 'force', pending stats updates are flushed happen once
     561                 :  * per PGSTAT_MIN_INTERVAL (1000ms). When not forced, stats flushes do not
     562                 :  * block on lock acquisition, except if stats updates have been pending for
     563                 :  * longer than PGSTAT_MAX_INTERVAL (60000ms).
     564                 :  *
     565                 :  * Whenever pending stats updates remain at the end of pgstat_report_stat() a
     566                 :  * suggested idle timeout is returned. Currently this is always
     567                 :  * PGSTAT_IDLE_INTERVAL (10000ms). Callers can use the returned time to set up
     568                 :  * a timeout after which to call pgstat_report_stat(true), but are not
     569                 :  * required to do so.
     570                 :  *
     571                 :  * Note that this is called only when not within a transaction, so it is fair
     572                 :  * to use transaction stop time as an approximation of current time.
     573                 :  */
     574                 : long
     575 GIC      422106 : pgstat_report_stat(bool force)
     576                 : {
     577                 :     static TimestampTz pending_since = 0;
     578                 :     static TimestampTz last_flush = 0;
     579                 :     bool        partial_flush;
     580                 :     TimestampTz now;
     581                 :     bool        nowait;
     582                 : 
     583          422106 :     pgstat_assert_is_up();
     584          422106 :     Assert(!IsTransactionOrTransactionBlock());
     585 ECB             : 
     586                 :     /* "absorb" the forced flush even if there's nothing to flush */
     587 GIC      422106 :     if (pgStatForceNextFlush)
     588                 :     {
     589             201 :         force = true;
     590             201 :         pgStatForceNextFlush = false;
     591                 :     }
     592                 : 
     593 ECB             :     /* Don't expend a clock check if nothing to do */
     594 CBC      422106 :     if (dlist_is_empty(&pgStatPending) &&
     595 GNC        7269 :         !have_iostats &&
     596 GIC        7154 :         !have_slrustats &&
     597            6346 :         !pgstat_have_pending_wal())
     598 ECB             :     {
     599 GIC        6288 :         Assert(pending_since == 0);
     600 CBC        6288 :         return 0;
     601 ECB             :     }
     602                 : 
     603                 :     /*
     604                 :      * There should never be stats to report once stats are shut down. Can't
     605                 :      * assert that before the checks above, as there is an unconditional
     606                 :      * pgstat_report_stat() call in pgstat_shutdown_hook() - which at least
     607                 :      * the process that ran pgstat_before_server_shutdown() will still call.
     608                 :      */
     609 GIC      415818 :     Assert(!pgStatLocal.shmem->is_shutdown);
     610 ECB             : 
     611 CBC      415818 :     now = GetCurrentTransactionStopTimestamp();
     612                 : 
     613 GIC      415818 :     if (!force)
     614                 :     {
     615          785208 :         if (pending_since > 0 &&
     616          382645 :             TimestampDifferenceExceeds(pending_since, now, PGSTAT_MAX_INTERVAL))
     617                 :         {
     618                 :             /* don't keep pending updates longer than PGSTAT_MAX_INTERVAL */
     619 UIC           0 :             force = true;
     620 ECB             :         }
     621 GIC      402563 :         else if (last_flush > 0 &&
     622 CBC      393687 :                  !TimestampDifferenceExceeds(last_flush, now, PGSTAT_MIN_INTERVAL))
     623                 :         {
     624 ECB             :             /* don't flush too frequently */
     625 GIC      391364 :             if (pending_since == 0)
     626 CBC       10960 :                 pending_since = now;
     627 ECB             : 
     628 GIC      391364 :             return PGSTAT_IDLE_INTERVAL;
     629                 :         }
     630 EUB             :     }
     631                 : 
     632 CBC       24454 :     pgstat_update_dbstats(now);
     633 ECB             : 
     634                 :     /* don't wait for lock acquisition when !force */
     635 GIC       24454 :     nowait = !force;
     636 ECB             : 
     637 CBC       24454 :     partial_flush = false;
     638                 : 
     639 ECB             :     /* flush database / relation / function / ... stats */
     640 GIC       24454 :     partial_flush |= pgstat_flush_pending_entries(nowait);
     641                 : 
     642                 :     /* flush IO stats */
     643 GNC       24454 :     partial_flush |= pgstat_flush_io(nowait);
     644                 : 
     645                 :     /* flush wal stats */
     646 CBC       24454 :     partial_flush |= pgstat_flush_wal(nowait);
     647                 : 
     648                 :     /* flush SLRU stats */
     649           24454 :     partial_flush |= pgstat_slru_flush(nowait);
     650                 : 
     651           24454 :     last_flush = now;
     652                 : 
     653                 :     /*
     654 ECB             :      * If some of the pending stats could not be flushed due to lock
     655                 :      * contention, let the caller know when to retry.
     656                 :      */
     657 CBC       24454 :     if (partial_flush)
     658                 :     {
     659                 :         /* force should have prevented us from getting here */
     660               8 :         Assert(!force);
     661                 : 
     662                 :         /* remember since when stats have been pending */
     663               8 :         if (pending_since == 0)
     664 GIC           8 :             pending_since = now;
     665 ECB             : 
     666 GIC           8 :         return PGSTAT_IDLE_INTERVAL;
     667                 :     }
     668                 : 
     669           24446 :     pending_since = 0;
     670                 : 
     671 CBC       24446 :     return 0;
     672                 : }
     673                 : 
     674 ECB             : /*
     675                 :  * Force locally pending stats to be flushed during the next
     676                 :  * pgstat_report_stat() call. This is useful for writing tests.
     677                 :  */
     678                 : void
     679 GIC         201 : pgstat_force_next_flush(void)
     680 ECB             : {
     681 GIC         201 :     pgStatForceNextFlush = true;
     682             201 : }
     683 ECB             : 
     684                 : /*
     685                 :  * Only for use by pgstat_reset_counters()
     686                 :  */
     687                 : static bool
     688 GIC       10238 : match_db_entries(PgStatShared_HashEntry *entry, Datum match_data)
     689                 : {
     690           10238 :     return entry->key.dboid == DatumGetObjectId(MyDatabaseId);
     691                 : }
     692                 : 
     693 ECB             : /*
     694                 :  * Reset counters for our database.
     695                 :  *
     696                 :  * Permission checking for this function is managed through the normal
     697                 :  * GRANT system.
     698                 :  */
     699                 : void
     700 GIC          13 : pgstat_reset_counters(void)
     701                 : {
     702 CBC          13 :     TimestampTz ts = GetCurrentTimestamp();
     703                 : 
     704              13 :     pgstat_reset_matching_entries(match_db_entries,
     705                 :                                   ObjectIdGetDatum(MyDatabaseId),
     706                 :                                   ts);
     707 GIC          13 : }
     708                 : 
     709                 : /*
     710                 :  * Reset a single variable-numbered entry.
     711                 :  *
     712                 :  * If the stats kind is within a database, also reset the database's
     713                 :  * stat_reset_timestamp.
     714 ECB             :  *
     715                 :  * Permission checking for this function is managed through the normal
     716                 :  * GRANT system.
     717                 :  */
     718                 : void
     719 GIC          16 : pgstat_reset(PgStat_Kind kind, Oid dboid, Oid objoid)
     720                 : {
     721 CBC          16 :     const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
     722 GIC          16 :     TimestampTz ts = GetCurrentTimestamp();
     723                 : 
     724                 :     /* not needed atm, and doesn't make sense with the current signature */
     725              16 :     Assert(!pgstat_get_kind_info(kind)->fixed_amount);
     726                 : 
     727                 :     /* reset the "single counter" */
     728              16 :     pgstat_reset_entry(kind, dboid, objoid, ts);
     729                 : 
     730              16 :     if (!kind_info->accessed_across_databases)
     731               5 :         pgstat_reset_database_timestamp(dboid, ts);
     732              16 : }
     733 ECB             : 
     734                 : /*
     735                 :  * Reset stats for all entries of a kind.
     736                 :  *
     737                 :  * Permission checking for this function is managed through the normal
     738                 :  * GRANT system.
     739                 :  */
     740                 : void
     741 GIC          25 : pgstat_reset_of_kind(PgStat_Kind kind)
     742 ECB             : {
     743 GIC          25 :     const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
     744 CBC          25 :     TimestampTz ts = GetCurrentTimestamp();
     745 ECB             : 
     746 CBC          25 :     if (kind_info->fixed_amount)
     747 GIC          21 :         kind_info->reset_all_cb(ts);
     748                 :     else
     749               4 :         pgstat_reset_entries_of_kind(kind, ts);
     750              25 : }
     751                 : 
     752                 : 
     753                 : /* ------------------------------------------------------------
     754                 :  * Fetching of stats
     755 ECB             :  * ------------------------------------------------------------
     756                 :  */
     757                 : 
     758                 : /*
     759                 :  * Discard any data collected in the current transaction.  Any subsequent
     760                 :  * request will cause new snapshots to be read.
     761                 :  *
     762                 :  * This is also invoked during transaction commit or abort to discard
     763                 :  * the no-longer-wanted snapshot.
     764                 :  */
     765                 : void
     766 GIC      486568 : pgstat_clear_snapshot(void)
     767                 : {
     768          486568 :     pgstat_assert_is_up();
     769                 : 
     770          486568 :     memset(&pgStatLocal.snapshot.fixed_valid, 0,
     771                 :            sizeof(pgStatLocal.snapshot.fixed_valid));
     772          486568 :     pgStatLocal.snapshot.stats = NULL;
     773          486568 :     pgStatLocal.snapshot.mode = PGSTAT_FETCH_CONSISTENCY_NONE;
     774                 : 
     775                 :     /* Release memory, if any was allocated */
     776          486568 :     if (pgStatLocal.snapshot.context)
     777                 :     {
     778             386 :         MemoryContextDelete(pgStatLocal.snapshot.context);
     779                 : 
     780 ECB             :         /* Reset variables */
     781 GIC         386 :         pgStatLocal.snapshot.context = NULL;
     782 ECB             :     }
     783                 : 
     784                 :     /*
     785                 :      * Historically the backend_status.c facilities lived in this file, and
     786                 :      * were reset with the same function. For now keep it that way, and
     787                 :      * forward the reset request.
     788                 :      */
     789 GIC      486568 :     pgstat_clear_backend_activity_snapshot();
     790 CBC      486568 : }
     791                 : 
     792 ECB             : void *
     793 GIC        8782 : pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, Oid objoid)
     794                 : {
     795 ECB             :     PgStat_HashKey key;
     796                 :     PgStat_EntryRef *entry_ref;
     797                 :     void       *stats_data;
     798 GIC        8782 :     const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
     799                 : 
     800                 :     /* should be called from backends */
     801            8782 :     Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
     802 GNC        8782 :     Assert(!kind_info->fixed_amount);
     803 ECB             : 
     804 CBC        8782 :     pgstat_prep_snapshot();
     805                 : 
     806 GIC        8782 :     key.kind = kind;
     807 CBC        8782 :     key.dboid = dboid;
     808 GIC        8782 :     key.objoid = objoid;
     809                 : 
     810                 :     /* if we need to build a full snapshot, do so */
     811            8782 :     if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
     812 CBC         227 :         pgstat_build_snapshot();
     813                 : 
     814                 :     /* if caching is desired, look up in cache */
     815            8782 :     if (pgstat_fetch_consistency > PGSTAT_FETCH_CONSISTENCY_NONE)
     816 ECB             :     {
     817 GIC        4481 :         PgStat_SnapshotEntry *entry = NULL;
     818 ECB             : 
     819 GIC        4481 :         entry = pgstat_snapshot_lookup(pgStatLocal.snapshot.stats, key);
     820 ECB             : 
     821 CBC        4481 :         if (entry)
     822             262 :             return entry->data;
     823                 : 
     824                 :         /*
     825 ECB             :          * If we built a full snapshot and the key is not in
     826                 :          * pgStatLocal.snapshot.stats, there are no matching stats.
     827                 :          */
     828 GIC        4219 :         if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
     829 CBC          11 :             return NULL;
     830                 :     }
     831 ECB             : 
     832 GIC        8509 :     pgStatLocal.snapshot.mode = pgstat_fetch_consistency;
     833 ECB             : 
     834 GIC        8509 :     entry_ref = pgstat_get_entry_ref(kind, dboid, objoid, false, NULL);
     835 ECB             : 
     836 CBC        8509 :     if (entry_ref == NULL || entry_ref->shared_entry->dropped)
     837                 :     {
     838                 :         /* create empty entry when using PGSTAT_FETCH_CONSISTENCY_CACHE */
     839 GIC        2501 :         if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_CACHE)
     840                 :         {
     841             811 :             PgStat_SnapshotEntry *entry = NULL;
     842 ECB             :             bool        found;
     843                 : 
     844 GIC         811 :             entry = pgstat_snapshot_insert(pgStatLocal.snapshot.stats, key, &found);
     845             811 :             Assert(!found);
     846 CBC         811 :             entry->data = NULL;
     847                 :         }
     848            2501 :         return NULL;
     849                 :     }
     850 ECB             : 
     851                 :     /*
     852                 :      * Allocate in caller's context for PGSTAT_FETCH_CONSISTENCY_NONE,
     853                 :      * otherwise we could quickly end up with a fair bit of memory used due to
     854                 :      * repeated accesses.
     855                 :      */
     856 GIC        6008 :     if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE)
     857            2611 :         stats_data = palloc(kind_info->shared_data_len);
     858 ECB             :     else
     859 CBC        3397 :         stats_data = MemoryContextAlloc(pgStatLocal.snapshot.context,
     860            3397 :                                         kind_info->shared_data_len);
     861                 : 
     862            6008 :     pgstat_lock_entry_shared(entry_ref, false);
     863 GIC       12016 :     memcpy(stats_data,
     864            6008 :            pgstat_get_entry_data(kind, entry_ref->shared_stats),
     865            6008 :            kind_info->shared_data_len);
     866            6008 :     pgstat_unlock_entry(entry_ref);
     867                 : 
     868            6008 :     if (pgstat_fetch_consistency > PGSTAT_FETCH_CONSISTENCY_NONE)
     869                 :     {
     870 CBC        3397 :         PgStat_SnapshotEntry *entry = NULL;
     871 ECB             :         bool        found;
     872                 : 
     873 CBC        3397 :         entry = pgstat_snapshot_insert(pgStatLocal.snapshot.stats, key, &found);
     874            3397 :         entry->data = stats_data;
     875                 :     }
     876 ECB             : 
     877 CBC        6008 :     return stats_data;
     878 ECB             : }
     879                 : 
     880                 : /*
     881                 :  * If a stats snapshot has been taken, return the timestamp at which that was
     882                 :  * done, and set *have_snapshot to true. Otherwise *have_snapshot is set to
     883                 :  * false.
     884                 :  */
     885                 : TimestampTz
     886 GIC          15 : pgstat_get_stat_snapshot_timestamp(bool *have_snapshot)
     887 ECB             : {
     888 CBC          15 :     if (pgStatLocal.snapshot.mode == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
     889                 :     {
     890 GIC           9 :         *have_snapshot = true;
     891 CBC           9 :         return pgStatLocal.snapshot.snapshot_timestamp;
     892                 :     }
     893                 : 
     894 GIC           6 :     *have_snapshot = false;
     895                 : 
     896               6 :     return 0;
     897                 : }
     898                 : 
     899                 : bool
     900 CBC          79 : pgstat_have_entry(PgStat_Kind kind, Oid dboid, Oid objoid)
     901                 : {
     902 ECB             :     /* fixed-numbered stats always exist */
     903 GIC          79 :     if (pgstat_get_kind_info(kind)->fixed_amount)
     904 CBC           6 :         return true;
     905 ECB             : 
     906 GIC          73 :     return pgstat_get_entry_ref(kind, dboid, objoid, false, NULL) != NULL;
     907                 : }
     908 ECB             : 
     909                 : /*
     910                 :  * Ensure snapshot for fixed-numbered 'kind' exists.
     911                 :  *
     912                 :  * Typically used by the pgstat_fetch_* functions for a kind of stats, before
     913                 :  * massaging the data into the desired format.
     914                 :  */
     915                 : void
     916 GIC         205 : pgstat_snapshot_fixed(PgStat_Kind kind)
     917 ECB             : {
     918 GNC         205 :     Assert(pgstat_is_kind_valid(kind));
     919             205 :     Assert(pgstat_get_kind_info(kind)->fixed_amount);
     920 ECB             : 
     921 GIC         205 :     if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
     922              12 :         pgstat_build_snapshot();
     923                 :     else
     924             193 :         pgstat_build_snapshot_fixed(kind);
     925                 : 
     926             205 :     Assert(pgStatLocal.snapshot.fixed_valid[kind]);
     927             205 : }
     928                 : 
     929                 : static void
     930 CBC        8805 : pgstat_prep_snapshot(void)
     931                 : {
     932            8805 :     if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE ||
     933            4504 :         pgStatLocal.snapshot.stats != NULL)
     934 GIC        8419 :         return;
     935 ECB             : 
     936 CBC         386 :     if (!pgStatLocal.snapshot.context)
     937 GIC         386 :         pgStatLocal.snapshot.context = AllocSetContextCreate(TopMemoryContext,
     938 ECB             :                                                              "PgStat Snapshot",
     939                 :                                                              ALLOCSET_SMALL_SIZES);
     940                 : 
     941 CBC         386 :     pgStatLocal.snapshot.stats =
     942 GIC         386 :         pgstat_snapshot_create(pgStatLocal.snapshot.context,
     943                 :                                PGSTAT_SNAPSHOT_HASH_SIZE,
     944 ECB             :                                NULL);
     945                 : }
     946                 : 
     947                 : static void
     948 CBC         239 : pgstat_build_snapshot(void)
     949                 : {
     950 ECB             :     dshash_seq_status hstat;
     951                 :     PgStatShared_HashEntry *p;
     952                 : 
     953                 :     /* should only be called when we need a snapshot */
     954 GIC         239 :     Assert(pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT);
     955 ECB             : 
     956                 :     /* snapshot already built */
     957 GIC         239 :     if (pgStatLocal.snapshot.mode == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
     958             216 :         return;
     959                 : 
     960              23 :     pgstat_prep_snapshot();
     961                 : 
     962 CBC          23 :     Assert(pgStatLocal.snapshot.stats->members == 0);
     963                 : 
     964 GIC          23 :     pgStatLocal.snapshot.snapshot_timestamp = GetCurrentTimestamp();
     965                 : 
     966                 :     /*
     967                 :      * Snapshot all variable stats.
     968 ECB             :      */
     969 GIC          23 :     dshash_seq_init(&hstat, pgStatLocal.shared_hash, false);
     970           18669 :     while ((p = dshash_seq_next(&hstat)) != NULL)
     971 ECB             :     {
     972 CBC       18646 :         PgStat_Kind kind = p->key.kind;
     973 GIC       18646 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
     974 ECB             :         bool        found;
     975                 :         PgStat_SnapshotEntry *entry;
     976                 :         PgStatShared_Common *stats_data;
     977                 : 
     978                 :         /*
     979                 :          * Check if the stats object should be included in the snapshot.
     980                 :          * Unless the stats kind can be accessed from all databases (e.g.,
     981                 :          * database stats themselves), we only include stats for the current
     982                 :          * database or objects not associated with a database (e.g. shared
     983                 :          * relations).
     984                 :          */
     985 GIC       18646 :         if (p->key.dboid != MyDatabaseId &&
     986 CBC        6832 :             p->key.dboid != InvalidOid &&
     987            5560 :             !kind_info->accessed_across_databases)
     988 GIC        5616 :             continue;
     989                 : 
     990           13132 :         if (p->dropped)
     991             102 :             continue;
     992                 : 
     993           13030 :         Assert(pg_atomic_read_u32(&p->refcount) > 0);
     994                 : 
     995           13030 :         stats_data = dsa_get_address(pgStatLocal.dsa, p->body);
     996           13030 :         Assert(stats_data);
     997                 : 
     998           13030 :         entry = pgstat_snapshot_insert(pgStatLocal.snapshot.stats, p->key, &found);
     999 CBC       13030 :         Assert(!found);
    1000 ECB             : 
    1001 CBC       26060 :         entry->data = MemoryContextAlloc(pgStatLocal.snapshot.context,
    1002           13030 :                                          kind_info->shared_size);
    1003                 : 
    1004                 :         /*
    1005 ECB             :          * Acquire the LWLock directly instead of using
    1006                 :          * pg_stat_lock_entry_shared() which requires a reference.
    1007                 :          */
    1008 CBC       13030 :         LWLockAcquire(&stats_data->lock, LW_SHARED);
    1009 GIC       26060 :         memcpy(entry->data,
    1010 CBC       13030 :                pgstat_get_entry_data(kind, stats_data),
    1011           13030 :                kind_info->shared_size);
    1012 GIC       13030 :         LWLockRelease(&stats_data->lock);
    1013 ECB             :     }
    1014 CBC          23 :     dshash_seq_term(&hstat);
    1015                 : 
    1016 ECB             :     /*
    1017                 :      * Build snapshot of all fixed-numbered stats.
    1018                 :      */
    1019 GIC         276 :     for (int kind = PGSTAT_KIND_FIRST_VALID; kind <= PGSTAT_KIND_LAST; kind++)
    1020                 :     {
    1021             253 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    1022                 : 
    1023 CBC         253 :         if (!kind_info->fixed_amount)
    1024 ECB             :         {
    1025 CBC         115 :             Assert(kind_info->snapshot_cb == NULL);
    1026             115 :             continue;
    1027 ECB             :         }
    1028                 : 
    1029 CBC         138 :         pgstat_build_snapshot_fixed(kind);
    1030                 :     }
    1031                 : 
    1032 GIC          23 :     pgStatLocal.snapshot.mode = PGSTAT_FETCH_CONSISTENCY_SNAPSHOT;
    1033                 : }
    1034 ECB             : 
    1035                 : static void
    1036 CBC        6145 : pgstat_build_snapshot_fixed(PgStat_Kind kind)
    1037                 : {
    1038            6145 :     const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    1039                 : 
    1040            6145 :     Assert(kind_info->fixed_amount);
    1041            6145 :     Assert(kind_info->snapshot_cb != NULL);
    1042                 : 
    1043 GIC        6145 :     if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE)
    1044 ECB             :     {
    1045                 :         /* rebuild every time */
    1046 GIC        5829 :         pgStatLocal.snapshot.fixed_valid[kind] = false;
    1047 ECB             :     }
    1048 GIC         316 :     else if (pgStatLocal.snapshot.fixed_valid[kind])
    1049                 :     {
    1050                 :         /* in snapshot mode we shouldn't get called again */
    1051 CBC           6 :         Assert(pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_CACHE);
    1052 GIC           6 :         return;
    1053 ECB             :     }
    1054                 : 
    1055 CBC        6139 :     Assert(!pgStatLocal.snapshot.fixed_valid[kind]);
    1056 ECB             : 
    1057 GIC        6139 :     kind_info->snapshot_cb();
    1058 ECB             : 
    1059 GIC        6139 :     Assert(!pgStatLocal.snapshot.fixed_valid[kind]);
    1060            6139 :     pgStatLocal.snapshot.fixed_valid[kind] = true;
    1061 ECB             : }
    1062                 : 
    1063                 : 
    1064                 : /* ------------------------------------------------------------
    1065                 :  * Backend-local pending stats infrastructure
    1066                 :  * ------------------------------------------------------------
    1067                 :  */
    1068                 : 
    1069                 : /*
    1070                 :  * Returns the appropriate PgStat_EntryRef, preparing it to receive pending
    1071                 :  * stats if not already done.
    1072                 :  *
    1073                 :  * If created_entry is non-NULL, it'll be set to true if the entry is newly
    1074                 :  * created, false otherwise.
    1075                 :  */
    1076                 : PgStat_EntryRef *
    1077 GIC     1541192 : pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, Oid objoid, bool *created_entry)
    1078                 : {
    1079                 :     PgStat_EntryRef *entry_ref;
    1080                 : 
    1081                 :     /* need to be able to flush out */
    1082         1541192 :     Assert(pgstat_get_kind_info(kind)->flush_pending_cb != NULL);
    1083                 : 
    1084         1541192 :     if (unlikely(!pgStatPendingContext))
    1085                 :     {
    1086           12442 :         pgStatPendingContext =
    1087           12442 :             AllocSetContextCreate(TopMemoryContext,
    1088                 :                                   "PgStat Pending",
    1089                 :                                   ALLOCSET_SMALL_SIZES);
    1090                 :     }
    1091                 : 
    1092 CBC     1541192 :     entry_ref = pgstat_get_entry_ref(kind, dboid, objoid,
    1093                 :                                      true, created_entry);
    1094                 : 
    1095 GIC     1541192 :     if (entry_ref->pending == NULL)
    1096                 :     {
    1097 CBC      787083 :         size_t      entrysize = pgstat_get_kind_info(kind)->pending_size;
    1098                 : 
    1099          787083 :         Assert(entrysize != (size_t) -1);
    1100                 : 
    1101          787083 :         entry_ref->pending = MemoryContextAllocZero(pgStatPendingContext, entrysize);
    1102          787083 :         dlist_push_tail(&pgStatPending, &entry_ref->pending_node);
    1103                 :     }
    1104                 : 
    1105 GIC     1541192 :     return entry_ref;
    1106                 : }
    1107 ECB             : 
    1108                 : /*
    1109                 :  * Return an existing stats entry, or NULL.
    1110                 :  *
    1111                 :  * This should only be used for helper function for pgstatfuncs.c - outside of
    1112                 :  * that it shouldn't be needed.
    1113                 :  */
    1114                 : PgStat_EntryRef *
    1115 GIC          42 : pgstat_fetch_pending_entry(PgStat_Kind kind, Oid dboid, Oid objoid)
    1116 ECB             : {
    1117                 :     PgStat_EntryRef *entry_ref;
    1118                 : 
    1119 GIC          42 :     entry_ref = pgstat_get_entry_ref(kind, dboid, objoid, false, NULL);
    1120 ECB             : 
    1121 GIC          42 :     if (entry_ref == NULL || entry_ref->pending == NULL)
    1122              15 :         return NULL;
    1123                 : 
    1124              27 :     return entry_ref;
    1125                 : }
    1126                 : 
    1127                 : void
    1128          787083 : pgstat_delete_pending_entry(PgStat_EntryRef *entry_ref)
    1129                 : {
    1130 CBC      787083 :     PgStat_Kind kind = entry_ref->shared_entry->key.kind;
    1131 GIC      787083 :     const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    1132          787083 :     void       *pending_data = entry_ref->pending;
    1133                 : 
    1134 CBC      787083 :     Assert(pending_data != NULL);
    1135                 :     /* !fixed_amount stats should be handled explicitly */
    1136          787083 :     Assert(!pgstat_get_kind_info(kind)->fixed_amount);
    1137 ECB             : 
    1138 GIC      787083 :     if (kind_info->delete_pending_cb)
    1139 CBC      745356 :         kind_info->delete_pending_cb(entry_ref);
    1140                 : 
    1141 GIC      787083 :     pfree(pending_data);
    1142          787083 :     entry_ref->pending = NULL;
    1143 ECB             : 
    1144 GIC      787083 :     dlist_delete(&entry_ref->pending_node);
    1145 CBC      787083 : }
    1146 ECB             : 
    1147                 : /*
    1148                 :  * Flush out pending stats for database objects (databases, relations,
    1149                 :  * functions).
    1150                 :  */
    1151                 : static bool
    1152 GIC       24454 : pgstat_flush_pending_entries(bool nowait)
    1153 ECB             : {
    1154 CBC       24454 :     bool        have_pending = false;
    1155 GIC       24454 :     dlist_node *cur = NULL;
    1156 ECB             : 
    1157                 :     /*
    1158                 :      * Need to be a bit careful iterating over the list of pending entries.
    1159                 :      * Processing a pending entry may queue further pending entries to the end
    1160                 :      * of the list that we want to process, so a simple iteration won't do.
    1161                 :      * Further complicating matters is that we want to delete the current
    1162                 :      * entry in each iteration from the list if we flushed successfully.
    1163                 :      *
    1164                 :      * So we just keep track of the next pointer in each loop iteration.
    1165                 :      */
    1166 GIC       24454 :     if (!dlist_is_empty(&pgStatPending))
    1167 CBC       24454 :         cur = dlist_head_node(&pgStatPending);
    1168                 : 
    1169          783818 :     while (cur)
    1170 ECB             :     {
    1171 GIC      759364 :         PgStat_EntryRef *entry_ref =
    1172          759364 :         dlist_container(PgStat_EntryRef, pending_node, cur);
    1173          759364 :         PgStat_HashKey key = entry_ref->shared_entry->key;
    1174          759364 :         PgStat_Kind kind = key.kind;
    1175          759364 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    1176                 :         bool        did_flush;
    1177                 :         dlist_node *next;
    1178                 : 
    1179          759364 :         Assert(!kind_info->fixed_amount);
    1180          759364 :         Assert(kind_info->flush_pending_cb != NULL);
    1181 ECB             : 
    1182                 :         /* flush the stats, if possible */
    1183 GIC      759364 :         did_flush = kind_info->flush_pending_cb(entry_ref, nowait);
    1184 ECB             : 
    1185 GIC      759364 :         Assert(did_flush || nowait);
    1186 ECB             : 
    1187                 :         /* determine next entry, before deleting the pending entry */
    1188 CBC      759364 :         if (dlist_has_next(&pgStatPending, cur))
    1189          734910 :             next = dlist_next_node(&pgStatPending, cur);
    1190 ECB             :         else
    1191 GIC       24454 :             next = NULL;
    1192                 : 
    1193                 :         /* if successfully flushed, remove entry */
    1194 CBC      759364 :         if (did_flush)
    1195          759359 :             pgstat_delete_pending_entry(entry_ref);
    1196                 :         else
    1197 GIC           5 :             have_pending = true;
    1198 ECB             : 
    1199 GIC      759364 :         cur = next;
    1200 ECB             :     }
    1201                 : 
    1202 GIC       24454 :     Assert(dlist_is_empty(&pgStatPending) == !have_pending);
    1203 ECB             : 
    1204 CBC       24454 :     return have_pending;
    1205                 : }
    1206 ECB             : 
    1207                 : 
    1208                 : /* ------------------------------------------------------------
    1209                 :  * Helper / infrastructure functions
    1210                 :  * ------------------------------------------------------------
    1211                 :  */
    1212                 : 
    1213                 : PgStat_Kind
    1214 CBC          82 : pgstat_get_kind_from_str(char *kind_str)
    1215                 : {
    1216 GIC         233 :     for (int kind = PGSTAT_KIND_FIRST_VALID; kind <= PGSTAT_KIND_LAST; kind++)
    1217 ECB             :     {
    1218 GIC         230 :         if (pg_strcasecmp(kind_str, pgstat_kind_infos[kind].name) == 0)
    1219 CBC          79 :             return kind;
    1220                 :     }
    1221                 : 
    1222 GIC           3 :     ereport(ERROR,
    1223                 :             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1224                 :              errmsg("invalid statistics kind: \"%s\"", kind_str)));
    1225                 :     return PGSTAT_KIND_DATABASE;    /* avoid compiler warnings */
    1226                 : }
    1227                 : 
    1228                 : static inline bool
    1229 CBC     6176704 : pgstat_is_kind_valid(int ikind)
    1230                 : {
    1231         6176704 :     return ikind >= PGSTAT_KIND_FIRST_VALID && ikind <= PGSTAT_KIND_LAST;
    1232                 : }
    1233 ECB             : 
    1234                 : const PgStat_KindInfo *
    1235 GIC     6022445 : pgstat_get_kind_info(PgStat_Kind kind)
    1236                 : {
    1237 GNC     6022445 :     Assert(pgstat_is_kind_valid(kind));
    1238                 : 
    1239 GIC     6022445 :     return &pgstat_kind_infos[kind];
    1240                 : }
    1241                 : 
    1242                 : /*
    1243                 :  * Stats should only be reported after pgstat_initialize() and before
    1244 ECB             :  * pgstat_shutdown(). This check is put in a few central places to catch
    1245                 :  * violations of this rule more easily.
    1246                 :  */
    1247                 : #ifdef USE_ASSERT_CHECKING
    1248                 : void
    1249 GIC     4873592 : pgstat_assert_is_up(void)
    1250 ECB             : {
    1251 GIC     4873592 :     Assert(pgstat_is_initialized && !pgstat_is_shutdown);
    1252 CBC     4873592 : }
    1253                 : #endif
    1254 ECB             : 
    1255                 : 
    1256                 : /* ------------------------------------------------------------
    1257                 :  * reading and writing of on-disk stats file
    1258                 :  * ------------------------------------------------------------
    1259                 :  */
    1260                 : 
    1261                 : /* helpers for pgstat_write_statsfile() */
    1262                 : static void
    1263 GIC      453092 : write_chunk(FILE *fpout, void *ptr, size_t len)
    1264 ECB             : {
    1265                 :     int         rc;
    1266                 : 
    1267 CBC      453092 :     rc = fwrite(ptr, len, 1, fpout);
    1268                 : 
    1269                 :     /* we'll check for errors with ferror once at the end */
    1270                 :     (void) rc;
    1271 GIC      453092 : }
    1272                 : 
    1273                 : #define write_chunk_s(fpout, ptr) write_chunk(fpout, ptr, sizeof(*ptr))
    1274                 : 
    1275                 : /*
    1276                 :  * This function is called in the last process that is accessing the shared
    1277                 :  * stats so locking is not required.
    1278 ECB             :  */
    1279                 : static void
    1280 GIC         969 : pgstat_write_statsfile(void)
    1281                 : {
    1282 ECB             :     FILE       *fpout;
    1283                 :     int32       format_id;
    1284 GIC         969 :     const char *tmpfile = PGSTAT_STAT_PERMANENT_TMPFILE;
    1285             969 :     const char *statfile = PGSTAT_STAT_PERMANENT_FILENAME;
    1286 ECB             :     dshash_seq_status hstat;
    1287                 :     PgStatShared_HashEntry *ps;
    1288                 : 
    1289 GIC         969 :     pgstat_assert_is_up();
    1290                 : 
    1291                 :     /* we're shutting down, so it's ok to just override this */
    1292             969 :     pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_NONE;
    1293                 : 
    1294             969 :     elog(DEBUG2, "writing stats file \"%s\"", statfile);
    1295 ECB             : 
    1296                 :     /*
    1297                 :      * Open the statistics temp file to write out the current values.
    1298                 :      */
    1299 CBC         969 :     fpout = AllocateFile(tmpfile, PG_BINARY_W);
    1300             969 :     if (fpout == NULL)
    1301                 :     {
    1302 UIC           0 :         ereport(LOG,
    1303                 :                 (errcode_for_file_access(),
    1304 ECB             :                  errmsg("could not open temporary statistics file \"%s\": %m",
    1305                 :                         tmpfile)));
    1306 UIC           0 :         return;
    1307 ECB             :     }
    1308                 : 
    1309                 :     /*
    1310                 :      * Write the file header --- currently just a format ID.
    1311                 :      */
    1312 GIC         969 :     format_id = PGSTAT_FILE_FORMAT_ID;
    1313             969 :     write_chunk_s(fpout, &format_id);
    1314 ECB             : 
    1315                 :     /*
    1316                 :      * XXX: The following could now be generalized to just iterate over
    1317 EUB             :      * pgstat_kind_infos instead of knowing about the different kinds of
    1318                 :      * stats.
    1319                 :      */
    1320                 : 
    1321                 :     /*
    1322                 :      * Write archiver stats struct
    1323                 :      */
    1324 GIC         969 :     pgstat_build_snapshot_fixed(PGSTAT_KIND_ARCHIVER);
    1325             969 :     write_chunk_s(fpout, &pgStatLocal.snapshot.archiver);
    1326                 : 
    1327 ECB             :     /*
    1328                 :      * Write bgwriter stats struct
    1329                 :      */
    1330 GIC         969 :     pgstat_build_snapshot_fixed(PGSTAT_KIND_BGWRITER);
    1331             969 :     write_chunk_s(fpout, &pgStatLocal.snapshot.bgwriter);
    1332                 : 
    1333                 :     /*
    1334                 :      * Write checkpointer stats struct
    1335                 :      */
    1336             969 :     pgstat_build_snapshot_fixed(PGSTAT_KIND_CHECKPOINTER);
    1337             969 :     write_chunk_s(fpout, &pgStatLocal.snapshot.checkpointer);
    1338                 : 
    1339                 :     /*
    1340                 :      * Write IO stats struct
    1341                 :      */
    1342 GNC         969 :     pgstat_build_snapshot_fixed(PGSTAT_KIND_IO);
    1343             969 :     write_chunk_s(fpout, &pgStatLocal.snapshot.io);
    1344                 : 
    1345 ECB             :     /*
    1346                 :      * Write SLRU stats struct
    1347                 :      */
    1348 GIC         969 :     pgstat_build_snapshot_fixed(PGSTAT_KIND_SLRU);
    1349             969 :     write_chunk_s(fpout, &pgStatLocal.snapshot.slru);
    1350                 : 
    1351 ECB             :     /*
    1352                 :      * Write WAL stats struct
    1353                 :      */
    1354 GIC         969 :     pgstat_build_snapshot_fixed(PGSTAT_KIND_WAL);
    1355             969 :     write_chunk_s(fpout, &pgStatLocal.snapshot.wal);
    1356                 : 
    1357 ECB             :     /*
    1358                 :      * Walk through the stats entries
    1359                 :      */
    1360 GIC         969 :     dshash_seq_init(&hstat, pgStatLocal.shared_hash, false);
    1361          224102 :     while ((ps = dshash_seq_next(&hstat)) != NULL)
    1362                 :     {
    1363 ECB             :         PgStatShared_Common *shstats;
    1364 CBC      223133 :         const PgStat_KindInfo *kind_info = NULL;
    1365                 : 
    1366 GIC      223133 :         CHECK_FOR_INTERRUPTS();
    1367                 : 
    1368                 :         /* we may have some "dropped" entries not yet removed, skip them */
    1369 CBC      223133 :         Assert(!ps->dropped);
    1370          223133 :         if (ps->dropped)
    1371 UIC           0 :             continue;
    1372                 : 
    1373 GIC      223133 :         shstats = (PgStatShared_Common *) dsa_get_address(pgStatLocal.dsa, ps->body);
    1374                 : 
    1375 CBC      223133 :         kind_info = pgstat_get_kind_info(ps->key.kind);
    1376 ECB             : 
    1377                 :         /* if not dropped the valid-entry refcount should exist */
    1378 GIC      223133 :         Assert(pg_atomic_read_u32(&ps->refcount) > 0);
    1379                 : 
    1380          223133 :         if (!kind_info->to_serialized_name)
    1381 ECB             :         {
    1382                 :             /* normal stats entry, identified by PgStat_HashKey */
    1383 GIC      223090 :             fputc('S', fpout);
    1384          223090 :             write_chunk_s(fpout, &ps->key);
    1385 ECB             :         }
    1386                 :         else
    1387                 :         {
    1388                 :             /* stats entry identified by name on disk (e.g. slots) */
    1389                 :             NameData    name;
    1390                 : 
    1391 CBC          43 :             kind_info->to_serialized_name(&ps->key, shstats, &name);
    1392 EUB             : 
    1393 GIC          43 :             fputc('N', fpout);
    1394 CBC          43 :             write_chunk_s(fpout, &ps->key.kind);
    1395 GIC          43 :             write_chunk_s(fpout, &name);
    1396 ECB             :         }
    1397                 : 
    1398                 :         /* Write except the header part of the entry */
    1399 CBC      223133 :         write_chunk(fpout,
    1400                 :                     pgstat_get_entry_data(ps->key.kind, shstats),
    1401 ECB             :                     pgstat_get_entry_len(ps->key.kind));
    1402                 :     }
    1403 GIC         969 :     dshash_seq_term(&hstat);
    1404 ECB             : 
    1405                 :     /*
    1406                 :      * No more output to be done. Close the temp file and replace the old
    1407                 :      * pgstat.stat with it.  The ferror() check replaces testing for error
    1408                 :      * after each individual fputc or fwrite (in write_chunk()) above.
    1409                 :      */
    1410 GIC         969 :     fputc('E', fpout);
    1411                 : 
    1412 CBC         969 :     if (ferror(fpout))
    1413                 :     {
    1414 LBC           0 :         ereport(LOG,
    1415 ECB             :                 (errcode_for_file_access(),
    1416                 :                  errmsg("could not write temporary statistics file \"%s\": %m",
    1417                 :                         tmpfile)));
    1418 UIC           0 :         FreeFile(fpout);
    1419               0 :         unlink(tmpfile);
    1420 ECB             :     }
    1421 GIC         969 :     else if (FreeFile(fpout) < 0)
    1422                 :     {
    1423 UIC           0 :         ereport(LOG,
    1424 ECB             :                 (errcode_for_file_access(),
    1425                 :                  errmsg("could not close temporary statistics file \"%s\": %m",
    1426                 :                         tmpfile)));
    1427 UIC           0 :         unlink(tmpfile);
    1428                 :     }
    1429 GIC         969 :     else if (rename(tmpfile, statfile) < 0)
    1430                 :     {
    1431 LBC           0 :         ereport(LOG,
    1432                 :                 (errcode_for_file_access(),
    1433 ECB             :                  errmsg("could not rename temporary statistics file \"%s\" to \"%s\": %m",
    1434                 :                         tmpfile, statfile)));
    1435 UBC           0 :         unlink(tmpfile);
    1436                 :     }
    1437                 : }
    1438                 : 
    1439 EUB             : /* helpers for pgstat_read_statsfile() */
    1440                 : static bool
    1441 GIC      313298 : read_chunk(FILE *fpin, void *ptr, size_t len)
    1442 ECB             : {
    1443 GIC      313298 :     return fread(ptr, 1, len, fpin) == len;
    1444 EUB             : }
    1445                 : 
    1446                 : #define read_chunk_s(fpin, ptr) read_chunk(fpin, ptr, sizeof(*ptr))
    1447                 : 
    1448                 : /*
    1449                 :  * Reads in existing statistics file into the shared stats hash.
    1450 ECB             :  *
    1451                 :  * This function is called in the only process that is accessing the shared
    1452 EUB             :  * stats so locking is not required.
    1453                 :  */
    1454                 : static void
    1455 GIC        1045 : pgstat_read_statsfile(void)
    1456 EUB             : {
    1457                 :     FILE       *fpin;
    1458                 :     int32       format_id;
    1459                 :     bool        found;
    1460 GIC        1045 :     const char *statfile = PGSTAT_STAT_PERMANENT_FILENAME;
    1461            1045 :     PgStat_ShmemControl *shmem = pgStatLocal.shmem;
    1462 ECB             : 
    1463                 :     /* shouldn't be called from postmaster */
    1464 CBC        1045 :     Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
    1465                 : 
    1466 GIC        1045 :     elog(DEBUG2, "reading stats file \"%s\"", statfile);
    1467                 : 
    1468                 :     /*
    1469                 :      * Try to open the stats file. If it doesn't exist, the backends simply
    1470                 :      * returns zero for anything and statistics simply starts from scratch
    1471                 :      * with empty counters.
    1472                 :      *
    1473                 :      * ENOENT is a possibility if stats collection was previously disabled or
    1474                 :      * has not yet written the stats file for the first time.  Any other
    1475                 :      * failure condition is suspicious.
    1476 ECB             :      */
    1477 GIC        1045 :     if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
    1478                 :     {
    1479             305 :         if (errno != ENOENT)
    1480 UIC           0 :             ereport(LOG,
    1481 ECB             :                     (errcode_for_file_access(),
    1482                 :                      errmsg("could not open statistics file \"%s\": %m",
    1483                 :                             statfile)));
    1484 GIC         305 :         pgstat_reset_after_failure();
    1485 CBC         305 :         return;
    1486                 :     }
    1487 ECB             : 
    1488                 :     /*
    1489                 :      * Verify it's of the expected format.
    1490                 :      */
    1491 GIC         740 :     if (!read_chunk_s(fpin, &format_id) ||
    1492             740 :         format_id != PGSTAT_FILE_FORMAT_ID)
    1493               1 :         goto error;
    1494                 : 
    1495                 :     /*
    1496                 :      * XXX: The following could now be generalized to just iterate over
    1497                 :      * pgstat_kind_infos instead of knowing about the different kinds of
    1498 ECB             :      * stats.
    1499                 :      */
    1500                 : 
    1501 EUB             :     /*
    1502                 :      * Read archiver stats struct
    1503                 :      */
    1504 GIC         739 :     if (!read_chunk_s(fpin, &shmem->archiver.stats))
    1505 LBC           0 :         goto error;
    1506 ECB             : 
    1507                 :     /*
    1508                 :      * Read bgwriter stats struct
    1509                 :      */
    1510 GIC         739 :     if (!read_chunk_s(fpin, &shmem->bgwriter.stats))
    1511 UIC           0 :         goto error;
    1512 ECB             : 
    1513                 :     /*
    1514                 :      * Read checkpointer stats struct
    1515                 :      */
    1516 GIC         739 :     if (!read_chunk_s(fpin, &shmem->checkpointer.stats))
    1517 UIC           0 :         goto error;
    1518                 : 
    1519                 :     /*
    1520                 :      * Read IO stats struct
    1521                 :      */
    1522 GNC         739 :     if (!read_chunk_s(fpin, &shmem->io.stats))
    1523 UNC           0 :         goto error;
    1524                 : 
    1525                 :     /*
    1526                 :      * Read SLRU stats struct
    1527                 :      */
    1528 GIC         739 :     if (!read_chunk_s(fpin, &shmem->slru.stats))
    1529 UIC           0 :         goto error;
    1530                 : 
    1531 ECB             :     /*
    1532 EUB             :      * Read WAL stats struct
    1533                 :      */
    1534 GIC         739 :     if (!read_chunk_s(fpin, &shmem->wal.stats))
    1535 UIC           0 :         goto error;
    1536                 : 
    1537 ECB             :     /*
    1538 EUB             :      * We found an existing statistics file. Read it and put all the hash
    1539                 :      * table entries into place.
    1540                 :      */
    1541                 :     for (;;)
    1542 GIC      154054 :     {
    1543 CBC      154793 :         int         t = fgetc(fpin);
    1544 EUB             : 
    1545 GIC      154793 :         switch (t)
    1546                 :         {
    1547          154054 :             case 'S':
    1548                 :             case 'N':
    1549 ECB             :                 {
    1550 EUB             :                     PgStat_HashKey key;
    1551                 :                     PgStatShared_HashEntry *p;
    1552                 :                     PgStatShared_Common *header;
    1553                 : 
    1554 GIC      154054 :                     CHECK_FOR_INTERRUPTS();
    1555 ECB             : 
    1556 GBC      154054 :                     if (t == 'S')
    1557                 :                     {
    1558                 :                         /* normal stats entry, identified by PgStat_HashKey */
    1559 GIC      154037 :                         if (!read_chunk_s(fpin, &key))
    1560 UIC           0 :                             goto error;
    1561 ECB             : 
    1562 GBC      154037 :                         if (!pgstat_is_kind_valid(key.kind))
    1563 UIC           0 :                             goto error;
    1564                 :                     }
    1565                 :                     else
    1566                 :                     {
    1567                 :                         /* stats entry identified by name on disk (e.g. slots) */
    1568 GIC          17 :                         const PgStat_KindInfo *kind_info = NULL;
    1569 ECB             :                         PgStat_Kind kind;
    1570                 :                         NameData    name;
    1571                 : 
    1572 CBC          17 :                         if (!read_chunk_s(fpin, &kind))
    1573 UIC           0 :                             goto error;
    1574 CBC          17 :                         if (!read_chunk_s(fpin, &name))
    1575 UIC           0 :                             goto error;
    1576 GIC          17 :                         if (!pgstat_is_kind_valid(kind))
    1577 UIC           0 :                             goto error;
    1578                 : 
    1579 GIC          17 :                         kind_info = pgstat_get_kind_info(kind);
    1580                 : 
    1581 CBC          17 :                         if (!kind_info->from_serialized_name)
    1582 UIC           0 :                             goto error;
    1583 ECB             : 
    1584 GIC          17 :                         if (!kind_info->from_serialized_name(&name, &key))
    1585                 :                         {
    1586 ECB             :                             /* skip over data for entry we don't care about */
    1587 GBC           1 :                             if (fseek(fpin, pgstat_get_entry_len(kind), SEEK_CUR) != 0)
    1588 UIC           0 :                                 goto error;
    1589 ECB             : 
    1590 GBC           1 :                             continue;
    1591                 :                         }
    1592                 : 
    1593 GIC          16 :                         Assert(key.kind == kind);
    1594                 :                     }
    1595 ECB             : 
    1596                 :                     /*
    1597                 :                      * This intentionally doesn't use pgstat_get_entry_ref() -
    1598                 :                      * putting all stats into checkpointer's
    1599                 :                      * pgStatEntryRefHash would be wasted effort and memory.
    1600 EUB             :                      */
    1601 CBC      154053 :                     p = dshash_find_or_insert(pgStatLocal.shared_hash, &key, &found);
    1602 EUB             : 
    1603 ECB             :                     /* don't allow duplicate entries */
    1604 GBC      154053 :                     if (found)
    1605                 :                     {
    1606 LBC           0 :                         dshash_release_lock(pgStatLocal.shared_hash, p);
    1607 UIC           0 :                         elog(WARNING, "found duplicate stats entry %d/%u/%u",
    1608 ECB             :                              key.kind, key.dboid, key.objoid);
    1609 UBC           0 :                         goto error;
    1610                 :                     }
    1611 ECB             : 
    1612 GIC      154053 :                     header = pgstat_init_entry(key.kind, p);
    1613          154053 :                     dshash_release_lock(pgStatLocal.shared_hash, p);
    1614 ECB             : 
    1615 GBC      154053 :                     if (!read_chunk(fpin,
    1616                 :                                     pgstat_get_entry_data(key.kind, header),
    1617 ECB             :                                     pgstat_get_entry_len(key.kind)))
    1618 UIC           0 :                         goto error;
    1619                 : 
    1620 CBC      154053 :                     break;
    1621                 :                 }
    1622 GIC         739 :             case 'E':
    1623                 :                 /* check that 'E' actually signals end of file */
    1624             739 :                 if (fgetc(fpin) != EOF)
    1625               1 :                     goto error;
    1626                 : 
    1627             738 :                 goto done;
    1628 ECB             : 
    1629 UIC           0 :             default:
    1630               0 :                 goto error;
    1631 ECB             :         }
    1632                 :     }
    1633 EUB             : 
    1634 GBC         740 : done:
    1635 GIC         740 :     FreeFile(fpin);
    1636 EUB             : 
    1637 GIC         740 :     elog(DEBUG2, "removing permanent stats file \"%s\"", statfile);
    1638             740 :     unlink(statfile);
    1639 ECB             : 
    1640 CBC         740 :     return;
    1641                 : 
    1642               2 : error:
    1643 GIC           2 :     ereport(LOG,
    1644                 :             (errmsg("corrupted statistics file \"%s\"", statfile)));
    1645 EUB             : 
    1646 GIC           2 :     pgstat_reset_after_failure();
    1647 ECB             : 
    1648 GIC           2 :     goto done;
    1649 ECB             : }
    1650                 : 
    1651                 : /*
    1652                 :  * Helper to reset / drop stats after a crash or after restoring stats from
    1653                 :  * disk failed, potentially after already loading parts.
    1654                 :  */
    1655                 : static void
    1656 GBC         438 : pgstat_reset_after_failure(void)
    1657 EUB             : {
    1658 GIC         438 :     TimestampTz ts = GetCurrentTimestamp();
    1659                 : 
    1660                 :     /* reset fixed-numbered stats */
    1661 CBC        5256 :     for (int kind = PGSTAT_KIND_FIRST_VALID; kind <= PGSTAT_KIND_LAST; kind++)
    1662 ECB             :     {
    1663 GIC        4818 :         const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
    1664 ECB             : 
    1665 CBC        4818 :         if (!kind_info->fixed_amount)
    1666 GIC        2190 :             continue;
    1667 ECB             : 
    1668 GIC        2628 :         kind_info->reset_all_cb(ts);
    1669 ECB             :     }
    1670                 : 
    1671                 :     /* and drop variable-numbered ones */
    1672 GIC         438 :     pgstat_drop_all_entries();
    1673 CBC         438 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a