pg_stat_lwlocks_20121013_2.diff

text/plain

Filename: pg_stat_lwlocks_20121013_2.diff
Type: text/plain
Part: 0
Message: Re: pg_stat_lwlocks view - lwlocks statistics, round 2

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
src/backend/catalog/system_views.sql 11 0
src/backend/postmaster/pgstat.c 113 12
src/backend/storage/lmgr/lwlock.c 118 0
src/backend/tcop/postgres.c 2 0
src/backend/utils/adt/pgstatfuncs.c 172 11
src/include/catalog/pg_proc.h 4 0
src/include/pgstat.h 47 4
src/include/storage/lwlock.h 10 0
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 607a72f..2f84940 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -671,6 +671,17 @@ 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.local_calls,
+            S.local_waits,
+            S.local_time_ms,
+            S.shared_calls,
+            S.shared_waits,
+            S.shared_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/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 8389d5c..970e8bd 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -282,6 +282,7 @@ static void pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len);
 static void pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len);
 static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
+static void pgstat_recv_lwlockstat(PgStat_MsgLWLockstat *msg, int len);
 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
 
@@ -1188,6 +1189,8 @@ pgstat_reset_shared_counters(const char *target)
 
 	if (strcmp(target, "bgwriter") == 0)
 		msg.m_resettarget = RESET_BGWRITER;
+	else if (strcmp(target, "lwlocks") == 0)
+		msg.m_resettarget = RESET_LWLOCKSTAT;
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1344,6 +1347,72 @@ pgstat_report_recovery_conflict(int reason)
 }
 
 /* --------
+ * pgstat_report_lwlockstat() -
+ *
+ *	Tell the collector about lwlock statistics.
+ * --------
+ */
+void
+pgstat_report_lwlockstat(void)
+{
+	PgStat_MsgLWLockstat msg;
+
+	int32 lockid = 0;
+	int need_continue = 0;
+
+ report_continue:
+	memset(&msg, 0, sizeof(PgStat_MsgLWLockstat));
+
+	for ( ; lockid<NumFixedLWLocks+1 ; lockid++)
+	{
+		uint64 calls, waits, time_ms;
+
+		calls = waits = time_ms = 0;
+
+		calls   = lwlock_get_stat_calls_global(lockid);
+		waits   = lwlock_get_stat_waits_global(lockid);
+		time_ms = lwlock_get_stat_time_ms_global(lockid);
+
+		if ( calls>0 || waits>0 || time_ms>0 )
+		{
+			msg.m_entry[msg.m_nentries].lockid      = lockid;
+			msg.m_entry[msg.m_nentries].calls       = calls;
+			msg.m_entry[msg.m_nentries].waits       = waits;
+			msg.m_entry[msg.m_nentries].waited_time = time_ms;
+
+			msg.m_nentries++;
+
+			lwlock_reset_stat_global(lockid);
+
+			/*
+			 * Need to keep a message packet smaller than PGSTAT_MSG_PAYLOAD.
+			 * So, going to split a report into multiple messages.
+			 */
+			if ( msg.m_nentries>=MAX_LWLOCKSTAT_ENTRIES )
+			{
+				need_continue = 1;
+				break;
+			}
+		}
+	}
+
+	if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
+		return;
+
+	pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_LWLOCKSTAT);
+	pgstat_send(&msg, sizeof(msg));
+
+	/*
+	 * Need to continue because of the larger report?
+	 */
+	if ( need_continue )
+	{
+		need_continue = 0;
+		goto report_continue;
+	}
+}
+
+/* --------
  * pgstat_report_deadlock() -
  *
  *	Tell the collector about a deadlock detected.
@@ -3219,6 +3288,10 @@ PgstatCollectorMain(int argc, char *argv[])
 					pgstat_recv_recoveryconflict((PgStat_MsgRecoveryConflict *) &msg, len);
 					break;
 
+				case PGSTAT_MTYPE_LWLOCKSTAT:
+					pgstat_recv_lwlockstat((PgStat_MsgLWLockstat *) &msg, len);
+					break;
+
 				case PGSTAT_MTYPE_DEADLOCK:
 					pgstat_recv_deadlock((PgStat_MsgDeadlock *) &msg, len);
 					break;
@@ -4379,8 +4452,15 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
 	if (msg->m_resettarget == RESET_BGWRITER)
 	{
 		/* Reset the global background writer statistics for the cluster. */
