Age Owner Branch data TLA 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
29 heikki.linnakangas@i 111 :GNC 1244 : WaitEventCustomShmemRequest(void *arg)
112 : : {
113 : 1244 : ShmemRequestStruct(.name = "WaitEventCustomCounterData",
114 : : .size = sizeof(WaitEventCustomCounterData),
115 : : .ptr = (void **) &WaitEventCustomCounter,
116 : : );
117 : 1244 : 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 : 1244 : 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 : : );
1009 michael@paquier.xyz 132 :GIC 1244 : }
133 : :
134 : : static void
29 heikki.linnakangas@i 135 :GNC 1241 : WaitEventCustomShmemInit(void *arg)
136 : : {
137 : : /* initialize the allocation counter and its spinlock. */
138 : 1241 : WaitEventCustomCounter->nextId = WAIT_EVENT_CUSTOM_INITIAL_ID;
139 : 1241 : SpinLockInit(&WaitEventCustomCounter->mutex);
1009 michael@paquier.xyz 140 :CBC 1241 : }
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
995 149 : 66 : WaitEventExtensionNew(const char *wait_event_name)
150 : : {
677 noah@leadboat.com 151 : 66 : return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
152 : : }
153 : :
154 : : uint32
155 : 80 : WaitEventInjectionPointNew(const char *wait_event_name)
156 : : {
157 : 80 : return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
158 : : }
159 : :
160 : : static uint32
161 : 146 : 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 */
995 michael@paquier.xyz 170 [ - + ]: 146 : if (strlen(wait_event_name) >= NAMEDATALEN)
995 michael@paquier.xyz 171 [ # # ]:UBC 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 : : */
677 noah@leadboat.com 179 :CBC 146 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
180 : : entry_by_name = (WaitEventCustomEntryByName *)
181 : 146 : hash_search(WaitEventCustomHashByName, wait_event_name,
182 : : HASH_FIND, &found);
183 : 146 : LWLockRelease(WaitEventCustomLock);
995 michael@paquier.xyz 184 [ + + ]: 146 : if (found)
185 : : {
186 : : uint32 oldClassId;
187 : :
677 noah@leadboat.com 188 : 82 : oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
189 [ - + ]: 82 : if (oldClassId != classId)
677 noah@leadboat.com 190 [ # # ]:UBC 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))));
677 noah@leadboat.com 195 :CBC 82 : 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 : 64 : LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
205 : : entry_by_name = (WaitEventCustomEntryByName *)
206 : 64 : hash_search(WaitEventCustomHashByName, wait_event_name,
207 : : HASH_FIND, &found);
995 michael@paquier.xyz 208 [ - + ]: 64 : if (found)
209 : : {
210 : : uint32 oldClassId;
211 : :
677 noah@leadboat.com 212 :UBC 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 */
677 noah@leadboat.com 224 [ - + ]:CBC 64 : SpinLockAcquire(&WaitEventCustomCounter->mutex);
225 : :
31 heikki.linnakangas@i 226 [ - + ]:GNC 64 : if (WaitEventCustomCounter->nextId >= WAIT_EVENT_CUSTOM_HASH_SIZE)
227 : : {
677 noah@leadboat.com 228 :UBC 0 : SpinLockRelease(&WaitEventCustomCounter->mutex);
1009 michael@paquier.xyz 229 [ # # ]: 0 : ereport(ERROR,
230 : : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
231 : : errmsg("too many custom wait events"));
232 : : }
233 : :
677 noah@leadboat.com 234 :CBC 64 : eventId = WaitEventCustomCounter->nextId++;
235 : :
236 : 64 : SpinLockRelease(&WaitEventCustomCounter->mutex);
237 : :
238 : : /* Register the new wait event */
239 : 64 : wait_event_info = classId | eventId;
240 : : entry_by_info = (WaitEventCustomEntryByInfo *)
241 : 64 : hash_search(WaitEventCustomHashByInfo, &wait_event_info,
242 : : HASH_ENTER, &found);
995 michael@paquier.xyz 243 [ - + ]: 64 : Assert(!found);
677 noah@leadboat.com 244 : 64 : 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 : 64 : hash_search(WaitEventCustomHashByName, wait_event_name,
249 : : HASH_ENTER, &found);
995 michael@paquier.xyz 250 [ - + ]: 64 : Assert(!found);
677 noah@leadboat.com 251 : 64 : entry_by_name->wait_event_info = wait_event_info;
252 : :
253 : 64 : LWLockRelease(WaitEventCustomLock);
254 : :
255 : 64 : return wait_event_info;
256 : : }
257 : :
258 : : /*
259 : : * Return the name of a custom wait event information.
260 : : */
261 : : static const char *
262 : 162 : GetWaitEventCustomIdentifier(uint32 wait_event_info)
263 : : {
264 : : bool found;
265 : : WaitEventCustomEntryByInfo *entry;
266 : :
267 : : /* Built-in event? */
268 [ - + ]: 162 : if (wait_event_info == PG_WAIT_EXTENSION)
1009 michael@paquier.xyz 269 :UBC 0 : return "Extension";
270 : :
271 : : /* It is a user-defined wait event, so lookup hash table. */
677 noah@leadboat.com 272 :CBC 162 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
273 : : entry = (WaitEventCustomEntryByInfo *)
274 : 162 : hash_search(WaitEventCustomHashByInfo, &wait_event_info,
275 : : HASH_FIND, &found);
276 : 162 : LWLockRelease(WaitEventCustomLock);
277 : :
995 michael@paquier.xyz 278 [ - + ]: 162 : if (!entry)
677 noah@leadboat.com 279 [ # # ]:UBC 0 : elog(ERROR,
280 : : "could not find custom name for wait event information %u",
281 : : wait_event_info);
282 : :
995 michael@paquier.xyz 283 :CBC 162 : 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 **
677 noah@leadboat.com 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 */
146 michael@paquier.xyz 306 :GNC 10 : waiteventnames = palloc_array(char *, els);
307 : :
308 : : /* Now scan the hash table to copy the data */
677 noah@leadboat.com 309 :CBC 10 : hash_seq_init(&hash_seq, WaitEventCustomHashByName);
310 : :
989 michael@paquier.xyz 311 : 10 : index = 0;
677 noah@leadboat.com 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;
989 michael@paquier.xyz 316 : 1 : waiteventnames[index] = pstrdup(hentry->wait_event_name);
317 : 1 : index++;
318 : : }
319 : :
677 noah@leadboat.com 320 : 10 : LWLockRelease(WaitEventCustomLock);
321 : :
989 michael@paquier.xyz 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
1858 andres@anarazel.de 335 : 23158 : pgstat_set_wait_event_storage(uint32 *wait_event_info)
336 : : {
337 : 23158 : my_wait_event_info = wait_event_info;
338 : 23158 : }
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 : 23158 : pgstat_reset_wait_event_storage(void)
348 : : {
349 : 23158 : my_wait_event_info = &local_my_wait_event_info;
350 : 23158 : }
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 *
1859 359 : 10459 : 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 [ + + ]: 10459 : if (wait_event_info == 0)
366 : 1763 : return NULL;
367 : :
1009 michael@paquier.xyz 368 : 8696 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
369 : :
1859 andres@anarazel.de 370 [ + + + + : 8696 : switch (classId)
+ + + + +
+ - ]
371 : : {
372 : 15 : case PG_WAIT_LWLOCK:
373 : 15 : event_type = "LWLock";
374 : 15 : break;
375 : 1262 : case PG_WAIT_LOCK:
376 : 1262 : event_type = "Lock";
377 : 1262 : break;
153 andres@anarazel.de 378 :GNC 12 : case PG_WAIT_BUFFER:
379 : 12 : event_type = "Buffer";
1859 andres@anarazel.de 380 :CBC 12 : break;
381 : 6002 : case PG_WAIT_ACTIVITY:
382 : 6002 : event_type = "Activity";
383 : 6002 : break;
384 : 820 : case PG_WAIT_CLIENT:
385 : 820 : event_type = "Client";
386 : 820 : break;
387 : 58 : case PG_WAIT_EXTENSION:
388 : 58 : event_type = "Extension";
389 : 58 : break;
390 : 260 : case PG_WAIT_IPC:
391 : 260 : event_type = "IPC";
392 : 260 : break;
393 : 31 : case PG_WAIT_TIMEOUT:
394 : 31 : event_type = "Timeout";
395 : 31 : break;
396 : 41 : case PG_WAIT_IO:
397 : 41 : event_type = "IO";
398 : 41 : break;
677 noah@leadboat.com 399 : 195 : case PG_WAIT_INJECTIONPOINT:
400 : 195 : event_type = "InjectionPoint";
401 : 195 : break;
1859 andres@anarazel.de 402 :UBC 0 : default:
403 : 0 : event_type = "???";
404 : 0 : break;
405 : : }
406 : :
1859 andres@anarazel.de 407 :CBC 8696 : 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 : 8311 : 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 [ + + ]: 8311 : if (wait_event_info == 0)
425 : 1212 : return NULL;
426 : :
1009 michael@paquier.xyz 427 : 7099 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
428 : 7099 : eventId = wait_event_info & WAIT_EVENT_ID_MASK;
429 : :
1859 andres@anarazel.de 430 [ + + + + : 7099 : switch (classId)
+ + + + +
- ]
431 : : {
432 : 15 : case PG_WAIT_LWLOCK:
433 : 15 : event_name = GetLWLockIdentifier(classId, eventId);
434 : 15 : break;
435 : 13 : case PG_WAIT_LOCK:
436 : 13 : event_name = GetLockNameFromTagType(eventId);
437 : 13 : break;
1009 michael@paquier.xyz 438 : 162 : case PG_WAIT_EXTENSION:
439 : : case PG_WAIT_INJECTIONPOINT:
677 noah@leadboat.com 440 : 162 : event_name = GetWaitEventCustomIdentifier(wait_event_info);
1009 michael@paquier.xyz 441 : 162 : break;
153 andres@anarazel.de 442 :GNC 12 : case PG_WAIT_BUFFER:
443 : : {
444 : 12 : WaitEventBuffer w = (WaitEventBuffer) wait_event_info;
445 : :
446 : 12 : event_name = pgstat_get_wait_buffer(w);
1037 michael@paquier.xyz 447 :CBC 12 : break;
448 : : }
1859 andres@anarazel.de 449 : 6002 : case PG_WAIT_ACTIVITY:
450 : : {
451 : 6002 : WaitEventActivity w = (WaitEventActivity) wait_event_info;
452 : :
453 : 6002 : event_name = pgstat_get_wait_activity(w);
454 : 6002 : break;
455 : : }
456 : 797 : case PG_WAIT_CLIENT:
457 : : {
458 : 797 : WaitEventClient w = (WaitEventClient) wait_event_info;
459 : :
460 : 797 : event_name = pgstat_get_wait_client(w);
461 : 797 : break;
462 : : }
463 : 31 : case PG_WAIT_IPC:
464 : : {
465 : 31 : WaitEventIPC w = (WaitEventIPC) wait_event_info;
466 : :
467 : 31 : event_name = pgstat_get_wait_ipc(w);
468 : 31 : break;
469 : : }
470 : 30 : case PG_WAIT_TIMEOUT:
471 : : {
472 : 30 : WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
473 : :
474 : 30 : event_name = pgstat_get_wait_timeout(w);
475 : 30 : break;
476 : : }
477 : 37 : case PG_WAIT_IO:
478 : : {
479 : 37 : WaitEventIO w = (WaitEventIO) wait_event_info;
480 : :
481 : 37 : event_name = pgstat_get_wait_io(w);
482 : 37 : break;
483 : : }
1859 andres@anarazel.de 484 :UBC 0 : default:
485 : 0 : event_name = "unknown wait event";
486 : 0 : break;
487 : : }
488 : :
1859 andres@anarazel.de 489 :CBC 7099 : return event_name;
490 : : }
491 : :
492 : : #include "utils/pgstat_wait_event.c"
|