nbtree: fix read page recheck typo.
[pgsql.git] / src / backend / utils / activity / wait_event.c
blobbbf59482be19623e959454079a9a78a9564c0042
1 /* ----------
2 * wait_event.c
3 * Wait event reporting infrastructure.
5 * Copyright (c) 2001-2024, PostgreSQL Global Development Group
8 * IDENTIFICATION
9 * src/backend/utils/activity/wait_event.c
11 * NOTES
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.
21 * ----------
23 #include "postgres.h"
25 #include "port/pg_bitutils.h"
26 #include "storage/lmgr.h" /* for GetLockNameFromTagType */
27 #include "storage/lwlock.h" /* for GetLWLockIdentifier */
28 #include "storage/spin.h"
29 #include "utils/wait_event.h"
32 static const char *pgstat_get_wait_activity(WaitEventActivity w);
33 static const char *pgstat_get_wait_bufferpin(WaitEventBufferPin w);
34 static const char *pgstat_get_wait_client(WaitEventClient w);
35 static const char *pgstat_get_wait_ipc(WaitEventIPC w);
36 static const char *pgstat_get_wait_timeout(WaitEventTimeout w);
37 static const char *pgstat_get_wait_io(WaitEventIO w);
40 static uint32 local_my_wait_event_info;
41 uint32 *my_wait_event_info = &local_my_wait_event_info;
43 #define WAIT_EVENT_CLASS_MASK 0xFF000000
44 #define WAIT_EVENT_ID_MASK 0x0000FFFF
47 * Hash tables for storing custom wait event ids and their names in
48 * shared memory.
50 * WaitEventCustomHashByInfo is used to find the name from wait event
51 * information. Any backend can search it to find custom wait events.
53 * WaitEventCustomHashByName is used to find the wait event information from a
54 * name. It is used to ensure that no duplicated entries are registered.
56 * For simplicity, we use the same ID counter across types of custom events.
57 * We could end that anytime the need arises.
59 * The size of the hash table is based on the assumption that
60 * WAIT_EVENT_CUSTOM_HASH_INIT_SIZE is enough for most cases, and it seems
61 * unlikely that the number of entries will reach
62 * WAIT_EVENT_CUSTOM_HASH_MAX_SIZE.
64 static HTAB *WaitEventCustomHashByInfo; /* find names from infos */
65 static HTAB *WaitEventCustomHashByName; /* find infos from names */
67 #define WAIT_EVENT_CUSTOM_HASH_INIT_SIZE 16
68 #define WAIT_EVENT_CUSTOM_HASH_MAX_SIZE 128
70 /* hash table entries */
71 typedef struct WaitEventCustomEntryByInfo
73 uint32 wait_event_info; /* hash key */
74 char wait_event_name[NAMEDATALEN]; /* custom wait event name */
75 } WaitEventCustomEntryByInfo;
77 typedef struct WaitEventCustomEntryByName
79 char wait_event_name[NAMEDATALEN]; /* hash key */
80 uint32 wait_event_info;
81 } WaitEventCustomEntryByName;
84 /* dynamic allocation counter for custom wait events */
85 typedef struct WaitEventCustomCounterData
87 int nextId; /* next ID to assign */
88 slock_t mutex; /* protects the counter */
89 } WaitEventCustomCounterData;
91 /* pointer to the shared memory */
92 static WaitEventCustomCounterData *WaitEventCustomCounter;
94 /* first event ID of custom wait events */
95 #define WAIT_EVENT_CUSTOM_INITIAL_ID 1
97 static uint32 WaitEventCustomNew(uint32 classId, const char *wait_event_name);
98 static const char *GetWaitEventCustomIdentifier(uint32 wait_event_info);
101 * Return the space for dynamic shared hash tables and dynamic allocation counter.
103 Size
104 WaitEventCustomShmemSize(void)
106 Size sz;
108 sz = MAXALIGN(sizeof(WaitEventCustomCounterData));
109 sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
110 sizeof(WaitEventCustomEntryByInfo)));
111 sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
112 sizeof(WaitEventCustomEntryByName)));
113 return sz;
117 * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
119 void
120 WaitEventCustomShmemInit(void)
122 bool found;
123 HASHCTL info;
125 WaitEventCustomCounter = (WaitEventCustomCounterData *)
126 ShmemInitStruct("WaitEventCustomCounterData",
127 sizeof(WaitEventCustomCounterData), &found);
129 if (!found)
131 /* initialize the allocation counter and its spinlock. */
132 WaitEventCustomCounter->nextId = WAIT_EVENT_CUSTOM_INITIAL_ID;
133 SpinLockInit(&WaitEventCustomCounter->mutex);
136 /* initialize or attach the hash tables to store custom wait events */
137 info.keysize = sizeof(uint32);
138 info.entrysize = sizeof(WaitEventCustomEntryByInfo);
139 WaitEventCustomHashByInfo =
140 ShmemInitHash("WaitEventCustom hash by wait event information",
141 WAIT_EVENT_CUSTOM_HASH_INIT_SIZE,
142 WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
143 &info,
144 HASH_ELEM | HASH_BLOBS);
146 /* key is a NULL-terminated string */
147 info.keysize = sizeof(char[NAMEDATALEN]);
148 info.entrysize = sizeof(WaitEventCustomEntryByName);
149 WaitEventCustomHashByName =
150 ShmemInitHash("WaitEventCustom hash by name",
151 WAIT_EVENT_CUSTOM_HASH_INIT_SIZE,
152 WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
153 &info,
154 HASH_ELEM | HASH_STRINGS);
158 * Allocate a new event ID and return the wait event info.
160 * If the wait event name is already defined, this does not allocate a new
161 * entry; it returns the wait event information associated to the name.
163 uint32
164 WaitEventExtensionNew(const char *wait_event_name)
166 return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
169 uint32
170 WaitEventInjectionPointNew(const char *wait_event_name)
172 return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
175 static uint32
176 WaitEventCustomNew(uint32 classId, const char *wait_event_name)
178 uint16 eventId;
179 bool found;
180 WaitEventCustomEntryByName *entry_by_name;
181 WaitEventCustomEntryByInfo *entry_by_info;
182 uint32 wait_event_info;
184 /* Check the limit of the length of the event name */
185 if (strlen(wait_event_name) >= NAMEDATALEN)
186 elog(ERROR,
187 "cannot use custom wait event string longer than %u characters",
188 NAMEDATALEN - 1);
191 * Check if the wait event info associated to the name is already defined,
192 * and return it if so.
194 LWLockAcquire(WaitEventCustomLock, LW_SHARED);
195 entry_by_name = (WaitEventCustomEntryByName *)
196 hash_search(WaitEventCustomHashByName, wait_event_name,
197 HASH_FIND, &found);
198 LWLockRelease(WaitEventCustomLock);
199 if (found)
201 uint32 oldClassId;
203 oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
204 if (oldClassId != classId)
205 ereport(ERROR,
206 (errcode(ERRCODE_DUPLICATE_OBJECT),
207 errmsg("wait event \"%s\" already exists in type \"%s\"",
208 wait_event_name,
209 pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
210 return entry_by_name->wait_event_info;
214 * Allocate and register a new wait event. Recheck if the event name
215 * exists, as it could be possible that a concurrent process has inserted
216 * one with the same name since the LWLock acquired again here was
217 * previously released.
219 LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
220 entry_by_name = (WaitEventCustomEntryByName *)
221 hash_search(WaitEventCustomHashByName, wait_event_name,
222 HASH_FIND, &found);
223 if (found)
225 uint32 oldClassId;
227 LWLockRelease(WaitEventCustomLock);
228 oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
229 if (oldClassId != classId)
230 ereport(ERROR,
231 (errcode(ERRCODE_DUPLICATE_OBJECT),
232 errmsg("wait event \"%s\" already exists in type \"%s\"",
233 wait_event_name,
234 pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
235 return entry_by_name->wait_event_info;
238 /* Allocate a new event Id */
239 SpinLockAcquire(&WaitEventCustomCounter->mutex);
241 if (WaitEventCustomCounter->nextId >= WAIT_EVENT_CUSTOM_HASH_MAX_SIZE)
243 SpinLockRelease(&WaitEventCustomCounter->mutex);
244 ereport(ERROR,
245 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
246 errmsg("too many custom wait events"));
249 eventId = WaitEventCustomCounter->nextId++;
251 SpinLockRelease(&WaitEventCustomCounter->mutex);
253 /* Register the new wait event */
254 wait_event_info = classId | eventId;
255 entry_by_info = (WaitEventCustomEntryByInfo *)
256 hash_search(WaitEventCustomHashByInfo, &wait_event_info,
257 HASH_ENTER, &found);
258 Assert(!found);
259 strlcpy(entry_by_info->wait_event_name, wait_event_name,
260 sizeof(entry_by_info->wait_event_name));
262 entry_by_name = (WaitEventCustomEntryByName *)
263 hash_search(WaitEventCustomHashByName, wait_event_name,
264 HASH_ENTER, &found);
265 Assert(!found);
266 entry_by_name->wait_event_info = wait_event_info;
268 LWLockRelease(WaitEventCustomLock);
270 return wait_event_info;
274 * Return the name of a custom wait event information.
276 static const char *
277 GetWaitEventCustomIdentifier(uint32 wait_event_info)
279 bool found;
280 WaitEventCustomEntryByInfo *entry;
282 /* Built-in event? */
283 if (wait_event_info == PG_WAIT_EXTENSION)
284 return "Extension";
286 /* It is a user-defined wait event, so lookup hash table. */
287 LWLockAcquire(WaitEventCustomLock, LW_SHARED);
288 entry = (WaitEventCustomEntryByInfo *)
289 hash_search(WaitEventCustomHashByInfo, &wait_event_info,
290 HASH_FIND, &found);
291 LWLockRelease(WaitEventCustomLock);
293 if (!entry)
294 elog(ERROR,
295 "could not find custom name for wait event information %u",
296 wait_event_info);
298 return entry->wait_event_name;
303 * Returns a list of currently defined custom wait event names. The result is
304 * a palloc'd array, with the number of elements saved in *nwaitevents.
306 char **
307 GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
309 char **waiteventnames;
310 WaitEventCustomEntryByName *hentry;
311 HASH_SEQ_STATUS hash_seq;
312 int index;
313 int els;
315 LWLockAcquire(WaitEventCustomLock, LW_SHARED);
317 /* Now we can safely count the number of entries */
318 els = hash_get_num_entries(WaitEventCustomHashByName);
320 /* Allocate enough space for all entries */
321 waiteventnames = palloc(els * sizeof(char *));
323 /* Now scan the hash table to copy the data */
324 hash_seq_init(&hash_seq, WaitEventCustomHashByName);
326 index = 0;
327 while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
329 if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
330 continue;
331 waiteventnames[index] = pstrdup(hentry->wait_event_name);
332 index++;
335 LWLockRelease(WaitEventCustomLock);
337 *nwaitevents = index;
338 return waiteventnames;
342 * Configure wait event reporting to report wait events to *wait_event_info.
343 * *wait_event_info needs to be valid until pgstat_reset_wait_event_storage()
344 * is called.
346 * Expected to be called during backend startup, to point my_wait_event_info
347 * into shared memory.
349 void
350 pgstat_set_wait_event_storage(uint32 *wait_event_info)
352 my_wait_event_info = wait_event_info;
356 * Reset wait event storage location.
358 * Expected to be called during backend shutdown, before the location set up
359 * pgstat_set_wait_event_storage() becomes invalid.
361 void
362 pgstat_reset_wait_event_storage(void)
364 my_wait_event_info = &local_my_wait_event_info;
367 /* ----------
368 * pgstat_get_wait_event_type() -
370 * Return a string representing the current wait event type, backend is
371 * waiting on.
373 const char *
374 pgstat_get_wait_event_type(uint32 wait_event_info)
376 uint32 classId;
377 const char *event_type;
379 /* report process as not waiting. */
380 if (wait_event_info == 0)
381 return NULL;
383 classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
385 switch (classId)
387 case PG_WAIT_LWLOCK:
388 event_type = "LWLock";
389 break;
390 case PG_WAIT_LOCK:
391 event_type = "Lock";
392 break;
393 case PG_WAIT_BUFFERPIN:
394 event_type = "BufferPin";
395 break;
396 case PG_WAIT_ACTIVITY:
397 event_type = "Activity";
398 break;
399 case PG_WAIT_CLIENT:
400 event_type = "Client";
401 break;
402 case PG_WAIT_EXTENSION:
403 event_type = "Extension";
404 break;
405 case PG_WAIT_IPC:
406 event_type = "IPC";
407 break;
408 case PG_WAIT_TIMEOUT:
409 event_type = "Timeout";
410 break;
411 case PG_WAIT_IO:
412 event_type = "IO";
413 break;
414 case PG_WAIT_INJECTIONPOINT:
415 event_type = "InjectionPoint";
416 break;
417 default:
418 event_type = "???";
419 break;
422 return event_type;
425 /* ----------
426 * pgstat_get_wait_event() -
428 * Return a string representing the current wait event, backend is
429 * waiting on.
431 const char *
432 pgstat_get_wait_event(uint32 wait_event_info)
434 uint32 classId;
435 uint16 eventId;
436 const char *event_name;
438 /* report process as not waiting. */
439 if (wait_event_info == 0)
440 return NULL;
442 classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
443 eventId = wait_event_info & WAIT_EVENT_ID_MASK;
445 switch (classId)
447 case PG_WAIT_LWLOCK:
448 event_name = GetLWLockIdentifier(classId, eventId);
449 break;
450 case PG_WAIT_LOCK:
451 event_name = GetLockNameFromTagType(eventId);
452 break;
453 case PG_WAIT_EXTENSION:
454 case PG_WAIT_INJECTIONPOINT:
455 event_name = GetWaitEventCustomIdentifier(wait_event_info);
456 break;
457 case PG_WAIT_BUFFERPIN:
459 WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
461 event_name = pgstat_get_wait_bufferpin(w);
462 break;
464 case PG_WAIT_ACTIVITY:
466 WaitEventActivity w = (WaitEventActivity) wait_event_info;
468 event_name = pgstat_get_wait_activity(w);
469 break;
471 case PG_WAIT_CLIENT:
473 WaitEventClient w = (WaitEventClient) wait_event_info;
475 event_name = pgstat_get_wait_client(w);
476 break;
478 case PG_WAIT_IPC:
480 WaitEventIPC w = (WaitEventIPC) wait_event_info;
482 event_name = pgstat_get_wait_ipc(w);
483 break;
485 case PG_WAIT_TIMEOUT:
487 WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
489 event_name = pgstat_get_wait_timeout(w);
490 break;
492 case PG_WAIT_IO:
494 WaitEventIO w = (WaitEventIO) wait_event_info;
496 event_name = pgstat_get_wait_io(w);
497 break;
499 default:
500 event_name = "unknown wait event";
501 break;
504 return event_name;
507 #include "pgstat_wait_event.c"