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" /* 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_buffer(WaitEventBuffer 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
626 noah@leadboat.com 103 :CBC 2147 : WaitEventCustomShmemSize(void)
104 : : {
105 : : Size sz;
106 : :
107 : 2147 : sz = MAXALIGN(sizeof(WaitEventCustomCounterData));
108 : 2147 : sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
109 : : sizeof(WaitEventCustomEntryByInfo)));
110 : 2147 : sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
111 : : sizeof(WaitEventCustomEntryByName)));
944 michael@paquier.xyz 112 : 2147 : return sz;
113 : : }
114 : :
115 : : /*
116 : : * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
117 : : */
118 : : void
626 noah@leadboat.com 119 : 1150 : WaitEventCustomShmemInit(void)
120 : : {
121 : : bool found;
122 : : HASHCTL info;
123 : :
124 : 1150 : WaitEventCustomCounter = (WaitEventCustomCounterData *)
125 : 1150 : ShmemInitStruct("WaitEventCustomCounterData",
126 : : sizeof(WaitEventCustomCounterData), &found);
127 : :
958 michael@paquier.xyz 128 [ + - ]: 1150 : if (!found)
129 : : {
130 : : /* initialize the allocation counter and its spinlock. */
626 noah@leadboat.com 131 : 1150 : WaitEventCustomCounter->nextId = WAIT_EVENT_CUSTOM_INITIAL_ID;
132 : 1150 : SpinLockInit(&WaitEventCustomCounter->mutex);
133 : : }
134 : :
135 : : /* initialize or attach the hash tables to store custom wait events */
136 : 1150 : info.keysize = sizeof(uint32);
137 : 1150 : info.entrysize = sizeof(WaitEventCustomEntryByInfo);
138 : 1150 : WaitEventCustomHashByInfo =
139 : 1150 : 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 */
944 michael@paquier.xyz 146 : 1150 : info.keysize = sizeof(char[NAMEDATALEN]);
626 noah@leadboat.com 147 : 1150 : info.entrysize = sizeof(WaitEventCustomEntryByName);
148 : 1150 : WaitEventCustomHashByName =
149 : 1150 : 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);
958 michael@paquier.xyz 154 : 1150 : }
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
944 163 : 70 : WaitEventExtensionNew(const char *wait_event_name)
164 : : {
626 noah@leadboat.com 165 : 70 : return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
166 : : }
167 : :
168 : : uint32
169 : 63 : WaitEventInjectionPointNew(const char *wait_event_name)
170 : : {
171 : 63 : return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
172 : : }
173 : :
174 : : static uint32
175 : 133 : 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 */
944 michael@paquier.xyz 184 [ - + ]: 133 : if (strlen(wait_event_name) >= NAMEDATALEN)
944 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 : : */
626 noah@leadboat.com 193 :CBC 133 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
194 : : entry_by_name = (WaitEventCustomEntryByName *)
195 : 133 : hash_search(WaitEventCustomHashByName, wait_event_name,
196 : : HASH_FIND, &found);
197 : 133 : LWLockRelease(WaitEventCustomLock);
944 michael@paquier.xyz 198 [ + + ]: 133 : if (found)
199 : : {
200 : : uint32 oldClassId;
201 : :
626 noah@leadboat.com 202 : 80 : oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
203 [ - + ]: 80 : if (oldClassId != classId)
626 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))));
626 noah@leadboat.com 209 :CBC 80 : 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 : 53 : LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
219 : : entry_by_name = (WaitEventCustomEntryByName *)
220 : 53 : hash_search(WaitEventCustomHashByName, wait_event_name,
221 : : HASH_FIND, &found);
944 michael@paquier.xyz 222 [ - + ]: 53 : if (found)
223 : : {
224 : : uint32 oldClassId;
225 : :
626 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 */
626 noah@leadboat.com 238 [ - + ]:CBC 53 : SpinLockAcquire(&WaitEventCustomCounter->mutex);
239 : :
240 [ - + ]: 53 : if (WaitEventCustomCounter->nextId >= WAIT_EVENT_CUSTOM_HASH_MAX_SIZE)
241 : : {
626 noah@leadboat.com 242 :UBC 0 : SpinLockRelease(&WaitEventCustomCounter->mutex);
958 michael@paquier.xyz 243 [ # # ]: 0 : ereport(ERROR,
244 : : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
245 : : errmsg("too many custom wait events"));
246 : : }
247 : :
626 noah@leadboat.com 248 :CBC 53 : eventId = WaitEventCustomCounter->nextId++;
249 : :
250 : 53 : SpinLockRelease(&WaitEventCustomCounter->mutex);
251 : :
252 : : /* Register the new wait event */
253 : 53 : wait_event_info = classId | eventId;
254 : : entry_by_info = (WaitEventCustomEntryByInfo *)
255 : 53 : hash_search(WaitEventCustomHashByInfo, &wait_event_info,
256 : : HASH_ENTER, &found);
944 michael@paquier.xyz 257 [ - + ]: 53 : Assert(!found);
626 noah@leadboat.com 258 : 53 : 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 : 53 : hash_search(WaitEventCustomHashByName, wait_event_name,
263 : : HASH_ENTER, &found);
944 michael@paquier.xyz 264 [ - + ]: 53 : Assert(!found);
626 noah@leadboat.com 265 : 53 : entry_by_name->wait_event_info = wait_event_info;
266 : :
267 : 53 : LWLockRelease(WaitEventCustomLock);
268 : :
269 : 53 : return wait_event_info;
270 : : }
271 : :
272 : : /*
273 : : * Return the name of a custom wait event information.
274 : : */
275 : : static const char *
276 : 126 : GetWaitEventCustomIdentifier(uint32 wait_event_info)
277 : : {
278 : : bool found;
279 : : WaitEventCustomEntryByInfo *entry;
280 : :
281 : : /* Built-in event? */
282 [ - + ]: 126 : if (wait_event_info == PG_WAIT_EXTENSION)
958 michael@paquier.xyz 283 :UBC 0 : return "Extension";
284 : :
285 : : /* It is a user-defined wait event, so lookup hash table. */
626 noah@leadboat.com 286 :CBC 126 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
287 : : entry = (WaitEventCustomEntryByInfo *)
288 : 126 : hash_search(WaitEventCustomHashByInfo, &wait_event_info,
289 : : HASH_FIND, &found);
290 : 126 : LWLockRelease(WaitEventCustomLock);
291 : :
944 michael@paquier.xyz 292 [ - + ]: 126 : if (!entry)
626 noah@leadboat.com 293 [ # # ]:UBC 0 : elog(ERROR,
294 : : "could not find custom name for wait event information %u",
295 : : wait_event_info);
296 : :
944 michael@paquier.xyz 297 :CBC 126 : 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 **
626 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 */
95 michael@paquier.xyz 320 :GNC 8 : waiteventnames = palloc_array(char *, els);
321 : :
322 : : /* Now scan the hash table to copy the data */
626 noah@leadboat.com 323 :CBC 8 : hash_seq_init(&hash_seq, WaitEventCustomHashByName);
324 : :
938 michael@paquier.xyz 325 : 8 : index = 0;
626 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;
938 michael@paquier.xyz 330 : 1 : waiteventnames[index] = pstrdup(hentry->wait_event_name);
331 : 1 : index++;
332 : : }
333 : :
626 noah@leadboat.com 334 : 8 : LWLockRelease(WaitEventCustomLock);
335 : :
938 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
1807 andres@anarazel.de 349 : 21744 : pgstat_set_wait_event_storage(uint32 *wait_event_info)
350 : : {
351 : 21744 : my_wait_event_info = wait_event_info;
352 : 21744 : }
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 : 21744 : pgstat_reset_wait_event_storage(void)
362 : : {
363 : 21744 : my_wait_event_info = &local_my_wait_event_info;
364 : 21744 : }
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 *
1808 373 : 12493 : 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 [ + + ]: 12493 : if (wait_event_info == 0)
380 : 1849 : return NULL;
381 : :
958 michael@paquier.xyz 382 : 10644 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
383 : :
1808 andres@anarazel.de 384 [ + + + + : 10644 : switch (classId)
+ + + + +
+ - ]
385 : : {
386 : 32 : case PG_WAIT_LWLOCK:
387 : 32 : event_type = "LWLock";
388 : 32 : break;
389 : 1207 : case PG_WAIT_LOCK:
390 : 1207 : event_type = "Lock";
391 : 1207 : break;
102 andres@anarazel.de 392 :GNC 4 : case PG_WAIT_BUFFER:
393 : 4 : event_type = "Buffer";
1808 andres@anarazel.de 394 :CBC 4 : break;
395 : 8125 : case PG_WAIT_ACTIVITY:
396 : 8125 : event_type = "Activity";
397 : 8125 : break;
398 : 801 : case PG_WAIT_CLIENT:
399 : 801 : event_type = "Client";
400 : 801 : break;
401 : 46 : case PG_WAIT_EXTENSION:
402 : 46 : event_type = "Extension";
403 : 46 : break;
404 : 236 : case PG_WAIT_IPC:
405 : 236 : event_type = "IPC";
406 : 236 : break;
407 : 31 : case PG_WAIT_TIMEOUT:
408 : 31 : event_type = "Timeout";
409 : 31 : break;
410 : 12 : case PG_WAIT_IO:
411 : 12 : event_type = "IO";
412 : 12 : break;
626 noah@leadboat.com 413 : 150 : case PG_WAIT_INJECTIONPOINT:
414 : 150 : event_type = "InjectionPoint";
415 : 150 : break;
1808 andres@anarazel.de 416 :UBC 0 : default:
417 : 0 : event_type = "???";
418 : 0 : break;
419 : : }
420 : :
1808 andres@anarazel.de 421 :CBC 10644 : 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 : 10437 : 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 [ + + ]: 10437 : if (wait_event_info == 0)
439 : 1294 : return NULL;
440 : :
958 michael@paquier.xyz 441 : 9143 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
442 : 9143 : eventId = wait_event_info & WAIT_EVENT_ID_MASK;
443 : :
1808 andres@anarazel.de 444 [ + + + + : 9143 : switch (classId)
+ + + + +
- ]
445 : : {
446 : 32 : case PG_WAIT_LWLOCK:
447 : 32 : event_name = GetLWLockIdentifier(classId, eventId);
448 : 32 : break;
449 : 13 : case PG_WAIT_LOCK:
450 : 13 : event_name = GetLockNameFromTagType(eventId);
451 : 13 : break;
958 michael@paquier.xyz 452 : 126 : case PG_WAIT_EXTENSION:
453 : : case PG_WAIT_INJECTIONPOINT:
626 noah@leadboat.com 454 : 126 : event_name = GetWaitEventCustomIdentifier(wait_event_info);
958 michael@paquier.xyz 455 : 126 : break;
102 andres@anarazel.de 456 :GNC 4 : case PG_WAIT_BUFFER:
457 : : {
458 : 4 : WaitEventBuffer w = (WaitEventBuffer) wait_event_info;
459 : :
460 : 4 : event_name = pgstat_get_wait_buffer(w);
986 michael@paquier.xyz 461 :CBC 4 : break;
462 : : }
1808 andres@anarazel.de 463 : 8125 : case PG_WAIT_ACTIVITY:
464 : : {
465 : 8125 : WaitEventActivity w = (WaitEventActivity) wait_event_info;
466 : :
467 : 8125 : event_name = pgstat_get_wait_activity(w);
468 : 8125 : break;
469 : : }
470 : 780 : case PG_WAIT_CLIENT:
471 : : {
472 : 780 : WaitEventClient w = (WaitEventClient) wait_event_info;
473 : :
474 : 780 : event_name = pgstat_get_wait_client(w);
475 : 780 : break;
476 : : }
477 : 22 : case PG_WAIT_IPC:
478 : : {
479 : 22 : WaitEventIPC w = (WaitEventIPC) wait_event_info;
480 : :
481 : 22 : event_name = pgstat_get_wait_ipc(w);
482 : 22 : break;
483 : : }
484 : 31 : case PG_WAIT_TIMEOUT:
485 : : {
486 : 31 : WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
487 : :
488 : 31 : event_name = pgstat_get_wait_timeout(w);
489 : 31 : break;
490 : : }
491 : 10 : case PG_WAIT_IO:
492 : : {
493 : 10 : WaitEventIO w = (WaitEventIO) wait_event_info;
494 : :
495 : 10 : event_name = pgstat_get_wait_io(w);
496 : 10 : break;
497 : : }
1808 andres@anarazel.de 498 :UBC 0 : default:
499 : 0 : event_name = "unknown wait event";
500 : 0 : break;
501 : : }
502 : :
1808 andres@anarazel.de 503 :CBC 9143 : return event_name;
504 : : }
505 : :
506 : : #include "utils/pgstat_wait_event.c"
|