LCOV - code coverage report
Current view: top level - src/backend/utils/activity - wait_event.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 89.1 % 156 139
Test Date: 2026-04-07 14:16:30 Functions: 100.0 % 11 11
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* ----------
       2              :  * wait_event.c
       3              :  *    Wait event reporting infrastructure.
       4              :  *
       5              :  * Copyright (c) 2001-2026, 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"
      26              : #include "storage/lwlock.h"
      27              : #include "storage/shmem.h"
      28              : #include "storage/subsystems.h"
      29              : #include "storage/spin.h"
      30              : #include "utils/wait_event.h"
      31              : 
      32              : 
      33              : static const char *pgstat_get_wait_activity(WaitEventActivity w);
      34              : static const char *pgstat_get_wait_buffer(WaitEventBuffer w);
      35              : static const char *pgstat_get_wait_client(WaitEventClient w);
      36              : static const char *pgstat_get_wait_ipc(WaitEventIPC w);
      37              : static const char *pgstat_get_wait_timeout(WaitEventTimeout w);
      38              : static const char *pgstat_get_wait_io(WaitEventIO w);
      39              : 
      40              : 
      41              : static uint32 local_my_wait_event_info;
      42              : uint32     *my_wait_event_info = &local_my_wait_event_info;
      43              : 
      44              : #define WAIT_EVENT_CLASS_MASK   0xFF000000
      45              : #define WAIT_EVENT_ID_MASK      0x0000FFFF
      46              : 
      47              : /*
      48              :  * Hash tables for storing custom wait event ids and their names in
      49              :  * shared memory.
      50              :  *
      51              :  * WaitEventCustomHashByInfo is used to find the name from wait event
      52              :  * information.  Any backend can search it to find custom wait events.
      53              :  *
      54              :  * WaitEventCustomHashByName is used to find the wait event information from a
      55              :  * name.  It is used to ensure that no duplicated entries are registered.
      56              :  *
      57              :  * For simplicity, we use the same ID counter across types of custom events.
      58              :  * We could end that anytime the need arises.
      59              :  *
      60              :  * The size of the hash table is based on the assumption that usually only a
      61              :  * handful of entries are needed, but since it's small in absolute terms
      62              :  * anyway, we leave a generous amount of headroom.
      63              :  */
      64              : static HTAB *WaitEventCustomHashByInfo; /* find names from infos */
      65              : static HTAB *WaitEventCustomHashByName; /* find infos from names */
      66              : 
      67              : #define WAIT_EVENT_CUSTOM_HASH_SIZE 128
      68              : 
      69              : /* hash table entries */
      70              : typedef struct WaitEventCustomEntryByInfo
      71              : {
      72              :     uint32      wait_event_info;    /* hash key */
      73              :     char        wait_event_name[NAMEDATALEN];   /* custom wait event name */
      74              : } WaitEventCustomEntryByInfo;
      75              : 
      76              : typedef struct WaitEventCustomEntryByName
      77              : {
      78              :     char        wait_event_name[NAMEDATALEN];   /* hash key */
      79              :     uint32      wait_event_info;
      80              : } WaitEventCustomEntryByName;
      81              : 
      82              : 
      83              : /* dynamic allocation counter for custom wait events */
      84              : typedef struct WaitEventCustomCounterData
      85              : {
      86              :     int         nextId;         /* next ID to assign */
      87              :     slock_t     mutex;          /* protects the counter */
      88              : } WaitEventCustomCounterData;
      89              : 
      90              : /* pointer to the shared memory */
      91              : static WaitEventCustomCounterData *WaitEventCustomCounter;
      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              : static void WaitEventCustomShmemRequest(void *arg);
     100              : static void WaitEventCustomShmemInit(void *arg);
     101              : 
     102              : const ShmemCallbacks WaitEventCustomShmemCallbacks = {
     103              :     .request_fn = WaitEventCustomShmemRequest,
     104              :     .init_fn = WaitEventCustomShmemInit,
     105              : };
     106              : 
     107              : /*
     108              :  * Register shmem space for dynamic shared hash and dynamic allocation counter.
     109              :  */
     110              : static void
     111         1234 : WaitEventCustomShmemRequest(void *arg)
     112              : {
     113         1234 :     ShmemRequestStruct(.name = "WaitEventCustomCounterData",
     114              :                        .size = sizeof(WaitEventCustomCounterData),
     115              :                        .ptr = (void **) &WaitEventCustomCounter,
     116              :         );
     117         1234 :     ShmemRequestHash(.name = "WaitEventCustom hash by wait event information",
     118              :                      .ptr = &WaitEventCustomHashByInfo,
     119              :                      .nelems = WAIT_EVENT_CUSTOM_HASH_SIZE,
     120              :                      .hash_info.keysize = sizeof(uint32),
     121              :                      .hash_info.entrysize = sizeof(WaitEventCustomEntryByInfo),
     122              :                      .hash_flags = HASH_ELEM | HASH_BLOBS,
     123              :         );
     124         1234 :     ShmemRequestHash(.name = "WaitEventCustom hash by name",
     125              :                      .ptr = &WaitEventCustomHashByName,
     126              :                      .nelems = WAIT_EVENT_CUSTOM_HASH_SIZE,
     127              :     /* key is a NULL-terminated string */
     128              :                      .hash_info.keysize = sizeof(char[NAMEDATALEN]),
     129              :                      .hash_info.entrysize = sizeof(WaitEventCustomEntryByName),
     130              :                      .hash_flags = HASH_ELEM | HASH_STRINGS,
     131              :         );
     132         1234 : }
     133              : 
     134              : static void
     135         1231 : WaitEventCustomShmemInit(void *arg)
     136              : {
     137              :     /* initialize the allocation counter and its spinlock. */
     138         1231 :     WaitEventCustomCounter->nextId = WAIT_EVENT_CUSTOM_INITIAL_ID;
     139         1231 :     SpinLockInit(&WaitEventCustomCounter->mutex);
     140         1231 : }
     141              : 
     142              : /*
     143              :  * Allocate a new event ID and return the wait event info.
     144              :  *
     145              :  * If the wait event name is already defined, this does not allocate a new
     146              :  * entry; it returns the wait event information associated to the name.
     147              :  */
     148              : uint32
     149           48 : WaitEventExtensionNew(const char *wait_event_name)
     150              : {
     151           48 :     return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
     152              : }
     153              : 
     154              : uint32
     155           75 : WaitEventInjectionPointNew(const char *wait_event_name)
     156              : {
     157           75 :     return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
     158              : }
     159              : 
     160              : static uint32
     161          123 : WaitEventCustomNew(uint32 classId, const char *wait_event_name)
     162              : {
     163              :     uint16      eventId;
     164              :     bool        found;
     165              :     WaitEventCustomEntryByName *entry_by_name;
     166              :     WaitEventCustomEntryByInfo *entry_by_info;
     167              :     uint32      wait_event_info;
     168              : 
     169              :     /* Check the limit of the length of the event name */
     170          123 :     if (strlen(wait_event_name) >= NAMEDATALEN)
     171            0 :         elog(ERROR,
     172              :              "cannot use custom wait event string longer than %u characters",
     173              :              NAMEDATALEN - 1);
     174              : 
     175              :     /*
     176              :      * Check if the wait event info associated to the name is already defined,
     177              :      * and return it if so.
     178              :      */
     179          123 :     LWLockAcquire(WaitEventCustomLock, LW_SHARED);
     180              :     entry_by_name = (WaitEventCustomEntryByName *)
     181          123 :         hash_search(WaitEventCustomHashByName, wait_event_name,
     182              :                     HASH_FIND, &found);
     183          123 :     LWLockRelease(WaitEventCustomLock);
     184          123 :     if (found)
     185              :     {
     186              :         uint32      oldClassId;
     187              : 
     188           73 :         oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
     189           73 :         if (oldClassId != classId)
     190            0 :             ereport(ERROR,
     191              :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
     192              :                      errmsg("wait event \"%s\" already exists in type \"%s\"",
     193              :                             wait_event_name,
     194              :                             pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
     195           73 :         return entry_by_name->wait_event_info;
     196              :     }
     197              : 
     198              :     /*
     199              :      * Allocate and register a new wait event.  Recheck if the event name
     200              :      * exists, as it could be possible that a concurrent process has inserted
     201              :      * one with the same name since the LWLock acquired again here was
     202              :      * previously released.
     203              :      */
     204           50 :     LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
     205              :     entry_by_name = (WaitEventCustomEntryByName *)
     206           50 :         hash_search(WaitEventCustomHashByName, wait_event_name,
     207              :                     HASH_FIND, &found);
     208           50 :     if (found)
     209              :     {
     210              :         uint32      oldClassId;
     211              : 
     212            0 :         LWLockRelease(WaitEventCustomLock);
     213            0 :         oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
     214            0 :         if (oldClassId != classId)
     215            0 :             ereport(ERROR,
     216              :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
     217              :                      errmsg("wait event \"%s\" already exists in type \"%s\"",
     218              :                             wait_event_name,
     219              :                             pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
     220            0 :         return entry_by_name->wait_event_info;
     221              :     }
     222              : 
     223              :     /* Allocate a new event Id */
     224           50 :     SpinLockAcquire(&WaitEventCustomCounter->mutex);
     225              : 
     226           50 :     if (WaitEventCustomCounter->nextId >= WAIT_EVENT_CUSTOM_HASH_SIZE)
     227              :     {
     228            0 :         SpinLockRelease(&WaitEventCustomCounter->mutex);
     229            0 :         ereport(ERROR,
     230              :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     231              :                 errmsg("too many custom wait events"));
     232              :     }
     233              : 
     234           50 :     eventId = WaitEventCustomCounter->nextId++;
     235              : 
     236           50 :     SpinLockRelease(&WaitEventCustomCounter->mutex);
     237              : 
     238              :     /* Register the new wait event */
     239           50 :     wait_event_info = classId | eventId;
     240              :     entry_by_info = (WaitEventCustomEntryByInfo *)
     241           50 :         hash_search(WaitEventCustomHashByInfo, &wait_event_info,
     242              :                     HASH_ENTER, &found);
     243              :     Assert(!found);
     244           50 :     strlcpy(entry_by_info->wait_event_name, wait_event_name,
     245              :             sizeof(entry_by_info->wait_event_name));
     246              : 
     247              :     entry_by_name = (WaitEventCustomEntryByName *)
     248           50 :         hash_search(WaitEventCustomHashByName, wait_event_name,
     249              :                     HASH_ENTER, &found);
     250              :     Assert(!found);
     251           50 :     entry_by_name->wait_event_info = wait_event_info;
     252              : 
     253           50 :     LWLockRelease(WaitEventCustomLock);
     254              : 
     255           50 :     return wait_event_info;
     256              : }
     257              : 
     258              : /*
     259              :  * Return the name of a custom wait event information.
     260              :  */
     261              : static const char *
     262          146 : GetWaitEventCustomIdentifier(uint32 wait_event_info)
     263              : {
     264              :     bool        found;
     265              :     WaitEventCustomEntryByInfo *entry;
     266              : 
     267              :     /* Built-in event? */
     268          146 :     if (wait_event_info == PG_WAIT_EXTENSION)
     269            0 :         return "Extension";
     270              : 
     271              :     /* It is a user-defined wait event, so lookup hash table. */
     272          146 :     LWLockAcquire(WaitEventCustomLock, LW_SHARED);
     273              :     entry = (WaitEventCustomEntryByInfo *)
     274          146 :         hash_search(WaitEventCustomHashByInfo, &wait_event_info,
     275              :                     HASH_FIND, &found);
     276          146 :     LWLockRelease(WaitEventCustomLock);
     277              : 
     278          146 :     if (!entry)
     279            0 :         elog(ERROR,
     280              :              "could not find custom name for wait event information %u",
     281              :              wait_event_info);
     282              : 
     283          146 :     return entry->wait_event_name;
     284              : }
     285              : 
     286              : 
     287              : /*
     288              :  * Returns a list of currently defined custom wait event names.  The result is
     289              :  * a palloc'd array, with the number of elements saved in *nwaitevents.
     290              :  */
     291              : char      **
     292           10 : GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
     293              : {
     294              :     char      **waiteventnames;
     295              :     WaitEventCustomEntryByName *hentry;
     296              :     HASH_SEQ_STATUS hash_seq;
     297              :     int         index;
     298              :     int         els;
     299              : 
     300           10 :     LWLockAcquire(WaitEventCustomLock, LW_SHARED);
     301              : 
     302              :     /* Now we can safely count the number of entries */
     303           10 :     els = hash_get_num_entries(WaitEventCustomHashByName);
     304              : 
     305              :     /* Allocate enough space for all entries */
     306           10 :     waiteventnames = palloc_array(char *, els);
     307              : 
     308              :     /* Now scan the hash table to copy the data */
     309           10 :     hash_seq_init(&hash_seq, WaitEventCustomHashByName);
     310              : 
     311           10 :     index = 0;
     312           12 :     while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
     313              :     {
     314            2 :         if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
     315            1 :             continue;
     316            1 :         waiteventnames[index] = pstrdup(hentry->wait_event_name);
     317            1 :         index++;
     318              :     }
     319              : 
     320           10 :     LWLockRelease(WaitEventCustomLock);
     321              : 
     322           10 :     *nwaitevents = index;
     323           10 :     return waiteventnames;
     324              : }
     325              : 
     326              : /*
     327              :  * Configure wait event reporting to report wait events to *wait_event_info.
     328              :  * *wait_event_info needs to be valid until pgstat_reset_wait_event_storage()
     329              :  * is called.
     330              :  *
     331              :  * Expected to be called during backend startup, to point my_wait_event_info
     332              :  * into shared memory.
     333              :  */
     334              : void
     335        24786 : pgstat_set_wait_event_storage(uint32 *wait_event_info)
     336              : {
     337        24786 :     my_wait_event_info = wait_event_info;
     338        24786 : }
     339              : 
     340              : /*
     341              :  * Reset wait event storage location.
     342              :  *
     343              :  * Expected to be called during backend shutdown, before the location set up
     344              :  * pgstat_set_wait_event_storage() becomes invalid.
     345              :  */
     346              : void
     347        24786 : pgstat_reset_wait_event_storage(void)
     348              : {
     349        24786 :     my_wait_event_info = &local_my_wait_event_info;
     350        24786 : }
     351              : 
     352              : /* ----------
     353              :  * pgstat_get_wait_event_type() -
     354              :  *
     355              :  *  Return a string representing the current wait event type, backend is
     356              :  *  waiting on.
     357              :  */
     358              : const char *
     359         9714 : pgstat_get_wait_event_type(uint32 wait_event_info)
     360              : {
     361              :     uint32      classId;
     362              :     const char *event_type;
     363              : 
     364              :     /* report process as not waiting. */
     365         9714 :     if (wait_event_info == 0)
     366         1065 :         return NULL;
     367              : 
     368         8649 :     classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
     369              : 
     370         8649 :     switch (classId)
     371              :     {
     372           19 :         case PG_WAIT_LWLOCK:
     373           19 :             event_type = "LWLock";
     374           19 :             break;
     375         1261 :         case PG_WAIT_LOCK:
     376         1261 :             event_type = "Lock";
     377         1261 :             break;
     378            4 :         case PG_WAIT_BUFFER:
     379            4 :             event_type = "Buffer";
     380            4 :             break;
     381         6115 :         case PG_WAIT_ACTIVITY:
     382         6115 :             event_type = "Activity";
     383         6115 :             break;
     384          769 :         case PG_WAIT_CLIENT:
     385          769 :             event_type = "Client";
     386          769 :             break;
     387           44 :         case PG_WAIT_EXTENSION:
     388           44 :             event_type = "Extension";
     389           44 :             break;
     390          183 :         case PG_WAIT_IPC:
     391          183 :             event_type = "IPC";
     392          183 :             break;
     393           28 :         case PG_WAIT_TIMEOUT:
     394           28 :             event_type = "Timeout";
     395           28 :             break;
     396           43 :         case PG_WAIT_IO:
     397           43 :             event_type = "IO";
     398           43 :             break;
     399          183 :         case PG_WAIT_INJECTIONPOINT:
     400          183 :             event_type = "InjectionPoint";
     401          183 :             break;
     402            0 :         default:
     403            0 :             event_type = "???";
     404            0 :             break;
     405              :     }
     406              : 
     407         8649 :     return event_type;
     408              : }
     409              : 
     410              : /* ----------
     411              :  * pgstat_get_wait_event() -
     412              :  *
     413              :  *  Return a string representing the current wait event, backend is
     414              :  *  waiting on.
     415              :  */
     416              : const char *
     417         7994 : pgstat_get_wait_event(uint32 wait_event_info)
     418              : {
     419              :     uint32      classId;
     420              :     uint16      eventId;
     421              :     const char *event_name;
     422              : 
     423              :     /* report process as not waiting. */
     424         7994 :     if (wait_event_info == 0)
     425          855 :         return NULL;
     426              : 
     427         7139 :     classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
     428         7139 :     eventId = wait_event_info & WAIT_EVENT_ID_MASK;
     429              : 
     430         7139 :     switch (classId)
     431              :     {
     432           19 :         case PG_WAIT_LWLOCK:
     433           19 :             event_name = GetLWLockIdentifier(classId, eventId);
     434           19 :             break;
     435           13 :         case PG_WAIT_LOCK:
     436           13 :             event_name = GetLockNameFromTagType(eventId);
     437           13 :             break;
     438          146 :         case PG_WAIT_EXTENSION:
     439              :         case PG_WAIT_INJECTIONPOINT:
     440          146 :             event_name = GetWaitEventCustomIdentifier(wait_event_info);
     441          146 :             break;
     442            4 :         case PG_WAIT_BUFFER:
     443              :             {
     444            4 :                 WaitEventBuffer w = (WaitEventBuffer) wait_event_info;
     445              : 
     446            4 :                 event_name = pgstat_get_wait_buffer(w);
     447            4 :                 break;
     448              :             }
     449         6115 :         case PG_WAIT_ACTIVITY:
     450              :             {
     451         6115 :                 WaitEventActivity w = (WaitEventActivity) wait_event_info;
     452              : 
     453         6115 :                 event_name = pgstat_get_wait_activity(w);
     454         6115 :                 break;
     455              :             }
     456          764 :         case PG_WAIT_CLIENT:
     457              :             {
     458          764 :                 WaitEventClient w = (WaitEventClient) wait_event_info;
     459              : 
     460          764 :                 event_name = pgstat_get_wait_client(w);
     461          764 :                 break;
     462              :             }
     463           24 :         case PG_WAIT_IPC:
     464              :             {
     465           24 :                 WaitEventIPC w = (WaitEventIPC) wait_event_info;
     466              : 
     467           24 :                 event_name = pgstat_get_wait_ipc(w);
     468           24 :                 break;
     469              :             }
     470           28 :         case PG_WAIT_TIMEOUT:
     471              :             {
     472           28 :                 WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
     473              : 
     474           28 :                 event_name = pgstat_get_wait_timeout(w);
     475           28 :                 break;
     476              :             }
     477           26 :         case PG_WAIT_IO:
     478              :             {
     479           26 :                 WaitEventIO w = (WaitEventIO) wait_event_info;
     480              : 
     481           26 :                 event_name = pgstat_get_wait_io(w);
     482           26 :                 break;
     483              :             }
     484            0 :         default:
     485            0 :             event_name = "unknown wait event";
     486            0 :             break;
     487              :     }
     488              : 
     489         7139 :     return event_name;
     490              : }
     491              : 
     492              : #include "utils/pgstat_wait_event.c"
        

Generated by: LCOV version 2.0-1