-		memset(&globalStats, 0, sizeof(globalStats));
-		globalStats.stat_reset_timestamp = GetCurrentTimestamp();
+		memset(&globalStats.bgwriterstats, 0, sizeof(globalStats.bgwriterstats));
+		globalStats.bgwriterstats.reset_timestamp = GetCurrentTimestamp();
+	}
+
+	if (msg->m_resettarget == RESET_LWLOCKSTAT)
+	{
+		/* Reset the global lwlock statistics for the cluster. */
+		memset(&globalStats.lwlockstats, 0, sizeof(globalStats.lwlockstats));
+		globalStats.lwlockstats.reset_timestamp = GetCurrentTimestamp();
 	}
 
 	/*
@@ -4521,16 +4601,16 @@ pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len)
 static void
 pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
 {
-	globalStats.timed_checkpoints += msg->m_timed_checkpoints;
-	globalStats.requested_checkpoints += msg->m_requested_checkpoints;
-	globalStats.checkpoint_write_time += msg->m_checkpoint_write_time;
-	globalStats.checkpoint_sync_time += msg->m_checkpoint_sync_time;
-	globalStats.buf_written_checkpoints += msg->m_buf_written_checkpoints;
-	globalStats.buf_written_clean += msg->m_buf_written_clean;
-	globalStats.maxwritten_clean += msg->m_maxwritten_clean;
-	globalStats.buf_written_backend += msg->m_buf_written_backend;
-	globalStats.buf_fsync_backend += msg->m_buf_fsync_backend;
-	globalStats.buf_alloc += msg->m_buf_alloc;
+	globalStats.bgwriterstats.timed_checkpoints += msg->m_timed_checkpoints;
+	globalStats.bgwriterstats.requested_checkpoints += msg->m_requested_checkpoints;
+	globalStats.bgwriterstats.checkpoint_write_time += msg->m_checkpoint_write_time;
+	globalStats.bgwriterstats.checkpoint_sync_time += msg->m_checkpoint_sync_time;
+	globalStats.bgwriterstats.buf_written_checkpoints += msg->m_buf_written_checkpoints;
+	globalStats.bgwriterstats.buf_written_clean += msg->m_buf_written_clean;
+	globalStats.bgwriterstats.maxwritten_clean += msg->m_maxwritten_clean;
+	globalStats.bgwriterstats.buf_written_backend += msg->m_buf_written_backend;
+	globalStats.bgwriterstats.buf_fsync_backend += msg->m_buf_fsync_backend;
+	globalStats.bgwriterstats.buf_alloc += msg->m_buf_alloc;
 }
 
 /* ----------
@@ -4574,6 +4654,27 @@ pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len)
 }
 
 /* ----------
+ * pgstat_recv_lwlockstat() -
+ *
+ *	Process a LWLockstat message.
+ * ----------
+ */
+static void
+pgstat_recv_lwlockstat(PgStat_MsgLWLockstat *msg, int len)
+{
+	int i;
+
+	for (i=0 ; i<msg->m_nentries ; i++)
+	{
+		int32 lockid = msg->m_entry[i].lockid;
+
+		globalStats.lwlockstats.lwlock_stat[lockid].calls       += msg->m_entry[i].calls;
+		globalStats.lwlockstats.lwlock_stat[lockid].waits       += msg->m_entry[i].waits;
+		globalStats.lwlockstats.lwlock_stat[lockid].waited_time += msg->m_entry[i].waited_time;
+	}
+}
+
+/* ----------
  * pgstat_recv_deadlock() -
  *
  *	Process a DEADLOCK message.
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 5e1ce17..402799d 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 <sys/time.h>
 
 /* We use the ShmemLock spinlock to protect LWLockAssign */
 extern slock_t *ShmemLock;
@@ -48,6 +49,25 @@ typedef struct LWLock
 	/* tail is undefined when head is NULL */
 } LWLock;
 
