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-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 
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 */
71 {
72  uint16 event_id; /* hash key */
73  char wait_event_name[NAMEDATALEN]; /* custom wait event name */
75 
77 {
78  char wait_event_name[NAMEDATALEN]; /* hash key */
79  uint16 event_id; /* wait event ID */
81 
82 
83 /* dynamic allocation counter for custom wait events in extensions */
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 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
107 {
108  Size sz;
109 
112  sizeof(WaitEventExtensionEntryById)));
115  return sz;
116 }
117 
118 /*
119  * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
120  */
121 void
123 {
124  bool found;
125  HASHCTL info;
126 
128  ShmemInitStruct("WaitEventExtensionCounterData",
129  sizeof(WaitEventExtensionCounterData), &found);
130 
131  if (!found)
132  {
133  /* initialize the allocation counter and its spinlock. */
136  }
137 
138  /* initialize or attach the hash tables to store custom wait events */
139  info.keysize = sizeof(uint16);
140  info.entrysize = sizeof(WaitEventExtensionEntryById);
141  WaitEventExtensionHashById = ShmemInitHash("WaitEventExtension hash by id",
144  &info,
146 
147  /* key is a NULL-terminated string */
148  info.keysize = sizeof(char[NAMEDATALEN]);
150  WaitEventExtensionHashByName = ShmemInitHash("WaitEventExtension 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  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  if (strlen(wait_event_name) >= NAMEDATALEN)
173  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  LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
182  entry_by_name = (WaitEventExtensionEntryByName *)
183  hash_search(WaitEventExtensionHashByName, wait_event_name,
184  HASH_FIND, &found);
185  LWLockRelease(WaitEventExtensionLock);
186  if (found)
187  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  LWLockAcquire(WaitEventExtensionLock, LW_EXCLUSIVE);
196  entry_by_name = (WaitEventExtensionEntryByName *)
197  hash_search(WaitEventExtensionHashByName, wait_event_name,
198  HASH_FIND, &found);
199  if (found)
200  {
201  LWLockRelease(WaitEventExtensionLock);
202  return WAIT_EVENT_EXTENSION_INFO(entry_by_name->event_id);
203  }
204 
205  /* Allocate a new event Id */
207 
209  {
211  ereport(ERROR,
212  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
213  errmsg("too many wait events for extensions"));
214  }
215 
216  eventId = WaitEventExtensionCounter->nextId++;
217 
219 
220  /* Register the new wait event */
221  entry_by_id = (WaitEventExtensionEntryById *)
223  HASH_ENTER, &found);
224  Assert(!found);
225  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  hash_search(WaitEventExtensionHashByName, wait_event_name,
230  HASH_ENTER, &found);
231  Assert(!found);
232  entry_by_name->event_id = eventId;
233 
234  LWLockRelease(WaitEventExtensionLock);
235 
236  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 *
244 {
245  bool found;
247 
248  /* Built-in event? */
249  if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION)
250  return "Extension";
251 
252  /* It is a user-defined wait event, so lookup hash table. */
253  LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
254  entry = (WaitEventExtensionEntryById *)
256  HASH_FIND, &found);
257  LWLockRelease(WaitEventExtensionLock);
258 
259  if (!entry)
260  elog(ERROR, "could not find custom wait event name for ID %u",
261  eventId);
262 
263  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 GetWaitEventExtensionNames(int *nwaitevents)
274 {
275  char **waiteventnames;
277  HASH_SEQ_STATUS hash_seq;
278  int index;
279  int els;
280 
281  LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
282 
283  /* Now we can safely count the number of entries */
285 
286  /* Allocate enough space for all entries */
287  waiteventnames = palloc(els * sizeof(char *));
288 
289  /* Now scan the hash table to copy the data */
291 
292  index = 0;
293  while ((hentry = (WaitEventExtensionEntryByName *) hash_seq_search(&hash_seq)) != NULL)
294  {
295  waiteventnames[index] = pstrdup(hentry->wait_event_name);
296  index++;
297  }
298 
299  LWLockRelease(WaitEventExtensionLock);
300 
301  Assert(index == els);
302 
303  *nwaitevents = index;
304  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
317 {
318  my_wait_event_info = wait_event_info;
319 }
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
329 {
331 }
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 *
341 {
342  uint32 classId;
343  const char *event_type;
344 
345  /* report process as not waiting. */
346  if (wait_event_info == 0)
347  return NULL;
348 
349  classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
350 
351  switch (classId)
352  {
353  case PG_WAIT_LWLOCK:
354  event_type = "LWLock";
355  break;
356  case PG_WAIT_LOCK:
357  event_type = "Lock";
358  break;
359  case PG_WAIT_BUFFERPIN:
360  event_type = "BufferPin";
361  break;
362  case PG_WAIT_ACTIVITY:
363  event_type = "Activity";
364  break;
365  case PG_WAIT_CLIENT:
366  event_type = "Client";
367  break;
368  case PG_WAIT_EXTENSION:
369  event_type = "Extension";
370  break;
371  case PG_WAIT_IPC:
372  event_type = "IPC";
373  break;
374  case PG_WAIT_TIMEOUT:
375  event_type = "Timeout";
376  break;
377  case PG_WAIT_IO:
378  event_type = "IO";
379  break;
380  default:
381  event_type = "???";
382  break;
383  }
384 
385  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 *
396 {
397  uint32 classId;
398  uint16 eventId;
399  const char *event_name;
400 
401  /* report process as not waiting. */
402  if (wait_event_info == 0)
403  return NULL;
404 
405  classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
406  eventId = wait_event_info & WAIT_EVENT_ID_MASK;
407 
408  switch (classId)
409  {
410  case PG_WAIT_LWLOCK:
411  event_name = GetLWLockIdentifier(classId, eventId);
412  break;
413  case PG_WAIT_LOCK:
414  event_name = GetLockNameFromTagType(eventId);
415  break;
416  case PG_WAIT_EXTENSION:
417  event_name = GetWaitEventExtensionIdentifier(eventId);
418  break;
419  case PG_WAIT_BUFFERPIN:
420  {
421  WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
422 
423  event_name = pgstat_get_wait_bufferpin(w);
424  break;
425  }
426  case PG_WAIT_ACTIVITY:
427  {
428  WaitEventActivity w = (WaitEventActivity) wait_event_info;
429 
430  event_name = pgstat_get_wait_activity(w);
431  break;
432  }
433  case PG_WAIT_CLIENT:
434  {
435  WaitEventClient w = (WaitEventClient) wait_event_info;
436 
437  event_name = pgstat_get_wait_client(w);
438  break;
439  }
440  case PG_WAIT_IPC:
441  {
442  WaitEventIPC w = (WaitEventIPC) wait_event_info;
443 
444  event_name = pgstat_get_wait_ipc(w);
445  break;
446  }
447  case PG_WAIT_TIMEOUT:
448  {
449  WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
450 
451  event_name = pgstat_get_wait_timeout(w);
452  break;
453  }
454  case PG_WAIT_IO:
455  {
456  WaitEventIO w = (WaitEventIO) wait_event_info;
457 
458  event_name = pgstat_get_wait_io(w);
459  break;
460  }
461  default:
462  event_name = "unknown wait event";
463  break;
464  }
465 
466  return event_name;
467 }
468 
469 #include "pgstat_wait_event.c"
unsigned short uint16
Definition: c.h:494
unsigned int uint32
Definition: c.h:495
#define MAXALIGN(LEN)
Definition: c.h:800
size_t Size
Definition: c.h:594
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:953
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1377
Size hash_estimate_size(long num_entries, Size entrysize)
Definition: dynahash.c:781
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1431
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1421
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ERROR
Definition: elog.h:39
#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
Assert(fmt[strlen(fmt) - 1] !='\n')
const char * GetLockNameFromTagType(uint16 locktag_type)
Definition: lmgr.c:1303
const char * GetLWLockIdentifier(uint32 classId, uint16 eventId)
Definition: lwlock.c:794
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1195
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1808
@ LW_SHARED
Definition: lwlock.h:117
@ LW_EXCLUSIVE
Definition: lwlock.h:116
char * pstrdup(const char *in)
Definition: mcxt.c:1644
void * palloc(Size size)
Definition: mcxt.c:1226
#define NAMEDATALEN
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
int slock_t
Definition: s_lock.h:754
Size add_size(Size s1, Size s2)
Definition: shmem.c:502
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:396
HTAB * ShmemInitHash(const char *name, long init_size, long max_size, HASHCTL *infoP, int hash_flags)
Definition: shmem.c:341
#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:73
char wait_event_name[NAMEDATALEN]
Definition: wait_event.c:78
Definition: type.h:95
static HTAB * WaitEventExtensionHashByName
Definition: wait_event.c:64
#define NUM_BUILTIN_WAIT_EVENT_EXTENSION
Definition: wait_event.c:94
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:395
void pgstat_set_wait_event_storage(uint32 *wait_event_info)
Definition: wait_event.c:316
static const char * pgstat_get_wait_bufferpin(WaitEventBufferPin w)
struct WaitEventExtensionCounterData WaitEventExtensionCounterData
Size WaitEventExtensionShmemSize(void)
Definition: wait_event.c:106
struct WaitEventExtensionEntryByName WaitEventExtensionEntryByName
static WaitEventExtensionCounterData * WaitEventExtensionCounter
Definition: wait_event.c:91
#define WAIT_EVENT_EXTENSION_INFO(eventId)
Definition: wait_event.c:98
static const char * pgstat_get_wait_timeout(WaitEventTimeout w)
static uint32 local_my_wait_event_info
Definition: wait_event.c:42
const char * pgstat_get_wait_event_type(uint32 wait_event_info)
Definition: wait_event.c:340
static HTAB * WaitEventExtensionHashById
Definition: wait_event.c:63
void pgstat_reset_wait_event_storage(void)
Definition: wait_event.c:328
#define WAIT_EVENT_EXTENSION_HASH_MAX_SIZE
Definition: wait_event.c:67
static const char * pgstat_get_wait_client(WaitEventClient w)
#define WAIT_EVENT_ID_MASK
Definition: wait_event.c:46
char ** GetWaitEventExtensionNames(int *nwaitevents)
Definition: wait_event.c:273
#define WAIT_EVENT_CLASS_MASK
Definition: wait_event.c:45
uint32 WaitEventExtensionNew(const char *wait_event_name)
Definition: wait_event.c:164
void WaitEventExtensionShmemInit(void)
Definition: wait_event.c:122
#define WAIT_EVENT_EXTENSION_HASH_INIT_SIZE
Definition: wait_event.c:66
struct WaitEventExtensionEntryById WaitEventExtensionEntryById
uint32 * my_wait_event_info
Definition: wait_event.c:43
static const char * GetWaitEventExtensionIdentifier(uint16 eventId)
Definition: wait_event.c:243
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