Age Owner Branch data TLA 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-2025, 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
280 michael@paquier.xyz 56 :CBC 2 : pgstat_count_backend_io_op_time(IOObject io_object, IOContext io_context,
57 : : IOOp io_op, instr_time io_time)
58 : : {
244 59 [ - + - - ]: 2 : Assert(track_io_timing || track_wal_io_timing);
60 : :
280 61 [ - + ]: 2 : if (!pgstat_tracks_backend_bktype(MyBackendType))
280 michael@paquier.xyz 62 :UBC 0 : return;
63 : :
280 michael@paquier.xyz 64 [ - + ]:CBC 2 : 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 : :
223 69 : 2 : backend_has_iostats = true;
92 70 : 2 : pgstat_report_fixed = true;
71 : : }
72 : :
73 : : void
280 74 : 62803734 : pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
75 : : IOOp io_op, uint32 cnt, uint64 bytes)
76 : : {
77 [ + + ]: 62803734 : if (!pgstat_tracks_backend_bktype(MyBackendType))
78 : 7268813 : return;
79 : :
80 [ - + ]: 55534921 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
81 : :
82 : 55534921 : PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
83 : 55534921 : PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
84 : :
223 85 : 55534921 : backend_has_iostats = true;
92 86 : 55534921 : pgstat_report_fixed = true;
87 : : }
88 : :
89 : : /*
90 : : * Returns statistics of a backend by proc number.
91 : : */
92 : : PgStat_Backend *
313 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 *
242 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 */
239 123 [ + + ]: 33 : if (!proc)
124 : 6 : proc = AuxiliaryPidGetProc(pid);
125 : :
242 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)
242 michael@paquier.xyz 133 :UBC 0 : return NULL;
134 : :
135 : : /* check if the backend type tracks statistics */
239 michael@paquier.xyz 136 [ + + ]:CBC 30 : if (!pgstat_tracks_backend_bktype(beentry->st_backendType))
137 : 3 : return NULL;
138 : :
139 : : /* if PID does not match, leave */
242 140 [ - + ]: 27 : if (beentry->st_procpid != pid)
242 michael@paquier.xyz 141 :UBC 0 : return NULL;
142 : :
242 michael@paquier.xyz 143 [ + + ]:CBC 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 : : */
204 150 : 27 : backend_stats = pgstat_fetch_stat_backend(procNumber);
151 [ - + ]: 27 : if (!backend_stats)
152 : : {
204 michael@paquier.xyz 153 [ # # ]:UBC 0 : if (bktype)
154 : 0 : *bktype = B_INVALID;
155 : 0 : return NULL;
156 : : }
157 : :
242 michael@paquier.xyz 158 :CBC 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
291 166 : 43067 : 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 : : */
223 177 [ + + ]: 43067 : if (!backend_has_iostats)
280 178 : 181 : return;
179 : :
291 180 : 42886 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
181 : 42886 : bktype_shstats = &shbackendent->stats.io_stats;
280 182 : 42886 : pending_io = PendingBackendStats.pending_io;
183 : :
313 184 [ + + ]: 171544 : for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
185 : : {
186 [ + + ]: 771948 : for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
187 : : {
188 [ + + ]: 5789610 : for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
189 : : {
190 : : instr_time time;
191 : :
192 : 5146320 : bktype_shstats->counts[io_object][io_context][io_op] +=
280 193 : 5146320 : pending_io.counts[io_object][io_context][io_op];
287 194 : 5146320 : bktype_shstats->bytes[io_object][io_context][io_op] +=
280 195 : 5146320 : pending_io.bytes[io_object][io_context][io_op];
196 : 5146320 : time = pending_io.pending_times[io_object][io_context][io_op];
197 : :
313 198 : 5146320 : bktype_shstats->times[io_object][io_context][io_op] +=
199 : 5146320 : INSTR_TIME_GET_MICROSEC(time);
200 : : }
201 : : }
202 : : }
203 : :
204 : : /*
205 : : * Clear out the statistics buffer, so it can be re-used.
206 : : */
280 207 [ + - + - : 42886 : MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
+ - - + -
- ]
208 : :
223 209 : 42886 : backend_has_iostats = false;
210 : : }
211 : :
212 : : /*
213 : : * To determine whether WAL usage happened.
214 : : */
215 : : static inline bool
231 216 : 56267 : pgstat_backend_wal_have_pending(void)
217 : : {
218 : 56267 : 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 : 16898 : pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
227 : : {
228 : : PgStatShared_Backend *shbackendent;
229 : : PgStat_WalCounters *bktype_shstats;
230 : 16898 : 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 [ + + ]: 16898 : if (!pgstat_backend_wal_have_pending())
238 : 8249 : return;
239 : :
240 : 8649 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
241 : 8649 : 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 : 8649 : WalUsageAccumDiff(&wal_usage_diff, &pgWalUsage, &prevBackendWalUsage);
248 : :
249 : : #define WALSTAT_ACC(fld, var_to_add) \
250 : : (bktype_shstats->fld += var_to_add.fld)
251 : 8649 : WALSTAT_ACC(wal_buffers_full, wal_usage_diff);
252 : 8649 : WALSTAT_ACC(wal_records, wal_usage_diff);
253 : 8649 : WALSTAT_ACC(wal_fpi, wal_usage_diff);
254 : 8649 : WALSTAT_ACC(wal_bytes, wal_usage_diff);
255 : : #undef WALSTAT_ACC
256 : :
257 : : /*
258 : : * Save the current counters for the subsequent calculation of WAL usage.
259 : : */
260 : 8649 : prevBackendWalUsage = pgWalUsage;
261 : : }
262 : :
263 : : /*
264 : : * Flush out locally pending backend statistics
265 : : *
266 : : * "flags" parameter controls which statistics to flush. Returns true
267 : : * if some statistics could not be flushed due to lock contention.
268 : : */
269 : : bool
280 270 : 105586 : pgstat_flush_backend(bool nowait, bits32 flags)
271 : : {
272 : : PgStat_EntryRef *entry_ref;
231 273 : 105586 : bool has_pending_data = false;
274 : :
291 275 [ + + ]: 105586 : if (!pgstat_tracks_backend_bktype(MyBackendType))
276 : 32939 : return false;
277 : :
278 : : /* Some IO data pending? */
223 279 [ + + + + ]: 72647 : if ((flags & PGSTAT_BACKEND_FLUSH_IO) && backend_has_iostats)
231 280 : 42886 : has_pending_data = true;
281 : :
282 : : /* Some WAL data pending? */
283 [ + + + + ]: 112016 : if ((flags & PGSTAT_BACKEND_FLUSH_WAL) &&
284 : 39369 : pgstat_backend_wal_have_pending())
285 : 8649 : has_pending_data = true;
286 : :
287 [ + + ]: 72647 : if (!has_pending_data)
291 288 : 29580 : return false;
289 : :
280 290 : 43067 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
291 : : MyProcNumber, nowait);
292 [ - + ]: 43067 : if (!entry_ref)
280 michael@paquier.xyz 293 :UBC 0 : return true;
294 : :
295 : : /* Flush requested statistics */
291 michael@paquier.xyz 296 [ + - ]:CBC 43067 : if (flags & PGSTAT_BACKEND_FLUSH_IO)
297 : 43067 : pgstat_flush_backend_entry_io(entry_ref);
298 : :
231 299 [ + + ]: 43067 : if (flags & PGSTAT_BACKEND_FLUSH_WAL)
300 : 16898 : pgstat_flush_backend_entry_wal(entry_ref);
301 : :
313 302 : 43067 : pgstat_unlock_entry(entry_ref);
303 : :
280 304 : 43067 : return false;
305 : : }
306 : :
307 : : /*
308 : : * Callback to flush out locally pending backend statistics.
309 : : *
310 : : * If some stats could not be flushed due to lock contention, return true.
311 : : */
312 : : bool
313 : 31396 : pgstat_backend_flush_cb(bool nowait)
314 : : {
315 : 31396 : return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
316 : : }
317 : :
318 : : /*
319 : : * Create backend statistics entry for proc number.
320 : : */
321 : : void
313 322 : 15087 : pgstat_create_backend(ProcNumber procnum)
323 : : {
324 : : PgStat_EntryRef *entry_ref;
325 : : PgStatShared_Backend *shstatent;
326 : :
280 327 : 15087 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
328 : : MyProcNumber, false);
313 329 : 15087 : shstatent = (PgStatShared_Backend *) entry_ref->shared_stats;
330 : :
331 : : /*
332 : : * NB: need to accept that there might be stats from an older backend,
333 : : * e.g. if we previously used this proc number.
334 : : */
335 : 15087 : memset(&shstatent->stats, 0, sizeof(shstatent->stats));
280 336 : 15087 : pgstat_unlock_entry(entry_ref);
337 : :
338 [ + - + - : 15087 : MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
+ - - + -
- ]
223 339 : 15087 : backend_has_iostats = false;
340 : :
341 : : /*
342 : : * Initialize prevBackendWalUsage with pgWalUsage so that
343 : : * pgstat_backend_flush_cb() can calculate how much pgWalUsage counters
344 : : * are increased by subtracting prevBackendWalUsage from pgWalUsage.
345 : : */
231 346 : 15087 : prevBackendWalUsage = pgWalUsage;
313 347 : 15087 : }
348 : :
349 : : /*
350 : : * Backend statistics are not collected for all BackendTypes.
351 : : *
352 : : * The following BackendTypes do not participate in the backend stats
353 : : * subsystem:
354 : : * - The same and for the same reasons as in pgstat_tracks_io_bktype().
355 : : * - B_BG_WRITER, B_CHECKPOINTER, B_STARTUP and B_AUTOVAC_LAUNCHER because their
356 : : * I/O stats are already visible in pg_stat_io and there is only one of those.
357 : : *
358 : : * Function returns true if BackendType participates in the backend stats
359 : : * subsystem and false if it does not.
360 : : *
361 : : * When adding a new BackendType, also consider adding relevant restrictions to
362 : : * pgstat_tracks_io_object() and pgstat_tracks_io_op().
363 : : */
364 : : bool
365 : 62928192 : pgstat_tracks_backend_bktype(BackendType bktype)
366 : : {
367 : : /*
368 : : * List every type so that new backend types trigger a warning about
369 : : * needing to adjust this switch.
370 : : */
371 [ + + - ]: 62928192 : switch (bktype)
372 : : {
373 : 7305505 : case B_INVALID:
374 : : case B_AUTOVAC_LAUNCHER:
375 : : case B_DEAD_END_BACKEND:
376 : : case B_ARCHIVER:
377 : : case B_LOGGER:
378 : : case B_BG_WRITER:
379 : : case B_CHECKPOINTER:
380 : : case B_IO_WORKER:
381 : : case B_STARTUP:
382 : 7305505 : return false;
383 : :
384 : 55622687 : case B_AUTOVAC_WORKER:
385 : : case B_BACKEND:
386 : : case B_BG_WORKER:
387 : : case B_STANDALONE_BACKEND:
388 : : case B_SLOTSYNC_WORKER:
389 : : case B_WAL_RECEIVER:
390 : : case B_WAL_SENDER:
391 : : case B_WAL_SUMMARIZER:
392 : : case B_WAL_WRITER:
393 : 55622687 : return true;
394 : : }
395 : :
313 michael@paquier.xyz 396 :UBC 0 : return false;
397 : : }
398 : :
399 : : void
313 michael@paquier.xyz 400 :CBC 3 : pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
401 : : {
402 : 3 : ((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
403 : 3 : }
|