diff -cpr pgsql-orig/src/backend/access/transam/slru.c pgsql/src/backend/access/transam/slru.c *** pgsql-orig/src/backend/access/transam/slru.c Mon Jul 31 09:22:04 2006 --- pgsql/src/backend/access/transam/slru.c Mon Jul 31 09:37:25 2006 *************** SimpleLruInit(SlruCtl ctl, const char *n *** 185,190 **** --- 185,191 ---- char *ptr; Size offset; int slotno; + LWLockKind kind; Assert(!found); *************** SimpleLruInit(SlruCtl ctl, const char *n *** 214,226 **** offset += MAXALIGN(nslots * sizeof(LWLockId)); ptr += BUFFERALIGN(offset); for (slotno = 0; slotno < nslots; slotno++) { shared->page_buffer[slotno] = ptr; shared->page_status[slotno] = SLRU_PAGE_EMPTY; shared->page_dirty[slotno] = false; shared->page_lru_count[slotno] = 0; ! shared->buffer_locks[slotno] = LWLockAssign(); ptr += BLCKSZ; } } --- 215,247 ---- offset += MAXALIGN(nslots * sizeof(LWLockId)); ptr += BUFFERALIGN(offset); + switch (ctllock) + { + case CLogControlLock: + kind = LW_KIND_CLOG; + break; + case SubtransControlLock: + kind = LW_KIND_SUBTRANS; + break; + case MultiXactOffsetControlLock: + kind = LW_KIND_MULTIXACTOFFSET; + break; + case MultiXactMemberControlLock: + kind = LW_KIND_MULTIXACTMEMBER; + break; + default: + elog(ERROR, "invalid SLRU lockid: %d", (int) ctllock); + kind = 0; + break; + } + for (slotno = 0; slotno < nslots; slotno++) { shared->page_buffer[slotno] = ptr; shared->page_status[slotno] = SLRU_PAGE_EMPTY; shared->page_dirty[slotno] = false; shared->page_lru_count[slotno] = 0; ! shared->buffer_locks[slotno] = LWLockAssign(kind); ptr += BLCKSZ; } } diff -cpr pgsql-orig/src/backend/catalog/system_views.sql pgsql/src/backend/catalog/system_views.sql *** pgsql-orig/src/backend/catalog/system_views.sql Mon Jul 31 09:22:04 2006 --- pgsql/src/backend/catalog/system_views.sql Mon Jul 31 09:37:25 2006 *************** CREATE VIEW pg_stat_database AS *** 355,360 **** --- 355,375 ---- pg_stat_get_db_blocks_hit(D.oid) AS blks_hit FROM pg_database D; + CREATE VIEW pg_stat_lwlocks AS + SELECT + kind, + pg_stat_get_lwlock_name(kind) AS lwlock, + sum(sh_call) AS sh_call, + sum(sh_wait) AS sh_wait, + sum(ex_call) AS ex_call, + sum(ex_wait) AS ex_wait + FROM pg_stat_get_lwlocks() AS L( + id int, kind int, + sh_call int8, sh_wait int8, + ex_call int8, ex_wait int8) + GROUP BY kind + ORDER BY kind; + -- -- Fix up built-in functions that make use of OUT parameters. -- We can't currently fill these values in during bootstrap, because diff -cpr pgsql-orig/src/backend/storage/buffer/buf_init.c pgsql/src/backend/storage/buffer/buf_init.c *** pgsql-orig/src/backend/storage/buffer/buf_init.c Mon Jul 31 09:22:05 2006 --- pgsql/src/backend/storage/buffer/buf_init.c Mon Jul 31 09:37:25 2006 *************** InitBufferPool(void) *** 125,132 **** */ buf->freeNext = i + 1; ! buf->io_in_progress_lock = LWLockAssign(); ! buf->content_lock = LWLockAssign(); } /* Correct last entry of linked list */ --- 125,132 ---- */ buf->freeNext = i + 1; ! buf->io_in_progress_lock = LWLockAssign(LW_KIND_BUFFERIO); ! buf->content_lock = LWLockAssign(LW_KIND_BUFFERCONTENT); } /* Correct last entry of linked list */ diff -cpr pgsql-orig/src/backend/storage/lmgr/lwlock.c pgsql/src/backend/storage/lmgr/lwlock.c *** pgsql-orig/src/backend/storage/lmgr/lwlock.c Mon Jul 31 09:22:05 2006 --- pgsql/src/backend/storage/lmgr/lwlock.c Mon Jul 31 09:37:25 2006 *************** typedef struct LWLock *** 41,46 **** --- 41,50 ---- int shared; /* # of shared holders (0..MaxBackends) */ PGPROC *head; /* head of list of waiting PGPROCs */ PGPROC *tail; /* tail of list of waiting PGPROCs */ + #ifdef LWLOCK_STAT + LWLockStat stat; + #endif + /* tail is undefined when head is NULL */ } LWLock; *************** typedef struct LWLock *** 53,62 **** * Opterons. (Of course, we have to also ensure that the array start * address is suitably aligned.) * ! * LWLock is between 16 and 32 bytes on all known platforms, so these two ! * cases are sufficient. ! */ ! #define LWLOCK_PADDED_SIZE (sizeof(LWLock) <= 16 ? 16 : 32) typedef union LWLockPadded { --- 57,69 ---- * Opterons. (Of course, we have to also ensure that the array start * address is suitably aligned.) * ! * LWLock is between 16 and 32 bytes on all known platforms if LWLOCK_STAT ! * is not defined, and less than 64 bytes if defined. So these three cases ! * are sufficient. ! */ ! #define LWLOCK_PADDED_SIZE \ ! (sizeof(LWLock) <= 16 ? 16 : \ ! (sizeof(LWLock) <= 32 ? 32 : 64)) typedef union LWLockPadded { *************** typedef union LWLockPadded *** 71,76 **** --- 78,84 ---- */ NON_EXEC_STATIC LWLockPadded *LWLockArray = NULL; + #define GetLWLockCounter() ((int *) ((char *) LWLockArray - 2 * sizeof(int))) /* * We use this structure to keep track of locked LWLocks for release *************** static int *ex_acquire_counts; *** 90,95 **** --- 98,149 ---- static int *block_counts; #endif + #ifdef LWLOCK_STAT + + static char* LWLockIdNames[] = + { + /* System fixed locks */ + "BufFreelistLock", + "ShmemIndexLock", + "OidGenLock", + "XidGenLock", + "ProcArrayLock", + "SInvalLock", + "FreeSpaceLock", + "WALInsertLock", + "WALWriteLock", + "ControlFileLock", + "CheckpointLock", + "CheckpointStartLock", + "CLogControlLock", + "SubtransControlLock", + "MultiXactGenLock", + "MultiXactOffsetControlLock", + "MultiXactMemberControlLock", + "RelCacheInitLock", + "BgWriterCommLock", + "TwoPhaseStateLock", + "TablespaceCreateLock", + "BtreeVacuumLock", + "BufMappingLock", + "LockMgrLock", + /* Dynamic locks */ + "BufferIO", + "BufferContent", + "CLogBuffer", + "SubTransBuffer", + "MultiXactOffsetBuffer", + "MultiXactMemberBuffer", + }; + + #define StaticAssert(cond) extern char static_assertion_failure[(cond) ? 1 : -1] + + StaticAssert( lengthof(LWLockIdNames) == NUM_LW_KINDS ); + + #define LWLockKinds ((LWLockKind *) \ + ((char *) LWLockArray + sizeof(LWLockPadded) * GetLWLockCounter()[1])) + + #endif /* LWLOCK_STAT */ #ifdef LOCK_DEBUG bool Trace_lwlocks = false; *************** static void *** 121,127 **** print_lwlock_stats(int code, Datum arg) { int i; ! int *LWLockCounter = (int *) ((char *) LWLockArray - 2 * sizeof(int)); int numLocks = LWLockCounter[1]; /* Grab an LWLock to keep different backends from mixing reports */ --- 175,181 ---- print_lwlock_stats(int code, Datum arg) { int i; ! int *LWLockCounter = GetLWLockCounter(); int numLocks = LWLockCounter[1]; /* Grab an LWLock to keep different backends from mixing reports */ *************** LWLockShmemSize(void) *** 193,198 **** --- 247,257 ---- /* Space for dynamic allocation counter, plus room for alignment. */ size = add_size(size, 2 * sizeof(int) + LWLOCK_PADDED_SIZE); + #ifdef LWLOCK_STAT + /* Space for the LWLockKind array. */ + size = add_size(size, numLocks * sizeof(LWLockKind)); + #endif + return size; } *************** CreateLWLocks(void) *** 232,246 **** lock->lock.shared = 0; lock->lock.head = NULL; lock->lock.tail = NULL; } /* * Initialize the dynamic-allocation counter, which is stored just before * the first LWLock. */ ! LWLockCounter = (int *) ((char *) LWLockArray - 2 * sizeof(int)); LWLockCounter[0] = (int) NumFixedLWLocks; LWLockCounter[1] = numLocks; } --- 291,318 ---- lock->lock.shared = 0; lock->lock.head = NULL; lock->lock.tail = NULL; + #ifdef LWLOCK_STAT + memset(&lock->lock.stat, 0, sizeof(LWLockStat)); + #endif } /* * Initialize the dynamic-allocation counter, which is stored just before * the first LWLock. */ ! LWLockCounter = GetLWLockCounter(); LWLockCounter[0] = (int) NumFixedLWLocks; LWLockCounter[1] = numLocks; + + #ifdef LWLOCK_STAT + /* Initialize the named fixed locks, BufMappingLocks and LockMgrLocks. */ + for (id = 0; id <= (int) LW_KIND_LASTFIXED; id++) + LWLockKinds[id] = (LWLockKind) id; + for (id = 0; id < NUM_BUFFER_PARTITIONS; id++) + LWLockKinds[FirstBufMappingLock + id] = LW_KIND_BUFFER; + for (id = 0; id < NUM_LOCK_PARTITIONS; id++) + LWLockKinds[FirstLockMgrLock + id] = LW_KIND_LOCK; + #endif } *************** CreateLWLocks(void) *** 253,266 **** * LWLocks after startup. */ LWLockId ! LWLockAssign(void) { LWLockId result; /* use volatile pointer to prevent code rearrangement */ volatile int *LWLockCounter; ! LWLockCounter = (int *) ((char *) LWLockArray - 2 * sizeof(int)); SpinLockAcquire(ShmemLock); if (LWLockCounter[0] >= LWLockCounter[1]) { --- 325,338 ---- * LWLocks after startup. */ LWLockId ! LWLockAssign(LWLockKind kind) { LWLockId result; /* use volatile pointer to prevent code rearrangement */ volatile int *LWLockCounter; ! LWLockCounter = GetLWLockCounter(); SpinLockAcquire(ShmemLock); if (LWLockCounter[0] >= LWLockCounter[1]) { *************** LWLockAssign(void) *** 269,274 **** --- 341,351 ---- } result = (LWLockId) (LWLockCounter[0]++); SpinLockRelease(ShmemLock); + + #ifdef LWLOCK_STAT + LWLockKinds[result] = kind; + #endif + return result; } *************** LWLockAcquire(LWLockId lockid, LWLockMod *** 294,300 **** /* Set up local count state first time through in a given process */ if (counts_for_pid != MyProcPid) { ! int *LWLockCounter = (int *) ((char *) LWLockArray - 2 * sizeof(int)); int numLocks = LWLockCounter[1]; sh_acquire_counts = calloc(numLocks, sizeof(int)); --- 371,377 ---- /* Set up local count state first time through in a given process */ if (counts_for_pid != MyProcPid) { ! int *LWLockCounter = GetLWLockCounter(); int numLocks = LWLockCounter[1]; sh_acquire_counts = calloc(numLocks, sizeof(int)); *************** LWLockAcquire(LWLockId lockid, LWLockMod *** 351,356 **** --- 428,438 ---- /* Acquire mutex. Time spent holding mutex should be short! */ SpinLockAcquire(&lock->mutex); + #ifdef LWLOCK_STAT + if (!retry) + lock->stat.ncall[mode]++; + #endif /* LWLOCK_STAT */ + /* If retrying, allow LWLockRelease to release waiters again */ if (retry) lock->releaseOK = true; *************** LWLockAcquire(LWLockId lockid, LWLockMod *** 380,385 **** --- 462,471 ---- if (!mustwait) break; /* got the lock */ + #ifdef LWLOCK_STAT + lock->stat.nwait[mode]++; + #endif /* LWLOCK_STAT */ + /* * Add myself to wait queue. * *************** LWLockHeldByMe(LWLockId lockid) *** 665,667 **** --- 751,800 ---- } return false; } + + #ifdef LWLOCK_STAT + + const char * + LWLockName(LWLockKind lwkind) + { + if (0 <= lwkind && lwkind < NUM_LW_KINDS) + return LWLockIdNames[lwkind]; + return "Unknown"; + } + + LWLockKind + LWLockGetStat(LWLockId lockid, LWLockStat *dst) + { + int *LWLockCounter = GetLWLockCounter(); + int nAvailableLWLocks = LWLockCounter[0]; + volatile LWLock *lock; + + if (lockid >= nAvailableLWLocks) + return LW_KIND_INVALID; + + lock = &LWLockArray[lockid].lock; + SpinLockAcquire(&lock->mutex); + memcpy(dst, (const void*) &lock->stat, sizeof(LWLockStat)); + SpinLockRelease(&lock->mutex); + return LWLockKinds[lockid]; + } + + void + LWLockResetStat(void) + { + int *LWLockCounter = GetLWLockCounter(); + int nAvailableLWLocks = LWLockCounter[0]; + int i; + + for (i = 0; i < nAvailableLWLocks; i++) + { + volatile LWLock *lock = &LWLockArray[i].lock; + volatile LWLockStat *lwstat = &lock->stat; + + SpinLockAcquire(&lock->mutex); + memset((void*) lwstat, 0, sizeof(LWLockStat)); + SpinLockRelease(&lock->mutex); + } + } + + #endif /* LWLOCK_STAT */ diff -cpr pgsql-orig/src/backend/utils/adt/pgstatfuncs.c pgsql/src/backend/utils/adt/pgstatfuncs.c *** pgsql-orig/src/backend/utils/adt/pgstatfuncs.c Mon Jul 31 09:22:05 2006 --- pgsql/src/backend/utils/adt/pgstatfuncs.c Mon Jul 31 09:37:25 2006 *************** *** 14,19 **** --- 14,21 ---- */ #include "postgres.h" + #include "access/heapam.h" + #include "catalog/pg_type.h" #include "funcapi.h" #include "miscadmin.h" #include "pgstat.h" *************** extern Datum pg_stat_get_db_xact_rollbac *** 53,58 **** --- 55,63 ---- extern Datum pg_stat_get_db_blocks_fetched(PG_FUNCTION_ARGS); extern Datum pg_stat_get_db_blocks_hit(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_lwlock_name(PG_FUNCTION_ARGS); + extern Datum pg_stat_get_lwlocks(PG_FUNCTION_ARGS); + extern Datum pg_stat_reset_lwlocks(PG_FUNCTION_ARGS); Datum pg_stat_get_numscans(PG_FUNCTION_ARGS) *************** pg_stat_get_db_blocks_hit(PG_FUNCTION_AR *** 600,602 **** --- 605,736 ---- PG_RETURN_INT64(result); } + + /* + * LWLOCK_STAT staff + */ + + #ifdef LWLOCK_STAT + + static HeapTuple + formLWLockStatTuple(FuncCallContext *funcctx) + { + int32 lockid = funcctx->call_cntr; + LWLockStat stat; + char nulls[7]; + Datum values[7]; + LWLockKind kind; + + kind = LWLockGetStat(lockid, &stat); + if (kind == LW_KIND_INVALID) + return NULL; + + MemSet(values, 0, sizeof(values)); + MemSet(nulls, ' ', sizeof(nulls)); + + values[0] = Int32GetDatum(lockid); + values[1] = Int32GetDatum((int32) kind); + values[2] = Int64GetDatum(stat.ncall[LW_SHARED]); + values[3] = Int64GetDatum(stat.nwait[LW_SHARED]); + values[4] = Int64GetDatum(stat.ncall[LW_EXCLUSIVE]); + values[5] = Int64GetDatum(stat.nwait[LW_EXCLUSIVE]); + + return heap_formtuple(funcctx->tuple_desc, values, nulls); + } + + /* pg_stat_get_lwlock_name(integer) : text */ + Datum + pg_stat_get_lwlock_name(PG_FUNCTION_ARGS) + { + LWLockKind lwkind; + const char *name; + int name_len; + int ret_len; + text *result; + + lwkind = (LWLockKind) PG_GETARG_INT32(0); + + name = LWLockName(lwkind); + name_len = strlen(name); + ret_len = VARHDRSZ + name_len; + result = palloc(ret_len); + VARATT_SIZEP(result) = ret_len; + memcpy(VARDATA(result), name, name_len); + + PG_RETURN_TEXT_P(result); + } + + /* pg_stat_get_lwlock(void) : set of lwlock_stat */ + Datum + pg_stat_get_lwlocks(PG_FUNCTION_ARGS) + { + FuncCallContext *funcctx; + HeapTuple tuple; + + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + TupleDesc desc; + + funcctx = SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + desc = CreateTemplateTupleDesc(6, false); + + TupleDescInitEntry(desc, (AttrNumber)1, "id", INT4OID, -1, 0); + TupleDescInitEntry(desc, (AttrNumber)2, "kind", INT4OID, -1, 0); + TupleDescInitEntry(desc, (AttrNumber)3, "sh_call", INT8OID, -1, 0); + TupleDescInitEntry(desc, (AttrNumber)4, "sh_wait", INT8OID, -1, 0); + TupleDescInitEntry(desc, (AttrNumber)5, "ex_call", INT8OID, -1, 0); + TupleDescInitEntry(desc, (AttrNumber)6, "ex_wait", INT8OID, -1, 0); + + funcctx->tuple_desc = BlessTupleDesc(desc); + funcctx->max_calls = (uint32)NumLWLocks(); + + MemoryContextSwitchTo(oldcontext); + } + + funcctx = SRF_PERCALL_SETUP(); + + if (funcctx->call_cntr < funcctx->max_calls + && (tuple = formLWLockStatTuple(funcctx)) != NULL) + { + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); + } + else + { + SRF_RETURN_DONE(funcctx); + } + } + + /* pg_stat_reset_lwlocks(void) : boolean */ + Datum + pg_stat_reset_lwlocks(PG_FUNCTION_ARGS) + { + LWLockResetStat(); + PG_RETURN_BOOL(true); + } + + #else /* LWLOCK_STAT */ + + Datum + pg_stat_get_lwlock_name(PG_FUNCTION_ARGS) + { + elog(ERROR, "LWLOCK_STAT is disabled"); + PG_RETURN_NULL(); + } + + Datum + pg_stat_get_lwlocks(PG_FUNCTION_ARGS) + { + elog(ERROR, "LWLOCK_STAT is disabled"); + PG_RETURN_NULL(); + } + + Datum + pg_stat_reset_lwlocks(PG_FUNCTION_ARGS) + { + elog(ERROR, "LWLOCK_STAT is disabled"); + PG_RETURN_NULL(); + } + + #endif /* LWLOCK_STAT */ diff -cpr pgsql-orig/src/include/catalog/pg_proc.h pgsql/src/include/catalog/pg_proc.h *** pgsql-orig/src/include/catalog/pg_proc.h Mon Jul 31 09:22:08 2006 --- pgsql/src/include/catalog/pg_proc.h Mon Jul 31 09:37:25 2006 *************** DESCR("Statistics: Blocks fetched for da *** 2917,2922 **** --- 2917,2929 ---- DATA(insert OID = 1945 ( pg_stat_get_db_blocks_hit PGNSP PGUID 12 f f t f s 1 20 "26" _null_ _null_ _null_ pg_stat_get_db_blocks_hit - _null_ )); DESCR("Statistics: Blocks found in cache for database"); + DATA(insert OID = 2830 ( pg_stat_get_lwlock_name PGNSP PGUID 12 f f t f s 1 25 "23" _null_ _null_ _null_ pg_stat_get_lwlock_name - _null_ )); + DESCR("Statistics: Name of lwlock"); + DATA(insert OID = 2831 ( pg_stat_get_lwlocks PGNSP PGUID 12 f f t t v 0 2249 "" _null_ _null_ _null_ pg_stat_get_lwlocks - _null_ )); + DESCR("Statistics: Set of lwlock_stat tuple"); + DATA(insert OID = 2832 ( pg_stat_reset_lwlocks PGNSP PGUID 12 f f f f v 0 16 "" _null_ _null_ _null_ pg_stat_reset_lwlocks - _null_ )); + DESCR("Statistics: Reset collected statistics"); + DATA(insert OID = 1946 ( encode PGNSP PGUID 12 f f t f i 2 25 "17 25" _null_ _null_ _null_ binary_encode - _null_ )); DESCR("Convert bytea value into some ascii-only text string"); DATA(insert OID = 1947 ( decode PGNSP PGUID 12 f f t f i 2 17 "25 25" _null_ _null_ _null_ binary_decode - _null_ )); diff -cpr pgsql-orig/src/include/pg_config_manual.h pgsql/src/include/pg_config_manual.h *** pgsql-orig/src/include/pg_config_manual.h Mon Jul 31 09:22:09 2006 --- pgsql/src/include/pg_config_manual.h Mon Jul 31 09:37:25 2006 *************** *** 245,250 **** --- 245,255 ---- /* #define WAL_DEBUG */ /* + * Enable statistics collector for LWLock operations + */ + /*#define LWLOCK_STAT*/ + + /* * Enable tracing of resource consumption during sort operations; * see also the trace_sort GUC var. For 8.1 this is enabled by default. */ diff -cpr pgsql-orig/src/include/storage/lwlock.h pgsql/src/include/storage/lwlock.h *** pgsql-orig/src/include/storage/lwlock.h Mon Jul 31 09:22:09 2006 --- pgsql/src/include/storage/lwlock.h Mon Jul 31 09:37:25 2006 *************** typedef enum LWLockMode *** 76,87 **** LW_SHARED } LWLockMode; #ifdef LOCK_DEBUG extern bool Trace_lwlocks; #endif ! extern LWLockId LWLockAssign(void); extern void LWLockAcquire(LWLockId lockid, LWLockMode mode); extern bool LWLockConditionalAcquire(LWLockId lockid, LWLockMode mode); extern void LWLockRelease(LWLockId lockid); --- 76,102 ---- LW_SHARED } LWLockMode; + typedef enum LWLockKind + { + LW_KIND_INVALID = -1, + LW_KIND_FIRSTFIXED = 0, + LW_KIND_LASTFIXED = FirstBufMappingLock - 1, + LW_KIND_BUFFER, /* BufferMappingLock(s) */ + LW_KIND_LOCK, /* LockMgrLock(s) */ + LW_KIND_BUFFERIO, /* BufferDesc.io_in_progress_lock */ + LW_KIND_BUFFERCONTENT, /* BufferDesc.content_lock */ + LW_KIND_CLOG, /* ClogCtl */ + LW_KIND_SUBTRANS, /* SubTransCtl */ + LW_KIND_MULTIXACTOFFSET,/* MultiXactOffsetCtl */ + LW_KIND_MULTIXACTMEMBER,/* MultiXactMemberCtl */ + NUM_LW_KINDS, /* last kind */ + } LWLockKind; #ifdef LOCK_DEBUG extern bool Trace_lwlocks; #endif ! extern LWLockId LWLockAssign(LWLockKind kind); extern void LWLockAcquire(LWLockId lockid, LWLockMode mode); extern bool LWLockConditionalAcquire(LWLockId lockid, LWLockMode mode); extern void LWLockRelease(LWLockId lockid); *************** extern bool LWLockHeldByMe(LWLockId lock *** 91,95 **** --- 106,124 ---- extern int NumLWLocks(void); extern Size LWLockShmemSize(void); extern void CreateLWLocks(void); + + #ifdef LWLOCK_STAT + + typedef struct LWLockStat + { + int64 ncall[2]; /* # of calls (0:Exclusive, 1:Shared) */ + int64 nwait[2]; /* # of waits (0:Exclusive, 1:Shared) */ + } LWLockStat; + + extern const char *LWLockName(LWLockKind lwkind); + extern LWLockKind LWLockGetStat(LWLockId lockid, LWLockStat *dst); + extern void LWLockResetStat(void); + + #endif /* LWLOCK_STAT */ #endif /* LWLOCK_H */