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
56 4 : pgstat_count_backend_io_op_time(IOObject io_object, IOContext io_context,
57 : IOOp io_op, instr_time io_time)
58 : {
59 : Assert(track_io_timing || track_wal_io_timing);
60 :
61 4 : if (!pgstat_tracks_backend_bktype(MyBackendType))
62 0 : return;
63 :
64 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
65 :
66 4 : INSTR_TIME_ADD(PendingBackendStats.pending_io.pending_times[io_object][io_context][io_op],
67 : io_time);
68 :
69 4 : backend_has_iostats = true;
70 4 : pgstat_report_fixed = true;
71 : }
72 :
73 : void
74 136255112 : pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
75 : IOOp io_op, uint32 cnt, uint64 bytes)
76 : {
77 136255112 : if (!pgstat_tracks_backend_bktype(MyBackendType))
78 14514432 : return;
79 :
80 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
81 :
82 121740680 : PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
83 121740680 : PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
84 :
85 121740680 : backend_has_iostats = true;
86 121740680 : pgstat_report_fixed = true;
87 : }
88 :
89 : /*
90 : * Returns statistics of a backend by proc number.
91 : */
92 : PgStat_Backend *
93 54 : pgstat_fetch_stat_backend(ProcNumber procNumber)
94 : {
95 : PgStat_Backend *backend_entry;
96 :
97 54 : backend_entry = (PgStat_Backend *) pgstat_fetch_entry(PGSTAT_KIND_BACKEND,
98 : InvalidOid, procNumber);
99 :
100 54 : 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 *
111 66 : 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 66 : proc = BackendPidGetProc(pid);
119 66 : if (bktype)
120 54 : *bktype = B_INVALID;
121 :
122 : /* this could be an auxiliary process */
123 66 : if (!proc)
124 12 : proc = AuxiliaryPidGetProc(pid);
125 :
126 66 : if (!proc)
127 6 : return NULL;
128 :
129 60 : procNumber = GetNumberFromPGProc(proc);
130 :
131 60 : beentry = pgstat_get_beentry_by_proc_number(procNumber);
132 60 : if (!beentry)
133 0 : return NULL;
134 :
135 : /* check if the backend type tracks statistics */
136 60 : if (!pgstat_tracks_backend_bktype(beentry->st_backendType))
137 6 : return NULL;
138 :
139 : /* if PID does not match, leave */
140 54 : if (beentry->st_procpid != pid)
141 0 : return NULL;
142 :
143 54 : if (bktype)
144 42 : *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 : */
150 54 : backend_stats = pgstat_fetch_stat_backend(procNumber);
151 54 : if (!backend_stats)
152 : {
153 0 : if (bktype)
154 0 : *bktype = B_INVALID;
155 0 : return NULL;
156 : }
157 :
158 54 : 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
166 341118 : 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 : */
177 341118 : if (!backend_has_iostats)
178 196 : return;
179 :
180 340922 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
181 340922 : bktype_shstats = &shbackendent->stats.io_stats;
182 340922 : pending_io = PendingBackendStats.pending_io;
183 :
184 1363688 : for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
185 : {
186 6136596 : for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
187 : {
188 46024470 : for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
189 : {
190 : instr_time time;
191 :
192 40910640 : bktype_shstats->counts[io_object][io_context][io_op] +=
193 40910640 : pending_io.counts[io_object][io_context][io_op];
194 40910640 : bktype_shstats->bytes[io_object][io_context][io_op] +=
195 40910640 : pending_io.bytes[io_object][io_context][io_op];
196 40910640 : time = pending_io.pending_times[io_object][io_context][io_op];
197 :
198 40910640 : bktype_shstats->times[io_object][io_context][io_op] +=
199 40910640 : INSTR_TIME_GET_MICROSEC(time);
200 : }
201 : }
202 : }
203 :
204 : /*
205 : * Clear out the statistics buffer, so it can be re-used.
206 : */
207 340922 : MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
208 :
209 340922 : backend_has_iostats = false;
210 : }
211 :
212 : /*
213 : * To determine whether WAL usage happened.
214 : */
215 : static inline bool
216 137964 : pgstat_backend_wal_have_pending(void)
217 : {
218 137964 : 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 39500 : pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
227 : {
228 : PgStatShared_Backend *shbackendent;
229 : PgStat_WalCounters *bktype_shstats;
230 39500 : 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 39500 : if (!pgstat_backend_wal_have_pending())
238 21600 : return;
239 :
240 17900 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
241 17900 : 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 17900 : WalUsageAccumDiff(&wal_usage_diff, &pgWalUsage, &prevBackendWalUsage);
248 :
249 : #define WALSTAT_ACC(fld, var_to_add) \
250 : (bktype_shstats->fld += var_to_add.fld)
251 17900 : WALSTAT_ACC(wal_buffers_full, wal_usage_diff);
252 17900 : WALSTAT_ACC(wal_records, wal_usage_diff);
253 17900 : WALSTAT_ACC(wal_fpi, wal_usage_diff);
254 17900 : WALSTAT_ACC(wal_bytes, wal_usage_diff);
255 17900 : 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 : */
261 17900 : 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
271 499846 : pgstat_flush_backend(bool nowait, bits32 flags)
272 : {
273 : PgStat_EntryRef *entry_ref;
274 499846 : bool has_pending_data = false;
275 :
276 499846 : if (!pgstat_tracks_backend_bktype(MyBackendType))
277 77072 : return false;
278 :
279 : /* Some IO data pending? */
280 422774 : if ((flags & PGSTAT_BACKEND_FLUSH_IO) && backend_has_iostats)
281 340922 : has_pending_data = true;
282 :
283 : /* Some WAL data pending? */
284 521238 : if ((flags & PGSTAT_BACKEND_FLUSH_WAL) &&
285 98464 : pgstat_backend_wal_have_pending())
286 17900 : has_pending_data = true;
287 :
288 422774 : if (!has_pending_data)
289 81656 : return false;
290 :
291 341118 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
292 : MyProcNumber, nowait);
293 341118 : if (!entry_ref)
294 0 : return true;
295 :
296 : /* Flush requested statistics */
297 341118 : if (flags & PGSTAT_BACKEND_FLUSH_IO)
298 341118 : pgstat_flush_backend_entry_io(entry_ref);
299 :
300 341118 : if (flags & PGSTAT_BACKEND_FLUSH_WAL)
301 39500 : pgstat_flush_backend_entry_wal(entry_ref);
302 :
303 341118 : pgstat_unlock_entry(entry_ref);
304 :
305 341118 : 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 70962 : pgstat_backend_flush_cb(bool nowait)
315 : {
316 70962 : return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
317 : }
318 :
319 : /*
320 : * Create backend statistics entry for proc number.
321 : */
322 : void
323 37034 : pgstat_create_backend(ProcNumber procnum)
324 : {
325 : PgStat_EntryRef *entry_ref;
326 : PgStatShared_Backend *shstatent;
327 :
328 37034 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
329 : MyProcNumber, false);
330 37034 : 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 37034 : memset(&shstatent->stats, 0, sizeof(shstatent->stats));
337 37034 : pgstat_unlock_entry(entry_ref);
338 :
339 37034 : MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
340 37034 : 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 : */
347 37034 : prevBackendWalUsage = pgWalUsage;
348 37034 : }
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 136800050 : 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 136800050 : switch (bktype)
373 : {
374 14599498 : 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 14599498 : return false;
384 :
385 122200552 : 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 122200552 : return true;
395 : }
396 :
397 0 : return false;
398 : }
399 :
400 : void
401 6 : pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
402 : {
403 6 : ((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
404 6 : }
|