+typedef struct LWLockCounter2
+{
+	/* statistics stuff */
+	uint64		calls;
+	uint64		waits;
+	uint64		time_ms;
+} LWLockCounter2;
+
+/*
+ * LWLockCounterLocal has <NumFixedLWLocks> counters
+ * and one additional counter for dynamic LWLocks
+ * to hold lwlock statistic in the local session.
+ */
+LWLockCounter2 LWLockCounterLocal[NumFixedLWLocks+1];
+
+LWLockCounter2 LWLockCounterGlobal[NumFixedLWLocks+1];
+
+#define LWLockCounterId(X) ((X) < (NumFixedLWLocks+1) ? (X) : (NumFixedLWLocks+1))
+
 /*
  * All the LWLock structs are allocated as an array in shared memory.
  * (LWLockIds are indexes into the array.)	We force the array stride to
@@ -90,6 +110,8 @@ static LWLockId held_lwlocks[MAX_SIMUL_LWLOCKS];
 static int	lock_addin_request = 0;
 static bool lock_addin_request_allowed = true;
 
+static void InitLWockCounter(void);
+
 #ifdef LWLOCK_STATS
 static int	counts_for_pid = 0;
 static int *sh_acquire_counts;
@@ -253,6 +275,26 @@ LWLockShmemSize(void)
 	return size;
 }
 
+/*
+ * Initialize local and global counters for lwlock statistics.
+ */
+static void
+InitLWockCounter(void)
+{
+	int i;
+
+	for (i=0 ; i<NumFixedLWLocks+1 ; i++)
+	{
+		LWLockCounterLocal[i].calls   = 0;
+		LWLockCounterLocal[i].waits   = 0;
+		LWLockCounterLocal[i].time_ms = 0;
+
+		LWLockCounterGlobal[i].calls   = 0;
+		LWLockCounterGlobal[i].waits   = 0;
+		LWLockCounterGlobal[i].time_ms = 0;
+	}
+}
+
 
 /*
  * Allocate shmem space for LWLocks and initialize the locks.
@@ -298,6 +340,8 @@ CreateLWLocks(void)
 	LWLockCounter = (int *) ((char *) LWLockArray - 2 * sizeof(int));
 	LWLockCounter[0] = (int) NumFixedLWLocks;
 	LWLockCounter[1] = numLocks;
+
+	InitLWockCounter();
 }
 
 
@@ -344,9 +388,13 @@ 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);
 
+	LWLockCounterLocal[ LWLockCounterId(lockid) ].calls++;
+	LWLockCounterGlobal[ LWLockCounterId(lockid) ].calls++;
+
 #ifdef LWLOCK_STATS
 	/* Set up local count state first time through in a given process */
 	if (counts_for_pid != MyProcPid)
@@ -395,6 +443,7 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
 	for (;;)
 	{
 		bool		mustwait;
+		uint64 waited;
 
 		/* Acquire mutex.  Time spent holding mutex should be short! */
 #ifdef LWLOCK_STATS
@@ -473,6 +522,9 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
 #endif
 
 		TRACE_POSTGRESQL_LWLOCK_WAIT_START(lockid, mode);
+		LWLockCounterLocal[ LWLockCounterId(lockid) ].waits++;
+		LWLockCounterGlobal[ LWLockCounterId(lockid) ].waits++;
+		gettimeofday(&wait_start, NULL);
 
 		for (;;)
 		{
@@ -484,6 +536,20 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
 		}
 
 		TRACE_POSTGRESQL_LWLOCK_WAIT_DONE(lockid, mode);
+		gettimeofday(&wait_done, NULL);
+
+		if ( wait_done.tv_usec >= wait_start.tv_usec )
+		{
+			waited = ( wait_done.tv_usec - wait_start.tv_usec ) / 1000 ;
+			waited += ( wait_done.tv_sec - wait_start.tv_sec ) * 1000 ;
+		}
+		else
+		{
+			waited = ( wait_done.tv_usec + 1000*1000 - wait_start.tv_usec ) / 1000 ;
+			waited += ( wait_done.tv_sec - 1 - wait_start.tv_sec ) * 1000 ;
+		}
+		LWLockCounterLocal[ LWLockCounterId(lockid) ].time_ms += waited;
+		LWLockCounterGlobal[ LWLockCounterId(lockid) ].time_ms += waited;
 
 		LOG_LWDEBUG("LWLockAcquire", lockid, "awakened");
 
@@ -885,3 +951,55 @@ LWLockHeldByMe(LWLockId lockid)
 	}
 	return false;
 }
