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 "storage/bufmgr.h"
28 : #include "utils/memutils.h"
29 : #include "utils/pgstat_internal.h"
30 :
31 : /*
32 : * Backend statistics counts waiting to be flushed out. These counters may be
33 : * reported within critical sections so we use static memory in order to avoid
34 : * memory allocation.
35 : */
36 : static PgStat_BackendPending PendingBackendStats;
37 :
38 : /*
39 : * Utility routines to report I/O stats for backends, kept here to avoid
40 : * exposing PendingBackendStats to the outside world.
41 : */
42 : void
43 2 : pgstat_count_backend_io_op_time(IOObject io_object, IOContext io_context,
44 : IOOp io_op, instr_time io_time)
45 : {
46 : Assert(track_io_timing);
47 :
48 2 : if (!pgstat_tracks_backend_bktype(MyBackendType))
49 0 : return;
50 :
51 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
52 :
53 2 : INSTR_TIME_ADD(PendingBackendStats.pending_io.pending_times[io_object][io_context][io_op],
54 : io_time);
55 : }
56 :
57 : void
58 122683264 : pgstat_count_backend_io_op(IOObject io_object, IOContext io_context,
59 : IOOp io_op, uint32 cnt, uint64 bytes)
60 : {
61 122683264 : if (!pgstat_tracks_backend_bktype(MyBackendType))
62 14084360 : return;
63 :
64 : Assert(pgstat_tracks_io_op(MyBackendType, io_object, io_context, io_op));
65 :
66 108598904 : PendingBackendStats.pending_io.counts[io_object][io_context][io_op] += cnt;
67 108598904 : PendingBackendStats.pending_io.bytes[io_object][io_context][io_op] += bytes;
68 : }
69 :
70 : /*
71 : * Returns statistics of a backend by proc number.
72 : */
73 : PgStat_Backend *
74 42 : pgstat_fetch_stat_backend(ProcNumber procNumber)
75 : {
76 : PgStat_Backend *backend_entry;
77 :
78 42 : backend_entry = (PgStat_Backend *) pgstat_fetch_entry(PGSTAT_KIND_BACKEND,
79 : InvalidOid, procNumber);
80 :
81 42 : return backend_entry;
82 : }
83 :
84 : /*
85 : * Flush out locally pending backend IO statistics. Locking is managed
86 : * by the caller.
87 : */
88 : static void
89 164134 : pgstat_flush_backend_entry_io(PgStat_EntryRef *entry_ref)
90 : {
91 : PgStatShared_Backend *shbackendent;
92 : PgStat_BktypeIO *bktype_shstats;
93 : PgStat_PendingIO pending_io;
94 :
95 : /*
96 : * This function can be called even if nothing at all has happened for IO
97 : * statistics. In this case, avoid unnecessarily modifying the stats
98 : * entry.
99 : */
100 164134 : if (pg_memory_is_all_zeros(&PendingBackendStats.pending_io,
101 : sizeof(struct PgStat_PendingIO)))
102 0 : return;
103 :
104 164134 : shbackendent = (PgStatShared_Backend *) entry_ref->shared_stats;
105 164134 : bktype_shstats = &shbackendent->stats.io_stats;
106 164134 : pending_io = PendingBackendStats.pending_io;
107 :
108 656536 : for (int io_object = 0; io_object < IOOBJECT_NUM_TYPES; io_object++)
109 : {
110 2954412 : for (int io_context = 0; io_context < IOCONTEXT_NUM_TYPES; io_context++)
111 : {
112 22158090 : for (int io_op = 0; io_op < IOOP_NUM_TYPES; io_op++)
113 : {
114 : instr_time time;
115 :
116 19696080 : bktype_shstats->counts[io_object][io_context][io_op] +=
117 19696080 : pending_io.counts[io_object][io_context][io_op];
118 19696080 : bktype_shstats->bytes[io_object][io_context][io_op] +=
119 19696080 : pending_io.bytes[io_object][io_context][io_op];
120 19696080 : time = pending_io.pending_times[io_object][io_context][io_op];
121 :
122 19696080 : bktype_shstats->times[io_object][io_context][io_op] +=
123 19696080 : INSTR_TIME_GET_MICROSEC(time);
124 : }
125 : }
126 : }
127 :
128 : /*
129 : * Clear out the statistics buffer, so it can be re-used.
130 : */
131 164134 : MemSet(&PendingBackendStats.pending_io, 0, sizeof(PgStat_PendingIO));
132 : }
133 :
134 : /*
135 : * Flush out locally pending backend statistics
136 : *
137 : * "flags" parameter controls which statistics to flush. Returns true
138 : * if some statistics could not be flushed due to lock contention.
139 : */
140 : bool
141 192322 : pgstat_flush_backend(bool nowait, bits32 flags)
142 : {
143 : PgStat_EntryRef *entry_ref;
144 :
145 192322 : if (!pgstat_tracks_backend_bktype(MyBackendType))
146 3716 : return false;
147 :
148 188606 : if (pg_memory_is_all_zeros(&PendingBackendStats,
149 : sizeof(struct PgStat_BackendPending)))
150 24472 : return false;
151 :
152 164134 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
153 : MyProcNumber, nowait);
154 164134 : if (!entry_ref)
155 0 : return true;
156 :
157 : /* Flush requested statistics */
158 164134 : if (flags & PGSTAT_BACKEND_FLUSH_IO)
159 164134 : pgstat_flush_backend_entry_io(entry_ref);
160 :
161 164134 : pgstat_unlock_entry(entry_ref);
162 :
163 164134 : return false;
164 : }
165 :
166 : /*
167 : * Check if there are any backend stats waiting for flush.
168 : */
169 : bool
170 13270 : pgstat_backend_have_pending_cb(void)
171 : {
172 13270 : return (!pg_memory_is_all_zeros(&PendingBackendStats,
173 13270 : sizeof(struct PgStat_BackendPending)));
174 : }
175 :
176 : /*
177 : * Callback to flush out locally pending backend statistics.
178 : *
179 : * If some stats could not be flushed due to lock contention, return true.
180 : */
181 : bool
182 60954 : pgstat_backend_flush_cb(bool nowait)
183 : {
184 60954 : return pgstat_flush_backend(nowait, PGSTAT_BACKEND_FLUSH_ALL);
185 : }
186 :
187 : /*
188 : * Create backend statistics entry for proc number.
189 : */
190 : void
191 30732 : pgstat_create_backend(ProcNumber procnum)
192 : {
193 : PgStat_EntryRef *entry_ref;
194 : PgStatShared_Backend *shstatent;
195 :
196 30732 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_BACKEND, InvalidOid,
197 : MyProcNumber, false);
198 30732 : shstatent = (PgStatShared_Backend *) entry_ref->shared_stats;
199 :
200 : /*
201 : * NB: need to accept that there might be stats from an older backend,
202 : * e.g. if we previously used this proc number.
203 : */
204 30732 : memset(&shstatent->stats, 0, sizeof(shstatent->stats));
205 30732 : pgstat_unlock_entry(entry_ref);
206 :
207 30732 : MemSet(&PendingBackendStats, 0, sizeof(PgStat_BackendPending));
208 30732 : }
209 :
210 : /*
211 : * Backend statistics are not collected for all BackendTypes.
212 : *
213 : * The following BackendTypes do not participate in the backend stats
214 : * subsystem:
215 : * - The same and for the same reasons as in pgstat_tracks_io_bktype().
216 : * - B_BG_WRITER, B_CHECKPOINTER, B_STARTUP and B_AUTOVAC_LAUNCHER because their
217 : * I/O stats are already visible in pg_stat_io and there is only one of those.
218 : *
219 : * Function returns true if BackendType participates in the backend stats
220 : * subsystem and false if it does not.
221 : *
222 : * When adding a new BackendType, also consider adding relevant restrictions to
223 : * pgstat_tracks_io_object() and pgstat_tracks_io_op().
224 : */
225 : bool
226 122910334 : pgstat_tracks_backend_bktype(BackendType bktype)
227 : {
228 : /*
229 : * List every type so that new backend types trigger a warning about
230 : * needing to adjust this switch.
231 : */
232 122910334 : switch (bktype)
233 : {
234 14092090 : case B_INVALID:
235 : case B_AUTOVAC_LAUNCHER:
236 : case B_DEAD_END_BACKEND:
237 : case B_ARCHIVER:
238 : case B_LOGGER:
239 : case B_BG_WRITER:
240 : case B_CHECKPOINTER:
241 : case B_STARTUP:
242 14092090 : return false;
243 :
244 108818244 : case B_AUTOVAC_WORKER:
245 : case B_BACKEND:
246 : case B_BG_WORKER:
247 : case B_STANDALONE_BACKEND:
248 : case B_SLOTSYNC_WORKER:
249 : case B_WAL_RECEIVER:
250 : case B_WAL_SENDER:
251 : case B_WAL_SUMMARIZER:
252 : case B_WAL_WRITER:
253 108818244 : return true;
254 : }
255 :
256 0 : return false;
257 : }
258 :
259 : void
260 6 : pgstat_backend_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
261 : {
262 6 : ((PgStatShared_Backend *) header)->stats.stat_reset_timestamp = ts;
263 6 : }
|