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: 2024-04-25 09:11:48 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-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 "port/pg_bitutils.h"
      26             : #include "storage/lmgr.h"     /* for GetLockNameFromTagType */
      27             : #include "storage/lwlock.h"       /* for GetLWLockIdentifier */
      28             : #include "storage/spin.h"
      29             : #include "utils/wait_event.h"
      30             : 
      31             : 
      32             : static const char *pgstat_get_wait_activity(WaitEventActivity w);
      33             : static const char *pgstat_get_wait_bufferpin(WaitEventBufferPin w);
      34             : static const char *pgstat_get_wait_client(WaitEventClient w);
      35             : static const char *pgstat_get_wait_ipc(WaitEventIPC w);
      36             : static const char *pgstat_get_wait_timeout(WaitEventTimeout w);
      37             : static const char *pgstat_get_wait_io(WaitEventIO w);
      38             : 
      39             : 
      40             : static uint32 local_my_wait_event_info;
      41             : uint32     *my_wait_event_info = &local_my_wait_event_info;
      42             : 
      43             : #define WAIT_EVENT_CLASS_MASK   0xFF000000
      44             : #define WAIT_EVENT_ID_MASK      0x0000FFFF
      45             : 
      46             : /*
      47             :  * Hash tables for storing custom wait event ids and their names in
      48             :  * shared memory.
      49             :  *
      50             :  * WaitEventExtensionHashById is used to find the name from an event id.
      51             :  * Any backend can search it to find custom wait events.
      52             :  *
      53             :  * WaitEventExtensionHashByName is used to find the event ID from a name.
      54             :  * It is used to ensure that no duplicated entries are registered.
      55             :  *
      56             :  * The size of the hash table is based on the assumption that
      57             :  * WAIT_EVENT_EXTENSION_HASH_INIT_SIZE is enough for most cases, and it seems
      58             :  * unlikely that the number of entries will reach
      59             :  * WAIT_EVENT_EXTENSION_HASH_MAX_SIZE.
      60             :  */
      61             : static HTAB *WaitEventExtensionHashById;    /* find names from IDs */
      62             : static HTAB *WaitEventExtensionHashByName;  /* find IDs from names */
      63             : 
      64             : #define WAIT_EVENT_EXTENSION_HASH_INIT_SIZE 16
      65             : #define WAIT_EVENT_EXTENSION_HASH_MAX_SIZE  128
      66             : 
      67             : /* hash table entries */
      68             : typedef struct WaitEventExtensionEntryById
      69             : {
      70             :     uint16      event_id;       /* hash key */
      71             :     char        wait_event_name[NAMEDATALEN];   /* custom wait event name */
      72             : } WaitEventExtensionEntryById;
      73             : 
      74             : typedef struct WaitEventExtensionEntryByName
      75             : {
      76             :     char        wait_event_name[NAMEDATALEN];   /* hash key */
      77             :     uint16      event_id;       /* wait event ID */
      78             : } WaitEventExtensionEntryByName;
      79             : 
      80             : 
      81             : /* dynamic allocation counter for custom wait events in extensions */
      82             : typedef struct WaitEventExtensionCounterData
      83             : {
      84             :     int         nextId;         /* next ID to assign */
      85             :     slock_t     mutex;          /* protects the counter */
      86             : } WaitEventExtensionCounterData;
      87             : 
      88             : /* pointer to the shared memory */
      89             : static WaitEventExtensionCounterData *WaitEventExtensionCounter;
      90             : 
      91             : /* first event ID of custom wait events for extensions */
      92             : #define NUM_BUILTIN_WAIT_EVENT_EXTENSION    \
      93             :     (WAIT_EVENT_EXTENSION_FIRST_USER_DEFINED - WAIT_EVENT_EXTENSION)
      94             : 
      95             : /* wait event info for extensions */
      96             : #define WAIT_EVENT_EXTENSION_INFO(eventId)  (PG_WAIT_EXTENSION | eventId)
      97             : 
      98             : static const char *GetWaitEventExtensionIdentifier(uint16 eventId);
      99             : 
     100             : /*
     101             :  *  Return the space for dynamic shared hash tables and dynamic allocation counter.
     102             :  */
     103             : Size
     104        3298 : WaitEventExtensionShmemSize(void)
     105             : {
     106             :     Size        sz;
     107             : 
     108        3298 :     sz = MAXALIGN(sizeof(WaitEventExtensionCounterData));
     109        3298 :     sz = add_size(sz, hash_estimate_size(WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
     110             :                                          sizeof(WaitEventExtensionEntryById)));
     111        3298 :     sz = add_size(sz, hash_estimate_size(WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
     112             :                                          sizeof(WaitEventExtensionEntryByName)));
     113        3298 :     return sz;
     114             : }
     115             : 
     116             : /*
     117             :  * Allocate shmem space for dynamic shared hash and dynamic allocation counter.
     118             :  */
     119             : void
     120        1768 : WaitEventExtensionShmemInit(void)
     121             : {
     122             :     bool        found;
     123             :     HASHCTL     info;
     124             : 
     125        1768 :     WaitEventExtensionCounter = (WaitEventExtensionCounterData *)
     126        1768 :         ShmemInitStruct("WaitEventExtensionCounterData",
     127             :                         sizeof(WaitEventExtensionCounterData), &found);
     128             : 
     129        1768 :     if (!found)
     130             :     {
     131             :         /* initialize the allocation counter and its spinlock. */
     132        1768 :         WaitEventExtensionCounter->nextId = NUM_BUILTIN_WAIT_EVENT_EXTENSION;
     133        1768 :         SpinLockInit(&WaitEventExtensionCounter->mutex);
     134             :     }
     135             : 
     136             :     /* initialize or attach the hash tables to store custom wait events */
     137        1768 :     info.keysize = sizeof(uint16);
     138        1768 :     info.entrysize = sizeof(WaitEventExtensionEntryById);
     139        1768 :     WaitEventExtensionHashById = ShmemInitHash("WaitEventExtension hash by id",
     140             :                                                WAIT_EVENT_EXTENSION_HASH_INIT_SIZE,
     141             :                                                WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
     142             :                                                &info,
     143             :                                                HASH_ELEM | HASH_BLOBS);
     144             : 
     145             :     /* key is a NULL-terminated string */
     146        1768 :     info.keysize = sizeof(char[NAMEDATALEN]);
     147        1768 :     info.entrysize = sizeof(WaitEventExtensionEntryByName);
     148        1768 :     WaitEventExtensionHashByName = ShmemInitHash("WaitEventExtension hash by name",
     149             :                                                  WAIT_EVENT_EXTENSION_HASH_INIT_SIZE,
     150             :                                                  WAIT_EVENT_EXTENSION_HASH_MAX_SIZE,
     151             :                                                  &info,
     152             :                                                  HASH_ELEM | HASH_STRINGS);
     153        1768 : }
     154             : 
     155             : /*
     156             :  * Allocate a new event ID and return the wait event info.
     157             :  *
     158             :  * If the wait event name is already defined, this does not allocate a new
     159             :  * entry; it returns the wait event information associated to the name.
     160             :  */
     161             : uint32
     162          38 : WaitEventExtensionNew(const char *wait_event_name)
     163             : {
     164             :     uint16      eventId;
     165             :     bool        found;
     166             :     WaitEventExtensionEntryByName *entry_by_name;
     167             :     WaitEventExtensionEntryById *entry_by_id;
     168             : 
     169             :     /* Check the limit of the length of the event name */
     170          38 :     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          38 :     LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
     180             :     entry_by_name = (WaitEventExtensionEntryByName *)
     181          38 :         hash_search(WaitEventExtensionHashByName, wait_event_name,
     182             :                     HASH_FIND, &found);
     183          38 :     LWLockRelease(WaitEventExtensionLock);
     184          38 :     if (found)
     185          20 :         return WAIT_EVENT_EXTENSION_INFO(entry_by_name->event_id);
     186             : 
     187             :     /*
     188             :      * Allocate and register a new wait event.  Recheck if the event name
     189             :      * exists, as it could be possible that a concurrent process has inserted
     190             :      * one with the same name since the LWLock acquired again here was
     191             :      * previously released.
     192             :      */
     193          18 :     LWLockAcquire(WaitEventExtensionLock, LW_EXCLUSIVE);
     194             :     entry_by_name = (WaitEventExtensionEntryByName *)
     195          18 :         hash_search(WaitEventExtensionHashByName, wait_event_name,
     196             :                     HASH_FIND, &found);
     197          18 :     if (found)
     198             :     {
     199           0 :         LWLockRelease(WaitEventExtensionLock);
     200           0 :         return WAIT_EVENT_EXTENSION_INFO(entry_by_name->event_id);
     201             :     }
     202             : 
     203             :     /* Allocate a new event Id */
     204          18 :     SpinLockAcquire(&WaitEventExtensionCounter->mutex);
     205             : 
     206          18 :     if (WaitEventExtensionCounter->nextId >= WAIT_EVENT_EXTENSION_HASH_MAX_SIZE)
     207             :     {
     208           0 :         SpinLockRelease(&WaitEventExtensionCounter->mutex);
     209           0 :         ereport(ERROR,
     210             :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     211             :                 errmsg("too many wait events for extensions"));
     212             :     }
     213             : 
     214          18 :     eventId = WaitEventExtensionCounter->nextId++;
     215             : 
     216          18 :     SpinLockRelease(&WaitEventExtensionCounter->mutex);
     217             : 
     218             :     /* Register the new wait event */
     219             :     entry_by_id = (WaitEventExtensionEntryById *)
     220          18 :         hash_search(WaitEventExtensionHashById, &eventId,
     221             :                     HASH_ENTER, &found);
     222             :     Assert(!found);
     223          18 :     strlcpy(entry_by_id->wait_event_name, wait_event_name,
     224             :             sizeof(entry_by_id->wait_event_name));
     225             : 
     226             :     entry_by_name = (WaitEventExtensionEntryByName *)
     227          18 :         hash_search(WaitEventExtensionHashByName, wait_event_name,
     228             :                     HASH_ENTER, &found);
     229             :     Assert(!found);
     230          18 :     entry_by_name->event_id = eventId;
     231             : 
     232          18 :     LWLockRelease(WaitEventExtensionLock);
     233             : 
     234          18 :     return WAIT_EVENT_EXTENSION_INFO(eventId);
     235             : }
     236             : 
     237             : /*
     238             :  * Return the name of an wait event ID for extension.
     239             :  */
     240             : static const char *
     241          38 : GetWaitEventExtensionIdentifier(uint16 eventId)
     242             : {
     243             :     bool        found;
     244             :     WaitEventExtensionEntryById *entry;
     245             : 
     246             :     /* Built-in event? */
     247          38 :     if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION)
     248           0 :         return "Extension";
     249             : 
     250             :     /* It is a user-defined wait event, so lookup hash table. */
     251          38 :     LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
     252             :     entry = (WaitEventExtensionEntryById *)
     253          38 :         hash_search(WaitEventExtensionHashById, &eventId,
     254             :                     HASH_FIND, &found);
     255          38 :     LWLockRelease(WaitEventExtensionLock);
     256             : 
     257          38 :     if (!entry)
     258           0 :         elog(ERROR, "could not find custom wait event name for ID %u",
     259             :              eventId);
     260             : 
     261          38 :     return entry->wait_event_name;
     262             : }
     263             : 
     264             : 
     265             : /*
     266             :  * Returns a list of currently defined custom wait event names for extensions.
     267             :  * The result is a palloc'd array, with the number of elements saved in
     268             :  * *nwaitevents.
     269             :  */
     270             : char      **
     271           8 : GetWaitEventExtensionNames(int *nwaitevents)
     272             : {
     273             :     char      **waiteventnames;
     274             :     WaitEventExtensionEntryByName *hentry;
     275             :     HASH_SEQ_STATUS hash_seq;
     276             :     int         index;
     277             :     int         els;
     278             : 
     279           8 :     LWLockAcquire(WaitEventExtensionLock, LW_SHARED);
     280             : 
     281             :     /* Now we can safely count the number of entries */
     282           8 :     els = hash_get_num_entries(WaitEventExtensionHashByName);
     283             : 
     284             :     /* Allocate enough space for all entries */
     285           8 :     waiteventnames = palloc(els * sizeof(char *));
     286             : 
     287             :     /* Now scan the hash table to copy the data */
     288           8 :     hash_seq_init(&hash_seq, WaitEventExtensionHashByName);
     289             : 
     290           8 :     index = 0;
     291          10 :     while ((hentry = (WaitEventExtensionEntryByName *) hash_seq_search(&hash_seq)) != NULL)
     292             :     {
     293           2 :         waiteventnames[index] = pstrdup(hentry->wait_event_name);
     294           2 :         index++;
     295             :     }
     296             : 
     297           8 :     LWLockRelease(WaitEventExtensionLock);
     298             : 
     299             :     Assert(index == els);
     300             : 
     301           8 :     *nwaitevents = index;
     302           8 :     return waiteventnames;
     303             : }
     304             : 
     305             : /*
     306             :  * Configure wait event reporting to report wait events to *wait_event_info.
     307             :  * *wait_event_info needs to be valid until pgstat_reset_wait_event_storage()
     308             :  * is called.
     309             :  *
     310             :  * Expected to be called during backend startup, to point my_wait_event_info
     311             :  * into shared memory.
     312             :  */
     313             : void
     314       29878 : pgstat_set_wait_event_storage(uint32 *wait_event_info)
     315             : {
     316       29878 :     my_wait_event_info = wait_event_info;
     317       29878 : }
     318             : 
     319             : /*
     320             :  * Reset wait event storage location.
     321             :  *
     322             :  * Expected to be called during backend shutdown, before the location set up
     323             :  * pgstat_set_wait_event_storage() becomes invalid.
     324             :  */
     325             : void
     326       29878 : pgstat_reset_wait_event_storage(void)
     327             : {
     328       29878 :     my_wait_event_info = &local_my_wait_event_info;
     329       29878 : }
     330             : 
     331             : /* ----------
     332             :  * pgstat_get_wait_event_type() -
     333             :  *
     334             :  *  Return a string representing the current wait event type, backend is
     335             :  *  waiting on.
     336             :  */
     337             : const char *
     338        9012 : pgstat_get_wait_event_type(uint32 wait_event_info)
     339             : {
     340             :     uint32      classId;
     341             :     const char *event_type;
     342             : 
     343             :     /* report process as not waiting. */
     344        9012 :     if (wait_event_info == 0)
     345        1290 :         return NULL;
     346             : 
     347        7722 :     classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
     348             : 
     349        7722 :     switch (classId)
     350             :     {
     351           0 :         case PG_WAIT_LWLOCK:
     352           0 :             event_type = "LWLock";
     353           0 :             break;
     354          14 :         case PG_WAIT_LOCK:
     355          14 :             event_type = "Lock";
     356          14 :             break;
     357           0 :         case PG_WAIT_BUFFERPIN:
     358           0 :             event_type = "BufferPin";
     359           0 :             break;
     360        6586 :         case PG_WAIT_ACTIVITY:
     361        6586 :             event_type = "Activity";
     362        6586 :             break;
     363        1042 :         case PG_WAIT_CLIENT:
     364        1042 :             event_type = "Client";
     365        1042 :             break;
     366          38 :         case PG_WAIT_EXTENSION:
     367          38 :             event_type = "Extension";
     368          38 :             break;
     369           0 :         case PG_WAIT_IPC:
     370           0 :             event_type = "IPC";
     371           0 :             break;
     372          34 :         case PG_WAIT_TIMEOUT:
     373          34 :             event_type = "Timeout";
     374          34 :             break;
     375           8 :         case PG_WAIT_IO:
     376           8 :             event_type = "IO";
     377           8 :             break;
     378           0 :         default:
     379           0 :             event_type = "???";
     380           0 :             break;
     381             :     }
     382             : 
     383        7722 :     return event_type;
     384             : }
     385             : 
     386             : /* ----------
     387             :  * pgstat_get_wait_event() -
     388             :  *
     389             :  *  Return a string representing the current wait event, backend is
     390             :  *  waiting on.
     391             :  */
     392             : const char *
     393        9012 : pgstat_get_wait_event(uint32 wait_event_info)
     394             : {
     395             :     uint32      classId;
     396             :     uint16      eventId;
     397             :     const char *event_name;
     398             : 
     399             :     /* report process as not waiting. */
     400        9012 :     if (wait_event_info == 0)
     401        1290 :         return NULL;
     402             : 
     403        7722 :     classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
     404        7722 :     eventId = wait_event_info & WAIT_EVENT_ID_MASK;
     405             : 
     406        7722 :     switch (classId)
     407             :     {
     408           0 :         case PG_WAIT_LWLOCK:
     409           0 :             event_name = GetLWLockIdentifier(classId, eventId);
     410           0 :             break;
     411          14 :         case PG_WAIT_LOCK:
     412          14 :             event_name = GetLockNameFromTagType(eventId);
     413          14 :             break;
     414          38 :         case PG_WAIT_EXTENSION:
     415          38 :             event_name = GetWaitEventExtensionIdentifier(eventId);
     416          38 :             break;
     417           0 :         case PG_WAIT_BUFFERPIN:
     418             :             {
     419           0 :                 WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
     420             : 
     421           0 :                 event_name = pgstat_get_wait_bufferpin(w);
     422           0 :                 break;
     423             :             }
     424        6586 :         case PG_WAIT_ACTIVITY:
     425             :             {
     426        6586 :                 WaitEventActivity w = (WaitEventActivity) wait_event_info;
     427             : 
     428        6586 :                 event_name = pgstat_get_wait_activity(w);
     429        6586 :                 break;
     430             :             }
     431        1042 :         case PG_WAIT_CLIENT:
     432             :             {
     433        1042 :                 WaitEventClient w = (WaitEventClient) wait_event_info;
     434             : 
     435        1042 :                 event_name = pgstat_get_wait_client(w);
     436        1042 :                 break;
     437             :             }
     438           0 :         case PG_WAIT_IPC:
     439             :             {
     440           0 :                 WaitEventIPC w = (WaitEventIPC) wait_event_info;
     441             : 
     442           0 :                 event_name = pgstat_get_wait_ipc(w);
     443           0 :                 break;
     444             :             }
     445          34 :         case PG_WAIT_TIMEOUT:
     446             :             {
     447          34 :                 WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
     448             : 
     449          34 :                 event_name = pgstat_get_wait_timeout(w);
     450          34 :                 break;
     451             :             }
     452           8 :         case PG_WAIT_IO:
     453             :             {
     454           8 :                 WaitEventIO w = (WaitEventIO) wait_event_info;
     455             : 
     456           8 :                 event_name = pgstat_get_wait_io(w);
     457           8 :                 break;
     458             :             }
     459           0 :         default:
     460           0 :             event_name = "unknown wait event";
     461           0 :             break;
     462             :     }
     463             : 
     464        7722 :     return event_name;
     465             : }
     466             : 
     467             : #include "pgstat_wait_event.c"

Generated by: LCOV version 1.14