LCOV - code coverage report
Current view: top level - src/backend/utils/activity - wait_event.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 116 149 77.9 %
Date: 2023-12-11 16:10:55 Functions: 9 9 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-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             : 
      42             : static uint32 local_my_wait_event_info;
      43             : uint32     *my_wait_event_info = &local_my_wait_event_info;
      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 */
      70             : typedef struct WaitEventExtensionEntryById
      71             : {
      72             :     uint16      event_id;       /* hash key */
      73             :     char        wait_event_name[NAMEDATALEN];   /* custom wait event name */
      74             : } WaitEventExtensionEntryById;
      75             : 
      76             : typedef struct WaitEventExtensionEntryByName
      77             : {
      78             :     char        wait_event_name[NAMEDATALEN];   /* hash key */
      79             :     uint16      event_id;       /* wait event ID */
      80             : } WaitEventExtensionEntryByName;
      81             : 
      82             : 
      83             : /* dynamic allocation counter for custom wait events in extensions */
      84             : typedef struct WaitEventExtensionCounterData
      85             : {
      86             :     int         nextId;         /* next ID to assign */
      87             :     slock_t     mutex;          /* protects the counter */
      88             : } WaitEventExtensionCounterData;
      89             : 
      90             : /* pointer to the shared memory */
      91             : static WaitEventExtensionCounterData *WaitEventExtensionCounter;
      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
     106        2934 : WaitEventExtensionShmemSize(void)
     107             : {
     108             :     Size        sz;
     109             : 
     110        2934 :     sz = MAXALIGN(sizeof(WaitEventExtensionCounterData));
     111        2934 :     sz = add_size(sz, hash_estimate_size(WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
     112             :                                          sizeof(WaitEventExtensionEntryById)));
     113        2934 :     sz = add_size(sz, hash_estimate_size(WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
     114             :                                          sizeof(WaitEventExtensionEntryByName)));
     115        2934 :     return sz;
     116             : }
     117             : 
     118             : /*
     119             :  * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
     120             :  */
     121             : void
     122        1562 : WaitEventExtensionShmemInit(void)
     123             : {
     124             :     bool        found;
     125             :     HASHCTL     info;
     126             : 
     127        1562 :     WaitEventExtensionCounter = (WaitEventExtensionCounterData *)
     128        1562 :         ShmemInitStruct("WaitEventExtensionCounterData",
     129             :                         sizeof(WaitEventExtensionCounterData), &found);
     130             : 
     131        1562 :     if (!found)
     132             :     {
     133             :         /* initialize the allocation counter and its spinlock. */
     134        1562 :         WaitEventExtensionCounter->nextId = NUM_BUILTIN_WAIT_EVENT_EXTENSION;
     135        1562 :         SpinLockInit(&WaitEventExtensionCounter->mutex);
     136             :     }
     137             : 
     138             :     /* initialize or attach the hash tables to store custom wait events */
     139        1562 :     info.keysize = sizeof(uint16);
     140        1562 :     info.entrysize = sizeof(WaitEventExtensionEntryById);
     141        1562 :     WaitEventExtensionHashById = ShmemInitHash("WaitEventExtension hash by id",
     142             :                                                WAIT_EVENT_EXTENSION_HASH_INIT_SIZE,
     143             :                                                WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
     144             :                                                &info,
     145             :                                                HASH_ELEM | HASH_BLOBS);
     146             : 
     147             :     /* key is a NULL-terminated string */
     148        1562 :     info.keysize = sizeof(char[NAMEDATALEN]);
     149        1562 :     info.entrysize = sizeof(WaitEventExtensionEntryByName);
     150        1562 :     WaitEventExtensionHashByName = ShmemInitHash("WaitEventExtension hash by name",
     151             :                                                  WAIT_EVENT_EXTENSION_HASH_INIT_SIZE,
     152             :                                                  WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
     153             :                                                  &info,
     154             :                                                  HASH_ELEM | HASH_STRINGS);
     155        1562 : }
     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          32 : 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          32 :     if (strlen(wait_event_name) >= NAMEDATALEN)
     173           0 :         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          32 :     LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
     182             :     entry_by_name = (WaitEventExtensionEntryByName *)
     183          32 :         hash_search(WaitEventExtensionHashByName, wait_event_name,
     184             :                     HASH_FIND, &found);
     185          32 :     LWLockRelease(WaitEventExtensionLock);
     186          32 :     if (found)
     187          16 :         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          16 :     LWLockAcquire(WaitEventExtensionLock, LW_EXCLUSIVE);
     196             :     entry_by_name = (WaitEventExtensionEntryByName *)
     197          16 :         hash_search(WaitEventExtensionHashByName, wait_event_name,
     198             :                     HASH_FIND, &found);
     199          16 :     if (found)
     200             :     {
     201           0 :         LWLockRelease(WaitEventExtensionLock);
     202           0 :         return WAIT_EVENT_EXTENSION_INFO(entry_by_name->event_id);
     203             :     }
     204             : 
     205             :     /* Allocate a new event Id */
     206          16 :     SpinLockAcquire(&WaitEventExtensionCounter->mutex);
     207             : 
     208          16 :     if (WaitEventExtensionCounter->nextId >= WAIT_EVENT_EXTENSION_HASH_MAX_SIZE)
     209             :     {
     210           0 :         SpinLockRelease(&WaitEventExtensionCounter->mutex);
     211           0 :         ereport(ERROR,
     212             :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     213             :                 errmsg("too many wait events for extensions"));
     214             :     }
     215             : 
     216          16 :     eventId = WaitEventExtensionCounter->nextId++;
     217             : 
     218          16 :     SpinLockRelease(&WaitEventExtensionCounter->mutex);
     219             : 
     220             :     /* Register the new wait event */
     221             :     entry_by_id = (WaitEventExtensionEntryById *)
     222          16 :         hash_search(WaitEventExtensionHashById, &eventId,
     223             :                     HASH_ENTER, &found);
     224             :     Assert(!found);
     225          16 :     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          16 :         hash_search(WaitEventExtensionHashByName, wait_event_name,
     230             :                     HASH_ENTER, &found);
     231             :     Assert(!found);
     232          16 :     entry_by_name->event_id = eventId;
     233             : 
     234          16 :     LWLockRelease(WaitEventExtensionLock);
     235             : 
     236          16 :     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 *
     243          38 : GetWaitEventExtensionIdentifier(uint16 eventId)
     244             : {
     245             :     bool        found;
     246             :     WaitEventExtensionEntryById *entry;
     247             : 
     248             :     /* Built-in event? */
     249          38 :     if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION)
     250           0 :         return "Extension";
     251             : 
     252             :     /* It is a user-defined wait event, so lookup hash table. */
     253          38 :     LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
     254             :     entry = (WaitEventExtensionEntryById *)
     255          38 :         hash_search(WaitEventExtensionHashById, &eventId,
     256             :                     HASH_FIND, &found);
     257          38 :     LWLockRelease(WaitEventExtensionLock);
     258             : 
     259          38 :     if (!entry)
     260           0 :         elog(ERROR, "could not find custom wait event name for ID %u",
     261             :              eventId);
     262             : 
     263          38 :     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           8 : GetWaitEventExtensionNames(int *nwaitevents)
     274             : {
     275             :     char      **waiteventnames;
     276             :     WaitEventExtensionEntryByName *hentry;
     277             :     HASH_SEQ_STATUS hash_seq;
     278             :     int         index;
     279             :     int         els;
     280             : 
     281           8 :     LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
     282             : 
     283             :     /* Now we can safely count the number of entries */
     284           8 :     els = hash_get_num_entries(WaitEventExtensionHashByName);
     285             : 
     286             :     /* Allocate enough space for all entries */
     287           8 :     waiteventnames = palloc(els * sizeof(char *));
     288             : 
     289             :     /* Now scan the hash table to copy the data */
     290           8 :     hash_seq_init(&hash_seq, WaitEventExtensionHashByName);
     291             : 
     292           8 :     index = 0;
     293          10 :     while ((hentry = (WaitEventExtensionEntryByName *) hash_seq_search(&hash_seq)) != NULL)
     294             :     {
     295           2 :         waiteventnames[index] = pstrdup(hentry->wait_event_name);
     296           2 :         index++;
     297             :     }
     298             : 
     299           8 :     LWLockRelease(WaitEventExtensionLock);
     300             : 
     301             :     Assert(index == els);
     302             : 
     303           8 :     *nwaitevents = index;
     304           8 :     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
     316       27816 : pgstat_set_wait_event_storage(uint32 *wait_event_info)
     317             : {
     318       27816 :     my_wait_event_info = wait_event_info;
     319       27816 : }
     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
     328       27816 : pgstat_reset_wait_event_storage(void)
     329             : {
     330       27816 :     my_wait_event_info = &local_my_wait_event_info;
     331       27816 : }
     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 *
     340        8466 : pgstat_get_wait_event_type(uint32 wait_event_info)
     341             : {
     342             :     uint32      classId;
     343             :     const char *event_type;
     344             : 
     345             :     /* report process as not waiting. */
     346        8466 :     if (wait_event_info == 0)
     347        1198 :         return NULL;
     348             : 
     349        7268 :     classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
     350             : 
     351        7268 :     switch (classId)
     352             :     {
     353           0 :         case PG_WAIT_LWLOCK:
     354           0 :             event_type = "LWLock";
     355           0 :             break;
     356          14 :         case PG_WAIT_LOCK:
     357          14 :             event_type = "Lock";
     358          14 :             break;
     359           0 :         case PG_WAIT_BUFFERPIN:
     360           0 :             event_type = "BufferPin";
     361           0 :             break;
     362        6226 :         case PG_WAIT_ACTIVITY:
     363        6226 :             event_type = "Activity";
     364        6226 :             break;
     365         960 :         case PG_WAIT_CLIENT:
     366         960 :             event_type = "Client";
     367         960 :             break;
     368          38 :         case PG_WAIT_EXTENSION:
     369          38 :             event_type = "Extension";
     370          38 :             break;
     371           0 :         case PG_WAIT_IPC:
     372           0 :             event_type = "IPC";
     373           0 :             break;
     374          24 :         case PG_WAIT_TIMEOUT:
     375          24 :             event_type = "Timeout";
     376          24 :             break;
     377           6 :         case PG_WAIT_IO:
     378           6 :             event_type = "IO";
     379           6 :             break;
     380           0 :         default:
     381           0 :             event_type = "???";
     382           0 :             break;
     383             :     }
     384             : 
     385        7268 :     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 *
     395        8466 : pgstat_get_wait_event(uint32 wait_event_info)
     396             : {
     397             :     uint32      classId;
     398             :     uint16      eventId;
     399             :     const char *event_name;
     400             : 
     401             :     /* report process as not waiting. */
     402        8466 :     if (wait_event_info == 0)
     403        1198 :         return NULL;
     404             : 
     405        7268 :     classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
     406        7268 :     eventId = wait_event_info & WAIT_EVENT_ID_MASK;
     407             : 
     408        7268 :     switch (classId)
     409             :     {
     410           0 :         case PG_WAIT_LWLOCK:
     411           0 :             event_name = GetLWLockIdentifier(classId, eventId);
     412           0 :             break;
     413          14 :         case PG_WAIT_LOCK:
     414          14 :             event_name = GetLockNameFromTagType(eventId);
     415          14 :             break;
     416          38 :         case PG_WAIT_EXTENSION:
     417          38 :             event_name = GetWaitEventExtensionIdentifier(eventId);
     418          38 :             break;
     419           0 :         case PG_WAIT_BUFFERPIN:
     420             :             {
     421           0 :                 WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
     422             : 
     423           0 :                 event_name = pgstat_get_wait_bufferpin(w);
     424           0 :                 break;
     425             :             }
     426        6226 :         case PG_WAIT_ACTIVITY:
     427             :             {
     428        6226 :                 WaitEventActivity w = (WaitEventActivity) wait_event_info;
     429             : 
     430        6226 :                 event_name = pgstat_get_wait_activity(w);
     431        6226 :                 break;
     432             :             }
     433         960 :         case PG_WAIT_CLIENT:
     434             :             {
     435         960 :                 WaitEventClient w = (WaitEventClient) wait_event_info;
     436             : 
     437         960 :                 event_name = pgstat_get_wait_client(w);
     438         960 :                 break;
     439             :             }
     440           0 :         case PG_WAIT_IPC:
     441             :             {
     442           0 :                 WaitEventIPC w = (WaitEventIPC) wait_event_info;
     443             : 
     444           0 :                 event_name = pgstat_get_wait_ipc(w);
     445           0 :                 break;
     446             :             }
     447          24 :         case PG_WAIT_TIMEOUT:
     448             :             {
     449          24 :                 WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
     450             : 
     451          24 :                 event_name = pgstat_get_wait_timeout(w);
     452          24 :                 break;
     453             :             }
     454           6 :         case PG_WAIT_IO:
     455             :             {
     456           6 :                 WaitEventIO w = (WaitEventIO) wait_event_info;
     457             : 
     458           6 :                 event_name = pgstat_get_wait_io(w);
     459           6 :                 break;
     460             :             }
     461           0 :         default:
     462           0 :             event_name = "unknown wait event";
     463           0 :             break;
     464             :     }
     465             : 
     466        7268 :     return event_name;
     467             : }
     468             : 
     469             : #include "pgstat_wait_event.c"

Generated by: LCOV version 1.14