Hi,

2012/10/13 23:05, Satoshi Nagayasu wrote:
> Hi all,
> 
> I have fixed my previous patch for pg_stat_lwlocks view, and
> as Josh commented, it now supports local and global (shared)
> statistics in the same system view.

Sorry, I found my mistakes. New fixed one is attached to this mail.

Regards,

> 
> Local statistics means the counters are only effective in the
> same session, and shared ones means the counters are shared within
> the entire cluster.
> 
> Also the global statistics would be collected via pgstat collector
> process like other statistics do.
> 
> Now, the global statistics struct has been splitted into two parts
> for different use, for bgwriter stats and lwlock stats.
> 
> Therefore, calling pg_stat_reset_shared('bgwriter') or
> pg_stat_reset_shared('lwlocks') would reset dedicated struct,
> not entire PgStat_GlobalStats.
> 
> Comments and review are always welcome.
> 
> Regards,
> 
> ------------------------------------------------------------------------------
> postgres=# SELECT * FROM pg_stat_lwlocks;
>   lwlockid | local_calls | local_waits | local_time_ms | shared_calls |
> shared_waits | shared_time_ms
> ----------+-------------+-------------+---------------+--------------+--------------+----------------
>          0 |           0 |           0 |             0 |         4268 |
>            0 |              0
>          1 |          43 |           0 |             0 |          387 |
>            0 |              0
>          2 |           0 |           0 |             0 |           19 |
>            0 |              0
>          3 |           0 |           0 |             0 |           28 |
>            0 |              0
>          4 |           3 |           0 |             0 |          315 |
>            0 |              0
>          5 |           0 |           0 |             0 |           24 |
>            0 |              0
>          6 |           1 |           0 |             0 |           76 |
>            0 |              0
>          7 |           0 |           0 |             0 |        16919 |
>            0 |              0
>          8 |           0 |           0 |             0 |            0 |
>            0 |              0
>          9 |           0 |           0 |             0 |            0 |
>            0 |              0
>         10 |           0 |           0 |             0 |            0 |
>            0 |              0
>         11 |           0 |           0 |             0 |           75 |
>            0 |              0
>         12 |           0 |           0 |             0 |            0 |
>            0 |              0
>         13 |           0 |           0 |             0 |            0 |
>            0 |              0
>         14 |           0 |           0 |             0 |            0 |
>            0 |              0
>         15 |           0 |           0 |             0 |            0 |
>            0 |              0
>         16 |           0 |           0 |             0 |            0 |
>            0 |              0
>         17 |           0 |           0 |             0 |        61451 |
>            6 |              0
>         18 |           0 |           0 |             0 |            0 |
>            0 |              0
>         19 |           0 |           0 |             0 |            0 |
>            0 |              0
>         20 |           0 |           0 |             0 |            0 |
>            0 |              0
>         21 |           1 |           0 |             0 |            9 |
>            0 |              0
>         22 |           0 |           0 |             0 |            0 |
>            0 |              0
>         23 |           0 |           0 |             0 |            0 |
>            0 |              0
>         24 |           0 |           0 |             0 |            1 |
>            0 |              0
>         25 |           0 |           0 |             0 |            0 |
>            0 |              0
>         26 |           2 |           0 |             0 |           18 |
>            0 |              0
>         27 |           0 |           0 |             0 |            0 |
>            0 |              0
>         28 |           0 |           0 |             0 |            0 |
>            0 |              0
>         29 |           0 |           0 |             0 |            0 |
>            0 |              0
>         30 |           0 |           0 |             0 |            0 |
>            0 |              0
>         31 |           0 |           0 |             0 |            0 |
>            0 |              0
>         32 |           0 |           0 |             0 |            0 |
>            0 |              0
>         33 |           4 |           0 |             0 |       207953 |
>            0 |              0
>         50 |           8 |           0 |             0 |        33388 |
>            0 |              0
>         67 |           0 |           0 |             0 |            0 |
>            0 |              0
> (36 rows)
> 
> postgres=#
> ------------------------------------------------------------------------------
> 
> 
> 2012/06/26 21:11, Satoshi Nagayasu wrote:
>> Hi all,
>>
>> I've modified the pg_stat_lwlocks patch to be able to work with
>> the latest PostgreSQL Git code.
>>
>> This patch provides:
>>     pg_stat_lwlocks           New system view to show lwlock statistics.
>>     pg_stat_get_lwlocks()     New function to retrieve lwlock statistics.
>>     pg_stat_reset_lwlocks()   New function to reset lwlock statistics.
>>
>> Please try it out.
>>
>> Regards,
>>
>> 2012/06/26 5:29, Satoshi Nagayasu wrote:
>>> Hi all,
>>>
>>> I've been working on a new system view, pg_stat_lwlocks, to observe
>>> LWLock, and just completed my 'proof-of-concept' code that can work
>>> with version 9.1.
>>>
>>> Now, I'd like to know the possibility of this feature for future
>>> release.
>>>
>>> With this patch, DBA can easily determine a bottleneck around lwlocks.
>>> --------------------------------------------------
>>> postgres=# SELECT * FROM pg_stat_lwlocks ORDER BY time_ms DESC LIMIT 10;
>>>     lwlockid | calls  | waits | time_ms
>>> ----------+--------+-------+---------
>>>           49 | 193326 | 32096 |   23688
>>>            8 |   3305 |   133 |    1335
>>>            2 |     21 |     0 |       0
>>>            4 | 135188 |     0 |       0
>>>            5 |  57935 |     0 |       0
>>>            6 |    141 |     0 |       0
>>>            7 |  24580 |     1 |       0
>>>            3 |   3282 |     0 |       0
>>>            1 |     41 |     0 |       0
>>>            9 |      3 |     0 |       0
>>> (10 rows)
>>>
>>> postgres=#
>>> --------------------------------------------------
>>>
>>> In this view,
>>>      'lwlockid' column represents LWLockId used in the backends.
>>>      'calls' represents how many times LWLockAcquire() was called.
>>>      'waits' represents how many times LWLockAcquire() needed to wait
>>>      within it before lock acquisition.
>>>      'time_ms' represents how long LWLockAcquire() totally waited on
>>>      a lwlock.
>>>
>>> And lwlocks that use a LWLockId range, such as BufMappingLock or
>>> LockMgrLock, would be grouped and summed up in a single record.
>>> For example, lwlockid 49 in the above view represents LockMgrLock
>>> statistics.
>>>
>>> Now, I know there are some considerations.
>>>
>>> (1) Performance
>>>
>>>      I've measured LWLock performance both with and without the patch,
>>>      and confirmed that this patch does not affect the LWLock perfomance
>>>      at all.
>>>
>>>      pgbench scores with the patch:
>>>        tps = 900.906658 (excluding connections establishing)
>>>        tps = 908.528422 (excluding connections establishing)
>>>        tps = 903.900977 (excluding connections establishing)
>>>        tps = 910.470595 (excluding connections establishing)
>>>        tps = 909.685396 (excluding connections establishing)
>>>
>>>      pgbench scores without the patch:
>>>        tps = 909.096785 (excluding connections establishing)
>>>        tps = 894.868712 (excluding connections establishing)
>>>        tps = 910.074669 (excluding connections establishing)
>>>        tps = 904.022770 (excluding connections establishing)
>>>        tps = 895.673830 (excluding connections establishing)
>>>
>>>      Of course, this experiment was not I/O bound, and the cache hit ratio
>>>      was>99.9%.
>>>
>>> (2) Memory space
>>>
>>>      In this patch, I added three new members to LWLock structure
>>>      as uint64 to collect statistics.
>>>
>>>      It means that those members must be held in the shared memory,
>>>      but I'm not sure whether it's appropriate.
>>>
>>>      I think another possible option is holding those statistics
>>>      values in local (backend) process memory, and send them through
>>>      the stat collector process (like other statistics values).
>>>
>>> (3) LWLock names (or labels)
>>>
>>>      Now, pg_stat_lwlocks view shows LWLockId itself. But LWLockId is
>>>      not easy for DBA to determine actual lock type.
>>>
>>>      So, I want to show LWLock names (or labels), like 'WALWriteLock'
>>>      or 'LockMgrLock', but how should I implement it?
>>>
>>> Any comments?
>>>
>>> Regards,
>>
>>
> 
> 


-- 
Satoshi Nagayasu <sn...@uptime.jp>
Uptime Technologies, LLC. http://www.uptime.jp
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 */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to