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