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 "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 
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
60  * WAIT_EVENT_CUSTOM_HASH_INIT_SIZE is enough for most cases, and it seems
61  * unlikely that the number of entries will reach
62  * WAIT_EVENT_CUSTOM_HASH_MAX_SIZE.
63  */
64 static HTAB *WaitEventCustomHashByInfo; /* find names from infos */
65 static HTAB *WaitEventCustomHashByName; /* find infos from names */
66 
67 #define WAIT_EVENT_CUSTOM_HASH_INIT_SIZE 16
68 #define WAIT_EVENT_CUSTOM_HASH_MAX_SIZE 128
69 
70 /* hash table entries */
72 {
73  uint32 wait_event_info; /* hash key */
74  char wait_event_name[NAMEDATALEN]; /* custom wait event name */
76 
78 {
79  char wait_event_name[NAMEDATALEN]; /* hash key */
82 
83 
84 /* dynamic allocation counter for custom wait events */
86 {
87  int nextId; /* next ID to assign */
88  slock_t mutex; /* protects the counter */
90 
91 /* pointer to the shared memory */
93 
94 /* first event ID of custom wait events */
95 #define WAIT_EVENT_CUSTOM_INITIAL_ID 1
96 
97 static uint32 WaitEventCustomNew(uint32 classId, const char *wait_event_name);
98 static const char *GetWaitEventCustomIdentifier(uint32 wait_event_info);
99 
100 /*
101  * Return the space for dynamic shared hash tables and dynamic allocation counter.
102  */
103 Size
105 {
106  Size sz;
107 
108  sz = MAXALIGN(sizeof(WaitEventCustomCounterData));
110  sizeof(WaitEventCustomEntryByInfo)));
112  sizeof(WaitEventCustomEntryByName)));
113  return sz;
114 }
115 
116 /*
117  * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
118  */
119 void
121 {
122  bool found;
123  HASHCTL info;
124 
126  ShmemInitStruct("WaitEventCustomCounterData",
127  sizeof(WaitEventCustomCounterData), &found);
128 
129  if (!found)
130  {
131  /* initialize the allocation counter and its spinlock. */
134  }
135 
136  /* initialize or attach the hash tables to store custom wait events */
137  info.keysize = sizeof(uint32);
138  info.entrysize = sizeof(WaitEventCustomEntryByInfo);
140  ShmemInitHash("WaitEventCustom hash by wait event information",
143  &info,
145 
146  /* key is a NULL-terminated string */
147  info.keysize = sizeof(char[NAMEDATALEN]);
148  info.entrysize = sizeof(WaitEventCustomEntryByName);
150  ShmemInitHash("WaitEventCustom hash by name",
153  &info,
155 }
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 WaitEventExtensionNew(const char *wait_event_name)
165 {
166  return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
167 }
168 
169 uint32
170 WaitEventInjectionPointNew(const char *wait_event_name)
171 {
172  return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
173 }
174 
175 static uint32
176 WaitEventCustomNew(uint32 classId, const char *wait_event_name)
177 {
178  uint16 eventId;
179  bool found;
180  WaitEventCustomEntryByName *entry_by_name;
181  WaitEventCustomEntryByInfo *entry_by_info;
182  uint32 wait_event_info;
183 
184  /* Check the limit of the length of the event name */
185  if (strlen(wait_event_name) >= NAMEDATALEN)
186  elog(ERROR,
187  "cannot use custom wait event string longer than %u characters",
188  NAMEDATALEN - 1);
189 
190  /*
191  * Check if the wait event info associated to the name is already defined,
192  * and return it if so.
193  */
194  LWLockAcquire(WaitEventCustomLock, LW_SHARED);
195  entry_by_name = (WaitEventCustomEntryByName *)
196  hash_search(WaitEventCustomHashByName, wait_event_name,
197  HASH_FIND, &found);
198  LWLockRelease(WaitEventCustomLock);
199  if (found)
200  {
201  uint32 oldClassId;
202 
203  oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
204  if (oldClassId != classId)
205  ereport(ERROR,
207  errmsg("wait event \"%s\" already exists in type \"%s\"",
208  wait_event_name,
209  pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
210  return entry_by_name->wait_event_info;
211  }
212 
213  /*
214  * Allocate and register a new wait event. Recheck if the event name
215  * exists, as it could be possible that a concurrent process has inserted
216  * one with the same name since the LWLock acquired again here was
217  * previously released.
218  */
219  LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
220  entry_by_name = (WaitEventCustomEntryByName *)
221  hash_search(WaitEventCustomHashByName, wait_event_name,
222  HASH_FIND, &found);
223  if (found)
224  {
225  uint32 oldClassId;
226 
227  LWLockRelease(WaitEventCustomLock);
228  oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
229  if (oldClassId != classId)
230  ereport(ERROR,
232  errmsg("wait event \"%s\" already exists in type \"%s\"",
233  wait_event_name,
234  pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
235  return entry_by_name->wait_event_info;
236  }
237 
238  /* Allocate a new event Id */
240 
242  {
244  ereport(ERROR,
245  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
246  errmsg("too many custom wait events"));
247  }
248 
249  eventId = WaitEventCustomCounter->nextId++;
250 
252 
253  /* Register the new wait event */
254  wait_event_info = classId | eventId;
255  entry_by_info = (WaitEventCustomEntryByInfo *)
256  hash_search(WaitEventCustomHashByInfo, &wait_event_info,
257  HASH_ENTER, &found);
258  Assert(!found);
259  strlcpy(entry_by_info->wait_event_name, wait_event_name,
260  sizeof(entry_by_info->wait_event_name));
261 
262  entry_by_name = (WaitEventCustomEntryByName *)
263  hash_search(WaitEventCustomHashByName, wait_event_name,
264  HASH_ENTER, &found);
265  Assert(!found);
266  entry_by_name->wait_event_info = wait_event_info;
267 
268  LWLockRelease(WaitEventCustomLock);
269 
270  return wait_event_info;
271 }
272 
273 /*
274  * Return the name of a custom wait event information.
275  */
276 static const char *
278 {
279  bool found;
281 
282  /* Built-in event? */
283  if (wait_event_info == PG_WAIT_EXTENSION)
284  return "Extension";
285 
286  /* It is a user-defined wait event, so lookup hash table. */
287  LWLockAcquire(WaitEventCustomLock, LW_SHARED);
288  entry = (WaitEventCustomEntryByInfo *)
289  hash_search(WaitEventCustomHashByInfo, &wait_event_info,
290  HASH_FIND, &found);
291  LWLockRelease(WaitEventCustomLock);
292 
293  if (!entry)
294  elog(ERROR,
295  "could not find custom name for wait event information %u",
296  wait_event_info);
297 
298  return entry->wait_event_name;
299 }
300 
301 
302 /*
303  * Returns a list of currently defined custom wait event names. The result is
304  * a palloc'd array, with the number of elements saved in *nwaitevents.
305  */
306 char **
307 GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
308 {
309  char **waiteventnames;
311  HASH_SEQ_STATUS hash_seq;
312  int index;
313  int els;
314 
315  LWLockAcquire(WaitEventCustomLock, LW_SHARED);
316 
317  /* Now we can safely count the number of entries */
319 
320  /* Allocate enough space for all entries */
321  waiteventnames = palloc(els * sizeof(char *));
322 
323  /* Now scan the hash table to copy the data */
325 
326  index = 0;
327  while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
328  {
329  if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
330  continue;
331  waiteventnames[index] = pstrdup(hentry->wait_event_name);
332  index++;
333  }
334 
335  LWLockRelease(WaitEventCustomLock);
336 
337  *nwaitevents = index;
338  return waiteventnames;
339 }
340 
341 /*
342  * Configure wait event reporting to report wait events to *wait_event_info.
343  * *wait_event_info needs to be valid until pgstat_reset_wait_event_storage()
344  * is called.
345  *
346  * Expected to be called during backend startup, to point my_wait_event_info
347  * into shared memory.
348  */
349 void
351 {
352  my_wait_event_info = wait_event_info;
353 }
354 
355 /*
356  * Reset wait event storage location.
357  *
358  * Expected to be called during backend shutdown, before the location set up
359  * pgstat_set_wait_event_storage() becomes invalid.
360  */
361 void
363 {
365 }
366 
367 /* ----------
368  * pgstat_get_wait_event_type() -
369  *
370  * Return a string representing the current wait event type, backend is
371  * waiting on.
372  */
373 const char *
375 {
376  uint32 classId;
377  const char *event_type;
378 
379  /* report process as not waiting. */
380  if (wait_event_info == 0)
381  return NULL;
382 
383  classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
384 
385  switch (classId)
386  {
387  case PG_WAIT_LWLOCK:
388  event_type = "LWLock";
389  break;
390  case PG_WAIT_LOCK:
391  event_type = "Lock";
392  break;
393  case PG_WAIT_BUFFERPIN:
394  event_type = "BufferPin";
395  break;
396  case PG_WAIT_ACTIVITY:
397  event_type = "Activity";
398  break;
399  case PG_WAIT_CLIENT:
400  event_type = "Client";
401  break;
402  case PG_WAIT_EXTENSION:
403  event_type = "Extension";
404  break;
405  case PG_WAIT_IPC:
406  event_type = "IPC";
407  break;
408  case PG_WAIT_TIMEOUT:
409  event_type = "Timeout";
410  break;
411  case PG_WAIT_IO:
412  event_type = "IO";
413  break;
415  event_type = "InjectionPoint";
416  break;
417  default:
418  event_type = "???";
419  break;
420  }
421 
422  return event_type;
423 }
424 
425 /* ----------
426  * pgstat_get_wait_event() -
427  *
428  * Return a string representing the current wait event, backend is
429  * waiting on.
430  */
431 const char *
433 {
434  uint32 classId;
435  uint16 eventId;
436  const char *event_name;
437 
438  /* report process as not waiting. */
439  if (wait_event_info == 0)
440  return NULL;
441 
442  classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
443  eventId = wait_event_info & WAIT_EVENT_ID_MASK;
444 
445  switch (classId)
446  {
447  case PG_WAIT_LWLOCK:
448  event_name = GetLWLockIdentifier(classId, eventId);
449  break;
450  case PG_WAIT_LOCK:
451  event_name = GetLockNameFromTagType(eventId);
452  break;
453  case PG_WAIT_EXTENSION:
455  event_name = GetWaitEventCustomIdentifier(wait_event_info);
456  break;
457  case PG_WAIT_BUFFERPIN:
458  {
459  WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
460 
461  event_name = pgstat_get_wait_bufferpin(w);
462  break;
463  }
464  case PG_WAIT_ACTIVITY:
465  {
466  WaitEventActivity w = (WaitEventActivity) wait_event_info;
467 
468  event_name = pgstat_get_wait_activity(w);
469  break;
470  }
471  case PG_WAIT_CLIENT:
472  {
473  WaitEventClient w = (WaitEventClient) wait_event_info;
474 
475  event_name = pgstat_get_wait_client(w);
476  break;
477  }
478  case PG_WAIT_IPC:
479  {
480  WaitEventIPC w = (WaitEventIPC) wait_event_info;
481 
482  event_name = pgstat_get_wait_ipc(w);
483  break;
484  }
485  case PG_WAIT_TIMEOUT:
486  {
487  WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
488 
489  event_name = pgstat_get_wait_timeout(w);
490  break;
491  }
492  case PG_WAIT_IO:
493  {
494  WaitEventIO w = (WaitEventIO) wait_event_info;
495 
496  event_name = pgstat_get_wait_io(w);
497  break;
498  }
499  default:
500  event_name = "unknown wait event";
501  break;
502  }
503 
504  return event_name;
505 }
506 
507 #include "pgstat_wait_event.c"
unsigned short uint16
Definition: c.h:505
unsigned int uint32
Definition: c.h:506
#define MAXALIGN(LEN)
Definition: c.h:811
#define Assert(condition)
Definition: c.h:858
size_t Size
Definition: c.h:605
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:1395
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:224
#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:1330
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
int slock_t
Definition: s_lock.h:670
Size add_size(Size s1, Size s2)
Definition: shmem.c:493
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:387
HTAB * ShmemInitHash(const char *name, long init_size, long max_size, HASHCTL *infoP, int hash_flags)
Definition: shmem.c:332
#define SpinLockInit(lock)
Definition: spin.h:60
#define SpinLockRelease(lock)
Definition: spin.h:64
#define SpinLockAcquire(lock)
Definition: spin.h:62
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
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:74
char wait_event_name[NAMEDATALEN]
Definition: wait_event.c:79
Definition: type.h:95
#define WAIT_EVENT_CUSTOM_HASH_MAX_SIZE
Definition: wait_event.c:68
struct WaitEventCustomEntryByName WaitEventCustomEntryByName
static const char * pgstat_get_wait_ipc(WaitEventIPC w)
Size WaitEventCustomShmemSize(void)
Definition: wait_event.c:104
static const char * pgstat_get_wait_io(WaitEventIO w)
#define WAIT_EVENT_CUSTOM_HASH_INIT_SIZE
Definition: wait_event.c:67
static WaitEventCustomCounterData * WaitEventCustomCounter
Definition: wait_event.c:92
const char * pgstat_get_wait_event(uint32 wait_event_info)
Definition: wait_event.c:432
void pgstat_set_wait_event_storage(uint32 *wait_event_info)
Definition: wait_event.c:350
static const char * pgstat_get_wait_bufferpin(WaitEventBufferPin w)
char ** GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
Definition: wait_event.c:307
static const char * GetWaitEventCustomIdentifier(uint32 wait_event_info)
Definition: wait_event.c:277
static const char * pgstat_get_wait_timeout(WaitEventTimeout w)
static uint32 local_my_wait_event_info
Definition: wait_event.c:40
uint32 WaitEventInjectionPointNew(const char *wait_event_name)
Definition: wait_event.c:170
const char * pgstat_get_wait_event_type(uint32 wait_event_info)
Definition: wait_event.c:374
static HTAB * WaitEventCustomHashByName
Definition: wait_event.c:65
struct WaitEventCustomEntryByInfo WaitEventCustomEntryByInfo
void pgstat_reset_wait_event_storage(void)
Definition: wait_event.c:362
struct WaitEventCustomCounterData WaitEventCustomCounterData
static const char * pgstat_get_wait_client(WaitEventClient w)
#define WAIT_EVENT_ID_MASK
Definition: wait_event.c:44
static HTAB * WaitEventCustomHashByInfo
Definition: wait_event.c:64
#define WAIT_EVENT_CLASS_MASK
Definition: wait_event.c:43
uint32 WaitEventExtensionNew(const char *wait_event_name)
Definition: wait_event.c:164
void WaitEventCustomShmemInit(void)
Definition: wait_event.c:120
static uint32 WaitEventCustomNew(uint32 classId, const char *wait_event_name)
Definition: wait_event.c:176
uint32 * my_wait_event_info
Definition: wait_event.c:41
#define WAIT_EVENT_CUSTOM_INITIAL_ID
Definition: wait_event.c:95
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