You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
postgres/src/backend/utils/activity/wait_event.c

506 lines
14 KiB

/* ----------
* wait_event.c
* Wait event reporting infrastructure.
*
* Copyright (c) 2001-2025, PostgreSQL Global Development Group
*
*
* IDENTIFICATION
* src/backend/utils/activity/wait_event.c
*
* NOTES
*
* To make pgstat_report_wait_start() and pgstat_report_wait_end() as
* lightweight as possible, they do not check if shared memory (MyProc
* specifically, where the wait event is stored) is already available. Instead
* we initially set my_wait_event_info to a process local variable, which then
* is redirected to shared memory using pgstat_set_wait_event_storage(). For
* the same reason pgstat_track_activities is not checked - the check adds
* more work than it saves.
*
* ----------
*/
#include "postgres.h"
#include "storage/lmgr.h" /* for GetLockNameFromTagType */
#include "storage/lwlock.h" /* for GetLWLockIdentifier */
#include "storage/spin.h"
#include "utils/wait_event.h"
static const char *pgstat_get_wait_activity(WaitEventActivity w);
static const char *pgstat_get_wait_bufferpin(WaitEventBufferPin w);
static const char *pgstat_get_wait_client(WaitEventClient w);
static const char *pgstat_get_wait_ipc(WaitEventIPC w);
static const char *pgstat_get_wait_timeout(WaitEventTimeout w);
static const char *pgstat_get_wait_io(WaitEventIO w);
static uint32 local_my_wait_event_info;
uint32 *my_wait_event_info = &local_my_wait_event_info;
#define WAIT_EVENT_CLASS_MASK 0xFF000000
#define WAIT_EVENT_ID_MASK 0x0000FFFF
/*
* Hash tables for storing custom wait event ids and their names in
* shared memory.
*
* WaitEventCustomHashByInfo is used to find the name from wait event
* information. Any backend can search it to find custom wait events.
*
* WaitEventCustomHashByName is used to find the wait event information from a
* name. It is used to ensure that no duplicated entries are registered.
*
* For simplicity, we use the same ID counter across types of custom events.
* We could end that anytime the need arises.
*
* The size of the hash table is based on the assumption that
* WAIT_EVENT_CUSTOM_HASH_INIT_SIZE is enough for most cases, and it seems
* unlikely that the number of entries will reach
* WAIT_EVENT_CUSTOM_HASH_MAX_SIZE.
*/
static HTAB *WaitEventCustomHashByInfo; /* find names from infos */
static HTAB *WaitEventCustomHashByName; /* find infos from names */
#define WAIT_EVENT_CUSTOM_HASH_INIT_SIZE 16
#define WAIT_EVENT_CUSTOM_HASH_MAX_SIZE 128
/* hash table entries */
typedef struct WaitEventCustomEntryByInfo
{
uint32 wait_event_info; /* hash key */
char wait_event_name[NAMEDATALEN]; /* custom wait event name */
} WaitEventCustomEntryByInfo;
typedef struct WaitEventCustomEntryByName
{
char wait_event_name[NAMEDATALEN]; /* hash key */
uint32 wait_event_info;
} WaitEventCustomEntryByName;
/* dynamic allocation counter for custom wait events */
typedef struct WaitEventCustomCounterData
{
int nextId; /* next ID to assign */
slock_t mutex; /* protects the counter */
} WaitEventCustomCounterData;
/* pointer to the shared memory */
static WaitEventCustomCounterData *WaitEventCustomCounter;
/* first event ID of custom wait events */
#define WAIT_EVENT_CUSTOM_INITIAL_ID 1
static uint32 WaitEventCustomNew(uint32 classId, const char *wait_event_name);
static const char *GetWaitEventCustomIdentifier(uint32 wait_event_info);
/*
* Return the space for dynamic shared hash tables and dynamic allocation counter.
*/
Size
WaitEventCustomShmemSize(void)
{
Size sz;
sz = MAXALIGN(sizeof(WaitEventCustomCounterData));
sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
sizeof(WaitEventCustomEntryByInfo)));
sz = add_size(sz, hash_estimate_size(WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
sizeof(WaitEventCustomEntryByName)));
return sz;
}
/*
* Allocate shmem space for dynamic shared hash and dynamic allocation counter.
*/
void
WaitEventCustomShmemInit(void)
{
bool found;
HASHCTL info;
WaitEventCustomCounter = (WaitEventCustomCounterData *)
ShmemInitStruct("WaitEventCustomCounterData",
sizeof(WaitEventCustomCounterData), &found);
if (!found)
{
/* initialize the allocation counter and its spinlock. */
WaitEventCustomCounter->nextId = WAIT_EVENT_CUSTOM_INITIAL_ID;
SpinLockInit(&WaitEventCustomCounter->mutex);
}
/* initialize or attach the hash tables to store custom wait events */
info.keysize = sizeof(uint32);
info.entrysize = sizeof(WaitEventCustomEntryByInfo);
WaitEventCustomHashByInfo =
ShmemInitHash("WaitEventCustom hash by wait event information",
WAIT_EVENT_CUSTOM_HASH_INIT_SIZE,
WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
&info,
HASH_ELEM | HASH_BLOBS);
/* key is a NULL-terminated string */
info.keysize = sizeof(char[NAMEDATALEN]);
info.entrysize = sizeof(WaitEventCustomEntryByName);
WaitEventCustomHashByName =
ShmemInitHash("WaitEventCustom hash by name",
WAIT_EVENT_CUSTOM_HASH_INIT_SIZE,
WAIT_EVENT_CUSTOM_HASH_MAX_SIZE,
&info,
HASH_ELEM | HASH_STRINGS);
}
/*
* Allocate a new event ID and return the wait event info.
*
* If the wait event name is already defined, this does not allocate a new
* entry; it returns the wait event information associated to the name.
*/
uint32
WaitEventExtensionNew(const char *wait_event_name)
{
return WaitEventCustomNew(PG_WAIT_EXTENSION, wait_event_name);
}
uint32
WaitEventInjectionPointNew(const char *wait_event_name)
{
return WaitEventCustomNew(PG_WAIT_INJECTIONPOINT, wait_event_name);
}
static uint32
WaitEventCustomNew(uint32 classId, const char *wait_event_name)
{
uint16 eventId;
bool found;
WaitEventCustomEntryByName *entry_by_name;
WaitEventCustomEntryByInfo *entry_by_info;
uint32 wait_event_info;
/* Check the limit of the length of the event name */
if (strlen(wait_event_name) >= NAMEDATALEN)
elog(ERROR,
"cannot use custom wait event string longer than %u characters",
NAMEDATALEN - 1);
/*
* Check if the wait event info associated to the name is already defined,
* and return it if so.
*/
LWLockAcquire(WaitEventCustomLock, LW_SHARED);
entry_by_name = (WaitEventCustomEntryByName *)
hash_search(WaitEventCustomHashByName, wait_event_name,
HASH_FIND, &found);
LWLockRelease(WaitEventCustomLock);
if (found)
{
uint32 oldClassId;
oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
if (oldClassId != classId)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("wait event \"%s\" already exists in type \"%s\"",
wait_event_name,
pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
return entry_by_name->wait_event_info;
}
/*
* Allocate and register a new wait event. Recheck if the event name
* exists, as it could be possible that a concurrent process has inserted
* one with the same name since the LWLock acquired again here was
* previously released.
*/
LWLockAcquire(WaitEventCustomLock, LW_EXCLUSIVE);
entry_by_name = (WaitEventCustomEntryByName *)
hash_search(WaitEventCustomHashByName, wait_event_name,
HASH_FIND, &found);
if (found)
{
uint32 oldClassId;
LWLockRelease(WaitEventCustomLock);
oldClassId = entry_by_name->wait_event_info & WAIT_EVENT_CLASS_MASK;
if (oldClassId != classId)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("wait event \"%s\" already exists in type \"%s\"",
wait_event_name,
pgstat_get_wait_event_type(entry_by_name->wait_event_info))));
return entry_by_name->wait_event_info;
}
/* Allocate a new event Id */
SpinLockAcquire(&WaitEventCustomCounter->mutex);
if (WaitEventCustomCounter->nextId >= WAIT_EVENT_CUSTOM_HASH_MAX_SIZE)
{
SpinLockRelease(&WaitEventCustomCounter->mutex);
ereport(ERROR,
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many custom wait events"));
}
eventId = WaitEventCustomCounter->nextId++;
SpinLockRelease(&WaitEventCustomCounter->mutex);
/* Register the new wait event */
wait_event_info = classId | eventId;
entry_by_info = (WaitEventCustomEntryByInfo *)
hash_search(WaitEventCustomHashByInfo, &wait_event_info,
HASH_ENTER, &found);
Assert(!found);
strlcpy(entry_by_info->wait_event_name, wait_event_name,
sizeof(entry_by_info->wait_event_name));
entry_by_name = (WaitEventCustomEntryByName *)
hash_search(WaitEventCustomHashByName, wait_event_name,
HASH_ENTER, &found);
Assert(!found);
entry_by_name->wait_event_info = wait_event_info;
LWLockRelease(WaitEventCustomLock);
return wait_event_info;
}
/*
* Return the name of a custom wait event information.
*/
static const char *
GetWaitEventCustomIdentifier(uint32 wait_event_info)
{
bool found;
WaitEventCustomEntryByInfo *entry;
/* Built-in event? */
if (wait_event_info == PG_WAIT_EXTENSION)
return "Extension";
/* It is a user-defined wait event, so lookup hash table. */
LWLockAcquire(WaitEventCustomLock, LW_SHARED);
entry = (WaitEventCustomEntryByInfo *)
hash_search(WaitEventCustomHashByInfo, &wait_event_info,
HASH_FIND, &found);
LWLockRelease(WaitEventCustomLock);
if (!entry)
elog(ERROR,
"could not find custom name for wait event information %u",
wait_event_info);
return entry->wait_event_name;
}
/*
* Returns a list of currently defined custom wait event names. The result is
* a palloc'd array, with the number of elements saved in *nwaitevents.
*/
char **
GetWaitEventCustomNames(uint32 classId, int *nwaitevents)
{
char **waiteventnames;
WaitEventCustomEntryByName *hentry;
HASH_SEQ_STATUS hash_seq;
int index;
int els;
LWLockAcquire(WaitEventCustomLock, LW_SHARED);
/* Now we can safely count the number of entries */
els = hash_get_num_entries(WaitEventCustomHashByName);
/* Allocate enough space for all entries */
waiteventnames = palloc(els * sizeof(char *));
/* Now scan the hash table to copy the data */
hash_seq_init(&hash_seq, WaitEventCustomHashByName);
index = 0;
while ((hentry = (WaitEventCustomEntryByName *) hash_seq_search(&hash_seq)) != NULL)
{
if ((hentry->wait_event_info & WAIT_EVENT_CLASS_MASK) != classId)
continue;
waiteventnames[index] = pstrdup(hentry->wait_event_name);
index++;
}
LWLockRelease(WaitEventCustomLock);
*nwaitevents = index;
return waiteventnames;
}
/*
* Configure wait event reporting to report wait events to *wait_event_info.
* *wait_event_info needs to be valid until pgstat_reset_wait_event_storage()
* is called.
*
* Expected to be called during backend startup, to point my_wait_event_info
* into shared memory.
*/
void
pgstat_set_wait_event_storage(uint32 *wait_event_info)
{
my_wait_event_info = wait_event_info;
}
/*
* Reset wait event storage location.
*
* Expected to be called during backend shutdown, before the location set up
* pgstat_set_wait_event_storage() becomes invalid.
*/
void
pgstat_reset_wait_event_storage(void)
{
my_wait_event_info = &local_my_wait_event_info;
}
/* ----------
* pgstat_get_wait_event_type() -
*
* Return a string representing the current wait event type, backend is
* waiting on.
*/
const char *
pgstat_get_wait_event_type(uint32 wait_event_info)
{
uint32 classId;
const char *event_type;
/* report process as not waiting. */
if (wait_event_info == 0)
return NULL;
classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
switch (classId)
{
case PG_WAIT_LWLOCK:
event_type = "LWLock";
break;
case PG_WAIT_LOCK:
event_type = "Lock";
break;
case PG_WAIT_BUFFERPIN:
event_type = "BufferPin";
break;
case PG_WAIT_ACTIVITY:
event_type = "Activity";
break;
case PG_WAIT_CLIENT:
event_type = "Client";
break;
case PG_WAIT_EXTENSION:
event_type = "Extension";
break;
case PG_WAIT_IPC:
event_type = "IPC";
break;
case PG_WAIT_TIMEOUT:
event_type = "Timeout";
break;
case PG_WAIT_IO:
event_type = "IO";
break;
case PG_WAIT_INJECTIONPOINT:
event_type = "InjectionPoint";
break;
default:
event_type = "???";
break;
}
return event_type;
}
/* ----------
* pgstat_get_wait_event() -
*
* Return a string representing the current wait event, backend is
* waiting on.
*/
const char *
pgstat_get_wait_event(uint32 wait_event_info)
{
uint32 classId;
uint16 eventId;
const char *event_name;
/* report process as not waiting. */
if (wait_event_info == 0)
return NULL;
classId = wait_event_info & WAIT_EVENT_CLASS_MASK;
eventId = wait_event_info & WAIT_EVENT_ID_MASK;
switch (classId)
{
case PG_WAIT_LWLOCK:
event_name = GetLWLockIdentifier(classId, eventId);
break;
case PG_WAIT_LOCK:
event_name = GetLockNameFromTagType(eventId);
break;
case PG_WAIT_EXTENSION:
case PG_WAIT_INJECTIONPOINT:
event_name = GetWaitEventCustomIdentifier(wait_event_info);
break;
case PG_WAIT_BUFFERPIN:
{
WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info;
event_name = pgstat_get_wait_bufferpin(w);
break;
}
case PG_WAIT_ACTIVITY:
{
WaitEventActivity w = (WaitEventActivity) wait_event_info;
event_name = pgstat_get_wait_activity(w);
break;
}
case PG_WAIT_CLIENT:
{
WaitEventClient w = (WaitEventClient) wait_event_info;
event_name = pgstat_get_wait_client(w);
break;
}
case PG_WAIT_IPC:
{
WaitEventIPC w = (WaitEventIPC) wait_event_info;
event_name = pgstat_get_wait_ipc(w);
break;
}
case PG_WAIT_TIMEOUT:
{
WaitEventTimeout w = (WaitEventTimeout) wait_event_info;
event_name = pgstat_get_wait_timeout(w);
break;
}
case PG_WAIT_IO:
{
WaitEventIO w = (WaitEventIO) wait_event_info;
event_name = pgstat_get_wait_io(w);
break;
}
default:
event_name = "unknown wait event";
break;
}
return event_name;
}
#include "pgstat_wait_event.c"