+
+uint64
+lwlock_get_stat_calls_local(LWLockId lockid)
+{
+	return LWLockCounterLocal[ LWLockCounterId(lockid) ].calls;
+}
+
+uint64
+lwlock_get_stat_waits_local(LWLockId lockid)
+{
+	return LWLockCounterLocal[ LWLockCounterId(lockid) ].waits;
+}
+
+uint64
+lwlock_get_stat_time_ms_local(LWLockId lockid)
+{
+	return LWLockCounterLocal[ LWLockCounterId(lockid) ].time_ms;
+}
+
+void
+lwlock_reset_stat_local(LWLockId lockid)
+{
+	LWLockCounterLocal[ LWLockCounterId(lockid) ].calls   = 0;
+	LWLockCounterLocal[ LWLockCounterId(lockid) ].waits   = 0;
+	LWLockCounterLocal[ LWLockCounterId(lockid) ].time_ms = 0;
+}
+
+uint64
+lwlock_get_stat_calls_global(LWLockId lockid)
+{
+	return LWLockCounterGlobal[ LWLockCounterId(lockid) ].calls;
+}
+
+uint64
+lwlock_get_stat_waits_global(LWLockId lockid)
+{
+	return LWLockCounterGlobal[ LWLockCounterId(lockid) ].waits;
+}
+
+uint64
+lwlock_get_stat_time_ms_global(LWLockId lockid)
+{
+	return LWLockCounterGlobal[ LWLockCounterId(lockid) ].time_ms;
+}
+
+void
+lwlock_reset_stat_global(LWLockId lockid)
+{
+	LWLockCounterGlobal[ LWLockCounterId(lockid) ].calls   = 0;
+	LWLockCounterGlobal[ LWLockCounterId(lockid) ].waits   = 0;
+	LWLockCounterGlobal[ LWLockCounterId(lockid) ].time_ms = 0;
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 585db1a..5ca2c6f 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3919,6 +3919,8 @@ PostgresMain(int argc, char *argv[], const char *username)
 				pgstat_report_activity(STATE_IDLE, NULL);
 			}
 
+			pgstat_report_lwlockstat();
+
 			ReadyForQuery(whereToSendOutput);
 			send_ready_for_query = false;
 		}
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 7d4059f..0a26626 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -118,6 +118,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;
 
@@ -1399,69 +1401,69 @@ pg_stat_get_db_blk_write_time(PG_FUNCTION_ARGS)
 Datum
 pg_stat_get_bgwriter_timed_checkpoints(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_INT64(pgstat_fetch_global()->timed_checkpoints);
+	PG_RETURN_INT64(pgstat_fetch_global()->bgwriterstats.timed_checkpoints);
 }
 
 Datum
 pg_stat_get_bgwriter_requested_checkpoints(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_INT64(pgstat_fetch_global()->requested_checkpoints);
+	PG_RETURN_INT64(pgstat_fetch_global()->bgwriterstats.requested_checkpoints);
 }
 
 Datum
 pg_stat_get_bgwriter_buf_written_checkpoints(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_INT64(pgstat_fetch_global()->buf_written_checkpoints);
+	PG_RETURN_INT64(pgstat_fetch_global()->bgwriterstats.buf_written_checkpoints);
 }
 
 Datum
 pg_stat_get_bgwriter_buf_written_clean(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_INT64(pgstat_fetch_global()->buf_written_clean);
+	PG_RETURN_INT64(pgstat_fetch_global()->bgwriterstats.buf_written_clean);
 }
 
 Datum
 pg_stat_get_bgwriter_maxwritten_clean(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_INT64(pgstat_fetch_global()->maxwritten_clean);
+	PG_RETURN_INT64(pgstat_fetch_global()->bgwriterstats.maxwritten_clean);
 }
 
 Datum
 pg_stat_get_checkpoint_write_time(PG_FUNCTION_ARGS)
 {
 	/* time is already in msec, just convert to double for presentation */
-	PG_RETURN_FLOAT8((double) pgstat_fetch_global()->checkpoint_write_time);
+	PG_RETURN_FLOAT8((double) pgstat_fetch_global()->bgwriterstats.checkpoint_write_time);
 }
 
 Datum
 pg_stat_get_checkpoint_sync_time(PG_FUNCTION_ARGS)
 {
 	/* time is already in msec, just convert to double for presentation */
-	PG_RETURN_FLOAT8((double) pgstat_fetch_global()->checkpoint_sync_time);
+	PG_RETURN_FLOAT8((double) pgstat_fetch_global()->bgwriterstats.checkpoint_sync_time);
 }
 
 Datum
 pg_stat_get_bgwriter_stat_reset_time(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_TIMESTAMPTZ(pgstat_fetch_global()->stat_reset_timestamp);
+	PG_RETURN_TIMESTAMPTZ(pgstat_fetch_global()->bgwriterstats.reset_timestamp);
 }
 
 Datum
 pg_stat_get_buf_written_backend(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_INT64(pgstat_fetch_global()->buf_written_backend);
+	PG_RETURN_INT64(pgstat_fetch_global()->bgwriterstats.buf_written_backend);
 }
 
 Datum
 pg_stat_get_buf_fsync_backend(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_INT64(pgstat_fetch_global()->buf_fsync_backend);
+	PG_RETURN_INT64(pgstat_fetch_global()->bgwriterstats.buf_fsync_backend);
 }
 
 Datum
 pg_stat_get_buf_alloc(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_INT64(pgstat_fetch_global()->buf_alloc);
+	PG_RETURN_INT64(pgstat_fetch_global()->bgwriterstats.buf_alloc);
 }
 
 Datum
