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 "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"
30 :
31 :
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);
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 : * WaitEventExtensionHashById is used to find the name from an event id.
51 : * Any backend can search it to find custom wait events.
52 : *
53 : * WaitEventExtensionHashByName is used to find the event ID from a name.
54 : * It is used to ensure that no duplicated entries are registered.
55 : *
56 : * The size of the hash table is based on the assumption that
57 : * WAIT_EVENT_EXTENSION_HASH_INIT_SIZE is enough for most cases, and it seems
58 : * unlikely that the number of entries will reach
59 : * WAIT_EVENT_EXTENSION_HASH_MAX_SIZE.
60 : */
61 : static HTAB *WaitEventExtensionHashById; /* find names from IDs */
62 : static HTAB *WaitEventExtensionHashByName; /* find IDs from names */
63 :
64 : #define WAIT_EVENT_EXTENSION_HASH_INIT_SIZE 16
65 : #define WAIT_EVENT_EXTENSION_HASH_MAX_SIZE 128
66 :
67 : /* hash table entries */
68 : typedef struct WaitEventExtensionEntryById
69 : {
70 : uint16 event_id; /* hash key */
71 : char wait_event_name[NAMEDATALEN]; /* custom wait event name */
72 : } WaitEventExtensionEntryById;
73 :
74 : typedef struct WaitEventExtensionEntryByName
75 : {
76 : char wait_event_name[NAMEDATALEN]; /* hash key */
77 : uint16 event_id; /* wait event ID */
78 : } WaitEventExtensionEntryByName;
79 :
80 :
81 : /* dynamic allocation counter for custom wait events in extensions */
82 : typedef struct WaitEventExtensionCounterData
83 : {
84 : int nextId; /* next ID to assign */
85 : slock_t mutex; /* protects the counter */
86 : } WaitEventExtensionCounterData;
87 :
88 : /* pointer to the shared memory */
89 : static WaitEventExtensionCounterData *WaitEventExtensionCounter;
90 :
91 : /* first event ID of custom wait events for extensions */
92 : #define NUM_BUILTIN_WAIT_EVENT_EXTENSION \
93 : (WAIT_EVENT_EXTENSION_FIRST_USER_DEFINED - WAIT_EVENT_EXTENSION)
94 :
95 : /* wait event info for extensions */
96 : #define WAIT_EVENT_EXTENSION_INFO(eventId) (PG_WAIT_EXTENSION | eventId)
97 :
98 : static const char *GetWaitEventExtensionIdentifier(uint16 eventId);
99 :
100 : /*
101 : * Return the space for dynamic shared hash tables and dynamic allocation counter.
102 : */
103 : Size
104 3298 : WaitEventExtensionShmemSize(void)
105 : {
106 : Size sz;
107 :
108 3298 : sz = MAXALIGN(sizeof(WaitEventExtensionCounterData));
109 3298 : sz = add_size(sz, hash_estimate_size(WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
110 : sizeof(WaitEventExtensionEntryById)));
111 3298 : sz = add_size(sz, hash_estimate_size(WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
112 : sizeof(WaitEventExtensionEntryByName)));
113 3298 : return sz;
114 : }
115 :
116 : /*
117 : * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
118 : */
119 : void
120 1768 : WaitEventExtensionShmemInit(void)
121 : {
122 : bool found;
123 : HASHCTL info;
124 :
125 1768 : WaitEventExtensionCounter = (WaitEventExtensionCounterData *)
126 1768 : ShmemInitStruct("WaitEventExtensionCounterData",
127 : sizeof(WaitEventExtensionCounterData), &found);
128 :
129 1768 : if (!found)
130 : {
131 : /* initialize the allocation counter and its spinlock. */
132 1768 : WaitEventExtensionCounter->nextId = NUM_BUILTIN_WAIT_EVENT_EXTENSION;
133 1768 : SpinLockInit(&WaitEventExtensionCounter->mutex);
134 : }
135 :
136 : /* initialize or attach the hash tables to store custom wait events */
137 1768 : info.keysize = sizeof(uint16);
138 1768 : info.entrysize = sizeof(WaitEventExtensionEntryById);
139 1768 : WaitEventExtensionHashById = ShmemInitHash("WaitEventExtension hash by id",
140 : WAIT_EVENT_EXTENSION_HASH_INIT_SIZE,
141 : WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
142 : &info,
143 : HASH_ELEM | HASH_BLOBS);
144 :
145 : /* key is a NULL-terminated string */
146 1768 : info.keysize = sizeof(char[NAMEDATALEN]);
147 1768 : info.entrysize = sizeof(WaitEventExtensionEntryByName);
148 1768 : WaitEventExtensionHashByName = ShmemInitHash("WaitEventExtension hash by name",
149 : WAIT_EVENT_EXTENSION_HASH_INIT_SIZE,
150 : WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
151 : &info,
152 : HASH_ELEM | HASH_STRINGS);
153 1768 : }
154 :
155 : /*
156 : * Allocate a new event ID and return the wait event info.
157 : *
158 : * If the wait event name is already defined, this does not allocate a new
159 : * entry; it returns the wait event information associated to the name.
160 : */
161 : uint32
162 38 : WaitEventExtensionNew(const char *wait_event_name)
163 : {
164 : uint16 eventId;
165 : bool found;
166 : WaitEventExtensionEntryByName *entry_by_name;
167 : WaitEventExtensionEntryById *entry_by_id;
168 :
169 : /* Check the limit of the length of the event name */
170 38 : if (strlen(wait_event_name) >= NAMEDATALEN)
171 0 : elog(ERROR,
172 : "cannot use custom wait event string longer than %u characters",
173 : NAMEDATALEN - 1);
174 :
175 : /*
176 : * Check if the wait event info associated to the name is already defined,
177 : * and return it if so.
178 : */
179 38 : LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
180 : entry_by_name = (WaitEventExtensionEntryByName *)
181 38 : hash_search(WaitEventExtensionHashByName, wait_event_name,
182 : HASH_FIND, &found);
183 38 : LWLockRelease(WaitEventExtensionLock);
184 38 : if (found)
185 20 : return WAIT_EVENT_EXTENSION_INFO(entry_by_name->event_id);
186 :
187 : /*
188 : * Allocate and register a new wait event. Recheck if the event name
189 : * exists, as it could be possible that a concurrent process has inserted
190 : * one with the same name since the LWLock acquired again here was
191 : * previously released.
192 : */
193 18 : LWLockAcquire(WaitEventExtensionLock, LW_EXCLUSIVE);
194 : entry_by_name = (WaitEventExtensionEntryByName *)
195 18 : hash_search(WaitEventExtensionHashByName, wait_event_name,
196 : HASH_FIND, &found);
197 18 : if (found)
198 : {
199 0 : LWLockRelease(WaitEventExtensionLock);
200 0 : return WAIT_EVENT_EXTENSION_INFO(entry_by_name->event_id);
201 : }
202 :
203 : /* Allocate a new event Id */
204 18 : SpinLockAcquire(&WaitEventExtensionCounter->mutex);
205 :
206 18 : if (WaitEventExtensionCounter->nextId >= WAIT_EVENT_EXTENSION_HASH_MAX_SIZE)
207 : {
208 0 : SpinLockRelease(&WaitEventExtensionCounter->mutex);
209 0 : ereport(ERROR,
210 : errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
211 : errmsg("too many wait events for extensions"));
212 : }
213 :
214 18 : eventId = WaitEventExtensionCounter->nextId++;
215 :
216 18 : SpinLockRelease(&WaitEventExtensionCounter->mutex);
217 :
218 : /* Register the new wait event */
219 : entry_by_id = (WaitEventExtensionEntryById *)
220 18 : hash_search(WaitEventExtensionHashById, &eventId,
221 : HASH_ENTER, &found);
222 : Assert(!found);
223 18 : strlcpy(entry_by_id->wait_event_name, wait_event_name,
224 : sizeof(entry_by_id->wait_event_name));
225 :
226 : entry_by_name = (WaitEventExtensionEntryByName *)
227 18 : hash_search(WaitEventExtensionHashByName, wait_event_name,
228 : HASH_ENTER, &found);
229 : Assert(!found);
230 18 : entry_by_name->event_id = eventId;
231 :
232 18 : LWLockRelease(WaitEventExtensionLock);
233 :
234 18 : return WAIT_EVENT_EXTENSION_INFO(eventId);
235 : }
236 :
237 : /*
238 : * Return the name of an wait event ID for extension.
239 : */
240 : static const char *
241 38 : GetWaitEventExtensionIdentifier(uint16 eventId)
242 : {
243 : bool found;
244 : WaitEventExtensionEntryById *entry;
245 :
246 : /* Built-in event? */
247 38 : if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION)
248 0 : return "Extension";
249 :
250 : /* It is a user-defined wait event, so lookup hash table. */
251 38 : LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
252 : entry = (WaitEventExtensionEntryById *)
253 38 : hash_search(WaitEventExtensionHashById, &eventId,
254 : HASH_FIND, &found);
255 38 : LWLockRelease(WaitEventExtensionLock);
256 :
257 38 : if (!entry)
258 0 : elog(ERROR, "could not find custom wait event name for ID %u",
259 : eventId);
260 :
261 38 : return entry->wait_event_name;
262 : }
263 :
264 :
265 : /*
266 : * Returns a list of currently defined custom wait event names for extensions.
267 : * The result is a palloc'd array, with the number of elements saved in
268 : * *nwaitevents.
269 : */
270 : char **
271 8 : GetWaitEventExtensionNames(int *nwaitevents)
272 : {
273 : char **waiteventnames;
274 : WaitEventExtensionEntryByName *hentry;
275 : HASH_SEQ_STATUS hash_seq;
276 : int index;
277 : int els;
278 :
279 8 : LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
280 :
281 : /* Now we can safely count the number of entries */
282 8 : els = hash_get_num_entries(WaitEventExtensionHashByName);
283 :
284 : /* Allocate enough space for all entries */
285 8 : waiteventnames = palloc(els * sizeof(char *));
286 :
287 : /* Now scan the hash table to copy the data */
288 8 : hash_seq_init(&hash_seq, WaitEventExtensionHashByName);
289 :
290 8 : index = 0;
291 10 : while ((hentry = (WaitEventExtensionEntryByName *) hash_seq_search(&hash_seq)) != NULL)
292 : {
293 2 : waiteventnames[index] = pstrdup(hentry->wait_event_name);
294 2 : index++;
295 : }
296 :
297 8 : LWLockRelease(WaitEventExtensionLock);
298 :
299 : Assert(index == els);
300 :
301 8 : *nwaitevents = index;
302 8 : return waiteventnames;
303 : }
304 :
305 : /*
306 : * Configure wait event reporting to report wait events to *wait_event_info.
307 : * *wait_event_info needs to be valid until pgstat_reset_wait_event_storage()
308 : * is called.
309 : *
310 : * Expected to be called during backend startup, to point my_wait_event_info
311 : * into shared memory.
312 : */
313 : void
314 29878 : pgstat_set_wait_event_storage(uint32 *wait_event_info)
315 : {
316 29878 : my_wait_event_info = wait_event_info;
317 29878 : }
318 :
319 : /*
320 : * Reset wait event storage location.
321 : *
322 : * Expected to be called during backend shutdown, before the location set up
323 : * pgstat_set_wait_event_storage() becomes invalid.
324 : */
325 : void
326 29878 : pgstat_reset_wait_event_storage(void)
327 : {
328 29878 : my_wait_event_info = &local_my_wait_event_info;
329 29878 : }
330 :
331 : /* ----------
332 : * pgstat_get_wait_event_type() -
333 : *
334 : * Return a string representing the current wait event type, backend is
335 : * waiting on.
336 : */
337 : const char *
338 9012 : pgstat_get_wait_event_type(uint32 wait_event_info)
339 : {
340 : uint32 classId;
341 : const char *event_type;
342 :
343 : /* report process as not waiting. */
344 9012 : if (wait_event_info == 0)
345 1290 : return NULL;
346 :
347 7722 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
348 :
349 7722 : switch (classId)
350 : {
351 0 : case PG_WAIT_LWLOCK:
352 0 : event_type = "LWLock";
353 0 : break;
354 14 : case PG_WAIT_LOCK:
355 14 : event_type = "Lock";
356 14 : break;
357 0 : case PG_WAIT_BUFFERPIN:
358 0 : event_type = "BufferPin";
359 0 : break;
360 6586 : case PG_WAIT_ACTIVITY:
361 6586 : event_type = "Activity";
362 6586 : break;
363 1042 : case PG_WAIT_CLIENT:
364 1042 : event_type = "Client";
365 1042 : break;
366 38 : case PG_WAIT_EXTENSION:
367 38 : event_type = "Extension";
368 38 : break;
369 0 : case PG_WAIT_IPC:
370 0 : event_type = "IPC";
371 0 : break;
372 34 : case PG_WAIT_TIMEOUT:
373 34 : event_type = "Timeout";
374 34 : break;
375 8 : case PG_WAIT_IO:
376 8 : event_type = "IO";
377 8 : break;
378 0 : default:
379 0 : event_type = "???";
380 0 : break;
381 : }
382 :
383 7722 : return event_type;
384 : }
385 :
386 : /* ----------
387 : * pgstat_get_wait_event() -
388 : *
389 : * Return a string representing the current wait event, backend is
390 : * waiting on.
391 : */
392 : const char *
393 9012 : pgstat_get_wait_event(uint32 wait_event_info)
394 : {
395 : uint32 classId;
396 : uint16 eventId;
397 : const char *event_name;
398 :
399 : /* report process as not waiting. */
400 9012 : if (wait_event_info == 0)
401 1290 : return NULL;
402 :
403 7722 : classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
404 7722 : eventId = wait_event_info & WAIT_EVENT_ID_MASK;
405 :
406 7722 : switch (classId)
407 : {
408 0 : case PG_WAIT_LWLOCK:
409 0 : event_name = GetLWLockIdentifier(classId, eventId);
410 0 : break;
411 14 : case PG_WAIT_LOCK:
412 14 : event_name = GetLockNameFromTagType(eventId);
413 14 : break;
414 38 : case PG_WAIT_EXTENSION:
415 38 : event_name = GetWaitEventExtensionIdentifier(eventId);
416 38 : break;
417 0 : case PG_WAIT_BUFFERPIN:
418 : {
419 0 : WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
420 :
421 0 : event_name = pgstat_get_wait_bufferpin(w);
422 0 : break;
423 : }
424 6586 : case PG_WAIT_ACTIVITY:
425 : {
426 6586 : WaitEventActivity w = (WaitEventActivity) wait_event_info;
427 :
428 6586 : event_name = pgstat_get_wait_activity(w);
429 6586 : break;
430 : }
431 1042 : case PG_WAIT_CLIENT:
432 : {
433 1042 : WaitEventClient w = (WaitEventClient) wait_event_info;
434 :
435 1042 : event_name = pgstat_get_wait_client(w);
436 1042 : break;
437 : }
438 0 : case PG_WAIT_IPC:
439 : {
440 0 : WaitEventIPC w = (WaitEventIPC) wait_event_info;
441 :
442 0 : event_name = pgstat_get_wait_ipc(w);
443 0 : break;
444 : }
445 34 : case PG_WAIT_TIMEOUT:
446 : {
447 34 : WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
448 :
449 34 : event_name = pgstat_get_wait_timeout(w);
450 34 : break;
451 : }
452 8 : case PG_WAIT_IO:
453 : {
454 8 : WaitEventIO w = (WaitEventIO) wait_event_info;
455 :
456 8 : event_name = pgstat_get_wait_io(w);
457 8 : break;
458 : }
459 0 : default:
460 0 : event_name = "unknown wait event";
461 0 : break;
462 : }
463 :
464 7722 : return event_name;
465 : }
466 :
467 : #include "pgstat_wait_event.c"
|