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-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
469 michael@paquier.xyz 56 :CBC 7 : pgstat_count_backend_io_op_time(IOObject io_object, IOContext io_context,
57 : : IOOp io_op, instr_time io_time)
58 : : {
433 59 [ - + - - ]: 7 : Assert(track_io_timing || track_wal_io_timing);
60 : :
469 61 [ - + ]: 7 : if (!pgstat_tracks_backend_bktype(MyBackendType))
469 michael@paquier.xyz 62 :UBC 0 : return;
63 : :
469 michael@paquier.xyz 64 [ - + ]:CBC 7 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
65 : :
66 : 7 : INSTR_TIME_ADD(PendingBackendStats.pending_io.pending_times[io_object][io_context][io_op],
67 : : io_time);
68 : :
412 69 : 7 : backend_has_iostats = true;
281 70 : 7 : pgstat_report_fixed = true;
71 : : }
72 : :
73 : : void
469 74 : 90977533 : pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
75 : : IOOp io_op, uint32 cnt, uint64 bytes)
76 : : {
77 [ + + ]: 90977533 : if (!pgstat_tracks_backend_bktype(MyBackendType))
78 : 7847849 : return;
79 : :
80 [ - + ]: 83129684 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
81 : :
82 : 83129684 : PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
83 : 83129684 : PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
84 : :
412 85 : 83129684 : backend_has_iostats = true;
281 86 : 83129684 : pgstat_report_fixed = true;
87 : : }
88 : :
89 : : /*
90 : : * Returns statistics of a backend by proc number.
91 : : */
92 : : PgStat_Backend *
502 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 *
431 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 */
428 124 [ + + ]: 44 : if (!proc)
125 : 8 : proc = AuxiliaryPidGetProc(pid);
126 : :
431 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)
431 michael@paquier.xyz 134 :UBC 0 : return NULL;
135 : :
136 : : /* check if the backend type tracks statistics */
428 michael@paquier.xyz 137 [ + + ]:CBC 40 : if (!pgstat_tracks_backend_bktype(beentry->st_backendType))
138 : 4 : return NULL;
139 : :
140 : : /* if PID does not match, leave */
431 141 [ - + ]: 36 : if (beentry->st_procpid != pid)
431 michael@paquier.xyz 142 :UBC 0 : return NULL;
143 : :
431 michael@paquier.xyz 144 [ + + ]:CBC 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 : : */
393 151 : 36 : backend_stats = pgstat_fetch_stat_backend(procNumber);
152 [ - + ]: 36 : if (!backend_stats)
153 : : {
393 michael@paquier.xyz 154 [ # # ]:UBC 0 : if (bktype)
155 : 0 : *bktype = B_INVALID;
156 : 0 : return NULL;
157 : : }
158 : :
431 michael@paquier.xyz 159 :CBC 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
480 167 : 54352 : 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 : : */
412 178 [ + + ]: 54352 : if (!backend_has_iostats)
469 179 : 206 : return;
180 : :
480 181 : 54146 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
182 : 54146 : bktype_shstats = &shbackendent->stats.io_stats;
469 183 : 54146 : pending_io = PendingBackendStats.pending_io;
184 : :
502 185 [ + + ]: 216584 : for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
186 : : {
187 [ + + ]: 974628 : for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
188 : : {
189 [ + + ]: 7309710 : for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
190 : : {
191 : : instr_time time;
192 : :
193 : 6497520 : bktype_shstats->counts[io_object][io_context][io_op] +=
469 194 : 6497520 : pending_io.counts[io_object][io_context][io_op];
476 195 : 6497520 : bktype_shstats->bytes[io_object][io_context][io_op] +=
469 196 : 6497520 : pending_io.bytes[io_object][io_context][io_op];
197 : 6497520 : time = pending_io.pending_times[io_object][io_context][io_op];
198 : :
502 199 : 6497520 : bktype_shstats->times[io_object][io_context][io_op] +=
200 : 6497520 : INSTR_TIME_GET_MICROSEC(time);
201 : : }
202 : : }
203 : : }
204 : :
205 : : /*
206 : : * Clear out the statistics buffer, so it can be re-used.
207 : : */
469 208 [ + - + - : 54146 : MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
+ - - + -
- ]
209 : :
412 210 : 54146 : backend_has_iostats = false;
211 : : }
212 : :
213 : : /*
214 : : * To determine whether WAL usage happened.
215 : : */
216 : : static inline bool
420 217 : 70433 : pgstat_backend_wal_have_pending(void)
218 : : {
219 : 70433 : 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 : 21485 : pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
228 : : {
229 : : PgStatShared_Backend *shbackendent;
230 : : PgStat_WalCounters *bktype_shstats;
231 : 21485 : 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 [ + + ]: 21485 : if (!pgstat_backend_wal_have_pending())
239 : 10507 : return;
240 : :
241 : 10978 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
242 : 10978 : 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 : 10978 : WalUsageAccumDiff(&wal_usage_diff, &pgWalUsage, &prevBackendWalUsage);
249 : :
250 : : #define WALSTAT_ACC(fld, var_to_add) \
251 : : (bktype_shstats->fld += var_to_add.fld)
252 : 10978 : WALSTAT_ACC(wal_buffers_full, wal_usage_diff);
253 : 10978 : WALSTAT_ACC(wal_records, wal_usage_diff);
254 : 10978 : WALSTAT_ACC(wal_fpi, wal_usage_diff);
255 : 10978 : WALSTAT_ACC(wal_bytes, wal_usage_diff);
189 michael@paquier.xyz 256 :GNC 10978 : 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 : : */
420 michael@paquier.xyz 262 :CBC 10978 : 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
36 nathan@postgresql.or 272 :GNC 133678 : pgstat_flush_backend(bool nowait, uint32 flags)
273 : : {
274 : : PgStat_EntryRef *entry_ref;
420 michael@paquier.xyz 275 :CBC 133678 : bool has_pending_data = false;
276 : :
480 277 [ + + ]: 133678 : if (!pgstat_tracks_backend_bktype(MyBackendType))
278 : 43306 : return false;
279 : :
280 : : /* Some IO data pending? */
412 281 [ + + + + ]: 90372 : if ((flags & PGSTAT_BACKEND_FLUSH_IO) && backend_has_iostats)
420 282 : 54146 : has_pending_data = true;
283 : :
284 : : /* Some WAL data pending? */
285 [ + + + + ]: 139320 : if ((flags & PGSTAT_BACKEND_FLUSH_WAL) &&
286 : 48948 : pgstat_backend_wal_have_pending())
287 : 10978 : has_pending_data = true;
288 : :
289 [ + + ]: 90372 : if (!has_pending_data)
480 290 : 36020 : return false;
291 : :
469 292 : 54352 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
293 : : MyProcNumber, nowait);
294 [ - + ]: 54352 : if (!entry_ref)
469 michael@paquier.xyz 295 :UBC 0 : return true;
296 : :
297 : : /* Flush requested statistics */
480 michael@paquier.xyz 298 [ + - ]:CBC 54352 : if (flags & PGSTAT_BACKEND_FLUSH_IO)
299 : 54352 : pgstat_flush_backend_entry_io(entry_ref);
300 : :
420 301 [ + + ]: 54352 : if (flags & PGSTAT_BACKEND_FLUSH_WAL)
302 : 21485 : pgstat_flush_backend_entry_wal(entry_ref);
303 : :
502 304 : 54352 : pgstat_unlock_entry(entry_ref);
305 : :
469 306 : 54352 : 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 : 39143 : pgstat_backend_flush_cb(bool nowait)
316 : : {
317 : 39143 : return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
318 : : }
319 : :
320 : : /*
321 : : * Create backend statistics entry for proc number.
322 : : */
323 : : void
502 324 : 18618 : pgstat_create_backend(ProcNumber procnum)
325 : : {
326 : : PgStat_EntryRef *entry_ref;
327 : : PgStatShared_Backend *shstatent;
328 : :
469 329 : 18618 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
330 : : procnum, false);
502 331 : 18618 : 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 : 18618 : memset(&shstatent->stats, 0, sizeof(shstatent->stats));
469 338 : 18618 : pgstat_unlock_entry(entry_ref);
339 : :
340 [ + - + - : 18618 : MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
+ - - + -
- ]
412 341 : 18618 : 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 : : */
420 348 : 18618 : prevBackendWalUsage = pgWalUsage;
502 349 : 18618 : }
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 : 91133943 : 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 [ + + - ]: 91133943 : switch (bktype)
374 : : {
375 : 7895222 : 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 : 7895222 : return false;
387 : :
388 : 83238721 : 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 : 83238721 : return true;
398 : : }
399 : :
502 michael@paquier.xyz 400 :UBC 0 : return false;
401 : : }
402 : :
403 : : void
502 michael@paquier.xyz 404 :CBC 4 : pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
405 : : {
406 : 4 : ((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
407 : 4 : }
|