Branch data 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
103 : 9 : WaitEventCustomShmemSize(void)
104 : : {
105 : 9 : Size sz;
106 : :
107 : 9 : sz = MAXALIGN(sizeof(WaitEventCustomCounterData));
108 : 9 : sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
109 : : sizeof(WaitEventCustomEntryByInfo)));
110 : 9 : sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
111 : : sizeof(WaitEventCustomEntryByName)));
112 : 18 : return sz;
113 : 9 : }
114 : :
115 : : /*
116 : : * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
117 : : */
118 : : void
119 : 6 : WaitEventCustomShmemInit(void)
120 : : {
121 : 6 : bool found;
122 : 6 : HASHCTL info;
123 : :
124 : 6 : WaitEventCustomCounter = (WaitEventCustomCounterData *)
125 : 6 : ShmemInitStruct("WaitEventCustomCounterData",
126 : : sizeof(WaitEventCustomCounterData), &found);
127 : :
128 [ - + ]: 6 : if (!found)
129 : : {
130 : : /* initialize the allocation counter and its spinlock. */
131 : 6 : WaitEventCustomCounter->nextId = WAIT_EVENT_CUSTOM_INITIAL_ID;
132 : 6 : SpinLockInit(&WaitEventCustomCounter->mutex);
133 : 6 : }
134 : :
135 : : /* initialize or attach the hash tables to store custom wait events */
136 : 6 : info.keysize = sizeof(uint32);
137 : 6 : info.entrysize = sizeof(WaitEventCustomEntryByInfo);
138 : 6 : WaitEventCustomHashByInfo =
139 : 6 : 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 */
146 : 6 : info.keysize = sizeof(char[NAMEDATALEN]);
147 : 6 : info.entrysize = sizeof(WaitEventCustomEntryByName);
148 : 6 : WaitEventCustomHashByName =
149 : 6 : 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);
154 : 6 : }
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
163 : 0 : WaitEventExtensionNew(const char *wait_event_name)
164 : : {
165 : 0 : return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
166 : : }
167 : :
168 : : uint32
169 : 0 : WaitEventInjectionPointNew(const char *wait_event_name)
170 : : {
171 : 0 : return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
172 : : }
173 : :
174 : : static uint32
175 : 0 : WaitEventCustomNew(uint32 classId, const char *wait_event_name)
176 : : {
177 : 0 : uint16 eventId;
178 : 0 : bool found;
179 : 0 : WaitEventCustomEntryByName *entry_by_name;
180 : 0 : WaitEventCustomEntryByInfo *entry_by_info;
181 : 0 : uint32 wait_event_info;
182 : :
183 : : /* Check the limit of the length of the event name */
184 [ # # ]: 0 : if (strlen(wait_event_name) >= NAMEDATALEN)
185 [ # # # # ]: 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 : : */
193 : 0 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
194 : 0 : entry_by_name = (WaitEventCustomEntryByName *)
195 : 0 : hash_search(WaitEventCustomHashByName, wait_event_name,
196 : : HASH_FIND, &found);
197 : 0 : LWLockRelease(WaitEventCustomLock);
198 [ # # ]: 0 : if (found)
199 : : {
200 : 0 : uint32 oldClassId;
201 : :
202 : 0 : oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
203 [ # # ]: 0 : if (oldClassId != classId)
204 [ # # # # ]: 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))));
209 : 0 : return entry_by_name->wait_event_info;
210 : 0 : }
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 : 0 : LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
219 : 0 : entry_by_name = (WaitEventCustomEntryByName *)
220 : 0 : hash_search(WaitEventCustomHashByName, wait_event_name,
221 : : HASH_FIND, &found);
222 [ # # ]: 0 : if (found)
223 : : {
224 : 0 : uint32 oldClassId;
225 : :
226 : 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 : 0 : }
236 : :
237 : : /* Allocate a new event Id */
238 [ # # ]: 0 : SpinLockAcquire(&WaitEventCustomCounter->mutex);
239 : :
240 [ # # ]: 0 : if (WaitEventCustomCounter->nextId >= WAIT_EVENT_CUSTOM_HASH_MAX_SIZE)
241 : : {
242 : 0 : SpinLockRelease(&WaitEventCustomCounter->mutex);
243 [ # # # # ]: 0 : ereport(ERROR,
244 : : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
245 : : errmsg("too many custom wait events"));
246 : 0 : }
247 : :
248 : 0 : eventId = WaitEventCustomCounter->nextId++;
249 : :
250 : 0 : SpinLockRelease(&WaitEventCustomCounter->mutex);
251 : :
252 : : /* Register the new wait event */
253 : 0 : wait_event_info = classId | eventId;
254 : 0 : entry_by_info = (WaitEventCustomEntryByInfo *)
255 : 0 : hash_search(WaitEventCustomHashByInfo, &wait_event_info,
256 : : HASH_ENTER, &found);
257 [ # # ]: 0 : Assert(!found);
258 : 0 : strlcpy(entry_by_info->wait_event_name, wait_event_name,
259 : : sizeof(entry_by_info->wait_event_name));
260 : :
261 : 0 : entry_by_name = (WaitEventCustomEntryByName *)
262 : 0 : hash_search(WaitEventCustomHashByName, wait_event_name,
263 : : HASH_ENTER, &found);
264 [ # # ]: 0 : Assert(!found);
265 : 0 : entry_by_name->wait_event_info = wait_event_info;
266 : :
267 : 0 : LWLockRelease(WaitEventCustomLock);
268 : :
269 : 0 : return wait_event_info;
270 : 0 : }
271 : :
272 : : /*
273 : : * Return the name of a custom wait event information.
274 : : */
275 : : static const char *
276 : 0 : GetWaitEventCustomIdentifier(uint32 wait_event_info)
277 : : {
278 : 0 : bool found;
279 : 0 : WaitEventCustomEntryByInfo *entry;
280 : :
281 : : /* Built-in event? */
282 [ # # ]: 0 : if (wait_event_info == PG_WAIT_EXTENSION)
283 : 0 : return "Extension";
284 : :
285 : : /* It is a user-defined wait event, so lookup hash table. */
286 : 0 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
287 : 0 : entry = (WaitEventCustomEntryByInfo *)
288 : 0 : hash_search(WaitEventCustomHashByInfo, &wait_event_info,
289 : : HASH_FIND, &found);
290 : 0 : LWLockRelease(WaitEventCustomLock);
291 : :
292 [ # # ]: 0 : if (!entry)
293 [ # # # # ]: 0 : elog(ERROR,
294 : : "could not find custom name for wait event information %u",
295 : : wait_event_info);
296 : :
297 : 0 : return entry->wait_event_name;
298 : 0 : }
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 **
306 : 2 : GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
307 : : {
308 : 2 : char **waiteventnames;
309 : 2 : WaitEventCustomEntryByName *hentry;
310 : 2 : HASH_SEQ_STATUS hash_seq;
311 : 2 : int index;
312 : 2 : int els;
313 : :
314 : 2 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
315 : :
316 : : /* Now we can safely count the number of entries */
317 : 2 : els = hash_get_num_entries(WaitEventCustomHashByName);
318 : :
319 : : /* Allocate enough space for all entries */
320 : 2 : waiteventnames = palloc_array(char *, els);
321 : :
322 : : /* Now scan the hash table to copy the data */
323 : 2 : hash_seq_init(&hash_seq, WaitEventCustomHashByName);
324 : :
325 : 2 : index = 0;
326 [ - + ]: 2 : while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
327 : : {
328 [ # # ]: 0 : if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
329 : 0 : continue;
330 : 0 : waiteventnames[index] = pstrdup(hentry->wait_event_name);
331 : 0 : index++;
332 : : }
333 : :
334 : 2 : LWLockRelease(WaitEventCustomLock);
335 : :
336 : 2 : *nwaitevents = index;
337 : 4 : return waiteventnames;
338 : 2 : }
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
349 : 806 : pgstat_set_wait_event_storage(uint32 *wait_event_info)
350 : : {
351 : 806 : my_wait_event_info = wait_event_info;
352 : 806 : }
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 : 806 : pgstat_reset_wait_event_storage(void)
362 : : {
363 : 806 : my_wait_event_info = &local_my_wait_event_info;
364 : 806 : }
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 *
373 : 81 : pgstat_get_wait_event_type(uint32 wait_event_info)
374 : : {
375 : 81 : uint32 classId;
376 : 81 : const char *event_type;
377 : :
378 : : /* report process as not waiting. */
379 [ + + ]: 81 : if (wait_event_info == 0)
380 : 21 : return NULL;
381 : :
382 : 60 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
383 : :
384 [ + - + + : 60 : switch (classId)
+ - + - -
- - ]
385 : : {
386 : : case PG_WAIT_LWLOCK:
387 : 1 : event_type = "LWLock";
388 : 1 : break;
389 : : case PG_WAIT_LOCK:
390 : 0 : event_type = "Lock";
391 : 0 : break;
392 : : case PG_WAIT_BUFFER:
393 : 1 : event_type = "Buffer";
394 : 1 : break;
395 : : case PG_WAIT_ACTIVITY:
396 : 40 : event_type = "Activity";
397 : 40 : break;
398 : : case PG_WAIT_CLIENT:
399 : 14 : event_type = "Client";
400 : 14 : break;
401 : : case PG_WAIT_EXTENSION:
402 : 0 : event_type = "Extension";
403 : 0 : break;
404 : : case PG_WAIT_IPC:
405 : 4 : event_type = "IPC";
406 : 4 : break;
407 : : case PG_WAIT_TIMEOUT:
408 : 0 : event_type = "Timeout";
409 : 0 : break;
410 : : case PG_WAIT_IO:
411 : 0 : event_type = "IO";
412 : 0 : break;
413 : : case PG_WAIT_INJECTIONPOINT:
414 : 0 : event_type = "InjectionPoint";
415 : 0 : break;
416 : : default:
417 : 0 : event_type = "???";
418 : 0 : break;
419 : : }
420 : :
421 : 60 : return event_type;
422 : 81 : }
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 : 81 : pgstat_get_wait_event(uint32 wait_event_info)
432 : : {
433 : 81 : uint32 classId;
434 : 81 : uint16 eventId;
435 : 81 : const char *event_name;
436 : :
437 : : /* report process as not waiting. */
438 [ + + ]: 81 : if (wait_event_info == 0)
439 : 21 : return NULL;
440 : :
441 : 60 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
442 : 60 : eventId = wait_event_info & WAIT_EVENT_ID_MASK;
443 : :
444 [ + - + + : 60 : switch (classId)
+ - - - +
- ]
445 : : {
446 : : case PG_WAIT_LWLOCK:
447 : 1 : event_name = GetLWLockIdentifier(classId, eventId);
448 : 1 : break;
449 : : case PG_WAIT_LOCK:
450 : 0 : event_name = GetLockNameFromTagType(eventId);
451 : 0 : break;
452 : : case PG_WAIT_EXTENSION:
453 : : case PG_WAIT_INJECTIONPOINT:
454 : 0 : event_name = GetWaitEventCustomIdentifier(wait_event_info);
455 : 0 : break;
456 : : case PG_WAIT_BUFFER:
457 : : {
458 : 1 : WaitEventBuffer w = (WaitEventBuffer) wait_event_info;
459 : :
460 : 1 : event_name = pgstat_get_wait_buffer(w);
461 : : break;
462 : 1 : }
463 : : case PG_WAIT_ACTIVITY:
464 : : {
465 : 40 : WaitEventActivity w = (WaitEventActivity) wait_event_info;
466 : :
467 : 40 : event_name = pgstat_get_wait_activity(w);
468 : : break;
469 : 40 : }
470 : : case PG_WAIT_CLIENT:
471 : : {
472 : 14 : WaitEventClient w = (WaitEventClient) wait_event_info;
473 : :
474 : 14 : event_name = pgstat_get_wait_client(w);
475 : : break;
476 : 14 : }
477 : : case PG_WAIT_IPC:
478 : : {
479 : 4 : WaitEventIPC w = (WaitEventIPC) wait_event_info;
480 : :
481 : 4 : event_name = pgstat_get_wait_ipc(w);
482 : : break;
483 : 4 : }
484 : : case PG_WAIT_TIMEOUT:
485 : : {
486 : 0 : WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
487 : :
488 : 0 : event_name = pgstat_get_wait_timeout(w);
489 : : break;
490 : 0 : }
491 : : case PG_WAIT_IO:
492 : : {
493 : 0 : WaitEventIO w = (WaitEventIO) wait_event_info;
494 : :
495 : 0 : event_name = pgstat_get_wait_io(w);
496 : : break;
497 : 0 : }
498 : : default:
499 : 0 : event_name = "unknown wait event";
500 : 0 : break;
501 : : }
502 : :
503 : 60 : return event_name;
504 : 81 : }
505 : :
506 : : #include "pgstat_wait_event.c"
|