Age Owner Branch data TLA Line data Source code
1 : : /* ----------
2 : : * wait_event.c
3 : : * Wait event reporting infrastructure.
4 : : *
5 : : * Copyright (c) 2001-2025, 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" /* for GetLockNameFromTagType */
26 : : #include "storage/lwlock.h" /* for GetLWLockIdentifier */
27 : : #include "storage/spin.h"
28 : : #include "utils/wait_event.h"
29 : :
30 : :
31 : : static const char *pgstat_get_wait_activity(WaitEventActivity w);
32 : : static const char *pgstat_get_wait_bufferpin(WaitEventBufferPin w);
33 : : static const char *pgstat_get_wait_client(WaitEventClient w);
34 : : static const char *pgstat_get_wait_ipc(WaitEventIPC w);
35 : : static const char *pgstat_get_wait_timeout(WaitEventTimeout w);
36 : : static const char *pgstat_get_wait_io(WaitEventIO w);
37 : :
38 : :
39 : : static uint32 local_my_wait_event_info;
40 : : uint32 *my_wait_event_info = &local_my_wait_event_info;
41 : :
42 : : #define WAIT_EVENT_CLASS_MASK 0xFF000000
43 : : #define WAIT_EVENT_ID_MASK 0x0000FFFF
44 : :
45 : : /*
46 : : * Hash tables for storing custom wait event ids and their names in
47 : : * shared memory.
48 : : *
49 : : * WaitEventCustomHashByInfo is used to find the name from wait event
50 : : * information. Any backend can search it to find custom wait events.
51 : : *
52 : : * WaitEventCustomHashByName is used to find the wait event information from a
53 : : * name. It is used to ensure that no duplicated entries are registered.
54 : : *
55 : : * For simplicity, we use the same ID counter across types of custom events.
56 : : * We could end that anytime the need arises.
57 : : *
58 : : * The size of the hash table is based on the assumption that
59 : : * WAIT_EVENT_CUSTOM_HASH_INIT_SIZE is enough for most cases, and it seems
60 : : * unlikely that the number of entries will reach
61 : : * WAIT_EVENT_CUSTOM_HASH_MAX_SIZE.
62 : : */
63 : : static HTAB *WaitEventCustomHashByInfo; /* find names from infos */
64 : : static HTAB *WaitEventCustomHashByName; /* find infos from names */
65 : :
66 : : #define WAIT_EVENT_CUSTOM_HASH_INIT_SIZE 16
67 : : #define WAIT_EVENT_CUSTOM_HASH_MAX_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 : : /*
100 : : * Return the space for dynamic shared hash tables and dynamic allocation counter.
101 : : */
102 : : Size
436 noah@leadboat.com 103 :CBC 1909 : WaitEventCustomShmemSize(void)
104 : : {
105 : : Size sz;
106 : :
107 : 1909 : sz = MAXALIGN(sizeof(WaitEventCustomCounterData));
108 : 1909 : sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
109 : : sizeof(WaitEventCustomEntryByInfo)));
110 : 1909 : sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
111 : : sizeof(WaitEventCustomEntryByName)));
754 michael@paquier.xyz 112 : 1909 : return sz;
113 : : }
114 : :
115 : : /*
116 : : * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
117 : : */
118 : : void
436 noah@leadboat.com 119 : 1029 : WaitEventCustomShmemInit(void)
120 : : {
121 : : bool found;
122 : : HASHCTL info;
123 : :
124 : 1029 : WaitEventCustomCounter = (WaitEventCustomCounterData *)
125 : 1029 : ShmemInitStruct("WaitEventCustomCounterData",
126 : : sizeof(WaitEventCustomCounterData), &found);
127 : :
768 michael@paquier.xyz 128 [ + - ]: 1029 : if (!found)
129 : : {
130 : : /* initialize the allocation counter and its spinlock. */
436 noah@leadboat.com 131 : 1029 : WaitEventCustomCounter->nextId = WAIT_EVENT_CUSTOM_INITIAL_ID;
132 : 1029 : SpinLockInit(&WaitEventCustomCounter->mutex);
133 : : }
134 : :
135 : : /* initialize or attach the hash tables to store custom wait events */
136 : 1029 : info.keysize = sizeof(uint32);
137 : 1029 : info.entrysize = sizeof(WaitEventCustomEntryByInfo);
138 : 1029 : WaitEventCustomHashByInfo =
139 : 1029 : ShmemInitHash("WaitEventCustom hash by wait event information",
140 : : WAIT_EVENT_CUSTOM_HASH_INIT_SIZE,
141 : : WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
142 : : &info,
143 : : HASH_ELEM | HASH_BLOBS);
144 : :
145 : : /* key is a NULL-terminated string */
754 michael@paquier.xyz 146 : 1029 : info.keysize = sizeof(char[NAMEDATALEN]);
436 noah@leadboat.com 147 : 1029 : info.entrysize = sizeof(WaitEventCustomEntryByName);
148 : 1029 : WaitEventCustomHashByName =
149 : 1029 : ShmemInitHash("WaitEventCustom hash by name",
150 : : WAIT_EVENT_CUSTOM_HASH_INIT_SIZE,
151 : : WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
152 : : &info,
153 : : HASH_ELEM | HASH_STRINGS);
768 michael@paquier.xyz 154 : 1029 : }
155 : :
156 : : /*
157 : : * Allocate a new event ID and return the wait event info.
158 : : *
159 : : * If the wait event name is already defined, this does not allocate a new
160 : : * entry; it returns the wait event information associated to the name.
161 : : */
162 : : uint32
754 163 : 59 : WaitEventExtensionNew(const char *wait_event_name)
164 : : {
436 noah@leadboat.com 165 : 59 : return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
166 : : }
167 : :
168 : : uint32
436 noah@leadboat.com 169 :UBC 0 : WaitEventInjectionPointNew(const char *wait_event_name)
170 : : {
171 : 0 : return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
172 : : }
173 : :
174 : : static uint32
436 noah@leadboat.com 175 :CBC 59 : WaitEventCustomNew(uint32 classId, const char *wait_event_name)
176 : : {
177 : : uint16 eventId;
178 : : bool found;
179 : : WaitEventCustomEntryByName *entry_by_name;
180 : : WaitEventCustomEntryByInfo *entry_by_info;
181 : : uint32 wait_event_info;
182 : :
183 : : /* Check the limit of the length of the event name */
754 michael@paquier.xyz 184 [ - + ]: 59 : if (strlen(wait_event_name) >= NAMEDATALEN)
754 michael@paquier.xyz 185 [ # # ]:UBC 0 : elog(ERROR,
186 : : "cannot use custom wait event string longer than %u characters",
187 : : NAMEDATALEN - 1);
188 : :
189 : : /*
190 : : * Check if the wait event info associated to the name is already defined,
191 : : * and return it if so.
192 : : */
436 noah@leadboat.com 193 :CBC 59 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
194 : : entry_by_name = (WaitEventCustomEntryByName *)
195 : 59 : hash_search(WaitEventCustomHashByName, wait_event_name,
196 : : HASH_FIND, &found);
197 : 59 : LWLockRelease(WaitEventCustomLock);
754 michael@paquier.xyz 198 [ + + ]: 59 : if (found)
199 : : {
200 : : uint32 oldClassId;
201 : :
436 noah@leadboat.com 202 : 32 : oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
203 [ - + ]: 32 : if (oldClassId != classId)
436 noah@leadboat.com 204 [ # # ]:UBC 0 : ereport(ERROR,
205 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
206 : : errmsg("wait event \"%s\" already exists in type \"%s\"",
207 : : wait_event_name,
208 : : pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
436 noah@leadboat.com 209 :CBC 32 : return entry_by_name->wait_event_info;
210 : : }
211 : :
212 : : /*
213 : : * Allocate and register a new wait event. Recheck if the event name
214 : : * exists, as it could be possible that a concurrent process has inserted
215 : : * one with the same name since the LWLock acquired again here was
216 : : * previously released.
217 : : */
218 : 27 : LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
219 : : entry_by_name = (WaitEventCustomEntryByName *)
220 : 27 : hash_search(WaitEventCustomHashByName, wait_event_name,
221 : : HASH_FIND, &found);
754 michael@paquier.xyz 222 [ - + ]: 27 : if (found)
223 : : {
224 : : uint32 oldClassId;
225 : :
436 noah@leadboat.com 226 :UBC 0 : LWLockRelease(WaitEventCustomLock);
227 : 0 : oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
228 [ # # ]: 0 : if (oldClassId != classId)
229 [ # # ]: 0 : ereport(ERROR,
230 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
231 : : errmsg("wait event \"%s\" already exists in type \"%s\"",
232 : : wait_event_name,
233 : : pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
234 : 0 : return entry_by_name->wait_event_info;
235 : : }
236 : :
237 : : /* Allocate a new event Id */
436 noah@leadboat.com 238 [ - + ]:CBC 27 : SpinLockAcquire(&WaitEventCustomCounter->mutex);
239 : :
240 [ - + ]: 27 : if (WaitEventCustomCounter->nextId >= WAIT_EVENT_CUSTOM_HASH_MAX_SIZE)
241 : : {
436 noah@leadboat.com 242 :UBC 0 : SpinLockRelease(&WaitEventCustomCounter->mutex);
768 michael@paquier.xyz 243 [ # # ]: 0 : ereport(ERROR,
244 : : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
245 : : errmsg("too many custom wait events"));
246 : : }
247 : :
436 noah@leadboat.com 248 :CBC 27 : eventId = WaitEventCustomCounter->nextId++;
249 : :
250 : 27 : SpinLockRelease(&WaitEventCustomCounter->mutex);
251 : :
252 : : /* Register the new wait event */
253 : 27 : wait_event_info = classId | eventId;
254 : : entry_by_info = (WaitEventCustomEntryByInfo *)
255 : 27 : hash_search(WaitEventCustomHashByInfo, &wait_event_info,
256 : : HASH_ENTER, &found);
754 michael@paquier.xyz 257 [ - + ]: 27 : Assert(!found);
436 noah@leadboat.com 258 : 27 : strlcpy(entry_by_info->wait_event_name, wait_event_name,
259 : : sizeof(entry_by_info->wait_event_name));
260 : :
261 : : entry_by_name = (WaitEventCustomEntryByName *)
262 : 27 : hash_search(WaitEventCustomHashByName, wait_event_name,
263 : : HASH_ENTER, &found);
754 michael@paquier.xyz 264 [ - + ]: 27 : Assert(!found);
436 noah@leadboat.com 265 : 27 : entry_by_name->wait_event_info = wait_event_info;
266 : :
267 : 27 : LWLockRelease(WaitEventCustomLock);
268 : :
269 : 27 : return wait_event_info;
270 : : }
271 : :
272 : : /*
273 : : * Return the name of a custom wait event information.
274 : : */
275 : : static const char *
276 : 26 : GetWaitEventCustomIdentifier(uint32 wait_event_info)
277 : : {
278 : : bool found;
279 : : WaitEventCustomEntryByInfo *entry;
280 : :
281 : : /* Built-in event? */
282 [ - + ]: 26 : if (wait_event_info == PG_WAIT_EXTENSION)
768 michael@paquier.xyz 283 :UBC 0 : return "Extension";
284 : :
285 : : /* It is a user-defined wait event, so lookup hash table. */
436 noah@leadboat.com 286 :CBC 26 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
287 : : entry = (WaitEventCustomEntryByInfo *)
288 : 26 : hash_search(WaitEventCustomHashByInfo, &wait_event_info,
289 : : HASH_FIND, &found);
290 : 26 : LWLockRelease(WaitEventCustomLock);
291 : :
754 michael@paquier.xyz 292 [ - + ]: 26 : if (!entry)
436 noah@leadboat.com 293 [ # # ]:UBC 0 : elog(ERROR,
294 : : "could not find custom name for wait event information %u",
295 : : wait_event_info);
296 : :
754 michael@paquier.xyz 297 :CBC 26 : return entry->wait_event_name;
298 : : }
299 : :
300 : :
301 : : /*
302 : : * Returns a list of currently defined custom wait event names. The result is
303 : : * a palloc'd array, with the number of elements saved in *nwaitevents.
304 : : */
305 : : char **
436 noah@leadboat.com 306 : 8 : GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
307 : : {
308 : : char **waiteventnames;
309 : : WaitEventCustomEntryByName *hentry;
310 : : HASH_SEQ_STATUS hash_seq;
311 : : int index;
312 : : int els;
313 : :
314 : 8 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
315 : :
316 : : /* Now we can safely count the number of entries */
317 : 8 : els = hash_get_num_entries(WaitEventCustomHashByName);
318 : :
319 : : /* Allocate enough space for all entries */
748 michael@paquier.xyz 320 : 8 : waiteventnames = palloc(els * sizeof(char *));
321 : :
322 : : /* Now scan the hash table to copy the data */
436 noah@leadboat.com 323 : 8 : hash_seq_init(&hash_seq, WaitEventCustomHashByName);
324 : :
748 michael@paquier.xyz 325 : 8 : index = 0;
436 noah@leadboat.com 326 [ + + ]: 10 : while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
327 : : {
328 [ + + ]: 2 : if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
329 : 1 : continue;
748 michael@paquier.xyz 330 : 1 : waiteventnames[index] = pstrdup(hentry->wait_event_name);
331 : 1 : index++;
332 : : }
333 : :
436 noah@leadboat.com 334 : 8 : LWLockRelease(WaitEventCustomLock);
335 : :
748 michael@paquier.xyz 336 : 8 : *nwaitevents = index;
337 : 8 : return waiteventnames;
338 : : }
339 : :
340 : : /*
341 : : * Configure wait event reporting to report wait events to *wait_event_info.
342 : : * *wait_event_info needs to be valid until pgstat_reset_wait_event_storage()
343 : : * is called.
344 : : *
345 : : * Expected to be called during backend startup, to point my_wait_event_info
346 : : * into shared memory.
347 : : */
348 : : void
1617 andres@anarazel.de 349 : 18935 : pgstat_set_wait_event_storage(uint32 *wait_event_info)
350 : : {
351 : 18935 : my_wait_event_info = wait_event_info;
352 : 18935 : }
353 : :
354 : : /*
355 : : * Reset wait event storage location.
356 : : *
357 : : * Expected to be called during backend shutdown, before the location set up
358 : : * pgstat_set_wait_event_storage() becomes invalid.
359 : : */
360 : : void
361 : 18935 : pgstat_reset_wait_event_storage(void)
362 : : {
363 : 18935 : my_wait_event_info = &local_my_wait_event_info;
364 : 18935 : }
365 : :
366 : : /* ----------
367 : : * pgstat_get_wait_event_type() -
368 : : *
369 : : * Return a string representing the current wait event type, backend is
370 : : * waiting on.
371 : : */
372 : : const char *
1618 373 : 10690 : pgstat_get_wait_event_type(uint32 wait_event_info)
374 : : {
375 : : uint32 classId;
376 : : const char *event_type;
377 : :
378 : : /* report process as not waiting. */
379 [ + + ]: 10690 : if (wait_event_info == 0)
380 : 1712 : return NULL;
381 : :
768 michael@paquier.xyz 382 : 8978 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
383 : :
1618 andres@anarazel.de 384 [ + + + + : 8978 : switch (classId)
+ + + + +
- - ]
385 : : {
386 : 20 : case PG_WAIT_LWLOCK:
387 : 20 : event_type = "LWLock";
388 : 20 : break;
389 : 1130 : case PG_WAIT_LOCK:
390 : 1130 : event_type = "Lock";
391 : 1130 : break;
796 michael@paquier.xyz 392 : 2 : case PG_WAIT_BUFFERPIN:
1618 andres@anarazel.de 393 : 2 : event_type = "BufferPin";
394 : 2 : break;
395 : 6861 : case PG_WAIT_ACTIVITY:
396 : 6861 : event_type = "Activity";
397 : 6861 : break;
398 : 761 : case PG_WAIT_CLIENT:
399 : 761 : event_type = "Client";
400 : 761 : break;
401 : 26 : case PG_WAIT_EXTENSION:
402 : 26 : event_type = "Extension";
403 : 26 : break;
404 : 132 : case PG_WAIT_IPC:
405 : 132 : event_type = "IPC";
406 : 132 : break;
407 : 32 : case PG_WAIT_TIMEOUT:
408 : 32 : event_type = "Timeout";
409 : 32 : break;
410 : 14 : case PG_WAIT_IO:
411 : 14 : event_type = "IO";
412 : 14 : break;
436 noah@leadboat.com 413 :UBC 0 : case PG_WAIT_INJECTIONPOINT:
414 : 0 : event_type = "InjectionPoint";
415 : 0 : break;
1618 andres@anarazel.de 416 : 0 : default:
417 : 0 : event_type = "???";
418 : 0 : break;
419 : : }
420 : :
1618 andres@anarazel.de 421 :CBC 8978 : return event_type;
422 : : }
423 : :
424 : : /* ----------
425 : : * pgstat_get_wait_event() -
426 : : *
427 : : * Return a string representing the current wait event, backend is
428 : : * waiting on.
429 : : */
430 : : const char *
431 : 8770 : pgstat_get_wait_event(uint32 wait_event_info)
432 : : {
433 : : uint32 classId;
434 : : uint16 eventId;
435 : : const char *event_name;
436 : :
437 : : /* report process as not waiting. */
438 [ + + ]: 8770 : if (wait_event_info == 0)
439 : 1113 : return NULL;
440 : :
768 michael@paquier.xyz 441 : 7657 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
442 : 7657 : eventId = wait_event_info & WAIT_EVENT_ID_MASK;
443 : :
1618 andres@anarazel.de 444 [ + + + + : 7657 : switch (classId)
+ + + + +
- ]
445 : : {
446 : 20 : case PG_WAIT_LWLOCK:
447 : 20 : event_name = GetLWLockIdentifier(classId, eventId);
448 : 20 : break;
449 : 7 : case PG_WAIT_LOCK:
450 : 7 : event_name = GetLockNameFromTagType(eventId);
451 : 7 : break;
768 michael@paquier.xyz 452 : 26 : case PG_WAIT_EXTENSION:
453 : : case PG_WAIT_INJECTIONPOINT:
436 noah@leadboat.com 454 : 26 : event_name = GetWaitEventCustomIdentifier(wait_event_info);
768 michael@paquier.xyz 455 : 26 : break;
796 456 : 2 : case PG_WAIT_BUFFERPIN:
457 : : {
458 : 2 : WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
459 : :
460 : 2 : event_name = pgstat_get_wait_bufferpin(w);
461 : 2 : break;
462 : : }
1618 andres@anarazel.de 463 : 6861 : case PG_WAIT_ACTIVITY:
464 : : {
465 : 6861 : WaitEventActivity w = (WaitEventActivity) wait_event_info;
466 : :
467 : 6861 : event_name = pgstat_get_wait_activity(w);
468 : 6861 : break;
469 : : }
470 : 691 : case PG_WAIT_CLIENT:
471 : : {
472 : 691 : WaitEventClient w = (WaitEventClient) wait_event_info;
473 : :
474 : 691 : event_name = pgstat_get_wait_client(w);
475 : 691 : break;
476 : : }
477 : 9 : case PG_WAIT_IPC:
478 : : {
479 : 9 : WaitEventIPC w = (WaitEventIPC) wait_event_info;
480 : :
481 : 9 : event_name = pgstat_get_wait_ipc(w);
482 : 9 : break;
483 : : }
484 : 32 : case PG_WAIT_TIMEOUT:
485 : : {
486 : 32 : WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
487 : :
488 : 32 : event_name = pgstat_get_wait_timeout(w);
489 : 32 : break;
490 : : }
491 : 9 : case PG_WAIT_IO:
492 : : {
493 : 9 : WaitEventIO w = (WaitEventIO) wait_event_info;
494 : :
495 : 9 : event_name = pgstat_get_wait_io(w);
496 : 9 : break;
497 : : }
1618 andres@anarazel.de 498 :UBC 0 : default:
499 : 0 : event_name = "unknown wait event";
500 : 0 : break;
501 : : }
502 : :
1618 andres@anarazel.de 503 :CBC 7657 : return event_name;
504 : : }
505 : :
506 : : #include "pgstat_wait_event.c"
|