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