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