LCOV - code coverage report
Current view: top level - src/backend/executor - instrument.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 141 150 94.0 %
Date: 2026-01-22 14:16:26 Functions: 14 14 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * instrument.c
       4             :  *   functions for instrumentation of plan execution
       5             :  *
       6             :  *
       7             :  * Copyright (c) 2001-2026, PostgreSQL Global Development Group
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/executor/instrument.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include <unistd.h>
      17             : 
      18             : #include "executor/instrument.h"
      19             : 
      20             : BufferUsage pgBufferUsage;
      21             : static BufferUsage save_pgBufferUsage;
      22             : WalUsage    pgWalUsage;
      23             : static WalUsage save_pgWalUsage;
      24             : 
      25             : static void BufferUsageAdd(BufferUsage *dst, const BufferUsage *add);
      26             : static void WalUsageAdd(WalUsage *dst, WalUsage *add);
      27             : 
      28             : 
      29             : /* Allocate new instrumentation structure(s) */
      30             : Instrumentation *
      31       89070 : InstrAlloc(int n, int instrument_options, bool async_mode)
      32             : {
      33             :     Instrumentation *instr;
      34             : 
      35             :     /* initialize all fields to zeroes, then modify as needed */
      36       89070 :     instr = palloc0(n * sizeof(Instrumentation));
      37       89070 :     if (instrument_options & (INSTRUMENT_BUFFERS | INSTRUMENT_TIMER | INSTRUMENT_WAL))
      38             :     {
      39       84022 :         bool        need_buffers = (instrument_options & INSTRUMENT_BUFFERS) != 0;
      40       84022 :         bool        need_wal = (instrument_options & INSTRUMENT_WAL) != 0;
      41       84022 :         bool        need_timer = (instrument_options & INSTRUMENT_TIMER) != 0;
      42             :         int         i;
      43             : 
      44      168044 :         for (i = 0; i < n; i++)
      45             :         {
      46       84022 :             instr[i].need_bufusage = need_buffers;
      47       84022 :             instr[i].need_walusage = need_wal;
      48       84022 :             instr[i].need_timer = need_timer;
      49       84022 :             instr[i].async_mode = async_mode;
      50             :         }
      51             :     }
      52             : 
      53       89070 :     return instr;
      54             : }
      55             : 
      56             : /* Initialize a pre-allocated instrumentation structure. */
      57             : void
      58        1680 : InstrInit(Instrumentation *instr, int instrument_options)
      59             : {
      60        1680 :     memset(instr, 0, sizeof(Instrumentation));
      61        1680 :     instr->need_bufusage = (instrument_options & INSTRUMENT_BUFFERS) != 0;
      62        1680 :     instr->need_walusage = (instrument_options & INSTRUMENT_WAL) != 0;
      63        1680 :     instr->need_timer = (instrument_options & INSTRUMENT_TIMER) != 0;
      64        1680 : }
      65             : 
      66             : /* Entry to a plan node */
      67             : void
      68    13562604 : InstrStartNode(Instrumentation *instr)
      69             : {
      70    24277024 :     if (instr->need_timer &&
      71    10714420 :         !INSTR_TIME_SET_CURRENT_LAZY(instr->starttime))
      72           0 :         elog(ERROR, "InstrStartNode called twice in a row");
      73             : 
      74             :     /* save buffer usage totals at node entry, if needed */
      75    13562604 :     if (instr->need_bufusage)
      76    10635652 :         instr->bufusage_start = pgBufferUsage;
      77             : 
      78    13562604 :     if (instr->need_walusage)
      79      154202 :         instr->walusage_start = pgWalUsage;
      80    13562604 : }
      81             : 
      82             : /* Exit from a plan node */
      83             : void
      84    13559484 : InstrStopNode(Instrumentation *instr, double nTuples)
      85             : {
      86    13559484 :     double      save_tuplecount = instr->tuplecount;
      87             :     instr_time  endtime;
      88             : 
      89             :     /* count the returned tuples */
      90    13559484 :     instr->tuplecount += nTuples;
      91             : 
      92             :     /* let's update the time only if the timer was requested */
      93    13559484 :     if (instr->need_timer)
      94             :     {
      95    10711312 :         if (INSTR_TIME_IS_ZERO(instr->starttime))
      96           0 :             elog(ERROR, "InstrStopNode called without start");
      97             : 
      98    10711312 :         INSTR_TIME_SET_CURRENT(endtime);
      99    10711312 :         INSTR_TIME_ACCUM_DIFF(instr->counter, endtime, instr->starttime);
     100             : 
     101    10711312 :         INSTR_TIME_SET_ZERO(instr->starttime);
     102             :     }
     103             : 
     104             :     /* Add delta of buffer usage since entry to node's totals */
     105    13559484 :     if (instr->need_bufusage)
     106    10632544 :         BufferUsageAccumDiff(&instr->bufusage,
     107    10632544 :                              &pgBufferUsage, &instr->bufusage_start);
     108             : 
     109    13559484 :     if (instr->need_walusage)
     110      151094 :         WalUsageAccumDiff(&instr->walusage,
     111      151094 :                           &pgWalUsage, &instr->walusage_start);
     112             : 
     113             :     /* Is this the first tuple of this cycle? */
     114    13559484 :     if (!instr->running)
     115             :     {
     116      127008 :         instr->running = true;
     117      127008 :         instr->firsttuple = instr->counter;
     118             :     }
     119             :     else
     120             :     {
     121             :         /*
     122             :          * In async mode, if the plan node hadn't emitted any tuples before,
     123             :          * this might be the first tuple
     124             :          */
     125    13432476 :         if (instr->async_mode && save_tuplecount < 1.0)
     126           0 :             instr->firsttuple = instr->counter;
     127             :     }
     128    13559484 : }
     129             : 
     130             : /* Update tuple count */
     131             : void
     132           4 : InstrUpdateTupleCount(Instrumentation *instr, double nTuples)
     133             : {
     134             :     /* count the returned tuples */
     135           4 :     instr->tuplecount += nTuples;
     136           4 : }
     137             : 
     138             : /* Finish a run cycle for a plan node */
     139             : void
     140      128862 : InstrEndLoop(Instrumentation *instr)
     141             : {
     142             :     /* Skip if nothing has happened, or already shut down */
     143      128862 :     if (!instr->running)
     144        2198 :         return;
     145             : 
     146      126664 :     if (!INSTR_TIME_IS_ZERO(instr->starttime))
     147           0 :         elog(ERROR, "InstrEndLoop called on running node");
     148             : 
     149             :     /* Accumulate per-cycle statistics into totals */
     150      126664 :     INSTR_TIME_ADD(instr->startup, instr->firsttuple);
     151      126664 :     INSTR_TIME_ADD(instr->total, instr->counter);
     152      126664 :     instr->ntuples += instr->tuplecount;
     153      126664 :     instr->nloops += 1;
     154             : 
     155             :     /* Reset for next cycle (if any) */
     156      126664 :     instr->running = false;
     157      126664 :     INSTR_TIME_SET_ZERO(instr->starttime);
     158      126664 :     INSTR_TIME_SET_ZERO(instr->counter);
     159      126664 :     INSTR_TIME_SET_ZERO(instr->firsttuple);
     160      126664 :     instr->tuplecount = 0;
     161             : }
     162             : 
     163             : /* aggregate instrumentation information */
     164             : void
     165        4056 : InstrAggNode(Instrumentation *dst, Instrumentation *add)
     166             : {
     167        4056 :     if (!dst->running && add->running)
     168             :     {
     169           0 :         dst->running = true;
     170           0 :         dst->firsttuple = add->firsttuple;
     171             :     }
     172        4056 :     else if (dst->running && add->running &&
     173           0 :              INSTR_TIME_LT(dst->firsttuple, add->firsttuple))
     174           0 :         dst->firsttuple = add->firsttuple;
     175             : 
     176        4056 :     INSTR_TIME_ADD(dst->counter, add->counter);
     177             : 
     178        4056 :     dst->tuplecount += add->tuplecount;
     179        4056 :     INSTR_TIME_ADD(dst->startup, add->startup);
     180        4056 :     INSTR_TIME_ADD(dst->total, add->total);
     181        4056 :     dst->ntuples += add->ntuples;
     182        4056 :     dst->ntuples2 += add->ntuples2;
     183        4056 :     dst->nloops += add->nloops;
     184        4056 :     dst->nfiltered1 += add->nfiltered1;
     185        4056 :     dst->nfiltered2 += add->nfiltered2;
     186             : 
     187             :     /* Add delta of buffer usage since entry to node's totals */
     188        4056 :     if (dst->need_bufusage)
     189        1908 :         BufferUsageAdd(&dst->bufusage, &add->bufusage);
     190             : 
     191        4056 :     if (dst->need_walusage)
     192           0 :         WalUsageAdd(&dst->walusage, &add->walusage);
     193        4056 : }
     194             : 
     195             : /* note current values during parallel executor startup */
     196             : void
     197        2874 : InstrStartParallelQuery(void)
     198             : {
     199        2874 :     save_pgBufferUsage = pgBufferUsage;
     200        2874 :     save_pgWalUsage = pgWalUsage;
     201        2874 : }
     202             : 
     203             : /* report usage after parallel executor shutdown */
     204             : void
     205        2862 : InstrEndParallelQuery(BufferUsage *bufusage, WalUsage *walusage)
     206             : {
     207        2862 :     memset(bufusage, 0, sizeof(BufferUsage));
     208        2862 :     BufferUsageAccumDiff(bufusage, &pgBufferUsage, &save_pgBufferUsage);
     209        2862 :     memset(walusage, 0, sizeof(WalUsage));
     210        2862 :     WalUsageAccumDiff(walusage, &pgWalUsage, &save_pgWalUsage);
     211        2862 : }
     212             : 
     213             : /* accumulate work done by workers in leader's stats */
     214             : void
     215        2862 : InstrAccumParallelQuery(BufferUsage *bufusage, WalUsage *walusage)
     216             : {
     217        2862 :     BufferUsageAdd(&pgBufferUsage, bufusage);
     218        2862 :     WalUsageAdd(&pgWalUsage, walusage);
     219        2862 : }
     220             : 
     221             : /* dst += add */
     222             : static void
     223        4770 : BufferUsageAdd(BufferUsage *dst, const BufferUsage *add)
     224             : {
     225        4770 :     dst->shared_blks_hit += add->shared_blks_hit;
     226        4770 :     dst->shared_blks_read += add->shared_blks_read;
     227        4770 :     dst->shared_blks_dirtied += add->shared_blks_dirtied;
     228        4770 :     dst->shared_blks_written += add->shared_blks_written;
     229        4770 :     dst->local_blks_hit += add->local_blks_hit;
     230        4770 :     dst->local_blks_read += add->local_blks_read;
     231        4770 :     dst->local_blks_dirtied += add->local_blks_dirtied;
     232        4770 :     dst->local_blks_written += add->local_blks_written;
     233        4770 :     dst->temp_blks_read += add->temp_blks_read;
     234        4770 :     dst->temp_blks_written += add->temp_blks_written;
     235        4770 :     INSTR_TIME_ADD(dst->shared_blk_read_time, add->shared_blk_read_time);
     236        4770 :     INSTR_TIME_ADD(dst->shared_blk_write_time, add->shared_blk_write_time);
     237        4770 :     INSTR_TIME_ADD(dst->local_blk_read_time, add->local_blk_read_time);
     238        4770 :     INSTR_TIME_ADD(dst->local_blk_write_time, add->local_blk_write_time);
     239        4770 :     INSTR_TIME_ADD(dst->temp_blk_read_time, add->temp_blk_read_time);
     240        4770 :     INSTR_TIME_ADD(dst->temp_blk_write_time, add->temp_blk_write_time);
     241        4770 : }
     242             : 
     243             : /* dst += add - sub */
     244             : void
     245    10792728 : BufferUsageAccumDiff(BufferUsage *dst,
     246             :                      const BufferUsage *add,
     247             :                      const BufferUsage *sub)
     248             : {
     249    10792728 :     dst->shared_blks_hit += add->shared_blks_hit - sub->shared_blks_hit;
     250    10792728 :     dst->shared_blks_read += add->shared_blks_read - sub->shared_blks_read;
     251    10792728 :     dst->shared_blks_dirtied += add->shared_blks_dirtied - sub->shared_blks_dirtied;
     252    10792728 :     dst->shared_blks_written += add->shared_blks_written - sub->shared_blks_written;
     253    10792728 :     dst->local_blks_hit += add->local_blks_hit - sub->local_blks_hit;
     254    10792728 :     dst->local_blks_read += add->local_blks_read - sub->local_blks_read;
     255    10792728 :     dst->local_blks_dirtied += add->local_blks_dirtied - sub->local_blks_dirtied;
     256    10792728 :     dst->local_blks_written += add->local_blks_written - sub->local_blks_written;
     257    10792728 :     dst->temp_blks_read += add->temp_blks_read - sub->temp_blks_read;
     258    10792728 :     dst->temp_blks_written += add->temp_blks_written - sub->temp_blks_written;
     259    10792728 :     INSTR_TIME_ACCUM_DIFF(dst->shared_blk_read_time,
     260             :                           add->shared_blk_read_time, sub->shared_blk_read_time);
     261    10792728 :     INSTR_TIME_ACCUM_DIFF(dst->shared_blk_write_time,
     262             :                           add->shared_blk_write_time, sub->shared_blk_write_time);
     263    10792728 :     INSTR_TIME_ACCUM_DIFF(dst->local_blk_read_time,
     264             :                           add->local_blk_read_time, sub->local_blk_read_time);
     265    10792728 :     INSTR_TIME_ACCUM_DIFF(dst->local_blk_write_time,
     266             :                           add->local_blk_write_time, sub->local_blk_write_time);
     267    10792728 :     INSTR_TIME_ACCUM_DIFF(dst->temp_blk_read_time,
     268             :                           add->temp_blk_read_time, sub->temp_blk_read_time);
     269    10792728 :     INSTR_TIME_ACCUM_DIFF(dst->temp_blk_write_time,
     270             :                           add->temp_blk_write_time, sub->temp_blk_write_time);
     271    10792728 : }
     272             : 
     273             : /* helper functions for WAL usage accumulation */
     274             : static void
     275        2862 : WalUsageAdd(WalUsage *dst, WalUsage *add)
     276             : {
     277        2862 :     dst->wal_bytes += add->wal_bytes;
     278        2862 :     dst->wal_records += add->wal_records;
     279        2862 :     dst->wal_fpi += add->wal_fpi;
     280        2862 :     dst->wal_fpi_bytes += add->wal_fpi_bytes;
     281        2862 :     dst->wal_buffers_full += add->wal_buffers_full;
     282        2862 : }
     283             : 
     284             : void
     285      349624 : WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
     286             : {
     287      349624 :     dst->wal_bytes += add->wal_bytes - sub->wal_bytes;
     288      349624 :     dst->wal_records += add->wal_records - sub->wal_records;
     289      349624 :     dst->wal_fpi += add->wal_fpi - sub->wal_fpi;
     290      349624 :     dst->wal_fpi_bytes += add->wal_fpi_bytes - sub->wal_fpi_bytes;
     291      349624 :     dst->wal_buffers_full += add->wal_buffers_full - sub->wal_buffers_full;
     292      349624 : }

Generated by: LCOV version 1.16