PostgreSQL Source Code git master
wait_event.c
Go to the documentation of this file.
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
31static const char *pgstat_get_wait_activity(WaitEventActivity w);
32static const char *pgstat_get_wait_bufferpin(WaitEventBufferPin w);
33static const char *pgstat_get_wait_client(WaitEventClient w);
34static const char *pgstat_get_wait_ipc(WaitEventIPC w);
35static const char *pgstat_get_wait_timeout(WaitEventTimeout w);
36static const char *pgstat_get_wait_io(WaitEventIO w);
37
38
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 */
63static HTAB *WaitEventCustomHashByInfo; /* find names from infos */
64static 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 */
71{
72 uint32 wait_event_info; /* hash key */
73 char wait_event_name[NAMEDATALEN]; /* custom wait event name */
75
77{
78 char wait_event_name[NAMEDATALEN]; /* hash key */
81
82
83/* dynamic allocation counter for custom wait events */
85{
86 int nextId; /* next ID to assign */
87 slock_t mutex; /* protects the counter */
89
90/* pointer to the shared memory */
92
93/* first event ID of custom wait events */
94#define WAIT_EVENT_CUSTOM_INITIAL_ID 1
95
96static uint32 WaitEventCustomNew(uint32 classId, const char *wait_event_name);
97static const char *GetWaitEventCustomIdentifier(uint32 wait_event_info);
98
99/*
100 * Return the space for dynamic shared hash tables and dynamic allocation counter.
101 */
102Size
104{
105 Size sz;
106
112 return sz;
113}
114
115/*
116 * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
117 */
118void
120{
121 bool found;
122 HASHCTL info;
123
125 ShmemInitStruct("WaitEventCustomCounterData",
126 sizeof(WaitEventCustomCounterData), &found);
127
128 if (!found)
129 {
130 /* initialize the allocation counter and its spinlock. */
133 }
134
135 /* initialize or attach the hash tables to store custom wait events */
136 info.keysize = sizeof(uint32);
139 ShmemInitHash("WaitEventCustom hash by wait event information",
142 &info,
144
145 /* key is a NULL-terminated string */
146 info.keysize = sizeof(char[NAMEDATALEN]);
149 ShmemInitHash("WaitEventCustom hash by name",
152 &info,
154}
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 */
162uint32
163WaitEventExtensionNew(const char *wait_event_name)
164{
165 return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
166}
167
168uint32
169WaitEventInjectionPointNew(const char *wait_event_name)
170{
171 return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
172}
173
174static uint32
175WaitEventCustomNew(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 */
184 if (strlen(wait_event_name) >= NAMEDATALEN)
185 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 */
193 LWLockAcquire(WaitEventCustomLock, LW_SHARED);
194 entry_by_name = (WaitEventCustomEntryByName *)
195 hash_search(WaitEventCustomHashByName, wait_event_name,
196 HASH_FIND, &found);
197 LWLockRelease(WaitEventCustomLock);
198 if (found)
199 {
200 uint32 oldClassId;
201
202 oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
203 if (oldClassId != classId)
206 errmsg("wait event \"%s\" already exists in type \"%s\"",
207 wait_event_name,
209 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 LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
219 entry_by_name = (WaitEventCustomEntryByName *)
220 hash_search(WaitEventCustomHashByName, wait_event_name,
221 HASH_FIND, &found);
222 if (found)
223 {
224 uint32 oldClassId;
225
226 LWLockRelease(WaitEventCustomLock);
227 oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
228 if (oldClassId != classId)
231 errmsg("wait event \"%s\" already exists in type \"%s\"",
232 wait_event_name,
234 return entry_by_name->wait_event_info;
235 }
236
237 /* Allocate a new event Id */
239
241 {
244 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
245 errmsg("too many custom wait events"));
246 }
247
248 eventId = WaitEventCustomCounter->nextId++;
249
251
252 /* Register the new wait event */
253 wait_event_info = classId | eventId;
254 entry_by_info = (WaitEventCustomEntryByInfo *)
255 hash_search(WaitEventCustomHashByInfo, &wait_event_info,
256 HASH_ENTER, &found);
257 Assert(!found);
258 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 hash_search(WaitEventCustomHashByName, wait_event_name,
263 HASH_ENTER, &found);
264 Assert(!found);
265 entry_by_name->wait_event_info = wait_event_info;
266
267 LWLockRelease(WaitEventCustomLock);
268
269 return wait_event_info;
270}
271
272/*
273 * Return the name of a custom wait event information.
274 */
275static const char *
277{
278 bool found;
280
281 /* Built-in event? */
282 if (wait_event_info == PG_WAIT_EXTENSION)
283 return "Extension";
284
285 /* It is a user-defined wait event, so lookup hash table. */
286 LWLockAcquire(WaitEventCustomLock, LW_SHARED);
288 hash_search(WaitEventCustomHashByInfo, &wait_event_info,
289 HASH_FIND, &found);
290 LWLockRelease(WaitEventCustomLock);
291
292 if (!entry)
293 elog(ERROR,
294 "could not find custom name for wait event information %u",
295 wait_event_info);
296
297 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 */
305char **
306GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
307{
308 char **waiteventnames;
310 HASH_SEQ_STATUS hash_seq;
311 int index;
312 int els;
313
314 LWLockAcquire(WaitEventCustomLock, LW_SHARED);
315
316 /* Now we can safely count the number of entries */
318
319 /* Allocate enough space for all entries */
320 waiteventnames = palloc(els * sizeof(char *));
321
322 /* Now scan the hash table to copy the data */
324
325 index = 0;
326 while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
327 {
328 if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
329 continue;
330 waiteventnames[index] = pstrdup(hentry->wait_event_name);
331 index++;
332 }
333
334 LWLockRelease(WaitEventCustomLock);
335
336 *nwaitevents = index;
337 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 */
348void
350{
351 my_wait_event_info = wait_event_info;
352}
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 */
360void
362{
364}
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 */
372const char *
374{
375 uint32 classId;
376 const char *event_type;
377
378 /* report process as not waiting. */
379 if (wait_event_info == 0)
380 return NULL;
381
382 classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
383
384 switch (classId)
385 {
386 case PG_WAIT_LWLOCK:
387 event_type = "LWLock";
388 break;
389 case PG_WAIT_LOCK:
390 event_type = "Lock";
391 break;
393 event_type = "BufferPin";
394 break;
395 case PG_WAIT_ACTIVITY:
396 event_type = "Activity";
397 break;
398 case PG_WAIT_CLIENT:
399 event_type = "Client";
400 break;
402 event_type = "Extension";
403 break;
404 case PG_WAIT_IPC:
405 event_type = "IPC";
406 break;
407 case PG_WAIT_TIMEOUT:
408 event_type = "Timeout";
409 break;
410 case PG_WAIT_IO:
411 event_type = "IO";
412 break;
414 event_type = "InjectionPoint";
415 break;
416 default:
417 event_type = "???";
418 break;
419 }
420
421 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 */
430const char *
432{
433 uint32 classId;
434 uint16 eventId;
435 const char *event_name;
436
437 /* report process as not waiting. */
438 if (wait_event_info == 0)
439 return NULL;
440
441 classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
442 eventId = wait_event_info & WAIT_EVENT_ID_MASK;
443
444 switch (classId)
445 {
446 case PG_WAIT_LWLOCK:
447 event_name = GetLWLockIdentifier(classId, eventId);
448 break;
449 case PG_WAIT_LOCK:
450 event_name = GetLockNameFromTagType(eventId);
451 break;
454 event_name = GetWaitEventCustomIdentifier(wait_event_info);
455 break;
457 {
458 WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
459
460 event_name = pgstat_get_wait_bufferpin(w);
461 break;
462 }
463 case PG_WAIT_ACTIVITY:
464 {
465 WaitEventActivity w = (WaitEventActivity) wait_event_info;
466
467 event_name = pgstat_get_wait_activity(w);
468 break;
469 }
470 case PG_WAIT_CLIENT:
471 {
472 WaitEventClient w = (WaitEventClient) wait_event_info;
473
474 event_name = pgstat_get_wait_client(w);
475 break;
476 }
477 case PG_WAIT_IPC:
478 {
479 WaitEventIPC w = (WaitEventIPC) wait_event_info;
480
481 event_name = pgstat_get_wait_ipc(w);
482 break;
483 }
484 case PG_WAIT_TIMEOUT:
485 {
486 WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
487
488 event_name = pgstat_get_wait_timeout(w);
489 break;
490 }
491 case PG_WAIT_IO:
492 {
493 WaitEventIO w = (WaitEventIO) wait_event_info;
494
495 event_name = pgstat_get_wait_io(w);
496 break;
497 }
498 default:
499 event_name = "unknown wait event";
500 break;
501 }
502
503 return event_name;
504}
505
506#include "pgstat_wait_event.c"
#define MAXALIGN(LEN)
Definition: c.h:782
uint16_t uint16
Definition: c.h:501
uint32_t uint32
Definition: c.h:502
size_t Size
Definition: c.h:576
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1420
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1341
Size hash_estimate_size(long num_entries, Size entrysize)
Definition: dynahash.c:783
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1385
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
Assert(PointerIsAligned(start, uint64))
#define HASH_STRINGS
Definition: hsearch.h:96
@ HASH_FIND
Definition: hsearch.h:113
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
const char * GetLockNameFromTagType(uint16 locktag_type)
Definition: lmgr.c:1340
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1179
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1899
const char * GetLWLockIdentifier(uint32 classId, uint16 eventId)
Definition: lwlock.c:778
@ LW_SHARED
Definition: lwlock.h:115
@ LW_EXCLUSIVE
Definition: lwlock.h:114
char * pstrdup(const char *in)
Definition: mcxt.c:1699
void * palloc(Size size)
Definition: mcxt.c:1317
#define NAMEDATALEN
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
HTAB * ShmemInitHash(const char *name, long init_size, long max_size, HASHCTL *infoP, int hash_flags)
Definition: shmem.c:327
Size add_size(Size s1, Size s2)
Definition: shmem.c:488
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:382
#define SpinLockInit(lock)
Definition: spin.h:57
#define SpinLockRelease(lock)
Definition: spin.h:61
#define SpinLockAcquire(lock)
Definition: spin.h:59
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
Definition: dynahash.c:220
char wait_event_name[NAMEDATALEN]
Definition: wait_event.c:73
char wait_event_name[NAMEDATALEN]
Definition: wait_event.c:78
Definition: type.h:96
#define WAIT_EVENT_CUSTOM_HASH_MAX_SIZE
Definition: wait_event.c:67
struct WaitEventCustomEntryByName WaitEventCustomEntryByName
Size WaitEventCustomShmemSize(void)
Definition: wait_event.c:103
static const char * pgstat_get_wait_timeout(WaitEventTimeout w)
char ** GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
Definition: wait_event.c:306
#define WAIT_EVENT_CUSTOM_HASH_INIT_SIZE
Definition: wait_event.c:66
static WaitEventCustomCounterData * WaitEventCustomCounter
Definition: wait_event.c:91
static const char * pgstat_get_wait_activity(WaitEventActivity w)
const char * pgstat_get_wait_event_type(uint32 wait_event_info)
Definition: wait_event.c:373
static const char * pgstat_get_wait_io(WaitEventIO w)
void pgstat_set_wait_event_storage(uint32 *wait_event_info)
Definition: wait_event.c:349
const char * pgstat_get_wait_event(uint32 wait_event_info)
Definition: wait_event.c:431
static const char * GetWaitEventCustomIdentifier(uint32 wait_event_info)
Definition: wait_event.c:276
static uint32 local_my_wait_event_info
Definition: wait_event.c:39
static const char * pgstat_get_wait_bufferpin(WaitEventBufferPin w)
uint32 WaitEventInjectionPointNew(const char *wait_event_name)
Definition: wait_event.c:169
static HTAB * WaitEventCustomHashByName
Definition: wait_event.c:64
static const char * pgstat_get_wait_client(WaitEventClient w)
struct WaitEventCustomEntryByInfo WaitEventCustomEntryByInfo
static const char * pgstat_get_wait_ipc(WaitEventIPC w)
void pgstat_reset_wait_event_storage(void)
Definition: wait_event.c:361
struct WaitEventCustomCounterData WaitEventCustomCounterData
#define WAIT_EVENT_ID_MASK
Definition: wait_event.c:43
static HTAB * WaitEventCustomHashByInfo
Definition: wait_event.c:63
#define WAIT_EVENT_CLASS_MASK
Definition: wait_event.c:42
uint32 WaitEventExtensionNew(const char *wait_event_name)
Definition: wait_event.c:163
void WaitEventCustomShmemInit(void)
Definition: wait_event.c:119
static uint32 WaitEventCustomNew(uint32 classId, const char *wait_event_name)
Definition: wait_event.c:175
uint32 * my_wait_event_info
Definition: wait_event.c:40
#define WAIT_EVENT_CUSTOM_INITIAL_ID
Definition: wait_event.c:94
#define PG_WAIT_TIMEOUT
Definition: wait_event.h:25
#define PG_WAIT_INJECTIONPOINT
Definition: wait_event.h:27
#define PG_WAIT_LWLOCK
Definition: wait_event.h:18
#define PG_WAIT_BUFFERPIN
Definition: wait_event.h:20
#define PG_WAIT_IPC
Definition: wait_event.h:24
#define PG_WAIT_CLIENT
Definition: wait_event.h:22
#define PG_WAIT_EXTENSION
Definition: wait_event.h:23
#define PG_WAIT_ACTIVITY
Definition: wait_event.h:21
#define PG_WAIT_LOCK
Definition: wait_event.h:19
#define PG_WAIT_IO
Definition: wait_event.h:26