PostgreSQL Source Code  git master
wait_event.c
Go to the documentation of this file.
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 
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 */
71 {
72  uint32 wait_event_info; /* hash key */
73  char wait_event_name[NAMEDATALEN]; /* custom wait event name */
75 
77 {
78  char wait_event_name[NAMEDATALEN]; /* hash key */
81 
82 
83 /* dynamic allocation counter for custom wait events */
85 {
86  int nextId; /* next ID to assign */
87  slock_t mutex; /* protects the counter */
89 
90 /* pointer to the shared memory */
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
104 {
105  Size sz;
106 
107  sz = MAXALIGN(sizeof(WaitEventCustomCounterData));
109  sizeof(WaitEventCustomEntryByInfo)));
111  sizeof(WaitEventCustomEntryByName)));
112  return sz;
113 }
114 
115 /*
116  * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
117  */
118 void
120 {
121  bool found;
122  HASHCTL info;
123 
125  ShmemInitStruct("WaitEventCustomCounterData",
126  sizeof(WaitEventCustomCounterData), &found);
127 
128  if (!found)
129  {
130  /* initialize the allocation counter and its spinlock. */
133  }
134 
135  /* initialize or attach the hash tables to store custom wait events */
136  info.keysize = sizeof(uint32);
137  info.entrysize = sizeof(WaitEventCustomEntryByInfo);
139  ShmemInitHash("WaitEventCustom hash by wait event information",
142  &info,
144 
145  /* key is a NULL-terminated string */
146  info.keysize = sizeof(char[NAMEDATALEN]);
147  info.entrysize = sizeof(WaitEventCustomEntryByName);
149  ShmemInitHash("WaitEventCustom hash by name",
152  &info,
154 }
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 WaitEventExtensionNew(const char *wait_event_name)
164 {
165  return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
166 }
167 
168 uint32
169 WaitEventInjectionPointNew(const char *wait_event_name)
170 {
171  return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
172 }
173 
174 static uint32
175 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  if (strlen(wait_event_name) >= NAMEDATALEN)
185  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  LWLockAcquire(WaitEventCustomLock, LW_SHARED);
194  entry_by_name = (WaitEventCustomEntryByName *)
195  hash_search(WaitEventCustomHashByName, wait_event_name,
196  HASH_FIND, &found);
197  LWLockRelease(WaitEventCustomLock);
198  if (found)
199  {
200  uint32 oldClassId;
201 
202  oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
203  if (oldClassId != classId)
204  ereport(ERROR,
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  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  LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
219  entry_by_name = (WaitEventCustomEntryByName *)
220  hash_search(WaitEventCustomHashByName, wait_event_name,
221  HASH_FIND, &found);
222  if (found)
223  {
224  uint32 oldClassId;
225 
226  LWLockRelease(WaitEventCustomLock);
227  oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
228  if (oldClassId != classId)
229  ereport(ERROR,
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  return entry_by_name->wait_event_info;
235  }
236 
237  /* Allocate a new event Id */
239 
241  {
243  ereport(ERROR,
244  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
245  errmsg("too many custom wait events"));
246  }
247 
248  eventId = WaitEventCustomCounter->nextId++;
249 
251 
252  /* Register the new wait event */
253  wait_event_info = classId | eventId;
254  entry_by_info = (WaitEventCustomEntryByInfo *)
255  hash_search(WaitEventCustomHashByInfo, &wait_event_info,
256  HASH_ENTER, &found);
257  Assert(!found);
258  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  hash_search(WaitEventCustomHashByName, wait_event_name,
263  HASH_ENTER, &found);
264  Assert(!found);
265  entry_by_name->wait_event_info = wait_event_info;
266 
267  LWLockRelease(WaitEventCustomLock);
268 
269  return wait_event_info;
270 }
271 
272 /*
273  * Return the name of a custom wait event information.
274  */
275 static const char *
277 {
278  bool found;
280 
281  /* Built-in event? */
282  if (wait_event_info == PG_WAIT_EXTENSION)
283  return "Extension";
284 
285  /* It is a user-defined wait event, so lookup hash table. */
286  LWLockAcquire(WaitEventCustomLock, LW_SHARED);
287  entry = (WaitEventCustomEntryByInfo *)
288  hash_search(WaitEventCustomHashByInfo, &wait_event_info,
289  HASH_FIND, &found);
290  LWLockRelease(WaitEventCustomLock);
291 
292  if (!entry)
293  elog(ERROR,
294  "could not find custom name for wait event information %u",
295  wait_event_info);
296 
297  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 GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
307 {
308  char **waiteventnames;
310  HASH_SEQ_STATUS hash_seq;
311  int index;
312  int els;
313 
314  LWLockAcquire(WaitEventCustomLock, LW_SHARED);
315 
316  /* Now we can safely count the number of entries */
318 
319  /* Allocate enough space for all entries */
320  waiteventnames = palloc(els * sizeof(char *));
321 
322  /* Now scan the hash table to copy the data */
324 
325  index = 0;
326  while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
327  {
328  if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
329  continue;
330  waiteventnames[index] = pstrdup(hentry->wait_event_name);
331  index++;
332  }
333 
334  LWLockRelease(WaitEventCustomLock);
335 
336  *nwaitevents = index;
337  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
350 {
351  my_wait_event_info = wait_event_info;
352 }
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
362 {
364 }
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 *
374 {
375  uint32 classId;
376  const char *event_type;
377 
378  /* report process as not waiting. */
379  if (wait_event_info == 0)
380  return NULL;
381 
382  classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
383 
384  switch (classId)
385  {
386  case PG_WAIT_LWLOCK:
387  event_type = "LWLock";
388  break;
389  case PG_WAIT_LOCK:
390  event_type = "Lock";
391  break;
392  case PG_WAIT_BUFFERPIN:
393  event_type = "BufferPin";
394  break;
395  case PG_WAIT_ACTIVITY:
396  event_type = "Activity";
397  break;
398  case PG_WAIT_CLIENT:
399  event_type = "Client";
400  break;
401  case PG_WAIT_EXTENSION:
402  event_type = "Extension";
403  break;
404  case PG_WAIT_IPC:
405  event_type = "IPC";
406  break;
407  case PG_WAIT_TIMEOUT:
408  event_type = "Timeout";
409  break;
410  case PG_WAIT_IO:
411  event_type = "IO";
412  break;
414  event_type = "InjectionPoint";
415  break;
416  default:
417  event_type = "???";
418  break;
419  }
420 
421  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 *
432 {
433  uint32 classId;
434  uint16 eventId;
435  const char *event_name;
436 
437  /* report process as not waiting. */
438  if (wait_event_info == 0)
439  return NULL;
440 
441  classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
442  eventId = wait_event_info & WAIT_EVENT_ID_MASK;
443 
444  switch (classId)
445  {
446  case PG_WAIT_LWLOCK:
447  event_name = GetLWLockIdentifier(classId, eventId);
448  break;
449  case PG_WAIT_LOCK:
450  event_name = GetLockNameFromTagType(eventId);
451  break;
452  case PG_WAIT_EXTENSION:
454  event_name = GetWaitEventCustomIdentifier(wait_event_info);
455  break;
456  case PG_WAIT_BUFFERPIN:
457  {
458  WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
459 
460  event_name = pgstat_get_wait_bufferpin(w);
461  break;
462  }
463  case PG_WAIT_ACTIVITY:
464  {
465  WaitEventActivity w = (WaitEventActivity) wait_event_info;
466 
467  event_name = pgstat_get_wait_activity(w);
468  break;
469  }
470  case PG_WAIT_CLIENT:
471  {
472  WaitEventClient w = (WaitEventClient) wait_event_info;
473 
474  event_name = pgstat_get_wait_client(w);
475  break;
476  }
477  case PG_WAIT_IPC:
478  {
479  WaitEventIPC w = (WaitEventIPC) wait_event_info;
480 
481  event_name = pgstat_get_wait_ipc(w);
482  break;
483  }
484  case PG_WAIT_TIMEOUT:
485  {
486  WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
487 
488  event_name = pgstat_get_wait_timeout(w);
489  break;
490  }
491  case PG_WAIT_IO:
492  {
493  WaitEventIO w = (WaitEventIO) wait_event_info;
494 
495  event_name = pgstat_get_wait_io(w);
496  break;
497  }
498  default:
499  event_name = "unknown wait event";
500  break;
501  }
502 
503  return event_name;
504 }
505 
506 #include "pgstat_wait_event.c"
#define MAXALIGN(LEN)
Definition: c.h:765
#define Assert(condition)
Definition: c.h:812
uint16_t uint16
Definition: c.h:484
uint32_t uint32
Definition: c.h:485
size_t Size
Definition: c.h:559
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1341
Size hash_estimate_size(long num_entries, Size entrysize)
Definition: dynahash.c:783
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1420
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1385
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
#define HASH_STRINGS
Definition: hsearch.h:96
@ HASH_FIND
Definition: hsearch.h:113
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
const char * GetLockNameFromTagType(uint16 locktag_type)
Definition: lmgr.c:1329
const char * GetLWLockIdentifier(uint32 classId, uint16 eventId)
Definition: lwlock.c:767
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1168
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1781
@ LW_SHARED
Definition: lwlock.h:115
@ LW_EXCLUSIVE
Definition: lwlock.h:114
char * pstrdup(const char *in)
Definition: mcxt.c:1696
void * palloc(Size size)
Definition: mcxt.c:1317
#define NAMEDATALEN
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
Size add_size(Size s1, Size s2)
Definition: shmem.c:488
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:382
HTAB * ShmemInitHash(const char *name, long init_size, long max_size, HASHCTL *infoP, int hash_flags)
Definition: shmem.c:327
#define SpinLockInit(lock)
Definition: spin.h:57
#define SpinLockRelease(lock)
Definition: spin.h:61
#define SpinLockAcquire(lock)
Definition: spin.h:59
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
Definition: dynahash.c:220
char wait_event_name[NAMEDATALEN]
Definition: wait_event.c:73
char wait_event_name[NAMEDATALEN]
Definition: wait_event.c:78
Definition: type.h:96
#define WAIT_EVENT_CUSTOM_HASH_MAX_SIZE
Definition: wait_event.c:67
struct WaitEventCustomEntryByName WaitEventCustomEntryByName
static const char * pgstat_get_wait_ipc(WaitEventIPC w)
Size WaitEventCustomShmemSize(void)
Definition: wait_event.c:103
static const char * pgstat_get_wait_io(WaitEventIO w)
#define WAIT_EVENT_CUSTOM_HASH_INIT_SIZE
Definition: wait_event.c:66
static WaitEventCustomCounterData * WaitEventCustomCounter
Definition: wait_event.c:91
const char * pgstat_get_wait_event(uint32 wait_event_info)
Definition: wait_event.c:431
void pgstat_set_wait_event_storage(uint32 *wait_event_info)
Definition: wait_event.c:349
static const char * pgstat_get_wait_bufferpin(WaitEventBufferPin w)
char ** GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
Definition: wait_event.c:306
static const char * GetWaitEventCustomIdentifier(uint32 wait_event_info)
Definition: wait_event.c:276
static const char * pgstat_get_wait_timeout(WaitEventTimeout w)
static uint32 local_my_wait_event_info
Definition: wait_event.c:39
uint32 WaitEventInjectionPointNew(const char *wait_event_name)
Definition: wait_event.c:169
const char * pgstat_get_wait_event_type(uint32 wait_event_info)
Definition: wait_event.c:373
static HTAB * WaitEventCustomHashByName
Definition: wait_event.c:64
struct WaitEventCustomEntryByInfo WaitEventCustomEntryByInfo
void pgstat_reset_wait_event_storage(void)
Definition: wait_event.c:361
struct WaitEventCustomCounterData WaitEventCustomCounterData
static const char * pgstat_get_wait_client(WaitEventClient w)
#define WAIT_EVENT_ID_MASK
Definition: wait_event.c:43
static HTAB * WaitEventCustomHashByInfo
Definition: wait_event.c:63
#define WAIT_EVENT_CLASS_MASK
Definition: wait_event.c:42
uint32 WaitEventExtensionNew(const char *wait_event_name)
Definition: wait_event.c:163
void WaitEventCustomShmemInit(void)
Definition: wait_event.c:119
static uint32 WaitEventCustomNew(uint32 classId, const char *wait_event_name)
Definition: wait_event.c:175
uint32 * my_wait_event_info
Definition: wait_event.c:40
#define WAIT_EVENT_CUSTOM_INITIAL_ID
Definition: wait_event.c:94
static const char * pgstat_get_wait_activity(WaitEventActivity w)
#define PG_WAIT_TIMEOUT
Definition: wait_event.h:25
#define PG_WAIT_INJECTIONPOINT
Definition: wait_event.h:27
#define PG_WAIT_LWLOCK
Definition: wait_event.h:18
#define PG_WAIT_BUFFERPIN
Definition: wait_event.h:20
#define PG_WAIT_IPC
Definition: wait_event.h:24
#define PG_WAIT_CLIENT
Definition: wait_event.h:22
#define PG_WAIT_EXTENSION
Definition: wait_event.h:23
#define PG_WAIT_ACTIVITY
Definition: wait_event.h:21
#define PG_WAIT_LOCK
Definition: wait_event.h:19
#define PG_WAIT_IO
Definition: wait_event.h:26