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