LCOV - code coverage report
Current view: top level - src/backend/utils/activity - wait_event.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 143 167 85.6 %
Date: 2024-12-03 09:15:01 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "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_bufferpin(WaitEventBufferPin 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        3534 : WaitEventCustomShmemSize(void)
     104             : {
     105             :     Size        sz;
     106             : 
     107        3534 :     sz = MAXALIGN(sizeof(WaitEventCustomCounterData));
     108        3534 :     sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
     109             :                                          sizeof(WaitEventCustomEntryByInfo)));
     110        3534 :     sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
     111             :                                          sizeof(WaitEventCustomEntryByName)));
     112        3534 :     return sz;
     113             : }
     114             : 
     115             : /*
     116             :  * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
     117             :  */
     118             : void
     119        1902 : WaitEventCustomShmemInit(void)
     120             : {
     121             :     bool        found;
     122             :     HASHCTL     info;
     123             : 
     124        1902 :     WaitEventCustomCounter = (WaitEventCustomCounterData *)
     125        1902 :         ShmemInitStruct("WaitEventCustomCounterData",
     126             :                         sizeof(WaitEventCustomCounterData), &found);
     127             : 
     128        1902 :     if (!found)
     129             :     {
     130             :         /* initialize the allocation counter and its spinlock. */
     131        1902 :         WaitEventCustomCounter->nextId = WAIT_EVENT_CUSTOM_INITIAL_ID;
     132        1902 :         SpinLockInit(&WaitEventCustomCounter->mutex);
     133             :     }
     134             : 
     135             :     /* initialize or attach the hash tables to store custom wait events */
     136        1902 :     info.keysize = sizeof(uint32);
     137        1902 :     info.entrysize = sizeof(WaitEventCustomEntryByInfo);
     138        1902 :     WaitEventCustomHashByInfo =
     139        1902 :         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        1902 :     info.keysize = sizeof(char[NAMEDATALEN]);
     147        1902 :     info.entrysize = sizeof(WaitEventCustomEntryByName);
     148        1902 :     WaitEventCustomHashByName =
     149        1902 :         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        1902 : }
     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          44 : WaitEventExtensionNew(const char *wait_event_name)
     164             : {
     165          44 :     return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
     166             : }
     167             : 
     168             : uint32
     169          30 : WaitEventInjectionPointNew(const char *wait_event_name)
     170             : {
     171          30 :     return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
     172             : }
     173             : 
     174             : static uint32
     175          74 : 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          74 :     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          74 :     LWLockAcquire(WaitEventCustomLock, LW_SHARED);
     194             :     entry_by_name = (WaitEventCustomEntryByName *)
     195          74 :         hash_search(WaitEventCustomHashByName, wait_event_name,
     196             :                     HASH_FIND, &found);
     197          74 :     LWLockRelease(WaitEventCustomLock);
     198          74 :     if (found)
     199             :     {
     200             :         uint32      oldClassId;
     201             : 
     202          38 :         oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
     203          38 :         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          38 :         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          36 :     LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
     219             :     entry_by_name = (WaitEventCustomEntryByName *)
     220          36 :         hash_search(WaitEventCustomHashByName, wait_event_name,
     221             :                     HASH_FIND, &found);
     222          36 :     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          36 :     SpinLockAcquire(&WaitEventCustomCounter->mutex);
     239             : 
     240          36 :     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          36 :     eventId = WaitEventCustomCounter->nextId++;
     249             : 
     250          36 :     SpinLockRelease(&WaitEventCustomCounter->mutex);
     251             : 
     252             :     /* Register the new wait event */
     253          36 :     wait_event_info = classId | eventId;
     254             :     entry_by_info = (WaitEventCustomEntryByInfo *)
     255          36 :         hash_search(WaitEventCustomHashByInfo, &wait_event_info,
     256             :                     HASH_ENTER, &found);
     257             :     Assert(!found);
     258          36 :     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          36 :         hash_search(WaitEventCustomHashByName, wait_event_name,
     263             :                     HASH_ENTER, &found);
     264             :     Assert(!found);
     265          36 :     entry_by_name->wait_event_info = wait_event_info;
     266             : 
     267          36 :     LWLockRelease(WaitEventCustomLock);
     268             : 
     269          36 :     return wait_event_info;
     270             : }
     271             : 
     272             : /*
     273             :  * Return the name of a custom wait event information.
     274             :  */
     275             : static const char *
     276          68 : GetWaitEventCustomIdentifier(uint32 wait_event_info)
     277             : {
     278             :     bool        found;
     279             :     WaitEventCustomEntryByInfo *entry;
     280             : 
     281             :     /* Built-in event? */
     282          68 :     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          68 :     LWLockAcquire(WaitEventCustomLock, LW_SHARED);
     287             :     entry = (WaitEventCustomEntryByInfo *)
     288          68 :         hash_search(WaitEventCustomHashByInfo, &wait_event_info,
     289             :                     HASH_FIND, &found);
     290          68 :     LWLockRelease(WaitEventCustomLock);
     291             : 
     292          68 :     if (!entry)
     293           0 :         elog(ERROR,
     294             :              "could not find custom name for wait event information %u",
     295             :              wait_event_info);
     296             : 
     297          68 :     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          16 : 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          16 :     LWLockAcquire(WaitEventCustomLock, LW_SHARED);
     315             : 
     316             :     /* Now we can safely count the number of entries */
     317          16 :     els = hash_get_num_entries(WaitEventCustomHashByName);
     318             : 
     319             :     /* Allocate enough space for all entries */
     320          16 :     waiteventnames = palloc(els * sizeof(char *));
     321             : 
     322             :     /* Now scan the hash table to copy the data */
     323          16 :     hash_seq_init(&hash_seq, WaitEventCustomHashByName);
     324             : 
     325          16 :     index = 0;
     326          20 :     while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
     327             :     {
     328           4 :         if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
     329           2 :             continue;
     330           2 :         waiteventnames[index] = pstrdup(hentry->wait_event_name);
     331           2 :         index++;
     332             :     }
     333             : 
     334          16 :     LWLockRelease(WaitEventCustomLock);
     335             : 
     336          16 :     *nwaitevents = index;
     337          16 :     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       32968 : pgstat_set_wait_event_storage(uint32 *wait_event_info)
     350             : {
     351       32968 :     my_wait_event_info = wait_event_info;
     352       32968 : }
     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       32968 : pgstat_reset_wait_event_storage(void)
     362             : {
     363       32968 :     my_wait_event_info = &local_my_wait_event_info;
     364       32968 : }
     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       13924 : 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       13924 :     if (wait_event_info == 0)
     380        2160 :         return NULL;
     381             : 
     382       11764 :     classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
     383             : 
     384       11764 :     switch (classId)
     385             :     {
     386          18 :         case PG_WAIT_LWLOCK:
     387          18 :             event_type = "LWLock";
     388          18 :             break;
     389        2250 :         case PG_WAIT_LOCK:
     390        2250 :             event_type = "Lock";
     391        2250 :             break;
     392           0 :         case PG_WAIT_BUFFERPIN:
     393           0 :             event_type = "BufferPin";
     394           0 :             break;
     395        7822 :         case PG_WAIT_ACTIVITY:
     396        7822 :             event_type = "Activity";
     397        7822 :             break;
     398        1114 :         case PG_WAIT_CLIENT:
     399        1114 :             event_type = "Client";
     400        1114 :             break;
     401          38 :         case PG_WAIT_EXTENSION:
     402          38 :             event_type = "Extension";
     403          38 :             break;
     404         352 :         case PG_WAIT_IPC:
     405         352 :             event_type = "IPC";
     406         352 :             break;
     407          48 :         case PG_WAIT_TIMEOUT:
     408          48 :             event_type = "Timeout";
     409          48 :             break;
     410          34 :         case PG_WAIT_IO:
     411          34 :             event_type = "IO";
     412          34 :             break;
     413          88 :         case PG_WAIT_INJECTIONPOINT:
     414          88 :             event_type = "InjectionPoint";
     415          88 :             break;
     416           0 :         default:
     417           0 :             event_type = "???";
     418           0 :             break;
     419             :     }
     420             : 
     421       11764 :     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       10754 : 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       10754 :     if (wait_event_info == 0)
     439        1674 :         return NULL;
     440             : 
     441        9080 :     classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
     442        9080 :     eventId = wait_event_info & WAIT_EVENT_ID_MASK;
     443             : 
     444        9080 :     switch (classId)
     445             :     {
     446          16 :         case PG_WAIT_LWLOCK:
     447          16 :             event_name = GetLWLockIdentifier(classId, eventId);
     448          16 :             break;
     449          14 :         case PG_WAIT_LOCK:
     450          14 :             event_name = GetLockNameFromTagType(eventId);
     451          14 :             break;
     452          68 :         case PG_WAIT_EXTENSION:
     453             :         case PG_WAIT_INJECTIONPOINT:
     454          68 :             event_name = GetWaitEventCustomIdentifier(wait_event_info);
     455          68 :             break;
     456           0 :         case PG_WAIT_BUFFERPIN:
     457             :             {
     458           0 :                 WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
     459             : 
     460           0 :                 event_name = pgstat_get_wait_bufferpin(w);
     461           0 :                 break;
     462             :             }
     463        7822 :         case PG_WAIT_ACTIVITY:
     464             :             {
     465        7822 :                 WaitEventActivity w = (WaitEventActivity) wait_event_info;
     466             : 
     467        7822 :                 event_name = pgstat_get_wait_activity(w);
     468        7822 :                 break;
     469             :             }
     470        1090 :         case PG_WAIT_CLIENT:
     471             :             {
     472        1090 :                 WaitEventClient w = (WaitEventClient) wait_event_info;
     473             : 
     474        1090 :                 event_name = pgstat_get_wait_client(w);
     475        1090 :                 break;
     476             :             }
     477           8 :         case PG_WAIT_IPC:
     478             :             {
     479           8 :                 WaitEventIPC w = (WaitEventIPC) wait_event_info;
     480             : 
     481           8 :                 event_name = pgstat_get_wait_ipc(w);
     482           8 :                 break;
     483             :             }
     484          48 :         case PG_WAIT_TIMEOUT:
     485             :             {
     486          48 :                 WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
     487             : 
     488          48 :                 event_name = pgstat_get_wait_timeout(w);
     489          48 :                 break;
     490             :             }
     491          14 :         case PG_WAIT_IO:
     492             :             {
     493          14 :                 WaitEventIO w = (WaitEventIO) wait_event_info;
     494             : 
     495          14 :                 event_name = pgstat_get_wait_io(w);
     496          14 :                 break;
     497             :             }
     498           0 :         default:
     499           0 :             event_name = "unknown wait event";
     500           0 :             break;
     501             :     }
     502             : 
     503        9080 :     return event_name;
     504             : }
     505             : 
     506             : #include "pgstat_wait_event.c"

Generated by: LCOV version 1.14