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 2 : 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 2 : 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 2 : INSTR_TIME_ADD(PendingBackendStats.pending_io.pending_times[io_object][io_context][io_op],
66 : io_time);
67 :
68 2 : backend_has_iostats = true;
69 : }
70 :
71 : void
72 125858216 : pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
73 : IOOp io_op, uint32 cnt, uint64 bytes)
74 : {
75 125858216 : if (!pgstat_tracks_backend_bktype(MyBackendType))
76 14277500 : return;
77 :
78 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
79 :
80 111580716 : PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
81 111580716 : PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
82 :
83 111580716 : 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 ensire 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 54 : backend_stats = pgstat_fetch_stat_backend(procNumber);
137 54 : if (!backend_stats)
138 0 : return NULL;
139 :
140 : /* if PID does not match, leave */
141 54 : if (beentry->st_procpid != pid)
142 0 : return NULL;
143 :
144 54 : if (bktype)
145 42 : *bktype = beentry->st_backendType;
146 :
147 54 : return backend_stats;
148 : }
149 :
150 : /*
151 : * Flush out locally pending backend IO statistics. Locking is managed
152 : * by the caller.
153 : */
154 : static void
155 176316 : pgstat_flush_backend_entry_io(PgStat_EntryRef *entry_ref)
156 : {
157 : PgStatShared_Backend *shbackendent;
158 : PgStat_BktypeIO *bktype_shstats;
159 : PgStat_PendingIO pending_io;
160 :
161 : /*
162 : * This function can be called even if nothing at all has happened for IO
163 : * statistics. In this case, avoid unnecessarily modifying the stats
164 : * entry.
165 : */
166 176316 : if (!backend_has_iostats)
167 28 : return;
168 :
169 176288 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
170 176288 : bktype_shstats = &shbackendent->stats.io_stats;
171 176288 : pending_io = PendingBackendStats.pending_io;
172 :
173 705152 : for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
174 : {
175 3173184 : for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
176 : {
177 23798880 : for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
178 : {
179 : instr_time time;
180 :
181 21154560 : bktype_shstats->counts[io_object][io_context][io_op] +=
182 21154560 : pending_io.counts[io_object][io_context][io_op];
183 21154560 : bktype_shstats->bytes[io_object][io_context][io_op] +=
184 21154560 : pending_io.bytes[io_object][io_context][io_op];
185 21154560 : time = pending_io.pending_times[io_object][io_context][io_op];
186 :
187 21154560 : bktype_shstats->times[io_object][io_context][io_op] +=
188 21154560 : INSTR_TIME_GET_MICROSEC(time);
189 : }
190 : }
191 : }
192 :
193 : /*
194 : * Clear out the statistics buffer, so it can be re-used.
195 : */
196 176288 : MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
197 :
198 176288 : backend_has_iostats = false;
199 : }
200 :
201 : /*
202 : * To determine whether WAL usage happened.
203 : */
204 : static inline bool
205 128464 : pgstat_backend_wal_have_pending(void)
206 : {
207 128464 : return (pgWalUsage.wal_records != prevBackendWalUsage.wal_records);
208 : }
209 :
210 : /*
211 : * Flush out locally pending backend WAL statistics. Locking is managed
212 : * by the caller.
213 : */
214 : static void
215 36490 : pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
216 : {
217 : PgStatShared_Backend *shbackendent;
218 : PgStat_WalCounters *bktype_shstats;
219 36490 : WalUsage wal_usage_diff = {0};
220 :
221 : /*
222 : * This function can be called even if nothing at all has happened for WAL
223 : * statistics. In this case, avoid unnecessarily modifying the stats
224 : * entry.
225 : */
226 36490 : if (!pgstat_backend_wal_have_pending())
227 20598 : return;
228 :
229 15892 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
230 15892 : bktype_shstats = &shbackendent->stats.wal_counters;
231 :
232 : /*
233 : * Calculate how much WAL usage counters were increased by subtracting the
234 : * previous counters from the current ones.
235 : */
236 15892 : WalUsageAccumDiff(&wal_usage_diff, &pgWalUsage, &prevBackendWalUsage);
237 :
238 : #define WALSTAT_ACC(fld, var_to_add) \
239 : (bktype_shstats->fld += var_to_add.fld)
240 15892 : WALSTAT_ACC(wal_buffers_full, wal_usage_diff);
241 15892 : WALSTAT_ACC(wal_records, wal_usage_diff);
242 15892 : WALSTAT_ACC(wal_fpi, wal_usage_diff);
243 15892 : WALSTAT_ACC(wal_bytes, wal_usage_diff);
244 : #undef WALSTAT_ACC
245 :
246 : /*
247 : * Save the current counters for the subsequent calculation of WAL usage.
248 : */
249 15892 : prevBackendWalUsage = pgWalUsage;
250 : }
251 :
252 : /*
253 : * Flush out locally pending backend statistics
254 : *
255 : * "flags" parameter controls which statistics to flush. Returns true
256 : * if some statistics could not be flushed due to lock contention.
257 : */
258 : bool
259 291368 : pgstat_flush_backend(bool nowait, bits32 flags)
260 : {
261 : PgStat_EntryRef *entry_ref;
262 291368 : bool has_pending_data = false;
263 :
264 291368 : if (!pgstat_tracks_backend_bktype(MyBackendType))
265 54982 : return false;
266 :
267 : /* Some IO data pending? */
268 236386 : if ((flags & PGSTAT_BACKEND_FLUSH_IO) && backend_has_iostats)
269 176288 : has_pending_data = true;
270 :
271 : /* Some WAL data pending? */
272 320560 : if ((flags & PGSTAT_BACKEND_FLUSH_WAL) &&
273 84174 : pgstat_backend_wal_have_pending())
274 15892 : has_pending_data = true;
275 :
276 236386 : if (!has_pending_data)
277 60070 : return false;
278 :
279 176316 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
280 : MyProcNumber, nowait);
281 176316 : if (!entry_ref)
282 0 : return true;
283 :
284 : /* Flush requested statistics */
285 176316 : if (flags & PGSTAT_BACKEND_FLUSH_IO)
286 176316 : pgstat_flush_backend_entry_io(entry_ref);
287 :
288 176316 : if (flags & PGSTAT_BACKEND_FLUSH_WAL)
289 36490 : pgstat_flush_backend_entry_wal(entry_ref);
290 :
291 176316 : pgstat_unlock_entry(entry_ref);
292 :
293 176316 : return false;
294 : }
295 :
296 : /*
297 : * Check if there are any backend stats waiting for flush.
298 : */
299 : bool
300 17186 : pgstat_backend_have_pending_cb(void)
301 : {
302 17186 : if (!pgstat_tracks_backend_bktype(MyBackendType))
303 8304 : return false;
304 :
305 8882 : return (backend_has_iostats || pgstat_backend_wal_have_pending());
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 68690 : pgstat_backend_flush_cb(bool nowait)
315 : {
316 68690 : return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
317 : }
318 :
319 : /*
320 : * Create backend statistics entry for proc number.
321 : */
322 : void
323 34742 : pgstat_create_backend(ProcNumber procnum)
324 : {
325 : PgStat_EntryRef *entry_ref;
326 : PgStatShared_Backend *shstatent;
327 :
328 34742 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
329 : MyProcNumber, false);
330 34742 : 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 34742 : memset(&shstatent->stats, 0, sizeof(shstatent->stats));
337 34742 : pgstat_unlock_entry(entry_ref);
338 :
339 34742 : MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
340 34742 : 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 34742 : prevBackendWalUsage = pgWalUsage;
348 34742 : }
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 126208842 : 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 126208842 : switch (bktype)
373 : {
374 14348054 : 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 14348054 : return false;
384 :
385 111860788 : 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 111860788 : 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 : }
|