*** /dev/null --- b/contrib/pg_stat_lwlock/Makefile *************** *** 0 **** --- 1,18 ---- + # contrib/pg_stat_lwlock/Makefile + + MODULE_big = pg_stat_lwlock + OBJS = pg_stat_lwlock_list.o + + EXTENSION = pg_stat_lwlock + DATA = pg_stat_lwlock--1.0.sql + + ifdef USE_PGXS + PG_CONFIG = pg_config + PGXS := $(shell $(PG_CONFIG) --pgxs) + include $(PGXS) + else + subdir = contrib/pg_stat_lwlock + top_builddir = ../.. + include $(top_builddir)/src/Makefile.global + include $(top_srcdir)/contrib/contrib-global.mk + endif *** /dev/null --- b/contrib/pg_stat_lwlock/pg_stat_lwlock--1.0.sql *************** *** 0 **** --- 1,19 ---- + /* contrib/pg_stats_lwlock/pg_stats_lwlock--1.0.sql */ + + -- complain if script is sourced in psql, rather than via CREATE EXTENSION + \echo Use "CREATE EXTENSION pg_stat_lwlock" to load this file. \quit + + -- Register the function. + CREATE FUNCTION pg_stat_lwlock_list() + RETURNS SETOF RECORD + AS 'MODULE_PATHNAME', 'pg_stat_lwlock_list' + LANGUAGE C; + + -- Create a view for convenient access. + CREATE VIEW pg_stat_lwlock AS + SELECT L.* FROM pg_stat_lwlock_list() AS L + (lwlockid integer, pid integer); + + -- Don't want these to be available to public. + REVOKE ALL ON FUNCTION pg_stat_lwlock_list() FROM PUBLIC; + REVOKE ALL ON pg_stat_lwlock FROM PUBLIC; *** /dev/null --- b/contrib/pg_stat_lwlock/pg_stat_lwlock.control *************** *** 0 **** --- 1,5 ---- + # pg_stat_lwlock extension + comment = 'shows the list of lwlock id currently holding by backend' + default_version = '1.0' + module_pathname = '$libdir/pg_stat_lwlock' + relocatable = true *** /dev/null --- b/contrib/pg_stat_lwlock/pg_stat_lwlock_list.c *************** *** 0 **** --- 1,147 ---- + /*------------------------------------------------------------------------- + * + * pg_stat_lwlock_list.c + * display some histogramm of lightweight locks + * + * contrib/pg_stat_lwlock/pg_stat_lwlock_list.c + *------------------------------------------------------------------------- + */ + #include "postgres.h" + + #include "access/htup_details.h" + #include "catalog/pg_type.h" + #include "funcapi.h" + #include "storage/buf_internals.h" + #include "storage/bufmgr.h" + #include "storage/proc.h" + + + #define NUM_STAT_LWLOCK_ELEM 2 + + PG_MODULE_MAGIC; + + + /* + * structure represents one record, contains lwlock id and process id + */ + + typedef struct + { + uint32 lwLockId; + uint32 pid; + } LwLockIdListRec; + + /* + * structure to hold function context between calls + */ + + typedef struct + { + TupleDesc tupdesc; + LwLockIdListRec *record; + } LwLockListContext; + + /* + * Function returning information about long lwlocks holding by pid: + * currently lwlock id and pid (if any). + */ + + PG_FUNCTION_INFO_V1(pg_stat_lwlock_list); + + Datum + pg_stat_lwlock_list(PG_FUNCTION_ARGS) + { + FuncCallContext *funcctx; + Datum result; + MemoryContext oldcontext; + LwLockListContext *fctx; + TupleDesc tupledesc; + HeapTuple tuple; + + + int call_cntr; + int max_calls; + + if (SRF_IS_FIRSTCALL()) + { + int ii; + List *lwLockList; + ListCell *lc; + + funcctx = SRF_FIRSTCALL_INIT(); + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + fctx = (LwLockListContext *) palloc(sizeof(LwLockListContext)); + + tupledesc = CreateTemplateTupleDesc(NUM_STAT_LWLOCK_ELEM, false); + + TupleDescInitEntry(tupledesc, (AttrNumber) 1, "lwLockId", + INT4OID, -1, 0); + + TupleDescInitEntry(tupledesc, (AttrNumber) 2, "pid", + INT4OID, -1, 0); + + + fctx->tupdesc = BlessTupleDesc(tupledesc); + fctx->record = (LwLockIdListRec *) palloc(sizeof(LwLockIdListRec) * NBuffers); + + /* + * getting list of pairs, lwlockid and pid, from procarray + */ + lwLockList = GetLwLockList(); + + funcctx->max_calls = list_length(lwLockList); + funcctx->user_fctx = fctx; + + MemoryContextSwitchTo(oldcontext); + + + for( ii = list_length(lwLockList) - 1; ii >= 0; ii--) + { + lwLockCell *lc = list_nth(lwLockList, ii); + + if( lc->lwLockId > 0 ) + { + fctx->record[ii].lwLockId = lc->lwLockId; + fctx->record[ii].pid = lc->pid; + } + else + { + fctx->record[ii].lwLockId = 0; + fctx->record[ii].pid = 0; + } + } + } + + funcctx = SRF_PERCALL_SETUP(); + + fctx = funcctx->user_fctx; + + if( funcctx->call_cntr < funcctx->max_calls ) + { + uint32 i = funcctx->call_cntr; + Datum values[NUM_STAT_LWLOCK_ELEM]; + bool nulls[NUM_STAT_LWLOCK_ELEM]; + + if( fctx->record[i].lwLockId > 0) + { + values[0] = Int32GetDatum(fctx->record[i].lwLockId); + nulls[0] = false; + + values[1] = Int32GetDatum(fctx->record[i].pid); + nulls[1] = false; + } + else + { + nulls[0] = true; + nulls[1] = true; + } + + tuple = heap_form_tuple(fctx->tupdesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + SRF_RETURN_NEXT(funcctx, result); + } + else + SRF_RETURN_DONE(funcctx); + } *** a/src/backend/access/transam/twophase.c --- b/src/backend/access/transam/twophase.c *************** *** 392,397 **** MarkAsPreparing(TransactionId xid, const char *gid, --- 392,398 ---- proc->lwWaitLink = NULL; proc->waitLock = NULL; proc->waitProcLock = NULL; + proc->lwLockId = 0; for (i = 0; i < NUM_LOCK_PARTITIONS; i++) SHMQueueInit(&(proc->myProcLocks[i])); /* subxid data must be filled later by GXactLoadSubxactData */ *** a/src/backend/storage/ipc/procarray.c --- b/src/backend/storage/ipc/procarray.c *************** *** 2795,2800 **** XidCacheRemoveRunningXids(TransactionId xid, --- 2795,2852 ---- LWLockRelease(ProcArrayLock); } + /* + * GetLwLockList --- puts all LWLockId's from procarrays to List + */ + + List + *GetLwLockList() + { + ProcArrayStruct *arrayP = procArray; + int count = 0; + int index; + + List *lwLockList = NIL; + + /* + * Acquiring procarray lock to read. Not sure if it is a good idea, + * since we actually do not need any consisitency for our purposes. + */ + LWLockAcquire(ProcArrayLock, LW_SHARED); + + for (index = 0; index < arrayP->numProcs; index++) + { + int pgprocno = arrayP->pgprocnos[index]; + volatile PGPROC *proc = &allProcs[pgprocno]; + + int lwLockId; + int pid; + + lwLockCell *lwLockListCell; + + lwLockId = proc->lwLockId; + pid = proc->pid; + /* elog(WARNING, " iteration is %u, lwLockId in cycle %u in getlwlocklist", index, lwLockId);*/ + + /* skipping if there is no active lwlocks*/ + if (lwLockId == 0) + continue; + if (lwLockId == -1) + continue; + + lwLockListCell = (lwLockCell *) palloc(sizeof(lwLockCell)); + lwLockListCell->lwLockId = lwLockId; + lwLockListCell->pid = pid; + + /* elog(WARNING, "lwLockId-> in cycle %u in getlwlocklist", lwLockListCell->id);*/ + lwLockList = lappend(lwLockList, lwLockListCell); + } + + LWLockRelease(ProcArrayLock); + + return lwLockList; + } + #ifdef XIDCACHE_DEBUG /* *** a/src/backend/storage/lmgr/lwlock.c --- b/src/backend/storage/lmgr/lwlock.c *************** *** 644,649 **** LWLockAcquireCommon(LWLock *lock, LWLockMode mode, uint64 *valptr, uint64 val) --- 644,652 ---- #endif TRACE_POSTGRESQL_LWLOCK_WAIT_START(T_NAME(lock), T_ID(lock), mode); + + /* we put lwloch id from tranche to procarray */ + proc->lwLockId = T_ID(l); for (;;) { *************** *** 654,659 **** LWLockAcquireCommon(LWLock *lock, LWLockMode mode, uint64 *valptr, uint64 val) --- 657,668 ---- extraWaits++; } + /* + * we set lwlock id to -1 to set it apart from currently active lwlock + * with id and from just initialized lwLockId which is 0 + */ + proc->lwLockId = -1; + TRACE_POSTGRESQL_LWLOCK_WAIT_DONE(T_NAME(lock), T_ID(lock), mode); LOG_LWDEBUG("LWLockAcquire", T_NAME(lock), T_ID(lock), "awakened"); *** a/src/backend/storage/lmgr/proc.c --- b/src/backend/storage/lmgr/proc.c *************** *** 375,380 **** InitProcess(void) --- 375,382 ---- MyProc->lwWaitLink = NULL; MyProc->waitLock = NULL; MyProc->waitProcLock = NULL; + MyProc->lwLockId = 0; + #ifdef USE_ASSERT_CHECKING { int i; *************** *** 538,543 **** InitAuxiliaryProcess(void) --- 540,546 ---- MyProc->lwWaitLink = NULL; MyProc->waitLock = NULL; MyProc->waitProcLock = NULL; + MyProc->lwLockId = 0; #ifdef USE_ASSERT_CHECKING { int i; *** a/src/backend/utils/adt/misc.c --- b/src/backend/utils/adt/misc.c *************** *** 572,574 **** pg_column_is_updatable(PG_FUNCTION_ARGS) --- 572,579 ---- PG_RETURN_BOOL((events & REQ_EVENTS) == REQ_EVENTS); } + + + + + *** a/src/include/storage/proc.h --- b/src/include/storage/proc.h *************** *** 105,110 **** struct PGPROC --- 105,111 ---- bool lwWaiting; /* true if waiting for an LW lock */ uint8 lwWaitMode; /* lwlock mode being waited for */ struct PGPROC *lwWaitLink; /* next waiter for same LW lock */ + int lwLockId; /* Info about lock the process is currently waiting for, if any. */ /* waitLock and waitProcLock are NULL if not currently waiting. */ *************** *** 211,216 **** extern PROC_HDR *ProcGlobal; --- 212,228 ---- extern PGPROC *PreparedXactProcs; + + /* + * We set this structure for pg_stat_lwlock extension. + * We use it as a List cell to deliver info about lwlock + * and process which is holding this lwlock + */ + typedef struct{ + int lwLockId; + int pid; + } lwLockCell; + /* * We set aside some extra PGPROC structures for auxiliary processes, * ie things that aren't full-fledged backends but need shmem access. *** a/src/include/storage/procarray.h --- b/src/include/storage/procarray.h *************** *** 78,84 **** extern bool CountOtherDBBackends(Oid databaseId, extern void XidCacheRemoveRunningXids(TransactionId xid, int nxids, const TransactionId *xids, TransactionId latestXid); ! extern void ProcArraySetReplicationSlotXmin(TransactionId xmin, TransactionId catalog_xmin, bool already_locked); --- 78,84 ---- extern void XidCacheRemoveRunningXids(TransactionId xid, int nxids, const TransactionId *xids, TransactionId latestXid); ! extern List *GetLwLockList(); extern void ProcArraySetReplicationSlotXmin(TransactionId xmin, TransactionId catalog_xmin, bool already_locked);