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-05-05 12:17:12 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            4 : 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            4 :     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            4 :     INSTR_TIME_ADD(PendingBackendStats.pending_io.pending_times[io_object][io_context][io_op],
      67              :                    io_time);
      68              : 
      69            4 :     backend_has_iostats = true;
      70            4 :     pgstat_report_fixed = true;
      71              : }
      72              : 
      73              : void
      74     92289823 : pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
      75              :                            IOOp io_op, uint32 cnt, uint64 bytes)
      76              : {
      77     92289823 :     if (!pgstat_tracks_backend_bktype(MyBackendType))
      78      7835188 :         return;
      79              : 
      80              :     Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
      81              : 
      82     84454635 :     PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
      83     84454635 :     PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
      84              : 
      85     84454635 :     backend_has_iostats = true;
      86     84454635 :     pgstat_report_fixed = true;
      87              : }
      88              : 
      89              : /*
      90              :  * Returns statistics of a backend by proc number.
      91              :  */
      92              : PgStat_Backend *
      93           36 : pgstat_fetch_stat_backend(ProcNumber procNumber)
      94              : {
      95              :     PgStat_Backend *backend_entry;
      96              : 
      97           36 :     backend_entry = (PgStat_Backend *) pgstat_fetch_entry(PGSTAT_KIND_BACKEND,
      98              :                                                           InvalidOid, procNumber,
      99              :                                                           NULL);
     100              : 
     101           36 :     return backend_entry;
     102              : }
     103              : 
     104              : /*
     105              :  * Returns statistics of a backend by pid.
     106              :  *
     107              :  * This routine includes sanity checks to ensure that the backend exists and
     108              :  * is running.  "bktype" can be optionally defined to return the BackendType
     109              :  * of the backend whose statistics are returned.
     110              :  */
     111              : PgStat_Backend *
     112           44 : pgstat_fetch_stat_backend_by_pid(int pid, BackendType *bktype)
     113              : {
     114              :     PGPROC     *proc;
     115              :     PgBackendStatus *beentry;
     116              :     ProcNumber  procNumber;
     117              :     PgStat_Backend *backend_stats;
     118              : 
     119           44 :     proc = BackendPidGetProc(pid);
     120           44 :     if (bktype)
     121           36 :         *bktype = B_INVALID;
     122              : 
     123              :     /* this could be an auxiliary process */
     124           44 :     if (!proc)
     125            8 :         proc = AuxiliaryPidGetProc(pid);
     126              : 
     127           44 :     if (!proc)
     128            4 :         return NULL;
     129              : 
     130           40 :     procNumber = GetNumberFromPGProc(proc);
     131              : 
     132           40 :     beentry = pgstat_get_beentry_by_proc_number(procNumber);
     133           40 :     if (!beentry)
     134            0 :         return NULL;
     135              : 
     136              :     /* check if the backend type tracks statistics */
     137           40 :     if (!pgstat_tracks_backend_bktype(beentry->st_backendType))
     138            4 :         return NULL;
     139              : 
     140              :     /* if PID does not match, leave */
     141           36 :     if (beentry->st_procpid != pid)
     142            0 :         return NULL;
     143              : 
     144           36 :     if (bktype)
     145           28 :         *bktype = beentry->st_backendType;
     146              : 
     147              :     /*
     148              :      * Retrieve the entry.  Note that "beentry" may be freed depending on the
     149              :      * value of stats_fetch_consistency, so do not access it from this point.
     150              :      */
     151           36 :     backend_stats = pgstat_fetch_stat_backend(procNumber);
     152           36 :     if (!backend_stats)
     153              :     {
     154            0 :         if (bktype)
     155            0 :             *bktype = B_INVALID;
     156            0 :         return NULL;
     157              :     }
     158              : 
     159           36 :     return backend_stats;
     160              : }
     161              : 
     162              : /*
     163              :  * Flush out locally pending backend IO statistics.  Locking is managed
     164              :  * by the caller.
     165              :  */
     166              : static void
     167       166559 : pgstat_flush_backend_entry_io(PgStat_EntryRef *entry_ref)
     168              : {
     169              :     PgStatShared_Backend *shbackendent;
     170              :     PgStat_BktypeIO *bktype_shstats;
     171              :     PgStat_PendingIO pending_io;
     172              : 
     173              :     /*
     174              :      * This function can be called even if nothing at all has happened for IO
     175              :      * statistics.  In this case, avoid unnecessarily modifying the stats
     176              :      * entry.
     177              :      */
     178       166559 :     if (!backend_has_iostats)
     179          183 :         return;
     180              : 
     181       166376 :     shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
     182       166376 :     bktype_shstats = &shbackendent->stats.io_stats;
     183       166376 :     pending_io = PendingBackendStats.pending_io;
     184              : 
     185       665504 :     for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
     186              :     {
     187      2994768 :         for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
     188              :         {
     189     22460760 :             for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
     190              :             {
     191              :                 instr_time  time;
     192              : 
     193     19965120 :                 bktype_shstats->counts[io_object][io_context][io_op] +=
     194     19965120 :                     pending_io.counts[io_object][io_context][io_op];
     195     19965120 :                 bktype_shstats->bytes[io_object][io_context][io_op] +=
     196     19965120 :                     pending_io.bytes[io_object][io_context][io_op];
     197     19965120 :                 time = pending_io.pending_times[io_object][io_context][io_op];
     198              : 
     199     19965120 :                 bktype_shstats->times[io_object][io_context][io_op] +=
     200     19965120 :                     INSTR_TIME_GET_MICROSEC(time);
     201              :             }
     202              :         }
     203              :     }
     204              : 
     205              :     /*
     206              :      * Clear out the statistics buffer, so it can be re-used.
     207              :      */
     208       166376 :     MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
     209              : 
     210       166376 :     backend_has_iostats = false;
     211              : }
     212              : 
     213              : /*
     214              :  * To determine whether WAL usage happened.
     215              :  */
     216              : static inline bool
     217        74290 : pgstat_backend_wal_have_pending(void)
     218              : {
     219        74290 :     return (pgWalUsage.wal_records != prevBackendWalUsage.wal_records);
     220              : }
     221              : 
     222              : /*
     223              :  * Flush out locally pending backend WAL statistics.  Locking is managed
     224              :  * by the caller.
     225              :  */
     226              : static void
     227        22046 : pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
     228              : {
     229              :     PgStatShared_Backend *shbackendent;
     230              :     PgStat_WalCounters *bktype_shstats;
     231        22046 :     WalUsage    wal_usage_diff = {0};
     232              : 
     233              :     /*
     234              :      * This function can be called even if nothing at all has happened for WAL
     235              :      * statistics.  In this case, avoid unnecessarily modifying the stats
     236              :      * entry.
     237              :      */
     238        22046 :     if (!pgstat_backend_wal_have_pending())
     239        11351 :         return;
     240              : 
     241        10695 :     shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
     242        10695 :     bktype_shstats = &shbackendent->stats.wal_counters;
     243              : 
     244              :     /*
     245              :      * Calculate how much WAL usage counters were increased by subtracting the
     246              :      * previous counters from the current ones.
     247              :      */
     248        10695 :     WalUsageAccumDiff(&wal_usage_diff, &pgWalUsage, &prevBackendWalUsage);
     249              : 
     250              : #define WALSTAT_ACC(fld, var_to_add) \
     251              :     (bktype_shstats->fld += var_to_add.fld)
     252        10695 :     WALSTAT_ACC(wal_buffers_full, wal_usage_diff);
     253        10695 :     WALSTAT_ACC(wal_records, wal_usage_diff);
     254        10695 :     WALSTAT_ACC(wal_fpi, wal_usage_diff);
     255        10695 :     WALSTAT_ACC(wal_bytes, wal_usage_diff);
     256        10695 :     WALSTAT_ACC(wal_fpi_bytes, wal_usage_diff);
     257              : #undef WALSTAT_ACC
     258              : 
     259              :     /*
     260              :      * Save the current counters for the subsequent calculation of WAL usage.
     261              :      */
     262        10695 :     prevBackendWalUsage = pgWalUsage;
     263              : }
     264              : 
     265              : /*
     266              :  * Flush out locally pending backend statistics
     267              :  *
     268              :  * "flags" parameter controls which statistics to flush.  Returns true
     269              :  * if some statistics could not be flushed due to lock contention.
     270              :  */
     271              : bool
     272       250380 : pgstat_flush_backend(bool nowait, uint32 flags)
     273              : {
     274              :     PgStat_EntryRef *entry_ref;
     275       250380 :     bool        has_pending_data = false;
     276              : 
     277       250380 :     if (!pgstat_tracks_backend_bktype(MyBackendType))
     278        43080 :         return false;
     279              : 
     280              :     /* Some IO data pending? */
     281       207300 :     if ((flags & PGSTAT_BACKEND_FLUSH_IO) && backend_has_iostats)
     282       166376 :         has_pending_data = true;
     283              : 
     284              :     /* Some WAL data pending? */
     285       259544 :     if ((flags & PGSTAT_BACKEND_FLUSH_WAL) &&
     286        52244 :         pgstat_backend_wal_have_pending())
     287        10695 :         has_pending_data = true;
     288              : 
     289       207300 :     if (!has_pending_data)
     290        40741 :         return false;
     291              : 
     292       166559 :     entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
     293              :                                             MyProcNumber, nowait);
     294       166559 :     if (!entry_ref)
     295            0 :         return true;
     296              : 
     297              :     /* Flush requested statistics */
     298       166559 :     if (flags & PGSTAT_BACKEND_FLUSH_IO)
     299       166559 :         pgstat_flush_backend_entry_io(entry_ref);
     300              : 
     301       166559 :     if (flags & PGSTAT_BACKEND_FLUSH_WAL)
     302        22046 :         pgstat_flush_backend_entry_wal(entry_ref);
     303              : 
     304       166559 :     pgstat_unlock_entry(entry_ref);
     305              : 
     306       166559 :     return false;
     307              : }
     308              : 
     309              : /*
     310              :  * Callback to flush out locally pending backend statistics.
     311              :  *
     312              :  * If some stats could not be flushed due to lock contention, return true.
     313              :  */
     314              : bool
     315        39832 : pgstat_backend_flush_cb(bool nowait)
     316              : {
     317        39832 :     return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
     318              : }
     319              : 
     320              : /*
     321              :  * Create backend statistics entry for proc number.
     322              :  */
     323              : void
     324        20228 : pgstat_create_backend(ProcNumber procnum)
     325              : {
     326              :     PgStat_EntryRef *entry_ref;
     327              :     PgStatShared_Backend *shstatent;
     328              : 
     329        20228 :     entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
     330              :                                             procnum, false);
     331        20228 :     shstatent = (PgStatShared_Backend *) entry_ref->shared_stats;
     332              : 
     333              :     /*
     334              :      * NB: need to accept that there might be stats from an older backend,
     335              :      * e.g. if we previously used this proc number.
     336              :      */
     337        20228 :     memset(&shstatent->stats, 0, sizeof(shstatent->stats));
     338        20228 :     pgstat_unlock_entry(entry_ref);
     339              : 
     340        20228 :     MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
     341        20228 :     backend_has_iostats = false;
     342              : 
     343              :     /*
     344              :      * Initialize prevBackendWalUsage with pgWalUsage so that
     345              :      * pgstat_backend_flush_cb() can calculate how much pgWalUsage counters
     346              :      * are increased by subtracting prevBackendWalUsage from pgWalUsage.
     347              :      */
     348        20228 :     prevBackendWalUsage = pgWalUsage;
     349        20228 : }
     350              : 
     351              : /*
     352              :  * Backend statistics are not collected for all BackendTypes.
     353              :  *
     354              :  * The following BackendTypes do not participate in the backend stats
     355              :  * subsystem:
     356              :  * - The same and for the same reasons as in pgstat_tracks_io_bktype().
     357              :  * - B_BG_WRITER, B_CHECKPOINTER, B_STARTUP and B_AUTOVAC_LAUNCHER because their
     358              :  * I/O stats are already visible in pg_stat_io and there is only one of those.
     359              :  *
     360              :  * Function returns true if BackendType participates in the backend stats
     361              :  * subsystem and false if it does not.
     362              :  *
     363              :  * When adding a new BackendType, also consider adding relevant restrictions to
     364              :  * pgstat_tracks_io_object() and pgstat_tracks_io_op().
     365              :  */
     366              : bool
     367     92564613 : pgstat_tracks_backend_bktype(BackendType bktype)
     368              : {
     369              :     /*
     370              :      * List every type so that new backend types trigger a warning about
     371              :      * needing to adjust this switch.
     372              :      */
     373     92564613 :     switch (bktype)
     374              :     {
     375      7882406 :         case B_INVALID:
     376              :         case B_AUTOVAC_LAUNCHER:
     377              :         case B_DEAD_END_BACKEND:
     378              :         case B_ARCHIVER:
     379              :         case B_LOGGER:
     380              :         case B_BG_WRITER:
     381              :         case B_CHECKPOINTER:
     382              :         case B_IO_WORKER:
     383              :         case B_STARTUP:
     384              :         case B_DATACHECKSUMSWORKER_LAUNCHER:
     385              :         case B_DATACHECKSUMSWORKER_WORKER:
     386      7882406 :             return false;
     387              : 
     388     84682207 :         case B_AUTOVAC_WORKER:
     389              :         case B_BACKEND:
     390              :         case B_BG_WORKER:
     391              :         case B_STANDALONE_BACKEND:
     392              :         case B_SLOTSYNC_WORKER:
     393              :         case B_WAL_RECEIVER:
     394              :         case B_WAL_SENDER:
     395              :         case B_WAL_SUMMARIZER:
     396              :         case B_WAL_WRITER:
     397     84682207 :             return true;
     398              :     }
     399              : 
     400            0 :     return false;
     401              : }
     402              : 
     403              : void
     404            4 : pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
     405              : {
     406            4 :     ((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
     407            4 : }
        

Generated by: LCOV version 2.0-1