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