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.8 % 167 150
Test Date: 2026-03-10 03:14:40 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"     /* 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_buffer(WaitEventBuffer 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              : 
      39              : static uint32 local_my_wait_event_info;
      40              : uint32     *my_wait_event_info = &local_my_wait_event_info;
      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 */
      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              : /*
     100              :  *  Return the space for dynamic shared hash tables and dynamic allocation counter.
     101              :  */
     102              : Size
     103         2163 : WaitEventCustomShmemSize(void)
     104              : {
     105              :     Size        sz;
     106              : 
     107         2163 :     sz = MAXALIGN(sizeof(WaitEventCustomCounterData));
     108         2163 :     sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
     109              :                                          sizeof(WaitEventCustomEntryByInfo)));
     110         2163 :     sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
     111              :                                          sizeof(WaitEventCustomEntryByName)));
     112         2163 :     return sz;
     113              : }
     114              : 
     115              : /*
     116              :  * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
     117              :  */
     118              : void
     119         1158 : WaitEventCustomShmemInit(void)
     120              : {
     121              :     bool        found;
     122              :     HASHCTL     info;
     123              : 
     124         1158 :     WaitEventCustomCounter = (WaitEventCustomCounterData *)
     125         1158 :         ShmemInitStruct("WaitEventCustomCounterData",
     126              :                         sizeof(WaitEventCustomCounterData), &found);
     127              : 
     128         1158 :     if (!found)
     129              :     {
     130              :         /* initialize the allocation counter and its spinlock. */
     131         1158 :         WaitEventCustomCounter->nextId = WAIT_EVENT_CUSTOM_INITIAL_ID;
     132         1158 :         SpinLockInit(&WaitEventCustomCounter->mutex);
     133              :     }
     134              : 
     135              :     /* initialize or attach the hash tables to store custom wait events */
     136         1158 :     info.keysize = sizeof(uint32);
     137         1158 :     info.entrysize = sizeof(WaitEventCustomEntryByInfo);
     138         1158 :     WaitEventCustomHashByInfo =
     139         1158 :         ShmemInitHash("WaitEventCustom hash by wait event information",
     140              :                       WAIT_EVENT_CUSTOM_HASH_INIT_SIZE,
     141              :                       WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
     142              :                       &info,
     143              :                       HASH_ELEM | HASH_BLOBS);
     144              : 
     145              :     /* key is a NULL-terminated string */
     146         1158 :     info.keysize = sizeof(char[NAMEDATALEN]);
     147         1158 :     info.entrysize = sizeof(WaitEventCustomEntryByName);
     148         1158 :     WaitEventCustomHashByName =
     149         1158 :         ShmemInitHash("WaitEventCustom hash by name",
     150              :                       WAIT_EVENT_CUSTOM_HASH_INIT_SIZE,
     151              :                       WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
     152              :                       &info,
     153              :                       HASH_ELEM | HASH_STRINGS);
     154         1158 : }
     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           54 : WaitEventExtensionNew(const char *wait_event_name)
     164              : {
     165           54 :     return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
     166              : }
     167              : 
     168              : uint32
     169           63 : WaitEventInjectionPointNew(const char *wait_event_name)
     170              : {
     171           63 :     return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
     172              : }
     173              : 
     174              : static uint32
     175          117 : 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          117 :     if (strlen(wait_event_name) >= NAMEDATALEN)
     185            0 :         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          117 :     LWLockAcquire(WaitEventCustomLock, LW_SHARED);
     194              :     entry_by_name = (WaitEventCustomEntryByName *)
     195          117 :         hash_search(WaitEventCustomHashByName, wait_event_name,
     196              :                     HASH_FIND, &found);
     197          117 :     LWLockRelease(WaitEventCustomLock);
     198          117 :     if (found)
     199              :     {
     200              :         uint32      oldClassId;
     201              : 
     202           74 :         oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
     203           74 :         if (oldClassId != classId)
     204            0 :             ereport(ERROR,
     205              :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
     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           74 :         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           43 :     LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
     219              :     entry_by_name = (WaitEventCustomEntryByName *)
     220           43 :         hash_search(WaitEventCustomHashByName, wait_event_name,
     221              :                     HASH_FIND, &found);
     222           43 :     if (found)
     223              :     {
     224              :         uint32      oldClassId;
     225              : 
     226            0 :         LWLockRelease(WaitEventCustomLock);
     227            0 :         oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
     228            0 :         if (oldClassId != classId)
     229            0 :             ereport(ERROR,
     230              :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
     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            0 :         return entry_by_name->wait_event_info;
     235              :     }
     236              : 
     237              :     /* Allocate a new event Id */
     238           43 :     SpinLockAcquire(&WaitEventCustomCounter->mutex);
     239              : 
     240           43 :     if (WaitEventCustomCounter->nextId >= WAIT_EVENT_CUSTOM_HASH_MAX_SIZE)
     241              :     {
     242            0 :         SpinLockRelease(&WaitEventCustomCounter->mutex);
     243            0 :         ereport(ERROR,
     244              :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     245              :                 errmsg("too many custom wait events"));
     246              :     }
     247              : 
     248           43 :     eventId = WaitEventCustomCounter->nextId++;
     249              : 
     250           43 :     SpinLockRelease(&WaitEventCustomCounter->mutex);
     251              : 
     252              :     /* Register the new wait event */
     253           43 :     wait_event_info = classId | eventId;
     254              :     entry_by_info = (WaitEventCustomEntryByInfo *)
     255           43 :         hash_search(WaitEventCustomHashByInfo, &wait_event_info,
     256              :                     HASH_ENTER, &found);
     257              :     Assert(!found);
     258           43 :     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           43 :         hash_search(WaitEventCustomHashByName, wait_event_name,
     263              :                     HASH_ENTER, &found);
     264              :     Assert(!found);
     265           43 :     entry_by_name->wait_event_info = wait_event_info;
     266              : 
     267           43 :     LWLockRelease(WaitEventCustomLock);
     268              : 
     269           43 :     return wait_event_info;
     270              : }
     271              : 
     272              : /*
     273              :  * Return the name of a custom wait event information.
     274              :  */
     275              : static const char *
     276          123 : GetWaitEventCustomIdentifier(uint32 wait_event_info)
     277              : {
     278              :     bool        found;
     279              :     WaitEventCustomEntryByInfo *entry;
     280              : 
     281              :     /* Built-in event? */
     282          123 :     if (wait_event_info == PG_WAIT_EXTENSION)
     283            0 :         return "Extension";
     284              : 
     285              :     /* It is a user-defined wait event, so lookup hash table. */
     286          123 :     LWLockAcquire(WaitEventCustomLock, LW_SHARED);
     287              :     entry = (WaitEventCustomEntryByInfo *)
     288          123 :         hash_search(WaitEventCustomHashByInfo, &wait_event_info,
     289              :                     HASH_FIND, &found);
     290          123 :     LWLockRelease(WaitEventCustomLock);
     291              : 
     292          123 :     if (!entry)
     293            0 :         elog(ERROR,
     294              :              "could not find custom name for wait event information %u",
     295              :              wait_event_info);
     296              : 
     297          123 :     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            8 : GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
     307              : {
     308              :     char      **waiteventnames;
     309              :     WaitEventCustomEntryByName *hentry;
     310              :     HASH_SEQ_STATUS hash_seq;
     311              :     int         index;
     312              :     int         els;
     313              : 
     314            8 :     LWLockAcquire(WaitEventCustomLock, LW_SHARED);
     315              : 
     316              :     /* Now we can safely count the number of entries */
     317            8 :     els = hash_get_num_entries(WaitEventCustomHashByName);
     318              : 
     319              :     /* Allocate enough space for all entries */
     320            8 :     waiteventnames = palloc_array(char *, els);
     321              : 
     322              :     /* Now scan the hash table to copy the data */
     323            8 :     hash_seq_init(&hash_seq, WaitEventCustomHashByName);
     324              : 
     325            8 :     index = 0;
     326           10 :     while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
     327              :     {
     328            2 :         if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
     329            1 :             continue;
     330            1 :         waiteventnames[index] = pstrdup(hentry->wait_event_name);
     331            1 :         index++;
     332              :     }
     333              : 
     334            8 :     LWLockRelease(WaitEventCustomLock);
     335              : 
     336            8 :     *nwaitevents = index;
     337            8 :     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
     349        23301 : pgstat_set_wait_event_storage(uint32 *wait_event_info)
     350              : {
     351        23301 :     my_wait_event_info = wait_event_info;
     352        23301 : }
     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
     361        23301 : pgstat_reset_wait_event_storage(void)
     362              : {
     363        23301 :     my_wait_event_info = &local_my_wait_event_info;
     364        23301 : }
     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 *
     373        11454 : pgstat_get_wait_event_type(uint32 wait_event_info)
     374              : {
     375              :     uint32      classId;
     376              :     const char *event_type;
     377              : 
     378              :     /* report process as not waiting. */
     379        11454 :     if (wait_event_info == 0)
     380         1329 :         return NULL;
     381              : 
     382        10125 :     classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
     383              : 
     384        10125 :     switch (classId)
     385              :     {
     386            7 :         case PG_WAIT_LWLOCK:
     387            7 :             event_type = "LWLock";
     388            7 :             break;
     389         1210 :         case PG_WAIT_LOCK:
     390         1210 :             event_type = "Lock";
     391         1210 :             break;
     392            6 :         case PG_WAIT_BUFFER:
     393            6 :             event_type = "Buffer";
     394            6 :             break;
     395         7342 :         case PG_WAIT_ACTIVITY:
     396         7342 :             event_type = "Activity";
     397         7342 :             break;
     398          757 :         case PG_WAIT_CLIENT:
     399          757 :             event_type = "Client";
     400          757 :             break;
     401           46 :         case PG_WAIT_EXTENSION:
     402           46 :             event_type = "Extension";
     403           46 :             break;
     404          161 :         case PG_WAIT_IPC:
     405          161 :             event_type = "IPC";
     406          161 :             break;
     407          402 :         case PG_WAIT_TIMEOUT:
     408          402 :             event_type = "Timeout";
     409          402 :             break;
     410           43 :         case PG_WAIT_IO:
     411           43 :             event_type = "IO";
     412           43 :             break;
     413          151 :         case PG_WAIT_INJECTIONPOINT:
     414          151 :             event_type = "InjectionPoint";
     415          151 :             break;
     416            0 :         default:
     417            0 :             event_type = "???";
     418            0 :             break;
     419              :     }
     420              : 
     421        10125 :     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 *
     431         9286 : pgstat_get_wait_event(uint32 wait_event_info)
     432              : {
     433              :     uint32      classId;
     434              :     uint16      eventId;
     435              :     const char *event_name;
     436              : 
     437              :     /* report process as not waiting. */
     438         9286 :     if (wait_event_info == 0)
     439          979 :         return NULL;
     440              : 
     441         8307 :     classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
     442         8307 :     eventId = wait_event_info & WAIT_EVENT_ID_MASK;
     443              : 
     444         8307 :     switch (classId)
     445              :     {
     446            7 :         case PG_WAIT_LWLOCK:
     447            7 :             event_name = GetLWLockIdentifier(classId, eventId);
     448            7 :             break;
     449           14 :         case PG_WAIT_LOCK:
     450           14 :             event_name = GetLockNameFromTagType(eventId);
     451           14 :             break;
     452          123 :         case PG_WAIT_EXTENSION:
     453              :         case PG_WAIT_INJECTIONPOINT:
     454          123 :             event_name = GetWaitEventCustomIdentifier(wait_event_info);
     455          123 :             break;
     456            6 :         case PG_WAIT_BUFFER:
     457              :             {
     458            6 :                 WaitEventBuffer w = (WaitEventBuffer) wait_event_info;
     459              : 
     460            6 :                 event_name = pgstat_get_wait_buffer(w);
     461            6 :                 break;
     462              :             }
     463         7342 :         case PG_WAIT_ACTIVITY:
     464              :             {
     465         7342 :                 WaitEventActivity w = (WaitEventActivity) wait_event_info;
     466              : 
     467         7342 :                 event_name = pgstat_get_wait_activity(w);
     468         7342 :                 break;
     469              :             }
     470          750 :         case PG_WAIT_CLIENT:
     471              :             {
     472          750 :                 WaitEventClient w = (WaitEventClient) wait_event_info;
     473              : 
     474          750 :                 event_name = pgstat_get_wait_client(w);
     475          750 :                 break;
     476              :             }
     477           22 :         case PG_WAIT_IPC:
     478              :             {
     479           22 :                 WaitEventIPC w = (WaitEventIPC) wait_event_info;
     480              : 
     481           22 :                 event_name = pgstat_get_wait_ipc(w);
     482           22 :                 break;
     483              :             }
     484           30 :         case PG_WAIT_TIMEOUT:
     485              :             {
     486           30 :                 WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
     487              : 
     488           30 :                 event_name = pgstat_get_wait_timeout(w);
     489           30 :                 break;
     490              :             }
     491           13 :         case PG_WAIT_IO:
     492              :             {
     493           13 :                 WaitEventIO w = (WaitEventIO) wait_event_info;
     494              : 
     495           13 :                 event_name = pgstat_get_wait_io(w);
     496           13 :                 break;
     497              :             }
     498            0 :         default:
     499            0 :             event_name = "unknown wait event";
     500            0 :             break;
     501              :     }
     502              : 
     503         8307 :     return event_name;
     504              : }
     505              : 
     506              : #include "utils/pgstat_wait_event.c"
        

Generated by: LCOV version 2.0-1