@@ -1701,3 +1703,162 @@ 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(7, false);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "lockid",
+						   INT8OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "local_calls",
+						   INT8OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "local_waits",
+						   INT8OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "local_time_ms",
+						   INT8OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "shared_calls",
+						   INT8OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 6, "shared_waits",
+						   INT8OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 7, "shared_time_ms",
+						   INT8OID, -1, 0);
+
+		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+		funcctx->max_calls = NumFixedLWLocks + 1;
+
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	/* stuff done on every call of the function */
+	funcctx = SRF_PERCALL_SETUP();
+
+	if (funcctx->call_cntr < funcctx->max_calls)
+	{
+		Datum		values[7];
+		bool		nulls[7];
+		HeapTuple	tuple;
+		LWLockId	lockid;
+		uint64		local_calls,local_waits,local_time_ms;
+		uint64		shared_calls,shared_waits,shared_time_ms;
+		int i;
+		PgStat_LWLockEntry *lwlock_stat = pgstat_fetch_global()->lwlockstats.lwlock_stat;
+
+		MemSet(values, 0, sizeof(values));
+		MemSet(nulls, 0, sizeof(nulls));
+
+		lockid = funcctx->call_cntr;
+
+		local_calls = local_waits = local_time_ms = 0;
+		shared_calls = shared_waits = shared_time_ms = 0;
+
+		/*
+		 * Partitioned locks need to be summed up by the lock group.
+		 */
+		if ( FirstBufMappingLock <= lockid && lockid < FirstLockMgrLock )
+		{
+			for (i=0 ; i<NUM_BUFFER_PARTITIONS ; i++)
+			{
+				/* local statistics */
+				local_calls   = lwlock_get_stat_calls_local(FirstBufMappingLock+i);
+				local_waits   = lwlock_get_stat_waits_local(FirstBufMappingLock+i);
+				local_time_ms = lwlock_get_stat_time_ms_local(FirstBufMappingLock+i);
+
+				/* global statistics */
+				shared_calls   += lwlock_stat[FirstBufMappingLock+i].calls;
+				shared_waits   += lwlock_stat[FirstBufMappingLock+i].waits;
+				shared_time_ms += lwlock_stat[FirstBufMappingLock+i].waited_time;
+			}
+
+			funcctx->call_cntr += NUM_BUFFER_PARTITIONS;
+		}
+		else if ( FirstLockMgrLock <= lockid && lockid < FirstPredicateLockMgrLock )
+		{
+			for (i=0 ; i<NUM_LOCK_PARTITIONS ; i++)
+			{
+				/* local statistics */
+				local_calls   = lwlock_get_stat_calls_local(FirstLockMgrLock+i);
+				local_waits   = lwlock_get_stat_waits_local(FirstLockMgrLock+i);
+				local_time_ms = lwlock_get_stat_time_ms_local(FirstLockMgrLock+i);
+
+				/* global statistics */
+				shared_calls   += lwlock_stat[FirstLockMgrLock+i].calls;
+				shared_waits   += lwlock_stat[FirstLockMgrLock+i].waits;
+				shared_time_ms += lwlock_stat[FirstLockMgrLock+i].waited_time;
+			}
+
+			funcctx->call_cntr += NUM_LOCK_PARTITIONS;
+		}
+		else if ( FirstPredicateLockMgrLock <= lockid && lockid < NumFixedLWLocks )
+		{
+			for (i=0 ; i<NUM_PREDICATELOCK_PARTITIONS ; i++)
+			{
+				/* local statistics */
+				local_calls   = lwlock_get_stat_calls_local(FirstPredicateLockMgrLock+i);
+				local_waits   = lwlock_get_stat_waits_local(FirstPredicateLockMgrLock+i);
+				local_time_ms = lwlock_get_stat_time_ms_local(FirstPredicateLockMgrLock+i);
+
+				/* global statistics */
+				shared_calls   += lwlock_stat[FirstPredicateLockMgrLock+i].calls;
+				shared_waits   += lwlock_stat[FirstPredicateLockMgrLock+i].waits;
+				shared_time_ms += lwlock_stat[FirstPredicateLockMgrLock+i].waited_time;
+			}
+
+			funcctx->call_cntr += NUM_PREDICATELOCK_PARTITIONS;
+		}
+		else
+		{
+			/* local statistics */
+			local_calls   = lwlock_get_stat_calls_local(lockid);
+			local_waits   = lwlock_get_stat_waits_local(lockid);
+			local_time_ms = lwlock_get_stat_time_ms_local(lockid);
+
+			/* global statistics */
+			shared_calls   = lwlock_stat[lockid].calls;
+			shared_waits   = lwlock_stat[lockid].waits;
+			shared_time_ms = lwlock_stat[lockid].waited_time;
+		}
+
+		values[0] = Int64GetDatum(lockid);
+		values[1] = Int64GetDatum(local_calls);
+		values[2] = Int64GetDatum(local_waits);
+		values[3] = Int64GetDatum(local_time_ms);
+		values[4] = Int64GetDatum(shared_calls);
+		values[5] = Int64GetDatum(shared_waits);
+		values[6] = Int64GetDatum(shared_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<NumLWLocks() ; lockid++)
+	{
+		lwlock_reset_stat_local(lockid);
+	}
+
+	PG_RETURN_VOID();
+}
+
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index f935eb1..4582b12 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2612,6 +2612,10 @@ DATA(insert OID = 1936 (  pg_stat_get_backend_idset		PGNSP PGUID 12 1 100 0 0 f
 DESCR("statistics: currently active backend IDs");
 DATA(insert OID = 2022 (  pg_stat_get_activity			PGNSP PGUID 12 1 100 0 0 f f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,25,16,1184,1184,1184,1184,869,25,23}" "{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,pid,usesysid,application_name,state,query,waiting,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port}" _null_ pg_stat_get_activity _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active backends");
+DATA(insert OID = 3764 (  pg_stat_get_lwlocks  PGNSP PGUID 12 1 100 0 0 f f f f f t s 0 0 2249 "" "{20,20,20,20,20,20,20}" "{o,o,o,o,o,o,o}" "{lwlockid,local_calls,local_waits,local_time_ms,shared_calls,shared_waits,shared_time_ms}" _null_ pg_stat_get_lwlocks _null_ _null_ _null_ ));
+DESCR("statistics: light-weight lock statistics");
+DATA(insert OID = 3765 (  pg_stat_reset_lwlocks  PGNSP PGUID 12 1 0 0 0 f f f f f f v 0 0 2278 "" _null_ _null_ _null_ _null_ pg_stat_reset_lwlocks _null_ _null_ _null_ ));
+DESCR("statistics: reset light-weight lock statistics");
 DATA(insert OID = 3099 (  pg_stat_get_wal_senders	PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,25,25,25,25,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ ));
 DESCR("statistics: information about currently active replication");
 DATA(insert OID = 2026 (  pg_backend_pid				PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ ));
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 613c1c2..0e52534 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -15,6 +15,7 @@
 #include "fmgr.h"
 #include "libpq/pqcomm.h"
 #include "portability/instr_time.h"
+#include "storage/lwlock.h"
 #include "utils/hsearch.h"
 #include "utils/relcache.h"
 
@@ -49,6 +50,7 @@ typedef enum StatMsgType
 	PGSTAT_MTYPE_FUNCPURGE,
 	PGSTAT_MTYPE_RECOVERYCONFLICT,
 	PGSTAT_MTYPE_TEMPFILE,
+	PGSTAT_MTYPE_LWLOCKSTAT,
 	PGSTAT_MTYPE_DEADLOCK
 } StatMsgType;
 
@@ -102,7 +104,8 @@ typedef struct PgStat_TableCounts
 /* Possible targets for resetting cluster-wide shared values */
 typedef enum PgStat_Shared_Reset_Target
 {
-	RESET_BGWRITER
+	RESET_BGWRITER,
+	RESET_LWLOCKSTAT
 } PgStat_Shared_Reset_Target;
 
 /* Possible object types for resetting single counters */
@@ -605,13 +608,31 @@ typedef struct PgStat_StatFuncEntry
 	PgStat_Counter f_self_time;
 } PgStat_StatFuncEntry;
 
