Branch data 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 : : static bool backend_has_lockstats = false;
43 : :
44 : : /*
45 : : * WAL usage counters saved from pgWalUsage at the previous call to
46 : : * pgstat_flush_backend(). This is used to calculate how much WAL usage
47 : : * happens between pgstat_flush_backend() calls, by subtracting the
48 : : * previous counters from the current ones.
49 : : */
50 : : static WalUsage prevBackendWalUsage;
51 : :
52 : : /*
53 : : * Utility routines to report I/O stats for backends, kept here to avoid
54 : : * exposing PendingBackendStats to the outside world.
55 : : */
56 : : void
57 : 7 : pgstat_count_backend_io_op_time(IOObject io_object, IOContext io_context,
58 : : IOOp io_op, instr_time io_time)
59 : : {
60 : : Assert(track_io_timing || track_wal_io_timing);
61 : :
62 [ - + ]: 7 : if (!pgstat_tracks_backend_bktype(MyBackendType))
63 : 0 : return;
64 : :
65 : : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
66 : :
67 : 7 : INSTR_TIME_ADD(PendingBackendStats.pending_io.pending_times[io_object][io_context][io_op],
68 : : io_time);
69 : :
70 : 7 : backend_has_iostats = true;
71 : 7 : pgstat_report_fixed = true;
72 : : }
73 : :
74 : : void
75 : 92181444 : pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
76 : : IOOp io_op, uint32 cnt, uint64 bytes)
77 : : {
78 [ + + ]: 92181444 : if (!pgstat_tracks_backend_bktype(MyBackendType))
79 : 7902761 : return;
80 : :
81 : : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
82 : :
83 : 84278683 : PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
84 : 84278683 : PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
85 : :
86 : 84278683 : backend_has_iostats = true;
87 : 84278683 : pgstat_report_fixed = true;
88 : : }
89 : :
90 : : /*
91 : : * Utility routines to report lock stats for backends, kept here to avoid
92 : : * exposing PendingBackendStats to the outside world.
93 : : */
94 : : void
95 : 27 : pgstat_count_backend_lock_waits(uint8 locktag_type, PgStat_Counter usecs)
96 : : {
97 [ - + ]: 27 : if (!pgstat_tracks_backend_bktype(MyBackendType))
98 : 0 : return;
99 : :
100 : : Assert(locktag_type <= LOCKTAG_LAST_TYPE);
101 : :
102 : 27 : PendingBackendStats.pending_lock.stats[locktag_type].waits++;
103 : 27 : PendingBackendStats.pending_lock.stats[locktag_type].wait_time += usecs;
104 : :
105 : 27 : backend_has_lockstats = true;
106 : 27 : pgstat_report_fixed = true;
107 : : }
108 : :
109 : : void
110 : 89507 : pgstat_count_backend_lock_fastpath_exceeded(uint8 locktag_type)
111 : : {
112 [ - + ]: 89507 : if (!pgstat_tracks_backend_bktype(MyBackendType))
113 : 0 : return;
114 : :
115 : : Assert(locktag_type <= LOCKTAG_LAST_TYPE);
116 : :
117 : 89507 : PendingBackendStats.pending_lock.stats[locktag_type].fastpath_exceeded++;
118 : :
119 : 89507 : backend_has_lockstats = true;
120 : 89507 : pgstat_report_fixed = true;
121 : : }
122 : :
123 : : /*
124 : : * Returns statistics of a backend by proc number.
125 : : */
126 : : PgStat_Backend *
127 : 44 : pgstat_fetch_stat_backend(ProcNumber procNumber)
128 : : {
129 : : PgStat_Backend *backend_entry;
130 : :
131 : 44 : backend_entry = (PgStat_Backend *) pgstat_fetch_entry(PGSTAT_KIND_BACKEND,
132 : : InvalidOid, procNumber,
133 : : NULL);
134 : :
135 : 44 : return backend_entry;
136 : : }
137 : :
138 : : /*
139 : : * Returns statistics of a backend by pid.
140 : : *
141 : : * This routine includes sanity checks to ensure that the backend exists and
142 : : * is running. "bktype" can be optionally defined to return the BackendType
143 : : * of the backend whose statistics are returned.
144 : : */
145 : : PgStat_Backend *
146 : 52 : pgstat_fetch_stat_backend_by_pid(int pid, BackendType *bktype)
147 : : {
148 : : PGPROC *proc;
149 : : PgBackendStatus *beentry;
150 : : ProcNumber procNumber;
151 : : PgStat_Backend *backend_stats;
152 : :
153 : 52 : proc = BackendPidGetProc(pid);
154 [ + + ]: 52 : if (bktype)
155 : 36 : *bktype = B_INVALID;
156 : :
157 : : /* this could be an auxiliary process */
158 [ + + ]: 52 : if (!proc)
159 : 8 : proc = AuxiliaryPidGetProc(pid);
160 : :
161 [ + + ]: 52 : if (!proc)
162 : 4 : return NULL;
163 : :
164 : 48 : procNumber = GetNumberFromPGProc(proc);
165 : :
166 : 48 : beentry = pgstat_get_beentry_by_proc_number(procNumber);
167 [ - + ]: 48 : if (!beentry)
168 : 0 : return NULL;
169 : :
170 : : /* check if the backend type tracks statistics */
171 [ + + ]: 48 : if (!pgstat_tracks_backend_bktype(beentry->st_backendType))
172 : 4 : return NULL;
173 : :
174 : : /* if PID does not match, leave */
175 [ - + ]: 44 : if (beentry->st_procpid != pid)
176 : 0 : return NULL;
177 : :
178 [ + + ]: 44 : if (bktype)
179 : 28 : *bktype = beentry->st_backendType;
180 : :
181 : : /*
182 : : * Retrieve the entry. Note that "beentry" may be freed depending on the
183 : : * value of stats_fetch_consistency, so do not access it from this point.
184 : : */
185 : 44 : backend_stats = pgstat_fetch_stat_backend(procNumber);
186 [ - + ]: 44 : if (!backend_stats)
187 : : {
188 [ # # ]: 0 : if (bktype)
189 : 0 : *bktype = B_INVALID;
190 : 0 : return NULL;
191 : : }
192 : :
193 : 44 : return backend_stats;
194 : : }
195 : :
196 : : /*
197 : : * Flush out locally pending backend IO statistics. Locking is managed
198 : : * by the caller.
199 : : */
200 : : static void
201 : 167703 : pgstat_flush_backend_entry_io(PgStat_EntryRef *entry_ref)
202 : : {
203 : : PgStatShared_Backend *shbackendent;
204 : : PgStat_BktypeIO *bktype_shstats;
205 : : PgStat_PendingIO pending_io;
206 : :
207 : : /*
208 : : * This function can be called even if nothing at all has happened for IO
209 : : * statistics. In this case, avoid unnecessarily modifying the stats
210 : : * entry.
211 : : */
212 [ + + ]: 167703 : if (!backend_has_iostats)
213 : 193 : return;
214 : :
215 : 167510 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
216 : 167510 : bktype_shstats = &shbackendent->stats.io_stats;
217 : 167510 : pending_io = PendingBackendStats.pending_io;
218 : :
219 [ + + ]: 670040 : for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
220 : : {
221 [ + + ]: 3015180 : for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
222 : : {
223 [ + + ]: 22613850 : for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
224 : : {
225 : : instr_time time;
226 : :
227 : 20101200 : bktype_shstats->counts[io_object][io_context][io_op] +=
228 : 20101200 : pending_io.counts[io_object][io_context][io_op];
229 : 20101200 : bktype_shstats->bytes[io_object][io_context][io_op] +=
230 : 20101200 : pending_io.bytes[io_object][io_context][io_op];
231 : 20101200 : time = pending_io.pending_times[io_object][io_context][io_op];
232 : :
233 : 20101200 : bktype_shstats->times[io_object][io_context][io_op] +=
234 : 20101200 : INSTR_TIME_GET_MICROSEC(time);
235 : : }
236 : : }
237 : : }
238 : :
239 : : /*
240 : : * Clear out the statistics buffer, so it can be re-used.
241 : : */
242 [ + - + - : 167510 : MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
+ - - + -
- ]
243 : :
244 : 167510 : backend_has_iostats = false;
245 : : }
246 : :
247 : : /*
248 : : * To determine whether WAL usage happened.
249 : : */
250 : : static inline bool
251 : 74901 : pgstat_backend_wal_have_pending(void)
252 : : {
253 : 74901 : return (pgWalUsage.wal_records != prevBackendWalUsage.wal_records);
254 : : }
255 : :
256 : : /*
257 : : * Flush out locally pending backend WAL statistics. Locking is managed
258 : : * by the caller.
259 : : */
260 : : static void
261 : 22165 : pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
262 : : {
263 : : PgStatShared_Backend *shbackendent;
264 : : PgStat_WalCounters *bktype_shstats;
265 : 22165 : WalUsage wal_usage_diff = {0};
266 : :
267 : : /*
268 : : * This function can be called even if nothing at all has happened for WAL
269 : : * statistics. In this case, avoid unnecessarily modifying the stats
270 : : * entry.
271 : : */
272 [ + + ]: 22165 : if (!pgstat_backend_wal_have_pending())
273 : 11479 : return;
274 : :
275 : 10686 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
276 : 10686 : bktype_shstats = &shbackendent->stats.wal_counters;
277 : :
278 : : /*
279 : : * Calculate how much WAL usage counters were increased by subtracting the
280 : : * previous counters from the current ones.
281 : : */
282 : 10686 : WalUsageAccumDiff(&wal_usage_diff, &pgWalUsage, &prevBackendWalUsage);
283 : :
284 : : #define WALSTAT_ACC(fld, var_to_add) \
285 : : (bktype_shstats->fld += var_to_add.fld)
286 : 10686 : WALSTAT_ACC(wal_buffers_full, wal_usage_diff);
287 : 10686 : WALSTAT_ACC(wal_records, wal_usage_diff);
288 : 10686 : WALSTAT_ACC(wal_fpi, wal_usage_diff);
289 : 10686 : WALSTAT_ACC(wal_bytes, wal_usage_diff);
290 : 10686 : WALSTAT_ACC(wal_fpi_bytes, wal_usage_diff);
291 : : #undef WALSTAT_ACC
292 : :
293 : : /*
294 : : * Save the current counters for the subsequent calculation of WAL usage.
295 : : */
296 : 10686 : prevBackendWalUsage = pgWalUsage;
297 : : }
298 : :
299 : : /*
300 : : * Flush out locally pending backend lock statistics. Locking is managed
301 : : * by the caller.
302 : : */
303 : : static void
304 : 22165 : pgstat_flush_backend_entry_lock(PgStat_EntryRef *entry_ref)
305 : : {
306 : : PgStatShared_Backend *shbackendent;
307 : : PgStat_PendingLock *bktype_shstats;
308 : :
309 [ + + ]: 22165 : if (!backend_has_lockstats)
310 : 21934 : return;
311 : :
312 : 231 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
313 : 231 : bktype_shstats = &shbackendent->stats.lock_stats;
314 : :
315 [ + + ]: 3003 : for (int i = 0; i <= LOCKTAG_LAST_TYPE; i++)
316 : : {
317 : : #define LOCKSTAT_ACC(fld) \
318 : : (bktype_shstats->stats[i].fld += PendingBackendStats.pending_lock.stats[i].fld)
319 : 2772 : LOCKSTAT_ACC(waits);
320 : 2772 : LOCKSTAT_ACC(wait_time);
321 : 2772 : LOCKSTAT_ACC(fastpath_exceeded);
322 : : #undef LOCKSTAT_ACC
323 : : }
324 : :
325 [ + - + - : 8547 : MemSet(&PendingBackendStats.pending_lock, 0, sizeof(PgStat_PendingLock));
+ - + - +
+ ]
326 : 231 : backend_has_lockstats = false;
327 : : }
328 : :
329 : : /*
330 : : * Flush out locally pending backend statistics
331 : : *
332 : : * "flags" parameter controls which statistics to flush. Returns true
333 : : * if some statistics could not be flushed due to lock contention.
334 : : */
335 : : bool
336 : 252560 : pgstat_flush_backend(bool nowait, uint32 flags)
337 : : {
338 : : PgStat_EntryRef *entry_ref;
339 : 252560 : bool has_pending_data = false;
340 : :
341 [ + + ]: 252560 : if (!pgstat_tracks_backend_bktype(MyBackendType))
342 : 43560 : return false;
343 : :
344 : : /* Some IO data pending? */
345 [ + + + + ]: 209000 : if ((flags & PGSTAT_BACKEND_FLUSH_IO) && backend_has_iostats)
346 : 167510 : has_pending_data = true;
347 : :
348 : : /* Some WAL data pending? */
349 [ + + + + ]: 261736 : if ((flags & PGSTAT_BACKEND_FLUSH_WAL) &&
350 : 52736 : pgstat_backend_wal_have_pending())
351 : 10686 : has_pending_data = true;
352 : :
353 : : /* Some lock data pending? */
354 [ + + + + ]: 209000 : if ((flags & PGSTAT_BACKEND_FLUSH_LOCK) && backend_has_lockstats)
355 : 231 : has_pending_data = true;
356 : :
357 [ + + ]: 209000 : if (!has_pending_data)
358 : 41297 : return false;
359 : :
360 : 167703 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
361 : : MyProcNumber, nowait);
362 [ - + ]: 167703 : if (!entry_ref)
363 : 0 : return true;
364 : :
365 : : /* Flush requested statistics */
366 [ + - ]: 167703 : if (flags & PGSTAT_BACKEND_FLUSH_IO)
367 : 167703 : pgstat_flush_backend_entry_io(entry_ref);
368 : :
369 [ + + ]: 167703 : if (flags & PGSTAT_BACKEND_FLUSH_WAL)
370 : 22165 : pgstat_flush_backend_entry_wal(entry_ref);
371 : :
372 [ + + ]: 167703 : if (flags & PGSTAT_BACKEND_FLUSH_LOCK)
373 : 22165 : pgstat_flush_backend_entry_lock(entry_ref);
374 : :
375 : 167703 : pgstat_unlock_entry(entry_ref);
376 : :
377 : 167703 : return false;
378 : : }
379 : :
380 : : /*
381 : : * Callback to flush out locally pending backend statistics.
382 : : *
383 : : * If some stats could not be flushed due to lock contention, return true.
384 : : */
385 : : bool
386 : 40116 : pgstat_backend_flush_cb(bool nowait)
387 : : {
388 : 40116 : return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
389 : : }
390 : :
391 : : /*
392 : : * Create backend statistics entry for proc number.
393 : : */
394 : : void
395 : 20434 : pgstat_create_backend(ProcNumber procnum)
396 : : {
397 : : PgStat_EntryRef *entry_ref;
398 : : PgStatShared_Backend *shstatent;
399 : :
400 : 20434 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
401 : : procnum, false);
402 : 20434 : shstatent = (PgStatShared_Backend *) entry_ref->shared_stats;
403 : :
404 : : /*
405 : : * NB: need to accept that there might be stats from an older backend,
406 : : * e.g. if we previously used this proc number.
407 : : */
408 : 20434 : memset(&shstatent->stats, 0, sizeof(shstatent->stats));
409 : 20434 : pgstat_unlock_entry(entry_ref);
410 : :
411 [ + - + - : 20434 : MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
+ - - + -
- ]
412 : 20434 : backend_has_iostats = false;
413 : 20434 : backend_has_lockstats = false;
414 : :
415 : : /*
416 : : * Initialize prevBackendWalUsage with pgWalUsage so that
417 : : * pgstat_backend_flush_cb() can calculate how much pgWalUsage counters
418 : : * are increased by subtracting prevBackendWalUsage from pgWalUsage.
419 : : */
420 : 20434 : prevBackendWalUsage = pgWalUsage;
421 : 20434 : }
422 : :
423 : : /*
424 : : * Backend statistics are not collected for all BackendTypes.
425 : : *
426 : : * The following BackendTypes do not participate in the backend stats
427 : : * subsystem:
428 : : * - The same and for the same reasons as in pgstat_tracks_io_bktype().
429 : : * - B_BG_WRITER, B_CHECKPOINTER, B_STARTUP and B_AUTOVAC_LAUNCHER because their
430 : : * I/O stats are already visible in pg_stat_io and there is only one of those.
431 : : *
432 : : * Function returns true if BackendType participates in the backend stats
433 : : * subsystem and false if it does not.
434 : : *
435 : : * When adding a new BackendType, also consider adding relevant restrictions to
436 : : * pgstat_tracks_io_object() and pgstat_tracks_io_op().
437 : : */
438 : : bool
439 : 92548126 : pgstat_tracks_backend_bktype(BackendType bktype)
440 : : {
441 : : /*
442 : : * List every type so that new backend types trigger a warning about
443 : : * needing to adjust this switch.
444 : : */
445 [ + + - ]: 92548126 : switch (bktype)
446 : : {
447 : 7950420 : case B_INVALID:
448 : : case B_AUTOVAC_LAUNCHER:
449 : : case B_DEAD_END_BACKEND:
450 : : case B_ARCHIVER:
451 : : case B_LOGGER:
452 : : case B_BG_WRITER:
453 : : case B_CHECKPOINTER:
454 : : case B_IO_WORKER:
455 : : case B_STARTUP:
456 : : case B_DATACHECKSUMSWORKER_LAUNCHER:
457 : : case B_DATACHECKSUMSWORKER_WORKER:
458 : 7950420 : return false;
459 : :
460 : 84597706 : case B_AUTOVAC_WORKER:
461 : : case B_BACKEND:
462 : : case B_BG_WORKER:
463 : : case B_STANDALONE_BACKEND:
464 : : case B_SLOTSYNC_WORKER:
465 : : case B_WAL_RECEIVER:
466 : : case B_WAL_SENDER:
467 : : case B_WAL_SUMMARIZER:
468 : : case B_WAL_WRITER:
469 : 84597706 : return true;
470 : : }
471 : :
472 : 0 : return false;
473 : : }
474 : :
475 : : void
476 : 4 : pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
477 : : {
478 : 4 : ((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
479 : 4 : }
|