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
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 92289823 : pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
75 : IOOp io_op, uint32 cnt, uint64 bytes)
76 : {
77 92289823 : if (!pgstat_tracks_backend_bktype(MyBackendType))
78 7835188 : return;
79 :
80 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
81 :
82 84454635 : PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
83 84454635 : PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
84 :
85 84454635 : backend_has_iostats = true;
86 84454635 : pgstat_report_fixed = true;
87 : }
88 :
89 : /*
90 : * Returns statistics of a backend by proc number.
91 : */
92 : PgStat_Backend *
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 *
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 */
124 44 : if (!proc)
125 8 : proc = AuxiliaryPidGetProc(pid);
126 :
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)
134 0 : return NULL;
135 :
136 : /* check if the backend type tracks statistics */
137 40 : if (!pgstat_tracks_backend_bktype(beentry->st_backendType))
138 4 : return NULL;
139 :
140 : /* if PID does not match, leave */
141 36 : if (beentry->st_procpid != pid)
142 0 : return NULL;
143 :
144 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 : */
151 36 : backend_stats = pgstat_fetch_stat_backend(procNumber);
152 36 : if (!backend_stats)
153 : {
154 0 : if (bktype)
155 0 : *bktype = B_INVALID;
156 0 : return NULL;
157 : }
158 :
159 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
167 166559 : 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 : */
178 166559 : if (!backend_has_iostats)
179 183 : return;
180 :
181 166376 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
182 166376 : bktype_shstats = &shbackendent->stats.io_stats;
183 166376 : pending_io = PendingBackendStats.pending_io;
184 :
185 665504 : for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
186 : {
187 2994768 : for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
188 : {
189 22460760 : for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
190 : {
191 : instr_time time;
192 :
193 19965120 : bktype_shstats->counts[io_object][io_context][io_op] +=
194 19965120 : pending_io.counts[io_object][io_context][io_op];
195 19965120 : bktype_shstats->bytes[io_object][io_context][io_op] +=
196 19965120 : pending_io.bytes[io_object][io_context][io_op];
197 19965120 : time = pending_io.pending_times[io_object][io_context][io_op];
198 :
199 19965120 : bktype_shstats->times[io_object][io_context][io_op] +=
200 19965120 : INSTR_TIME_GET_MICROSEC(time);
201 : }
202 : }
203 : }
204 :
205 : /*
206 : * Clear out the statistics buffer, so it can be re-used.
207 : */
208 166376 : MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
209 :
210 166376 : backend_has_iostats = false;
211 : }
212 :
213 : /*
214 : * To determine whether WAL usage happened.
215 : */
216 : static inline bool
217 74290 : pgstat_backend_wal_have_pending(void)
218 : {
219 74290 : 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 22046 : pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
228 : {
229 : PgStatShared_Backend *shbackendent;
230 : PgStat_WalCounters *bktype_shstats;
231 22046 : 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 22046 : if (!pgstat_backend_wal_have_pending())
239 11351 : return;
240 :
241 10695 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
242 10695 : 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 10695 : WalUsageAccumDiff(&wal_usage_diff, &pgWalUsage, &prevBackendWalUsage);
249 :
250 : #define WALSTAT_ACC(fld, var_to_add) \
251 : (bktype_shstats->fld += var_to_add.fld)
252 10695 : WALSTAT_ACC(wal_buffers_full, wal_usage_diff);
253 10695 : WALSTAT_ACC(wal_records, wal_usage_diff);
254 10695 : WALSTAT_ACC(wal_fpi, wal_usage_diff);
255 10695 : WALSTAT_ACC(wal_bytes, wal_usage_diff);
256 10695 : 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 : */
262 10695 : 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
272 250380 : pgstat_flush_backend(bool nowait, uint32 flags)
273 : {
274 : PgStat_EntryRef *entry_ref;
275 250380 : bool has_pending_data = false;
276 :
277 250380 : if (!pgstat_tracks_backend_bktype(MyBackendType))
278 43080 : return false;
279 :
280 : /* Some IO data pending? */
281 207300 : if ((flags & PGSTAT_BACKEND_FLUSH_IO) && backend_has_iostats)
282 166376 : has_pending_data = true;
283 :
284 : /* Some WAL data pending? */
285 259544 : if ((flags & PGSTAT_BACKEND_FLUSH_WAL) &&
286 52244 : pgstat_backend_wal_have_pending())
287 10695 : has_pending_data = true;
288 :
289 207300 : if (!has_pending_data)
290 40741 : return false;
291 :
292 166559 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
293 : MyProcNumber, nowait);
294 166559 : if (!entry_ref)
295 0 : return true;
296 :
297 : /* Flush requested statistics */
298 166559 : if (flags & PGSTAT_BACKEND_FLUSH_IO)
299 166559 : pgstat_flush_backend_entry_io(entry_ref);
300 :
301 166559 : if (flags & PGSTAT_BACKEND_FLUSH_WAL)
302 22046 : pgstat_flush_backend_entry_wal(entry_ref);
303 :
304 166559 : pgstat_unlock_entry(entry_ref);
305 :
306 166559 : 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 39832 : pgstat_backend_flush_cb(bool nowait)
316 : {
317 39832 : return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
318 : }
319 :
320 : /*
321 : * Create backend statistics entry for proc number.
322 : */
323 : void
324 20228 : pgstat_create_backend(ProcNumber procnum)
325 : {
326 : PgStat_EntryRef *entry_ref;
327 : PgStatShared_Backend *shstatent;
328 :
329 20228 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
330 : procnum, false);
331 20228 : 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 20228 : memset(&shstatent->stats, 0, sizeof(shstatent->stats));
338 20228 : pgstat_unlock_entry(entry_ref);
339 :
340 20228 : MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
341 20228 : 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 : */
348 20228 : prevBackendWalUsage = pgWalUsage;
349 20228 : }
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 92564613 : 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 92564613 : switch (bktype)
374 : {
375 7882406 : 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 7882406 : return false;
387 :
388 84682207 : 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 84682207 : return true;
398 : }
399 :
400 0 : return false;
401 : }
402 :
403 : void
404 4 : pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
405 : {
406 4 : ((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
407 4 : }
|