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 "storage/bufmgr.h"
29 : : #include "storage/proc.h"
30 : : #include "storage/procarray.h"
31 : : #include "utils/memutils.h"
32 : : #include "utils/pgstat_internal.h"
33 : :
34 : : /*
35 : : * Backend statistics counts waiting to be flushed out. These counters may be
36 : : * reported within critical sections so we use static memory in order to avoid
37 : : * memory allocation.
38 : : */
39 : : static PgStat_BackendPending PendingBackendStats;
40 : : static bool backend_has_iostats = false;
41 : :
42 : : /*
43 : : * WAL usage counters saved from pgWalUsage at the previous call to
44 : : * pgstat_flush_backend(). This is used to calculate how much WAL usage
45 : : * happens between pgstat_flush_backend() calls, by subtracting the
46 : : * previous counters from the current ones.
47 : : */
48 : : static WalUsage prevBackendWalUsage;
49 : :
50 : : /*
51 : : * Utility routines to report I/O stats for backends, kept here to avoid
52 : : * exposing PendingBackendStats to the outside world.
53 : : */
54 : : void
228 michael@paquier.xyz 55 :CBC 3 : pgstat_count_backend_io_op_time(IOObject io_object, IOContext io_context,
56 : : IOOp io_op, instr_time io_time)
57 : : {
192 58 [ - + - - ]: 3 : Assert(track_io_timing || track_wal_io_timing);
59 : :
228 60 [ - + ]: 3 : if (!pgstat_tracks_backend_bktype(MyBackendType))
228 michael@paquier.xyz 61 :UBC 0 : return;
62 : :
228 michael@paquier.xyz 63 [ - + ]:CBC 3 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
64 : :
65 : 3 : INSTR_TIME_ADD(PendingBackendStats.pending_io.pending_times[io_object][io_context][io_op],
66 : : io_time);
67 : :
171 68 : 3 : backend_has_iostats = true;
40 69 : 3 : pgstat_report_fixed = true;
70 : : }
71 : :
72 : : void
228 73 : 62329751 : pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
74 : : IOOp io_op, uint32 cnt, uint64 bytes)
75 : : {
76 [ + + ]: 62329751 : if (!pgstat_tracks_backend_bktype(MyBackendType))
77 : 7255993 : return;
78 : :
79 [ - + ]: 55073758 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
80 : :
81 : 55073758 : PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
82 : 55073758 : PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
83 : :
171 84 : 55073758 : backend_has_iostats = true;
40 85 : 55073758 : pgstat_report_fixed = true;
86 : : }
87 : :
88 : : /*
89 : : * Returns statistics of a backend by proc number.
90 : : */
91 : : PgStat_Backend *
261 92 : 27 : pgstat_fetch_stat_backend(ProcNumber procNumber)
93 : : {
94 : : PgStat_Backend *backend_entry;
95 : :
96 : 27 : backend_entry = (PgStat_Backend *) pgstat_fetch_entry(PGSTAT_KIND_BACKEND,
97 : : InvalidOid, procNumber);
98 : :
99 : 27 : return backend_entry;
100 : : }
101 : :
102 : : /*
103 : : * Returns statistics of a backend by pid.
104 : : *
105 : : * This routine includes sanity checks to ensure that the backend exists and
106 : : * is running. "bktype" can be optionally defined to return the BackendType
107 : : * of the backend whose statistics are returned.
108 : : */
109 : : PgStat_Backend *
190 110 : 33 : pgstat_fetch_stat_backend_by_pid(int pid, BackendType *bktype)
111 : : {
112 : : PGPROC *proc;
113 : : PgBackendStatus *beentry;
114 : : ProcNumber procNumber;
115 : : PgStat_Backend *backend_stats;
116 : :
117 : 33 : proc = BackendPidGetProc(pid);
118 [ + + ]: 33 : if (bktype)
119 : 27 : *bktype = B_INVALID;
120 : :
121 : : /* this could be an auxiliary process */
187 122 [ + + ]: 33 : if (!proc)
123 : 6 : proc = AuxiliaryPidGetProc(pid);
124 : :
190 125 [ + + ]: 33 : if (!proc)
126 : 3 : return NULL;
127 : :
128 : 30 : procNumber = GetNumberFromPGProc(proc);
129 : :
130 : 30 : beentry = pgstat_get_beentry_by_proc_number(procNumber);
131 [ - + ]: 30 : if (!beentry)
190 michael@paquier.xyz 132 :UBC 0 : return NULL;
133 : :
134 : : /* check if the backend type tracks statistics */
187 michael@paquier.xyz 135 [ + + ]:CBC 30 : if (!pgstat_tracks_backend_bktype(beentry->st_backendType))
136 : 3 : return NULL;
137 : :
138 : : /* if PID does not match, leave */
190 139 [ - + ]: 27 : if (beentry->st_procpid != pid)
190 michael@paquier.xyz 140 :UBC 0 : return NULL;
141 : :
190 michael@paquier.xyz 142 [ + + ]:CBC 27 : if (bktype)
143 : 21 : *bktype = beentry->st_backendType;
144 : :
145 : : /*
146 : : * Retrieve the entry. Note that "beentry" may be freed depending on the
147 : : * value of stats_fetch_consistency, so do not access it from this point.
148 : : */
152 149 : 27 : backend_stats = pgstat_fetch_stat_backend(procNumber);
150 [ - + ]: 27 : if (!backend_stats)
151 : : {
152 michael@paquier.xyz 152 [ # # ]:UBC 0 : if (bktype)
153 : 0 : *bktype = B_INVALID;
154 : 0 : return NULL;
155 : : }
156 : :
190 michael@paquier.xyz 157 :CBC 27 : return backend_stats;
158 : : }
159 : :
160 : : /*
161 : : * Flush out locally pending backend IO statistics. Locking is managed
162 : : * by the caller.
163 : : */
164 : : static void
239 165 : 42740 : pgstat_flush_backend_entry_io(PgStat_EntryRef *entry_ref)
166 : : {
167 : : PgStatShared_Backend *shbackendent;
168 : : PgStat_BktypeIO *bktype_shstats;
169 : : PgStat_PendingIO pending_io;
170 : :
171 : : /*
172 : : * This function can be called even if nothing at all has happened for IO
173 : : * statistics. In this case, avoid unnecessarily modifying the stats
174 : : * entry.
175 : : */
171 176 [ + + ]: 42740 : if (!backend_has_iostats)
228 177 : 176 : return;
178 : :
239 179 : 42564 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
180 : 42564 : bktype_shstats = &shbackendent->stats.io_stats;
228 181 : 42564 : pending_io = PendingBackendStats.pending_io;
182 : :
261 183 [ + + ]: 170256 : for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
184 : : {
185 [ + + ]: 766152 : for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
186 : : {
187 [ + + ]: 5746140 : for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
188 : : {
189 : : instr_time time;
190 : :
191 : 5107680 : bktype_shstats->counts[io_object][io_context][io_op] +=
228 192 : 5107680 : pending_io.counts[io_object][io_context][io_op];
235 193 : 5107680 : bktype_shstats->bytes[io_object][io_context][io_op] +=
228 194 : 5107680 : pending_io.bytes[io_object][io_context][io_op];
195 : 5107680 : time = pending_io.pending_times[io_object][io_context][io_op];
196 : :
261 197 : 5107680 : bktype_shstats->times[io_object][io_context][io_op] +=
198 : 5107680 : INSTR_TIME_GET_MICROSEC(time);
199 : : }
200 : : }
201 : : }
202 : :
203 : : /*
204 : : * Clear out the statistics buffer, so it can be re-used.
205 : : */
228 206 [ + - + - : 42564 : MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
+ - - + -
- ]
207 : :
171 208 : 42564 : backend_has_iostats = false;
209 : : }
210 : :
211 : : /*
212 : : * To determine whether WAL usage happened.
213 : : */
214 : : static inline bool
179 215 : 55435 : pgstat_backend_wal_have_pending(void)
216 : : {
217 : 55435 : return (pgWalUsage.wal_records != prevBackendWalUsage.wal_records);
218 : : }
219 : :
220 : : /*
221 : : * Flush out locally pending backend WAL statistics. Locking is managed
222 : : * by the caller.
223 : : */
224 : : static void
225 : 16703 : pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
226 : : {
227 : : PgStatShared_Backend *shbackendent;
228 : : PgStat_WalCounters *bktype_shstats;
229 : 16703 : WalUsage wal_usage_diff = {0};
230 : :
231 : : /*
232 : : * This function can be called even if nothing at all has happened for WAL
233 : : * statistics. In this case, avoid unnecessarily modifying the stats
234 : : * entry.
235 : : */
236 [ + + ]: 16703 : if (!pgstat_backend_wal_have_pending())
237 : 8160 : return;
238 : :
239 : 8543 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
240 : 8543 : bktype_shstats = &shbackendent->stats.wal_counters;
241 : :
242 : : /*
243 : : * Calculate how much WAL usage counters were increased by subtracting the
244 : : * previous counters from the current ones.
245 : : */
246 : 8543 : WalUsageAccumDiff(&wal_usage_diff, &pgWalUsage, &prevBackendWalUsage);
247 : :
248 : : #define WALSTAT_ACC(fld, var_to_add) \
249 : : (bktype_shstats->fld += var_to_add.fld)
250 : 8543 : WALSTAT_ACC(wal_buffers_full, wal_usage_diff);
251 : 8543 : WALSTAT_ACC(wal_records, wal_usage_diff);
252 : 8543 : WALSTAT_ACC(wal_fpi, wal_usage_diff);
253 : 8543 : WALSTAT_ACC(wal_bytes, wal_usage_diff);
254 : : #undef WALSTAT_ACC
255 : :
256 : : /*
257 : : * Save the current counters for the subsequent calculation of WAL usage.
258 : : */
259 : 8543 : prevBackendWalUsage = pgWalUsage;
260 : : }
261 : :
262 : : /*
263 : : * Flush out locally pending backend statistics
264 : : *
265 : : * "flags" parameter controls which statistics to flush. Returns true
266 : : * if some statistics could not be flushed due to lock contention.
267 : : */
268 : : bool
228 269 : 103799 : pgstat_flush_backend(bool nowait, bits32 flags)
270 : : {
271 : : PgStat_EntryRef *entry_ref;
179 272 : 103799 : bool has_pending_data = false;
273 : :
239 274 [ + + ]: 103799 : if (!pgstat_tracks_backend_bktype(MyBackendType))
275 : 32244 : return false;
276 : :
277 : : /* Some IO data pending? */
171 278 [ + + + + ]: 71555 : if ((flags & PGSTAT_BACKEND_FLUSH_IO) && backend_has_iostats)
179 279 : 42564 : has_pending_data = true;
280 : :
281 : : /* Some WAL data pending? */
282 [ + + + + ]: 110287 : if ((flags & PGSTAT_BACKEND_FLUSH_WAL) &&
283 : 38732 : pgstat_backend_wal_have_pending())
284 : 8543 : has_pending_data = true;
285 : :
286 [ + + ]: 71555 : if (!has_pending_data)
239 287 : 28815 : return false;
288 : :
228 289 : 42740 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
290 : : MyProcNumber, nowait);
291 [ - + ]: 42740 : if (!entry_ref)
228 michael@paquier.xyz 292 :UBC 0 : return true;
293 : :
294 : : /* Flush requested statistics */
239 michael@paquier.xyz 295 [ + - ]:CBC 42740 : if (flags & PGSTAT_BACKEND_FLUSH_IO)
296 : 42740 : pgstat_flush_backend_entry_io(entry_ref);
297 : :
179 298 [ + + ]: 42740 : if (flags & PGSTAT_BACKEND_FLUSH_WAL)
299 : 16703 : pgstat_flush_backend_entry_wal(entry_ref);
300 : :
261 301 : 42740 : pgstat_unlock_entry(entry_ref);
302 : :
228 303 : 42740 : return false;
304 : : }
305 : :
306 : : /*
307 : : * Callback to flush out locally pending backend statistics.
308 : : *
309 : : * If some stats could not be flushed due to lock contention, return true.
310 : : */
311 : : bool
312 : 30995 : pgstat_backend_flush_cb(bool nowait)
313 : : {
314 : 30995 : return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
315 : : }
316 : :
317 : : /*
318 : : * Create backend statistics entry for proc number.
319 : : */
320 : : void
261 321 : 14887 : pgstat_create_backend(ProcNumber procnum)
322 : : {
323 : : PgStat_EntryRef *entry_ref;
324 : : PgStatShared_Backend *shstatent;
325 : :
228 326 : 14887 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
327 : : MyProcNumber, false);
261 328 : 14887 : shstatent = (PgStatShared_Backend *) entry_ref->shared_stats;
329 : :
330 : : /*
331 : : * NB: need to accept that there might be stats from an older backend,
332 : : * e.g. if we previously used this proc number.
333 : : */
334 : 14887 : memset(&shstatent->stats, 0, sizeof(shstatent->stats));
228 335 : 14887 : pgstat_unlock_entry(entry_ref);
336 : :
337 [ + - + - : 14887 : MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
+ - - + -
- ]
171 338 : 14887 : backend_has_iostats = false;
339 : :
340 : : /*
341 : : * Initialize prevBackendWalUsage with pgWalUsage so that
342 : : * pgstat_backend_flush_cb() can calculate how much pgWalUsage counters
343 : : * are increased by subtracting prevBackendWalUsage from pgWalUsage.
344 : : */
179 345 : 14887 : prevBackendWalUsage = pgWalUsage;
261 346 : 14887 : }
347 : :
348 : : /*
349 : : * Backend statistics are not collected for all BackendTypes.
350 : : *
351 : : * The following BackendTypes do not participate in the backend stats
352 : : * subsystem:
353 : : * - The same and for the same reasons as in pgstat_tracks_io_bktype().
354 : : * - B_BG_WRITER, B_CHECKPOINTER, B_STARTUP and B_AUTOVAC_LAUNCHER because their
355 : : * I/O stats are already visible in pg_stat_io and there is only one of those.
356 : : *
357 : : * Function returns true if BackendType participates in the backend stats
358 : : * subsystem and false if it does not.
359 : : *
360 : : * When adding a new BackendType, also consider adding relevant restrictions to
361 : : * pgstat_tracks_io_object() and pgstat_tracks_io_op().
362 : : */
363 : : bool
364 : 62452094 : pgstat_tracks_backend_bktype(BackendType bktype)
365 : : {
366 : : /*
367 : : * List every type so that new backend types trigger a warning about
368 : : * needing to adjust this switch.
369 : : */
370 [ + + - ]: 62452094 : switch (bktype)
371 : : {
372 : 7291861 : case B_INVALID:
373 : : case B_AUTOVAC_LAUNCHER:
374 : : case B_DEAD_END_BACKEND:
375 : : case B_ARCHIVER:
376 : : case B_LOGGER:
377 : : case B_BG_WRITER:
378 : : case B_CHECKPOINTER:
379 : : case B_IO_WORKER:
380 : : case B_STARTUP:
381 : 7291861 : return false;
382 : :
383 : 55160233 : case B_AUTOVAC_WORKER:
384 : : case B_BACKEND:
385 : : case B_BG_WORKER:
386 : : case B_STANDALONE_BACKEND:
387 : : case B_SLOTSYNC_WORKER:
388 : : case B_WAL_RECEIVER:
389 : : case B_WAL_SENDER:
390 : : case B_WAL_SUMMARIZER:
391 : : case B_WAL_WRITER:
392 : 55160233 : return true;
393 : : }
394 : :
261 michael@paquier.xyz 395 :UBC 0 : return false;
396 : : }
397 : :
398 : : void
261 michael@paquier.xyz 399 :CBC 3 : pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
400 : : {
401 : 3 : ((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
402 : 3 : }
|