LCOV - code coverage report
Current view: top level - src/backend/utils/activity - pgstat_backend.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 93.0 % 114 106
Test Date: 2026-03-01 07:15:08 Functions: 100.0 % 12 12
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* -------------------------------------------------------------------------
       2              :  *
       3              :  * pgstat_backend.c
       4              :  *    Implementation of backend statistics.
       5              :  *
       6              :  * This file contains the implementation of backend statistics.  It is kept
       7              :  * separate from pgstat.c to enforce the line between the statistics access /
       8              :  * storage implementation and the details about individual types of
       9              :  * statistics.
      10              :  *
      11              :  * This statistics kind uses a proc number as object ID for the hash table
      12              :  * of pgstats.  Entries are created each time a process is spawned, and are
      13              :  * dropped when the process exits.  These are not written to the pgstats file
      14              :  * on disk.  Pending statistics are managed without direct interactions with
      15              :  * PgStat_EntryRef->pending, relying on PendingBackendStats instead so as it
      16              :  * is possible to report data within critical sections.
      17              :  *
      18              :  * Copyright (c) 2001-2026, PostgreSQL Global Development Group
      19              :  *
      20              :  * IDENTIFICATION
      21              :  *    src/backend/utils/activity/pgstat_backend.c
      22              :  * -------------------------------------------------------------------------
      23              :  */
      24              : 
      25              : #include "postgres.h"
      26              : 
      27              : #include "access/xlog.h"
      28              : #include "executor/instrument.h"
      29              : #include "storage/bufmgr.h"
      30              : #include "storage/proc.h"
      31              : #include "storage/procarray.h"
      32              : #include "utils/memutils.h"
      33              : #include "utils/pgstat_internal.h"
      34              : 
      35              : /*
      36              :  * Backend statistics counts waiting to be flushed out. These counters may be
      37              :  * reported within critical sections so we use static memory in order to avoid
      38              :  * memory allocation.
      39              :  */
      40              : static PgStat_BackendPending PendingBackendStats;
      41              : static bool backend_has_iostats = false;
      42              : 
      43              : /*
      44              :  * WAL usage counters saved from pgWalUsage at the previous call to
      45              :  * pgstat_flush_backend().  This is used to calculate how much WAL usage
      46              :  * happens between pgstat_flush_backend() calls, by subtracting the
      47              :  * previous counters from the current ones.
      48              :  */
      49              : static WalUsage prevBackendWalUsage;
      50              : 
      51              : /*
      52              :  * Utility routines to report I/O stats for backends, kept here to avoid
      53              :  * exposing PendingBackendStats to the outside world.
      54              :  */
      55              : void
      56            2 : pgstat_count_backend_io_op_time(IOObject io_object, IOContext io_context,
      57              :                                 IOOp io_op, instr_time io_time)
      58              : {
      59              :     Assert(track_io_timing || track_wal_io_timing);
      60              : 
      61            2 :     if (!pgstat_tracks_backend_bktype(MyBackendType))
      62            0 :         return;
      63              : 
      64              :     Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
      65              : 
      66            2 :     INSTR_TIME_ADD(PendingBackendStats.pending_io.pending_times[io_object][io_context][io_op],
      67              :                    io_time);
      68              : 
      69            2 :     backend_has_iostats = true;
      70            2 :     pgstat_report_fixed = true;
      71              : }
      72              : 
      73              : void
      74     69483285 : pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
      75              :                            IOOp io_op, uint32 cnt, uint64 bytes)
      76              : {
      77     69483285 :     if (!pgstat_tracks_backend_bktype(MyBackendType))
      78      7424194 :         return;
      79              : 
      80              :     Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
      81              : 
      82     62059091 :     PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
      83     62059091 :     PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
      84              : 
      85     62059091 :     backend_has_iostats = true;
      86     62059091 :     pgstat_report_fixed = true;
      87              : }
      88              : 
      89              : /*
      90              :  * Returns statistics of a backend by proc number.
      91              :  */
      92              : PgStat_Backend *
      93           27 : pgstat_fetch_stat_backend(ProcNumber procNumber)
      94              : {
      95              :     PgStat_Backend *backend_entry;
      96              : 
      97           27 :     backend_entry = (PgStat_Backend *) pgstat_fetch_entry(PGSTAT_KIND_BACKEND,
      98              :                                                           InvalidOid, procNumber);
      99              : 
     100           27 :     return backend_entry;
     101              : }
     102              : 
     103              : /*
     104              :  * Returns statistics of a backend by pid.
     105              :  *
     106              :  * This routine includes sanity checks to ensure that the backend exists and
     107              :  * is running.  "bktype" can be optionally defined to return the BackendType
     108              :  * of the backend whose statistics are returned.
     109              :  */
     110              : PgStat_Backend *
     111           33 : pgstat_fetch_stat_backend_by_pid(int pid, BackendType *bktype)
     112              : {
     113              :     PGPROC     *proc;
     114              :     PgBackendStatus *beentry;
     115              :     ProcNumber  procNumber;
     116              :     PgStat_Backend *backend_stats;
     117              : 
     118           33 :     proc = BackendPidGetProc(pid);
     119           33 :     if (bktype)
     120           27 :         *bktype = B_INVALID;
     121              : 
     122              :     /* this could be an auxiliary process */
     123           33 :     if (!proc)
     124            6 :         proc = AuxiliaryPidGetProc(pid);
     125              : 
     126           33 :     if (!proc)
     127            3 :         return NULL;
     128              : 
     129           30 :     procNumber = GetNumberFromPGProc(proc);
     130              : 
     131           30 :     beentry = pgstat_get_beentry_by_proc_number(procNumber);
     132           30 :     if (!beentry)
     133            0 :         return NULL;
     134              : 
     135              :     /* check if the backend type tracks statistics */
     136           30 :     if (!pgstat_tracks_backend_bktype(beentry->st_backendType))
     137            3 :         return NULL;
     138              : 
     139              :     /* if PID does not match, leave */
     140           27 :     if (beentry->st_procpid != pid)
     141            0 :         return NULL;
     142              : 
     143           27 :     if (bktype)
     144           21 :         *bktype = beentry->st_backendType;
     145              : 
     146              :     /*
     147              :      * Retrieve the entry.  Note that "beentry" may be freed depending on the
     148              :      * value of stats_fetch_consistency, so do not access it from this point.
     149              :      */
     150           27 :     backend_stats = pgstat_fetch_stat_backend(procNumber);
     151           27 :     if (!backend_stats)
     152              :     {
     153            0 :         if (bktype)
     154            0 :             *bktype = B_INVALID;
     155            0 :         return NULL;
     156              :     }
     157              : 
     158           27 :     return backend_stats;
     159              : }
     160              : 
     161              : /*
     162              :  * Flush out locally pending backend IO statistics.  Locking is managed
     163              :  * by the caller.
     164              :  */
     165              : static void
     166       150073 : pgstat_flush_backend_entry_io(PgStat_EntryRef *entry_ref)
     167              : {
     168              :     PgStatShared_Backend *shbackendent;
     169              :     PgStat_BktypeIO *bktype_shstats;
     170              :     PgStat_PendingIO pending_io;
     171              : 
     172              :     /*
     173              :      * This function can be called even if nothing at all has happened for IO
     174              :      * statistics.  In this case, avoid unnecessarily modifying the stats
     175              :      * entry.
     176              :      */
     177       150073 :     if (!backend_has_iostats)
     178          188 :         return;
     179              : 
     180       149885 :     shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
     181       149885 :     bktype_shstats = &shbackendent->stats.io_stats;
     182       149885 :     pending_io = PendingBackendStats.pending_io;
     183              : 
     184       599540 :     for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
     185              :     {
     186      2697930 :         for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
     187              :         {
     188     20234475 :             for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
     189              :             {
     190              :                 instr_time  time;
     191              : 
     192     17986200 :                 bktype_shstats->counts[io_object][io_context][io_op] +=
     193     17986200 :                     pending_io.counts[io_object][io_context][io_op];
     194     17986200 :                 bktype_shstats->bytes[io_object][io_context][io_op] +=
     195     17986200 :                     pending_io.bytes[io_object][io_context][io_op];
     196     17986200 :                 time = pending_io.pending_times[io_object][io_context][io_op];
     197              : 
     198     17986200 :                 bktype_shstats->times[io_object][io_context][io_op] +=
     199     17986200 :                     INSTR_TIME_GET_MICROSEC(time);
     200              :             }
     201              :         }
     202              :     }
     203              : 
     204              :     /*
     205              :      * Clear out the statistics buffer, so it can be re-used.
     206              :      */
     207       149885 :     MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
     208              : 
     209       149885 :     backend_has_iostats = false;
     210              : }
     211              : 
     212              : /*
     213              :  * To determine whether WAL usage happened.
     214              :  */
     215              : static inline bool
     216        70812 : pgstat_backend_wal_have_pending(void)
     217              : {
     218        70812 :     return (pgWalUsage.wal_records != prevBackendWalUsage.wal_records);
     219              : }
     220              : 
     221              : /*
     222              :  * Flush out locally pending backend WAL statistics.  Locking is managed
     223              :  * by the caller.
     224              :  */
     225              : static void
     226        20643 : pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
     227              : {
     228              :     PgStatShared_Backend *shbackendent;
     229              :     PgStat_WalCounters *bktype_shstats;
     230        20643 :     WalUsage    wal_usage_diff = {0};
     231              : 
     232              :     /*
     233              :      * This function can be called even if nothing at all has happened for WAL
     234              :      * statistics.  In this case, avoid unnecessarily modifying the stats
     235              :      * entry.
     236              :      */
     237        20643 :     if (!pgstat_backend_wal_have_pending())
     238        10719 :         return;
     239              : 
     240         9924 :     shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
     241         9924 :     bktype_shstats = &shbackendent->stats.wal_counters;
     242              : 
     243              :     /*
     244              :      * Calculate how much WAL usage counters were increased by subtracting the
     245              :      * previous counters from the current ones.
     246              :      */
     247         9924 :     WalUsageAccumDiff(&wal_usage_diff, &pgWalUsage, &prevBackendWalUsage);
     248              : 
     249              : #define WALSTAT_ACC(fld, var_to_add) \
     250              :     (bktype_shstats->fld += var_to_add.fld)
     251         9924 :     WALSTAT_ACC(wal_buffers_full, wal_usage_diff);
     252         9924 :     WALSTAT_ACC(wal_records, wal_usage_diff);
     253         9924 :     WALSTAT_ACC(wal_fpi, wal_usage_diff);
     254         9924 :     WALSTAT_ACC(wal_bytes, wal_usage_diff);
     255         9924 :     WALSTAT_ACC(wal_fpi_bytes, wal_usage_diff);
     256              : #undef WALSTAT_ACC
     257              : 
     258              :     /*
     259              :      * Save the current counters for the subsequent calculation of WAL usage.
     260              :      */
     261         9924 :     prevBackendWalUsage = pgWalUsage;
     262              : }
     263              : 
     264              : /*
     265              :  * Flush out locally pending backend statistics
     266              :  *
     267              :  * "flags" parameter controls which statistics to flush.  Returns true
     268              :  * if some statistics could not be flushed due to lock contention.
     269              :  */
     270              : bool
     271       231490 : pgstat_flush_backend(bool nowait, bits32 flags)
     272              : {
     273              :     PgStat_EntryRef *entry_ref;
     274       231490 :     bool        has_pending_data = false;
     275              : 
     276       231490 :     if (!pgstat_tracks_backend_bktype(MyBackendType))
     277        41326 :         return false;
     278              : 
     279              :     /* Some IO data pending? */
     280       190164 :     if ((flags & PGSTAT_BACKEND_FLUSH_IO) && backend_has_iostats)
     281       149885 :         has_pending_data = true;
     282              : 
     283              :     /* Some WAL data pending? */
     284       240333 :     if ((flags & PGSTAT_BACKEND_FLUSH_WAL) &&
     285        50169 :         pgstat_backend_wal_have_pending())
     286         9924 :         has_pending_data = true;
     287              : 
     288       190164 :     if (!has_pending_data)
     289        40091 :         return false;
     290              : 
     291       150073 :     entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
     292              :                                             MyProcNumber, nowait);
     293       150073 :     if (!entry_ref)
     294            0 :         return true;
     295              : 
     296              :     /* Flush requested statistics */
     297       150073 :     if (flags & PGSTAT_BACKEND_FLUSH_IO)
     298       150073 :         pgstat_flush_backend_entry_io(entry_ref);
     299              : 
     300       150073 :     if (flags & PGSTAT_BACKEND_FLUSH_WAL)
     301        20643 :         pgstat_flush_backend_entry_wal(entry_ref);
     302              : 
     303       150073 :     pgstat_unlock_entry(entry_ref);
     304              : 
     305       150073 :     return false;
     306              : }
     307              : 
     308              : /*
     309              :  * Callback to flush out locally pending backend statistics.
     310              :  *
     311              :  * If some stats could not be flushed due to lock contention, return true.
     312              :  */
     313              : bool
     314        37280 : pgstat_backend_flush_cb(bool nowait)
     315              : {
     316        37280 :     return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
     317              : }
     318              : 
     319              : /*
     320              :  * Create backend statistics entry for proc number.
     321              :  */
     322              : void
     323        18771 : pgstat_create_backend(ProcNumber procnum)
     324              : {
     325              :     PgStat_EntryRef *entry_ref;
     326              :     PgStatShared_Backend *shstatent;
     327              : 
     328        18771 :     entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
     329              :                                             procnum, false);
     330        18771 :     shstatent = (PgStatShared_Backend *) entry_ref->shared_stats;
     331              : 
     332              :     /*
     333              :      * NB: need to accept that there might be stats from an older backend,
     334              :      * e.g. if we previously used this proc number.
     335              :      */
     336        18771 :     memset(&shstatent->stats, 0, sizeof(shstatent->stats));
     337        18771 :     pgstat_unlock_entry(entry_ref);
     338              : 
     339        18771 :     MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
     340        18771 :     backend_has_iostats = false;
     341              : 
     342              :     /*
     343              :      * Initialize prevBackendWalUsage with pgWalUsage so that
     344              :      * pgstat_backend_flush_cb() can calculate how much pgWalUsage counters
     345              :      * are increased by subtracting prevBackendWalUsage from pgWalUsage.
     346              :      */
     347        18771 :     prevBackendWalUsage = pgWalUsage;
     348        18771 : }
     349              : 
     350              : /*
     351              :  * Backend statistics are not collected for all BackendTypes.
     352              :  *
     353              :  * The following BackendTypes do not participate in the backend stats
     354              :  * subsystem:
     355              :  * - The same and for the same reasons as in pgstat_tracks_io_bktype().
     356              :  * - B_BG_WRITER, B_CHECKPOINTER, B_STARTUP and B_AUTOVAC_LAUNCHER because their
     357              :  * I/O stats are already visible in pg_stat_io and there is only one of those.
     358              :  *
     359              :  * Function returns true if BackendType participates in the backend stats
     360              :  * subsystem and false if it does not.
     361              :  *
     362              :  * When adding a new BackendType, also consider adding relevant restrictions to
     363              :  * pgstat_tracks_io_object() and pgstat_tracks_io_op().
     364              :  */
     365              : bool
     366     69737855 : pgstat_tracks_backend_bktype(BackendType bktype)
     367              : {
     368              :     /*
     369              :      * List every type so that new backend types trigger a warning about
     370              :      * needing to adjust this switch.
     371              :      */
     372     69737855 :     switch (bktype)
     373              :     {
     374      7469797 :         case B_INVALID:
     375              :         case B_AUTOVAC_LAUNCHER:
     376              :         case B_DEAD_END_BACKEND:
     377              :         case B_ARCHIVER:
     378              :         case B_LOGGER:
     379              :         case B_BG_WRITER:
     380              :         case B_CHECKPOINTER:
     381              :         case B_IO_WORKER:
     382              :         case B_STARTUP:
     383      7469797 :             return false;
     384              : 
     385     62268058 :         case B_AUTOVAC_WORKER:
     386              :         case B_BACKEND:
     387              :         case B_BG_WORKER:
     388              :         case B_STANDALONE_BACKEND:
     389              :         case B_SLOTSYNC_WORKER:
     390              :         case B_WAL_RECEIVER:
     391              :         case B_WAL_SENDER:
     392              :         case B_WAL_SUMMARIZER:
     393              :         case B_WAL_WRITER:
     394     62268058 :             return true;
     395              :     }
     396              : 
     397            0 :     return false;
     398              : }
     399              : 
     400              : void
     401            3 : pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
     402              : {
     403            3 :     ((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
     404            3 : }
        

Generated by: LCOV version 2.0-1