+#define MAX_LWLOCKSTAT_ENTRIES 20
+
+typedef struct PgStat_LWLockEntry
+{
+	LWLockId       lockid;
+	PgStat_Counter calls;
+	PgStat_Counter waits;
+	PgStat_Counter waited_time;     /* time in milliseconds */
+} PgStat_LWLockEntry;
+
+typedef struct PgStat_MsgLWLockstat
+{
+	PgStat_MsgHdr m_hdr;
+	int			m_nentries;
+
+	/* Need to keep a msg smaller than PGSTAT_MSG_PAYLOAD */
+	PgStat_LWLockEntry m_entry[MAX_LWLOCKSTAT_ENTRIES];
+} PgStat_MsgLWLockstat;
+
 
 /*
- * Global statistics kept in the stats collector
+ * Global statistics for BgWriter
  */
-typedef struct PgStat_GlobalStats
+typedef struct PgStat_BgWriterGlobalStats
 {
-	TimestampTz stats_timestamp;	/* time of stats file update */
 	PgStat_Counter timed_checkpoints;
 	PgStat_Counter requested_checkpoints;
 	PgStat_Counter checkpoint_write_time;		/* times in milliseconds */
@@ -622,6 +643,28 @@ typedef struct PgStat_GlobalStats
 	PgStat_Counter buf_written_backend;
 	PgStat_Counter buf_fsync_backend;
 	PgStat_Counter buf_alloc;
+	TimestampTz reset_timestamp;
+} PgStat_BgWriterGlobalStats;
+
+/*
+ * Global statistics for LWLocks
+ */
+typedef struct PgStat_LWLockGlobalStats
+{
+	PgStat_LWLockEntry lwlock_stat[NumFixedLWLocks+1];
+	TimestampTz reset_timestamp;
+} PgStat_LWLockGlobalStats;
+
+/*
+ * Global statistics kept in the stats collector
+ */
+typedef struct PgStat_GlobalStats
+{
+	TimestampTz stats_timestamp;	/* time of stats file update */
+
+	PgStat_BgWriterGlobalStats bgwriterstats;
+	PgStat_LWLockGlobalStats lwlockstats;
+
 	TimestampTz stat_reset_timestamp;
 } PgStat_GlobalStats;
 
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 82d8ec4..a101db5 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -119,4 +119,14 @@ extern void CreateLWLocks(void);
 
 extern void RequestAddinLWLocks(int n);
 
+extern uint64 lwlock_get_stat_calls_local(LWLockId);
+extern uint64 lwlock_get_stat_waits_local(LWLockId);
+extern uint64 lwlock_get_stat_time_ms_local(LWLockId);
+extern void lwlock_reset_stat_local(LWLockId);
+
+extern uint64 lwlock_get_stat_calls_global(LWLockId);
+extern uint64 lwlock_get_stat_waits_global(LWLockId);
+extern uint64 lwlock_get_stat_time_ms_global(LWLockId);
+extern void lwlock_reset_stat_global(LWLockId);
+
 #endif   /* LWLOCK_H */