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 6 : 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 6 : 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 6 : INSTR_TIME_ADD(PendingBackendStats.pending_io.pending_times[io_object][io_context][io_op],
67 : io_time);
68 :
69 6 : backend_has_iostats = true;
70 6 : pgstat_report_fixed = true;
71 : }
72 :
73 : void
74 134680072 : pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
75 : IOOp io_op, uint32 cnt, uint64 bytes)
76 : {
77 134680072 : if (!pgstat_tracks_backend_bktype(MyBackendType))
78 14532126 : return;
79 :
80 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
81 :
82 120147946 : PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
83 120147946 : PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
84 :
85 120147946 : backend_has_iostats = true;
86 120147946 : 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 307858 : 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 307858 : if (!backend_has_iostats)
178 230 : return;
179 :
180 307628 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
181 307628 : bktype_shstats = &shbackendent->stats.io_stats;
182 307628 : pending_io = PendingBackendStats.pending_io;
183 :
184 1230512 : for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
185 : {
186 5537304 : for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
187 : {
188 41529780 : for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
189 : {
190 : instr_time time;
191 :
192 36915360 : bktype_shstats->counts[io_object][io_context][io_op] +=
193 36915360 : pending_io.counts[io_object][io_context][io_op];
194 36915360 : bktype_shstats->bytes[io_object][io_context][io_op] +=
195 36915360 : pending_io.bytes[io_object][io_context][io_op];
196 36915360 : time = pending_io.pending_times[io_object][io_context][io_op];
197 :
198 36915360 : bktype_shstats->times[io_object][io_context][io_op] +=
199 36915360 : INSTR_TIME_GET_MICROSEC(time);
200 : }
201 : }
202 : }
203 :
204 : /*
205 : * Clear out the statistics buffer, so it can be re-used.
206 : */
207 307628 : MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
208 :
209 307628 : backend_has_iostats = false;
210 : }
211 :
212 : /*
213 : * To determine whether WAL usage happened.
214 : */
215 : static inline bool
216 127764 : pgstat_backend_wal_have_pending(void)
217 : {
218 127764 : 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 37596 : pgstat_flush_backend_entry_wal(PgStat_EntryRef *entry_ref)
227 : {
228 : PgStatShared_Backend *shbackendent;
229 : PgStat_WalCounters *bktype_shstats;
230 37596 : 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 37596 : if (!pgstat_backend_wal_have_pending())
238 20554 : return;
239 :
240 17042 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
241 17042 : 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 17042 : WalUsageAccumDiff(&wal_usage_diff, &pgWalUsage, &prevBackendWalUsage);
248 :
249 : #define WALSTAT_ACC(fld, var_to_add) \
250 : (bktype_shstats->fld += var_to_add.fld)
251 17042 : WALSTAT_ACC(wal_buffers_full, wal_usage_diff);
252 17042 : WALSTAT_ACC(wal_records, wal_usage_diff);
253 17042 : WALSTAT_ACC(wal_fpi, wal_usage_diff);
254 17042 : WALSTAT_ACC(wal_bytes, wal_usage_diff);
255 : #undef WALSTAT_ACC
256 :
257 : /*
258 : * Save the current counters for the subsequent calculation of WAL usage.
259 : */
260 17042 : prevBackendWalUsage = pgWalUsage;
261 : }
262 :
263 : /*
264 : * Flush out locally pending backend statistics
265 : *
266 : * "flags" parameter controls which statistics to flush. Returns true
267 : * if some statistics could not be flushed due to lock contention.
268 : */
269 : bool
270 443784 : pgstat_flush_backend(bool nowait, bits32 flags)
271 : {
272 : PgStat_EntryRef *entry_ref;
273 443784 : bool has_pending_data = false;
274 :
275 443784 : if (!pgstat_tracks_backend_bktype(MyBackendType))
276 65440 : return false;
277 :
278 : /* Some IO data pending? */
279 378344 : if ((flags & PGSTAT_BACKEND_FLUSH_IO) && backend_has_iostats)
280 307628 : has_pending_data = true;
281 :
282 : /* Some WAL data pending? */
283 468512 : if ((flags & PGSTAT_BACKEND_FLUSH_WAL) &&
284 90168 : pgstat_backend_wal_have_pending())
285 17042 : has_pending_data = true;
286 :
287 378344 : if (!has_pending_data)
288 70486 : return false;
289 :
290 307858 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
291 : MyProcNumber, nowait);
292 307858 : if (!entry_ref)
293 0 : return true;
294 :
295 : /* Flush requested statistics */
296 307858 : if (flags & PGSTAT_BACKEND_FLUSH_IO)
297 307858 : pgstat_flush_backend_entry_io(entry_ref);
298 :
299 307858 : if (flags & PGSTAT_BACKEND_FLUSH_WAL)
300 37596 : pgstat_flush_backend_entry_wal(entry_ref);
301 :
302 307858 : pgstat_unlock_entry(entry_ref);
303 :
304 307858 : return false;
305 : }
306 :
307 : /*
308 : * Callback to flush out locally pending backend statistics.
309 : *
310 : * If some stats could not be flushed due to lock contention, return true.
311 : */
312 : bool
313 68556 : pgstat_backend_flush_cb(bool nowait)
314 : {
315 68556 : return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
316 : }
317 :
318 : /*
319 : * Create backend statistics entry for proc number.
320 : */
321 : void
322 36154 : pgstat_create_backend(ProcNumber procnum)
323 : {
324 : PgStat_EntryRef *entry_ref;
325 : PgStatShared_Backend *shstatent;
326 :
327 36154 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
328 : MyProcNumber, false);
329 36154 : shstatent = (PgStatShared_Backend *) entry_ref->shared_stats;
330 :
331 : /*
332 : * NB: need to accept that there might be stats from an older backend,
333 : * e.g. if we previously used this proc number.
334 : */
335 36154 : memset(&shstatent->stats, 0, sizeof(shstatent->stats));
336 36154 : pgstat_unlock_entry(entry_ref);
337 :
338 36154 : MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
339 36154 : backend_has_iostats = false;
340 :
341 : /*
342 : * Initialize prevBackendWalUsage with pgWalUsage so that
343 : * pgstat_backend_flush_cb() can calculate how much pgWalUsage counters
344 : * are increased by subtracting prevBackendWalUsage from pgWalUsage.
345 : */
346 36154 : prevBackendWalUsage = pgWalUsage;
347 36154 : }
348 :
349 : /*
350 : * Backend statistics are not collected for all BackendTypes.
351 : *
352 : * The following BackendTypes do not participate in the backend stats
353 : * subsystem:
354 : * - The same and for the same reasons as in pgstat_tracks_io_bktype().
355 : * - B_BG_WRITER, B_CHECKPOINTER, B_STARTUP and B_AUTOVAC_LAUNCHER because their
356 : * I/O stats are already visible in pg_stat_io and there is only one of those.
357 : *
358 : * Function returns true if BackendType participates in the backend stats
359 : * subsystem and false if it does not.
360 : *
361 : * When adding a new BackendType, also consider adding relevant restrictions to
362 : * pgstat_tracks_io_object() and pgstat_tracks_io_op().
363 : */
364 : bool
365 135168000 : pgstat_tracks_backend_bktype(BackendType bktype)
366 : {
367 : /*
368 : * List every type so that new backend types trigger a warning about
369 : * needing to adjust this switch.
370 : */
371 135168000 : switch (bktype)
372 : {
373 14605490 : case B_INVALID:
374 : case B_AUTOVAC_LAUNCHER:
375 : case B_DEAD_END_BACKEND:
376 : case B_ARCHIVER:
377 : case B_LOGGER:
378 : case B_BG_WRITER:
379 : case B_CHECKPOINTER:
380 : case B_IO_WORKER:
381 : case B_STARTUP:
382 14605490 : return false;
383 :
384 120562510 : case B_AUTOVAC_WORKER:
385 : case B_BACKEND:
386 : case B_BG_WORKER:
387 : case B_STANDALONE_BACKEND:
388 : case B_SLOTSYNC_WORKER:
389 : case B_WAL_RECEIVER:
390 : case B_WAL_SENDER:
391 : case B_WAL_SUMMARIZER:
392 : case B_WAL_WRITER:
393 120562510 : return true;
394 : }
395 :
396 0 : return false;
397 : }
398 :
399 : void
400 6 : pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
401 : {
402 6 : ((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
403 6 : }
|