Line data Source code
1 : /* ----------
2 : * wait_event.c
3 : * Wait event reporting infrastructure.
4 : *
5 : * Copyright (c) 2001-2026, PostgreSQL Global Development Group
6 : *
7 : *
8 : * IDENTIFICATION
9 : * src/backend/utils/activity/wait_event.c
10 : *
11 : * NOTES
12 : *
13 : * To make pgstat_report_wait_start() and pgstat_report_wait_end() as
14 : * lightweight as possible, they do not check if shared memory (MyProc
15 : * specifically, where the wait event is stored) is already available. Instead
16 : * we initially set my_wait_event_info to a process local variable, which then
17 : * is redirected to shared memory using pgstat_set_wait_event_storage(). For
18 : * the same reason pgstat_track_activities is not checked - the check adds
19 : * more work than it saves.
20 : *
21 : * ----------
22 : */
23 : #include "postgres.h"
24 :
25 : #include "storage/lmgr.h"
26 : #include "storage/lwlock.h"
27 : #include "storage/shmem.h"
28 : #include "storage/subsystems.h"
29 : #include "storage/spin.h"
30 : #include "utils/wait_event.h"
31 :
32 :
33 : static const char *pgstat_get_wait_activity(WaitEventActivity w);
34 : static const char *pgstat_get_wait_buffer(WaitEventBuffer w);
35 : static const char *pgstat_get_wait_client(WaitEventClient w);
36 : static const char *pgstat_get_wait_ipc(WaitEventIPC w);
37 : static const char *pgstat_get_wait_timeout(WaitEventTimeout w);
38 : static const char *pgstat_get_wait_io(WaitEventIO w);
39 :
40 :
41 : static uint32 local_my_wait_event_info;
42 : uint32 *my_wait_event_info = &local_my_wait_event_info;
43 :
44 : #define WAIT_EVENT_CLASS_MASK 0xFF000000
45 : #define WAIT_EVENT_ID_MASK 0x0000FFFF
46 :
47 : /*
48 : * Hash tables for storing custom wait event ids and their names in
49 : * shared memory.
50 : *
51 : * WaitEventCustomHashByInfo is used to find the name from wait event
52 : * information. Any backend can search it to find custom wait events.
53 : *
54 : * WaitEventCustomHashByName is used to find the wait event information from a
55 : * name. It is used to ensure that no duplicated entries are registered.
56 : *
57 : * For simplicity, we use the same ID counter across types of custom events.
58 : * We could end that anytime the need arises.
59 : *
60 : * The size of the hash table is based on the assumption that usually only a
61 : * handful of entries are needed, but since it's small in absolute terms
62 : * anyway, we leave a generous amount of headroom.
63 : */
64 : static HTAB *WaitEventCustomHashByInfo; /* find names from infos */
65 : static HTAB *WaitEventCustomHashByName; /* find infos from names */
66 :
67 : #define WAIT_EVENT_CUSTOM_HASH_SIZE 128
68 :
69 : /* hash table entries */
70 : typedef struct WaitEventCustomEntryByInfo
71 : {
72 : uint32 wait_event_info; /* hash key */
73 : char wait_event_name[NAMEDATALEN]; /* custom wait event name */
74 : } WaitEventCustomEntryByInfo;
75 :
76 : typedef struct WaitEventCustomEntryByName
77 : {
78 : char wait_event_name[NAMEDATALEN]; /* hash key */
79 : uint32 wait_event_info;
80 : } WaitEventCustomEntryByName;
81 :
82 :
83 : /* dynamic allocation counter for custom wait events */
84 : typedef struct WaitEventCustomCounterData
85 : {
86 : int nextId; /* next ID to assign */
87 : slock_t mutex; /* protects the counter */
88 : } WaitEventCustomCounterData;
89 :
90 : /* pointer to the shared memory */
91 : static WaitEventCustomCounterData *WaitEventCustomCounter;
92 :
93 : /* first event ID of custom wait events */
94 : #define WAIT_EVENT_CUSTOM_INITIAL_ID 1
95 :
96 : static uint32 WaitEventCustomNew(uint32 classId, const char *wait_event_name);
97 : static const char *GetWaitEventCustomIdentifier(uint32 wait_event_info);
98 :
99 : static void WaitEventCustomShmemRequest(void *arg);
100 : static void WaitEventCustomShmemInit(void *arg);
101 :
102 : const ShmemCallbacks WaitEventCustomShmemCallbacks = {
103 : .request_fn = WaitEventCustomShmemRequest,
104 : .init_fn = WaitEventCustomShmemInit,
105 : };
106 :
107 : /*
108 : * Register shmem space for dynamic shared hash and dynamic allocation counter.
109 : */
110 : static void
111 1234 : WaitEventCustomShmemRequest(void *arg)
112 : {
113 1234 : ShmemRequestStruct(.name = "WaitEventCustomCounterData",
114 : .size = sizeof(WaitEventCustomCounterData),
115 : .ptr = (void **) &WaitEventCustomCounter,
116 : );
117 1234 : ShmemRequestHash(.name = "WaitEventCustom hash by wait event information",
118 : .ptr = &WaitEventCustomHashByInfo,
119 : .nelems = WAIT_EVENT_CUSTOM_HASH_SIZE,
120 : .hash_info.keysize = sizeof(uint32),
121 : .hash_info.entrysize = sizeof(WaitEventCustomEntryByInfo),
122 : .hash_flags = HASH_ELEM | HASH_BLOBS,
123 : );
124 1234 : ShmemRequestHash(.name = "WaitEventCustom hash by name",
125 : .ptr = &WaitEventCustomHashByName,
126 : .nelems = WAIT_EVENT_CUSTOM_HASH_SIZE,
127 : /* key is a NULL-terminated string */
128 : .hash_info.keysize = sizeof(char[NAMEDATALEN]),
129 : .hash_info.entrysize = sizeof(WaitEventCustomEntryByName),
130 : .hash_flags = HASH_ELEM | HASH_STRINGS,
131 : );
132 1234 : }
133 :
134 : static void
135 1231 : WaitEventCustomShmemInit(void *arg)
136 : {
137 : /* initialize the allocation counter and its spinlock. */
138 1231 : WaitEventCustomCounter->nextId = WAIT_EVENT_CUSTOM_INITIAL_ID;
139 1231 : SpinLockInit(&WaitEventCustomCounter->mutex);
140 1231 : }
141 :
142 : /*
143 : * Allocate a new event ID and return the wait event info.
144 : *
145 : * If the wait event name is already defined, this does not allocate a new
146 : * entry; it returns the wait event information associated to the name.
147 : */
148 : uint32
149 48 : WaitEventExtensionNew(const char *wait_event_name)
150 : {
151 48 : return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
152 : }
153 :
154 : uint32
155 75 : WaitEventInjectionPointNew(const char *wait_event_name)
156 : {
157 75 : return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
158 : }
159 :
160 : static uint32
161 123 : WaitEventCustomNew(uint32 classId, const char *wait_event_name)
162 : {
163 : uint16 eventId;
164 : bool found;
165 : WaitEventCustomEntryByName *entry_by_name;
166 : WaitEventCustomEntryByInfo *entry_by_info;
167 : uint32 wait_event_info;
168 :
169 : /* Check the limit of the length of the event name */
170 123 : if (strlen(wait_event_name) >= NAMEDATALEN)
171 0 : elog(ERROR,
172 : "cannot use custom wait event string longer than %u characters",
173 : NAMEDATALEN - 1);
174 :
175 : /*
176 : * Check if the wait event info associated to the name is already defined,
177 : * and return it if so.
178 : */
179 123 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
180 : entry_by_name = (WaitEventCustomEntryByName *)
181 123 : hash_search(WaitEventCustomHashByName, wait_event_name,
182 : HASH_FIND, &found);
183 123 : LWLockRelease(WaitEventCustomLock);
184 123 : if (found)
185 : {
186 : uint32 oldClassId;
187 :
188 73 : oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
189 73 : if (oldClassId != classId)
190 0 : ereport(ERROR,
191 : (errcode(ERRCODE_DUPLICATE_OBJECT),
192 : errmsg("wait event \"%s\" already exists in type \"%s\"",
193 : wait_event_name,
194 : pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
195 73 : return entry_by_name->wait_event_info;
196 : }
197 :
198 : /*
199 : * Allocate and register a new wait event. Recheck if the event name
200 : * exists, as it could be possible that a concurrent process has inserted
201 : * one with the same name since the LWLock acquired again here was
202 : * previously released.
203 : */
204 50 : LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
205 : entry_by_name = (WaitEventCustomEntryByName *)
206 50 : hash_search(WaitEventCustomHashByName, wait_event_name,
207 : HASH_FIND, &found);
208 50 : if (found)
209 : {
210 : uint32 oldClassId;
211 :
212 0 : LWLockRelease(WaitEventCustomLock);
213 0 : oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
214 0 : if (oldClassId != classId)
215 0 : ereport(ERROR,
216 : (errcode(ERRCODE_DUPLICATE_OBJECT),
217 : errmsg("wait event \"%s\" already exists in type \"%s\"",
218 : wait_event_name,
219 : pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
220 0 : return entry_by_name->wait_event_info;
221 : }
222 :
223 : /* Allocate a new event Id */
224 50 : SpinLockAcquire(&WaitEventCustomCounter->mutex);
225 :
226 50 : if (WaitEventCustomCounter->nextId >= WAIT_EVENT_CUSTOM_HASH_SIZE)
227 : {
228 0 : SpinLockRelease(&WaitEventCustomCounter->mutex);
229 0 : ereport(ERROR,
230 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
231 : errmsg("too many custom wait events"));
232 : }
233 :
234 50 : eventId = WaitEventCustomCounter->nextId++;
235 :
236 50 : SpinLockRelease(&WaitEventCustomCounter->mutex);
237 :
238 : /* Register the new wait event */
239 50 : wait_event_info = classId | eventId;
240 : entry_by_info = (WaitEventCustomEntryByInfo *)
241 50 : hash_search(WaitEventCustomHashByInfo, &wait_event_info,
242 : HASH_ENTER, &found);
243 : Assert(!found);
244 50 : strlcpy(entry_by_info->wait_event_name, wait_event_name,
245 : sizeof(entry_by_info->wait_event_name));
246 :
247 : entry_by_name = (WaitEventCustomEntryByName *)
248 50 : hash_search(WaitEventCustomHashByName, wait_event_name,
249 : HASH_ENTER, &found);
250 : Assert(!found);
251 50 : entry_by_name->wait_event_info = wait_event_info;
252 :
253 50 : LWLockRelease(WaitEventCustomLock);
254 :
255 50 : return wait_event_info;
256 : }
257 :
258 : /*
259 : * Return the name of a custom wait event information.
260 : */
261 : static const char *
262 146 : GetWaitEventCustomIdentifier(uint32 wait_event_info)
263 : {
264 : bool found;
265 : WaitEventCustomEntryByInfo *entry;
266 :
267 : /* Built-in event? */
268 146 : if (wait_event_info == PG_WAIT_EXTENSION)
269 0 : return "Extension";
270 :
271 : /* It is a user-defined wait event, so lookup hash table. */
272 146 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
273 : entry = (WaitEventCustomEntryByInfo *)
274 146 : hash_search(WaitEventCustomHashByInfo, &wait_event_info,
275 : HASH_FIND, &found);
276 146 : LWLockRelease(WaitEventCustomLock);
277 :
278 146 : if (!entry)
279 0 : elog(ERROR,
280 : "could not find custom name for wait event information %u",
281 : wait_event_info);
282 :
283 146 : return entry->wait_event_name;
284 : }
285 :
286 :
287 : /*
288 : * Returns a list of currently defined custom wait event names. The result is
289 : * a palloc'd array, with the number of elements saved in *nwaitevents.
290 : */
291 : char **
292 10 : GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
293 : {
294 : char **waiteventnames;
295 : WaitEventCustomEntryByName *hentry;
296 : HASH_SEQ_STATUS hash_seq;
297 : int index;
298 : int els;
299 :
300 10 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
301 :
302 : /* Now we can safely count the number of entries */
303 10 : els = hash_get_num_entries(WaitEventCustomHashByName);
304 :
305 : /* Allocate enough space for all entries */
306 10 : waiteventnames = palloc_array(char *, els);
307 :
308 : /* Now scan the hash table to copy the data */
309 10 : hash_seq_init(&hash_seq, WaitEventCustomHashByName);
310 :
311 10 : index = 0;
312 12 : while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
313 : {
314 2 : if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
315 1 : continue;
316 1 : waiteventnames[index] = pstrdup(hentry->wait_event_name);
317 1 : index++;
318 : }
319 :
320 10 : LWLockRelease(WaitEventCustomLock);
321 :
322 10 : *nwaitevents = index;
323 10 : return waiteventnames;
324 : }
325 :
326 : /*
327 : * Configure wait event reporting to report wait events to *wait_event_info.
328 : * *wait_event_info needs to be valid until pgstat_reset_wait_event_storage()
329 : * is called.
330 : *
331 : * Expected to be called during backend startup, to point my_wait_event_info
332 : * into shared memory.
333 : */
334 : void
335 24786 : pgstat_set_wait_event_storage(uint32 *wait_event_info)
336 : {
337 24786 : my_wait_event_info = wait_event_info;
338 24786 : }
339 :
340 : /*
341 : * Reset wait event storage location.
342 : *
343 : * Expected to be called during backend shutdown, before the location set up
344 : * pgstat_set_wait_event_storage() becomes invalid.
345 : */
346 : void
347 24786 : pgstat_reset_wait_event_storage(void)
348 : {
349 24786 : my_wait_event_info = &local_my_wait_event_info;
350 24786 : }
351 :
352 : /* ----------
353 : * pgstat_get_wait_event_type() -
354 : *
355 : * Return a string representing the current wait event type, backend is
356 : * waiting on.
357 : */
358 : const char *
359 9714 : pgstat_get_wait_event_type(uint32 wait_event_info)
360 : {
361 : uint32 classId;
362 : const char *event_type;
363 :
364 : /* report process as not waiting. */
365 9714 : if (wait_event_info == 0)
366 1065 : return NULL;
367 :
368 8649 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
369 :
370 8649 : switch (classId)
371 : {
372 19 : case PG_WAIT_LWLOCK:
373 19 : event_type = "LWLock";
374 19 : break;
375 1261 : case PG_WAIT_LOCK:
376 1261 : event_type = "Lock";
377 1261 : break;
378 4 : case PG_WAIT_BUFFER:
379 4 : event_type = "Buffer";
380 4 : break;
381 6115 : case PG_WAIT_ACTIVITY:
382 6115 : event_type = "Activity";
383 6115 : break;
384 769 : case PG_WAIT_CLIENT:
385 769 : event_type = "Client";
386 769 : break;
387 44 : case PG_WAIT_EXTENSION:
388 44 : event_type = "Extension";
389 44 : break;
390 183 : case PG_WAIT_IPC:
391 183 : event_type = "IPC";
392 183 : break;
393 28 : case PG_WAIT_TIMEOUT:
394 28 : event_type = "Timeout";
395 28 : break;
396 43 : case PG_WAIT_IO:
397 43 : event_type = "IO";
398 43 : break;
399 183 : case PG_WAIT_INJECTIONPOINT:
400 183 : event_type = "InjectionPoint";
401 183 : break;
402 0 : default:
403 0 : event_type = "???";
404 0 : break;
405 : }
406 :
407 8649 : return event_type;
408 : }
409 :
410 : /* ----------
411 : * pgstat_get_wait_event() -
412 : *
413 : * Return a string representing the current wait event, backend is
414 : * waiting on.
415 : */
416 : const char *
417 7994 : pgstat_get_wait_event(uint32 wait_event_info)
418 : {
419 : uint32 classId;
420 : uint16 eventId;
421 : const char *event_name;
422 :
423 : /* report process as not waiting. */
424 7994 : if (wait_event_info == 0)
425 855 : return NULL;
426 :
427 7139 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
428 7139 : eventId = wait_event_info & WAIT_EVENT_ID_MASK;
429 :
430 7139 : switch (classId)
431 : {
432 19 : case PG_WAIT_LWLOCK:
433 19 : event_name = GetLWLockIdentifier(classId, eventId);
434 19 : break;
435 13 : case PG_WAIT_LOCK:
436 13 : event_name = GetLockNameFromTagType(eventId);
437 13 : break;
438 146 : case PG_WAIT_EXTENSION:
439 : case PG_WAIT_INJECTIONPOINT:
440 146 : event_name = GetWaitEventCustomIdentifier(wait_event_info);
441 146 : break;
442 4 : case PG_WAIT_BUFFER:
443 : {
444 4 : WaitEventBuffer w = (WaitEventBuffer) wait_event_info;
445 :
446 4 : event_name = pgstat_get_wait_buffer(w);
447 4 : break;
448 : }
449 6115 : case PG_WAIT_ACTIVITY:
450 : {
451 6115 : WaitEventActivity w = (WaitEventActivity) wait_event_info;
452 :
453 6115 : event_name = pgstat_get_wait_activity(w);
454 6115 : break;
455 : }
456 764 : case PG_WAIT_CLIENT:
457 : {
458 764 : WaitEventClient w = (WaitEventClient) wait_event_info;
459 :
460 764 : event_name = pgstat_get_wait_client(w);
461 764 : break;
462 : }
463 24 : case PG_WAIT_IPC:
464 : {
465 24 : WaitEventIPC w = (WaitEventIPC) wait_event_info;
466 :
467 24 : event_name = pgstat_get_wait_ipc(w);
468 24 : break;
469 : }
470 28 : case PG_WAIT_TIMEOUT:
471 : {
472 28 : WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
473 :
474 28 : event_name = pgstat_get_wait_timeout(w);
475 28 : break;
476 : }
477 26 : case PG_WAIT_IO:
478 : {
479 26 : WaitEventIO w = (WaitEventIO) wait_event_info;
480 :
481 26 : event_name = pgstat_get_wait_io(w);
482 26 : break;
483 : }
484 0 : default:
485 0 : event_name = "unknown wait event";
486 0 : break;
487 : }
488 :
489 7139 : return event_name;
490 : }
491 :
492 : #include "utils/pgstat_wait_event.c"
|