3 * Wait event reporting infrastructure.
5 * Copyright (c) 2001-2024, PostgreSQL Global Development Group
9 * src/backend/utils/activity/wait_event.c
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.
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
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.
104 WaitEventCustomShmemSize(void)
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
)));
117 * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
120 WaitEventCustomShmemInit(void)
125 WaitEventCustomCounter
= (WaitEventCustomCounterData
*)
126 ShmemInitStruct("WaitEventCustomCounterData",
127 sizeof(WaitEventCustomCounterData
), &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
,
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
,
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.
164 WaitEventExtensionNew(const char *wait_event_name
)
166 return WaitEventCustomNew(PG_WAIT_EXTENSION
, wait_event_name
);
170 WaitEventInjectionPointNew(const char *wait_event_name
)
172 return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT
, wait_event_name
);
176 WaitEventCustomNew(uint32 classId
, const char *wait_event_name
)
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
)
187 "cannot use custom wait event string longer than %u characters",
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
,
198 LWLockRelease(WaitEventCustomLock
);
203 oldClassId
= entry_by_name
->wait_event_info
& WAIT_EVENT_CLASS_MASK
;
204 if (oldClassId
!= classId
)
206 (errcode(ERRCODE_DUPLICATE_OBJECT
),
207 errmsg("wait event \"%s\" already exists in type \"%s\"",
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
,
227 LWLockRelease(WaitEventCustomLock
);
228 oldClassId
= entry_by_name
->wait_event_info
& WAIT_EVENT_CLASS_MASK
;
229 if (oldClassId
!= classId
)
231 (errcode(ERRCODE_DUPLICATE_OBJECT
),
232 errmsg("wait event \"%s\" already exists in type \"%s\"",
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
);
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
,
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
,
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.
277 GetWaitEventCustomIdentifier(uint32 wait_event_info
)
280 WaitEventCustomEntryByInfo
*entry
;
282 /* Built-in event? */
283 if (wait_event_info
== PG_WAIT_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
,
291 LWLockRelease(WaitEventCustomLock
);
295 "could not find custom name for wait event information %u",
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.
307 GetWaitEventCustomNames(uint32 classId
, int *nwaitevents
)
309 char **waiteventnames
;
310 WaitEventCustomEntryByName
*hentry
;
311 HASH_SEQ_STATUS hash_seq
;
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
);
327 while ((hentry
= (WaitEventCustomEntryByName
*) hash_seq_search(&hash_seq
)) != NULL
)
329 if ((hentry
->wait_event_info
& WAIT_EVENT_CLASS_MASK
) != classId
)
331 waiteventnames
[index
] = pstrdup(hentry
->wait_event_name
);
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()
346 * Expected to be called during backend startup, to point my_wait_event_info
347 * into shared memory.
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.
362 pgstat_reset_wait_event_storage(void)
364 my_wait_event_info
= &local_my_wait_event_info
;
368 * pgstat_get_wait_event_type() -
370 * Return a string representing the current wait event type, backend is
374 pgstat_get_wait_event_type(uint32 wait_event_info
)
377 const char *event_type
;
379 /* report process as not waiting. */
380 if (wait_event_info
== 0)
383 classId
= wait_event_info
& WAIT_EVENT_CLASS_MASK
;
388 event_type
= "LWLock";
393 case PG_WAIT_BUFFERPIN
:
394 event_type
= "BufferPin";
396 case PG_WAIT_ACTIVITY
:
397 event_type
= "Activity";
400 event_type
= "Client";
402 case PG_WAIT_EXTENSION
:
403 event_type
= "Extension";
408 case PG_WAIT_TIMEOUT
:
409 event_type
= "Timeout";
414 case PG_WAIT_INJECTIONPOINT
:
415 event_type
= "InjectionPoint";
426 * pgstat_get_wait_event() -
428 * Return a string representing the current wait event, backend is
432 pgstat_get_wait_event(uint32 wait_event_info
)
436 const char *event_name
;
438 /* report process as not waiting. */
439 if (wait_event_info
== 0)
442 classId
= wait_event_info
& WAIT_EVENT_CLASS_MASK
;
443 eventId
= wait_event_info
& WAIT_EVENT_ID_MASK
;
448 event_name
= GetLWLockIdentifier(classId
, eventId
);
451 event_name
= GetLockNameFromTagType(eventId
);
453 case PG_WAIT_EXTENSION
:
454 case PG_WAIT_INJECTIONPOINT
:
455 event_name
= GetWaitEventCustomIdentifier(wait_event_info
);
457 case PG_WAIT_BUFFERPIN
:
459 WaitEventBufferPin w
= (WaitEventBufferPin
) wait_event_info
;
461 event_name
= pgstat_get_wait_bufferpin(w
);
464 case PG_WAIT_ACTIVITY
:
466 WaitEventActivity w
= (WaitEventActivity
) wait_event_info
;
468 event_name
= pgstat_get_wait_activity(w
);
473 WaitEventClient w
= (WaitEventClient
) wait_event_info
;
475 event_name
= pgstat_get_wait_client(w
);
480 WaitEventIPC w
= (WaitEventIPC
) wait_event_info
;
482 event_name
= pgstat_get_wait_ipc(w
);
485 case PG_WAIT_TIMEOUT
:
487 WaitEventTimeout w
= (WaitEventTimeout
) wait_event_info
;
489 event_name
= pgstat_get_wait_timeout(w
);
494 WaitEventIO w
= (WaitEventIO
) wait_event_info
;
496 event_name
= pgstat_get_wait_io(w
);
500 event_name
= "unknown wait event";
507 #include "pgstat_wait_event.c"