diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 7cc1d41..f832b45 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -658,6 +658,14 @@ CREATE VIEW pg_stat_bgwriter AS pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; +CREATE VIEW pg_stat_lwlocks AS + SELECT + S.lwlockid, + S.calls, + S.waits, + S.time_ms + FROM pg_stat_get_lwlocks() AS S; + CREATE VIEW pg_user_mappings AS SELECT U.oid AS umid, diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c index 95d4b37..2a2c197 100644 --- a/src/backend/storage/lmgr/lwlock.c +++ b/src/backend/storage/lmgr/lwlock.c @@ -32,6 +32,7 @@ #include "storage/proc.h" #include "storage/spin.h" +#include /* We use the ShmemLock spinlock to protect LWLockAssign */ extern slock_t *ShmemLock; @@ -46,6 +47,11 @@ typedef struct LWLock PGPROC *head; /* head of list of waiting PGPROCs */ PGPROC *tail; /* tail of list of waiting PGPROCs */ /* tail is undefined when head is NULL */ + + /* statistics stuff */ + uint64 calls; + uint64 waits; + uint64 time_ms; } LWLock; /* @@ -287,6 +293,9 @@ CreateLWLocks(void) lock->lock.shared = 0; lock->lock.head = NULL; lock->lock.tail = NULL; + lock->lock.calls = 0; + lock->lock.waits = 0; + lock->lock.time_ms = 0; } /* @@ -342,8 +351,10 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode) PGPROC *proc = MyProc; bool retry = false; int extraWaits = 0; + struct timeval wait_start,wait_done; PRINT_LWDEBUG("LWLockAcquire", lockid, lock); + lock->calls++; #ifdef LWLOCK_STATS /* Set up local count state first time through in a given process */ @@ -467,6 +478,8 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode) #endif TRACE_POSTGRESQL_LWLOCK_WAIT_START(lockid, mode); + lock->waits++; + gettimeofday(&wait_start, NULL); for (;;) { @@ -478,6 +491,8 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode) } TRACE_POSTGRESQL_LWLOCK_WAIT_DONE(lockid, mode); + gettimeofday(&wait_done, NULL); + lock->time_ms += (wait_done.tv_sec-wait_start.tv_sec)*1000 + (wait_done.tv_usec-wait_start.tv_usec)/1000; LOG_LWDEBUG("LWLockAcquire", lockid, "awakened"); @@ -879,3 +894,48 @@ LWLockHeldByMe(LWLockId lockid) } return false; } + +void +lwlock_get_stat(LWLockId lockid, uint64 *calls, uint64 *waits, uint64 *time_ms) +{ + volatile LWLock *lock = &(LWLockArray[lockid].lock); + + SpinLockAcquire(&lock->mutex); + *calls = lock->calls; + *waits = lock->waits; + *time_ms = lock->time_ms; + SpinLockRelease(&lock->mutex); +} + +void +lwlock_get_stat_sum(LWLockId start, LWLockId end, uint64 *calls, uint64 *waits, uint64 *time_ms) +{ + LWLockId lockid; + + *calls = 0; + *waits = 0; + *time_ms = 0; + + for (lockid=start ; lockid<=end ; lockid++) + { + uint64 calls2, waits2, time_ms2; + + lwlock_get_stat(lockid, &calls2, &waits2, &time_ms2); + + *calls += calls2; + *waits += waits2; + *time_ms += time_ms2; + } +} + +void +lwlock_reset_stat(LWLockId lockid) +{ + volatile LWLock *lock = &(LWLockArray[lockid].lock); + + SpinLockAcquire(&lock->mutex); + lock->calls = 0; + lock->waits = 0; + lock->time_ms = 0; + SpinLockRelease(&lock->mutex); +} diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 7c0705a..b4e453f 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -117,6 +117,8 @@ extern Datum pg_stat_reset_shared(PG_FUNCTION_ARGS); extern Datum pg_stat_reset_single_table_counters(PG_FUNCTION_ARGS); extern Datum pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_lwlocks(PG_FUNCTION_ARGS); + /* Global bgwriter statistics, from bgwriter.c */ extern PgStat_MsgBgWriter bgwriterStats; @@ -1700,3 +1702,109 @@ pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } + +Datum +pg_stat_get_lwlocks(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + + /* stuff done only on the first call of the function */ + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + TupleDesc tupdesc; + + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + tupdesc = CreateTemplateTupleDesc(4, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "lockid", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "calls", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "waits", + INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "time_ms", + INT8OID, -1, 0); + + funcctx->tuple_desc = BlessTupleDesc(tupdesc); + funcctx->max_calls = NumLWLocks(); + + MemoryContextSwitchTo(oldcontext); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + + if (funcctx->call_cntr < funcctx->max_calls) + { + Datum values[4]; + bool nulls[4]; + HeapTuple tuple; + LWLockId lockid; + uint64 calls,waits,time_ms; + + MemSet(values, 0, sizeof(values)); + MemSet(nulls, 0, sizeof(nulls)); + + lockid = funcctx->call_cntr; + + if ( lockidcall_cntr = FirstLockMgrLock - 1; + } + else if ( FirstLockMgrLock<=lockid && lockidcall_cntr = FirstPredicateLockMgrLock - 1; + } + else if ( FirstPredicateLockMgrLock<=lockid && lockidcall_cntr = NumFixedLWLocks - 1; + } + else if ( NumFixedLWLocks<=lockid && lockidcall_cntr = NumLWLocks() - 1; + } + + values[0] = Int64GetDatum(lockid); + values[1] = Int64GetDatum(calls); + values[2] = Int64GetDatum(waits); + values[3] = Int64GetDatum(time_ms); + + tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); + + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); + } + else + { + SRF_RETURN_DONE(funcctx); + } +} + +Datum +pg_stat_reset_lwlocks(PG_FUNCTION_ARGS) +{ + LWLockId lockid; + + for (lockid=0 ; lockid