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 2 : pgstat_report_fixed = true;
70 : }
71 :
72 : void
73 133276752 : pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
74 : IOOp io_op, uint32 cnt, uint64 bytes)
75 : {
76 133276752 : if (!pgstat_tracks_backend_bktype(MyBackendType))
77 14482718 : return;
78 :
79 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
80 :
81 118794034 : PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
82 118794034 : PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
83 :
84 118794034 : backend_has_iostats = true;
85 118794034 : pgstat_report_fixed = true;
86 : }
87 :
88 : /*
89 : * Returns statistics of a backend by proc number.
90 : */
91 : PgStat_Backend *
92 54 : pgstat_fetch_stat_backend(ProcNumber procNumber)
93 : {
94 : PgStat_Backend *backend_entry;
95 :
96 54 : backend_entry = (PgStat_Backend *) pgstat_fetch_entry(PGSTAT_KIND_BACKEND,
97 : InvalidOid, procNumber);
98 :
99 54 : 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 *
110 66 : 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 66 : proc = BackendPidGetProc(pid);
118 66 : if (bktype)
119 54 : *bktype = B_INVALID;
120 :
121 : /* this could be an auxiliary process */
122 66 : if (!proc)
123 12 : proc = AuxiliaryPidGetProc(pid);
124 :
125 66 : if (!proc)
126 6 : return NULL;
127 :
128 60 : procNumber = GetNumberFromPGProc(proc);
129 :
130 60 : beentry = pgstat_get_beentry_by_proc_number(procNumber);
131 60 : if (!beentry)
132 0 : return NULL;
133 :
134 : /* check if the backend type tracks statistics */
135 60 : if (!pgstat_tracks_backend_bktype(beentry->st_backendType))
136 6 : return NULL;
137 :
138 : /* if PID does not match, leave */
139 54 : if (beentry->st_procpid != pid)
140 0 : return NULL;
141 :
142 54 : if (bktype)
143 42 : *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 : */
149 54 : backend_stats = pgstat_fetch_stat_backend(procNumber);
150 54 : if (!backend_stats)
151 : {
152 0 : if (bktype)
153 0 : *bktype = B_INVALID;
154 0 : return NULL;
155 : }
156 :
157 54 : 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
165 290748 : 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 : */
176 290748 : if (!backend_has_iostats)
177 202 : return;
178 :
179 290546 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
180 290546 : bktype_shstats = &shbackendent->stats.io_stats;
181 290546 : pending_io = PendingBackendStats.pending_io;
182 :
183 1162184 : for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
184 : {
185 5229828 : for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
186 : {
187 39223710 : for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
188 : {
189 : instr_time time;
190 :
191 34865520 : bktype_shstats->counts[io_object][io_context][io_op] +=
192 34865520 : pending_io.counts[io_object][io_context][io_op];
193 34865520 : bktype_shstats->bytes[io_object][io_context][io_op] +=
194 34865520 : pending_io.bytes[io_object][io_context][io_op];
195 34865520 : time = pending_io.pending_times[io_object][io_context][io_op];
196 :
197 34865520 : bktype_shstats->times[io_object][io_context][io_op] +=
198 34865520 : INSTR_TIME_GET_MICROSEC(time);
199 : }
200 : }
201 : }
202 :
203 : /*
204 : * Clear out the statistics buffer, so it can be re-used.
205 : */
206 290546 : MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
207 :
208 290546 : backend_has_iostats = false;
209 : }
210 :
211 : /*
212 : * To determine whether WAL usage happened.
213 : */
214 : static inline bool
215 126964 : pgstat_backend_wal_have_pending(void)
216 : {
217 126964 : 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 37096 : pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
226 : {
227 : PgStatShared_Backend *shbackendent;
228 : PgStat_WalCounters *bktype_shstats;
229 37096 : 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 37096 : if (!pgstat_backend_wal_have_pending())
237 19982 : return;
238 :
239 17114 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
240 17114 : 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 17114 : WalUsageAccumDiff(&wal_usage_diff, &pgWalUsage, &prevBackendWalUsage);
247 :
248 : #define WALSTAT_ACC(fld, var_to_add) \
249 : (bktype_shstats->fld += var_to_add.fld)
250 17114 : WALSTAT_ACC(wal_buffers_full, wal_usage_diff);
251 17114 : WALSTAT_ACC(wal_records, wal_usage_diff);
252 17114 : WALSTAT_ACC(wal_fpi, wal_usage_diff);
253 17114 : 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 17114 : 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
269 427542 : pgstat_flush_backend(bool nowait, bits32 flags)
270 : {
271 : PgStat_EntryRef *entry_ref;
272 427542 : bool has_pending_data = false;
273 :
274 427542 : if (!pgstat_tracks_backend_bktype(MyBackendType))
275 65684 : return false;
276 :
277 : /* Some IO data pending? */
278 361858 : if ((flags & PGSTAT_BACKEND_FLUSH_IO) && backend_has_iostats)
279 290546 : has_pending_data = true;
280 :
281 : /* Some WAL data pending? */
282 451726 : if ((flags & PGSTAT_BACKEND_FLUSH_WAL) &&
283 89868 : pgstat_backend_wal_have_pending())
284 17114 : has_pending_data = true;
285 :
286 361858 : if (!has_pending_data)
287 71110 : return false;
288 :
289 290748 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
290 : MyProcNumber, nowait);
291 290748 : if (!entry_ref)
292 0 : return true;
293 :
294 : /* Flush requested statistics */
295 290748 : if (flags & PGSTAT_BACKEND_FLUSH_IO)
296 290748 : pgstat_flush_backend_entry_io(entry_ref);
297 :
298 290748 : if (flags & PGSTAT_BACKEND_FLUSH_WAL)
299 37096 : pgstat_flush_backend_entry_wal(entry_ref);
300 :
301 290748 : pgstat_unlock_entry(entry_ref);
302 :
303 290748 : 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 67510 : pgstat_backend_flush_cb(bool nowait)
313 : {
314 67510 : return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
315 : }
316 :
317 : /*
318 : * Create backend statistics entry for proc number.
319 : */
320 : void
321 35294 : pgstat_create_backend(ProcNumber procnum)
322 : {
323 : PgStat_EntryRef *entry_ref;
324 : PgStatShared_Backend *shstatent;
325 :
326 35294 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
327 : MyProcNumber, false);
328 35294 : 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 35294 : memset(&shstatent->stats, 0, sizeof(shstatent->stats));
335 35294 : pgstat_unlock_entry(entry_ref);
336 :
337 35294 : MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
338 35294 : 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 : */
345 35294 : prevBackendWalUsage = pgWalUsage;
346 35294 : }
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 133747322 : 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 133747322 : switch (bktype)
371 : {
372 14556074 : 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 14556074 : return false;
382 :
383 119191248 : 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 119191248 : return true;
393 : }
394 :
395 0 : return false;
396 : }
397 :
398 : void
399 6 : pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
400 : {
401 6 : ((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
402 6 : }
|