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"
26 : : #include "storage/lwlock.h"
27 : : #include "storage/shmem.h"
28 : : #include "storage/subsystems.h"
29 : : #include "utils/wait_event.h"
30 : :
31 : :
32 : : static const char *pgstat_get_wait_activity(WaitEventActivity w);
33 : : static const char *pgstat_get_wait_buffer(WaitEventBuffer 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);
38 : :
39 : :
40 : : static uint32 local_my_wait_event_info;
41 : : uint32 *my_wait_event_info = &local_my_wait_event_info;
42 : :
43 : : #define WAIT_EVENT_CLASS_MASK 0xFF000000
44 : : #define WAIT_EVENT_ID_MASK 0x0000FFFF
45 : :
46 : : /*
47 : : * Hash tables for storing custom wait event ids and their names in
48 : : * shared memory.
49 : : *
50 : : * WaitEventCustomHashByInfo is used to find the name from wait event
51 : : * information. Any backend can search it to find custom wait events.
52 : : *
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.
55 : : *
56 : : * For simplicity, we use the same ID counter across types of custom events.
57 : : * We could end that anytime the need arises.
58 : : *
59 : : * The size of the hash table is based on the assumption that usually only a
60 : : * handful of entries are needed, but since it's small in absolute terms
61 : : * anyway, we leave a generous amount of headroom.
62 : : */
63 : : static HTAB *WaitEventCustomHashByInfo; /* find names from infos */
64 : : static HTAB *WaitEventCustomHashByName; /* find infos from names */
65 : :
66 : : #define WAIT_EVENT_CUSTOM_HASH_SIZE 128
67 : :
68 : : /* hash table entries */
69 : : typedef struct WaitEventCustomEntryByInfo
70 : : {
71 : : uint32 wait_event_info; /* hash key */
72 : : char wait_event_name[NAMEDATALEN]; /* custom wait event name */
73 : : } WaitEventCustomEntryByInfo;
74 : :
75 : : typedef struct WaitEventCustomEntryByName
76 : : {
77 : : char wait_event_name[NAMEDATALEN]; /* hash key */
78 : : uint32 wait_event_info;
79 : : } WaitEventCustomEntryByName;
80 : :
81 : :
82 : : /* dynamic allocation counter for custom wait events */
83 : : static int *WaitEventCustomCounter;
84 : :
85 : : /* first event ID of custom wait events */
86 : : #define WAIT_EVENT_CUSTOM_INITIAL_ID 1
87 : :
88 : : static uint32 WaitEventCustomNew(uint32 classId, const char *wait_event_name);
89 : : static const char *GetWaitEventCustomIdentifier(uint32 wait_event_info);
90 : :
91 : : static void WaitEventCustomShmemRequest(void *arg);
92 : : static void WaitEventCustomShmemInit(void *arg);
93 : :
94 : : const ShmemCallbacks WaitEventCustomShmemCallbacks = {
95 : : .request_fn = WaitEventCustomShmemRequest,
96 : : .init_fn = WaitEventCustomShmemInit,
97 : : };
98 : :
99 : : /*
100 : : * Register shmem space for dynamic shared hash and dynamic allocation counter.
101 : : */
102 : : static void
110 heikki.linnakangas@i 103 :CBC 1225 : WaitEventCustomShmemRequest(void *arg)
104 : : {
16 nathan@postgresql.or 105 :GNC 1225 : ShmemRequestStruct(.name = "WaitEventCustomCounter",
106 : : .size = sizeof(int),
107 : : .ptr = (void **) &WaitEventCustomCounter,
108 : : );
110 heikki.linnakangas@i 109 :CBC 1225 : ShmemRequestHash(.name = "WaitEventCustom hash by wait event information",
110 : : .ptr = &WaitEventCustomHashByInfo,
111 : : .nelems = WAIT_EVENT_CUSTOM_HASH_SIZE,
112 : : .hash_info.keysize = sizeof(uint32),
113 : : .hash_info.entrysize = sizeof(WaitEventCustomEntryByInfo),
114 : : .hash_flags = HASH_ELEM | HASH_BLOBS,
115 : : );
116 : 1225 : ShmemRequestHash(.name = "WaitEventCustom hash by name",
117 : : .ptr = &WaitEventCustomHashByName,
118 : : .nelems = WAIT_EVENT_CUSTOM_HASH_SIZE,
119 : : /* key is a NULL-terminated string */
120 : : .hash_info.keysize = sizeof(char[NAMEDATALEN]),
121 : : .hash_info.entrysize = sizeof(WaitEventCustomEntryByName),
122 : : .hash_flags = HASH_ELEM | HASH_STRINGS,
123 : : );
1090 michael@paquier.xyz 124 : 1225 : }
125 : :
126 : : static void
110 heikki.linnakangas@i 127 : 1222 : WaitEventCustomShmemInit(void *arg)
128 : : {
129 : : /* initialize the allocation counter */
16 nathan@postgresql.or 130 :GNC 1222 : *WaitEventCustomCounter = WAIT_EVENT_CUSTOM_INITIAL_ID;
1090 michael@paquier.xyz 131 :CBC 1222 : }
132 : :
133 : : /*
134 : : * Allocate a new event ID and return the wait event info.
135 : : *
136 : : * If the wait event name is already defined, this does not allocate a new
137 : : * entry; it returns the wait event information associated to the name.
138 : : */
139 : : uint32
1076 140 : 52 : WaitEventExtensionNew(const char *wait_event_name)
141 : : {
758 noah@leadboat.com 142 : 52 : return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
143 : : }
144 : :
145 : : uint32
146 : 85 : WaitEventInjectionPointNew(const char *wait_event_name)
147 : : {
148 : 85 : return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
149 : : }
150 : :
151 : : static uint32
152 : 137 : WaitEventCustomNew(uint32 classId, const char *wait_event_name)
153 : : {
154 : : uint16 eventId;
155 : : bool found;
156 : : WaitEventCustomEntryByName *entry_by_name;
157 : : WaitEventCustomEntryByInfo *entry_by_info;
158 : : uint32 wait_event_info;
159 : :
160 : : /* Check the limit of the length of the event name */
1076 michael@paquier.xyz 161 [ - + ]: 137 : if (strlen(wait_event_name) >= NAMEDATALEN)
1076 michael@paquier.xyz 162 [ # # ]:UBC 0 : elog(ERROR,
163 : : "cannot use custom wait event string longer than %u characters",
164 : : NAMEDATALEN - 1);
165 : :
166 : : /*
167 : : * Check if the wait event info associated to the name is already defined,
168 : : * and return it if so.
169 : : */
758 noah@leadboat.com 170 :CBC 137 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
171 : : entry_by_name = (WaitEventCustomEntryByName *)
172 : 137 : hash_search(WaitEventCustomHashByName, wait_event_name,
173 : : HASH_FIND, &found);
174 : 137 : LWLockRelease(WaitEventCustomLock);
1076 michael@paquier.xyz 175 [ + + ]: 137 : if (found)
176 : : {
177 : : uint32 oldClassId;
178 : :
758 noah@leadboat.com 179 : 85 : oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
180 [ - + ]: 85 : if (oldClassId != classId)
758 noah@leadboat.com 181 [ # # ]:UBC 0 : ereport(ERROR,
182 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
183 : : errmsg("wait event \"%s\" already exists in type \"%s\"",
184 : : wait_event_name,
185 : : pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
758 noah@leadboat.com 186 :CBC 85 : return entry_by_name->wait_event_info;
187 : : }
188 : :
189 : : /*
190 : : * Allocate and register a new wait event. Recheck if the event name
191 : : * exists, as it could be possible that a concurrent process has inserted
192 : : * one with the same name since the LWLock acquired again here was
193 : : * previously released.
194 : : */
195 : 52 : LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
196 : : entry_by_name = (WaitEventCustomEntryByName *)
197 : 52 : hash_search(WaitEventCustomHashByName, wait_event_name,
198 : : HASH_FIND, &found);
1076 michael@paquier.xyz 199 [ - + ]: 52 : if (found)
200 : : {
201 : : uint32 oldClassId;
202 : :
758 noah@leadboat.com 203 :UBC 0 : LWLockRelease(WaitEventCustomLock);
204 : 0 : oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
205 [ # # ]: 0 : if (oldClassId != classId)
206 [ # # ]: 0 : ereport(ERROR,
207 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
208 : : errmsg("wait event \"%s\" already exists in type \"%s\"",
209 : : wait_event_name,
210 : : pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
211 : 0 : return entry_by_name->wait_event_info;
212 : : }
213 : :
214 : : /* Allocate a new event Id */
16 nathan@postgresql.or 215 [ - + ]:GNC 52 : if (*WaitEventCustomCounter >= WAIT_EVENT_CUSTOM_HASH_SIZE)
1090 michael@paquier.xyz 216 [ # # ]:UBC 0 : ereport(ERROR,
217 : : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
218 : : errmsg("too many custom wait events"));
219 : :
16 nathan@postgresql.or 220 :GNC 52 : eventId = (*WaitEventCustomCounter)++;
221 : :
222 : : /* Register the new wait event */
758 noah@leadboat.com 223 :CBC 52 : wait_event_info = classId | eventId;
224 : : entry_by_info = (WaitEventCustomEntryByInfo *)
225 : 52 : hash_search(WaitEventCustomHashByInfo, &wait_event_info,
226 : : HASH_ENTER, &found);
1076 michael@paquier.xyz 227 [ - + ]: 52 : Assert(!found);
758 noah@leadboat.com 228 : 52 : strlcpy(entry_by_info->wait_event_name, wait_event_name,
229 : : sizeof(entry_by_info->wait_event_name));
230 : :
231 : : entry_by_name = (WaitEventCustomEntryByName *)
232 : 52 : hash_search(WaitEventCustomHashByName, wait_event_name,
233 : : HASH_ENTER, &found);
1076 michael@paquier.xyz 234 [ - + ]: 52 : Assert(!found);
758 noah@leadboat.com 235 : 52 : entry_by_name->wait_event_info = wait_event_info;
236 : :
237 : 52 : LWLockRelease(WaitEventCustomLock);
238 : :
239 : 52 : return wait_event_info;
240 : : }
241 : :
242 : : /*
243 : : * Return the name of a custom wait event information.
244 : : */
245 : : static const char *
246 : 157 : GetWaitEventCustomIdentifier(uint32 wait_event_info)
247 : : {
248 : : bool found;
249 : : WaitEventCustomEntryByInfo *entry;
250 : :
251 : : /* Built-in event? */
252 [ - + ]: 157 : if (wait_event_info == PG_WAIT_EXTENSION)
1090 michael@paquier.xyz 253 :UBC 0 : return "Extension";
254 : :
255 : : /* It is a user-defined wait event, so lookup hash table. */
758 noah@leadboat.com 256 :CBC 157 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
257 : : entry = (WaitEventCustomEntryByInfo *)
258 : 157 : hash_search(WaitEventCustomHashByInfo, &wait_event_info,
259 : : HASH_FIND, &found);
260 : 157 : LWLockRelease(WaitEventCustomLock);
261 : :
1076 michael@paquier.xyz 262 [ - + ]: 157 : if (!entry)
758 noah@leadboat.com 263 [ # # ]:UBC 0 : elog(ERROR,
264 : : "could not find custom name for wait event information %u",
265 : : wait_event_info);
266 : :
1076 michael@paquier.xyz 267 :CBC 157 : return entry->wait_event_name;
268 : : }
269 : :
270 : :
271 : : /*
272 : : * Returns a list of currently defined custom wait event names. The result is
273 : : * a palloc'd array, with the number of elements saved in *nwaitevents.
274 : : */
275 : : char **
758 noah@leadboat.com 276 : 10 : GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
277 : : {
278 : : char **waiteventnames;
279 : : WaitEventCustomEntryByName *hentry;
280 : : HASH_SEQ_STATUS hash_seq;
281 : : int index;
282 : : int els;
283 : :
284 : 10 : LWLockAcquire(WaitEventCustomLock, LW_SHARED);
285 : :
286 : : /* Now we can safely count the number of entries */
287 : 10 : els = hash_get_num_entries(WaitEventCustomHashByName);
288 : :
289 : : /* Allocate enough space for all entries */
227 michael@paquier.xyz 290 : 10 : waiteventnames = palloc_array(char *, els);
291 : :
292 : : /* Now scan the hash table to copy the data */
758 noah@leadboat.com 293 : 10 : hash_seq_init(&hash_seq, WaitEventCustomHashByName);
294 : :
1070 michael@paquier.xyz 295 : 10 : index = 0;
758 noah@leadboat.com 296 [ + + ]: 12 : while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
297 : : {
298 [ + + ]: 2 : if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
299 : 1 : continue;
1070 michael@paquier.xyz 300 : 1 : waiteventnames[index] = pstrdup(hentry->wait_event_name);
301 : 1 : index++;
302 : : }
303 : :
758 noah@leadboat.com 304 : 10 : LWLockRelease(WaitEventCustomLock);
305 : :
1070 michael@paquier.xyz 306 : 10 : *nwaitevents = index;
307 : 10 : return waiteventnames;
308 : : }
309 : :
310 : : /*
311 : : * Configure wait event reporting to report wait events to *wait_event_info.
312 : : * *wait_event_info needs to be valid until pgstat_reset_wait_event_storage()
313 : : * is called.
314 : : *
315 : : * Expected to be called during backend startup, to point my_wait_event_info
316 : : * into shared memory.
317 : : */
318 : : void
1939 andres@anarazel.de 319 : 22969 : pgstat_set_wait_event_storage(uint32 *wait_event_info)
320 : : {
321 : 22969 : my_wait_event_info = wait_event_info;
322 : 22969 : }
323 : :
324 : : /*
325 : : * Reset wait event storage location.
326 : : *
327 : : * Expected to be called during backend shutdown, before the location set up
328 : : * pgstat_set_wait_event_storage() becomes invalid.
329 : : */
330 : : void
331 : 22969 : pgstat_reset_wait_event_storage(void)
332 : : {
333 : 22969 : my_wait_event_info = &local_my_wait_event_info;
334 : 22969 : }
335 : :
336 : : /* ----------
337 : : * pgstat_get_wait_event_type() -
338 : : *
339 : : * Return a string representing the current wait event type, backend is
340 : : * waiting on.
341 : : */
342 : : const char *
1940 343 : 12605 : pgstat_get_wait_event_type(uint32 wait_event_info)
344 : : {
345 : : uint32 classId;
346 : : const char *event_type;
347 : :
348 : : /* report process as not waiting. */
349 [ + + ]: 12605 : if (wait_event_info == 0)
350 : 3453 : return NULL;
351 : :
1090 michael@paquier.xyz 352 : 9152 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
353 : :
1940 andres@anarazel.de 354 [ + + + + : 9152 : switch (classId)
+ + + + +
+ - ]
355 : : {
356 : 24 : case PG_WAIT_LWLOCK:
357 : 24 : event_type = "LWLock";
358 : 24 : break;
359 : 1284 : case PG_WAIT_LOCK:
360 : 1284 : event_type = "Lock";
361 : 1284 : break;
234 362 : 15 : case PG_WAIT_BUFFER:
363 : 15 : event_type = "Buffer";
1940 364 : 15 : break;
365 : 6222 : case PG_WAIT_ACTIVITY:
366 : 6222 : event_type = "Activity";
367 : 6222 : break;
368 : 941 : case PG_WAIT_CLIENT:
369 : 941 : event_type = "Client";
370 : 941 : break;
371 : 51 : case PG_WAIT_EXTENSION:
372 : 51 : event_type = "Extension";
373 : 51 : break;
374 : 328 : case PG_WAIT_IPC:
375 : 328 : event_type = "IPC";
376 : 328 : break;
377 : 31 : case PG_WAIT_TIMEOUT:
378 : 31 : event_type = "Timeout";
379 : 31 : break;
380 : 41 : case PG_WAIT_IO:
381 : 41 : event_type = "IO";
382 : 41 : break;
758 noah@leadboat.com 383 : 215 : case PG_WAIT_INJECTIONPOINT:
384 : 215 : event_type = "InjectionPoint";
385 : 215 : break;
1940 andres@anarazel.de 386 :UBC 0 : default:
387 : 0 : event_type = "???";
388 : 0 : break;
389 : : }
390 : :
1940 andres@anarazel.de 391 :CBC 9152 : return event_type;
392 : : }
393 : :
394 : : /* ----------
395 : : * pgstat_get_wait_event() -
396 : : *
397 : : * Return a string representing the current wait event, backend is
398 : : * waiting on.
399 : : */
400 : : const char *
401 : 8386 : pgstat_get_wait_event(uint32 wait_event_info)
402 : : {
403 : : uint32 classId;
404 : : uint16 eventId;
405 : : const char *event_name;
406 : :
407 : : /* report process as not waiting. */
408 [ + + ]: 8386 : if (wait_event_info == 0)
409 : 1042 : return NULL;
410 : :
1090 michael@paquier.xyz 411 : 7344 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
412 : 7344 : eventId = wait_event_info & WAIT_EVENT_ID_MASK;
413 : :
1940 andres@anarazel.de 414 [ + + + + : 7344 : switch (classId)
+ + + + +
- ]
415 : : {
416 : 24 : case PG_WAIT_LWLOCK:
417 : 24 : event_name = GetLWLockIdentifier(classId, eventId);
418 : 24 : break;
419 : 12 : case PG_WAIT_LOCK:
420 : 12 : event_name = GetLockNameFromTagType(eventId);
421 : 12 : break;
1090 michael@paquier.xyz 422 : 157 : case PG_WAIT_EXTENSION:
423 : : case PG_WAIT_INJECTIONPOINT:
758 noah@leadboat.com 424 : 157 : event_name = GetWaitEventCustomIdentifier(wait_event_info);
1090 michael@paquier.xyz 425 : 157 : break;
234 andres@anarazel.de 426 : 15 : case PG_WAIT_BUFFER:
427 : : {
428 : 15 : WaitEventBuffer w = (WaitEventBuffer) wait_event_info;
429 : :
430 : 15 : event_name = pgstat_get_wait_buffer(w);
1118 michael@paquier.xyz 431 : 15 : break;
432 : : }
1940 andres@anarazel.de 433 : 6222 : case PG_WAIT_ACTIVITY:
434 : : {
435 : 6222 : WaitEventActivity w = (WaitEventActivity) wait_event_info;
436 : :
437 : 6222 : event_name = pgstat_get_wait_activity(w);
438 : 6222 : break;
439 : : }
440 : 809 : case PG_WAIT_CLIENT:
441 : : {
442 : 809 : WaitEventClient w = (WaitEventClient) wait_event_info;
443 : :
444 : 809 : event_name = pgstat_get_wait_client(w);
445 : 809 : break;
446 : : }
447 : 46 : case PG_WAIT_IPC:
448 : : {
449 : 46 : WaitEventIPC w = (WaitEventIPC) wait_event_info;
450 : :
451 : 46 : event_name = pgstat_get_wait_ipc(w);
452 : 46 : break;
453 : : }
454 : 31 : case PG_WAIT_TIMEOUT:
455 : : {
456 : 31 : WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
457 : :
458 : 31 : event_name = pgstat_get_wait_timeout(w);
459 : 31 : break;
460 : : }
461 : 28 : case PG_WAIT_IO:
462 : : {
463 : 28 : WaitEventIO w = (WaitEventIO) wait_event_info;
464 : :
465 : 28 : event_name = pgstat_get_wait_io(w);
466 : 28 : break;
467 : : }
1940 andres@anarazel.de 468 :UBC 0 : default:
469 : 0 : event_name = "unknown wait event";
470 : 0 : break;
471 : : }
472 : :
1940 andres@anarazel.de 473 :CBC 7344 : return event_name;
474 : : }
475 : :
476 : : #include "utils/pgstat_wait_event.c"
|