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