diff options
Diffstat (limited to 'src/backend/utils/activity/wait_event.c')
-rw-r--r-- | src/backend/utils/activity/wait_event.c | 178 |
1 files changed, 170 insertions, 8 deletions
diff --git a/src/backend/utils/activity/wait_event.c b/src/backend/utils/activity/wait_event.c index 59177de7a04..b3596ece80d 100644 --- a/src/backend/utils/activity/wait_event.c +++ b/src/backend/utils/activity/wait_event.c @@ -22,15 +22,18 @@ */ #include "postgres.h" +#include "miscadmin.h" +#include "port/pg_bitutils.h" #include "storage/lmgr.h" /* for GetLockNameFromTagType */ #include "storage/lwlock.h" /* for GetLWLockIdentifier */ +#include "storage/spin.h" +#include "utils/memutils.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_extension(WaitEventExtension 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); @@ -42,6 +45,169 @@ uint32 *my_wait_event_info = &local_my_wait_event_info; #define WAIT_EVENT_CLASS_MASK 0xFF000000 #define WAIT_EVENT_ID_MASK 0x0000FFFF +/* dynamic allocation counter for custom wait events in extensions */ +typedef struct WaitEventExtensionCounterData +{ + int nextId; /* next ID to assign */ + slock_t mutex; /* protects the counter */ +} WaitEventExtensionCounterData; + +/* pointer to the shared memory */ +static WaitEventExtensionCounterData *WaitEventExtensionCounter; + +/* first event ID of custom wait events for extensions */ +#define NUM_BUILTIN_WAIT_EVENT_EXTENSION \ + (WAIT_EVENT_EXTENSION_FIRST_USER_DEFINED - WAIT_EVENT_EXTENSION) + +/* + * This is indexed by event ID minus NUM_BUILTIN_WAIT_EVENT_EXTENSION, and + * stores the names of all dynamically-created event IDs known to the current + * process. Any unused entries in the array will contain NULL. + */ +static const char **WaitEventExtensionNames = NULL; +static int WaitEventExtensionNamesAllocated = 0; + +static const char *GetWaitEventExtensionIdentifier(uint16 eventId); + +/* + * Return the space for dynamic allocation counter. + */ +Size +WaitEventExtensionShmemSize(void) +{ + return sizeof(WaitEventExtensionCounterData); +} + +/* + * Allocate shmem space for dynamic allocation counter. + */ +void +WaitEventExtensionShmemInit(void) +{ + bool found; + + WaitEventExtensionCounter = (WaitEventExtensionCounterData *) + ShmemInitStruct("WaitEventExtensionCounterData", + WaitEventExtensionShmemSize(), &found); + + if (!found) + { + /* initialize the allocation counter and its spinlock. */ + WaitEventExtensionCounter->nextId = NUM_BUILTIN_WAIT_EVENT_EXTENSION; + SpinLockInit(&WaitEventExtensionCounter->mutex); + } +} + +/* + * Allocate a new event ID and return the wait event. + */ +uint32 +WaitEventExtensionNew(void) +{ + uint16 eventId; + + Assert(LWLockHeldByMeInMode(AddinShmemInitLock, LW_EXCLUSIVE)); + + SpinLockAcquire(&WaitEventExtensionCounter->mutex); + + if (WaitEventExtensionCounter->nextId > PG_UINT16_MAX) + { + SpinLockRelease(&WaitEventExtensionCounter->mutex); + ereport(ERROR, + errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("too many wait events for extensions")); + } + + eventId = WaitEventExtensionCounter->nextId++; + + SpinLockRelease(&WaitEventExtensionCounter->mutex); + + return PG_WAIT_EXTENSION | eventId; +} + +/* + * Register a dynamic wait event name for extension in the lookup table + * of the current process. + * + * This routine will save a pointer to the wait event name passed as an argument, + * so the name should be allocated in a backend-lifetime context + * (shared memory, TopMemoryContext, static constant, or similar). + * + * The "wait_event_name" will be user-visible as a wait event name, so try to + * use a name that fits the style for those. + */ +void +WaitEventExtensionRegisterName(uint32 wait_event_info, + const char *wait_event_name) +{ + uint32 classId; + uint16 eventId; + + classId = wait_event_info & WAIT_EVENT_CLASS_MASK; + eventId = wait_event_info & WAIT_EVENT_ID_MASK; + + /* Check the wait event class. */ + if (classId != PG_WAIT_EXTENSION) + ereport(ERROR, + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid wait event class %u", classId)); + + /* This should only be called for user-defined wait event. */ + if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION) + ereport(ERROR, + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid wait event ID %u", eventId)); + + /* Convert to array index. */ + eventId -= NUM_BUILTIN_WAIT_EVENT_EXTENSION; + + /* If necessary, create or enlarge array. */ + if (eventId >= WaitEventExtensionNamesAllocated) + { + uint32 newalloc; + + newalloc = pg_nextpower2_32(Max(8, eventId + 1)); + + if (WaitEventExtensionNames == NULL) + WaitEventExtensionNames = (const char **) + MemoryContextAllocZero(TopMemoryContext, + newalloc * sizeof(char *)); + else + WaitEventExtensionNames = + repalloc0_array(WaitEventExtensionNames, const char *, + WaitEventExtensionNamesAllocated, newalloc); + WaitEventExtensionNamesAllocated = newalloc; + } + + WaitEventExtensionNames[eventId] = wait_event_name; +} + +/* + * Return the name of an wait event ID for extension. + */ +static const char * +GetWaitEventExtensionIdentifier(uint16 eventId) +{ + /* Built-in event? */ + if (eventId < NUM_BUILTIN_WAIT_EVENT_EXTENSION) + return "Extension"; + + /* + * It is a user-defined wait event, so look at WaitEventExtensionNames[]. + * However, it is possible that the name has never been registered by + * calling WaitEventExtensionRegisterName() in the current process, in + * which case give up and return "extension". + */ + eventId -= NUM_BUILTIN_WAIT_EVENT_EXTENSION; + + if (eventId >= WaitEventExtensionNamesAllocated || + WaitEventExtensionNames[eventId] == NULL) + return "extension"; + + return WaitEventExtensionNames[eventId]; +} + + /* * 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() @@ -151,6 +317,9 @@ pgstat_get_wait_event(uint32 wait_event_info) case PG_WAIT_LOCK: event_name = GetLockNameFromTagType(eventId); break; + case PG_WAIT_EXTENSION: + event_name = GetWaitEventExtensionIdentifier(eventId); + break; case PG_WAIT_BUFFERPIN: { WaitEventBufferPin w = (WaitEventBufferPin) wait_event_info; @@ -172,13 +341,6 @@ pgstat_get_wait_event(uint32 wait_event_info) event_name = pgstat_get_wait_client(w); break; } - case PG_WAIT_EXTENSION: - { - WaitEventExtension w = (WaitEventExtension) wait_event_info; - - event_name = pgstat_get_wait_extension(w); - break; - } case PG_WAIT_IPC: { WaitEventIPC w = (WaitEventIPC) wait_event_info; |