Tracking wait event for latches

Started by Michael Paquierover 9 years ago58 messages
#1Michael Paquier
michael.paquier@gmail.com
1 attachment(s)

Hi all,

As I mentioned $subject a couple of months back after looking at the
wait event facility here:
/messages/by-id/CAB7nPqTJpgAvOK4qSC96Fpm5W+aCtJ9D=3Vn9AfiEYsur=-juw@mail.gmail.com
I have actually taken some time to implement this idea.

The particular case that I had in mind was to be able to track in
pg_stat_activity processes that are waiting on a latch for synchronous
replication, and here is what this patch gives in this case:
=# alter system set synchronous_standby_names = 'foo';
ALTER SYSTEM
=# select pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
=# -- Do something
[...]

And from another session:
=# select wait_event_type, wait_event from pg_stat_activity where pid = 83316;
wait_event_type | wait_event
-----------------+------------
Latch | SyncRep
(1 row)

This is a boring patch, and it relies on the wait event facility that
has been added recently in 9.6. Note a couple of things though:
1) There is something like 30 code paths calling WaitLatch in the
backend code, all those events are classified and documented similarly
to LWLock events.
2) After discussing this stuff while at PGCon, it does not seem worth
to have any kind of APIs to be able to add in shared memory custom
latch names that extensions could load through _PG_init(). In
replacement to that, there is a latch type flag called "Extension"
that can be used for this purpose.
Comments are welcome, I am adding that in the 9.7 queue.

Regards,
--
Michael

Attachments:

wait-event-latch.patchinvalid/octet-stream; name=wait-event-latch.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 43c7fc9..9f4efc3 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -496,7 +496,7 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L, LATCH_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 2b2f7e3..f7f3a59 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,16 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>Latch</>: The server process is waiting for a latch.
+          Latch is an inter-process facility using boolean variables letting
+          a process sleep until it is set. Latches support several type of
+          operations, like postmaster death handling, timeout and socket
+          activity lookup, and are a useful replacement for <function>sleep</>
+          where signal handling matters.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1081,6 +1091,131 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="30"><literal>Latch</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in the code path of an extension, should be used by custom plugins and modules</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to put new message in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>PGSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>PGStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSleep</></entry>
+         <entry>Waiting for a specific lock.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSignal</></entry>
+         <entry>Waiting for signal from another backend.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWALStream</></entry>
+         <entry>Waiting for WAL from a stram at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWALAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for WAL commit during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WALWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
+        <row>
+         <entry><literal>WALReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WALReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WALSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WALSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WALSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index 934dba8..13805da 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -534,7 +534,8 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  LATCH_PARALLEL_WAIT_FINISH);
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b473f19..a1afff9 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5758,7 +5758,8 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  LATCH_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -11296,7 +11297,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+							 wait_time, LATCH_RECOVERY_WAL_STREAM);
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11459,7 +11460,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L, LATCH_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 3834ed6..98fa579 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -381,7 +381,7 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, LATCH_EXECUTE_GATHER);
 			CHECK_FOR_INTERRUPTS();
 			ResetLatch(MyLatch);
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 95cceee..257c013 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -418,7 +418,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+								  LATCH_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index 350210b..fa0cad1 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -171,7 +171,7 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0, LATCH_MQ_PUT_MESSAGE);
 		CHECK_FOR_INTERRUPTS();
 		ResetLatch(&MyProc->procLatch);
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 6bdaac5..218f53b 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -598,7 +598,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   LATCH_AUTOVACUUM_MAIN);
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 382edad..a605225 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -969,7 +969,8 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   LATCH_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1009,8 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			return status;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   LATCH_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 			return BGWH_POSTMASTER_DIED;
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 00f03d8..c3b5427 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -347,7 +347,8 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */,
+					   LATCH_BGWRITER_MAIN);
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -374,7 +375,8 @@ BackgroundWriterMain(void)
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
 						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+						   BgWriterDelay * HIBERNATE_FACTOR,
+						   LATCH_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 8d4b353..f617f86 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -558,7 +558,8 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   LATCH_CHECKPOINTER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..3815a3c 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,7 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							 timeout * 1000L, LATCH_ARCHIVER_MAIN);
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 41f4374..bd9a58b 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3150,6 +3150,9 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_LATCH:
+			event_type = "Latch";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3191,6 +3194,9 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_LATCH:
+			event_name = GetLatchIdentifier(eventId);
+			break;
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3681,7 +3687,7 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
 							   pgStatSock,
-							   -1L);
+							   -1L, LATCH_PGSTAT_MAIN);
 #else
 
 		/*
@@ -3697,7 +3703,8 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 		WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
 							   pgStatSock,
-							   2 * 1000L /* msec */ );
+							   2 * 1000L /* msec */,
+							   LATCH_PGSTAT_MAIN);
 #endif
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..2aedd32 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -424,7 +424,8 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+							   LATCH_SYSLOGGER_MAIN);
 
 		if (rc & WL_SOCKET_READABLE)
 		{
@@ -475,7 +476,8 @@ SysLoggerMain(int argc, char *argv[])
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | cur_flags,
-						 cur_timeout);
+						 cur_timeout,
+						 LATCH_SYSLOGGER_MAIN);
 
 		EnterCriticalSection(&sysloggerSection);
 #endif   /* WIN32 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 228190a..4b0a074 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -292,7 +292,8 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   LATCH_WALWRITER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index 1008873..d7dfc27 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1286,7 +1286,8 @@ throttle(size_t increment)
 		 */
 		wait_result = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+								(long) (sleep / 1000),
+								LATCH_BASEBACKUP_THROTTLE);
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 959ca78..aab6a7b 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -265,7 +265,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  LATCH_SYNC_REP);
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 6fd5952..b5ac1bb 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -466,7 +466,8 @@ WalReceiverMain(void)
 									   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   LATCH_WAL_RECEIVER_MAIN);
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -665,7 +666,8 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  LATCH_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 926a247..8094b3ce 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,8 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  LATCH_WAL_SENDER_WRITE_DATA);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1273,8 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  LATCH_WAL_SENDER_WAIT_WAL);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1923,7 +1925,8 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  LATCH_WAL_SENDER_LOOP);
 		}
 	}
 	return;
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 3fbe0e5..434a0fd 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -297,9 +298,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  LatchIdentifier eventId)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 eventId);
 }
 
 /*
@@ -316,13 +319,15 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, LatchIdentifier eventId)
 {
 	int			ret = 0;
 	int			rc;
 	WaitEvent	event;
 	WaitEventSet *set = CreateWaitEventSet(CurrentMemoryContext, 3);
 
+	pgstat_report_wait_start(WAIT_LATCH, (uint16) eventId);
+
 	if (wakeEvents & WL_TIMEOUT)
 		Assert(timeout >= 0);
 	else
@@ -356,6 +361,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 							   WL_SOCKET_WRITEABLE);
 	}
 
+	pgstat_report_wait_end();
 	FreeWaitEventSet(set);
 
 	return ret;
@@ -1485,6 +1491,119 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 }
 #endif
 
+
+/*
+ * Return an identifier for a Latch depending on an event for statistics
+ * collector.
+ */
+const char *
+GetLatchIdentifier(uint16 eventId)
+{
+	const char *res;
+
+	switch (eventId)
+	{
+		case LATCH_ARCHIVER_MAIN:
+			res = "ArchiverMain";
+			break;
+		case LATCH_AUTOVACUUM_MAIN:
+			res = "AutoVacuumMain";
+			break;
+		case LATCH_BASEBACKUP_THROTTLE:
+			res = "BaseBackupThrottle";
+			break;
+		case LATCH_BGWORKER_STARTUP:
+			res = "BgWorkerStartup";
+			break;
+		case LATCH_BGWORKER_SHUTDOWN:
+			res = "BgWorkerShutdown";
+			break;
+		case LATCH_BGWRITER_MAIN:
+			res = "BgWriterMain";
+			break;
+		case LATCH_BGWRITER_HIBERNATE:
+			res = "BgWriterHibernate";
+			break;
+		case LATCH_CHECKPOINTER_MAIN:
+			res = "CheckpointerMain";
+			break;
+		case LATCH_EXECUTE_GATHER:
+			res = "ExecuteGather";
+			break;
+		case LATCH_EXTENSION:
+			res = "Extension";
+			break;
+		case LATCH_MQ_PUT_MESSAGE:
+			res = "MessageQueuePutMessage";
+			break;
+		case LATCH_MQ_SEND_BYTES:
+			res = "MessageQueueSend";
+			break;
+		case LATCH_MQ_RECEIVE_BYTES:
+			res = "MessageQueueReceive";
+			break;
+		case LATCH_MQ_WAIT_INTERNAL:
+			res = "MessageQueueInternal";
+			break;
+		case LATCH_PARALLEL_WAIT_FINISH:
+			res = "ParallelFinish";
+			break;
+		case LATCH_PG_SLEEP:
+			res = "PGSleep";
+			break;
+		case LATCH_PGSTAT_MAIN:
+			res = "PGStatMain";
+			break;
+		case LATCH_PROC_SLEEP:
+			res = "ProcSleep";
+			break;
+		case LATCH_PROC_SIGNAL:
+			res = "ProcSignal";
+			break;
+		case LATCH_RECOVERY_APPLY_DELAY:
+			res = "XLogApplyDelay";
+			break;
+		case LATCH_RECOVERY_WAL_STREAM:
+			res = "RecoveryWALStream";
+			break;
+		case LATCH_RECOVERY_WAL_ALL:
+			res = "XLogAfterAvailable";
+			break;
+		case LATCH_SSL_OPEN_SERVER:
+			res = "SSLOpenServer";
+			break;
+		case LATCH_SYNC_REP:
+			res = "SyncRep";
+			break;
+		case LATCH_SYSLOGGER_MAIN:
+			res = "SysLoggerMain";
+			break;
+		case LATCH_WALWRITER_MAIN:
+			res = "WALWriterMain";
+			break;
+		case LATCH_WAL_RECEIVER_WAIT_START:
+			res = "WALSenderWaitStart";
+			break;
+		case LATCH_WAL_RECEIVER_MAIN:
+			res = "WALReceiverMain";
+			break;
+		case LATCH_WAL_SENDER_LOOP:
+			res = "WALSenderMain";
+			break;
+		case LATCH_WAL_SENDER_WAIT_WAL:
+			res = "WALSenderWaitForWAL";
+			break;
+		case LATCH_WAL_SENDER_WRITE_DATA:
+			res = "WALSenderWriteData";
+			break;
+		default:
+			res = "???";
+	}
+
+	return res;
+}
+
+
 /*
  * SetLatch uses SIGUSR1 to wake up the process waiting on the latch.
  *
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 7d1c9cd..a9542c2 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -866,7 +866,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, LATCH_MQ_SEND_BYTES);
 
 			/* An interrupt may have occurred while we were waiting. */
 			CHECK_FOR_INTERRUPTS();
@@ -963,7 +963,7 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, LATCH_MQ_RECEIVE_BYTES);
 
 		/* An interrupt may have occurred while we were waiting. */
 		CHECK_FOR_INTERRUPTS();
@@ -1062,7 +1062,7 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, LATCH_MQ_WAIT_INTERNAL);
 
 		/* An interrupt may have occurred while we were waiting. */
 		CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index a66e07b..8f910d74 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -1216,7 +1216,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, LATCH_PROC_SLEEP);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1728,7 +1728,7 @@ CheckDeadLockAlert(void)
 void
 ProcWaitForSignal(void)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, LATCH_PROC_SIGNAL);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index a44fa38..36de861 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -560,7 +560,7 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms, LATCH_PG_SLEEP);
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 4be09fe..80db5ef 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -707,7 +707,8 @@ typedef enum WaitClass
 	WAIT_LWLOCK_NAMED,
 	WAIT_LWLOCK_TRANCHE,
 	WAIT_LOCK,
-	WAIT_BUFFER_PIN
+	WAIT_BUFFER_PIN,
+	WAIT_LATCH
 }	WaitClass;
 
 
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 85d211c..cf2c9e1 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -119,6 +119,45 @@ typedef struct WaitEvent
 	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
 } WaitEvent;
 
+/*
+ * List of latch identifiers used when reporting activity to statistics
+ * collector.
+ */
+typedef enum LatchIdentifier
+{
+	LATCH_ARCHIVER_MAIN,
+	LATCH_AUTOVACUUM_MAIN,
+	LATCH_BASEBACKUP_THROTTLE,
+	LATCH_BGWORKER_STARTUP,
+	LATCH_BGWORKER_SHUTDOWN,
+	LATCH_BGWRITER_MAIN,
+	LATCH_BGWRITER_HIBERNATE,
+	LATCH_CHECKPOINTER_MAIN,
+	LATCH_EXECUTE_GATHER,
+	LATCH_EXTENSION,
+	LATCH_MQ_PUT_MESSAGE,
+	LATCH_MQ_SEND_BYTES,
+	LATCH_MQ_RECEIVE_BYTES,
+	LATCH_MQ_WAIT_INTERNAL,
+	LATCH_PARALLEL_WAIT_FINISH,
+	LATCH_PGSTAT_MAIN,
+	LATCH_PROC_SLEEP,
+	LATCH_PROC_SIGNAL,
+	LATCH_PG_SLEEP,
+	LATCH_SSL_OPEN_SERVER,
+	LATCH_SYNC_REP,
+	LATCH_SYSLOGGER_MAIN,
+	LATCH_RECOVERY_APPLY_DELAY,
+	LATCH_RECOVERY_WAL_ALL,
+	LATCH_RECOVERY_WAL_STREAM,
+	LATCH_WAL_RECEIVER_WAIT_START,
+	LATCH_WAL_RECEIVER_MAIN,
+	LATCH_WAL_SENDER_WRITE_DATA,
+	LATCH_WAL_SENDER_LOOP,
+	LATCH_WAL_SENDER_WAIT_WAL,
+	LATCH_WALWRITER_MAIN,
+} LatchIdentifier;
+
 /* forward declaration to avoid exposing latch.c implementation details */
 typedef struct WaitEventSet WaitEventSet;
 
@@ -140,9 +179,11 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
 extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+				  LatchIdentifier eventId);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, LatchIdentifier eventId);
+extern const char *GetLatchIdentifier(uint16 eventId);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 5bd2820..2fd4e24 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -279,7 +279,7 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, LATCH_EXTENSION);
 
 		/* An interrupt may have occurred while we were waiting. */
 		CHECK_FOR_INTERRUPTS();
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index 6948e20..a89a52a 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -230,7 +230,7 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, LATCH_EXTENSION);
 			CHECK_FOR_INTERRUPTS();
 			ResetLatch(MyLatch);
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 314e371..af4549a 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -227,7 +227,8 @@ worker_spi_main(Datum main_arg)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   worker_spi_naptime * 1000L);
+					   worker_spi_naptime * 1000L,
+					   LATCH_EXTENSION);
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#2Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#1)
Re: Tracking wait event for latches

On Thu, May 19, 2016 at 4:14 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Comments are welcome, I am adding that in the 9.7 queue.

Take that as 10.0 as things are going.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Michael Paquier (#1)
Re: Tracking wait event for latches

On Fri, May 20, 2016 at 8:14 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Hi all,

As I mentioned $subject a couple of months back after looking at the
wait event facility here:
/messages/by-id/CAB7nPqTJpgAvOK4qSC96Fpm5W+aCtJ9D=3Vn9AfiEYsur=-juw@mail.gmail.com
I have actually taken some time to implement this idea.

The particular case that I had in mind was to be able to track in
pg_stat_activity processes that are waiting on a latch for synchronous
replication, and here is what this patch gives in this case:
=# alter system set synchronous_standby_names = 'foo';
ALTER SYSTEM
=# select pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
=# -- Do something
[...]

And from another session:
=# select wait_event_type, wait_event from pg_stat_activity where pid = 83316;
wait_event_type | wait_event
-----------------+------------
Latch | SyncRep
(1 row)

This is a boring patch, and it relies on the wait event facility that
has been added recently in 9.6. Note a couple of things though:
1) There is something like 30 code paths calling WaitLatch in the
backend code, all those events are classified and documented similarly
to LWLock events.
2) After discussing this stuff while at PGCon, it does not seem worth
to have any kind of APIs to be able to add in shared memory custom
latch names that extensions could load through _PG_init(). In
replacement to that, there is a latch type flag called "Extension"
that can be used for this purpose.
Comments are welcome, I am adding that in the 9.7 queue.

This is very cool, and I can't wait to have this feature! It'll be
useful for all kinds of developers and users. I wanted this today to
help debug something I am working on, and remembered this patch. I
have signed up as a reviewer for the September commitfest. But here
is some initial feedback based on a quick glance at it:

This patch allows identifiers to be specified by the WaitLatch and
WaitLatchOrSocket calls, but not for WaitEventSetWait, which is the
more general waiting primitive. I think it should be done by
WaitEventSetWait, and merely passed down by those wrappers functions.

These are actually names for *wait points*, not names for latches.
Some of the language in this patch makes it sound like they are latch
names/latch identifiers, which is inaccurate (the latches themselves
wouldn't be very interesting eg MyLatch). In some cases the main
thing of interest is actually a socket or timer anyway, not a latch,
so maybe it should appear as wait_event_type = WaitEventSet?

Is there a reason you chose names like 'WALWriterMain'? That
particular wait point is in the function WalWriterMain (note different
case). It seems odd to use the containing function names to guide
naming, but not use them exactly. Since this namespace needs to be
able to name wait points anywhere in the postgres source tree (and
maybe outside it too, for extensions), maybe it should be made
hierarchical, like 'walwriter.WalWriterMain' (or some other
organisational scheme).

I personally think it would be very useful for extensions to be able
to name their wait points. For example, I'd rather see
'postgres_fdw.pgfdw_get_result' or similar than a vague 'Extension'
string which appears not only for all wait points in an extension but
also for all extensions. I hope we can figure out a good way to do
that. Clearly that would involve some shmem registry machinery to
make the names visible across backends (a similar problem exists with
lock tranche names).

--
Thomas Munro
http://www.enterprisedb.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Michael Paquier
michael.paquier@gmail.com
In reply to: Thomas Munro (#3)
Re: Tracking wait event for latches

On Thu, Jun 2, 2016 at 12:25 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

This patch allows identifiers to be specified by the WaitLatch and
WaitLatchOrSocket calls, but not for WaitEventSetWait, which is the
more general waiting primitive. I think it should be done by
WaitEventSetWait, and merely passed down by those wrappers functions.

The advantage of having WaitEventSetWait track is that we could track
the events of secure_read and secure_write. One potential problem by
doing so is if those routines are called during authentication. I
don't recall that's the case, but this needs a double-check.

These are actually names for *wait points*, not names for latches.

OK.

Some of the language in this patch makes it sound like they are latch
names/latch identifiers, which is inaccurate (the latches themselves
wouldn't be very interesting eg MyLatch). In some cases the main
thing of interest is actually a socket or timer anyway, not a latch,
so maybe it should appear as wait_event_type = WaitEventSet?

Hm. A latch could wait for multiple types things it is waiting for, so
don't you think we'd need to add more details in what is reported to
pg_stat_activity? There are two fields now, and in the context of this
patch:
- wait_event_type, which I'd like to think is associated to a latch,
so I named it so.
- wait_event, which is the code path that we are waiting at, like
SyncRep, the WAL writer main routine, etc.

Now if you would like to get a list of all the things that are being
waited for, shouldn't we add a third column to the set that has text[]
as return type? Let's name it wait_event_details, and for a latch we
have the following:
- WL_LATCH_SET
- WL_POSTMASTER_DEATH
- WL_SOCKET_READABLE
- WL_SOCKET_WRITEABLE
Compared to all the other existing wait types, that's a bit new and
that's exclusive to latches because we need a higher level of details.
Don't you think so? But actually I don't think that's necessary to go
into this level of details. We already know what a latch is waiting
for by looking at the code...

Is there a reason you chose names like 'WALWriterMain'? That
particular wait point is in the function WalWriterMain (note different
case). It seems odd to use the containing function names to guide
naming, but not use them exactly. Since this namespace needs to be
able to name wait points anywhere in the postgres source tree (and
maybe outside it too, for extensions), maybe it should be made
hierarchical, like 'walwriter.WalWriterMain' (or some other
organisational scheme).

Yeah, possibly this could be improved. I have put some thoughts in
having clear names for each one of them, so I'd rather keep them
simple.

I personally think it would be very useful for extensions to be able
to name their wait points. For example, I'd rather see
'postgres_fdw.pgfdw_get_result' or similar than a vague 'Extension'
string which appears not only for all wait points in an extension but
also for all extensions. I hope we can figure out a good way to do
that. Clearly that would involve some shmem registry machinery to
make the names visible across backends (a similar problem exists with
lock tranche names).

This patch is shaped this way intentionally based on the feedback I
received at PGCon (Robert and others). We could provide a routine that
extensions call in _PG_init to register a new latch event name in
shared memory, but I didn't see much use in doing so, take for example
the case of background worker, it is possible to register a custom
string for pg_stat_activity via pgstat_report_activity. One could take
advantage of having custom latch wait names in shared memory if an
extension has wait points with latches though... But I am still not
sure if that's worth the complexity.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#4)
Re: Tracking wait event for latches

On Thu, Jun 2, 2016 at 1:34 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

This patch is shaped this way intentionally based on the feedback I
received at PGCon (Robert and others). We could provide a routine that
extensions call in _PG_init to register a new latch event name in
shared memory, but I didn't see much use in doing so, take for example
the case of background worker, it is possible to register a custom
string for pg_stat_activity via pgstat_report_activity. One could take
advantage of having custom latch wait names in shared memory if an
extension has wait points with latches though... But I am still not
sure if that's worth the complexity.

I can't see how you could ever guarantee that it wouldn't just fail.
We allocate a certain amount of "slop" in the main shared memory
segment, but it's not infinite and can certainly be exhausted. It
seems like it would suck if you tried to load your extension and it
failed because there was no room left for more wait-point names.
Maybe it would suck less than not having wait-point names, but I'm not
really sure. I think we'd do better to get something that handles the
core stuff well and then consider extensions later or not at all. It
only matters if you're running multiple extensions that all use LWLock
tranches and you need to distinguish between waits on their various
LWLocks. But since LWLock contention is something we hope will be
infrequent I'm just not sure that case is common enough to justify
building a lot of mechanism.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#5)
Re: Tracking wait event for latches

On Sat, Jun 4, 2016 at 2:41 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Jun 2, 2016 at 1:34 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

This patch is shaped this way intentionally based on the feedback I
received at PGCon (Robert and others). We could provide a routine that
extensions call in _PG_init to register a new latch event name in
shared memory, but I didn't see much use in doing so, take for example
the case of background worker, it is possible to register a custom
string for pg_stat_activity via pgstat_report_activity. One could take
advantage of having custom latch wait names in shared memory if an
extension has wait points with latches though... But I am still not
sure if that's worth the complexity.

I can't see how you could ever guarantee that it wouldn't just fail.
We allocate a certain amount of "slop" in the main shared memory
segment, but it's not infinite and can certainly be exhausted. It
seems like it would suck if you tried to load your extension and it
failed because there was no room left for more wait-point names.
Maybe it would suck less than not having wait-point names, but I'm not
really sure. I think we'd do better to get something that handles the
core stuff well and then consider extensions later or not at all.

Yeah, that's as well my line of thoughts on the matter since the
beginning: keep it simple and done. What is written just after those
words is purely hand-waving and I have no way to prove it, but my
instinctive guess is that more than 90% of the real use cases where we
need to track the latch waits in pgstat would be covered without the
need of this extra shared memory infrastructure for extensions.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#6)
1 attachment(s)
Re: Tracking wait event for latches

On Wed, Jun 8, 2016 at 10:11 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Yeah, that's as well my line of thoughts on the matter since the
beginning: keep it simple and done. What is written just after those
words is purely hand-waving and I have no way to prove it, but my
instinctive guess is that more than 90% of the real use cases where we
need to track the latch waits in pgstat would be covered without the
need of this extra shared memory infrastructure for extensions.

So, I have done some extra tests with my patch to see if I move the
event reporting down to WaitEventSetWait and see what is the effect on
secure_read and secure_write. And the conclusion is that I am seeing
no difference, so I changed the patch to the way suggested by Thomas.
In this test, what I have done was using the following pgbench script
with -C via an SSL connection:
\set id random(1,1000)
As the script file is not empty, a connection to the server is opened,
so this goes though secure_read at minimal cost on the backend.

Also, I have change the event ID notation as follows to be consistent
with the routine names:
WAL -> Wal
PG -> Pg
I also found that LATCH_RECOVERY_WAL_ALL was reporting
"XLogAfterAvailable" as name, which was incorrect.

Finally, I have changed the patch so as it does not track "Latch"
events, but "EventSet" events instead, per the suggestion of Thomas.
"WaitEventSet" is too close to wait_event in my taste so I shortened
the suggeston. There were also some conflicts caused by the recent
commit 887feefe, which are fixed.

Attached is an updated patch.
--
Michael

Attachments:

wait-event-set-v2.patchapplication/x-download; name=wait-event-set-v2.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..954a166 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -496,7 +496,7 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L, EVENT_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 1c283a0..15e58a6 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,16 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>EventSet</>: The server process is waiting on a socket
+          or a timer. This wait happens in a latch, an inter-process facility
+          using boolean variables letting a process sleep until it is set.
+          Latches support several type of operations, like postmaster death
+          handling, timeout and socket activity lookup, and are a useful
+          replacement for <function>sleep</> where signal handling matters.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1081,6 +1091,139 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="30"><literal>EventSet</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in the code path of an extension, should be used by custom plugins and modules</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to put new message in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSleep</></entry>
+         <entry>Waiting for a specific lock.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSignal</></entry>
+         <entry>Waiting for signal from another backend.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stram at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for WAL commit during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index a303fca..88b659b 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -541,7 +541,8 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  EVENT_PARALLEL_WAIT_FINISH);
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f13f9c1..19d9977 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5756,7 +5756,8 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  EVENT_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -11294,7 +11295,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+							 wait_time, EVENT_RECOVERY_WAL_STREAM);
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11457,7 +11458,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L, EVENT_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..d8fe2a5 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -387,7 +387,7 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_EXECUTE_GATHER);
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index f6adb15..759b337 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -418,7 +418,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+								  EVENT_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..5880818 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -146,7 +146,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 EVENT_SECURE_READ);
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +248,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 EVENT_SECURE_WRITE);
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index 921242f..6c39e51 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -171,7 +171,7 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0, EVENT_MQ_PUT_MESSAGE);
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 3768f50..bb80c11 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -600,7 +600,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   EVENT_AUTOVACUUM_MAIN);
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 382edad..1d41b00 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -969,7 +969,8 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   EVENT_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1009,8 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			return status;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   EVENT_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 			return BGWH_POSTMASTER_DIED;
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 00f03d8..1c135de 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -347,7 +347,8 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */,
+					   EVENT_BGWRITER_MAIN);
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -374,7 +375,8 @@ BackgroundWriterMain(void)
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
 						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+						   BgWriterDelay * HIBERNATE_FACTOR,
+						   EVENT_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 8d4b353..60bab87 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -558,7 +558,8 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   EVENT_CHECKPOINTER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..156fff7 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,7 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							 timeout * 1000L, EVENT_ARCHIVER_MAIN);
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 8fa9edb..9dd49ec 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3152,6 +3152,9 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_EVENT_SET:
+			event_type = "EventSet";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3193,6 +3196,9 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_EVENT_SET:
+			event_name = GetEventIdentifier(eventId);
+			break;
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3682,7 +3688,7 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
 							   pgStatSock,
-							   -1L);
+							   -1L, EVENT_PGSTAT_MAIN);
 #else
 
 		/*
@@ -3698,7 +3704,8 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 		WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
 							   pgStatSock,
-							   2 * 1000L /* msec */ );
+							   2 * 1000L /* msec */,
+							   EVENT_PGSTAT_MAIN);
 #endif
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..c147fd1 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -424,7 +424,8 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+							   EVENT_SYSLOGGER_MAIN);
 
 		if (rc & WL_SOCKET_READABLE)
 		{
@@ -475,7 +476,8 @@ SysLoggerMain(int argc, char *argv[])
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | cur_flags,
-						 cur_timeout);
+						 cur_timeout,
+						 EVENT_SYSLOGGER_MAIN);
 
 		EnterCriticalSection(&sysloggerSection);
 #endif   /* WIN32 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 228190a..781e7b0 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -292,7 +292,8 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   EVENT_WALWRITER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index da9b7a6..308220f 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1286,7 +1286,8 @@ throttle(size_t increment)
 		 */
 		wait_result = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+								(long) (sleep / 1000),
+								EVENT_BASEBACKUP_THROTTLE);
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 67249d8..9b997c8 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -265,7 +265,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  EVENT_SYNC_REP);
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..dfd7d37 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -486,7 +486,8 @@ WalReceiverMain(void)
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   EVENT_WAL_RECEIVER_MAIN);
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +686,8 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  EVENT_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a0dba19..b612395 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,8 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  EVENT_WAL_SENDER_WRITE_DATA);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1273,8 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  EVENT_WAL_SENDER_WAIT_WAL);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1923,7 +1925,8 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  EVENT_WAL_SENDER_LOOP);
 		}
 	}
 	return;
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..435f183 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -297,9 +298,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  EventIdentifier eventId)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 eventId);
 }
 
 /*
@@ -316,7 +319,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, EventIdentifier eventId)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +347,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, eventId);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -863,7 +866,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 EventIdentifier eventId)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -872,6 +876,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 
 	Assert(nevents > 0);
 
+	pgstat_report_wait_start(WAIT_EVENT_SET, (uint16) eventId);
+
 	/*
 	 * Initialize timeout if requested.  We must record the current time so
 	 * that we can determine the remaining timeout if interrupted.
@@ -960,6 +966,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
@@ -1491,6 +1499,125 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 }
 #endif
 
+
+/*
+ * Return an identifier for a Latch depending on an event for statistics
+ * collector.
+ */
+const char *
+GetEventIdentifier(uint16 eventId)
+{
+	const char *res;
+
+	switch (eventId)
+	{
+		case EVENT_ARCHIVER_MAIN:
+			res = "ArchiverMain";
+			break;
+		case EVENT_AUTOVACUUM_MAIN:
+			res = "AutoVacuumMain";
+			break;
+		case EVENT_BASEBACKUP_THROTTLE:
+			res = "BaseBackupThrottle";
+			break;
+		case EVENT_BGWORKER_STARTUP:
+			res = "BgWorkerStartup";
+			break;
+		case EVENT_BGWORKER_SHUTDOWN:
+			res = "BgWorkerShutdown";
+			break;
+		case EVENT_BGWRITER_MAIN:
+			res = "BgWriterMain";
+			break;
+		case EVENT_BGWRITER_HIBERNATE:
+			res = "BgWriterHibernate";
+			break;
+		case EVENT_CHECKPOINTER_MAIN:
+			res = "CheckpointerMain";
+			break;
+		case EVENT_EXECUTE_GATHER:
+			res = "ExecuteGather";
+			break;
+		case EVENT_EXTENSION:
+			res = "Extension";
+			break;
+		case EVENT_MQ_PUT_MESSAGE:
+			res = "MessageQueuePutMessage";
+			break;
+		case EVENT_MQ_SEND_BYTES:
+			res = "MessageQueueSend";
+			break;
+		case EVENT_MQ_RECEIVE_BYTES:
+			res = "MessageQueueReceive";
+			break;
+		case EVENT_MQ_WAIT_INTERNAL:
+			res = "MessageQueueInternal";
+			break;
+		case EVENT_PARALLEL_WAIT_FINISH:
+			res = "ParallelFinish";
+			break;
+		case EVENT_PG_SLEEP:
+			res = "PgSleep";
+			break;
+		case EVENT_PGSTAT_MAIN:
+			res = "PgStatMain";
+			break;
+		case EVENT_PROC_SLEEP:
+			res = "ProcSleep";
+			break;
+		case EVENT_PROC_SIGNAL:
+			res = "ProcSignal";
+			break;
+		case EVENT_RECOVERY_APPLY_DELAY:
+			res = "RecoveryApplyDelay";
+			break;
+		case EVENT_RECOVERY_WAL_STREAM:
+			res = "RecoveryWalStream";
+			break;
+		case EVENT_RECOVERY_WAL_ALL:
+			res = "RecoveryWalAll";
+			break;
+		case EVENT_SECURE_READ:
+			res = "SecureRead";
+			break;
+		case EVENT_SECURE_WRITE:
+			res = "SecureWrite";
+			break;
+		case EVENT_SSL_OPEN_SERVER:
+			res = "SSLOpenServer";
+			break;
+		case EVENT_SYNC_REP:
+			res = "SyncRep";
+			break;
+		case EVENT_SYSLOGGER_MAIN:
+			res = "SysLoggerMain";
+			break;
+		case EVENT_WALWRITER_MAIN:
+			res = "WalWriterMain";
+			break;
+		case EVENT_WAL_RECEIVER_WAIT_START:
+			res = "WalSenderWaitStart";
+			break;
+		case EVENT_WAL_RECEIVER_MAIN:
+			res = "WalReceiverMain";
+			break;
+		case EVENT_WAL_SENDER_LOOP:
+			res = "WalSenderMain";
+			break;
+		case EVENT_WAL_SENDER_WAIT_WAL:
+			res = "WalSenderWaitForWAL";
+			break;
+		case EVENT_WAL_SENDER_WRITE_DATA:
+			res = "WalSenderWriteData";
+			break;
+		default:
+			res = "???";
+	}
+
+	return res;
+}
+
+
 /*
  * SetLatch uses SIGUSR1 to wake up the process waiting on the latch.
  *
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..af5344c 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -894,7 +894,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_MQ_SEND_BYTES);
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +991,7 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_MQ_RECEIVE_BYTES);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1090,7 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_MQ_WAIT_INTERNAL);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 9a758bd..2ed3f41 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -1216,7 +1216,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_PROC_SLEEP);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1728,7 +1728,7 @@ CheckDeadLockAlert(void)
 void
 ProcWaitForSignal(void)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_PROC_SIGNAL);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..ea88c87 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -560,7 +560,7 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms, EVENT_PG_SLEEP);
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index dc3320d..bca16e4 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -721,7 +721,8 @@ typedef enum WaitClass
 	WAIT_LWLOCK_NAMED,
 	WAIT_LWLOCK_TRANCHE,
 	WAIT_LOCK,
-	WAIT_BUFFER_PIN
+	WAIT_BUFFER_PIN,
+	WAIT_EVENT_SET
 }	WaitClass;
 
 
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..41dc553 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -135,6 +135,47 @@ typedef struct WaitEvent
 	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
 } WaitEvent;
 
+/*
+ * List of latch identifiers used when reporting activity to statistics
+ * collector.
+ */
+typedef enum EventIdentifier
+{
+	EVENT_ARCHIVER_MAIN,
+	EVENT_AUTOVACUUM_MAIN,
+	EVENT_BASEBACKUP_THROTTLE,
+	EVENT_BGWORKER_STARTUP,
+	EVENT_BGWORKER_SHUTDOWN,
+	EVENT_BGWRITER_MAIN,
+	EVENT_BGWRITER_HIBERNATE,
+	EVENT_CHECKPOINTER_MAIN,
+	EVENT_EXECUTE_GATHER,
+	EVENT_EXTENSION,
+	EVENT_MQ_PUT_MESSAGE,
+	EVENT_MQ_SEND_BYTES,
+	EVENT_MQ_RECEIVE_BYTES,
+	EVENT_MQ_WAIT_INTERNAL,
+	EVENT_PARALLEL_WAIT_FINISH,
+	EVENT_PGSTAT_MAIN,
+	EVENT_PROC_SLEEP,
+	EVENT_PROC_SIGNAL,
+	EVENT_PG_SLEEP,
+	EVENT_SECURE_READ,
+	EVENT_SECURE_WRITE,
+	EVENT_SSL_OPEN_SERVER,
+	EVENT_SYNC_REP,
+	EVENT_SYSLOGGER_MAIN,
+	EVENT_RECOVERY_APPLY_DELAY,
+	EVENT_RECOVERY_WAL_ALL,
+	EVENT_RECOVERY_WAL_STREAM,
+	EVENT_WAL_RECEIVER_WAIT_START,
+	EVENT_WAL_RECEIVER_MAIN,
+	EVENT_WAL_SENDER_WRITE_DATA,
+	EVENT_WAL_SENDER_LOOP,
+	EVENT_WAL_SENDER_WAIT_WAL,
+	EVENT_WALWRITER_MAIN
+} EventIdentifier;
+
 /* forward declaration to avoid exposing latch.c implementation details */
 typedef struct WaitEventSet WaitEventSet;
 
@@ -155,10 +196,14 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
+				  WaitEvent *occurred_events, int nevents,
+				  EventIdentifier eventId);
+extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+				  EventIdentifier eventId);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, EventIdentifier eventId);
+extern const char *GetEventIdentifier(uint16 eventId);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 143df4e..d32e42e 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -279,7 +279,7 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_EXTENSION);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index dd34bc7..caddf83 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -230,7 +230,7 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_EXTENSION);
 			ResetLatch(MyLatch);
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 314e371..bb0d7ff 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -227,7 +227,8 @@ worker_spi_main(Datum main_arg)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   worker_spi_naptime * 1000L);
+					   worker_spi_naptime * 1000L,
+					   EVENT_EXTENSION);
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#8Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#7)
1 attachment(s)
Re: Tracking wait event for latches

On Tue, Aug 2, 2016 at 10:31 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Attached is an updated patch.

Updated version for 2 minor issues:
1) s/stram/stream/
2) Docs used incorrect number
--
Michael

Attachments:

wait-event-set-v3.patchtext/x-diff; charset=US-ASCII; name=wait-event-set-v3.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..954a166 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -496,7 +496,7 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L, EVENT_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 0776428..a14abe4 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,16 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>EventSet</>: The server process is waiting on a socket
+          or a timer. This wait happens in a latch, an inter-process facility
+          using boolean variables letting a process sleep until it is set.
+          Latches support several type of operations, like postmaster death
+          handling, timeout and socket activity lookup, and are a useful
+          replacement for <function>sleep</> where signal handling matters.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1085,6 +1095,139 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="32"><literal>EventSet</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in the code path of an extension, should be used by custom plugins and modules</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to put new message in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSleep</></entry>
+         <entry>Waiting for a specific lock.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSignal</></entry>
+         <entry>Waiting for signal from another backend.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stream at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for WAL commit during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index a47eba6..2495e61 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -541,7 +541,8 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  EVENT_PARALLEL_WAIT_FINISH);
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f13f9c1..19d9977 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5756,7 +5756,8 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  EVENT_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -11294,7 +11295,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+							 wait_time, EVENT_RECOVERY_WAL_STREAM);
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11457,7 +11458,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L, EVENT_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..d8fe2a5 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -387,7 +387,7 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_EXECUTE_GATHER);
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index f6adb15..759b337 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -418,7 +418,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+								  EVENT_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..5880818 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -146,7 +146,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 EVENT_SECURE_READ);
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +248,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 EVENT_SECURE_WRITE);
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index 921242f..6c39e51 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -171,7 +171,7 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0, EVENT_MQ_PUT_MESSAGE);
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 3768f50..bb80c11 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -600,7 +600,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   EVENT_AUTOVACUUM_MAIN);
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 382edad..1d41b00 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -969,7 +969,8 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   EVENT_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1009,8 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			return status;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   EVENT_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 			return BGWH_POSTMASTER_DIED;
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 00f03d8..1c135de 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -347,7 +347,8 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */,
+					   EVENT_BGWRITER_MAIN);
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -374,7 +375,8 @@ BackgroundWriterMain(void)
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
 						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+						   BgWriterDelay * HIBERNATE_FACTOR,
+						   EVENT_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 8d4b353..60bab87 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -558,7 +558,8 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   EVENT_CHECKPOINTER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..156fff7 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,7 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							 timeout * 1000L, EVENT_ARCHIVER_MAIN);
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 8fa9edb..9dd49ec 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3152,6 +3152,9 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_EVENT_SET:
+			event_type = "EventSet";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3193,6 +3196,9 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_EVENT_SET:
+			event_name = GetEventIdentifier(eventId);
+			break;
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3682,7 +3688,7 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
 							   pgStatSock,
-							   -1L);
+							   -1L, EVENT_PGSTAT_MAIN);
 #else
 
 		/*
@@ -3698,7 +3704,8 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 		WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
 							   pgStatSock,
-							   2 * 1000L /* msec */ );
+							   2 * 1000L /* msec */,
+							   EVENT_PGSTAT_MAIN);
 #endif
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..c147fd1 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -424,7 +424,8 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+							   EVENT_SYSLOGGER_MAIN);
 
 		if (rc & WL_SOCKET_READABLE)
 		{
@@ -475,7 +476,8 @@ SysLoggerMain(int argc, char *argv[])
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | cur_flags,
-						 cur_timeout);
+						 cur_timeout,
+						 EVENT_SYSLOGGER_MAIN);
 
 		EnterCriticalSection(&sysloggerSection);
 #endif   /* WIN32 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 228190a..781e7b0 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -292,7 +292,8 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   EVENT_WALWRITER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index da9b7a6..308220f 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1286,7 +1286,8 @@ throttle(size_t increment)
 		 */
 		wait_result = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+								(long) (sleep / 1000),
+								EVENT_BASEBACKUP_THROTTLE);
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 67249d8..9b997c8 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -265,7 +265,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  EVENT_SYNC_REP);
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..dfd7d37 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -486,7 +486,8 @@ WalReceiverMain(void)
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   EVENT_WAL_RECEIVER_MAIN);
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +686,8 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  EVENT_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a0dba19..b612395 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,8 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  EVENT_WAL_SENDER_WRITE_DATA);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1273,8 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  EVENT_WAL_SENDER_WAIT_WAL);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1923,7 +1925,8 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  EVENT_WAL_SENDER_LOOP);
 		}
 	}
 	return;
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..435f183 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -297,9 +298,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  EventIdentifier eventId)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 eventId);
 }
 
 /*
@@ -316,7 +319,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, EventIdentifier eventId)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +347,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, eventId);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -863,7 +866,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 EventIdentifier eventId)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -872,6 +876,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 
 	Assert(nevents > 0);
 
+	pgstat_report_wait_start(WAIT_EVENT_SET, (uint16) eventId);
+
 	/*
 	 * Initialize timeout if requested.  We must record the current time so
 	 * that we can determine the remaining timeout if interrupted.
@@ -960,6 +966,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
@@ -1491,6 +1499,125 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 }
 #endif
 
+
+/*
+ * Return an identifier for a Latch depending on an event for statistics
+ * collector.
+ */
+const char *
+GetEventIdentifier(uint16 eventId)
+{
+	const char *res;
+
+	switch (eventId)
+	{
+		case EVENT_ARCHIVER_MAIN:
+			res = "ArchiverMain";
+			break;
+		case EVENT_AUTOVACUUM_MAIN:
+			res = "AutoVacuumMain";
+			break;
+		case EVENT_BASEBACKUP_THROTTLE:
+			res = "BaseBackupThrottle";
+			break;
+		case EVENT_BGWORKER_STARTUP:
+			res = "BgWorkerStartup";
+			break;
+		case EVENT_BGWORKER_SHUTDOWN:
+			res = "BgWorkerShutdown";
+			break;
+		case EVENT_BGWRITER_MAIN:
+			res = "BgWriterMain";
+			break;
+		case EVENT_BGWRITER_HIBERNATE:
+			res = "BgWriterHibernate";
+			break;
+		case EVENT_CHECKPOINTER_MAIN:
+			res = "CheckpointerMain";
+			break;
+		case EVENT_EXECUTE_GATHER:
+			res = "ExecuteGather";
+			break;
+		case EVENT_EXTENSION:
+			res = "Extension";
+			break;
+		case EVENT_MQ_PUT_MESSAGE:
+			res = "MessageQueuePutMessage";
+			break;
+		case EVENT_MQ_SEND_BYTES:
+			res = "MessageQueueSend";
+			break;
+		case EVENT_MQ_RECEIVE_BYTES:
+			res = "MessageQueueReceive";
+			break;
+		case EVENT_MQ_WAIT_INTERNAL:
+			res = "MessageQueueInternal";
+			break;
+		case EVENT_PARALLEL_WAIT_FINISH:
+			res = "ParallelFinish";
+			break;
+		case EVENT_PG_SLEEP:
+			res = "PgSleep";
+			break;
+		case EVENT_PGSTAT_MAIN:
+			res = "PgStatMain";
+			break;
+		case EVENT_PROC_SLEEP:
+			res = "ProcSleep";
+			break;
+		case EVENT_PROC_SIGNAL:
+			res = "ProcSignal";
+			break;
+		case EVENT_RECOVERY_APPLY_DELAY:
+			res = "RecoveryApplyDelay";
+			break;
+		case EVENT_RECOVERY_WAL_STREAM:
+			res = "RecoveryWalStream";
+			break;
+		case EVENT_RECOVERY_WAL_ALL:
+			res = "RecoveryWalAll";
+			break;
+		case EVENT_SECURE_READ:
+			res = "SecureRead";
+			break;
+		case EVENT_SECURE_WRITE:
+			res = "SecureWrite";
+			break;
+		case EVENT_SSL_OPEN_SERVER:
+			res = "SSLOpenServer";
+			break;
+		case EVENT_SYNC_REP:
+			res = "SyncRep";
+			break;
+		case EVENT_SYSLOGGER_MAIN:
+			res = "SysLoggerMain";
+			break;
+		case EVENT_WALWRITER_MAIN:
+			res = "WalWriterMain";
+			break;
+		case EVENT_WAL_RECEIVER_WAIT_START:
+			res = "WalSenderWaitStart";
+			break;
+		case EVENT_WAL_RECEIVER_MAIN:
+			res = "WalReceiverMain";
+			break;
+		case EVENT_WAL_SENDER_LOOP:
+			res = "WalSenderMain";
+			break;
+		case EVENT_WAL_SENDER_WAIT_WAL:
+			res = "WalSenderWaitForWAL";
+			break;
+		case EVENT_WAL_SENDER_WRITE_DATA:
+			res = "WalSenderWriteData";
+			break;
+		default:
+			res = "???";
+	}
+
+	return res;
+}
+
+
 /*
  * SetLatch uses SIGUSR1 to wake up the process waiting on the latch.
  *
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..af5344c 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -894,7 +894,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_MQ_SEND_BYTES);
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +991,7 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_MQ_RECEIVE_BYTES);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1090,7 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_MQ_WAIT_INTERNAL);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 9a758bd..2ed3f41 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -1216,7 +1216,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_PROC_SLEEP);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1728,7 +1728,7 @@ CheckDeadLockAlert(void)
 void
 ProcWaitForSignal(void)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_PROC_SIGNAL);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..ea88c87 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -560,7 +560,7 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms, EVENT_PG_SLEEP);
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index dc3320d..bca16e4 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -721,7 +721,8 @@ typedef enum WaitClass
 	WAIT_LWLOCK_NAMED,
 	WAIT_LWLOCK_TRANCHE,
 	WAIT_LOCK,
-	WAIT_BUFFER_PIN
+	WAIT_BUFFER_PIN,
+	WAIT_EVENT_SET
 }	WaitClass;
 
 
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..41dc553 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -135,6 +135,47 @@ typedef struct WaitEvent
 	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
 } WaitEvent;
 
+/*
+ * List of latch identifiers used when reporting activity to statistics
+ * collector.
+ */
+typedef enum EventIdentifier
+{
+	EVENT_ARCHIVER_MAIN,
+	EVENT_AUTOVACUUM_MAIN,
+	EVENT_BASEBACKUP_THROTTLE,
+	EVENT_BGWORKER_STARTUP,
+	EVENT_BGWORKER_SHUTDOWN,
+	EVENT_BGWRITER_MAIN,
+	EVENT_BGWRITER_HIBERNATE,
+	EVENT_CHECKPOINTER_MAIN,
+	EVENT_EXECUTE_GATHER,
+	EVENT_EXTENSION,
+	EVENT_MQ_PUT_MESSAGE,
+	EVENT_MQ_SEND_BYTES,
+	EVENT_MQ_RECEIVE_BYTES,
+	EVENT_MQ_WAIT_INTERNAL,
+	EVENT_PARALLEL_WAIT_FINISH,
+	EVENT_PGSTAT_MAIN,
+	EVENT_PROC_SLEEP,
+	EVENT_PROC_SIGNAL,
+	EVENT_PG_SLEEP,
+	EVENT_SECURE_READ,
+	EVENT_SECURE_WRITE,
+	EVENT_SSL_OPEN_SERVER,
+	EVENT_SYNC_REP,
+	EVENT_SYSLOGGER_MAIN,
+	EVENT_RECOVERY_APPLY_DELAY,
+	EVENT_RECOVERY_WAL_ALL,
+	EVENT_RECOVERY_WAL_STREAM,
+	EVENT_WAL_RECEIVER_WAIT_START,
+	EVENT_WAL_RECEIVER_MAIN,
+	EVENT_WAL_SENDER_WRITE_DATA,
+	EVENT_WAL_SENDER_LOOP,
+	EVENT_WAL_SENDER_WAIT_WAL,
+	EVENT_WALWRITER_MAIN
+} EventIdentifier;
+
 /* forward declaration to avoid exposing latch.c implementation details */
 typedef struct WaitEventSet WaitEventSet;
 
@@ -155,10 +196,14 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
+				  WaitEvent *occurred_events, int nevents,
+				  EventIdentifier eventId);
+extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+				  EventIdentifier eventId);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, EventIdentifier eventId);
+extern const char *GetEventIdentifier(uint16 eventId);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 143df4e..d32e42e 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -279,7 +279,7 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_EXTENSION);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index dd34bc7..caddf83 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -230,7 +230,7 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_EXTENSION);
 			ResetLatch(MyLatch);
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 314e371..bb0d7ff 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -227,7 +227,8 @@ worker_spi_main(Datum main_arg)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   worker_spi_naptime * 1000L);
+					   worker_spi_naptime * 1000L,
+					   EVENT_EXTENSION);
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#9Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Michael Paquier (#8)
Re: Tracking wait event for latches

Hi, Michael!

On Thu, Aug 4, 2016 at 8:26 AM, Michael Paquier <michael.paquier@gmail.com>
wrote:

On Tue, Aug 2, 2016 at 10:31 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Attached is an updated patch.

Updated version for 2 minor issues:
1) s/stram/stream/
2) Docs used incorrect number

I took a look at your patch. Couple of notes from me.

const char *

GetEventIdentifier(uint16 eventId)
{
const char *res;
switch (eventId)
{
case EVENT_ARCHIVER_MAIN:
res = "ArchiverMain";
break;
... long long list of events ...
case EVENT_WAL_SENDER_WRITE_DATA:
res = "WalSenderWriteData";
break;
default:
res = "???";
}
return res;
}

Would it be better to use an array here?

typedef enum EventIdentifier

{

EventIdentifier seems too general name for me, isn't it? Could we name it
WaitEventIdentifier? Or WaitEventId for shortcut?

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#10Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Alexander Korotkov (#9)
Re: Tracking wait event for latches

On Mon, Aug 22, 2016 at 12:09 PM, Alexander Korotkov <
a.korotkov@postgrespro.ru> wrote:

I took a look at your patch. Couple of notes from me.

const char *

GetEventIdentifier(uint16 eventId)
{
const char *res;
switch (eventId)
{
case EVENT_ARCHIVER_MAIN:
res = "ArchiverMain";
break;
... long long list of events ...
case EVENT_WAL_SENDER_WRITE_DATA:
res = "WalSenderWriteData";
break;
default:
res = "???";
}
return res;
}

Would it be better to use an array here?

typedef enum EventIdentifier

{

EventIdentifier seems too general name for me, isn't it? Could we name it
WaitEventIdentifier? Or WaitEventId for shortcut?

I'm also not sure about handling of secure_read() and secure_write()
functions.
In the current patch we're tracking latch event wait inside them. But we
setup latch only for blocking sockets and can do it multiple times in loop.
Would it be better to make separate wait events NETWORK_READ and
NETWORK_WRITE and setup them for the whole time spent in secure_read()
and secure_write()?

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#11Michael Paquier
michael.paquier@gmail.com
In reply to: Alexander Korotkov (#9)
Re: Tracking wait event for latches

On Mon, Aug 22, 2016 at 6:09 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

Hi, Michael!

On Thu, Aug 4, 2016 at 8:26 AM, Michael Paquier <michael.paquier@gmail.com>
wrote:
I took a look at your patch. Couple of notes from me.

Thanks!

const char *
GetEventIdentifier(uint16 eventId)
{
const char *res;
switch (eventId)
{
case EVENT_ARCHIVER_MAIN:
res = "ArchiverMain";
break;
... long long list of events ...
case EVENT_WAL_SENDER_WRITE_DATA:
res = "WalSenderWriteData";
break;
default:
res = "???";
}
return res;
}

Would it be better to use an array here?

The reason why I chose this way is that there are a lot of them. It is
painful to maintain the order of the array elements in perfect mapping
with the list of IDs...

typedef enum EventIdentifier
{

EventIdentifier seems too general name for me, isn't it? Could we name it
WaitEventIdentifier? Or WaitEventId for shortcut?

OK. So WaitEventIdentifier? The reason to include Identifier is for
consistency with lwlock structure notation.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#11)
Re: Tracking wait event for latches

On Mon, Aug 22, 2016 at 9:49 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

The reason why I chose this way is that there are a lot of them. It is
painful to maintain the order of the array elements in perfect mapping
with the list of IDs...

You can use stupid macro tricks to help with that problem...

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#12)
Re: Tracking wait event for latches

On Tue, Aug 23, 2016 at 12:44 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Mon, Aug 22, 2016 at 9:49 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

The reason why I chose this way is that there are a lot of them. It is
painful to maintain the order of the array elements in perfect mapping
with the list of IDs...

You can use stupid macro tricks to help with that problem...

Yeah, still after thinking about it I think I would just go with an
array like lock types and be done with it. With a comment to mention
that the order should be respected things would be enough...
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Michael Paquier
michael.paquier@gmail.com
In reply to: Alexander Korotkov (#10)
1 attachment(s)
Re: Tracking wait event for latches

On Mon, Aug 22, 2016 at 6:46 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

On Mon, Aug 22, 2016 at 12:09 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

I took a look at your patch. Couple of notes from me.

const char *
GetEventIdentifier(uint16 eventId)
{
const char *res;
switch (eventId)
{
case EVENT_ARCHIVER_MAIN:
res = "ArchiverMain";
break;
... long long list of events ...
case EVENT_WAL_SENDER_WRITE_DATA:
res = "WalSenderWriteData";
break;
default:
res = "???";
}
return res;
}

Would it be better to use an array here?

Okay, I have switched to an array....

typedef enum EventIdentifier
{

EventIdentifier seems too general name for me, isn't it? Could we name it
WaitEventIdentifier? Or WaitEventId for shortcut?

... And WaitEventIdentifier.

I'm also not sure about handling of secure_read() and secure_write()
functions.
In the current patch we're tracking latch event wait inside them. But we
setup latch only for blocking sockets and can do it multiple times in loop.
Would it be better to make separate wait events NETWORK_READ and
NETWORK_WRITE and setup them for the whole time spent in secure_read() and
secure_write()?

The whole point is to track a waiting event when we are sure that it
is going to wait, which is why the patch depends on WaitEventSetWait.
If we would set up those flags at the beginning and reset them of
secure_read and secure_write, we may actually track an event that is
not blocking.
--
Michael

Attachments:

wait-event-set-v4.patchtext/plain; charset=US-ASCII; name=wait-event-set-v4.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..954a166 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -496,7 +496,7 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L, EVENT_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 0776428..a14abe4 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,16 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>EventSet</>: The server process is waiting on a socket
+          or a timer. This wait happens in a latch, an inter-process facility
+          using boolean variables letting a process sleep until it is set.
+          Latches support several type of operations, like postmaster death
+          handling, timeout and socket activity lookup, and are a useful
+          replacement for <function>sleep</> where signal handling matters.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1085,6 +1095,139 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="32"><literal>EventSet</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in the code path of an extension, should be used by custom plugins and modules</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to put new message in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSleep</></entry>
+         <entry>Waiting for a specific lock.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSignal</></entry>
+         <entry>Waiting for signal from another backend.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stream at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for WAL commit during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index a47eba6..2495e61 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -541,7 +541,8 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  EVENT_PARALLEL_WAIT_FINISH);
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f13f9c1..19d9977 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5756,7 +5756,8 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  EVENT_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -11294,7 +11295,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+							 wait_time, EVENT_RECOVERY_WAL_STREAM);
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11457,7 +11458,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L, EVENT_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..d8fe2a5 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -387,7 +387,7 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_EXECUTE_GATHER);
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index f6adb15..759b337 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -418,7 +418,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+								  EVENT_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..5880818 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -146,7 +146,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 EVENT_SECURE_READ);
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +248,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 EVENT_SECURE_WRITE);
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index 921242f..6c39e51 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -171,7 +171,7 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0, EVENT_MQ_PUT_MESSAGE);
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 3768f50..bb80c11 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -600,7 +600,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   EVENT_AUTOVACUUM_MAIN);
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 699c934..ee4ed7e 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -969,7 +969,8 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   EVENT_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1009,8 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			break;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   EVENT_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 00f03d8..1c135de 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -347,7 +347,8 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */,
+					   EVENT_BGWRITER_MAIN);
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -374,7 +375,8 @@ BackgroundWriterMain(void)
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
 						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+						   BgWriterDelay * HIBERNATE_FACTOR,
+						   EVENT_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 8d4b353..60bab87 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -558,7 +558,8 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   EVENT_CHECKPOINTER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..156fff7 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,7 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							 timeout * 1000L, EVENT_ARCHIVER_MAIN);
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 8fa9edb..08384dc 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3152,6 +3152,9 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_EVENT_SET:
+			event_type = "EventSet";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3193,6 +3196,9 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_EVENT_SET:
+			event_name = GetWaitEventIdentifier(eventId);
+			break;
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3682,7 +3688,7 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
 							   pgStatSock,
-							   -1L);
+							   -1L, EVENT_PGSTAT_MAIN);
 #else
 
 		/*
@@ -3698,7 +3704,8 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 		WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
 							   pgStatSock,
-							   2 * 1000L /* msec */ );
+							   2 * 1000L /* msec */,
+							   EVENT_PGSTAT_MAIN);
 #endif
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..c147fd1 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -424,7 +424,8 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+							   EVENT_SYSLOGGER_MAIN);
 
 		if (rc & WL_SOCKET_READABLE)
 		{
@@ -475,7 +476,8 @@ SysLoggerMain(int argc, char *argv[])
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | cur_flags,
-						 cur_timeout);
+						 cur_timeout,
+						 EVENT_SYSLOGGER_MAIN);
 
 		EnterCriticalSection(&sysloggerSection);
 #endif   /* WIN32 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 228190a..781e7b0 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -292,7 +292,8 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   EVENT_WALWRITER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index da9b7a6..308220f 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1286,7 +1286,8 @@ throttle(size_t increment)
 		 */
 		wait_result = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+								(long) (sleep / 1000),
+								EVENT_BASEBACKUP_THROTTLE);
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index b442d06..2884944 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -258,7 +258,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  EVENT_SYNC_REP);
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..dfd7d37 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -486,7 +486,8 @@ WalReceiverMain(void)
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   EVENT_WAL_RECEIVER_MAIN);
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +686,8 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  EVENT_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a0dba19..45d9471 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,8 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  EVENT_WAL_SENDER_WRITE_DATA);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1273,8 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  EVENT_WAL_SENDER_WAIT_WAL);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1923,7 +1925,8 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  EVENT_WAL_SENDER_MAIN);
 		}
 	}
 	return;
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..e6cc6d0 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -122,6 +123,43 @@ struct WaitEventSet
 #endif
 };
 
+/* This must match enum WaitEventIdentifier! */
+const char *const WaitEventNames[] = {
+	"ArchiverMain",
+	"AutoVacuumMain",
+	"BaseBackupThrottle",
+	"BgWorkerStartup",
+	"BgWorkerShutdown",
+	"BgWriterMain",
+	"BgWriterHibernate",
+	"CheckpointerMain",
+	"ExecuteGather",
+	"Extension",
+	"MessageQueuePutMessage",
+	"MessageQueueSend",
+	"MessageQueueReceive",
+	"MessageQueueInternal",
+	"ParallelFinish",
+	"PgStatMain",
+	"ProcSleep",
+	"ProcSignal",
+	"PgSleep",
+	"SecureRead",
+	"SecureWrite",
+	"SSLOpenServer",
+	"SyncRep",
+	"SysLoggerMain",
+	"RecoveryApplyDelay",
+	"RecoveryWalAll",
+	"RecoveryWalStream",
+	"WalReceiverWaitStart",
+	"WalReceiverMain",
+	"WalSenderMain",
+	"WalSenderWaitForWAL",
+	"WalSenderWriteData"
+	"WalWriterMain",
+};
+
 #ifndef WIN32
 /* Are we currently in WaitLatch? The signal handler would like to know. */
 static volatile sig_atomic_t waiting = false;
@@ -297,9 +335,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  WaitEventIdentifier eventId)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 eventId);
 }
 
 /*
@@ -316,7 +356,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, WaitEventIdentifier eventId)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +384,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, eventId);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -863,7 +903,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 WaitEventIdentifier eventId)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -872,6 +913,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 
 	Assert(nevents > 0);
 
+	pgstat_report_wait_start(WAIT_EVENT_SET, (uint16) eventId);
+
 	/*
 	 * Initialize timeout if requested.  We must record the current time so
 	 * that we can determine the remaining timeout if interrupted.
@@ -960,6 +1003,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
@@ -1491,6 +1536,20 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 }
 #endif
 
+
+/*
+ * Return an identifier for a Latch depending on an event for statistics
+ * collector.
+ */
+const char *
+GetWaitEventIdentifier(uint16 eventId)
+{
+	if (eventId > EVENT_LAST_TYPE)
+		return "???";
+	return WaitEventNames[eventId];
+}
+
+
 /*
  * SetLatch uses SIGUSR1 to wake up the process waiting on the latch.
  *
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..af5344c 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -894,7 +894,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_MQ_SEND_BYTES);
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +991,7 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_MQ_RECEIVE_BYTES);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1090,7 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_MQ_WAIT_INTERNAL);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 9a758bd..2ed3f41 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -1216,7 +1216,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_PROC_SLEEP);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1728,7 +1728,7 @@ CheckDeadLockAlert(void)
 void
 ProcWaitForSignal(void)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_PROC_SIGNAL);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..ea88c87 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -560,7 +560,7 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms, EVENT_PG_SLEEP);
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index dc3320d..bca16e4 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -721,7 +721,8 @@ typedef enum WaitClass
 	WAIT_LWLOCK_NAMED,
 	WAIT_LWLOCK_TRANCHE,
 	WAIT_LOCK,
-	WAIT_BUFFER_PIN
+	WAIT_BUFFER_PIN,
+	WAIT_EVENT_SET
 }	WaitClass;
 
 
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..18c78de 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -135,6 +135,51 @@ typedef struct WaitEvent
 	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
 } WaitEvent;
 
+/*
+ * List of latch identifiers used when reporting activity to statistics
+ * collector. Up to 256 different WaitEventIdentifier can be handled.
+ */
+typedef enum WaitEventIdentifier
+{
+	EVENT_ARCHIVER_MAIN,
+	EVENT_AUTOVACUUM_MAIN,
+	EVENT_BASEBACKUP_THROTTLE,
+	EVENT_BGWORKER_STARTUP,
+	EVENT_BGWORKER_SHUTDOWN,
+	EVENT_BGWRITER_MAIN,
+	EVENT_BGWRITER_HIBERNATE,
+	EVENT_CHECKPOINTER_MAIN,
+	EVENT_EXECUTE_GATHER,
+	EVENT_EXTENSION,
+	EVENT_MQ_PUT_MESSAGE,
+	EVENT_MQ_SEND_BYTES,
+	EVENT_MQ_RECEIVE_BYTES,
+	EVENT_MQ_WAIT_INTERNAL,
+	EVENT_PARALLEL_WAIT_FINISH,
+	EVENT_PGSTAT_MAIN,
+	EVENT_PROC_SLEEP,
+	EVENT_PROC_SIGNAL,
+	EVENT_PG_SLEEP,
+	EVENT_SECURE_READ,
+	EVENT_SECURE_WRITE,
+	EVENT_SSL_OPEN_SERVER,
+	EVENT_SYNC_REP,
+	EVENT_SYSLOGGER_MAIN,
+	EVENT_RECOVERY_APPLY_DELAY,
+	EVENT_RECOVERY_WAL_ALL,
+	EVENT_RECOVERY_WAL_STREAM,
+	EVENT_WAL_RECEIVER_WAIT_START,
+	EVENT_WAL_RECEIVER_MAIN,
+	EVENT_WAL_SENDER_WRITE_DATA,
+	EVENT_WAL_SENDER_MAIN,
+	EVENT_WAL_SENDER_WAIT_WAL,
+	EVENT_WALWRITER_MAIN
+} WaitEventIdentifier;
+
+#define EVENT_LAST_TYPE EVENT_WALWRITER_MAIN
+
+extern const char *const WaitEventName[];
+
 /* forward declaration to avoid exposing latch.c implementation details */
 typedef struct WaitEventSet WaitEventSet;
 
@@ -155,10 +200,14 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
+				  WaitEvent *occurred_events, int nevents,
+				  WaitEventIdentifier eventId);
+extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+				  WaitEventIdentifier eventId);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, WaitEventIdentifier eventId);
+extern const char *GetWaitEventIdentifier(uint16 eventId);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 143df4e..d32e42e 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -279,7 +279,7 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_EXTENSION);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index dd34bc7..caddf83 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -230,7 +230,7 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, EVENT_EXTENSION);
 			ResetLatch(MyLatch);
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 314e371..bb0d7ff 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -227,7 +227,8 @@ worker_spi_main(Datum main_arg)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   worker_spi_naptime * 1000L);
+					   worker_spi_naptime * 1000L,
+					   EVENT_EXTENSION);
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#15Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Michael Paquier (#14)
Re: Tracking wait event for latches
+         <para>
+          <literal>EventSet</>: The server process is waiting on a socket
+          or a timer. This wait happens in a latch, an inter-process facility
+          using boolean variables letting a process sleep until it is set.
+          Latches support several type of operations, like postmaster death
+          handling, timeout and socket activity lookup, and are a useful
+          replacement for <function>sleep</> where signal handling matters.
+         </para>

This paragraph seems a bit confused. I suggest something more like
this: "The server process is waiting for one or more sockets, a timer
or an interprocess latch. The wait happens in a WaitEventSet,
<productname>PostgreSQL</>'s portable IO multiplexing abstraction."

On Tue, Aug 23, 2016 at 7:01 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Mon, Aug 22, 2016 at 12:09 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

Would it be better to use an array here?

Okay, I have switched to an array....

I looked at various macro tricks[1]http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types-as-string-in-c but they're all pretty unpleasant!
+1 for the simple array with carefully managed order. About that
order...

+const char *const WaitEventNames[] = {
+ "ArchiverMain",
+ "AutoVacuumMain",
+ "BaseBackupThrottle",
+ "BgWorkerStartup",
+ "BgWorkerShutdown",
+ "BgWriterMain",
+ "BgWriterHibernate",
+ "CheckpointerMain",
+ "ExecuteGather",
+ "Extension",
+ "MessageQueuePutMessage",
+ "MessageQueueSend",
+ "MessageQueueReceive",
+ "MessageQueueInternal",
+ "ParallelFinish",
+ "PgStatMain",
+ "ProcSleep",
+ "ProcSignal",
+ "PgSleep",
+ "SecureRead",
+ "SecureWrite",
+ "SSLOpenServer",
+ "SyncRep",
+ "SysLoggerMain",
+ "RecoveryApplyDelay",
+ "RecoveryWalAll",
+ "RecoveryWalStream",
+ "WalReceiverWaitStart",
+ "WalReceiverMain",
+ "WalSenderMain",
+ "WalSenderWaitForWAL",
+ "WalSenderWriteData"
+ "WalWriterMain",
+};

It looks like this array wants to be in alphabetical order, but it
isn't quite. Also, perhaps a compile time assertion about the size of
the array matching EVENT_LAST_TYPE could be useful?

+typedef enum WaitEventIdentifier
+{
+ EVENT_ARCHIVER_MAIN,
+ EVENT_AUTOVACUUM_MAIN,
+ EVENT_BASEBACKUP_THROTTLE,
+ EVENT_BGWORKER_STARTUP,
+ EVENT_BGWORKER_SHUTDOWN,
+ EVENT_BGWRITER_MAIN,
+ EVENT_BGWRITER_HIBERNATE,
+ EVENT_CHECKPOINTER_MAIN,
+ EVENT_EXECUTE_GATHER,
+ EVENT_EXTENSION,
+ EVENT_MQ_PUT_MESSAGE,
+ EVENT_MQ_SEND_BYTES,
+ EVENT_MQ_RECEIVE_BYTES,
+ EVENT_MQ_WAIT_INTERNAL,
+ EVENT_PARALLEL_WAIT_FINISH,
+ EVENT_PGSTAT_MAIN,
+ EVENT_PROC_SLEEP,
+ EVENT_PROC_SIGNAL,
+ EVENT_PG_SLEEP,
+ EVENT_SECURE_READ,
+ EVENT_SECURE_WRITE,
+ EVENT_SSL_OPEN_SERVER,
+ EVENT_SYNC_REP,
+ EVENT_SYSLOGGER_MAIN,
+ EVENT_RECOVERY_APPLY_DELAY,
+ EVENT_RECOVERY_WAL_ALL,
+ EVENT_RECOVERY_WAL_STREAM,
+ EVENT_WAL_RECEIVER_WAIT_START,
+ EVENT_WAL_RECEIVER_MAIN,
+ EVENT_WAL_SENDER_WRITE_DATA,
+ EVENT_WAL_SENDER_MAIN,
+ EVENT_WAL_SENDER_WAIT_WAL,
+ EVENT_WALWRITER_MAIN
+} WaitEventIdentifier;

This is also nearly but not exactly in alphabetical order
(EVENT_PROC_SIGNAL comes after EVENT_PROC_SLEEP for example). But
it's not currently possible to have the strings and the enumerators
both in alphabetical order because they're not the same, even
accounting for camel-case to upper-case transliteration. I think at
least one of them should be sorted. Shouldn't they match fully and
then *both* be sorted alphabetically? For example
"MessageQueueInternal" doesn't match EVENT_MQ_WAIT_INTERNAL. Then
there are some cases where I'd expect underscores for consistency with
other enumerators and with the corresponding camel-case strings: you
have EVENT_WAL_SENDER_MAIN, but EVENT_WALWRITER_MAIN.

The documentation is in a slightly different order again but also not
exactly alphabetical: for example ProcSleep is listed before
ProcSignal.

Sorry if this all sounds terribly picky but I think we should try to
be strictly systematic here.

EventIdentifier seems too general name for me, isn't it? Could we name it
WaitEventIdentifier? Or WaitEventId for shortcut?

... And WaitEventIdentifier.

+1 from me too for avoiding the overly general term 'event'. It does
seem a little odd to leave the enumerators names as EVENT_... though;
shouldn't these be WAIT_EVENT_... or WE_...? Or perhaps you could
consider WaitPointIdentifier and WP_SECURE_READ or
WaitEventPointIdentifier and WEP_SECURE_READ, if you buy my earlier
argument that what we are really naming here is point in the code
where we wait, not the events we're waiting for. Contrast with
LWLocks where we report the lock that you're waiting for, not the
place in the code where you're waiting for that lock.

On Wed, Aug 3, 2016 at 1:31 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Finally, I have changed the patch so as it does not track "Latch"
events, but "EventSet" events instead, per the suggestion of Thomas.
"WaitEventSet" is too close to wait_event in my taste so I shortened
the suggeston.

This is good, because showing "Latch" when we were really waiting for
a socket was misleading.

On the other hand, if we could *accurately* report whether it's a
"Latch", "Socket" or "Latch|Socket" that we're waiting for, it might
be cool to do that instead. One way to do that would be to use up
several class IDs: WAIT_EVENT_LATCH, WAIT_EVENT_LATCH_OR_SOCKET,
WAIT_EVENT_SOCKET (or perhaps WAIT_EVENT_LATCH | WAIT_EVENT_SOCKET,
reserving 2 or 3 upper bits from the 8 bit class ID for this). Then
we could figure out the right class ID by looking at set->latch and
set->nevents (perhaps an extra boolean would be needed to record
whether postmaster death is in there so we could deduce whether there
are any sockets). It would be interesting specifically for the case
of FDWs where it would be nice to be able to see clearly that it's
waiting for a remote server ("Socket"). It may also be interesting to
know if there is a timeout. Postmaster death doesn't seem newsworthy,
we're nearly always also waiting for that exceptional event so it'd
just be clutter to report it.

[1]: http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types-as-string-in-c

--
Thomas Munro
http://www.enterprisedb.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Michael Paquier
michael.paquier@gmail.com
In reply to: Thomas Munro (#15)
1 attachment(s)
Re: Tracking wait event for latches

On Wed, Sep 21, 2016 at 8:13 AM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

+         <para>
+          <literal>EventSet</>: The server process is waiting on a socket
+          or a timer. This wait happens in a latch, an inter-process facility
+          using boolean variables letting a process sleep until it is set.
+          Latches support several type of operations, like postmaster death
+          handling, timeout and socket activity lookup, and are a useful
+          replacement for <function>sleep</> where signal handling matters.
+         </para>

This paragraph seems a bit confused. I suggest something more like
this: "The server process is waiting for one or more sockets, a timer
or an interprocess latch. The wait happens in a WaitEventSet,
<productname>PostgreSQL</>'s portable IO multiplexing abstraction."

OK, I have tweaked the paragraph as follows using your suggestion:
+        <listitem>
+         <para>
+          <literal>EventSet</>: The server process is waiting on one or more
+          sockets, a time or an inter-process latch.  The wait happens in a
+          <function>WaitEventSet</>, <productname>PostgreSQL</>'s portable
+          I/O multiplexing abstraction using boolean variables letting a
+          process sleep until it is set.  It supports several type of
+          operations, like postmaster death handling, timeout and socket
+          activity lookup, and are a useful replacement for <function>sleep</>
+          where signal handling matters.
+         </para>
+        </listitem>

On Tue, Aug 23, 2016 at 7:01 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Mon, Aug 22, 2016 at 12:09 PM, Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

Would it be better to use an array here?

Okay, I have switched to an array....

I looked at various macro tricks[1] but they're all pretty unpleasant!
+1 for the simple array with carefully managed order. About that
order...
[1] http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types-as-string-in-c

Yes, I recall bumping on this one, or something really similar to that...

+const char *const WaitEventNames[] = {
[...]
+};

It looks like this array wants to be in alphabetical order, but it
isn't quite. Also, perhaps a compile time assertion about the size of
the array matching EVENT_LAST_TYPE could be useful?

In GetWaitEventIdentifier()? I'd think that just returning ??? would
have been fine if there is a non-matching call.

+typedef enum WaitEventIdentifier
+{
[...]
+} WaitEventIdentifier;

This is also nearly but not exactly in alphabetical order
(EVENT_PROC_SIGNAL comes after EVENT_PROC_SLEEP for example). But
it's not currently possible to have the strings and the enumerators
both in alphabetical order because they're not the same, even
accounting for camel-case to upper-case transliteration. I think at
least one of them should be sorted. Shouldn't they match fully and
then *both* be sorted alphabetically? For example
"MessageQueueInternal" doesn't match EVENT_MQ_WAIT_INTERNAL. Then
there are some cases where I'd expect underscores for consistency with
other enumerators and with the corresponding camel-case strings: you
have EVENT_WAL_SENDER_MAIN, but EVENT_WALWRITER_MAIN.

Not wrong..

The documentation is in a slightly different order again but also not
exactly alphabetical: for example ProcSleep is listed before
ProcSignal.

Right.

Sorry if this all sounds terribly picky but I think we should try to
be strictly systematic here.

No worries about that, it matters a lot for this patch. The user-faced
documentation is what should do the decision-making I think. So let's
order the names, and adapt the enum depending on that. I have done so
after double-checking both lists, and added a comment for anybody
updating that in the fiture.

EventIdentifier seems too general name for me, isn't it? Could we name it
WaitEventIdentifier? Or WaitEventId for shortcut?

... And WaitEventIdentifier.

+1 from me too for avoiding the overly general term 'event'. It does
seem a little odd to leave the enumerators names as EVENT_... though;
shouldn't these be WAIT_EVENT_... or WE_...? Or perhaps you could
consider WaitPointIdentifier and WP_SECURE_READ or
WaitEventPointIdentifier and WEP_SECURE_READ, if you buy my earlier
argument that what we are really naming here is point in the code
where we wait, not the events we're waiting for. Contrast with
LWLocks where we report the lock that you're waiting for, not the
place in the code where you're waiting for that lock.

Well, WE_ if I need make a choice for something else than EVENT_.

On the other hand, if we could *accurately* report whether it's a
"Latch", "Socket" or "Latch|Socket" that we're waiting for, it might
be cool to do that instead. One way to do that would be to use up
several class IDs: WAIT_EVENT_LATCH, WAIT_EVENT_LATCH_OR_SOCKET,
WAIT_EVENT_SOCKET (or perhaps WAIT_EVENT_LATCH | WAIT_EVENT_SOCKET,
reserving 2 or 3 upper bits from the 8 bit class ID for this). Then
we could figure out the right class ID by looking at set->latch and
set->nevents (perhaps an extra boolean would be needed to record
whether postmaster death is in there so we could deduce whether there
are any sockets). It would be interesting specifically for the case
of FDWs where it would be nice to be able to see clearly that it's
waiting for a remote server ("Socket"). It may also be interesting to
know if there is a timeout. Postmaster death doesn't seem newsworthy,
we're nearly always also waiting for that exceptional event so it'd
just be clutter to report it.

That's actually pretty close to what I mentioned upthread here:
/messages/by-id/CAB7nPqQx4OEym9cf22CY=5eWqqiAMjij6EBCoNReezt9-NvGkw@mail.gmail.com
In order to support that adding a column wait_event_details with
text[] makes the most sense I guess. Still I think that's another
discussion, this patch does already a lot.

So I have adjusted the patch in many ways, tweaked the order of the
items, and adjusted some of their names as suggested by Thomas.
Updated version attached.
--
Michael

Attachments:

wait-event-set-v5.patchtext/plain; charset=US-ASCII; name=wait-event-set-v5.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..7cecef8 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -496,7 +496,7 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L, WE_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 0776428..5185d65 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,18 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>EventSet</>: The server process is waiting on one or more
+          sockets, a time or an inter-process latch.  The wait happens in a
+          <function>WaitEventSet</>, <productname>PostgreSQL</>'s portable
+          I/O multiplexing abstraction using boolean variables letting a
+          process sleep until it is set.  It supports several type of
+          operations, like postmaster death handling, timeout and socket
+          activity lookup, and are a useful replacement for <function>sleep</>
+          where signal handling matters.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1085,6 +1097,139 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="32"><literal>EventSet</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in the code path of an extension, should be used by custom plugins and modules</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to put new message in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSignal</></entry>
+         <entry>Waiting for signal from another backend.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSleep</></entry>
+         <entry>Waiting for a specific lock.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stream at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for WAL commit during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index cde0ed3..60d5c9c 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -540,7 +540,8 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  WE_PARALLEL_FINISH);
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 2189c22..678b6de 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5827,7 +5827,8 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  WE_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -11376,7 +11377,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+							 wait_time, WE_RECOVERY_WAL_STREAM);
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11539,7 +11540,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L, WE_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..b832d91 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -387,7 +387,7 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_EXECUTE_GATHER);
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index fedb02c..571d4d6 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -419,7 +419,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+								  WE_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..4e16073 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -146,7 +146,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WE_SECURE_READ);
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +248,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WE_SECURE_WRITE);
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index bfe66c6..625b987 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -171,7 +171,7 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0, WE_MQ_PUT_MESSAGE);
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1a92ca1..019075e 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -598,7 +598,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   WE_AUTOVACUUM_MAIN);
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 699c934..7da01df 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -969,7 +969,8 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WE_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1009,8 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			break;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WE_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 1002034..a89d39d 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -345,7 +345,8 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */,
+					   WE_BGWRITER_MAIN);
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -372,7 +373,8 @@ BackgroundWriterMain(void)
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
 						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+						   BgWriterDelay * HIBERNATE_FACTOR,
+						   WE_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index d702a48..e18ef9f 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -556,7 +556,8 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   WE_CHECKPOINTER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..018878c 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,7 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							 timeout * 1000L, WE_ARCHIVER_MAIN);
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 8a2ce91..110eb50 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3152,6 +3152,9 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_EVENT_SET:
+			event_type = "EventSet";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3193,6 +3196,9 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_EVENT_SET:
+			event_name = GetWaitEventIdentifier(eventId);
+			break;
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3682,7 +3688,7 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
 							   pgStatSock,
-							   -1L);
+							   -1L, WE_PGSTAT_MAIN);
 #else
 
 		/*
@@ -3698,7 +3704,8 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 		WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
 							   pgStatSock,
-							   2 * 1000L /* msec */ );
+							   2 * 1000L /* msec */,
+							   EVENT_PGSTAT_MAIN);
 #endif
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..5bdfa479 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -424,7 +424,8 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+							   WE_SYSLOGGER_MAIN);
 
 		if (rc & WL_SOCKET_READABLE)
 		{
@@ -475,7 +476,8 @@ SysLoggerMain(int argc, char *argv[])
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | cur_flags,
-						 cur_timeout);
+						 cur_timeout,
+						 EVENT_SYSLOGGER_MAIN);
 
 		EnterCriticalSection(&sysloggerSection);
 #endif   /* WIN32 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 11ec56a..5b6fa15 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -290,7 +290,8 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   WE_WAL_WRITER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index da9b7a6..82e8d03 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1286,7 +1286,8 @@ throttle(size_t increment)
 		 */
 		wait_result = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+								(long) (sleep / 1000),
+								WE_BASE_BACKUP_THROTTLE);
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index b442d06..f1acb4a 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -258,7 +258,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  WE_SYNC_REP);
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..046a760 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -486,7 +486,8 @@ WalReceiverMain(void)
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   WE_WAL_RECEIVER_MAIN);
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +686,8 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  WE_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c7743da..3cac83b 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,8 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WE_WAL_SENDER_WRITE_DATA);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1273,8 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WE_WAL_SENDER_WAIT_WAL);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1924,7 +1926,8 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  WE_WAL_SENDER_MAIN);
 		}
 	}
 	return;
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..3394238 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -122,6 +123,46 @@ struct WaitEventSet
 #endif
 };
 
+/*
+ * This must match enum WaitEventIdentifier! The alphabetical order here
+ * is kept in priority and in-sync with the documentation.
+ */
+const char *const WaitEventNames[] = {
+	"ArchiverMain",
+	"AutoVacuumMain",
+	"BaseBackupThrottle",
+	"BgWorkerShutdown",
+	"BgWorkerStartup",
+	"BgWriterHibernate",
+	"BgWriterMain",
+	"CheckpointerMain",
+	"ExecuteGather",
+	"Extension",
+	"MessageQueueInternal",
+	"MessageQueuePutMessage",
+	"MessageQueueReceive",
+	"MessageQueueSend",
+	"ParallelFinish",
+	"PgSleep",
+	"PgStatMain",
+	"ProcSignal",
+	"ProcSleep",
+	"RecoveryApplyDelay",
+	"RecoveryWalAll",
+	"RecoveryWalStream",
+	"SecureRead",
+	"SecureWrite",
+	"SSLOpenServer",
+	"SyncRep",
+	"SysLoggerMain",
+	"WalReceiverMain",
+	"WalReceiverWaitStart",
+	"WalSenderMain",
+	"WalSenderWaitForWAL",
+	"WalSenderWriteData",
+	"WalWriterMain"
+};
+
 #ifndef WIN32
 /* Are we currently in WaitLatch? The signal handler would like to know. */
 static volatile sig_atomic_t waiting = false;
@@ -297,9 +338,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  WaitEventIdentifier eventId)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 eventId);
 }
 
 /*
@@ -316,7 +359,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, WaitEventIdentifier eventId)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +387,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, eventId);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -863,7 +906,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 WaitEventIdentifier eventId)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -872,6 +916,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 
 	Assert(nevents > 0);
 
+	pgstat_report_wait_start(WAIT_EVENT_SET, (uint16) eventId);
+
 	/*
 	 * Initialize timeout if requested.  We must record the current time so
 	 * that we can determine the remaining timeout if interrupted.
@@ -960,6 +1006,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
@@ -1491,6 +1539,20 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 }
 #endif
 
+
+/*
+ * Return an identifier for a Latch depending on an event for statistics
+ * collector.
+ */
+const char *
+GetWaitEventIdentifier(uint16 eventId)
+{
+	if (eventId > WE_LAST_TYPE)
+		return "???";
+	return WaitEventNames[eventId];
+}
+
+
 /*
  * SetLatch uses SIGUSR1 to wake up the process waiting on the latch.
  *
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..c47cf2b 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -894,7 +894,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_MQ_SEND);
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +991,7 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_MQ_RECEIVE);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1090,7 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_MQ_INTERNAL);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 33e7023..23deeb6 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -1212,7 +1212,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_PROC_SLEEP);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1724,7 +1724,7 @@ CheckDeadLockAlert(void)
 void
 ProcWaitForSignal(void)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_PROC_SIGNAL);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..a814a06 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -560,7 +560,7 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms, WE_PG_SLEEP);
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index dc3320d..bca16e4 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -721,7 +721,8 @@ typedef enum WaitClass
 	WAIT_LWLOCK_NAMED,
 	WAIT_LWLOCK_TRANCHE,
 	WAIT_LOCK,
-	WAIT_BUFFER_PIN
+	WAIT_BUFFER_PIN,
+	WAIT_EVENT_SET
 }	WaitClass;
 
 
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..45859bb 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -135,6 +135,51 @@ typedef struct WaitEvent
 	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
 } WaitEvent;
 
+/*
+ * List of latch identifiers used when reporting activity to statistics
+ * collector. Up to 256 different WaitEventIdentifier can be handled.
+ */
+typedef enum WaitEventIdentifier
+{
+	WE_ARCHIVER_MAIN,
+	WE_AUTOVACUUM_MAIN,
+	WE_BASE_BACKUP_THROTTLE,
+	WE_BGWORKER_SHUTDOWN,
+	WE_BGWORKER_STARTUP,
+	WE_BGWRITER_HIBERNATE,
+	WE_BGWRITER_MAIN,
+	WE_CHECKPOINTER_MAIN,
+	WE_EXECUTE_GATHER,
+	WE_EXTENSION,
+	WE_MQ_INTERNAL,
+	WE_MQ_PUT_MESSAGE,
+	WE_MQ_RECEIVE,
+	WE_MQ_SEND,
+	WE_PARALLEL_FINISH,
+	WE_PG_SLEEP,
+	WE_PGSTAT_MAIN,
+	WE_PROC_SIGNAL,
+	WE_PROC_SLEEP,
+	WE_RECOVERY_APPLY_DELAY,
+	WE_RECOVERY_WAL_ALL,
+	WE_RECOVERY_WAL_STREAM,
+	WE_SECURE_READ,
+	WE_SECURE_WRITE,
+	WE_SSL_OPEN_SERVER,
+	WE_SYNC_REP,
+	WE_SYSLOGGER_MAIN,
+	WE_WAL_RECEIVER_MAIN,
+	WE_WAL_RECEIVER_WAIT_START,
+	WE_WAL_SENDER_MAIN,
+	WE_WAL_SENDER_WAIT_WAL,
+	WE_WAL_SENDER_WRITE_DATA,
+	WE_WAL_WRITER_MAIN
+} WaitEventIdentifier;
+
+#define WE_LAST_TYPE WE_WAL_WRITER_MAIN
+
+extern const char *const WaitEventName[];
+
 /* forward declaration to avoid exposing latch.c implementation details */
 typedef struct WaitEventSet WaitEventSet;
 
@@ -155,10 +200,14 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
+				  WaitEvent *occurred_events, int nevents,
+				  WaitEventIdentifier eventId);
+extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+				  WaitEventIdentifier eventId);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, WaitEventIdentifier eventId);
+extern const char *GetWaitEventIdentifier(uint16 eventId);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 143df4e..adebfc8 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -279,7 +279,7 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_EXTENSION);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index dd34bc7..22145db 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -230,7 +230,7 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_EXTENSION);
 			ResetLatch(MyLatch);
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 314e371..aa7f7f9 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -227,7 +227,8 @@ worker_spi_main(Datum main_arg)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   worker_spi_naptime * 1000L);
+					   worker_spi_naptime * 1000L,
+					   WE_EXTENSION);
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#17Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Michael Paquier (#16)
Re: Tracking wait event for latches

On Wed, Sep 21, 2016 at 3:40 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Wed, Sep 21, 2016 at 8:13 AM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

It looks like this array wants to be in alphabetical order, but it
isn't quite. Also, perhaps a compile time assertion about the size of
the array matching EVENT_LAST_TYPE could be useful?

In GetWaitEventIdentifier()? I'd think that just returning ??? would
have been fine if there is a non-matching call.

Yeah but that's at run time. I meant you could help developers
discover ASAP if they add a new item to one place but not the other
with a compile time assertion:

const char *
GetWaitEventIdentifier(uint16 eventId)
{
StaticAssertStmt(lengthof(WaitEventNames) == WE_LAST_TYPE + 1,
"WaitEventNames must match WaitEventIdentifiers");
if (eventId > WE_LAST_TYPE)
return "???";
return WaitEventNames[eventId];
}

+1 from me too for avoiding the overly general term 'event'. It does
seem a little odd to leave the enumerators names as EVENT_... though;
shouldn't these be WAIT_EVENT_... or WE_...? Or perhaps you could
consider WaitPointIdentifier and WP_SECURE_READ or
WaitEventPointIdentifier and WEP_SECURE_READ, if you buy my earlier
argument that what we are really naming here is point in the code
where we wait, not the events we're waiting for. Contrast with
LWLocks where we report the lock that you're waiting for, not the
place in the code where you're waiting for that lock.

Well, WE_ if I need make a choice for something else than EVENT_.

You missed a couple that are hiding inside #ifdef WIN32:

From pgstat.c:
+ EVENT_PGSTAT_MAIN);

From syslogger.c:
+ EVENT_SYSLOGGER_MAIN);

--
Thomas Munro
http://www.enterprisedb.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Michael Paquier
michael.paquier@gmail.com
In reply to: Thomas Munro (#17)
1 attachment(s)
Re: Tracking wait event for latches

On Wed, Sep 21, 2016 at 1:03 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

Yeah but that's at run time. I meant you could help developers
discover ASAP if they add a new item to one place but not the other
with a compile time assertion:
const char *
GetWaitEventIdentifier(uint16 eventId)
{
StaticAssertStmt(lengthof(WaitEventNames) == WE_LAST_TYPE + 1,
"WaitEventNames must match WaitEventIdentifiers");
if (eventId > WE_LAST_TYPE)
return "???";
return WaitEventNames[eventId];
}

Ah, OK, good idea. I had AssertStmt in mind, not StaticAssertStmt.

You missed a couple that are hiding inside #ifdef WIN32:

From pgstat.c:
+ EVENT_PGSTAT_MAIN);

From syslogger.c:
+ EVENT_SYSLOGGER_MAIN);

Oops. Fixed those ones and checked the builds on WIN32.
--
Michael

Attachments:

wait-event-set-v6.patchtext/plain; charset=US-ASCII; name=wait-event-set-v6.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..7cecef8 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -496,7 +496,7 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L, WE_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 0776428..5185d65 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,18 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>EventSet</>: The server process is waiting on one or more
+          sockets, a time or an inter-process latch.  The wait happens in a
+          <function>WaitEventSet</>, <productname>PostgreSQL</>'s portable
+          I/O multiplexing abstraction using boolean variables letting a
+          process sleep until it is set.  It supports several type of
+          operations, like postmaster death handling, timeout and socket
+          activity lookup, and are a useful replacement for <function>sleep</>
+          where signal handling matters.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1085,6 +1097,139 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="32"><literal>EventSet</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in the code path of an extension, should be used by custom plugins and modules</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to put new message in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSignal</></entry>
+         <entry>Waiting for signal from another backend.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSleep</></entry>
+         <entry>Waiting for a specific lock.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stream at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for WAL commit during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index cde0ed3..60d5c9c 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -540,7 +540,8 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  WE_PARALLEL_FINISH);
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 2189c22..678b6de 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5827,7 +5827,8 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  WE_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -11376,7 +11377,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+							 wait_time, WE_RECOVERY_WAL_STREAM);
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11539,7 +11540,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L, WE_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..b832d91 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -387,7 +387,7 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_EXECUTE_GATHER);
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index fedb02c..571d4d6 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -419,7 +419,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+								  WE_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..4e16073 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -146,7 +146,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WE_SECURE_READ);
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +248,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WE_SECURE_WRITE);
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index bfe66c6..625b987 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -171,7 +171,7 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0, WE_MQ_PUT_MESSAGE);
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1a92ca1..019075e 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -598,7 +598,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   WE_AUTOVACUUM_MAIN);
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 699c934..7da01df 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -969,7 +969,8 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WE_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1009,8 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			break;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WE_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 1002034..a89d39d 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -345,7 +345,8 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */,
+					   WE_BGWRITER_MAIN);
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -372,7 +373,8 @@ BackgroundWriterMain(void)
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
 						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+						   BgWriterDelay * HIBERNATE_FACTOR,
+						   WE_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index d702a48..e18ef9f 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -556,7 +556,8 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   WE_CHECKPOINTER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..018878c 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,7 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							 timeout * 1000L, WE_ARCHIVER_MAIN);
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 8a2ce91..ca1b129 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3152,6 +3152,9 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_EVENT_SET:
+			event_type = "EventSet";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3193,6 +3196,9 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_EVENT_SET:
+			event_name = GetWaitEventIdentifier(eventId);
+			break;
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3682,7 +3688,7 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
 							   pgStatSock,
-							   -1L);
+							   -1L, WE_PGSTAT_MAIN);
 #else
 
 		/*
@@ -3698,7 +3704,8 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 		WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
 							   pgStatSock,
-							   2 * 1000L /* msec */ );
+							   2 * 1000L /* msec */,
+							   WE_PGSTAT_MAIN);
 #endif
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..c081224 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -424,7 +424,8 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+							   WE_SYSLOGGER_MAIN);
 
 		if (rc & WL_SOCKET_READABLE)
 		{
@@ -475,7 +476,8 @@ SysLoggerMain(int argc, char *argv[])
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | cur_flags,
-						 cur_timeout);
+						 cur_timeout,
+						 WE_SYSLOGGER_MAIN);
 
 		EnterCriticalSection(&sysloggerSection);
 #endif   /* WIN32 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 11ec56a..5b6fa15 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -290,7 +290,8 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   WE_WAL_WRITER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index da9b7a6..82e8d03 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1286,7 +1286,8 @@ throttle(size_t increment)
 		 */
 		wait_result = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+								(long) (sleep / 1000),
+								WE_BASE_BACKUP_THROTTLE);
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index b442d06..f1acb4a 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -258,7 +258,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  WE_SYNC_REP);
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..046a760 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -486,7 +486,8 @@ WalReceiverMain(void)
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   WE_WAL_RECEIVER_MAIN);
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +686,8 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  WE_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c7743da..3cac83b 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,8 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WE_WAL_SENDER_WRITE_DATA);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1273,8 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WE_WAL_SENDER_WAIT_WAL);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1924,7 +1926,8 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  WE_WAL_SENDER_MAIN);
 		}
 	}
 	return;
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..57590ac 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -122,6 +123,46 @@ struct WaitEventSet
 #endif
 };
 
+/*
+ * This must match enum WaitEventIdentifier! The alphabetical order here
+ * is kept in priority and in-sync with the documentation.
+ */
+const char *const WaitEventNames[] = {
+	"ArchiverMain",
+	"AutoVacuumMain",
+	"BaseBackupThrottle",
+	"BgWorkerShutdown",
+	"BgWorkerStartup",
+	"BgWriterHibernate",
+	"BgWriterMain",
+	"CheckpointerMain",
+	"ExecuteGather",
+	"Extension",
+	"MessageQueueInternal",
+	"MessageQueuePutMessage",
+	"MessageQueueReceive",
+	"MessageQueueSend",
+	"ParallelFinish",
+	"PgSleep",
+	"PgStatMain",
+	"ProcSignal",
+	"ProcSleep",
+	"RecoveryApplyDelay",
+	"RecoveryWalAll",
+	"RecoveryWalStream",
+	"SecureRead",
+	"SecureWrite",
+	"SSLOpenServer",
+	"SyncRep",
+	"SysLoggerMain",
+	"WalReceiverMain",
+	"WalReceiverWaitStart",
+	"WalSenderMain",
+	"WalSenderWaitForWAL",
+	"WalSenderWriteData",
+	"WalWriterMain"
+};
+
 #ifndef WIN32
 /* Are we currently in WaitLatch? The signal handler would like to know. */
 static volatile sig_atomic_t waiting = false;
@@ -297,9 +338,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  WaitEventIdentifier eventId)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 eventId);
 }
 
 /*
@@ -316,7 +359,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, WaitEventIdentifier eventId)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +387,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, eventId);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -863,7 +906,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 WaitEventIdentifier eventId)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -872,6 +916,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 
 	Assert(nevents > 0);
 
+	pgstat_report_wait_start(WAIT_EVENT_SET, (uint16) eventId);
+
 	/*
 	 * Initialize timeout if requested.  We must record the current time so
 	 * that we can determine the remaining timeout if interrupted.
@@ -960,6 +1006,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
@@ -1491,6 +1539,22 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 }
 #endif
 
+
+/*
+ * Return an identifier for a Latch depending on an event for statistics
+ * collector.
+ */
+const char *
+GetWaitEventIdentifier(uint16 eventId)
+{
+	StaticAssertStmt(lengthof(WaitEventNames) == WE_LAST_TYPE + 1,
+					 "WaitEventNames must match WaitEventIdentifiers");
+	if (eventId > WE_LAST_TYPE)
+		return "???";
+	return WaitEventNames[eventId];
+}
+
+
 /*
  * SetLatch uses SIGUSR1 to wake up the process waiting on the latch.
  *
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..c47cf2b 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -894,7 +894,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_MQ_SEND);
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +991,7 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_MQ_RECEIVE);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1090,7 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_MQ_INTERNAL);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 33e7023..23deeb6 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -1212,7 +1212,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_PROC_SLEEP);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1724,7 +1724,7 @@ CheckDeadLockAlert(void)
 void
 ProcWaitForSignal(void)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_PROC_SIGNAL);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..a814a06 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -560,7 +560,7 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms, WE_PG_SLEEP);
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index dc3320d..bca16e4 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -721,7 +721,8 @@ typedef enum WaitClass
 	WAIT_LWLOCK_NAMED,
 	WAIT_LWLOCK_TRANCHE,
 	WAIT_LOCK,
-	WAIT_BUFFER_PIN
+	WAIT_BUFFER_PIN,
+	WAIT_EVENT_SET
 }	WaitClass;
 
 
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..45859bb 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -135,6 +135,51 @@ typedef struct WaitEvent
 	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
 } WaitEvent;
 
+/*
+ * List of latch identifiers used when reporting activity to statistics
+ * collector. Up to 256 different WaitEventIdentifier can be handled.
+ */
+typedef enum WaitEventIdentifier
+{
+	WE_ARCHIVER_MAIN,
+	WE_AUTOVACUUM_MAIN,
+	WE_BASE_BACKUP_THROTTLE,
+	WE_BGWORKER_SHUTDOWN,
+	WE_BGWORKER_STARTUP,
+	WE_BGWRITER_HIBERNATE,
+	WE_BGWRITER_MAIN,
+	WE_CHECKPOINTER_MAIN,
+	WE_EXECUTE_GATHER,
+	WE_EXTENSION,
+	WE_MQ_INTERNAL,
+	WE_MQ_PUT_MESSAGE,
+	WE_MQ_RECEIVE,
+	WE_MQ_SEND,
+	WE_PARALLEL_FINISH,
+	WE_PG_SLEEP,
+	WE_PGSTAT_MAIN,
+	WE_PROC_SIGNAL,
+	WE_PROC_SLEEP,
+	WE_RECOVERY_APPLY_DELAY,
+	WE_RECOVERY_WAL_ALL,
+	WE_RECOVERY_WAL_STREAM,
+	WE_SECURE_READ,
+	WE_SECURE_WRITE,
+	WE_SSL_OPEN_SERVER,
+	WE_SYNC_REP,
+	WE_SYSLOGGER_MAIN,
+	WE_WAL_RECEIVER_MAIN,
+	WE_WAL_RECEIVER_WAIT_START,
+	WE_WAL_SENDER_MAIN,
+	WE_WAL_SENDER_WAIT_WAL,
+	WE_WAL_SENDER_WRITE_DATA,
+	WE_WAL_WRITER_MAIN
+} WaitEventIdentifier;
+
+#define WE_LAST_TYPE WE_WAL_WRITER_MAIN
+
+extern const char *const WaitEventName[];
+
 /* forward declaration to avoid exposing latch.c implementation details */
 typedef struct WaitEventSet WaitEventSet;
 
@@ -155,10 +200,14 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
+				  WaitEvent *occurred_events, int nevents,
+				  WaitEventIdentifier eventId);
+extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+				  WaitEventIdentifier eventId);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, WaitEventIdentifier eventId);
+extern const char *GetWaitEventIdentifier(uint16 eventId);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 143df4e..adebfc8 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -279,7 +279,7 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_EXTENSION);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index dd34bc7..22145db 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -230,7 +230,7 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_EXTENSION);
 			ResetLatch(MyLatch);
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 314e371..aa7f7f9 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -227,7 +227,8 @@ worker_spi_main(Datum main_arg)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   worker_spi_naptime * 1000L);
+					   worker_spi_naptime * 1000L,
+					   WE_EXTENSION);
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#19Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#15)
Re: Tracking wait event for latches

On Tue, Sep 20, 2016 at 7:13 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

This paragraph seems a bit confused. I suggest something more like
this: "The server process is waiting for one or more sockets, a timer
or an interprocess latch. The wait happens in a WaitEventSet,
<productname>PostgreSQL</>'s portable IO multiplexing abstraction."

I'm worried we're exposing an awful lot of internal detail here.
Moreover, it's pretty confusing that we have this general concept of
wait events in pg_stat_activity, and then here the specific type of
wait event we're waiting for is the ... wait event kind. Uh, what?

I have to admit that I like the individual event names quite a bit,
and I think the detail will be useful to users. But I wonder if
there's a better way to describe the class of events that we're
talking about that's not so dependent on internal data structures.
Maybe we could divide these waits into a couple of categories - e.g.
"Socket", "Timeout", "Process" - and then divide these detailed wait
events among those classes.

The "SecureRead" and "SecureWrite" wait events are going to be
confusing, because the connection isn't necessarily secure. I think
those should be called "ClientRead" and "ClientWrite".
Comprehensibility is more important than absolute consistency with the
C function names.

Another thing to think about is that there's no way to actually see
wait event information for a number of the processes which this patch
instruments, because they don't appear in pg_stat_activity.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#19)
Re: Tracking wait event for latches

On Wed, Sep 21, 2016 at 10:23 PM, Robert Haas <robertmhaas@gmail.com> wrote:

I have to admit that I like the individual event names quite a bit,
and I think the detail will be useful to users. But I wonder if
there's a better way to describe the class of events that we're
talking about that's not so dependent on internal data structures.
Maybe we could divide these waits into a couple of categories - e.g.
"Socket", "Timeout", "Process" - and then divide these detailed wait
events among those classes.

pgstat.h is mentioning that there is 1 byte still free. I did not
notice that until a couple of minutes ago. There are 2 bytes used for
the event ID, and 1 byte for the class ID, but there are 4 bytes
available. Perhaps we could use this extra byte to store this extra
status information, then use it for WaitEventSet to build up a string
that will be stored in classId field? For example if a process is
waiting on a socket and a timeout, we'd write "Socket,Timeout" as a
text field.

The "SecureRead" and "SecureWrite" wait events are going to be
confusing, because the connection isn't necessarily secure. I think
those should be called "ClientRead" and "ClientWrite".
Comprehensibility is more important than absolute consistency with the
C function names.

Noted.

Another thing to think about is that there's no way to actually see
wait event information for a number of the processes which this patch
instruments, because they don't appear in pg_stat_activity.

We could create a new system to track the activity of system-related
processes, for example pg_stat_system_activity, or pg_system_activity,
and list all the processes that are not counted in max_connections...
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#20)
Re: Tracking wait event for latches

On Wed, Sep 21, 2016 at 10:02 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Wed, Sep 21, 2016 at 10:23 PM, Robert Haas <robertmhaas@gmail.com> wrote:

I have to admit that I like the individual event names quite a bit,
and I think the detail will be useful to users. But I wonder if
there's a better way to describe the class of events that we're
talking about that's not so dependent on internal data structures.
Maybe we could divide these waits into a couple of categories - e.g.
"Socket", "Timeout", "Process" - and then divide these detailed wait
events among those classes.

pgstat.h is mentioning that there is 1 byte still free. I did not
notice that until a couple of minutes ago. There are 2 bytes used for
the event ID, and 1 byte for the class ID, but there are 4 bytes
available. Perhaps we could use this extra byte to store this extra
status information, then use it for WaitEventSet to build up a string
that will be stored in classId field? For example if a process is
waiting on a socket and a timeout, we'd write "Socket,Timeout" as a
text field.

No, that's not what I want to do. I think we should categorize the
events administratively by their main purpose, rather than
technologically by what we're waiting for.

Another thing to think about is that there's no way to actually see
wait event information for a number of the processes which this patch
instruments, because they don't appear in pg_stat_activity.

We could create a new system to track the activity of system-related
processes, for example pg_stat_system_activity, or pg_system_activity,
and list all the processes that are not counted in max_connections...

Yes. Or we could decide to include everything in pg_stat_activity. I
think those are the two reasonable options.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#22Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#21)
Re: Tracking wait event for latches

On Wed, Sep 21, 2016 at 11:18 PM, Robert Haas <robertmhaas@gmail.com> wrote:

No, that's not what I want to do. I think we should categorize the
events administratively by their main purpose, rather than
technologically by what we're waiting for.

So we'd just have three class IDs instead of one? Well why not.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#22)
Re: Tracking wait event for latches

On Wed, Sep 21, 2016 at 10:23 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Wed, Sep 21, 2016 at 11:18 PM, Robert Haas <robertmhaas@gmail.com> wrote:

No, that's not what I want to do. I think we should categorize the
events administratively by their main purpose, rather than
technologically by what we're waiting for.

So we'd just have three class IDs instead of one? Well why not.

Yeah, or, I mean, it doesn't have to be three precisely, but I'd like
to try to avoid exposing the users to the fact that we have an
internal data structure called a WaitEventSet and instead classify by
function.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Robert Haas (#19)
Re: Tracking wait event for latches

On Thu, Sep 22, 2016 at 1:23 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Sep 20, 2016 at 7:13 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

This paragraph seems a bit confused. I suggest something more like
this: "The server process is waiting for one or more sockets, a timer
or an interprocess latch. The wait happens in a WaitEventSet,
<productname>PostgreSQL</>'s portable IO multiplexing abstraction."

I'm worried we're exposing an awful lot of internal detail here.

Ok, maybe it's not a good idea to mention WaitEventSet in the manual.
I was trying to keep the same basic structure of text as Michael
proposed, but fix the bit that implied that waiting happens "in a
latch", even when no latch is involved. Perhaps only the first
sentence is necessary. This will all become much clearer if there is
a follow-up patch that shows strings like "Socket", "Socket|Latch",
"Latch" instead of a general catch-all wait_event_type of "EventSet",
as discussed.

Moreover, it's pretty confusing that we have this general concept of
wait events in pg_stat_activity, and then here the specific type of
wait event we're waiting for is the ... wait event kind. Uh, what?

Yeah, that is confusing. It comes about because of the coincidence
that pg_stat_activity finished up with a wait_event column, and our IO
multiplexing abstraction finished up with the name WaitEventSet.
<stuck-record-mode>We could instead call these new things "wait
points", because, well, erm, they name points in the code at which we
wait. They don't name events (they just happen to use the
WaitEventSet mechanism, which itself does involve events: but those
events are things like "hey, this socket is now ready for
writing").</>

I have to admit that I like the individual event names quite a bit,
and I think the detail will be useful to users. But I wonder if
there's a better way to describe the class of events that we're
talking about that's not so dependent on internal data structures.
Maybe we could divide these waits into a couple of categories - e.g.
"Socket", "Timeout", "Process" - and then divide these detailed wait
events among those classes.

Well we already discussed a couple of different ways to get "Socket",
"Latch", "Socket|Latch", ... or something like that into the
wait_event_type column or new columns. Wouldn't that be better, and
automatically fall out of the code rather than needing human curated
categories? Although Michael suggested that that should be done as a
separate patch on top of the basic feature.

The "SecureRead" and "SecureWrite" wait events are going to be
confusing, because the connection isn't necessarily secure. I think
those should be called "ClientRead" and "ClientWrite".
Comprehensibility is more important than absolute consistency with the
C function names.

Devil's advocate mode: Then why not improve those function names?
Keeping the wait point names systematically in sync with the actual
code makes things super simple and avoids a whole decision process and
discussion to create new user-friendly obfuscation every time anyone
introduces a new wait point. This is fundamentally an introspection
mechanism allowing expert users to shine a light on the engine and see
what's going on inside it, so I don't see what's wrong with being
straight up about what is actually going on and using the names for
parts of our code that we already have. If that leads us to improve
some function names I'm not sure why that's a bad thing.

Obviously this gets complicated by the existence of static functions
whose names are ambiguous and lack context, and multiple wait points
in a single function. Previously I've suggested a hierarchical
arrangement for these names which might help with that. Imagine names
like: executor.Hash.<function> (reported by a background worker
executing a hypothetical parallel hash join),
executor.Hash.<function>.<something> to disambiguate multiple wait
points in one function, walsender.<function> etc. That way we could
have a tidy curated meaningful naming scheme based on modules, but a
no-brainer systematic way to name the most numerous leaf bits of that
hierarchy. Just an idea...

Another thing to think about is that there's no way to actually see
wait event information for a number of the processes which this patch
instruments, because they don't appear in pg_stat_activity.

Good point. Perhaps there could be another extended view, or system
process view, or some other mechanism. That could be material for a
separate patch?

--
Thomas Munro
http://www.enterprisedb.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Michael Paquier
michael.paquier@gmail.com
In reply to: Thomas Munro (#24)
Re: Tracking wait event for latches

On Thu, Sep 22, 2016 at 6:49 AM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

On Thu, Sep 22, 2016 at 1:23 AM, Robert Haas <robertmhaas@gmail.com> wrote:

Moreover, it's pretty confusing that we have this general concept of
wait events in pg_stat_activity, and then here the specific type of
wait event we're waiting for is the ... wait event kind. Uh, what?

Yeah, that is confusing. It comes about because of the coincidence
that pg_stat_activity finished up with a wait_event column, and our IO
multiplexing abstraction finished up with the name WaitEventSet.
<stuck-record-mode>We could instead call these new things "wait
points", because, well, erm, they name points in the code at which we
wait. They don't name events (they just happen to use the
WaitEventSet mechanism, which itself does involve events: but those
events are things like "hey, this socket is now ready for
writing").</>

What about trying first to come up with a class name generic enough
that would cover the set of events we are trying to cover. One idea is
to simply call the class "Process" ("Point", "Poll"), and mention in
the docs that this can wait for a socket, a timeout, postmaster death
or another process to tell it to move on. The idea is to come with a
name generic enough. In the first patch we don't detail much what a
process is waiting for at a given point, like what has been submitted
does. This would still be helpful for the user, because it would be
possible to look back at the code and guess where this is waiting.

Then comes the interesting bits: I don't think that it is a good idea
to associate only one event for a waiting point in the system views.
Say that at a waiting point WaitLatch is called with
WL_POSTMASTER_DEATH and WL_TIMEOUT, I'd rather let the user know that
both of them have been set up and not choose just one of them using
some hierarchy. But I rather think that we should list all of them
depending on the WL_* flags set up by the caller. Here comes a second
patch: let's use the last byte of the wait events and add this new
text[] column in pg_stat_activity, say wait_details. So for a waiting
point it would be possible to tell to the user that it is waiting on
'{socket,timeout,postmaster_death}' for example.

Then comes a third patch: addition of a system view to list all the
activity happening for processes that are not dependent on
max_connections. pg_stat_activity has the advantage, as Tom mentioned
a couple of days ago, that one can just run count(*) on it to estimate
the number of connections left. I think this is quite helpful. Or we
could just add a column in pg_stat_activity to mark a process type: is
it a system process or not? This deserves its own discussion for sure.

Patches 2 and 3 are independent things. Patch 1 is a requirement for 2 and 3.

Another thing to think about is that there's no way to actually see
wait event information for a number of the processes which this patch
instruments, because they don't appear in pg_stat_activity.

Good point. Perhaps there could be another extended view, or system
process view, or some other mechanism. That could be material for a
separate patch?

Definitely a separate patch..
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#26Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#24)
Re: Tracking wait event for latches

On Wed, Sep 21, 2016 at 5:49 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

Moreover, it's pretty confusing that we have this general concept of
wait events in pg_stat_activity, and then here the specific type of
wait event we're waiting for is the ... wait event kind. Uh, what?

Yeah, that is confusing. It comes about because of the coincidence
that pg_stat_activity finished up with a wait_event column, and our IO
multiplexing abstraction finished up with the name WaitEventSet.
<stuck-record-mode>We could instead call these new things "wait
points", because, well, erm, they name points in the code at which we
wait. They don't name events (they just happen to use the
WaitEventSet mechanism, which itself does involve events: but those
events are things like "hey, this socket is now ready for
writing").</>

Sure, we could do that, but it means breaking backward compatibility
for pg_stat_activity *again*. I'd be willing to do it but I don't
think I'd win any popularity contests.

I have to admit that I like the individual event names quite a bit,
and I think the detail will be useful to users. But I wonder if
there's a better way to describe the class of events that we're
talking about that's not so dependent on internal data structures.
Maybe we could divide these waits into a couple of categories - e.g.
"Socket", "Timeout", "Process" - and then divide these detailed wait
events among those classes.

Well we already discussed a couple of different ways to get "Socket",
"Latch", "Socket|Latch", ... or something like that into the
wait_event_type column or new columns. Wouldn't that be better, and
automatically fall out of the code rather than needing human curated
categories? Although Michael suggested that that should be done as a
separate patch on top of the basic feature.

I think making that a separate patch is just punting the decision down
the field to a day that may never come. Let's try to agree on
something that we can all live with and implement it now. In terms of
avoiding human-curated categories, I basically see two options:

1. Find a name that is sufficiently generic that it covers everything
that might reach WaitEventSetWait either now or in the future when it
might wait for even more kinds of things than it does now. For
example, we could call it "Stuff" or "Thing". Less tongue-in-cheek
suggestions are welcome, but it's hard to come up with something that
is sufficiently-generic without being tautological. "Event" is an
example of a name which is general enough to encompass everything but
also stupid: the column is called "wait_event" so everything that
appears in it is an event by definition.

2. Classify the events that fall into this category by some rigid
principle based on the types of things being awaited. For example, we
could decide that if any sockets are awaited, the event class will be
"Client" if it is connected to a user and "IPC" for auxiliary
processes.

For myself, I don't see any real problem with using humans to classify
things; that's pretty much the one thing humans are much better at
than computers, so we might as well take advantage of it. I think
that it's useful to try to group together types of waits which the
user will see as logically related to each other, even if that
involves applying some human judgement that might someday lead to some
discussion about what the best categorization for some new thing is.
PostgreSQL is intended to be used by humans, and avoiding discussions
(or even arguments) on pgsql-hackers shouldn't outrank usability on
the list of concerns.

So, I tried to classify these. Here's what I came up with.

Activity: ArchiverMain, AutoVacuumMain, BgWriterMain,
BgWriterHibernate, CheckpointerMain, PgStatMain, RecoveryWalAll,
RecoveryWalStream, SysLoggerMain, WalReceiverMain, WalWriterMain

Client: SecureRead, SecureWrite, SSLOpenServer, WalSenderMain,
WalSenderWaitForWAL, WalSenderWriteData, WalReceiverWaitStart

Timeout: BaseBackupThrottle, PgSleep, RecoveryApplyDelay

IPC: BgWorkerShutdown, BgWorkerStartup, ExecuteGather,
MessageQueueInternal, MessageQueuePutMessage, MessageQueueReceive,
MessageQueueSend, ParallelFinish, ProcSignal, ProcSleep, SyncRep

Extension: Extension

I classified all of the main loop waits as waiting for activity; all
of those are background processes that are waiting for something to
happen and are more or less happy to sleep forever until it does. I
also included the RecoveryWalAll and RecoveryWalStream events in
there; those don't have the sort of "main loop" flavor of the others
but they are happy to wait more or less indefinitely for something to
occur. Likewise, it was pretty easy to find all of the events that
were waiting for client I/O, and I grouped those all under "Client".
A few of the remaining wait events seemed like they were clearly
waiting for a particular timeout to expire, so I gave those their own
"Timeout" category.

I believe these categorizations are actually useful for users. For
example, you might want to see all of the waits in the system but
exclude the "Client", "Activity", and "Timeout" categories because
those are things that aren't signs of a problem. A "Timeout" wait is
one that you explicitly requested, a "Client" wait isn't the server's
fault, and an "Activity" wait just means nothing is happening. In
contrast, a "Lock" or "LWLock" or "IPC" wait shows that something is
actually delaying work that we'd ideally prefer to have get done
sooner.

I grouped the rest of this stuff as "IPC" because all of these events
are cases where one server process is waiting for another server
processes . That could be further subdivided, of course: most of
those events are only going to occur in relation to parallel query,
but I didn't want to group it that way explicitly because both
background workers and shm_mq have other potential uses. ProcSignal
and ProcSleep are related to heavyweight locks and SyncRep is of
course related to synchronous replication. But they're all related
in that one server process is waiting for another server process to
tell it that a certain state has been reached, so IPC seems like a
good categorization.

Finally, extensions got their own category in this taxonomy, though I
wonder if it would be better to instead have
Activity/ExtensionActivity, Client/ExtensionClient,
Timeout/ExtensionTimeout, and IPC/ExtensionIPC instead of making it a
separate toplevel category.

To me, this seems like a pretty solid toplevel categorization and a
lot more useful than just throwing all of these in one bucket and
saying "good luck". I'm not super-attached to the details but, again,
I think it's worth trying to bin things in a way that will be useful.

The "SecureRead" and "SecureWrite" wait events are going to be
confusing, because the connection isn't necessarily secure. I think
those should be called "ClientRead" and "ClientWrite".
Comprehensibility is more important than absolute consistency with the
C function names.

Devil's advocate mode: Then why not improve those function names?

That'd be fine.

Obviously this gets complicated by the existence of static functions
whose names are ambiguous and lack context, and multiple wait points
in a single function. Previously I've suggested a hierarchical
arrangement for these names which might help with that. Imagine names
like: executor.Hash.<function> (reported by a background worker
executing a hypothetical parallel hash join),
executor.Hash.<function>.<something> to disambiguate multiple wait
points in one function, walsender.<function> etc. That way we could
have a tidy curated meaningful naming scheme based on modules, but a
no-brainer systematic way to name the most numerous leaf bits of that
hierarchy. Just an idea...

Considering we only have a few dozen of those, this feels like it
might be overkill to me, and I suspect we'll end up finding that it's
a bit harder to make it consistent and useful than one might hope. I
am basically happy with the way Michael named them, but that's not to
say I could never be happy with anything else.

Another thing to think about is that there's no way to actually see
wait event information for a number of the processes which this patch
instruments, because they don't appear in pg_stat_activity.

Good point. Perhaps there could be another extended view, or system
process view, or some other mechanism. That could be material for a
separate patch?

I agree.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#25)
Re: Tracking wait event for latches

On Thu, Sep 22, 2016 at 1:52 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Then comes the interesting bits: I don't think that it is a good idea
to associate only one event for a waiting point in the system views.
Say that at a waiting point WaitLatch is called with
WL_POSTMASTER_DEATH and WL_TIMEOUT, I'd rather let the user know that
both of them have been set up and not choose just one of them using
some hierarchy. But I rather think that we should list all of them
depending on the WL_* flags set up by the caller. Here comes a second
patch: let's use the last byte of the wait events and add this new
text[] column in pg_stat_activity, say wait_details. So for a waiting
point it would be possible to tell to the user that it is waiting on
'{socket,timeout,postmaster_death}' for example.

I think this is focusing on the wrong thing. What we need here is not
infinite detail on exactly what is going on under the hood but useful
classification rules. For example, as I said in my email to Thomas,
being able to exclude all cases of waiting for client I/O is useful
because those aren't signs of a problem in the way that LWLock or Lock
waits are. It's better for us to provide that classification using
the existing system than for users to have to work out exactly which
things ought to be excluded on the basis of specific event names.

On the other hand, I submit that knowing which waits will be
interrupted by the death of the postmaster and which will not doesn't
add much. In fact, I think that's almost an anti-feature, because it
will encourage users to care about details that are very rarely
relevant.

Then comes a third patch: addition of a system view to list all the
activity happening for processes that are not dependent on
max_connections. pg_stat_activity has the advantage, as Tom mentioned
a couple of days ago, that one can just run count(*) on it to estimate
the number of connections left. I think this is quite helpful. Or we
could just add a column in pg_stat_activity to mark a process type: is
it a system process or not? This deserves its own discussion for sure.

Sure.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#28Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Robert Haas (#26)
Re: Tracking wait event for latches

On Fri, Sep 23, 2016 at 1:49 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Sep 21, 2016 at 5:49 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

Moreover, it's pretty confusing that we have this general concept of
wait events in pg_stat_activity, and then here the specific type of
wait event we're waiting for is the ... wait event kind. Uh, what?

Yeah, that is confusing. It comes about because of the coincidence
that pg_stat_activity finished up with a wait_event column, and our IO
multiplexing abstraction finished up with the name WaitEventSet.
<stuck-record-mode>We could instead call these new things "wait
points", because, well, erm, they name points in the code at which we
wait. They don't name events (they just happen to use the
WaitEventSet mechanism, which itself does involve events: but those
events are things like "hey, this socket is now ready for
writing").</>

Sure, we could do that, but it means breaking backward compatibility
for pg_stat_activity *again*. I'd be willing to do it but I don't
think I'd win any popularity contests.

I didn't mean changing the column headings in pg_stat_activity. I
meant that the enum called WaitEventIdentifier in Michael's v5 patch
should be called WaitPointIdentifier, and if we go with a single name
to appear in the wait_event_type column then it could be "WaitPoint".
But I would also prefer to see something more informative in that
column, as discussed below (and upthread).

Well we already discussed a couple of different ways to get "Socket",
"Latch", "Socket|Latch", ... or something like that into the
wait_event_type column or new columns. Wouldn't that be better, and
automatically fall out of the code rather than needing human curated
categories? Although Michael suggested that that should be done as a
separate patch on top of the basic feature.

I think making that a separate patch is just punting the decision down
the field to a day that may never come. Let's try to agree on
something that we can all live with and implement it now. In terms of
avoiding human-curated categories, I basically see two options:

1. Find a name that is sufficiently generic that it covers everything
that might reach WaitEventSetWait either now or in the future when it
might wait for even more kinds of things than it does now. For
example, we could call it "Stuff" or "Thing". Less tongue-in-cheek
suggestions are welcome, but it's hard to come up with something that
is sufficiently-generic without being tautological. "Event" is an
example of a name which is general enough to encompass everything but
also stupid: the column is called "wait_event" so everything that
appears in it is an event by definition.

2. Classify the events that fall into this category by some rigid
principle based on the types of things being awaited. For example, we
could decide that if any sockets are awaited, the event class will be
"Client" if it is connected to a user and "IPC" for auxiliary
processes.

[...]
occur. Likewise, it was pretty easy to find all of the events that
were waiting for client I/O, and I grouped those all under "Client".
A few of the remaining wait events seemed like they were clearly
waiting for a particular timeout to expire, so I gave those their own
"Timeout" category.

Interesting. OK, I agree that it'd be useful to show that we're
waiting because there's nothing happening, or waiting because the user
asked us to sleep, or waiting on IO, or waiting for an IPC response
because something is happening, and that higher level information is
difficult/impossible to extract automatically from the WaitEventSet.

I understand that "Activity" is the category of wait points that are
waiting for activity, but I wonder if it might be clearer to users if
that were called "Idle", because it's the category of idle waits
caused by non-activity.

Why is WalSenderMain not in that category alongside WalReceiverMain?
Hmm, actually it's kind of a tricky one: whether it's really idle or
waiting for IO depends. It's always ready to wait for clients to send
messages, but I'd say that's part of its "idle" behaviour. But it's
sometimes waiting for the socket to be writable: if
(pq_is_send_pending()) wakeEvents |= WL_SOCKET_WRITABLE, and that's
when it's definitely not idle, it's actively trying to feed WAL down
the pipe. Do we want to get into dynamic categories depending on
conditions like that?

I was thinking about suggesting a category "Replication" to cover the
waits for client IO relating to replication, as opposed to client IO
waits relating to regular user connections. Then you could put sync
rep into that category instead of IPC, even though technically it is
waiting for IPC from walsender process(es), on the basis that it's
more newsworthy to a DBA that it's really waiting for a remote replica
to respond. But it's probably pretty clear what's going on from the
the wait point names, so maybe it's not worth a category. Thoughts?

WalSenderWaitForWAL is waiting for both IPC and Client, but if you
have to pick one isn't IPC the main thing it's waiting for? It'll
stop waiting when it gets IPC telling it about flushed location
reaching some point. It's multiplexing client IO processing sort of
"incidentally" while it does so.

WalReceiverWaitStart is waiting for IPC, not Client.

Obviously this gets complicated by the existence of static functions
whose names are ambiguous and lack context, and multiple wait points
in a single function. Previously I've suggested a hierarchical
arrangement for these names which might help with that. Imagine names
like: executor.Hash.<function> (reported by a background worker
executing a hypothetical parallel hash join),
executor.Hash.<function>.<something> to disambiguate multiple wait
points in one function, walsender.<function> etc. That way we could
have a tidy curated meaningful naming scheme based on modules, but a
no-brainer systematic way to name the most numerous leaf bits of that
hierarchy. Just an idea...

Considering we only have a few dozen of those, this feels like it
might be overkill to me, and I suspect we'll end up finding that it's
a bit harder to make it consistent and useful than one might hope. I
am basically happy with the way Michael named them, but that's not to
say I could never be happy with anything else.

Yeah, it's fine as it is.

I do suspect that the set of wait points will grow quite a bit as we
develop more parallel stuff though. For example, I have been working
on a patch that adds several more wait points, indirectly via
condition variables (using your patch). Actually in my case it's
BarrierWait -> ConditionVariableWait -> WaitEventSetWait. I propose
that these higher level wait primitives should support passing a wait
point identifier through to WaitEventSetWait. Some of my patch's wait
points are in the same function and some are in static functions, so
I'll finish up making up various affixes to disambiguate, and then I
might be tempted to include some separator characters... I am also
aware of three other hackers who are working on patches that also make
use of condition variables, so (if you agree that condition variable
waits are wait points) they'll be adding more too. That said, ad-hoc
name construction is fine and there is no reason we couldn't revise
things if names start to look messy when concrete patches that add
more names emerge.

--
Thomas Munro
http://www.enterprisedb.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#29Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#28)
Re: Tracking wait event for latches

On Thu, Sep 22, 2016 at 7:10 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

Interesting. OK, I agree that it'd be useful to show that we're
waiting because there's nothing happening, or waiting because the user
asked us to sleep, or waiting on IO, or waiting for an IPC response
because something is happening, and that higher level information is
difficult/impossible to extract automatically from the WaitEventSet.

Cool. :-)

I understand that "Activity" is the category of wait points that are
waiting for activity, but I wonder if it might be clearer to users if
that were called "Idle", because it's the category of idle waits
caused by non-activity.

I thought about that but figured it would be better to consistently
state the thing *for which* we were waiting. We wait FOR a client or
a timeout or activity. We do not wait FOR idle; we wait to be NOT
idle.

Why is WalSenderMain not in that category alongside WalReceiverMain?
Hmm, actually it's kind of a tricky one: whether it's really idle or
waiting for IO depends. It's always ready to wait for clients to send
messages, but I'd say that's part of its "idle" behaviour. But it's
sometimes waiting for the socket to be writable: if
(pq_is_send_pending()) wakeEvents |= WL_SOCKET_WRITABLE, and that's
when it's definitely not idle, it's actively trying to feed WAL down
the pipe. Do we want to get into dynamic categories depending on
conditions like that?

I suspect that's overkill. I don't want wait-point-naming to make
programming the system noticeably more difficult, so I think it's fine
to pick a categorization of what we think the typical case will be and
call it good. If we try that and people find it's a nuisance, we can
fix it then. In the case of WAL sender, I assume it will normally be
waiting for more WAL to be generated; whereas in the case of WAL
receiver, I assume it will normally be waiting for more WAL to be
received from the remote side. The reverse cases are possible: the
sender could be waiting for the socket buffer to drain so it can push
more WAL onto the wire, and the receiver could likewise be waiting for
buffer space to push out feedback messages. But probably mostly not.
At least for a first cut, I'd be inclined to handle this fuzziness by
putting weasel-words in the documentation rather than by trying to
make the reporting 100% perfectly accurate.

I was thinking about suggesting a category "Replication" to cover the
waits for client IO relating to replication, as opposed to client IO
waits relating to regular user connections. Then you could put sync
rep into that category instead of IPC, even though technically it is
waiting for IPC from walsender process(es), on the basis that it's
more newsworthy to a DBA that it's really waiting for a remote replica
to respond. But it's probably pretty clear what's going on from the
the wait point names, so maybe it's not worth a category. Thoughts?

I thought about a replication category but either it will only have
SyncRep in it, which is odd, or it will pull in other things that
otherwise fit nicely into the Activity category, and then that
boundaries of all the categories become mushy: is the subsystem that
causes the wait that we are trying to document, or the kind of thing
for which we are waiting?

I do suspect that the set of wait points will grow quite a bit as we
develop more parallel stuff though. For example, I have been working
on a patch that adds several more wait points, indirectly via
condition variables (using your patch). Actually in my case it's
BarrierWait -> ConditionVariableWait -> WaitEventSetWait. I propose
that these higher level wait primitives should support passing a wait
point identifier through to WaitEventSetWait.

+1.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#26)
Re: Tracking wait event for latches

On Thu, Sep 22, 2016 at 7:19 PM, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Sep 21, 2016 at 5:49 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

So, I tried to classify these. Here's what I came up with.

Activity: ArchiverMain, AutoVacuumMain, BgWriterMain,
BgWriterHibernate, CheckpointerMain, PgStatMain, RecoveryWalAll,
RecoveryWalStream, SysLoggerMain, WalReceiverMain, WalWriterMain

Client: SecureRead, SecureWrite, SSLOpenServer, WalSenderMain,
WalSenderWaitForWAL, WalSenderWriteData, WalReceiverWaitStart

Timeout: BaseBackupThrottle, PgSleep, RecoveryApplyDelay

IPC: BgWorkerShutdown, BgWorkerStartup, ExecuteGather,
MessageQueueInternal, MessageQueuePutMessage, MessageQueueReceive,
MessageQueueSend, ParallelFinish, ProcSignal, ProcSleep, SyncRep

Extension: Extension

We already call lwlock waits from an extension as "extension", so I
think just naming this an Extension might create some confusion.

I classified all of the main loop waits as waiting for activity; all
of those are background processes that are waiting for something to
happen and are more or less happy to sleep forever until it does. I
also included the RecoveryWalAll and RecoveryWalStream events in
there; those don't have the sort of "main loop" flavor of the others
but they are happy to wait more or less indefinitely for something to
occur. Likewise, it was pretty easy to find all of the events that
were waiting for client I/O, and I grouped those all under "Client".
A few of the remaining wait events seemed like they were clearly
waiting for a particular timeout to expire, so I gave those their own
"Timeout" category.

I believe these categorizations are actually useful for users. For
example, you might want to see all of the waits in the system but
exclude the "Client", "Activity", and "Timeout" categories because
those are things that aren't signs of a problem. A "Timeout" wait is
one that you explicitly requested, a "Client" wait isn't the server's
fault, and an "Activity" wait just means nothing is happening. In
contrast, a "Lock" or "LWLock" or "IPC" wait shows that something is
actually delaying work that we'd ideally prefer to have get done
sooner.

I grouped the rest of this stuff as "IPC" because all of these events
are cases where one server process is waiting for another server
processes . That could be further subdivided, of course: most of
those events are only going to occur in relation to parallel query,
but I didn't want to group it that way explicitly because both
background workers and shm_mq have other potential uses. ProcSignal
and ProcSleep are related to heavyweight locks and SyncRep is of
course related to synchronous replication. But they're all related
in that one server process is waiting for another server process to
tell it that a certain state has been reached, so IPC seems like a
good categorization.

Finally, extensions got their own category in this taxonomy, though I
wonder if it would be better to instead have
Activity/ExtensionActivity, Client/ExtensionClient,
Timeout/ExtensionTimeout, and IPC/ExtensionIPC instead of making it a
separate toplevel category.

+1. It can avoid confusion.

To me, this seems like a pretty solid toplevel categorization and a
lot more useful than just throwing all of these in one bucket and
saying "good luck".

Agreed. This categorisation is very good and can help patch author to proceed.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#31Amit Kapila
amit.kapila16@gmail.com
In reply to: Robert Haas (#29)
Re: Tracking wait event for latches

On Fri, Sep 23, 2016 at 7:02 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Sep 22, 2016 at 7:10 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

I was thinking about suggesting a category "Replication" to cover the
waits for client IO relating to replication, as opposed to client IO
waits relating to regular user connections. Then you could put sync
rep into that category instead of IPC, even though technically it is
waiting for IPC from walsender process(es), on the basis that it's
more newsworthy to a DBA that it's really waiting for a remote replica
to respond. But it's probably pretty clear what's going on from the
the wait point names, so maybe it's not worth a category. Thoughts?

I thought about a replication category but either it will only have
SyncRep in it, which is odd, or it will pull in other things that
otherwise fit nicely into the Activity category, and then that
boundaries of all the categories become mushy: is the subsystem that
causes the wait that we are trying to document, or the kind of thing
for which we are waiting?

I also think that it can add some confusion in defining boundaries and
also might not be consistent with current way we have defined the
waits, however there is some value in using subsystem which is that
user can identify the bottlenecks with ease. I think this applies to
both Replication and Parallel Query.

--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#32Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#29)
1 attachment(s)
Re: Tracking wait event for latches

On Fri, Sep 23, 2016 at 10:32 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Thu, Sep 22, 2016 at 7:10 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

I was thinking about suggesting a category "Replication" to cover the
waits for client IO relating to replication, as opposed to client IO
waits relating to regular user connections. Then you could put sync
rep into that category instead of IPC, even though technically it is
waiting for IPC from walsender process(es), on the basis that it's
more newsworthy to a DBA that it's really waiting for a remote replica
to respond. But it's probably pretty clear what's going on from the
the wait point names, so maybe it's not worth a category. Thoughts?

I thought about a replication category but either it will only have
SyncRep in it, which is odd, or it will pull in other things that
otherwise fit nicely into the Activity category, and then that
boundaries of all the categories become mushy: is the subsystem that
causes the wait that we are trying to document, or the kind of thing
for which we are waiting?

Using category IPC for SyncRep looks fine to me.

I do suspect that the set of wait points will grow quite a bit as we
develop more parallel stuff though. For example, I have been working
on a patch that adds several more wait points, indirectly via
condition variables (using your patch). Actually in my case it's
BarrierWait -> ConditionVariableWait -> WaitEventSetWait. I propose
that these higher level wait primitives should support passing a wait
point identifier through to WaitEventSetWait.

+1.

As much as I suspect that inclusion of pgstat.h will become more and
more usual to allow more code paths to access to a given WaitClass.

After digesting all the comments given, I have produced the patch
attached that uses the following categories:
+const char *const WaitEventNames[] = {
+   /* activity */
+   "ArchiverMain",
+   "AutoVacuumMain",
+   "BgWriterHibernate",
+   "BgWriterMain",
+   "CheckpointerMain",
+   "PgStatMain",
+   "RecoveryWalAll",
+   "RecoveryWalStream",
+   "SysLoggerMain",
+   "WalReceiverMain",
+   "WalSenderMain",
+   "WalWriterMain",
+   /* client */
+   "SecureRead",
+   "SecureWrite",
+   "SSLOpenServer",
+   "WalReceiverWaitStart",
+   "WalSenderWaitForWAL",
+   "WalSenderWriteData",
+   /* Extension */
+   "Extension",
+   /* IPC */
+   "BgWorkerShutdown",
+   "BgWorkerStartup",
+   "ExecuteGather",
+   "MessageQueueInternal",
+   "MessageQueuePutMessage",
+   "MessageQueueReceive",
+   "MessageQueueSend",
+   "ParallelFinish",
+   "ProcSignal",
+   "ProcSleep",
+   "SyncRep",
+   /* timeout */
+   "BaseBackupThrottle",
+   "PgSleep",
+   "RecoveryApplyDelay",
+};
I have moved WalSenderMain as it tracks a main loop, so it was strange
to not have it in Activity. I also kept SecureRead and SecureWrite
because this is referring to the functions of the same name. For the
rest, I got convinced by what has been discussed and it makes things
clearer. My head is not spinning anymore when I try to keep in sync
the list of names and its associated enum, which is good. I have as
well rewritten the docs to follow that.
-- 
Michael

Attachments:

wait-event-set-v7.patchapplication/x-patch; name=wait-event-set-v7.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..9222b73 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
 #include "access/xact.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
@@ -496,7 +497,9 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L,
+								   WAIT_EXTENSION,
+								   WE_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 0776428..6f7eef9 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,44 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>Activity</>: The server process is waiting for some
+          activity to happen on a socket.  This is mainly used system processes
+          in their main processing loop.  <literal>wait_event</> will identify
+          the type of activity waited for.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Extension</>: The server process is waiting for activity
+          in a plugin or an extension.  This category is useful for plugin
+          and module developers to track custom waiting points.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Client</>: The server process is waiting for some activity
+          on a socket from a client process, and that the server expects
+          something to happen that is independent from its internal processes.
+          <literal>wait_event</> will identify the type of client activity
+          waited for.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>IPC</>: The server process is waiting for some activity
+          from an auxilliary process.  <literal>wait_event</> will identify
+          the type of activity waited for.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Timeout</>: The server process is waiting for a timeout
+          to expire.  <literal>wait_event</> will identify the type of timeout
+          waited for.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1085,6 +1123,143 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="11"><literal>Activity</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stream at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
+        <row>
+         <entry morerows="5"><literal>Client</></entry>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in the code path of an extension, should be used by custom plugins and modules</entry>
+        </row>
+        <row>
+         <entry morerows="10"><literal>IPC</></entry>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to put new message in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSignal</></entry>
+         <entry>Waiting for signal from another backend.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSleep</></entry>
+         <entry>Waiting for a specific lock.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for WAL commit during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry morerows="2"><literal>Timeout</></entry>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index cde0ed3..63d4969 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -24,6 +24,7 @@
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
 #include "optimizer/planmain.h"
+#include "pgstat.h"
 #include "storage/ipc.h"
 #include "storage/sinval.h"
 #include "storage/spin.h"
@@ -540,7 +541,8 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  WAIT_IPC, WE_PARALLEL_FINISH);
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 2189c22..2c51c48 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5827,7 +5827,8 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  WAIT_TIMEOUT, WE_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -11376,7 +11377,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+							 wait_time, WAIT_ACTIVITY, WE_RECOVERY_WAL_STREAM);
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11539,7 +11540,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L, WAIT_ACTIVITY, WE_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..0178cd1 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -38,6 +38,7 @@
 #include "executor/nodeSubplan.h"
 #include "executor/tqueue.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
 
@@ -387,7 +388,7 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_EXECUTE_GATHER);
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index fedb02c..4f78863 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -60,6 +60,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
@@ -419,7 +420,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+								  WAIT_CLIENT, WE_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..671bffc 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -33,6 +33,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 #include "storage/ipc.h"
@@ -146,7 +147,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WAIT_CLIENT, WE_SECURE_READ);
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +249,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WAIT_CLIENT, WE_SECURE_WRITE);
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index bfe66c6..8c7fff3 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -17,6 +17,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 
@@ -171,7 +172,8 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0, WAIT_IPC,
+				  WE_MQ_PUT_MESSAGE);
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1a92ca1..8558110 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -598,7 +598,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   WAIT_ACTIVITY, WE_AUTOVACUUM_MAIN);
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 699c934..9bdbe68 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -16,6 +16,7 @@
 
 #include "miscadmin.h"
 #include "libpq/pqsignal.h"
+#include "pgstat.h"
 #include "postmaster/bgworker_internals.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -969,7 +970,8 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WAIT_IPC, WE_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1010,8 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			break;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WAIT_IPC, WE_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 1002034..67ec0ce 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -345,7 +345,9 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */,
+					   WAIT_ACTIVITY,
+					   WE_BGWRITER_MAIN);
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -372,7 +374,9 @@ BackgroundWriterMain(void)
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
 						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+						   BgWriterDelay * HIBERNATE_FACTOR,
+						   WAIT_ACTIVITY,
+						   WE_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index d702a48..a382157 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -556,7 +556,9 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   WAIT_ACTIVITY,
+					   WE_CHECKPOINTER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..1fc2794 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,7 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							 timeout * 1000L, WAIT_ACTIVITY, WE_ARCHIVER_MAIN);
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 8a2ce91..e67abe2 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3152,6 +3152,18 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+			event_type = "Activity";
+			break;
+		case WAIT_CLIENT:
+			event_type = "Client";
+			break;
+		case WAIT_IPC:
+			event_type = "IPC";
+			break;
+		case WAIT_TIMEOUT:
+			event_type = "Timeout";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3193,6 +3205,13 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+		case WAIT_CLIENT:
+		case WAIT_EXTENSION:
+		case WAIT_IPC:
+		case WAIT_TIMEOUT:
+			event_name = GetWaitEventIdentifier(eventId);
+			break;
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3681,8 +3700,7 @@ PgstatCollectorMain(int argc, char *argv[])
 #ifndef WIN32
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
-							   pgStatSock,
-							   -1L);
+					 pgStatSock, -1L, WAIT_ACTIVITY, WE_PGSTAT_MAIN);
 #else
 
 		/*
@@ -3698,7 +3716,9 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 		WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
 							   pgStatSock,
-							   2 * 1000L /* msec */ );
+							   2 * 1000L /* msec */,
+							   WAIT_ACTIVITY,
+							   WE_PGSTAT_MAIN);
 #endif
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..c937831 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -35,6 +35,7 @@
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "nodes/pg_list.h"
+#include "pgstat.h"
 #include "pgtime.h"
 #include "postmaster/fork_process.h"
 #include "postmaster/postmaster.h"
@@ -424,7 +425,9 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+							   WAIT_ACTIVITY,
+							   WE_SYSLOGGER_MAIN);
 
 		if (rc & WL_SOCKET_READABLE)
 		{
@@ -475,7 +478,9 @@ SysLoggerMain(int argc, char *argv[])
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | cur_flags,
-						 cur_timeout);
+						 cur_timeout,
+						 WAIT_ACTIVITY,
+						 WE_SYSLOGGER_MAIN);
 
 		EnterCriticalSection(&sysloggerSection);
 #endif   /* WIN32 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 11ec56a..e798d83 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -290,7 +290,9 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   WAIT_ACTIVITY,
+					   WE_WAL_WRITER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index da9b7a6..8e40c34 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1286,7 +1286,9 @@ throttle(size_t increment)
 		 */
 		wait_result = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+								(long) (sleep / 1000),
+								WAIT_TIMEOUT,
+								WE_BASE_BACKUP_THROTTLE);
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index b442d06..09c8ce4 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -61,6 +61,7 @@
 
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/syncrep.h"
 #include "replication/walsender.h"
 #include "replication/walsender_private.h"
@@ -258,7 +259,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  WAIT_IPC, WE_SYNC_REP);
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..2660016 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -55,6 +55,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
 #include "storage/ipc.h"
@@ -486,7 +487,9 @@ WalReceiverMain(void)
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   WAIT_ACTIVITY,
+									   WE_WAL_RECEIVER_MAIN);
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +688,8 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  WAIT_CLIENT, WE_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c7743da..d3f66bb 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,9 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WAIT_CLIENT,
+						  WE_WAL_SENDER_WRITE_DATA);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1274,9 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WAIT_CLIENT,
+						  WE_WAL_SENDER_WAIT_WAL);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1924,7 +1928,9 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  WAIT_ACTIVITY,
+							  WE_WAL_SENDER_MAIN);
 		}
 	}
 	return;
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..8dd9540 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -122,6 +123,50 @@ struct WaitEventSet
 #endif
 };
 
+/*
+ * This must match enum WaitEventIdentifier!
+ */
+const char *const WaitEventNames[] = {
+	/* activity */
+	"ArchiverMain",
+	"AutoVacuumMain",
+	"BgWriterHibernate",
+	"BgWriterMain",
+	"CheckpointerMain",
+	"PgStatMain",
+	"RecoveryWalAll",
+	"RecoveryWalStream",
+	"SysLoggerMain",
+	"WalReceiverMain",
+	"WalSenderMain",
+	"WalWriterMain",
+	/* client */
+	"SecureRead",
+	"SecureWrite",
+	"SSLOpenServer",
+	"WalReceiverWaitStart",
+	"WalSenderWaitForWAL",
+	"WalSenderWriteData",
+	/* Extension */
+	"Extension",
+	/* IPC */
+	"BgWorkerShutdown",
+	"BgWorkerStartup",
+	"ExecuteGather",
+	"MessageQueueInternal",
+	"MessageQueuePutMessage",
+	"MessageQueueReceive",
+	"MessageQueueSend",
+	"ParallelFinish",
+	"ProcSignal",
+	"ProcSleep",
+	"SyncRep",
+	/* timeout */
+	"BaseBackupThrottle",
+	"PgSleep",
+	"RecoveryApplyDelay",
+};
+
 #ifndef WIN32
 /* Are we currently in WaitLatch? The signal handler would like to know. */
 static volatile sig_atomic_t waiting = false;
@@ -297,9 +342,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  uint8 classId, uint16 eventId)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 classId, eventId);
 }
 
 /*
@@ -316,7 +363,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, uint8 classId, uint16 eventId)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +391,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, classId, eventId);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -863,7 +910,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 uint8 classId, uint16 eventId)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -872,6 +920,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 
 	Assert(nevents > 0);
 
+	pgstat_report_wait_start((uint8) classId, (uint16) eventId);
+
 	/*
 	 * Initialize timeout if requested.  We must record the current time so
 	 * that we can determine the remaining timeout if interrupted.
@@ -960,6 +1010,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
@@ -1491,6 +1543,22 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 }
 #endif
 
+
+/*
+ * Return an identifier for a WaitEventSet depending on an event for
+ * statistics collector.
+ */
+const char *
+GetWaitEventIdentifier(uint16 eventId)
+{
+	StaticAssertStmt(lengthof(WaitEventNames) == WE_LAST_TYPE + 1,
+					 "WaitEventNames must match WaitEventIdentifiers");
+	if (eventId > WE_LAST_TYPE)
+		return "???";
+	return WaitEventNames[eventId];
+}
+
+
 /*
  * SetLatch uses SIGUSR1 to wake up the process waiting on the latch.
  *
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..8d8dc99 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -19,6 +19,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker.h"
 #include "storage/procsignal.h"
 #include "storage/shm_mq.h"
@@ -894,7 +895,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_MQ_SEND);
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +992,7 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_MQ_RECEIVE);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1091,7 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_MQ_INTERNAL);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 33e7023..ca29f14 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -39,6 +39,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/autovacuum.h"
 #include "replication/slot.h"
 #include "replication/syncrep.h"
@@ -1212,7 +1213,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_PROC_SLEEP);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1724,7 +1725,7 @@ CheckDeadLockAlert(void)
 void
 ProcWaitForSignal(void)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_PROC_SIGNAL);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..0e2ff93 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -30,6 +30,7 @@
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "parser/scansup.h"
+#include "pgstat.h"
 #include "postmaster/syslogger.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/fd.h"
@@ -560,7 +561,9 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms,
+						 WAIT_TIMEOUT,
+						 WE_PG_SLEEP);
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index dc3320d..124cc65 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -721,8 +721,13 @@ typedef enum WaitClass
 	WAIT_LWLOCK_NAMED,
 	WAIT_LWLOCK_TRANCHE,
 	WAIT_LOCK,
-	WAIT_BUFFER_PIN
-}	WaitClass;
+	WAIT_BUFFER_PIN,
+	WAIT_ACTIVITY,
+	WAIT_CLIENT,
+	WAIT_EXTENSION,
+	WAIT_IPC,
+	WAIT_TIMEOUT
+} WaitClass;
 
 
 /* ----------
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..b0b8436 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -135,6 +135,65 @@ typedef struct WaitEvent
 	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
 } WaitEvent;
 
+/*
+ * List of WaitEventSet identifiers used when reporting activity to
+ * statistics collector. Up to 256 different WaitEventIdentifier can be
+ * handled. Those are classified by category first, and then by
+ * alphabetical order. Events are classified into sub-categories following
+ * some basic hierarchy rules:
+ * - "Activity" for main loops of processes waiting for an event.
+ * - "Client" for a socket awaited when a user is connected.
+ * - "IPC", similarly to "Client", for a socket awaited in an auxilliary
+ *   process.
+ * - "Timeout", for a timeout waiting to expire.
+ * - "Extension", to let extension authors a way to use a custom category.
+ */
+typedef enum WaitEventIdentifier
+{
+	/* Activity */
+	WE_ARCHIVER_MAIN,
+	WE_AUTOVACUUM_MAIN,
+	WE_BGWRITER_HIBERNATE,
+	WE_BGWRITER_MAIN,
+	WE_CHECKPOINTER_MAIN,
+	WE_PGSTAT_MAIN,
+	WE_RECOVERY_WAL_ALL,
+	WE_RECOVERY_WAL_STREAM,
+	WE_SYSLOGGER_MAIN,
+	WE_WAL_RECEIVER_MAIN,
+	WE_WAL_SENDER_MAIN,
+	WE_WAL_WRITER_MAIN,
+	/* Client */
+	WE_SECURE_READ,
+	WE_SECURE_WRITE,
+	WE_SSL_OPEN_SERVER,
+	WE_WAL_RECEIVER_WAIT_START,
+	WE_WAL_SENDER_WAIT_WAL,
+	WE_WAL_SENDER_WRITE_DATA,
+	/* Extension */
+	WE_EXTENSION,
+	/* IPC */
+	WE_BGWORKER_SHUTDOWN,
+	WE_BGWORKER_STARTUP,
+	WE_EXECUTE_GATHER,
+	WE_MQ_INTERNAL,
+	WE_MQ_PUT_MESSAGE,
+	WE_MQ_RECEIVE,
+	WE_MQ_SEND,
+	WE_PARALLEL_FINISH,
+	WE_PROC_SIGNAL,
+	WE_PROC_SLEEP,
+	WE_SYNC_REP,
+	/* Timeout */
+	WE_BASE_BACKUP_THROTTLE,
+	WE_PG_SLEEP,
+	WE_RECOVERY_APPLY_DELAY
+} WaitEventIdentifier;
+
+#define WE_LAST_TYPE WE_RECOVERY_APPLY_DELAY
+
+extern const char *const WaitEventName[];
+
 /* forward declaration to avoid exposing latch.c implementation details */
 typedef struct WaitEventSet WaitEventSet;
 
@@ -155,10 +214,15 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
+				  WaitEvent *occurred_events, int nevents,
+				  uint8 classId, uint16 eventId);
+extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+				  uint8 classId, uint16 eventId);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, uint8 classId,
+				  uint16 eventId);
+extern const char *GetWaitEventIdentifier(uint16 eventId);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 143df4e..e7f76cd 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker.h"
 #include "storage/procsignal.h"
 #include "storage/shm_toc.h"
@@ -279,7 +280,7 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EXTENSION, WE_EXTENSION);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index dd34bc7..15d3874 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -15,6 +15,7 @@
 
 #include "fmgr.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 
 #include "test_shm_mq.h"
 
@@ -230,7 +231,7 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EXTENSION, WE_EXTENSION);
 			ResetLatch(MyLatch);
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 314e371..697dc42 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -227,7 +227,9 @@ worker_spi_main(Datum main_arg)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   worker_spi_naptime * 1000L);
+					   worker_spi_naptime * 1000L,
+					   WAIT_EXTENSION,
+					   WE_EXTENSION);
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#33Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#26)
Re: Tracking wait event for latches

On Thu, Sep 22, 2016 at 10:49 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Finally, extensions got their own category in this taxonomy, though I
wonder if it would be better to instead have
Activity/ExtensionActivity, Client/ExtensionClient,
Timeout/ExtensionTimeout, and IPC/ExtensionIPC instead of making it a
separate toplevel category.

I don't think that it is necessary to go up to that level. If you look
at the latest patch, WaitLatch & friends have been extended with two
arguments: classId and eventId, so extensions could just use
WAIT_ACTIVITY as classId and WE_EXTENSION as eventId to do the
equivalent of ExtensionActivity.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#34Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Michael Paquier (#32)
Re: Tracking wait event for latches

On Sat, Sep 24, 2016 at 1:44 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

After digesting all the comments given, I have produced the patch
attached that uses the following categories:
+const char *const WaitEventNames[] = {
+   /* activity */
+   "ArchiverMain",
+   "AutoVacuumMain",
+   "BgWriterHibernate",
+   "BgWriterMain",
+   "CheckpointerMain",
+   "PgStatMain",
+   "RecoveryWalAll",
+   "RecoveryWalStream",
+   "SysLoggerMain",
+   "WalReceiverMain",
+   "WalSenderMain",
+   "WalWriterMain",
+   /* client */
+   "SecureRead",
+   "SecureWrite",
+   "SSLOpenServer",
+   "WalReceiverWaitStart",
+   "WalSenderWaitForWAL",
+   "WalSenderWriteData",
+   /* Extension */
+   "Extension",
+   /* IPC */
+   "BgWorkerShutdown",
+   "BgWorkerStartup",
+   "ExecuteGather",
+   "MessageQueueInternal",
+   "MessageQueuePutMessage",
+   "MessageQueueReceive",
+   "MessageQueueSend",
+   "ParallelFinish",
+   "ProcSignal",
+   "ProcSleep",
+   "SyncRep",
+   /* timeout */
+   "BaseBackupThrottle",
+   "PgSleep",
+   "RecoveryApplyDelay",
+};
I have moved WalSenderMain as it tracks a main loop, so it was strange
to not have it in Activity. I also kept SecureRead and SecureWrite
because this is referring to the functions of the same name. For the
rest, I got convinced by what has been discussed and it makes things
clearer. My head is not spinning anymore when I try to keep in sync
the list of names and its associated enum, which is good. I have as
well rewritten the docs to follow that.
-extern int WaitEventSetWait(WaitEventSet *set, long timeout,
WaitEvent *occurred_events, int nevents);
-extern int WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int WaitEventSetWait(WaitEventSet *set, long timeout,
+  WaitEvent *occurred_events, int nevents,
+  uint8 classId, uint16 eventId);
+extern int WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+  uint8 classId, uint16 eventId);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-  pgsocket sock, long timeout);
+  pgsocket sock, long timeout, uint8 classId,
+  uint16 eventId);

+ WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_MQ_RECEIVE);

If the class really is strictly implied by the WaitEventIdentifier,
then do we really need to supply it everywhere when calling the
various wait functions? That's going to be quite a few functions:
WaitLatch, WaitLatchOrSocket, WaitEventSetWait for now, and if some
more patches out there have legs then also ConditionVariableWait,
BarrierWait, and possibly further kinds of wait points. And then all
their call sites will have have to supply the wait class ID, even
though it is implied by the other ID. Perhaps that array that
currently holds the names should instead hold { classId, displayName }
so we could just look it up?

--
Thomas Munro
http://www.enterprisedb.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#35Michael Paquier
michael.paquier@gmail.com
In reply to: Thomas Munro (#34)
Re: Tracking wait event for latches

On Mon, Sep 26, 2016 at 1:46 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

If the class really is strictly implied by the WaitEventIdentifier,
then do we really need to supply it everywhere when calling the
various wait functions? That's going to be quite a few functions:
WaitLatch, WaitLatchOrSocket, WaitEventSetWait for now, and if some
more patches out there have legs then also ConditionVariableWait,
BarrierWait, and possibly further kinds of wait points. And then all
their call sites will have have to supply the wait class ID, even
though it is implied by the other ID. Perhaps that array that
currently holds the names should instead hold { classId, displayName }
so we could just look it up?

I considered this reverse-engineering, but arrived at the conclusion
that having a flexible API mattered more to give more flexibility to
module developers. In short this avoids having extra class IDs like
ActivityExtention, TimeoutExtension, etc. But perhaps that's just a
matter of taste..
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#36Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Michael Paquier (#35)
Re: Tracking wait event for latches

On Mon, Sep 26, 2016 at 7:07 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Mon, Sep 26, 2016 at 1:46 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

If the class really is strictly implied by the WaitEventIdentifier,
then do we really need to supply it everywhere when calling the
various wait functions? That's going to be quite a few functions:
WaitLatch, WaitLatchOrSocket, WaitEventSetWait for now, and if some
more patches out there have legs then also ConditionVariableWait,
BarrierWait, and possibly further kinds of wait points. And then all
their call sites will have have to supply the wait class ID, even
though it is implied by the other ID. Perhaps that array that
currently holds the names should instead hold { classId, displayName }
so we could just look it up?

I considered this reverse-engineering, but arrived at the conclusion
that having a flexible API mattered more to give more flexibility to
module developers. In short this avoids having extra class IDs like
ActivityExtention, TimeoutExtension, etc. But perhaps that's just a
matter of taste..

Ok, if they really are independent then shouldn't we take advantage of
that at call sites where we might be idle but we might also be waiting
for the network? For example we could do this:

/* Sleep until something happens or we time out */
WaitLatchOrSocket(MyLatch, wakeEvents,
MyProcPort->sock, sleeptime,
pq_is_send_pending() ? WAIT_CLIENT :
WAIT_ACTIVITY,
WE_WAL_SENDER_MAIN);

Isn't WE_WAL_SENDER_WAIT_WAL primarily WAIT_IPC, not WAIT_CLIENT? Or
perhaps "pq_is_send_pending() ? WAIT_CLIENT : WAIT_IPC".

Actually, I'm still not sold on "Activity" and "Client". I think
"Idle" and "Network" would be better. Everybody knows intuitively
what "Idle" means. "Network" is better than "Client" because it
avoids confusion about user applications vs replication connections or
clients vs servers.

+        <listitem>
+         <para>
+          <literal>Activity</>: The server process is waiting for some
+          activity to happen on a socket.  This is mainly used system processes
+          in their main processing loop.  <literal>wait_event</> will identify
+          the type of activity waited for.
+         </para>
+        </listitem>

"The server process is waiting for some activity to happen on a
socket." Not true: could be a latch, or both.

I think what this category is really getting at is that the server
process is idle. Everything in it could otherwise go in the IPC or
Client categories on the basis that it's mainly waiting for a socket
or a latch, but we're deciding to separate the wait points
representing "idleness" and put them here.

How about: "The server process is idle. This is used by system
processes waiting for activity in their main processing loop.
<literal>wait_event</a> will identify the specific wait point."

+        <listitem>
+         <para>
+          <literal>Client</>: The server process is waiting for some activity
+          on a socket from a client process, and that the server expects
+          something to happen that is independent from its internal processes.
+          <literal>wait_event</> will identify the type of client activity
+          waited for.
+         </para>
+        </listitem>

Is it worth spelling out that "client process" here doesn't just mean
user applications, it's also remote PostgreSQL servers doing
replication? "wait_event" doesn't really identify the type of client
activity waited for, it identifies the code that is waiting.

+        <listitem>
+         <para>
+          <literal>Extension</>: The server process is waiting for activity
+          in a plugin or an extension.  This category is useful for plugin
+          and module developers to track custom waiting points.
+         </para>
+        </listitem>

Plugin, extension, module? How about just "extension"? Why developers?

+        <listitem>
+         <para>
+          <literal>IPC</>: The server process is waiting for some activity
+          from an auxilliary process.  <literal>wait_event</> will identify
+          the type of activity waited for.
+         </para>
+        </listitem>

s/auxilliary/auxiliary/, but I wouldn't it be better to say something
more general like "from another process in the cluster"? Background
workers are not generally called auxiliary processes, and some of
these wait points are waiting for those.

--
Thomas Munro
http://www.enterprisedb.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#37Michael Paquier
michael.paquier@gmail.com
In reply to: Thomas Munro (#36)
1 attachment(s)
Re: Tracking wait event for latches

On Wed, Sep 28, 2016 at 9:39 AM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

Ok, if they really are independent then shouldn't we take advantage of
that at call sites where we might be idle but we might also be waiting
for the network? For example we could do this:

/* Sleep until something happens or we time out */
WaitLatchOrSocket(MyLatch, wakeEvents,
MyProcPort->sock, sleeptime,
pq_is_send_pending() ? WAIT_CLIENT :
WAIT_ACTIVITY,
WE_WAL_SENDER_MAIN);

Yes, we can do fancy things with this API. Extensions could do the
same, the important thing being to have generic enough event
categories (take class).

Isn't WE_WAL_SENDER_WAIT_WAL primarily WAIT_IPC, not WAIT_CLIENT? Or
perhaps "pq_is_send_pending() ? WAIT_CLIENT : WAIT_IPC".

Actually, I'm still not sold on "Activity" and "Client". I think
"Idle" and "Network" would be better. Everybody knows intuitively
what "Idle" means. "Network" is better than "Client" because it
avoids confusion about user applications vs replication connections or
clients vs servers.

Personally I am buying the argument of Robert instead. The class of an
event means what it is waiting for, not in which state in it waiting
for something. "Idle" means that the process *is* idle, not that it is
waiting for something.

+        <listitem>
+         <para>
+          <literal>Activity</>: The server process is waiting for some
+          activity to happen on a socket.  This is mainly used system processes
+          in their main processing loop.  <literal>wait_event</> will identify
+          the type of activity waited for.
+         </para>
+        </listitem>

"The server process is waiting for some activity to happen on a
socket." Not true: could be a latch, or both.

I think what this category is really getting at is that the server
process is idle. Everything in it could otherwise go in the IPC or
Client categories on the basis that it's mainly waiting for a socket
or a latch, but we're deciding to separate the wait points
representing "idleness" and put them here.

How about: "The server process is idle. This is used by system
processes waiting for activity in their main processing loop.
<literal>wait_event</a> will identify the specific wait point."

You have a better way to word things than I do.

+        <listitem>
+         <para>
+          <literal>Client</>: The server process is waiting for some activity
+          on a socket from a client process, and that the server expects
+          something to happen that is independent from its internal processes.
+          <literal>wait_event</> will identify the type of client activity
+          waited for.
+         </para>
+        </listitem>

Is it worth spelling out that "client process" here doesn't just mean
user applications, it's also remote PostgreSQL servers doing
replication? "wait_event" doesn't really identify the type of client
activity waited for, it identifies the code that is waiting.

Okay, switched to "user applications", and precised that this is a
wait point that wait_event tracks.

+        <listitem>
+         <para>
+          <literal>Extension</>: The server process is waiting for activity
+          in a plugin or an extension.  This category is useful for plugin
+          and module developers to track custom waiting points.
+         </para>
+        </listitem>

Plugin, extension, module? How about just "extension"? Why developers?

Let's say "extension module", this is used in a couple of places in the docs.

+        <listitem>
+         <para>
+          <literal>IPC</>: The server process is waiting for some activity
+          from an auxilliary process.  <literal>wait_event</> will identify
+          the type of activity waited for.
+         </para>
+        </listitem>

s/auxilliary/auxiliary/, but I wouldn't it be better to say something
more general like "from another process in the cluster"? Background
workers are not generally called auxiliary processes, and some of
these wait points are waiting for those.

There was the same typo in latch.h, still "from another process" looks better.
--
Michael

Attachments:

wait-event-set-v8.patchapplication/x-patch; name=wait-event-set-v8.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..9222b73 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
 #include "access/xact.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
@@ -496,7 +497,9 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L,
+								   WAIT_EXTENSION,
+								   WE_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index f400785..bb975c1 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,42 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>Activity</>: The server process is idle.  This is used by
+          system processes waiting for activity in their main processing loop.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Extension</>: The server process is waiting for activity
+          in an extension module.  This category is useful for modules to
+          track custom waiting points.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Client</>: The server process is waiting for some activity
+          on a socket from user applications, and that the server expects
+          something to happen that is independent from its internal processes.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>IPC</>: The server process is waiting for some activity
+          from another process in the server.  <literal>wait_event</> will
+          identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Timeout</>: The server process is waiting for a timeout
+          to expire.  <literal>wait_event</> will identify the specific wait
+          point.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1085,6 +1121,143 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="11"><literal>Activity</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stream at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
+        <row>
+         <entry morerows="5"><literal>Client</></entry>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in the code path of an extension, should be used by custom plugins and modules</entry>
+        </row>
+        <row>
+         <entry morerows="10"><literal>IPC</></entry>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to put new message in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSignal</></entry>
+         <entry>Waiting for signal from another backend.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSleep</></entry>
+         <entry>Waiting for a specific lock.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for WAL commit during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry morerows="2"><literal>Timeout</></entry>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index cde0ed3..63d4969 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -24,6 +24,7 @@
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
 #include "optimizer/planmain.h"
+#include "pgstat.h"
 #include "storage/ipc.h"
 #include "storage/sinval.h"
 #include "storage/spin.h"
@@ -540,7 +541,8 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  WAIT_IPC, WE_PARALLEL_FINISH);
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index c1b9a97..e65fd0a 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5827,7 +5827,8 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  WAIT_TIMEOUT, WE_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -11387,7 +11388,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+							 wait_time, WAIT_ACTIVITY, WE_RECOVERY_WAL_STREAM);
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11550,7 +11551,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L, WAIT_ACTIVITY, WE_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..0178cd1 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -38,6 +38,7 @@
 #include "executor/nodeSubplan.h"
 #include "executor/tqueue.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
 
@@ -387,7 +388,7 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_EXECUTE_GATHER);
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index fedb02c..4f78863 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -60,6 +60,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
@@ -419,7 +420,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+								  WAIT_CLIENT, WE_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..671bffc 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -33,6 +33,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 #include "storage/ipc.h"
@@ -146,7 +147,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WAIT_CLIENT, WE_SECURE_READ);
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +249,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WAIT_CLIENT, WE_SECURE_WRITE);
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index bfe66c6..8c7fff3 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -17,6 +17,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 
@@ -171,7 +172,8 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0, WAIT_IPC,
+				  WE_MQ_PUT_MESSAGE);
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1a92ca1..8558110 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -598,7 +598,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   WAIT_ACTIVITY, WE_AUTOVACUUM_MAIN);
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 699c934..9bdbe68 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -16,6 +16,7 @@
 
 #include "miscadmin.h"
 #include "libpq/pqsignal.h"
+#include "pgstat.h"
 #include "postmaster/bgworker_internals.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -969,7 +970,8 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WAIT_IPC, WE_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1010,8 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			break;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WAIT_IPC, WE_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 1002034..67ec0ce 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -345,7 +345,9 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */,
+					   WAIT_ACTIVITY,
+					   WE_BGWRITER_MAIN);
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -372,7 +374,9 @@ BackgroundWriterMain(void)
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
 						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+						   BgWriterDelay * HIBERNATE_FACTOR,
+						   WAIT_ACTIVITY,
+						   WE_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index d702a48..a382157 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -556,7 +556,9 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   WAIT_ACTIVITY,
+					   WE_CHECKPOINTER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..1fc2794 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,7 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							 timeout * 1000L, WAIT_ACTIVITY, WE_ARCHIVER_MAIN);
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 96578dc..019086a 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3155,6 +3155,18 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+			event_type = "Activity";
+			break;
+		case WAIT_CLIENT:
+			event_type = "Client";
+			break;
+		case WAIT_IPC:
+			event_type = "IPC";
+			break;
+		case WAIT_TIMEOUT:
+			event_type = "Timeout";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3196,6 +3208,13 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+		case WAIT_CLIENT:
+		case WAIT_EXTENSION:
+		case WAIT_IPC:
+		case WAIT_TIMEOUT:
+			event_name = GetWaitEventIdentifier(eventId);
+			break;
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3684,8 +3703,7 @@ PgstatCollectorMain(int argc, char *argv[])
 #ifndef WIN32
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
-							   pgStatSock,
-							   -1L);
+					 pgStatSock, -1L, WAIT_ACTIVITY, WE_PGSTAT_MAIN);
 #else
 
 		/*
@@ -3701,7 +3719,9 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 		WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
 							   pgStatSock,
-							   2 * 1000L /* msec */ );
+							   2 * 1000L /* msec */,
+							   WAIT_ACTIVITY,
+							   WE_PGSTAT_MAIN);
 #endif
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..c937831 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -35,6 +35,7 @@
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "nodes/pg_list.h"
+#include "pgstat.h"
 #include "pgtime.h"
 #include "postmaster/fork_process.h"
 #include "postmaster/postmaster.h"
@@ -424,7 +425,9 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+							   WAIT_ACTIVITY,
+							   WE_SYSLOGGER_MAIN);
 
 		if (rc & WL_SOCKET_READABLE)
 		{
@@ -475,7 +478,9 @@ SysLoggerMain(int argc, char *argv[])
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | cur_flags,
-						 cur_timeout);
+						 cur_timeout,
+						 WAIT_ACTIVITY,
+						 WE_SYSLOGGER_MAIN);
 
 		EnterCriticalSection(&sysloggerSection);
 #endif   /* WIN32 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 11ec56a..e798d83 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -290,7 +290,9 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   WAIT_ACTIVITY,
+					   WE_WAL_WRITER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index da9b7a6..8e40c34 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1286,7 +1286,9 @@ throttle(size_t increment)
 		 */
 		wait_result = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+								(long) (sleep / 1000),
+								WAIT_TIMEOUT,
+								WE_BASE_BACKUP_THROTTLE);
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index b442d06..09c8ce4 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -61,6 +61,7 @@
 
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/syncrep.h"
 #include "replication/walsender.h"
 #include "replication/walsender_private.h"
@@ -258,7 +259,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  WAIT_IPC, WE_SYNC_REP);
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..2660016 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -55,6 +55,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
 #include "storage/ipc.h"
@@ -486,7 +487,9 @@ WalReceiverMain(void)
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   WAIT_ACTIVITY,
+									   WE_WAL_RECEIVER_MAIN);
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +688,8 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  WAIT_CLIENT, WE_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c7743da..d3f66bb 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,9 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WAIT_CLIENT,
+						  WE_WAL_SENDER_WRITE_DATA);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1274,9 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WAIT_CLIENT,
+						  WE_WAL_SENDER_WAIT_WAL);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1924,7 +1928,9 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  WAIT_ACTIVITY,
+							  WE_WAL_SENDER_MAIN);
 		}
 	}
 	return;
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..8dcf569 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -122,6 +123,50 @@ struct WaitEventSet
 #endif
 };
 
+/*
+ * This must match enum WaitEventIdentifier!
+ */
+const char *const WaitEventNames[] = {
+	/* Activity */
+	"ArchiverMain",
+	"AutoVacuumMain",
+	"BgWriterHibernate",
+	"BgWriterMain",
+	"CheckpointerMain",
+	"PgStatMain",
+	"RecoveryWalAll",
+	"RecoveryWalStream",
+	"SysLoggerMain",
+	"WalReceiverMain",
+	"WalSenderMain",
+	"WalWriterMain",
+	/* Client */
+	"SecureRead",
+	"SecureWrite",
+	"SSLOpenServer",
+	"WalReceiverWaitStart",
+	"WalSenderWaitForWAL",
+	"WalSenderWriteData",
+	/* Extension */
+	"Extension",
+	/* IPC */
+	"BgWorkerShutdown",
+	"BgWorkerStartup",
+	"ExecuteGather",
+	"MessageQueueInternal",
+	"MessageQueuePutMessage",
+	"MessageQueueReceive",
+	"MessageQueueSend",
+	"ParallelFinish",
+	"ProcSignal",
+	"ProcSleep",
+	"SyncRep",
+	/* Timeout */
+	"BaseBackupThrottle",
+	"PgSleep",
+	"RecoveryApplyDelay",
+};
+
 #ifndef WIN32
 /* Are we currently in WaitLatch? The signal handler would like to know. */
 static volatile sig_atomic_t waiting = false;
@@ -297,9 +342,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  uint8 classId, uint16 eventId)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 classId, eventId);
 }
 
 /*
@@ -316,7 +363,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, uint8 classId, uint16 eventId)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +391,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, classId, eventId);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -863,7 +910,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 uint8 classId, uint16 eventId)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -872,6 +920,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 
 	Assert(nevents > 0);
 
+	pgstat_report_wait_start((uint8) classId, (uint16) eventId);
+
 	/*
 	 * Initialize timeout if requested.  We must record the current time so
 	 * that we can determine the remaining timeout if interrupted.
@@ -960,6 +1010,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
@@ -1491,6 +1543,22 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 }
 #endif
 
+
+/*
+ * Return an identifier for a WaitEventSet depending on an event for
+ * statistics collector.
+ */
+const char *
+GetWaitEventIdentifier(uint16 eventId)
+{
+	StaticAssertStmt(lengthof(WaitEventNames) == WE_LAST_TYPE + 1,
+					 "WaitEventNames must match WaitEventIdentifiers");
+	if (eventId > WE_LAST_TYPE)
+		return "???";
+	return WaitEventNames[eventId];
+}
+
+
 /*
  * SetLatch uses SIGUSR1 to wake up the process waiting on the latch.
  *
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..8d8dc99 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -19,6 +19,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker.h"
 #include "storage/procsignal.h"
 #include "storage/shm_mq.h"
@@ -894,7 +895,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_MQ_SEND);
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +992,7 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_MQ_RECEIVE);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1091,7 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_MQ_INTERNAL);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 33e7023..ca29f14 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -39,6 +39,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/autovacuum.h"
 #include "replication/slot.h"
 #include "replication/syncrep.h"
@@ -1212,7 +1213,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_PROC_SLEEP);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1724,7 +1725,7 @@ CheckDeadLockAlert(void)
 void
 ProcWaitForSignal(void)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_PROC_SIGNAL);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..0e2ff93 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -30,6 +30,7 @@
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "parser/scansup.h"
+#include "pgstat.h"
 #include "postmaster/syslogger.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/fd.h"
@@ -560,7 +561,9 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms,
+						 WAIT_TIMEOUT,
+						 WE_PG_SLEEP);
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 0c98c59..b3f707e 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -721,8 +721,13 @@ typedef enum WaitClass
 	WAIT_LWLOCK_NAMED,
 	WAIT_LWLOCK_TRANCHE,
 	WAIT_LOCK,
-	WAIT_BUFFER_PIN
-}	WaitClass;
+	WAIT_BUFFER_PIN,
+	WAIT_ACTIVITY,
+	WAIT_CLIENT,
+	WAIT_EXTENSION,
+	WAIT_IPC,
+	WAIT_TIMEOUT
+} WaitClass;
 
 
 /* ----------
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..8261830 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -135,6 +135,66 @@ typedef struct WaitEvent
 	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
 } WaitEvent;
 
+/*
+ * List of WaitEventSet identifiers used when reporting activity to
+ * statistics collector. Up to 256 different WaitEventIdentifier can be
+ * handled. Those are classified by category first, and then by
+ * alphabetical order. Events are classified into sub-categories following
+ * some basic hierarchy rules:
+ * - "Activity" for main loops of processes waiting for an event.
+ * - "Client" for a socket awaited when a user is connected.
+ * - "IPC", similarly to "Client", for a socket awaited from another
+ *   server process.
+ * - "Timeout", for a timeout waiting to expire.
+ * - "Extension", to let extension modules a way to define a custom wait
+ *   point.
+ */
+typedef enum WaitEventIdentifier
+{
+	/* Activity */
+	WE_ARCHIVER_MAIN,
+	WE_AUTOVACUUM_MAIN,
+	WE_BGWRITER_HIBERNATE,
+	WE_BGWRITER_MAIN,
+	WE_CHECKPOINTER_MAIN,
+	WE_PGSTAT_MAIN,
+	WE_RECOVERY_WAL_ALL,
+	WE_RECOVERY_WAL_STREAM,
+	WE_SYSLOGGER_MAIN,
+	WE_WAL_RECEIVER_MAIN,
+	WE_WAL_SENDER_MAIN,
+	WE_WAL_WRITER_MAIN,
+	/* Client */
+	WE_SECURE_READ,
+	WE_SECURE_WRITE,
+	WE_SSL_OPEN_SERVER,
+	WE_WAL_RECEIVER_WAIT_START,
+	WE_WAL_SENDER_WAIT_WAL,
+	WE_WAL_SENDER_WRITE_DATA,
+	/* Extension */
+	WE_EXTENSION,
+	/* IPC */
+	WE_BGWORKER_SHUTDOWN,
+	WE_BGWORKER_STARTUP,
+	WE_EXECUTE_GATHER,
+	WE_MQ_INTERNAL,
+	WE_MQ_PUT_MESSAGE,
+	WE_MQ_RECEIVE,
+	WE_MQ_SEND,
+	WE_PARALLEL_FINISH,
+	WE_PROC_SIGNAL,
+	WE_PROC_SLEEP,
+	WE_SYNC_REP,
+	/* Timeout */
+	WE_BASE_BACKUP_THROTTLE,
+	WE_PG_SLEEP,
+	WE_RECOVERY_APPLY_DELAY
+} WaitEventIdentifier;
+
+#define WE_LAST_TYPE WE_RECOVERY_APPLY_DELAY
+
+extern const char *const WaitEventName[];
+
 /* forward declaration to avoid exposing latch.c implementation details */
 typedef struct WaitEventSet WaitEventSet;
 
@@ -155,10 +215,15 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
+				  WaitEvent *occurred_events, int nevents,
+				  uint8 classId, uint16 eventId);
+extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+				  uint8 classId, uint16 eventId);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, uint8 classId,
+				  uint16 eventId);
+extern const char *GetWaitEventIdentifier(uint16 eventId);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 143df4e..e7f76cd 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker.h"
 #include "storage/procsignal.h"
 #include "storage/shm_toc.h"
@@ -279,7 +280,7 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EXTENSION, WE_EXTENSION);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index dd34bc7..15d3874 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -15,6 +15,7 @@
 
 #include "fmgr.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 
 #include "test_shm_mq.h"
 
@@ -230,7 +231,7 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EXTENSION, WE_EXTENSION);
 			ResetLatch(MyLatch);
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 314e371..697dc42 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -227,7 +227,9 @@ worker_spi_main(Datum main_arg)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   worker_spi_naptime * 1000L);
+					   worker_spi_naptime * 1000L,
+					   WAIT_EXTENSION,
+					   WE_EXTENSION);
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#38Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Michael Paquier (#37)
Re: Tracking wait event for latches

On Wed, Sep 28, 2016 at 6:25 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

wait-event-set-v8.patch

Ok, I'm just about ready to mark this as 'Ready for Committer'. Just
a couple of things:

+ pgstat_report_wait_start((uint8) classId, (uint16) eventId);

Unnecessary casts.

+        <row>
+         <entry morerows="5"><literal>Client</></entry>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>

I think we want to drop the word 'secure' from the description lines
in this patch. Then I think we plan to make a separate patch that
will rename the functions themselves and the corresponding wait points
to something more generic?

I'm assuming that my suggestions for making WE_WAL_SENDER_WAIT_WAL and
WE_WAL_SENDER_MAIN have a dynamically chosen class ID would also be
material for another patch, but it doesn't matter much because those
processes won't show up yet anyway.

--
Thomas Munro
http://www.enterprisedb.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#39Michael Paquier
michael.paquier@gmail.com
In reply to: Thomas Munro (#38)
1 attachment(s)
Re: Tracking wait event for latches

On Wed, Sep 28, 2016 at 3:40 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

On Wed, Sep 28, 2016 at 6:25 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

wait-event-set-v8.patch

Ok, I'm just about ready to mark this as 'Ready for Committer'.

Thanks.

Just a couple of things:
+ pgstat_report_wait_start((uint8) classId, (uint16) eventId);
Unnecessary casts.

Right....

+        <row>
+         <entry morerows="5"><literal>Client</></entry>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>

I think we want to drop the word 'secure' from the description lines
in this patch. Then I think we plan to make a separate patch that
will rename the functions themselves and the corresponding wait points
to something more generic?

Robert mentioned ClientRead/ClientWrite upthread. I still think that
SecureRead/SecureWrite is better as it respects the routine name where
the wait point is, and that's consistent with the rest.

I'm assuming that my suggestions for making WE_WAL_SENDER_WAIT_WAL and
WE_WAL_SENDER_MAIN have a dynamically chosen class ID would also be
material for another patch, but it doesn't matter much because those
processes won't show up yet anyway.

WAL senders do show up since 8299471 because they are counted as in
max_connections. That's pretty cool combined with this patch.

I am sending a new patch to save 30s to the committer potentially
looking at this patch.
--
Michael

Attachments:

wait-event-set-v9.patchapplication/x-patch; name=wait-event-set-v9.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..9222b73 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
 #include "access/xact.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
@@ -496,7 +497,9 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L,
+								   WAIT_EXTENSION,
+								   WE_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index f400785..bb975c1 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,42 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>Activity</>: The server process is idle.  This is used by
+          system processes waiting for activity in their main processing loop.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Extension</>: The server process is waiting for activity
+          in an extension module.  This category is useful for modules to
+          track custom waiting points.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Client</>: The server process is waiting for some activity
+          on a socket from user applications, and that the server expects
+          something to happen that is independent from its internal processes.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>IPC</>: The server process is waiting for some activity
+          from another process in the server.  <literal>wait_event</> will
+          identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Timeout</>: The server process is waiting for a timeout
+          to expire.  <literal>wait_event</> will identify the specific wait
+          point.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1085,6 +1121,143 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="11"><literal>Activity</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stream at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
+        <row>
+         <entry morerows="5"><literal>Client</></entry>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in the code path of an extension, should be used by custom plugins and modules</entry>
+        </row>
+        <row>
+         <entry morerows="10"><literal>IPC</></entry>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to put new message in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSignal</></entry>
+         <entry>Waiting for signal from another backend.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSleep</></entry>
+         <entry>Waiting for a specific lock.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for WAL commit during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry morerows="2"><literal>Timeout</></entry>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index cde0ed3..63d4969 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -24,6 +24,7 @@
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
 #include "optimizer/planmain.h"
+#include "pgstat.h"
 #include "storage/ipc.h"
 #include "storage/sinval.h"
 #include "storage/spin.h"
@@ -540,7 +541,8 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  WAIT_IPC, WE_PARALLEL_FINISH);
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index c1b9a97..e65fd0a 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5827,7 +5827,8 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  WAIT_TIMEOUT, WE_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -11387,7 +11388,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+							 wait_time, WAIT_ACTIVITY, WE_RECOVERY_WAL_STREAM);
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11550,7 +11551,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L, WAIT_ACTIVITY, WE_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..0178cd1 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -38,6 +38,7 @@
 #include "executor/nodeSubplan.h"
 #include "executor/tqueue.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
 
@@ -387,7 +388,7 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_EXECUTE_GATHER);
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index fedb02c..4f78863 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -60,6 +60,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
@@ -419,7 +420,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+								  WAIT_CLIENT, WE_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..671bffc 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -33,6 +33,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 #include "storage/ipc.h"
@@ -146,7 +147,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WAIT_CLIENT, WE_SECURE_READ);
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +249,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WAIT_CLIENT, WE_SECURE_WRITE);
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index bfe66c6..8c7fff3 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -17,6 +17,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 
@@ -171,7 +172,8 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0, WAIT_IPC,
+				  WE_MQ_PUT_MESSAGE);
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1a92ca1..8558110 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -598,7 +598,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   WAIT_ACTIVITY, WE_AUTOVACUUM_MAIN);
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 699c934..9bdbe68 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -16,6 +16,7 @@
 
 #include "miscadmin.h"
 #include "libpq/pqsignal.h"
+#include "pgstat.h"
 #include "postmaster/bgworker_internals.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -969,7 +970,8 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WAIT_IPC, WE_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1010,8 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			break;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WAIT_IPC, WE_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 1002034..67ec0ce 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -345,7 +345,9 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */,
+					   WAIT_ACTIVITY,
+					   WE_BGWRITER_MAIN);
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -372,7 +374,9 @@ BackgroundWriterMain(void)
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
 						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+						   BgWriterDelay * HIBERNATE_FACTOR,
+						   WAIT_ACTIVITY,
+						   WE_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index d702a48..a382157 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -556,7 +556,9 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   WAIT_ACTIVITY,
+					   WE_CHECKPOINTER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..1fc2794 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,7 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							 timeout * 1000L, WAIT_ACTIVITY, WE_ARCHIVER_MAIN);
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 96578dc..019086a 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3155,6 +3155,18 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+			event_type = "Activity";
+			break;
+		case WAIT_CLIENT:
+			event_type = "Client";
+			break;
+		case WAIT_IPC:
+			event_type = "IPC";
+			break;
+		case WAIT_TIMEOUT:
+			event_type = "Timeout";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3196,6 +3208,13 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+		case WAIT_CLIENT:
+		case WAIT_EXTENSION:
+		case WAIT_IPC:
+		case WAIT_TIMEOUT:
+			event_name = GetWaitEventIdentifier(eventId);
+			break;
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3684,8 +3703,7 @@ PgstatCollectorMain(int argc, char *argv[])
 #ifndef WIN32
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
-							   pgStatSock,
-							   -1L);
+					 pgStatSock, -1L, WAIT_ACTIVITY, WE_PGSTAT_MAIN);
 #else
 
 		/*
@@ -3701,7 +3719,9 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 		WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
 							   pgStatSock,
-							   2 * 1000L /* msec */ );
+							   2 * 1000L /* msec */,
+							   WAIT_ACTIVITY,
+							   WE_PGSTAT_MAIN);
 #endif
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..c937831 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -35,6 +35,7 @@
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "nodes/pg_list.h"
+#include "pgstat.h"
 #include "pgtime.h"
 #include "postmaster/fork_process.h"
 #include "postmaster/postmaster.h"
@@ -424,7 +425,9 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+							   WAIT_ACTIVITY,
+							   WE_SYSLOGGER_MAIN);
 
 		if (rc & WL_SOCKET_READABLE)
 		{
@@ -475,7 +478,9 @@ SysLoggerMain(int argc, char *argv[])
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | cur_flags,
-						 cur_timeout);
+						 cur_timeout,
+						 WAIT_ACTIVITY,
+						 WE_SYSLOGGER_MAIN);
 
 		EnterCriticalSection(&sysloggerSection);
 #endif   /* WIN32 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 11ec56a..e798d83 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -290,7 +290,9 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   WAIT_ACTIVITY,
+					   WE_WAL_WRITER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index da9b7a6..8e40c34 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1286,7 +1286,9 @@ throttle(size_t increment)
 		 */
 		wait_result = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+								(long) (sleep / 1000),
+								WAIT_TIMEOUT,
+								WE_BASE_BACKUP_THROTTLE);
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index b442d06..09c8ce4 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -61,6 +61,7 @@
 
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/syncrep.h"
 #include "replication/walsender.h"
 #include "replication/walsender_private.h"
@@ -258,7 +259,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  WAIT_IPC, WE_SYNC_REP);
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..2660016 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -55,6 +55,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
 #include "storage/ipc.h"
@@ -486,7 +487,9 @@ WalReceiverMain(void)
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   WAIT_ACTIVITY,
+									   WE_WAL_RECEIVER_MAIN);
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +688,8 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  WAIT_CLIENT, WE_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c7743da..d3f66bb 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,9 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WAIT_CLIENT,
+						  WE_WAL_SENDER_WRITE_DATA);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1274,9 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WAIT_CLIENT,
+						  WE_WAL_SENDER_WAIT_WAL);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1924,7 +1928,9 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  WAIT_ACTIVITY,
+							  WE_WAL_SENDER_MAIN);
 		}
 	}
 	return;
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..b40c454 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -122,6 +123,50 @@ struct WaitEventSet
 #endif
 };
 
+/*
+ * This must match enum WaitEventIdentifier!
+ */
+const char *const WaitEventNames[] = {
+	/* Activity */
+	"ArchiverMain",
+	"AutoVacuumMain",
+	"BgWriterHibernate",
+	"BgWriterMain",
+	"CheckpointerMain",
+	"PgStatMain",
+	"RecoveryWalAll",
+	"RecoveryWalStream",
+	"SysLoggerMain",
+	"WalReceiverMain",
+	"WalSenderMain",
+	"WalWriterMain",
+	/* Client */
+	"SecureRead",
+	"SecureWrite",
+	"SSLOpenServer",
+	"WalReceiverWaitStart",
+	"WalSenderWaitForWAL",
+	"WalSenderWriteData",
+	/* Extension */
+	"Extension",
+	/* IPC */
+	"BgWorkerShutdown",
+	"BgWorkerStartup",
+	"ExecuteGather",
+	"MessageQueueInternal",
+	"MessageQueuePutMessage",
+	"MessageQueueReceive",
+	"MessageQueueSend",
+	"ParallelFinish",
+	"ProcSignal",
+	"ProcSleep",
+	"SyncRep",
+	/* Timeout */
+	"BaseBackupThrottle",
+	"PgSleep",
+	"RecoveryApplyDelay",
+};
+
 #ifndef WIN32
 /* Are we currently in WaitLatch? The signal handler would like to know. */
 static volatile sig_atomic_t waiting = false;
@@ -297,9 +342,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  uint8 classId, uint16 eventId)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 classId, eventId);
 }
 
 /*
@@ -316,7 +363,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, uint8 classId, uint16 eventId)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +391,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, classId, eventId);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -863,7 +910,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 uint8 classId, uint16 eventId)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -872,6 +920,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 
 	Assert(nevents > 0);
 
+	pgstat_report_wait_start(classId, eventId);
+
 	/*
 	 * Initialize timeout if requested.  We must record the current time so
 	 * that we can determine the remaining timeout if interrupted.
@@ -960,6 +1010,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
@@ -1491,6 +1543,22 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 }
 #endif
 
+
+/*
+ * Return an identifier for a WaitEventSet depending on an event for
+ * statistics collector.
+ */
+const char *
+GetWaitEventIdentifier(uint16 eventId)
+{
+	StaticAssertStmt(lengthof(WaitEventNames) == WE_LAST_TYPE + 1,
+					 "WaitEventNames must match WaitEventIdentifiers");
+	if (eventId > WE_LAST_TYPE)
+		return "???";
+	return WaitEventNames[eventId];
+}
+
+
 /*
  * SetLatch uses SIGUSR1 to wake up the process waiting on the latch.
  *
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..8d8dc99 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -19,6 +19,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker.h"
 #include "storage/procsignal.h"
 #include "storage/shm_mq.h"
@@ -894,7 +895,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_MQ_SEND);
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +992,7 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_MQ_RECEIVE);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1091,7 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_MQ_INTERNAL);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 33e7023..ca29f14 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -39,6 +39,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/autovacuum.h"
 #include "replication/slot.h"
 #include "replication/syncrep.h"
@@ -1212,7 +1213,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_PROC_SLEEP);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1724,7 +1725,7 @@ CheckDeadLockAlert(void)
 void
 ProcWaitForSignal(void)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_IPC, WE_PROC_SIGNAL);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..0e2ff93 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -30,6 +30,7 @@
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "parser/scansup.h"
+#include "pgstat.h"
 #include "postmaster/syslogger.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/fd.h"
@@ -560,7 +561,9 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms,
+						 WAIT_TIMEOUT,
+						 WE_PG_SLEEP);
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 0c98c59..b3f707e 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -721,8 +721,13 @@ typedef enum WaitClass
 	WAIT_LWLOCK_NAMED,
 	WAIT_LWLOCK_TRANCHE,
 	WAIT_LOCK,
-	WAIT_BUFFER_PIN
-}	WaitClass;
+	WAIT_BUFFER_PIN,
+	WAIT_ACTIVITY,
+	WAIT_CLIENT,
+	WAIT_EXTENSION,
+	WAIT_IPC,
+	WAIT_TIMEOUT
+} WaitClass;
 
 
 /* ----------
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..8261830 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -135,6 +135,66 @@ typedef struct WaitEvent
 	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
 } WaitEvent;
 
+/*
+ * List of WaitEventSet identifiers used when reporting activity to
+ * statistics collector. Up to 256 different WaitEventIdentifier can be
+ * handled. Those are classified by category first, and then by
+ * alphabetical order. Events are classified into sub-categories following
+ * some basic hierarchy rules:
+ * - "Activity" for main loops of processes waiting for an event.
+ * - "Client" for a socket awaited when a user is connected.
+ * - "IPC", similarly to "Client", for a socket awaited from another
+ *   server process.
+ * - "Timeout", for a timeout waiting to expire.
+ * - "Extension", to let extension modules a way to define a custom wait
+ *   point.
+ */
+typedef enum WaitEventIdentifier
+{
+	/* Activity */
+	WE_ARCHIVER_MAIN,
+	WE_AUTOVACUUM_MAIN,
+	WE_BGWRITER_HIBERNATE,
+	WE_BGWRITER_MAIN,
+	WE_CHECKPOINTER_MAIN,
+	WE_PGSTAT_MAIN,
+	WE_RECOVERY_WAL_ALL,
+	WE_RECOVERY_WAL_STREAM,
+	WE_SYSLOGGER_MAIN,
+	WE_WAL_RECEIVER_MAIN,
+	WE_WAL_SENDER_MAIN,
+	WE_WAL_WRITER_MAIN,
+	/* Client */
+	WE_SECURE_READ,
+	WE_SECURE_WRITE,
+	WE_SSL_OPEN_SERVER,
+	WE_WAL_RECEIVER_WAIT_START,
+	WE_WAL_SENDER_WAIT_WAL,
+	WE_WAL_SENDER_WRITE_DATA,
+	/* Extension */
+	WE_EXTENSION,
+	/* IPC */
+	WE_BGWORKER_SHUTDOWN,
+	WE_BGWORKER_STARTUP,
+	WE_EXECUTE_GATHER,
+	WE_MQ_INTERNAL,
+	WE_MQ_PUT_MESSAGE,
+	WE_MQ_RECEIVE,
+	WE_MQ_SEND,
+	WE_PARALLEL_FINISH,
+	WE_PROC_SIGNAL,
+	WE_PROC_SLEEP,
+	WE_SYNC_REP,
+	/* Timeout */
+	WE_BASE_BACKUP_THROTTLE,
+	WE_PG_SLEEP,
+	WE_RECOVERY_APPLY_DELAY
+} WaitEventIdentifier;
+
+#define WE_LAST_TYPE WE_RECOVERY_APPLY_DELAY
+
+extern const char *const WaitEventName[];
+
 /* forward declaration to avoid exposing latch.c implementation details */
 typedef struct WaitEventSet WaitEventSet;
 
@@ -155,10 +215,15 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
+				  WaitEvent *occurred_events, int nevents,
+				  uint8 classId, uint16 eventId);
+extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+				  uint8 classId, uint16 eventId);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, uint8 classId,
+				  uint16 eventId);
+extern const char *GetWaitEventIdentifier(uint16 eventId);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 143df4e..e7f76cd 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker.h"
 #include "storage/procsignal.h"
 #include "storage/shm_toc.h"
@@ -279,7 +280,7 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EXTENSION, WE_EXTENSION);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index dd34bc7..15d3874 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -15,6 +15,7 @@
 
 #include "fmgr.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 
 #include "test_shm_mq.h"
 
@@ -230,7 +231,7 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EXTENSION, WE_EXTENSION);
 			ResetLatch(MyLatch);
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 314e371..697dc42 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -227,7 +227,9 @@ worker_spi_main(Datum main_arg)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   worker_spi_naptime * 1000L);
+					   worker_spi_naptime * 1000L,
+					   WAIT_EXTENSION,
+					   WE_EXTENSION);
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#40Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#36)
Re: Tracking wait event for latches

On Tue, Sep 27, 2016 at 8:39 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

Ok, if they really are independent then shouldn't we take advantage of
that at call sites where we might be idle but we might also be waiting
for the network?

I certainly didn't intend for them to be independent, and I don't
think they should be. I think it should be a hierarchy - as it is
currently. I think it's a bad idea to introduce the notational
overhead of having to pass through two integers rather than one
everywhere, and a worse idea to encourage people to think of the
wait_event_type and wait_event are related any way other than
hierarchically.

Actually, I'm still not sold on "Activity" and "Client". I think
"Idle" and "Network" would be better. Everybody knows intuitively
what "Idle" means. "Network" is better than "Client" because it
avoids confusion about user applications vs replication connections or
clients vs servers.

Hmm, I could live with that, if other people like it.

s/auxilliary/auxiliary/, but I wouldn't it be better to say something
more general like "from another process in the cluster"? Background
workers are not generally called auxiliary processes, and some of
these wait points are waiting for those.

Agreed; or perhaps it could even be waiting for another regular backend.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#41Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#40)
Re: Tracking wait event for latches

On Wed, Sep 28, 2016 at 9:35 PM, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Sep 27, 2016 at 8:39 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

Ok, if they really are independent then shouldn't we take advantage of
that at call sites where we might be idle but we might also be waiting
for the network?

I certainly didn't intend for them to be independent, and I don't
think they should be. I think it should be a hierarchy - as it is
currently. I think it's a bad idea to introduce the notational
overhead of having to pass through two integers rather than one
everywhere, and a worse idea to encourage people to think of the
wait_event_type and wait_event are related any way other than
hierarchically.

So should I change back the patch to have only one argument for the
eventId, and guess classId from it?
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#42Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#41)
Re: Tracking wait event for latches

On Wed, Sep 28, 2016 at 8:38 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Wed, Sep 28, 2016 at 9:35 PM, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Sep 27, 2016 at 8:39 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

Ok, if they really are independent then shouldn't we take advantage of
that at call sites where we might be idle but we might also be waiting
for the network?

I certainly didn't intend for them to be independent, and I don't
think they should be. I think it should be a hierarchy - as it is
currently. I think it's a bad idea to introduce the notational
overhead of having to pass through two integers rather than one
everywhere, and a worse idea to encourage people to think of the
wait_event_type and wait_event are related any way other than
hierarchically.

So should I change back the patch to have only one argument for the
eventId, and guess classId from it?

Why would you need to guess? But, yes, I think one argument is much preferable.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#43Lou Picciano
loupicciano@comcast.net
In reply to: Robert Haas (#42)
Python3.4 detection on 9.6 configuration

PostgreSQL Friends:

Trying to build 9.6RC1, with Python3.4, on OpenIndiana (Illumos). It seems the detection of shared library status of the .so has changed. This appears to be related to a different(?) elucidation of python configuration.

A 'hardwired' change to the configure script to trap platform 'solaris' will work, but this seems the inappropriate approach.

Would be happy to work through this here - I'd like to make it a small 'contribution'.

Clipped from configure script:

1.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link an embedded Python application" >&5

2.
$as_echo_n "checking how to link an embedded Python application... " >&6; }

3.

4.
python_libdir=`${PYTHON} -c "import distutils.sysconfig; print(' '.join(filter(None,distutils.sysconfig.get_config_vars('LIBDIR'))))"`

5.
python_ldlibrary=`${PYTHON} -c "import distutils.sysconfig; print(' '.join(filter(None,distutils.sysconfig.get_config_vars('LDLIBRARY'))))"`

6.
python_so=`${PYTHON} -c "import distutils.sysconfig; print(' '.join(filter(None,distutils.sysconfig.get_config_vars('SO'))))"`

7.
echo "-----"

8.
echo "----- LOU MOD: python_so: $python_so"

9.
echo "-----"

10.

11.
configure finds '.so' on Python2.7

12.
configure finds '.cpython-34m.so' on Python3.4

13.

14.
------------- LATER in the config script, the following 'hardwired' change will 'fix' this, but is likely not the right approach:

15.
(our Python _is_ built as a shared lib. So, this is wonky, but it will work: )

16.

17.
# We need libpython as a shared library. With Python >=2.5, we

18.
# check the Py_ENABLE_SHARED setting. On Debian, the setting is not

19.
# correct before the jessie release (http://bugs.debian.org/695979).

20.
# We also want to support older Python versions. So as a fallback

21.
# we see if there is a file that is named like a shared library.

22.

23.
if test "$python_enable_shared" != 1; then

24.
if test "$PORTNAME" = darwin; then

25.
# macOS does supply a .dylib even though Py_ENABLE_SHARED does

26.
# not get set. The file detection logic below doesn't succeed

27.
# on older macOS versions, so make it explicit.

28.
python_enable_shared=1

29.
elif test "$PORTNAME" = win32; then

30.
# Windows also needs an explicit override.

31.
python_enable_shared=1

32.
# ----- MOD BY LOU: ----------------------------------------

33.
elif test "$PORTNAME" = solaris; then

34.
# Solaris explicit override.

35.
python_enable_shared=1

36.
else

37.
# We don't know the platform shared library extension here yet,

38.
# so we try some candidates.

39.
for dlsuffix in .so .sl; do

40.
if ls "$python_libdir"/libpython*${dlsuffix}* >/dev/null 2>&1; then

41.
python_enable_shared=1

42.
break

43.
fi

44.
done

45.
fi

46.
fi

#44Tom Lane
tgl@sss.pgh.pa.us
In reply to: Lou Picciano (#43)
Re: Python3.4 detection on 9.6 configuration

Lou Picciano <loupicciano@comcast.net> writes:

Trying to build 9.6RC1, with Python3.4, on OpenIndiana (Illumos). It
seems the detection of shared library status of the .so has changed.

Changed from what? I don't recall that we've touched that code in quite
some time. What was the last version that worked for you? What
exactly is failing?

I'm having a hard time following your not-really-a-patch, but it looks
like you're proposing forcing python_enable_shared=1 on Solaris, which
sounds like a pretty bad idea. AFAIK the shared-ness of libpython is
up to whoever built it.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#45Lou Picciano
loupicciano@comcast.net
In reply to: Tom Lane (#44)
Re: Python3.4 detection on 9.6 configuration

----- Original Message -----

From: "Tom Lane" <tgl@sss.pgh.pa.us>
To: "Lou Picciano" <loupicciano@comcast.net>
Cc: pgsql-hackers@postgresql.org
Sent: Wednesday, September 28, 2016 9:33:06 AM
Subject: Re: [HACKERS] Python3.4 detection on 9.6 configuration

Lou Picciano <loupicciano@comcast.net> writes:

Trying to build 9.6RC1, with Python3.4, on OpenIndiana (Illumos). It
seems the detection of shared library status of the .so has changed.

Specifically, configure is not finding the .so. It's not that the so isn't built 'shared'; it is.

Changed from what? I don't recall that we've touched that code in quite
some time. What was the last version that worked for you? What
exactly is failing?

Core bit seems to be the python3.4-config behavior:

/usr/bin/python3.4-config --extension-suffix

.cpython-34m.so
I don't know if this is designed behavior for Python3.4 - or if it's a bug there? I'm working this with the Python gang as well.

Of course, this option doesn't exist under Python2.7.

I'm having a hard time following your not-really-a-patch, but it looks
like you're proposing forcing python_enable_shared=1 on Solaris,

Certainly not! I was simply offering this as evidence that PostgreSQL will build just fine, against Python3.4, using this hack. (It's useful in getting us a working build in situ o continue other testing - even before the more elegant fix - whatever that turns out to be!)

which sounds like a pretty bad idea. AFAIK the shared-ness of libpython is
up to whoever built it.

Indeed. As I mentioned, our Python3.4 is built shared.

Show quoted text

regards, tom lane

#46Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#42)
1 attachment(s)
Re: Tracking wait event for latches

On Wed, Sep 28, 2016 at 9:45 PM, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Sep 28, 2016 at 8:38 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

So should I change back the patch to have only one argument for the
eventId, and guess classId from it?

Why would you need to guess?

Incorrect wording from me perhaps? i just meant that processing needs
to know what is the classId coming for a specific eventId.

But, yes, I think one argument is much preferable.

OK. Here is the wanted patch. I have reduced the routines of WaitLatch
& friends to use only one argument, and added what is the classId
associated with a given eventId in an array of multiple fields, giving
something like that:
+ const struct wait_event_entry WaitEventEntries[] = {
+   /* Activity */
+   {WAIT_ACTIVITY, "ArchiverMain"},
[...]

I have cleaned up as well the inclusions of pgstat.h that I added
previously. Patch is attached.
--
Michael

Attachments:

wait-event-set-v10.patchtext/x-diff; charset=US-ASCII; name=wait-event-set-v10.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..9265e00 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -496,7 +496,8 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L,
+								   WE_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index f400785..bb975c1 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,42 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>Activity</>: The server process is idle.  This is used by
+          system processes waiting for activity in their main processing loop.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Extension</>: The server process is waiting for activity
+          in an extension module.  This category is useful for modules to
+          track custom waiting points.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Client</>: The server process is waiting for some activity
+          on a socket from user applications, and that the server expects
+          something to happen that is independent from its internal processes.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>IPC</>: The server process is waiting for some activity
+          from another process in the server.  <literal>wait_event</> will
+          identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Timeout</>: The server process is waiting for a timeout
+          to expire.  <literal>wait_event</> will identify the specific wait
+          point.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1085,6 +1121,143 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="11"><literal>Activity</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stream at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
+        <row>
+         <entry morerows="5"><literal>Client</></entry>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in the code path of an extension, should be used by custom plugins and modules</entry>
+        </row>
+        <row>
+         <entry morerows="10"><literal>IPC</></entry>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to put new message in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSignal</></entry>
+         <entry>Waiting for signal from another backend.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSleep</></entry>
+         <entry>Waiting for a specific lock.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for WAL commit during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry morerows="2"><literal>Timeout</></entry>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index cde0ed3..60d5c9c 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -540,7 +540,8 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  WE_PARALLEL_FINISH);
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index c1b9a97..65fe014 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5827,7 +5827,8 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  WE_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -11387,7 +11388,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+							 wait_time, WE_RECOVERY_WAL_STREAM);
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11550,7 +11551,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L, WE_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..b832d91 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -387,7 +387,7 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_EXECUTE_GATHER);
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index fedb02c..571d4d6 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -419,7 +419,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+								  WE_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..4e16073 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -146,7 +146,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WE_SECURE_READ);
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +248,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WE_SECURE_WRITE);
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index bfe66c6..280ea80 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -171,7 +171,8 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0,
+				  WE_MQ_PUT_MESSAGE);
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1a92ca1..019075e 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -598,7 +598,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   WE_AUTOVACUUM_MAIN);
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 699c934..7da01df 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -969,7 +969,8 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WE_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1009,8 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			break;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WE_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 1002034..a89d39d 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -345,7 +345,8 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */,
+					   WE_BGWRITER_MAIN);
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -372,7 +373,8 @@ BackgroundWriterMain(void)
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
 						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+						   BgWriterDelay * HIBERNATE_FACTOR,
+						   WE_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index d702a48..e18ef9f 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -556,7 +556,8 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   WE_CHECKPOINTER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..018878c 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,7 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							 timeout * 1000L, WE_ARCHIVER_MAIN);
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 96578dc..9679b91 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3155,6 +3155,18 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+			event_type = "Activity";
+			break;
+		case WAIT_CLIENT:
+			event_type = "Client";
+			break;
+		case WAIT_IPC:
+			event_type = "IPC";
+			break;
+		case WAIT_TIMEOUT:
+			event_type = "Timeout";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3196,6 +3208,13 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+		case WAIT_CLIENT:
+		case WAIT_EXTENSION:
+		case WAIT_IPC:
+		case WAIT_TIMEOUT:
+			event_name = GetWaitEventIdentifier(eventId);
+			break;
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3684,8 +3703,7 @@ PgstatCollectorMain(int argc, char *argv[])
 #ifndef WIN32
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
-							   pgStatSock,
-							   -1L);
+					 pgStatSock, -1L, WE_PGSTAT_MAIN);
 #else
 
 		/*
@@ -3701,7 +3719,8 @@ PgstatCollectorMain(int argc, char *argv[])
 		wr = WaitLatchOrSocket(MyLatch,
 		WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
 							   pgStatSock,
-							   2 * 1000L /* msec */ );
+							   2 * 1000L /* msec */,
+							   WE_PGSTAT_MAIN);
 #endif
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..c081224 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -424,7 +424,8 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+							   WE_SYSLOGGER_MAIN);
 
 		if (rc & WL_SOCKET_READABLE)
 		{
@@ -475,7 +476,8 @@ SysLoggerMain(int argc, char *argv[])
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | cur_flags,
-						 cur_timeout);
+						 cur_timeout,
+						 WE_SYSLOGGER_MAIN);
 
 		EnterCriticalSection(&sysloggerSection);
 #endif   /* WIN32 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 11ec56a..5b6fa15 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -290,7 +290,8 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   WE_WAL_WRITER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index da9b7a6..82e8d03 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1286,7 +1286,8 @@ throttle(size_t increment)
 		 */
 		wait_result = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+								(long) (sleep / 1000),
+								WE_BASE_BACKUP_THROTTLE);
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index b442d06..f1acb4a 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -258,7 +258,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  WE_SYNC_REP);
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..046a760 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -486,7 +486,8 @@ WalReceiverMain(void)
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   WE_WAL_RECEIVER_MAIN);
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +686,8 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  WE_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c7743da..3cac83b 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,8 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WE_WAL_SENDER_WRITE_DATA);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1273,8 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WE_WAL_SENDER_WAIT_WAL);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1924,7 +1926,8 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  WE_WAL_SENDER_MAIN);
 		}
 	}
 	return;
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..a0be2a7 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -122,6 +123,50 @@ struct WaitEventSet
 #endif
 };
 
+/*
+ * This must match enum WaitEventIdentifier!
+ */
+const struct wait_event_entry WaitEventEntries[] = {
+	/* Activity */
+	{WAIT_ACTIVITY, "ArchiverMain"},
+	{WAIT_ACTIVITY, "AutoVacuumMain"},
+	{WAIT_ACTIVITY, "BgWriterHibernate"},
+	{WAIT_ACTIVITY, "BgWriterMain"},
+	{WAIT_ACTIVITY, "CheckpointerMain"},
+	{WAIT_ACTIVITY, "PgStatMain"},
+	{WAIT_ACTIVITY, "RecoveryWalAll"},
+	{WAIT_ACTIVITY, "RecoveryWalStream"},
+	{WAIT_ACTIVITY, "SysLoggerMain"},
+	{WAIT_ACTIVITY, "WalReceiverMain"},
+	{WAIT_ACTIVITY, "WalSenderMain"},
+	{WAIT_ACTIVITY, "WalWriterMain"},
+	/* Client */
+	{WAIT_CLIENT, "SecureRead"},
+	{WAIT_CLIENT, "SecureWrite"},
+	{WAIT_CLIENT, "SSLOpenServer"},
+	{WAIT_CLIENT, "WalReceiverWaitStart"},
+	{WAIT_CLIENT, "WalSenderWaitForWAL"},
+	{WAIT_CLIENT, "WalSenderWriteData"},
+	/* Extension */
+	{WAIT_EXTENSION, "Extension"},
+	/* IPC */
+	{WAIT_IPC, "BgWorkerShutdown"},
+	{WAIT_IPC, "BgWorkerStartup"},
+	{WAIT_IPC, "ExecuteGather"},
+	{WAIT_IPC, "MessageQueueInternal"},
+	{WAIT_IPC, "MessageQueuePutMessage"},
+	{WAIT_IPC, "MessageQueueReceive"},
+	{WAIT_IPC, "MessageQueueSend"},
+	{WAIT_IPC, "ParallelFinish"},
+	{WAIT_IPC, "ProcSignal"},
+	{WAIT_IPC, "ProcSleep"},
+	{WAIT_IPC, "SyncRep"},
+	/* Timeout */
+	{WAIT_TIMEOUT, "BaseBackupThrottle"},
+	{WAIT_TIMEOUT, "PgSleep"},
+	{WAIT_TIMEOUT, "RecoveryApplyDelay"},
+};
+
 #ifndef WIN32
 /* Are we currently in WaitLatch? The signal handler would like to know. */
 static volatile sig_atomic_t waiting = false;
@@ -297,9 +342,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  uint16 eventId)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 eventId);
 }
 
 /*
@@ -316,7 +363,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, uint16 eventId)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +391,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, eventId);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -863,7 +910,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 uint16 eventId)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -872,6 +920,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 
 	Assert(nevents > 0);
 
+	pgstat_report_wait_start(GetWaitEventClass(eventId), eventId);
+
 	/*
 	 * Initialize timeout if requested.  We must record the current time so
 	 * that we can determine the remaining timeout if interrupted.
@@ -960,6 +1010,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
@@ -1491,6 +1543,37 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 }
 #endif
 
+
+/*
+ * Return an event name for a WaitEventSet event depending on the ID
+ * provided by caller, ID used for the statistics collector.
+ */
+const char *
+GetWaitEventIdentifier(uint16 eventId)
+{
+	StaticAssertStmt(lengthof(WaitEventEntries) == WE_LAST_TYPE + 1,
+					 "WaitEventEntries must match WaitEventIdentifiers");
+	if (eventId > WE_LAST_TYPE)
+		return "???";
+	return WaitEventEntries[eventId].name;
+}
+
+
+/*
+ * Determine the class ID of a WaitEventSet event depending on the ID
+ * provided by caller, ID used for the statistics collector.
+ */
+uint8
+GetWaitEventClass(uint16 eventId)
+{
+	StaticAssertStmt(lengthof(WaitEventEntries) == WE_LAST_TYPE + 1,
+					 "WaitEventEntries must match WaitEventIdentifiers");
+	if (eventId > WE_LAST_TYPE)
+		return WAIT_UNDEFINED;
+	return WaitEventEntries[eventId].classId;
+}
+
+
 /*
  * SetLatch uses SIGUSR1 to wake up the process waiting on the latch.
  *
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..c47cf2b 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -894,7 +894,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_MQ_SEND);
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +991,7 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_MQ_RECEIVE);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1090,7 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_MQ_INTERNAL);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 33e7023..23deeb6 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -1212,7 +1212,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_PROC_SLEEP);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1724,7 +1724,7 @@ CheckDeadLockAlert(void)
 void
 ProcWaitForSignal(void)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_PROC_SIGNAL);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..642f619 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -560,7 +560,8 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms,
+						 WE_PG_SLEEP);
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 0c98c59..b3f707e 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -721,8 +721,13 @@ typedef enum WaitClass
 	WAIT_LWLOCK_NAMED,
 	WAIT_LWLOCK_TRANCHE,
 	WAIT_LOCK,
-	WAIT_BUFFER_PIN
-}	WaitClass;
+	WAIT_BUFFER_PIN,
+	WAIT_ACTIVITY,
+	WAIT_CLIENT,
+	WAIT_EXTENSION,
+	WAIT_IPC,
+	WAIT_TIMEOUT
+} WaitClass;
 
 
 /* ----------
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..c4ad1cb 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -135,6 +135,75 @@ typedef struct WaitEvent
 	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
 } WaitEvent;
 
+/*
+ * List of WaitEventSet identifiers used when reporting activity to
+ * statistics collector. Up to 256 different WaitEventIdentifier can be
+ * handled. Those are classified by category first, and then by
+ * alphabetical order. Events are classified into sub-categories following
+ * some basic hierarchy rules:
+ * - "Activity" for main loops of processes waiting for an event.
+ * - "Client" for a socket awaited when a user is connected.
+ * - "IPC", similarly to "Client", for a socket awaited from another
+ *   server process.
+ * - "Timeout", for a timeout waiting to expire.
+ * - "Extension", to let extension modules a way to define a custom wait
+ *   point.
+ */
+typedef enum WaitEventIdentifier
+{
+	/* Activity */
+	WE_ARCHIVER_MAIN,
+	WE_AUTOVACUUM_MAIN,
+	WE_BGWRITER_HIBERNATE,
+	WE_BGWRITER_MAIN,
+	WE_CHECKPOINTER_MAIN,
+	WE_PGSTAT_MAIN,
+	WE_RECOVERY_WAL_ALL,
+	WE_RECOVERY_WAL_STREAM,
+	WE_SYSLOGGER_MAIN,
+	WE_WAL_RECEIVER_MAIN,
+	WE_WAL_SENDER_MAIN,
+	WE_WAL_WRITER_MAIN,
+	/* Client */
+	WE_SECURE_READ,
+	WE_SECURE_WRITE,
+	WE_SSL_OPEN_SERVER,
+	WE_WAL_RECEIVER_WAIT_START,
+	WE_WAL_SENDER_WAIT_WAL,
+	WE_WAL_SENDER_WRITE_DATA,
+	/* Extension */
+	WE_EXTENSION,
+	/* IPC */
+	WE_BGWORKER_SHUTDOWN,
+	WE_BGWORKER_STARTUP,
+	WE_EXECUTE_GATHER,
+	WE_MQ_INTERNAL,
+	WE_MQ_PUT_MESSAGE,
+	WE_MQ_RECEIVE,
+	WE_MQ_SEND,
+	WE_PARALLEL_FINISH,
+	WE_PROC_SIGNAL,
+	WE_PROC_SLEEP,
+	WE_SYNC_REP,
+	/* Timeout */
+	WE_BASE_BACKUP_THROTTLE,
+	WE_PG_SLEEP,
+	WE_RECOVERY_APPLY_DELAY
+} WaitEventIdentifier;
+
+#define WE_LAST_TYPE WE_RECOVERY_APPLY_DELAY
+
+/*
+ * The information details about each WaitEventIdentifier listed above
+ * are specified by an array of name-pair values.
+ */
+struct wait_event_entry
+{
+	uint8       classId;
+	const char *name;
+};
+extern const struct wait_event_entry WaitEventEntries[];
+
 /* forward declaration to avoid exposing latch.c implementation details */
 typedef struct WaitEventSet WaitEventSet;
 
@@ -155,10 +224,15 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
+				  WaitEvent *occurred_events, int nevents,
+				  uint16 eventId);
+extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+				  uint16 eventId);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, uint16 eventId);
+extern uint8 GetWaitEventClass(uint16 eventId);
+extern const char *GetWaitEventIdentifier(uint16 eventId);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 143df4e..adebfc8 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -279,7 +279,7 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_EXTENSION);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index dd34bc7..22145db 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -230,7 +230,7 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WE_EXTENSION);
 			ResetLatch(MyLatch);
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 7c9a3eb..9673b20 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -227,7 +227,8 @@ worker_spi_main(Datum main_arg)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   worker_spi_naptime * 1000L);
+					   worker_spi_naptime * 1000L,
+					   WE_EXTENSION);
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#47Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#46)
Re: Tracking wait event for latches

On Wed, Sep 28, 2016 at 9:40 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Wed, Sep 28, 2016 at 9:45 PM, Robert Haas <robertmhaas@gmail.com> wrote:

On Wed, Sep 28, 2016 at 8:38 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

So should I change back the patch to have only one argument for the
eventId, and guess classId from it?

Why would you need to guess?

Incorrect wording from me perhaps? i just meant that processing needs
to know what is the classId coming for a specific eventId.

But, yes, I think one argument is much preferable.

OK. Here is the wanted patch. I have reduced the routines of WaitLatch
& friends to use only one argument, and added what is the classId
associated with a given eventId in an array of multiple fields, giving
something like that:
+ const struct wait_event_entry WaitEventEntries[] = {
+   /* Activity */
+   {WAIT_ACTIVITY, "ArchiverMain"},
[...]

I have cleaned up as well the inclusions of pgstat.h that I added
previously. Patch is attached.

It seems to me that you haven't tested this patch very carefully,
because as far as I can see it breaks wait event reporting for both
heavyweight locks and buffer pins - or in other words two out of the
three existing cases covered by the mechanism you are patching.

The heavyweight lock portion is broken because WaitOnLock() reports a
Lock wait before calling ProcSleep(), which now clobbers it. Instead
of reporting that we're waiting for Lock/relation, a LOCK TABLE
statement that hangs now reports IPC/ProcSleep. Similarly, a conflict
over a tuple lock is now also reported as IPC/ProcSleep, and ditto for
any other kind of lock, which were all reported separately before.
Obviously, that's no good. I think it would be just fine to push down
setting the wait event into ProcSleep(), doing it when we actually
WaitLatch() or ResolveRecoveryConflictWithLock(), but we ought to
report that we're waiting for the heavyweight lock, not ProcSleep().

As for buffer pins, note that LockBufferForCleanup() calls
ProcWaitForSignal(), which now overwrites the wait event set in by its
caller. I think we could just make ProcWaitForSignal() take a wait
event as an argument; then LockBufferForCleanup() could pass an
appropriate constant and other callers of ProcWaitForSignal() could
pass their own wait events.

The way that we're constructing the wait event ID that ultimately gets
advertised in pg_stat_activity is a bit silly in this patch. We start
with an event ID (which is an integer) and we then do an array lookup
(via GetWaitEventClass) to get a second integer which is then XOR'd
back into the first integer (via pgstat_report_wait_start) before
storing it in the PGPROC. New idea: let's change
pgstat_report_wait_start() to take a single integer argument which it
stores without interpretation. Let's separate the construction of the
wait event into a separate macro, say make_wait_event(). Then
existing callers of pgstat_report_wait_start(a, b) will instead do
pgstat_report_wait_start(make_wait_event(a, b)), but callers that want
to construct the wait event and push it through a few levels of the
call stack before advertising it only need to pass one integer. If
done correctly, this should be cleaner and faster than what you've got
right now.

I am not a huge fan of the "WE_" prefix you've used. It's not
terrible, but "we" doesn't obviously stand for "wait event" and it's
also a word itself. I think a little more verbosity would add
clarity. Maybe we could go with WAIT_EVENT_ instead.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#48Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#47)
1 attachment(s)
Re: Tracking wait event for latches

On Fri, Sep 30, 2016 at 1:48 AM, Robert Haas <robertmhaas@gmail.com> wrote:

It seems to me that you haven't tested this patch very carefully,
because as far as I can see it breaks wait event reporting for both
heavyweight locks and buffer pins - or in other words two out of the
three existing cases covered by the mechanism you are patching.

Oops. The WaitLatch calls overlap the other things if another event is reported.

The heavyweight lock portion is broken because WaitOnLock() reports a
Lock wait before calling ProcSleep(), which now clobbers it. Instead
of reporting that we're waiting for Lock/relation, a LOCK TABLE
statement that hangs now reports IPC/ProcSleep. Similarly, a conflict
over a tuple lock is now also reported as IPC/ProcSleep, and ditto for
any other kind of lock, which were all reported separately before.
Obviously, that's no good. I think it would be just fine to push down
setting the wait event into ProcSleep(), doing it when we actually
WaitLatch() or ResolveRecoveryConflictWithLock(), but we ought to
report that we're waiting for the heavyweight lock, not ProcSleep().

Somewhat similar problem with ResolveRecoveryConflictWithBufferPin(),
per this reasoning what we should wait for here is a buffer pin and
not a IPC/WaitForSignal.

As for buffer pins, note that LockBufferForCleanup() calls
ProcWaitForSignal(), which now overwrites the wait event set in by its
caller. I think we could just make ProcWaitForSignal() take a wait
event as an argument; then LockBufferForCleanup() could pass an
appropriate constant and other callers of ProcWaitForSignal() could
pass their own wait events.

Agreed. So changed the patch this way.

The way that we're constructing the wait event ID that ultimately gets
advertised in pg_stat_activity is a bit silly in this patch. We start
with an event ID (which is an integer) and we then do an array lookup
(via GetWaitEventClass) to get a second integer which is then XOR'd
back into the first integer (via pgstat_report_wait_start) before
storing it in the PGPROC. New idea: let's change
pgstat_report_wait_start() to take a single integer argument which it
stores without interpretation. Let's separate the construction of the
wait event into a separate macro, say make_wait_event(). Then
existing callers of pgstat_report_wait_start(a, b) will instead do
pgstat_report_wait_start(make_wait_event(a, b)), but callers that want
to construct the wait event and push it through a few levels of the
call stack before advertising it only need to pass one integer. If
done correctly, this should be cleaner and faster than what you've got
right now.

Hm, OK.... So ProcSleep() and ProcWaitForSignal() need an additional
argument in the shape of a uint32 wait_event. So we need to change the
shape of WaitLatch&friends to have also just a uint32 as an extra
argument. This has as result to force all the callers of WaitLatch to
use the new routine pgstat_make_wait_event() (renamed it), so pgstat.h
needs to be included where WaitLatch() is called.

And this has as consequence to make the addition of classId in
WaitEventEntries completely useless, because including them has the
advantage to reduce the places where pgstat.h is included, but as
make_wait_event is needed to the wait_event value...

I am not a huge fan of the "WE_" prefix you've used. It's not
terrible, but "we" doesn't obviously stand for "wait event" and it's
also a word itself. I think a little more verbosity would add
clarity. Maybe we could go with WAIT_EVENT_ instead.

OK. Switched that back. Hopefully you find the result cleaner.
--
Michael

Attachments:

wait-event-set-v11.patchtext/x-diff; charset=US-ASCII; name=wait-event-set-v11.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..0841fd7 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
 #include "access/xact.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
@@ -491,12 +492,18 @@ pgfdw_get_result(PGconn *conn, const char *query)
 		while (PQisBusy(conn))
 		{
 			int			wc;
+			uint32		wait_event;
+
+			/* Define event to wait for */
+			wait_event = pgstat_make_wait_event(WAIT_EXTENSION,
+												WAIT_EVENT_EXTENSION);
 
 			/* Sleep until there's something to do */
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L,
+								   wait_event);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index f400785..bb975c1 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,42 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>Activity</>: The server process is idle.  This is used by
+          system processes waiting for activity in their main processing loop.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Extension</>: The server process is waiting for activity
+          in an extension module.  This category is useful for modules to
+          track custom waiting points.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Client</>: The server process is waiting for some activity
+          on a socket from user applications, and that the server expects
+          something to happen that is independent from its internal processes.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>IPC</>: The server process is waiting for some activity
+          from another process in the server.  <literal>wait_event</> will
+          identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Timeout</>: The server process is waiting for a timeout
+          to expire.  <literal>wait_event</> will identify the specific wait
+          point.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1085,6 +1121,143 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="11"><literal>Activity</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stream at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
+        <row>
+         <entry morerows="5"><literal>Client</></entry>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in the code path of an extension, should be used by custom plugins and modules</entry>
+        </row>
+        <row>
+         <entry morerows="10"><literal>IPC</></entry>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to put new message in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSignal</></entry>
+         <entry>Waiting for signal from another backend.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSleep</></entry>
+         <entry>Waiting for a specific lock.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for WAL commit during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry morerows="2"><literal>Timeout</></entry>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index cde0ed3..58843ec 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -23,6 +23,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "optimizer/planmain.h"
 #include "storage/ipc.h"
 #include "storage/sinval.h"
@@ -540,7 +541,9 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  pgstat_make_wait_event(WAIT_IPC,
+										 WAIT_EVENT_PARALLEL_FINISH));
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index c1b9a97..52362d6 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5827,7 +5827,9 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  pgstat_make_wait_event(WAIT_TIMEOUT,
+										 WAIT_EVENT_RECOVERY_APPLY_DELAY));
 	}
 	return true;
 }
@@ -11380,14 +11382,19 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 						long		secs,
 									wait_time;
 						int			usecs;
+						uint32		wait_event;
 
 						TimestampDifference(last_fail_time, now, &secs, &usecs);
 						wait_time = wal_retrieve_retry_interval -
 							(secs * 1000 + usecs / 1000);
+						wait_event = pgstat_make_wait_event(WAIT_ACTIVITY,
+											WAIT_EVENT_RECOVERY_APPLY_DELAY);
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+							 wait_time,
+							 wait_event);
+
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11550,7 +11557,9 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L,
+							  pgstat_make_wait_event(WAIT_ACTIVITY,
+												WAIT_EVENT_RECOVERY_WAL_ALL));
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..1a7b303 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -38,6 +38,7 @@
 #include "executor/nodeSubplan.h"
 #include "executor/tqueue.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
 
@@ -387,7 +388,9 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0,
+					  pgstat_make_wait_event(WAIT_IPC,
+											 WAIT_EVENT_EXECUTE_GATHER));
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index fedb02c..bc87a47 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -60,6 +60,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
@@ -419,7 +420,9 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+						  pgstat_make_wait_event(WAIT_CLIENT,
+												 WAIT_EVENT_SSL_OPEN_SERVER));
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..9f89491 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -33,6 +33,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 #include "storage/ipc.h"
@@ -146,7 +147,9 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 pgstat_make_wait_event(WAIT_CLIENT,
+												WAIT_EVENT_SECURE_READ));
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +250,9 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 pgstat_make_wait_event(WAIT_CLIENT,
+												WAIT_EVENT_SECURE_WRITE));
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index bfe66c6..5121335 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -17,6 +17,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 
@@ -171,7 +172,9 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0,
+				  pgstat_make_wait_event(WAIT_IPC,
+										 WAIT_EVENT_MQ_PUT_MESSAGE));
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1a92ca1..93e4567 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -598,7 +598,9 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   pgstat_make_wait_event(WAIT_ACTIVITY,
+											  WAIT_EVENT_AUTOVACUUM_MAIN));
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 699c934..62ce3c8 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -16,6 +16,7 @@
 
 #include "miscadmin.h"
 #include "libpq/pqsignal.h"
+#include "pgstat.h"
 #include "postmaster/bgworker_internals.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -969,7 +970,9 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   pgstat_make_wait_event(WAIT_IPC,
+											  WAIT_EVENT_BGWORKER_STARTUP));
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1011,9 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			break;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   pgstat_make_wait_event(WAIT_IPC,
+											  WAIT_EVENT_BGWORKER_SHUTDOWN));
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 1002034..8683323 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -345,7 +345,9 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */,
+					   pgstat_make_wait_event(WAIT_ACTIVITY,
+											  WAIT_EVENT_BGWRITER_MAIN));
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -371,8 +373,10 @@ BackgroundWriterMain(void)
 			StrategyNotifyBgWriter(MyProc->pgprocno);
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
-						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+				   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+				   BgWriterDelay * HIBERNATE_FACTOR,
+				   pgstat_make_wait_event(WAIT_ACTIVITY,
+										  WAIT_EVENT_BGWRITER_HIBERNATE));
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index d702a48..512ce20 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -556,7 +556,9 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   pgstat_make_wait_event(WAIT_ACTIVITY,
+											  WAIT_EVENT_CHECKPOINTER_MAIN));
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..249674f 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,9 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							 timeout * 1000L,
+							 pgstat_make_wait_event(WAIT_ACTIVITY,
+													WAIT_EVENT_ARCHIVER_MAIN));
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 96578dc..97fd4ea 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3155,6 +3155,18 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+			event_type = "Activity";
+			break;
+		case WAIT_CLIENT:
+			event_type = "Client";
+			break;
+		case WAIT_IPC:
+			event_type = "IPC";
+			break;
+		case WAIT_TIMEOUT:
+			event_type = "Timeout";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3196,6 +3208,13 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+		case WAIT_CLIENT:
+		case WAIT_EXTENSION:
+		case WAIT_IPC:
+		case WAIT_TIMEOUT:
+			event_name = GetWaitEventIdentifier(eventId);
+			break;
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3684,8 +3703,9 @@ PgstatCollectorMain(int argc, char *argv[])
 #ifndef WIN32
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
-							   pgStatSock,
-							   -1L);
+					 pgStatSock, -1L,
+					 pgstat_make_wait_event(WAIT_ACTIVITY,
+											WAIT_EVENT_PGSTAT_MAIN));
 #else
 
 		/*
@@ -3700,8 +3720,10 @@ PgstatCollectorMain(int argc, char *argv[])
 		 */
 		wr = WaitLatchOrSocket(MyLatch,
 		WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
-							   pgStatSock,
-							   2 * 1000L /* msec */ );
+					 pgStatSock,
+					 2 * 1000L /* msec */,
+					 pgstat_make_wait_event(WAIT_ACTIVITY,
+											WAIT_EVENT_PGSTAT_MAIN));
 #endif
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..5658454 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -34,6 +34,7 @@
 #include "lib/stringinfo.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "nodes/pg_list.h"
 #include "pgtime.h"
 #include "postmaster/fork_process.h"
@@ -424,7 +425,9 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+					   pgstat_make_wait_event(WAIT_ACTIVITY,
+											  WAIT_EVENT_SYSLOGGER_MAIN));
 
 		if (rc & WL_SOCKET_READABLE)
 		{
@@ -475,7 +478,9 @@ SysLoggerMain(int argc, char *argv[])
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | cur_flags,
-						 cur_timeout);
+						 cur_timeout,
+						 pgstat_make_wait_event(WAIT_ACTIVITY,
+												WAIT_EVENT_SYSLOGGER_MAIN));
 
 		EnterCriticalSection(&sysloggerSection);
 #endif   /* WIN32 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 11ec56a..1b31582 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -290,7 +290,9 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   pgstat_make_wait_event(WAIT_ACTIVITY,
+											  WAIT_EVENT_WAL_WRITER_MAIN));
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index 1eabaef..76571e5 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1363,8 +1363,10 @@ throttle(size_t increment)
 		 * the maximum time to sleep. Thus the cast to long is safe.
 		 */
 		wait_result = WaitLatch(MyLatch,
-							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+					WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					(long) (sleep / 1000),
+					pgstat_make_wait_event(WAIT_TIMEOUT,
+										   WAIT_EVENT_BASE_BACKUP_THROTTLE));
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index b442d06..47e33b1 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -61,6 +61,7 @@
 
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/syncrep.h"
 #include "replication/walsender.h"
 #include "replication/walsender_private.h"
@@ -258,7 +259,9 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  pgstat_make_wait_event(WAIT_IPC,
+										 WAIT_EVENT_SYNC_REP));
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..f7839c9 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -55,6 +55,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
 #include "storage/ipc.h"
@@ -401,6 +402,7 @@ WalReceiverMain(void)
 				bool		endofwal = false;
 				pgsocket	wait_fd = PGINVALID_SOCKET;
 				int			rc;
+				uint32		wait_event;
 
 				/*
 				 * Exit walreceiver if we're not in recovery. This should not
@@ -482,11 +484,15 @@ WalReceiverMain(void)
 				 * avoiding some system calls.
 				 */
 				Assert(wait_fd != PGINVALID_SOCKET);
+				wait_event = pgstat_make_wait_event(WAIT_ACTIVITY,
+										  WAIT_EVENT_WAL_RECEIVER_MAIN);
 				rc = WaitLatchOrSocket(&walrcv->latch,
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   wait_event);
+
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +691,9 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  pgstat_make_wait_event(WAIT_CLIENT,
+										 WAIT_EVENT_WAL_RECEIVER_WAIT_START));
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c7743da..715785c 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,9 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  pgstat_make_wait_event(WAIT_CLIENT,
+										 WAIT_EVENT_WAL_SENDER_WRITE_DATA));
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1274,9 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  pgstat_make_wait_event(WAIT_CLIENT,
+										 WAIT_EVENT_WAL_SENDER_WAIT_WAL));
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1924,7 +1928,9 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  pgstat_make_wait_event(WAIT_ACTIVITY,
+										 WAIT_EVENT_WAL_SENDER_MAIN));
 		}
 	}
 	return;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 90804a3..84715ae 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3610,6 +3610,7 @@ LockBufferForCleanup(Buffer buffer)
 	for (;;)
 	{
 		uint32		buf_state;
+		uint32		wait_event;
 
 		/* Try to acquire lock */
 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
@@ -3635,8 +3636,8 @@ LockBufferForCleanup(Buffer buffer)
 		UnlockBufHdr(bufHdr, buf_state);
 		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 
-		/* Report the wait */
-		pgstat_report_wait_start(WAIT_BUFFER_PIN, 0);
+		/* Define the event to wait for */
+		wait_event = pgstat_make_wait_event(WAIT_BUFFER_PIN, 0);
 
 		/* Wait to be signaled by UnpinBuffer() */
 		if (InHotStandby)
@@ -3644,14 +3645,12 @@ LockBufferForCleanup(Buffer buffer)
 			/* Publish the bufid that Startup process waits on */
 			SetStartupBufferPinWaitBufId(buffer - 1);
 			/* Set alarm and then wait to be signaled by UnpinBuffer() */
-			ResolveRecoveryConflictWithBufferPin();
+			ResolveRecoveryConflictWithBufferPin(wait_event);
 			/* Reset the published bufid */
 			SetStartupBufferPinWaitBufId(-1);
 		}
 		else
-			ProcWaitForSignal();
-
-		pgstat_report_wait_end();
+			ProcWaitForSignal(wait_event);
 
 		/*
 		 * Remove flag marking us as waiter. Normally this will not be set
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..b91568b 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -122,6 +123,50 @@ struct WaitEventSet
 #endif
 };
 
+/*
+ * This must match enum WaitEventIdentifier!
+ */
+const char *const WaitEventNames[] = {
+	/* Activity */
+	"ArchiverMain",
+	"AutoVacuumMain",
+	"BgWriterHibernate",
+	"BgWriterMain",
+	"CheckpointerMain",
+	"PgStatMain",
+	"RecoveryWalAll",
+	"RecoveryWalStream",
+	"SysLoggerMain",
+	"WalReceiverMain",
+	"WalSenderMain",
+	"WalWriterMain",
+	/* Client */
+	"SecureRead",
+	"SecureWrite",
+	"SSLOpenServer",
+	"WalReceiverWaitStart",
+	"WalSenderWaitForWAL",
+	"WalSenderWriteData",
+	/* Extension */
+	"Extension",
+	/* IPC */
+	"BgWorkerShutdown",
+	"BgWorkerStartup",
+	"ExecuteGather",
+	"MessageQueueInternal",
+	"MessageQueuePutMessage",
+	"MessageQueueReceive",
+	"MessageQueueSend",
+	"ParallelFinish",
+	"ProcSignal",
+	"ProcSleep",
+	"SyncRep",
+	/* Timeout */
+	"BaseBackupThrottle",
+	"PgSleep",
+	"RecoveryApplyDelay"
+};
+
 #ifndef WIN32
 /* Are we currently in WaitLatch? The signal handler would like to know. */
 static volatile sig_atomic_t waiting = false;
@@ -297,9 +342,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  uint32 wait_event)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 wait_event);
 }
 
 /*
@@ -316,7 +363,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, uint32 wait_event)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +391,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, wait_event);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -863,7 +910,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 uint32 wait_event)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -872,6 +920,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 
 	Assert(nevents > 0);
 
+	pgstat_report_wait_start(wait_event);
+
 	/*
 	 * Initialize timeout if requested.  We must record the current time so
 	 * that we can determine the remaining timeout if interrupted.
@@ -960,6 +1010,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
@@ -1491,6 +1543,22 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 }
 #endif
 
+
+/*
+ * Return an event name for a WaitEventSet event depending on the ID
+ * provided by caller, ID used for the statistics collector.
+ */
+const char *
+GetWaitEventIdentifier(uint16 eventId)
+{
+	StaticAssertStmt(lengthof(WaitEventNames) == WAIT_EVENT_LAST_TYPE + 1,
+					 "WaitEventEntries must match WaitEventIdentifiers");
+	if (eventId > WAIT_EVENT_LAST_TYPE)
+		return "???";
+	return WaitEventNames[eventId];
+}
+
+
 /*
  * SetLatch uses SIGUSR1 to wake up the process waiting on the latch.
  *
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..4eea77d 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -19,6 +19,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker.h"
 #include "storage/procsignal.h"
 #include "storage/shm_mq.h"
@@ -894,7 +895,8 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0,
+					  pgstat_make_wait_event(WAIT_IPC, WAIT_EVENT_MQ_SEND));
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +993,8 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0,
+				  pgstat_make_wait_event(WAIT_IPC, WAIT_EVENT_MQ_RECEIVE));
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1093,8 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0,
+				  pgstat_make_wait_event(WAIT_IPC, WAIT_EVENT_MQ_INTERNAL));
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 547f1a8..bf0efef 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -356,7 +356,7 @@ ResolveRecoveryConflictWithDatabase(Oid dbid)
  * will be detected by the deadlock detector within the ordinary backend.
  */
 void
-ResolveRecoveryConflictWithLock(LOCKTAG locktag)
+ResolveRecoveryConflictWithLock(LOCKTAG locktag, uint32 wait_event)
 {
 	TimestampTz ltime;
 
@@ -389,7 +389,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag)
 	}
 
 	/* Wait to be signaled by the release of the Relation Lock */
-	ProcWaitForSignal();
+	ProcWaitForSignal(wait_event);
 
 	/*
 	 * Clear any timeout requests established above.  We assume here that the
@@ -428,7 +428,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag)
  * at least deadlock_timeout.
  */
 void
-ResolveRecoveryConflictWithBufferPin(void)
+ResolveRecoveryConflictWithBufferPin(uint32 wait_event)
 {
 	TimestampTz ltime;
 
@@ -469,7 +469,7 @@ ResolveRecoveryConflictWithBufferPin(void)
 	}
 
 	/* Wait to be signaled by UnpinBuffer() */
-	ProcWaitForSignal();
+	ProcWaitForSignal(wait_event);
 
 	/*
 	 * Clear any timeout requests established above.  We assume here that the
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index dba3809..8fa0b1f 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -1659,6 +1659,7 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 	LOCKMETHODID lockmethodid = LOCALLOCK_LOCKMETHOD(*locallock);
 	LockMethod	lockMethodTable = LockMethods[lockmethodid];
 	char	   *volatile new_status = NULL;
+	uint32		wait_event;
 
 	LOCK_PRINT("WaitOnLock: sleeping on lock",
 			   locallock->lock, locallock->tag.mode);
@@ -1676,7 +1677,9 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 		set_ps_display(new_status, false);
 		new_status[len] = '\0'; /* truncate off " waiting" */
 	}
-	pgstat_report_wait_start(WAIT_LOCK, locallock->tag.lock.locktag_type);
+
+	wait_event = pgstat_make_wait_event(WAIT_LOCK,
+										locallock->tag.lock.locktag_type);
 
 	awaitedLock = locallock;
 	awaitedOwner = owner;
@@ -1700,7 +1703,7 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 	 */
 	PG_TRY();
 	{
-		if (ProcSleep(locallock, lockMethodTable) != STATUS_OK)
+		if (ProcSleep(locallock, lockMethodTable, wait_event) != STATUS_OK)
 		{
 			/*
 			 * We failed as a result of a deadlock, see CheckDeadLock(). Quit
@@ -1724,7 +1727,6 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 		/* In this path, awaitedLock remains set until LockErrorCleanup */
 
 		/* Report change to non-waiting status */
-		pgstat_report_wait_end();
 		if (update_process_title)
 		{
 			set_ps_display(new_status, false);
@@ -1739,7 +1741,6 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 	awaitedLock = NULL;
 
 	/* Report change to non-waiting status */
-	pgstat_report_wait_end();
 	if (update_process_title)
 	{
 		set_ps_display(new_status, false);
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 9d08de7..cc7bef9 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -730,11 +730,16 @@ static inline void
 LWLockReportWaitStart(LWLock *lock)
 {
 	int			lockId = T_ID(lock);
+	uint32		wait_event;
 
 	if (lock->tranche == 0)
-		pgstat_report_wait_start(WAIT_LWLOCK_NAMED, (uint16) lockId);
+		wait_event = pgstat_make_wait_event(WAIT_LWLOCK_NAMED,
+											(uint16) lockId);
 	else
-		pgstat_report_wait_start(WAIT_LWLOCK_TRANCHE, lock->tranche);
+		wait_event = pgstat_make_wait_event(WAIT_LWLOCK_TRANCHE,
+											lock->tranche);
+
+	pgstat_report_wait_start(wait_event);
 }
 
 /*
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 4064b20..f292834 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -1518,7 +1518,7 @@ GetSafeSnapshot(Snapshot origSnapshot)
 				 SxactIsROUnsafe(MySerializableXact)))
 		{
 			LWLockRelease(SerializableXactHashLock);
-			ProcWaitForSignal();
+			ProcWaitForSignal(0);
 			LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
 		}
 		MySerializableXact->flags &= ~SXACT_FLAG_DEFERRABLE_WAITING;
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 33e7023..7353094 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -39,6 +39,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/autovacuum.h"
 #include "replication/slot.h"
 #include "replication/syncrep.h"
@@ -977,6 +978,9 @@ ProcQueueInit(PROC_QUEUE *queue)
  * The lock table's partition lock must be held at entry, and will be held
  * at exit.
  *
+ * wait_event tracks what is the wait event caller is expecting to wait for.
+ * If set to 0 the default wait event for ProcSleep() is used.
+ *
  * Result: STATUS_OK if we acquired the lock, STATUS_ERROR if not (deadlock).
  *
  * ASSUME: that no one will fiddle with the queue until after
@@ -985,7 +989,8 @@ ProcQueueInit(PROC_QUEUE *queue)
  * NOTES: The process queue is now a priority queue for locking.
  */
 int
-ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
+ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable,
+		  uint32 wait_event)
 {
 	LOCKMODE	lockmode = locallock->tag.mode;
 	LOCK	   *lock = locallock->lock;
@@ -1023,6 +1028,14 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 	}
 
 	/*
+	 * If caller did not provide the wait event it is expected to look for,
+	 * set up a default one.
+	 */
+	if (wait_event == 0)
+		wait_event = pgstat_make_wait_event(WAIT_ACTIVITY,
+											WAIT_EVENT_PROC_SLEEP);
+
+	/*
 	 * Determine where to add myself in the wait queue.
 	 *
 	 * Normally I should go at the end of the queue.  However, if I already
@@ -1208,11 +1221,11 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		if (InHotStandby)
 		{
 			/* Set a timer and wait for that or for the Lock to be granted */
-			ResolveRecoveryConflictWithLock(locallock->tag.lock);
+			ResolveRecoveryConflictWithLock(locallock->tag.lock, wait_event);
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, wait_event);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1717,14 +1730,24 @@ CheckDeadLockAlert(void)
 /*
  * ProcWaitForSignal - wait for a signal from another backend.
  *
+ * wait_event tracks what is the wait event caller is expecting to wait for.
+ * If set to 0 the default wait event for ProcWaitForSignal() is used.
+ *
  * As this uses the generic process latch the caller has to be robust against
  * unrelated wakeups: Always check that the desired state has occurred, and
  * wait again if not.
  */
 void
-ProcWaitForSignal(void)
+ProcWaitForSignal(uint32 wait_event)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	/*
+	 * If caller did not provide the wait event it is expected to look for,
+	 * set up a default one.
+	 */
+	if (wait_event == 0)
+		pgstat_make_wait_event(WAIT_IPC, WAIT_EVENT_PROC_SIGNAL);
+
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, wait_event);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..880e72c 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -30,6 +30,7 @@
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "parser/scansup.h"
+#include "pgstat.h"
 #include "postmaster/syslogger.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/fd.h"
@@ -560,7 +561,9 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms,
+						 pgstat_make_wait_event(WAIT_TIMEOUT,
+												WAIT_EVENT_PG_SLEEP));
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 0c98c59..bc8081b 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -721,8 +721,13 @@ typedef enum WaitClass
 	WAIT_LWLOCK_NAMED,
 	WAIT_LWLOCK_TRANCHE,
 	WAIT_LOCK,
-	WAIT_BUFFER_PIN
-}	WaitClass;
+	WAIT_BUFFER_PIN,
+	WAIT_ACTIVITY,
+	WAIT_CLIENT,
+	WAIT_EXTENSION,
+	WAIT_IPC,
+	WAIT_TIMEOUT
+} WaitClass;
 
 
 /* ----------
@@ -1018,23 +1023,18 @@ extern void pgstat_initstats(Relation rel);
  * ----------
  */
 static inline void
-pgstat_report_wait_start(uint8 classId, uint16 eventId)
+pgstat_report_wait_start(uint32 wait_event)
 {
 	volatile PGPROC *proc = MyProc;
-	uint32		wait_event_val;
 
 	if (!pgstat_track_activities || !proc)
 		return;
 
-	wait_event_val = classId;
-	wait_event_val <<= 24;
-	wait_event_val |= eventId;
-
 	/*
 	 * Since this is a four-byte field which is always read and written as
 	 * four-bytes, updates are atomic.
 	 */
-	proc->wait_event_info = wait_event_val;
+	proc->wait_event_info = wait_event;
 }
 
 /* ----------
@@ -1061,6 +1061,16 @@ pgstat_report_wait_end(void)
 	proc->wait_event_info = 0;
 }
 
+/* ----------
+ * pgstat_make_wait_event()
+ *
+ *	Build a value to be used for report in pgstat_report_wait_start().
+ *	This respects the format per the description above.
+ * ----------
+ */
+#define pgstat_make_wait_event(classId, eventId)					\
+	((classId << 24) | eventId)
+
 /* nontransactional event counts are simple enough to inline */
 
 #define pgstat_count_heap_scan(rel)									\
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..293ea99 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -135,6 +135,70 @@ typedef struct WaitEvent
 	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
 } WaitEvent;
 
+/*
+ * List of WaitEventSet identifiers used when reporting activity to
+ * statistics collector. Up to 256 different WaitEventIdentifier can be
+ * handled. Those are classified by category first, and then by
+ * alphabetical order. Events are classified into sub-categories following
+ * some basic hierarchy rules:
+ * - "Activity" for main loops of processes waiting for an event.
+ * - "Client" for a socket awaited when a user is connected.
+ * - "IPC", similarly to "Client", for a socket awaited from another
+ *   server process.
+ * - "Timeout", for a timeout waiting to expire.
+ * - "Extension", to let extension modules a way to define a custom wait
+ *   point.
+ */
+typedef enum WaitEventIdentifier
+{
+	/* Activity */
+	WAIT_EVENT_ARCHIVER_MAIN,
+	WAIT_EVENT_AUTOVACUUM_MAIN,
+	WAIT_EVENT_BGWRITER_HIBERNATE,
+	WAIT_EVENT_BGWRITER_MAIN,
+	WAIT_EVENT_CHECKPOINTER_MAIN,
+	WAIT_EVENT_PGSTAT_MAIN,
+	WAIT_EVENT_RECOVERY_WAL_ALL,
+	WAIT_EVENT_RECOVERY_WAL_STREAM,
+	WAIT_EVENT_SYSLOGGER_MAIN,
+	WAIT_EVENT_WAL_RECEIVER_MAIN,
+	WAIT_EVENT_WAL_SENDER_MAIN,
+	WAIT_EVENT_WAL_WRITER_MAIN,
+	/* Client */
+	WAIT_EVENT_SECURE_READ,
+	WAIT_EVENT_SECURE_WRITE,
+	WAIT_EVENT_SSL_OPEN_SERVER,
+	WAIT_EVENT_WAL_RECEIVER_WAIT_START,
+	WAIT_EVENT_WAL_SENDER_WAIT_WAL,
+	WAIT_EVENT_WAL_SENDER_WRITE_DATA,
+	/* Extension */
+	WAIT_EVENT_EXTENSION,
+	/* IPC */
+	WAIT_EVENT_BGWORKER_SHUTDOWN,
+	WAIT_EVENT_BGWORKER_STARTUP,
+	WAIT_EVENT_EXECUTE_GATHER,
+	WAIT_EVENT_MQ_INTERNAL,
+	WAIT_EVENT_MQ_PUT_MESSAGE,
+	WAIT_EVENT_MQ_RECEIVE,
+	WAIT_EVENT_MQ_SEND,
+	WAIT_EVENT_PARALLEL_FINISH,
+	WAIT_EVENT_PROC_SIGNAL,
+	WAIT_EVENT_PROC_SLEEP,
+	WAIT_EVENT_SYNC_REP,
+	/* Timeout */
+	WAIT_EVENT_BASE_BACKUP_THROTTLE,
+	WAIT_EVENT_PG_SLEEP,
+	WAIT_EVENT_RECOVERY_APPLY_DELAY
+} WaitEventIdentifier;
+
+#define WAIT_EVENT_LAST_TYPE WAIT_EVENT_RECOVERY_APPLY_DELAY
+
+/*
+ * The information details about each WaitEventIdentifier listed above
+ * are specified by an array of name-pair values.
+ */
+extern const char *const WaitEventName[];
+
 /* forward declaration to avoid exposing latch.c implementation details */
 typedef struct WaitEventSet WaitEventSet;
 
@@ -155,10 +219,14 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
+				  WaitEvent *occurred_events, int nevents,
+				  uint32 wait_event);
+extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+				  uint32 wait_event);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, uint32 wait_event);
+extern const char *GetWaitEventIdentifier(uint16 eventId);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index f576f05..65212ac 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -284,14 +284,15 @@ extern bool HaveNFreeProcs(int n);
 extern void ProcReleaseLocks(bool isCommit);
 
 extern void ProcQueueInit(PROC_QUEUE *queue);
-extern int	ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable);
+extern int	ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable,
+					  uint32 wait_event);
 extern PGPROC *ProcWakeup(PGPROC *proc, int waitStatus);
 extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock);
 extern void CheckDeadLockAlert(void);
 extern bool IsWaitingForLock(void);
 extern void LockErrorCleanup(void);
 
-extern void ProcWaitForSignal(void);
+extern void ProcWaitForSignal(uint32 wait_event);
 extern void ProcSendSignal(int pid);
 
 extern void BecomeLockGroupLeader(void);
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
index dcebf72..3c189c2 100644
--- a/src/include/storage/standby.h
+++ b/src/include/storage/standby.h
@@ -32,8 +32,9 @@ extern void ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid,
 extern void ResolveRecoveryConflictWithTablespace(Oid tsid);
 extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
 
-extern void ResolveRecoveryConflictWithLock(LOCKTAG locktag);
-extern void ResolveRecoveryConflictWithBufferPin(void);
+extern void ResolveRecoveryConflictWithLock(LOCKTAG locktag,
+											uint32 wait_event);
+extern void ResolveRecoveryConflictWithBufferPin(uint32 wait_event);
 extern void CheckRecoveryConflictDeadlock(void);
 extern void StandbyDeadLockHandler(void);
 extern void StandbyTimeoutHandler(void);
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 143df4e..64f9b8b 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker.h"
 #include "storage/procsignal.h"
 #include "storage/shm_toc.h"
@@ -279,7 +280,9 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0,
+				  pgstat_make_wait_event(WAIT_EXTENSION,
+										 WAIT_EVENT_EXTENSION));
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index dd34bc7..9b3e80e 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -15,6 +15,7 @@
 
 #include "fmgr.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 
 #include "test_shm_mq.h"
 
@@ -230,7 +231,9 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0,
+					  pgstat_make_wait_event(WAIT_EXTENSION,
+											 WAIT_EVENT_EXTENSION));
 			ResetLatch(MyLatch);
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 7c9a3eb..d738f21 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -227,7 +227,9 @@ worker_spi_main(Datum main_arg)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   worker_spi_naptime * 1000L);
+					   worker_spi_naptime * 1000L,
+					   pgstat_make_wait_event(WAIT_EXTENSION,
+											  WAIT_EVENT_EXTENSION));
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#49Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Michael Paquier (#48)
Re: Tracking wait event for latches

On Fri, Sep 30, 2016 at 5:47 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Fri, Sep 30, 2016 at 1:48 AM, Robert Haas <robertmhaas@gmail.com> wrote:

The way that we're constructing the wait event ID that ultimately gets
advertised in pg_stat_activity is a bit silly in this patch. We start
with an event ID (which is an integer) and we then do an array lookup
(via GetWaitEventClass) to get a second integer which is then XOR'd
back into the first integer (via pgstat_report_wait_start) before
storing it in the PGPROC. New idea: let's change
pgstat_report_wait_start() to take a single integer argument which it
stores without interpretation. Let's separate the construction of the
wait event into a separate macro, say make_wait_event(). Then
existing callers of pgstat_report_wait_start(a, b) will instead do
pgstat_report_wait_start(make_wait_event(a, b)), but callers that want
to construct the wait event and push it through a few levels of the
call stack before advertising it only need to pass one integer. If
done correctly, this should be cleaner and faster than what you've got
right now.

Hm, OK.... So ProcSleep() and ProcWaitForSignal() need an additional
argument in the shape of a uint32 wait_event. So we need to change the
shape of WaitLatch&friends to have also just a uint32 as an extra
argument. This has as result to force all the callers of WaitLatch to
use the new routine pgstat_make_wait_event() (renamed it), so pgstat.h
needs to be included where WaitLatch() is called.

Hmm. I like the use of pgstat in that name. It helps with the
confusion created by the overloading of the term 'wait event' in the
pg_stat_activity view and the WaitEventSet API, by putting it into a
different pseudo-namespace.

+ uint32 wait_event;

How about a typedef for that instead of using raw uint32 everywhere?
Something like pgstat_wait_descriptor? Then a variable name like
pgstat_desc, since this is most definitely not just a wait_event
anymore.

+ /* Define event to wait for */

It's not defining the event to wait for at all, it's building a
description for pgstat.

+ wait_event = pgstat_make_wait_event(WAIT_EXTENSION,
+ WAIT_EVENT_EXTENSION);

It's not making a wait event, it's combining a class and an event.
How about something like this:

pgstat_desc = pgstat_make_wait_descriptor(WAIT_CLASS_EXTENSION,
WAIT_EVENT_EXTENSION)?

/* Sleep until there's something to do */
wc = WaitLatchOrSocket(MyLatch,
WL_LATCH_SET | WL_SOCKET_READABLE,
PQsocket(conn),
- -1L);
+ -1L,
+ wait_event);

... then use 'pgstat_desc' here.

--
Thomas Munro
http://www.enterprisedb.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#50Michael Paquier
michael.paquier@gmail.com
In reply to: Thomas Munro (#49)
1 attachment(s)
Re: Tracking wait event for latches

On Mon, Oct 3, 2016 at 12:35 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

Hmm. I like the use of pgstat in that name. It helps with the
confusion created by the overloading of the term 'wait event' in the
pg_stat_activity view and the WaitEventSet API, by putting it into a
different pseudo-namespace.

+ uint32 wait_event;

How about a typedef for that instead of using raw uint32 everywhere?
Something like pgstat_wait_descriptor? Then a variable name like
pgstat_desc, since this is most definitely not just a wait_event
anymore.

We cannot do that because of latch.h, which now only includes
<signal.h> and it would be a bad idea to add more dependencies to
PG-specific headers.

+ /* Define event to wait for */

It's not defining the event to wait for at all, it's building a
description for pgstat.

+ wait_event = pgstat_make_wait_event(WAIT_EXTENSION,
+ WAIT_EVENT_EXTENSION);

It's not making a wait event, it's combining a class and an event.
How about something like this:

pgstat_desc = pgstat_make_wait_descriptor(WAIT_CLASS_EXTENSION,
WAIT_EVENT_EXTENSION)?

Maybe I sound stupid here, but I am trying to keep the same of this
macro short so I'd go for pgstat_make_wait_desc().

/* Sleep until there's something to do */
wc = WaitLatchOrSocket(MyLatch,
WL_LATCH_SET | WL_SOCKET_READABLE,
PQsocket(conn),
- -1L);
+ -1L,
+ wait_event);

... then use 'pgstat_desc' here.

For this one I agree, your naming is better. It is kind of good to let
callers of WaitLatch know where this is actually used. I have added as
well comments on top of WaitLatch & friends to mention what
pgstat_desc does, that's useful for the user.
--
Michael

Attachments:

wait-event-set-v12.patchtext/x-diff; charset=US-ASCII; name=wait-event-set-v12.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..ffa72b8 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
 #include "access/xact.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
@@ -491,12 +492,18 @@ pgfdw_get_result(PGconn *conn, const char *query)
 		while (PQisBusy(conn))
 		{
 			int			wc;
+			uint32		pgstat_desc;
+
+			/* Define event to wait for */
+			pgstat_desc = pgstat_make_wait_desc(WAIT_EXTENSION,
+												WAIT_EVENT_EXTENSION);
 
 			/* Sleep until there's something to do */
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L,
+								   pgstat_desc);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index f400785..bb975c1 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,42 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>Activity</>: The server process is idle.  This is used by
+          system processes waiting for activity in their main processing loop.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Extension</>: The server process is waiting for activity
+          in an extension module.  This category is useful for modules to
+          track custom waiting points.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Client</>: The server process is waiting for some activity
+          on a socket from user applications, and that the server expects
+          something to happen that is independent from its internal processes.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>IPC</>: The server process is waiting for some activity
+          from another process in the server.  <literal>wait_event</> will
+          identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Timeout</>: The server process is waiting for a timeout
+          to expire.  <literal>wait_event</> will identify the specific wait
+          point.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1085,6 +1121,143 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="11"><literal>Activity</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stream at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
+        <row>
+         <entry morerows="5"><literal>Client</></entry>
+         <entry><literal>SecureRead</></entry>
+         <entry>Waiting to read data from a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SecureWrite</></entry>
+         <entry>Waiting to write data to a secure connection.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in the code path of an extension, should be used by custom plugins and modules</entry>
+        </row>
+        <row>
+         <entry morerows="10"><literal>IPC</></entry>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to put new message in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSignal</></entry>
+         <entry>Waiting for signal from another backend.</entry>
+        </row>
+        <row>
+         <entry><literal>ProcSleep</></entry>
+         <entry>Waiting for a specific lock.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for WAL commit during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry morerows="2"><literal>Timeout</></entry>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index cde0ed3..dd0eb76 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -23,6 +23,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "optimizer/planmain.h"
 #include "storage/ipc.h"
 #include "storage/sinval.h"
@@ -540,7 +541,9 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  pgstat_make_wait_desc(WAIT_IPC,
+										WAIT_EVENT_PARALLEL_FINISH));
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index c1b9a97..8e7626f 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5827,7 +5827,9 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  pgstat_make_wait_desc(WAIT_TIMEOUT,
+										WAIT_EVENT_RECOVERY_APPLY_DELAY));
 	}
 	return true;
 }
@@ -11380,14 +11382,19 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 						long		secs,
 									wait_time;
 						int			usecs;
+						uint32		pgstat_desc;
 
 						TimestampDifference(last_fail_time, now, &secs, &usecs);
 						wait_time = wal_retrieve_retry_interval -
 							(secs * 1000 + usecs / 1000);
+						pgstat_desc = pgstat_make_wait_desc(WAIT_ACTIVITY,
+											WAIT_EVENT_RECOVERY_APPLY_DELAY);
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+							 wait_time,
+							 pgstat_desc);
+
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11550,7 +11557,9 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L,
+							  pgstat_make_wait_desc(WAIT_ACTIVITY,
+												WAIT_EVENT_RECOVERY_WAL_ALL));
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..af2f0f1 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -38,6 +38,7 @@
 #include "executor/nodeSubplan.h"
 #include "executor/tqueue.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
 
@@ -387,7 +388,9 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0,
+					  pgstat_make_wait_desc(WAIT_IPC,
+											WAIT_EVENT_EXECUTE_GATHER));
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index fedb02c..4dfe969 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -60,6 +60,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
@@ -419,7 +420,9 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+						  pgstat_make_wait_desc(WAIT_CLIENT,
+												WAIT_EVENT_SSL_OPEN_SERVER));
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..323283e 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -33,6 +33,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 #include "storage/ipc.h"
@@ -146,7 +147,9 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 pgstat_make_wait_desc(WAIT_CLIENT,
+											   WAIT_EVENT_SECURE_READ));
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +250,9 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 pgstat_make_wait_desc(WAIT_CLIENT,
+											   WAIT_EVENT_SECURE_WRITE));
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index bfe66c6..3b697e6 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -17,6 +17,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 
@@ -171,7 +172,9 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0,
+				  pgstat_make_wait_desc(WAIT_IPC,
+										WAIT_EVENT_MQ_PUT_MESSAGE));
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1a92ca1..7f0ad32 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -598,7 +598,9 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   pgstat_make_wait_desc(WAIT_ACTIVITY,
+											 WAIT_EVENT_AUTOVACUUM_MAIN));
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 699c934..3f2457a 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -16,6 +16,7 @@
 
 #include "miscadmin.h"
 #include "libpq/pqsignal.h"
+#include "pgstat.h"
 #include "postmaster/bgworker_internals.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -969,7 +970,9 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   pgstat_make_wait_desc(WAIT_IPC,
+											 WAIT_EVENT_BGWORKER_STARTUP));
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1011,9 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			break;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   pgstat_make_wait_desc(WAIT_IPC,
+											 WAIT_EVENT_BGWORKER_SHUTDOWN));
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 1002034..1326d17 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -345,7 +345,9 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */,
+					   pgstat_make_wait_desc(WAIT_ACTIVITY,
+											 WAIT_EVENT_BGWRITER_MAIN));
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -371,8 +373,10 @@ BackgroundWriterMain(void)
 			StrategyNotifyBgWriter(MyProc->pgprocno);
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
-						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+				   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+				   BgWriterDelay * HIBERNATE_FACTOR,
+				   pgstat_make_wait_desc(WAIT_ACTIVITY,
+										 WAIT_EVENT_BGWRITER_HIBERNATE));
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index d702a48..9d3121d 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -556,7 +556,9 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   pgstat_make_wait_desc(WAIT_ACTIVITY,
+											 WAIT_EVENT_CHECKPOINTER_MAIN));
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..e54e30a 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,9 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							 timeout * 1000L,
+							 pgstat_make_wait_desc(WAIT_ACTIVITY,
+												   WAIT_EVENT_ARCHIVER_MAIN));
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 96578dc..fef1aab 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3155,6 +3155,18 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+			event_type = "Activity";
+			break;
+		case WAIT_CLIENT:
+			event_type = "Client";
+			break;
+		case WAIT_IPC:
+			event_type = "IPC";
+			break;
+		case WAIT_TIMEOUT:
+			event_type = "Timeout";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3196,6 +3208,13 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+		case WAIT_CLIENT:
+		case WAIT_EXTENSION:
+		case WAIT_IPC:
+		case WAIT_TIMEOUT:
+			event_name = GetWaitEventIdentifier(eventId);
+			break;
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3684,8 +3703,9 @@ PgstatCollectorMain(int argc, char *argv[])
 #ifndef WIN32
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
-							   pgStatSock,
-							   -1L);
+					 pgStatSock, -1L,
+					 pgstat_make_wait_desc(WAIT_ACTIVITY,
+										   WAIT_EVENT_PGSTAT_MAIN));
 #else
 
 		/*
@@ -3700,8 +3720,10 @@ PgstatCollectorMain(int argc, char *argv[])
 		 */
 		wr = WaitLatchOrSocket(MyLatch,
 		WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
-							   pgStatSock,
-							   2 * 1000L /* msec */ );
+					 pgStatSock,
+					 2 * 1000L /* msec */,
+					 pgstat_make_wait_desc(WAIT_ACTIVITY,
+										   WAIT_EVENT_PGSTAT_MAIN));
 #endif
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..358d3ad 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -34,6 +34,7 @@
 #include "lib/stringinfo.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "nodes/pg_list.h"
 #include "pgtime.h"
 #include "postmaster/fork_process.h"
@@ -424,7 +425,9 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+					   pgstat_make_wait_desc(WAIT_ACTIVITY,
+											 WAIT_EVENT_SYSLOGGER_MAIN));
 
 		if (rc & WL_SOCKET_READABLE)
 		{
@@ -475,7 +478,9 @@ SysLoggerMain(int argc, char *argv[])
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | cur_flags,
-						 cur_timeout);
+						 cur_timeout,
+						 pgstat_make_wait_desc(WAIT_ACTIVITY,
+											   WAIT_EVENT_SYSLOGGER_MAIN));
 
 		EnterCriticalSection(&sysloggerSection);
 #endif   /* WIN32 */
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 11ec56a..a96c4be 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -290,7 +290,9 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   pgstat_make_wait_desc(WAIT_ACTIVITY,
+											 WAIT_EVENT_WAL_WRITER_MAIN));
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index 1eabaef..ea6286e 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1363,8 +1363,10 @@ throttle(size_t increment)
 		 * the maximum time to sleep. Thus the cast to long is safe.
 		 */
 		wait_result = WaitLatch(MyLatch,
-							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+					WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
+					(long) (sleep / 1000),
+					pgstat_make_wait_desc(WAIT_TIMEOUT,
+										  WAIT_EVENT_BASE_BACKUP_THROTTLE));
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index b442d06..d6b8d92 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -61,6 +61,7 @@
 
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/syncrep.h"
 #include "replication/walsender.h"
 #include "replication/walsender_private.h"
@@ -258,7 +259,9 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  pgstat_make_wait_desc(WAIT_IPC,
+										WAIT_EVENT_SYNC_REP));
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..8715332 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -55,6 +55,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
 #include "storage/ipc.h"
@@ -401,6 +402,7 @@ WalReceiverMain(void)
 				bool		endofwal = false;
 				pgsocket	wait_fd = PGINVALID_SOCKET;
 				int			rc;
+				uint32		pgstat_desc;
 
 				/*
 				 * Exit walreceiver if we're not in recovery. This should not
@@ -482,11 +484,15 @@ WalReceiverMain(void)
 				 * avoiding some system calls.
 				 */
 				Assert(wait_fd != PGINVALID_SOCKET);
+				pgstat_desc = pgstat_make_wait_desc(WAIT_ACTIVITY,
+										  WAIT_EVENT_WAL_RECEIVER_MAIN);
 				rc = WaitLatchOrSocket(&walrcv->latch,
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   pgstat_desc);
+
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +691,9 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  pgstat_make_wait_desc(WAIT_CLIENT,
+										WAIT_EVENT_WAL_RECEIVER_WAIT_START));
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c7743da..573107f 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,9 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  pgstat_make_wait_desc(WAIT_CLIENT,
+										 WAIT_EVENT_WAL_SENDER_WRITE_DATA));
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1274,9 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  pgstat_make_wait_desc(WAIT_CLIENT,
+										 WAIT_EVENT_WAL_SENDER_WAIT_WAL));
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1924,7 +1928,9 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  pgstat_make_wait_desc(WAIT_ACTIVITY,
+										 WAIT_EVENT_WAL_SENDER_MAIN));
 		}
 	}
 	return;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 90804a3..aaeb75c 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3610,6 +3610,7 @@ LockBufferForCleanup(Buffer buffer)
 	for (;;)
 	{
 		uint32		buf_state;
+		uint32		pgstat_desc;
 
 		/* Try to acquire lock */
 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
@@ -3635,8 +3636,8 @@ LockBufferForCleanup(Buffer buffer)
 		UnlockBufHdr(bufHdr, buf_state);
 		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 
-		/* Report the wait */
-		pgstat_report_wait_start(WAIT_BUFFER_PIN, 0);
+		/* Define the event to wait for */
+		pgstat_desc = pgstat_make_wait_desc(WAIT_BUFFER_PIN, 0);
 
 		/* Wait to be signaled by UnpinBuffer() */
 		if (InHotStandby)
@@ -3644,14 +3645,12 @@ LockBufferForCleanup(Buffer buffer)
 			/* Publish the bufid that Startup process waits on */
 			SetStartupBufferPinWaitBufId(buffer - 1);
 			/* Set alarm and then wait to be signaled by UnpinBuffer() */
-			ResolveRecoveryConflictWithBufferPin();
+			ResolveRecoveryConflictWithBufferPin(pgstat_desc);
 			/* Reset the published bufid */
 			SetStartupBufferPinWaitBufId(-1);
 		}
 		else
-			ProcWaitForSignal();
-
-		pgstat_report_wait_end();
+			ProcWaitForSignal(pgstat_desc);
 
 		/*
 		 * Remove flag marking us as waiter. Normally this will not be set
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..c29cab2 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -122,6 +123,50 @@ struct WaitEventSet
 #endif
 };
 
+/*
+ * This must match enum WaitEventIdentifier!
+ */
+const char *const WaitEventNames[] = {
+	/* Activity */
+	"ArchiverMain",
+	"AutoVacuumMain",
+	"BgWriterHibernate",
+	"BgWriterMain",
+	"CheckpointerMain",
+	"PgStatMain",
+	"RecoveryWalAll",
+	"RecoveryWalStream",
+	"SysLoggerMain",
+	"WalReceiverMain",
+	"WalSenderMain",
+	"WalWriterMain",
+	/* Client */
+	"SecureRead",
+	"SecureWrite",
+	"SSLOpenServer",
+	"WalReceiverWaitStart",
+	"WalSenderWaitForWAL",
+	"WalSenderWriteData",
+	/* Extension */
+	"Extension",
+	/* IPC */
+	"BgWorkerShutdown",
+	"BgWorkerStartup",
+	"ExecuteGather",
+	"MessageQueueInternal",
+	"MessageQueuePutMessage",
+	"MessageQueueReceive",
+	"MessageQueueSend",
+	"ParallelFinish",
+	"ProcSignal",
+	"ProcSleep",
+	"SyncRep",
+	/* Timeout */
+	"BaseBackupThrottle",
+	"PgSleep",
+	"RecoveryApplyDelay"
+};
+
 #ifndef WIN32
 /* Are we currently in WaitLatch? The signal handler would like to know. */
 static volatile sig_atomic_t waiting = false;
@@ -292,14 +337,19 @@ DisownLatch(volatile Latch *latch)
  * backend-local latch initialized with InitLatch, or a shared latch
  * associated with the current process by calling OwnLatch.
  *
+ * pgstat_desc can be used to define what the wait event is when waiting for
+ * this latch when activity is reported to pgstat.
+ *
  * Returns bit mask indicating which condition(s) caused the wake-up. Note
  * that if multiple wake-up conditions are true, there is no guarantee that
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  uint32 pgstat_desc)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 pgstat_desc);
 }
 
 /*
@@ -310,13 +360,16 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  * returning the socket as readable/writable or both, depending on
  * WL_SOCKET_READABLE/WL_SOCKET_WRITEABLE being specified.
  *
+ * pgstat_desc can be used to define what the wait event is when waiting for
+ * this latch or socket when activity is reported to pgstat.
+ *
  * NB: These days this is just a wrapper around the WaitEventSet API. When
  * using a latch very frequently, consider creating a longer living
  * WaitEventSet instead; that's more efficient.
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, uint32 pgstat_desc)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +397,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, pgstat_desc);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -856,6 +909,9 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  * If timeout = -1, block until an event occurs; if 0, check sockets for
  * readiness, but don't block; if > 0, block for at most timeout miliseconds.
  *
+ * pgstat_desc can be used to define what the wait event is when waiting for
+ * events to happen when activity is reported to pgstat.
+ *
  * Returns the number of events occurred, or 0 if the timeout was reached.
  *
  * Returned events will have the fd, pos, user_data fields set to the
@@ -863,7 +919,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 uint32 pgstat_desc)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -872,6 +929,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 
 	Assert(nevents > 0);
 
+	pgstat_report_wait_start(pgstat_desc);
+
 	/*
 	 * Initialize timeout if requested.  We must record the current time so
 	 * that we can determine the remaining timeout if interrupted.
@@ -960,6 +1019,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
@@ -1491,6 +1552,22 @@ WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
 }
 #endif
 
+
+/*
+ * Return an event name for a WaitEventSet event depending on the ID
+ * provided by caller, ID used for the statistics collector.
+ */
+const char *
+GetWaitEventIdentifier(uint16 eventId)
+{
+	StaticAssertStmt(lengthof(WaitEventNames) == WAIT_EVENT_LAST_TYPE + 1,
+					 "WaitEventEntries must match WaitEventIdentifiers");
+	if (eventId > WAIT_EVENT_LAST_TYPE)
+		return "???";
+	return WaitEventNames[eventId];
+}
+
+
 /*
  * SetLatch uses SIGUSR1 to wake up the process waiting on the latch.
  *
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..9019f6f 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -19,6 +19,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker.h"
 #include "storage/procsignal.h"
 #include "storage/shm_mq.h"
@@ -894,7 +895,8 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0,
+					  pgstat_make_wait_desc(WAIT_IPC, WAIT_EVENT_MQ_SEND));
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +993,8 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0,
+				  pgstat_make_wait_desc(WAIT_IPC, WAIT_EVENT_MQ_RECEIVE));
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1093,8 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0,
+				  pgstat_make_wait_desc(WAIT_IPC, WAIT_EVENT_MQ_INTERNAL));
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 547f1a8..2d824be 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -354,9 +354,12 @@ ResolveRecoveryConflictWithDatabase(Oid dbid)
  *
  * Deadlocks involving the Startup process and an ordinary backend process
  * will be detected by the deadlock detector within the ordinary backend.
+ *
+ * pgstat_desc can be used to define the wait event this conflict resolution
+ * should wait for pgstat tracking.
  */
 void
-ResolveRecoveryConflictWithLock(LOCKTAG locktag)
+ResolveRecoveryConflictWithLock(LOCKTAG locktag, uint32 pgstat_desc)
 {
 	TimestampTz ltime;
 
@@ -389,7 +392,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag)
 	}
 
 	/* Wait to be signaled by the release of the Relation Lock */
-	ProcWaitForSignal();
+	ProcWaitForSignal(pgstat_desc);
 
 	/*
 	 * Clear any timeout requests established above.  We assume here that the
@@ -426,9 +429,12 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag)
  * Deadlocks are extremely rare, and relatively expensive to check for,
  * so we don't do a deadlock check right away ... only if we have had to wait
  * at least deadlock_timeout.
+ *
+ * pgstat_desc can be used to override the wait event this recovery conflict
+ * is waiting for when reporting to pgstat.
  */
 void
-ResolveRecoveryConflictWithBufferPin(void)
+ResolveRecoveryConflictWithBufferPin(uint32 pgstat_desc)
 {
 	TimestampTz ltime;
 
@@ -469,7 +475,7 @@ ResolveRecoveryConflictWithBufferPin(void)
 	}
 
 	/* Wait to be signaled by UnpinBuffer() */
-	ProcWaitForSignal();
+	ProcWaitForSignal(pgstat_desc);
 
 	/*
 	 * Clear any timeout requests established above.  We assume here that the
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index dba3809..d2e2331 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -1659,6 +1659,7 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 	LOCKMETHODID lockmethodid = LOCALLOCK_LOCKMETHOD(*locallock);
 	LockMethod	lockMethodTable = LockMethods[lockmethodid];
 	char	   *volatile new_status = NULL;
+	uint32		pgstat_desc;
 
 	LOCK_PRINT("WaitOnLock: sleeping on lock",
 			   locallock->lock, locallock->tag.mode);
@@ -1676,7 +1677,9 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 		set_ps_display(new_status, false);
 		new_status[len] = '\0'; /* truncate off " waiting" */
 	}
-	pgstat_report_wait_start(WAIT_LOCK, locallock->tag.lock.locktag_type);
+
+	pgstat_desc = pgstat_make_wait_desc(WAIT_LOCK,
+										locallock->tag.lock.locktag_type);
 
 	awaitedLock = locallock;
 	awaitedOwner = owner;
@@ -1700,7 +1703,7 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 	 */
 	PG_TRY();
 	{
-		if (ProcSleep(locallock, lockMethodTable) != STATUS_OK)
+		if (ProcSleep(locallock, lockMethodTable, pgstat_desc) != STATUS_OK)
 		{
 			/*
 			 * We failed as a result of a deadlock, see CheckDeadLock(). Quit
@@ -1724,7 +1727,6 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 		/* In this path, awaitedLock remains set until LockErrorCleanup */
 
 		/* Report change to non-waiting status */
-		pgstat_report_wait_end();
 		if (update_process_title)
 		{
 			set_ps_display(new_status, false);
@@ -1739,7 +1741,6 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 	awaitedLock = NULL;
 
 	/* Report change to non-waiting status */
-	pgstat_report_wait_end();
 	if (update_process_title)
 	{
 		set_ps_display(new_status, false);
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 9d08de7..caf018b 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -730,11 +730,16 @@ static inline void
 LWLockReportWaitStart(LWLock *lock)
 {
 	int			lockId = T_ID(lock);
+	uint32		pgstat_desc;
 
 	if (lock->tranche == 0)
-		pgstat_report_wait_start(WAIT_LWLOCK_NAMED, (uint16) lockId);
+		pgstat_desc = pgstat_make_wait_desc(WAIT_LWLOCK_NAMED,
+											(uint16) lockId);
 	else
-		pgstat_report_wait_start(WAIT_LWLOCK_TRANCHE, lock->tranche);
+		pgstat_desc = pgstat_make_wait_desc(WAIT_LWLOCK_TRANCHE,
+											lock->tranche);
+
+	pgstat_report_wait_start(pgstat_desc);
 }
 
 /*
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 4064b20..f292834 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -1518,7 +1518,7 @@ GetSafeSnapshot(Snapshot origSnapshot)
 				 SxactIsROUnsafe(MySerializableXact)))
 		{
 			LWLockRelease(SerializableXactHashLock);
-			ProcWaitForSignal();
+			ProcWaitForSignal(0);
 			LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
 		}
 		MySerializableXact->flags &= ~SXACT_FLAG_DEFERRABLE_WAITING;
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 33e7023..6ef0d94 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -39,6 +39,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/autovacuum.h"
 #include "replication/slot.h"
 #include "replication/syncrep.h"
@@ -977,6 +978,9 @@ ProcQueueInit(PROC_QUEUE *queue)
  * The lock table's partition lock must be held at entry, and will be held
  * at exit.
  *
+ * pgstat_desc tracks what is the wait event caller is expecting to wait for.
+ * If set to 0 the default wait event for ProcSleep() is used.
+ *
  * Result: STATUS_OK if we acquired the lock, STATUS_ERROR if not (deadlock).
  *
  * ASSUME: that no one will fiddle with the queue until after
@@ -985,7 +989,8 @@ ProcQueueInit(PROC_QUEUE *queue)
  * NOTES: The process queue is now a priority queue for locking.
  */
 int
-ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
+ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable,
+		  uint32 pgstat_desc)
 {
 	LOCKMODE	lockmode = locallock->tag.mode;
 	LOCK	   *lock = locallock->lock;
@@ -1023,6 +1028,14 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 	}
 
 	/*
+	 * If caller did not provide the wait event it is expected to look for,
+	 * set up a default one.
+	 */
+	if (pgstat_desc == 0)
+		pgstat_desc = pgstat_make_wait_desc(WAIT_ACTIVITY,
+											WAIT_EVENT_PROC_SLEEP);
+
+	/*
 	 * Determine where to add myself in the wait queue.
 	 *
 	 * Normally I should go at the end of the queue.  However, if I already
@@ -1208,11 +1221,11 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		if (InHotStandby)
 		{
 			/* Set a timer and wait for that or for the Lock to be granted */
-			ResolveRecoveryConflictWithLock(locallock->tag.lock);
+			ResolveRecoveryConflictWithLock(locallock->tag.lock, pgstat_desc);
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, pgstat_desc);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1717,14 +1730,24 @@ CheckDeadLockAlert(void)
 /*
  * ProcWaitForSignal - wait for a signal from another backend.
  *
+ * pgstat_desc tracks what is the wait event caller is expecting to wait for.
+ * If set to 0 the default wait event for ProcWaitForSignal() is used.
+ *
  * As this uses the generic process latch the caller has to be robust against
  * unrelated wakeups: Always check that the desired state has occurred, and
  * wait again if not.
  */
 void
-ProcWaitForSignal(void)
+ProcWaitForSignal(uint32 pgstat_desc)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	/*
+	 * If caller did not provide the wait event it is expected to look for,
+	 * set up a default one.
+	 */
+	if (pgstat_desc == 0)
+		pgstat_make_wait_desc(WAIT_IPC, WAIT_EVENT_PROC_SIGNAL);
+
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, pgstat_desc);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..02cabcd 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -30,6 +30,7 @@
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "parser/scansup.h"
+#include "pgstat.h"
 #include "postmaster/syslogger.h"
 #include "rewrite/rewriteHandler.h"
 #include "storage/fd.h"
@@ -560,7 +561,9 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms,
+						 pgstat_make_wait_desc(WAIT_TIMEOUT,
+											   WAIT_EVENT_PG_SLEEP));
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 0c98c59..e1557da 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -721,8 +721,13 @@ typedef enum WaitClass
 	WAIT_LWLOCK_NAMED,
 	WAIT_LWLOCK_TRANCHE,
 	WAIT_LOCK,
-	WAIT_BUFFER_PIN
-}	WaitClass;
+	WAIT_BUFFER_PIN,
+	WAIT_ACTIVITY,
+	WAIT_CLIENT,
+	WAIT_EXTENSION,
+	WAIT_IPC,
+	WAIT_TIMEOUT
+} WaitClass;
 
 
 /* ----------
@@ -1018,23 +1023,18 @@ extern void pgstat_initstats(Relation rel);
  * ----------
  */
 static inline void
-pgstat_report_wait_start(uint8 classId, uint16 eventId)
+pgstat_report_wait_start(uint32 wait_event_info)
 {
 	volatile PGPROC *proc = MyProc;
-	uint32		wait_event_val;
 
 	if (!pgstat_track_activities || !proc)
 		return;
 
-	wait_event_val = classId;
-	wait_event_val <<= 24;
-	wait_event_val |= eventId;
-
 	/*
 	 * Since this is a four-byte field which is always read and written as
 	 * four-bytes, updates are atomic.
 	 */
-	proc->wait_event_info = wait_event_val;
+	proc->wait_event_info = wait_event_info;
 }
 
 /* ----------
@@ -1061,6 +1061,16 @@ pgstat_report_wait_end(void)
 	proc->wait_event_info = 0;
 }
 
+/* ----------
+ * pgstat_make_wait_desc()
+ *
+ *	Build a value to be used for report in pgstat_report_wait_start().
+ *	This respects the format per the description above.
+ * ----------
+ */
+#define pgstat_make_wait_desc(classId, eventId)					\
+	((classId << 24) | eventId)
+
 /* nontransactional event counts are simple enough to inline */
 
 #define pgstat_count_heap_scan(rel)									\
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..baaa6b2 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -135,6 +135,70 @@ typedef struct WaitEvent
 	void	   *user_data;		/* pointer provided in AddWaitEventToSet */
 } WaitEvent;
 
+/*
+ * List of WaitEventSet identifiers used when reporting activity to
+ * statistics collector. Up to 256 different WaitEventIdentifier can be
+ * handled. Those are classified by category first, and then by
+ * alphabetical order. Events are classified into sub-categories following
+ * some basic hierarchy rules:
+ * - "Activity" for main loops of processes waiting for an event.
+ * - "Client" for a socket awaited when a user is connected.
+ * - "IPC", similarly to "Client", for a socket awaited from another
+ *   server process.
+ * - "Timeout", for a timeout waiting to expire.
+ * - "Extension", to let extension modules a way to define a custom wait
+ *   point.
+ */
+typedef enum WaitEventIdentifier
+{
+	/* Activity */
+	WAIT_EVENT_ARCHIVER_MAIN,
+	WAIT_EVENT_AUTOVACUUM_MAIN,
+	WAIT_EVENT_BGWRITER_HIBERNATE,
+	WAIT_EVENT_BGWRITER_MAIN,
+	WAIT_EVENT_CHECKPOINTER_MAIN,
+	WAIT_EVENT_PGSTAT_MAIN,
+	WAIT_EVENT_RECOVERY_WAL_ALL,
+	WAIT_EVENT_RECOVERY_WAL_STREAM,
+	WAIT_EVENT_SYSLOGGER_MAIN,
+	WAIT_EVENT_WAL_RECEIVER_MAIN,
+	WAIT_EVENT_WAL_SENDER_MAIN,
+	WAIT_EVENT_WAL_WRITER_MAIN,
+	/* Client */
+	WAIT_EVENT_SECURE_READ,
+	WAIT_EVENT_SECURE_WRITE,
+	WAIT_EVENT_SSL_OPEN_SERVER,
+	WAIT_EVENT_WAL_RECEIVER_WAIT_START,
+	WAIT_EVENT_WAL_SENDER_WAIT_WAL,
+	WAIT_EVENT_WAL_SENDER_WRITE_DATA,
+	/* Extension */
+	WAIT_EVENT_EXTENSION,
+	/* IPC */
+	WAIT_EVENT_BGWORKER_SHUTDOWN,
+	WAIT_EVENT_BGWORKER_STARTUP,
+	WAIT_EVENT_EXECUTE_GATHER,
+	WAIT_EVENT_MQ_INTERNAL,
+	WAIT_EVENT_MQ_PUT_MESSAGE,
+	WAIT_EVENT_MQ_RECEIVE,
+	WAIT_EVENT_MQ_SEND,
+	WAIT_EVENT_PARALLEL_FINISH,
+	WAIT_EVENT_PROC_SIGNAL,
+	WAIT_EVENT_PROC_SLEEP,
+	WAIT_EVENT_SYNC_REP,
+	/* Timeout */
+	WAIT_EVENT_BASE_BACKUP_THROTTLE,
+	WAIT_EVENT_PG_SLEEP,
+	WAIT_EVENT_RECOVERY_APPLY_DELAY
+} WaitEventIdentifier;
+
+#define WAIT_EVENT_LAST_TYPE WAIT_EVENT_RECOVERY_APPLY_DELAY
+
+/*
+ * The information details about each WaitEventIdentifier listed above
+ * are specified by an array of name-pair values.
+ */
+extern const char *const WaitEventName[];
+
 /* forward declaration to avoid exposing latch.c implementation details */
 typedef struct WaitEventSet WaitEventSet;
 
@@ -155,10 +219,14 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int	WaitEventSetWait(WaitEventSet *set, long timeout,
+				  WaitEvent *occurred_events, int nevents,
+				  uint32 pgstat_desc);
+extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+				  uint32 pgstat_desc);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, uint32 pgstat_desc);
+extern const char *GetWaitEventIdentifier(uint16 eventId);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index f576f05..158cb26 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -284,14 +284,15 @@ extern bool HaveNFreeProcs(int n);
 extern void ProcReleaseLocks(bool isCommit);
 
 extern void ProcQueueInit(PROC_QUEUE *queue);
-extern int	ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable);
+extern int	ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable,
+					  uint32 pgstat_desc);
 extern PGPROC *ProcWakeup(PGPROC *proc, int waitStatus);
 extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock);
 extern void CheckDeadLockAlert(void);
 extern bool IsWaitingForLock(void);
 extern void LockErrorCleanup(void);
 
-extern void ProcWaitForSignal(void);
+extern void ProcWaitForSignal(uint32 pgstat_desc);
 extern void ProcSendSignal(int pid);
 
 extern void BecomeLockGroupLeader(void);
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
index dcebf72..9e54a3b 100644
--- a/src/include/storage/standby.h
+++ b/src/include/storage/standby.h
@@ -32,8 +32,9 @@ extern void ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid,
 extern void ResolveRecoveryConflictWithTablespace(Oid tsid);
 extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
 
-extern void ResolveRecoveryConflictWithLock(LOCKTAG locktag);
-extern void ResolveRecoveryConflictWithBufferPin(void);
+extern void ResolveRecoveryConflictWithLock(LOCKTAG locktag,
+											uint32 pgstat_desc);
+extern void ResolveRecoveryConflictWithBufferPin(uint32 pgstat_desc);
 extern void CheckRecoveryConflictDeadlock(void);
 extern void StandbyDeadLockHandler(void);
 extern void StandbyTimeoutHandler(void);
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index 143df4e..0cfb447 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker.h"
 #include "storage/procsignal.h"
 #include "storage/shm_toc.h"
@@ -279,7 +280,9 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0,
+				  pgstat_make_wait_desc(WAIT_EXTENSION,
+										WAIT_EVENT_EXTENSION));
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index dd34bc7..2953062 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -15,6 +15,7 @@
 
 #include "fmgr.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 
 #include "test_shm_mq.h"
 
@@ -230,7 +231,9 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0,
+					  pgstat_make_wait_desc(WAIT_EXTENSION,
+											WAIT_EVENT_EXTENSION));
 			ResetLatch(MyLatch);
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index 7c9a3eb..531eb22 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -227,7 +227,9 @@ worker_spi_main(Datum main_arg)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   worker_spi_naptime * 1000L);
+					   worker_spi_naptime * 1000L,
+					   pgstat_make_wait_desc(WAIT_EXTENSION,
+											 WAIT_EVENT_EXTENSION));
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#51Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#50)
1 attachment(s)
Re: Tracking wait event for latches

On Mon, Oct 3, 2016 at 3:30 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

[ new patch ]

I think this is unnecessarily awkward for callers; the attached
version takes a different approach which I think will be more
convenient. The attached version also (1) moves a lot more of the
logic from latch.c/h to pgstat.c/h, which I think is more appropriate;
(2) more thoroughly separates the wait events by class; (3) renames
SecureRead/SecureWrite to ClientRead/ClientWrite (whether to also
rename the C functions is an interesting question, but not the most
pressing one IMHO), (4) creates a real wait event for GetSafeSnapshot
and removes the unnecessary and overly generic ProcSleep and
ProcSignal wait events, and (5) incorporates a bit of copy editing.

I've tested that this seems to work in basic cases, but more testing
is surely welcome. If there are no major objections, I will commit
this version.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

wait-event-rmh.patchtext/x-diff; charset=US-ASCII; name=wait-event-rmh.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..9badfe6 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
 #include "access/xact.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
@@ -496,7 +497,7 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L, WAIT_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index f400785..c5d7728 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,42 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>Activity</>: The server process is idle.  This is used by
+          system processes waiting for activity in their main processing loop.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Extension</>: The server process is waiting for activity
+          in an extension module.  This category is useful for modules to
+          track custom waiting points.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Client</>: The server process is waiting for some activity
+          on a socket from user applications, and that the server expects
+          something to happen that is independent from its internal processes.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>IPC</>: The server process is waiting for some activity
+          from another process in the server.  <literal>wait_event</> will
+          identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Timeout</>: The server process is waiting for a timeout
+          to expire.  <literal>wait_event</> will identify the specific wait
+          point.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1085,6 +1121,139 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="11"><literal>Activity</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stream at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
+        <row>
+         <entry morerows="5"><literal>Client</></entry>
+         <entry><literal>ClientRead</></entry>
+         <entry>Waiting to read data from the client.</entry>
+        </row>
+        <row>
+         <entry><literal>ClientWrite</></entry>
+         <entry>Waiting to write data from the client.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in an extension.</entry>
+        </row>
+        <row>
+         <entry morerows="10"><literal>IPC</></entry>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to write a protoocol message to a shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes from a shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes to a shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>SafeSnapshot</></entry>
+         <entry>Waiting for a snapshot for a <literal>READ ONLY DEFERRABLE</> transaction.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for confirmation from remote server during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry morerows="2"><literal>Timeout</></entry>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index cde0ed3..59dc394 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -24,6 +24,7 @@
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
 #include "optimizer/planmain.h"
+#include "pgstat.h"
 #include "storage/ipc.h"
 #include "storage/sinval.h"
 #include "storage/spin.h"
@@ -540,7 +541,8 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  WAIT_EVENT_PARALLEL_FINISH);
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index c1b9a97..08c87f9 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5827,7 +5827,8 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  WAIT_EVENT_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -11387,7 +11388,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+								  wait_time, WAIT_EVENT_RECOVERY_WAL_STREAM);
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11550,7 +11551,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L, WAIT_EVENT_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..880ca62 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -38,6 +38,7 @@
 #include "executor/nodeSubplan.h"
 #include "executor/tqueue.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
 
@@ -387,7 +388,7 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EVENT_EXECUTE_GATHER);
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index fedb02c..668f217 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -60,6 +60,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
@@ -419,7 +420,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+								  WAIT_EVENT_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..b267507 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -33,6 +33,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 #include "storage/ipc.h"
@@ -146,7 +147,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WAIT_EVENT_CLIENT_READ);
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +249,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WAIT_EVENT_CLIENT_WRITE);
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index bfe66c6..f93ccae 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -17,6 +17,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 
@@ -171,7 +172,8 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0,
+				  WAIT_EVENT_MQ_PUT_MESSAGE);
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1a92ca1..e3a6911 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -598,7 +598,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   WAIT_EVENT_AUTOVACUUM_MAIN);
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 699c934..028a9ee 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -18,6 +18,7 @@
 #include "libpq/pqsignal.h"
 #include "postmaster/bgworker_internals.h"
 #include "postmaster/postmaster.h"
+#include "pgstat.h"
 #include "storage/barrier.h"
 #include "storage/dsm.h"
 #include "storage/ipc.h"
@@ -969,7 +970,8 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WAIT_EVENT_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1010,8 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			break;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WAIT_EVENT_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 1002034..c3f3356 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -345,7 +345,7 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */, WAIT_EVENT_BGWRITER_MAIN);
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -372,7 +372,8 @@ BackgroundWriterMain(void)
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
 						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+						   BgWriterDelay * HIBERNATE_FACTOR,
+						   WAIT_EVENT_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index d702a48..397267c 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -556,7 +556,8 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   WAIT_EVENT_CHECKPOINTER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..62783d9 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,8 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							   timeout * 1000L,
+							   WAIT_EVENT_ARCHIVER_MAIN);
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 96578dc..deadf65 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -276,6 +276,11 @@ static PgStat_TableStatus *get_tabstat_entry(Oid rel_id, bool isshared);
 
 static void pgstat_setup_memcxt(void);
 
+static const char *pgstat_get_wait_activity(WaitEventActivity w);
+static const char *pgstat_get_wait_client(WaitEventClient w);
+static const char *pgstat_get_wait_ipc(WaitEventIPC w);
+static const char *pgstat_get_wait_timeout(WaitEventTimeout w);
+
 static void pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype);
 static void pgstat_send(void *msg, int len);
 
@@ -3131,15 +3136,14 @@ pgstat_read_current_status(void)
 const char *
 pgstat_get_wait_event_type(uint32 wait_event_info)
 {
-	uint8		classId;
+	uint32		classId;
 	const char *event_type;
 
 	/* report process as not waiting. */
 	if (wait_event_info == 0)
 		return NULL;
 
-	wait_event_info = wait_event_info >> 24;
-	classId = wait_event_info & 0XFF;
+	classId = wait_event_info & 0xFF000000;
 
 	switch (classId)
 	{
@@ -3155,6 +3159,18 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+			event_type = "Activity";
+			break;
+		case WAIT_CLIENT:
+			event_type = "Client";
+			break;
+		case WAIT_EXTENSION:
+			event_type = "Extension";
+			break;
+		case WAIT_TIMEOUT:
+			event_type = "Timeout";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3172,7 +3188,7 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 const char *
 pgstat_get_wait_event(uint32 wait_event_info)
 {
-	uint8		classId;
+	uint32		classId;
 	uint16		eventId;
 	const char *event_name;
 
@@ -3180,9 +3196,8 @@ pgstat_get_wait_event(uint32 wait_event_info)
 	if (wait_event_info == 0)
 		return NULL;
 
-	eventId = wait_event_info & ((1 << 24) - 1);
-	wait_event_info = wait_event_info >> 24;
-	classId = wait_event_info & 0XFF;
+	classId = wait_event_info & 0xFF000000;
+	eventId = wait_event_info & 0x0000FFFF;
 
 	switch (classId)
 	{
@@ -3196,6 +3211,37 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+			{
+				WaitEventActivity	w = (WaitEventActivity) wait_event_info;
+
+				event_name = pgstat_get_wait_activity(w);
+				break;
+			}
+		case WAIT_CLIENT:
+			{
+				WaitEventClient	w = (WaitEventClient) wait_event_info;
+
+				event_name = pgstat_get_wait_client(w);
+				break;
+			}
+		case WAIT_EXTENSION:
+			event_name = "Extension";
+			break;
+		case WAIT_IPC:
+			{
+				WaitEventIPC	w = (WaitEventIPC) wait_event_info;
+
+				event_name = pgstat_get_wait_ipc(w);
+				break;
+			}
+		case WAIT_TIMEOUT:
+			{
+				WaitEventTimeout	w = (WaitEventTimeout) wait_event_info;
+
+				event_name = pgstat_get_wait_timeout(w);
+				break;
+			}
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3205,6 +3251,175 @@ pgstat_get_wait_event(uint32 wait_event_info)
 }
 
 /* ----------
+ * pgstat_get_wait_activity() -
+ *
+ * Convert WaitEventActivity to string.
+ * ----------
+ */
+static const char *
+pgstat_get_wait_activity(WaitEventActivity w)
+{
+	const char *event_name = "unknown wait event";
+
+	switch (w)
+	{
+		case WAIT_EVENT_ARCHIVER_MAIN:
+			event_name = "ArchiverMain";
+			break;
+		case WAIT_EVENT_AUTOVACUUM_MAIN:
+			event_name = "AutoVacuumMain";
+			break;
+		case WAIT_EVENT_BGWRITER_HIBERNATE:
+			event_name = "BgWriterHibernate";
+			break;
+		case WAIT_EVENT_BGWRITER_MAIN:
+			event_name = "BgWriterMain";
+			break;
+		case WAIT_EVENT_CHECKPOINTER_MAIN:
+			event_name = "CheckpointerMain";
+			break;
+		case WAIT_EVENT_PGSTAT_MAIN:
+			event_name = "PgStatMain";
+			break;
+		case WAIT_EVENT_RECOVERY_WAL_ALL:
+			event_name = "RecoveryWalAll";
+			break;
+		case WAIT_EVENT_RECOVERY_WAL_STREAM:
+			event_name = "RecoveryWalStream";
+			break;
+		case WAIT_EVENT_SYSLOGGER_MAIN:
+			event_name = "SysLoggerMain";
+			break;
+		case WAIT_EVENT_WAL_RECEIVER_MAIN:
+			event_name = "WalReceiverMain";
+			break;
+		case WAIT_EVENT_WAL_SENDER_MAIN:
+			event_name = "WalSenderMain";
+			break;
+		case WAIT_EVENT_WAL_WRITER_MAIN:
+			event_name = "WalWriterMain";
+			break;
+		/* no default case, so that compiler will warn */
+	}
+
+	return event_name;
+}
+
+/* ----------
+ * pgstat_get_wait_client() -
+ *
+ * Convert WaitEventClient to string.
+ * ----------
+ */
+static const char *
+pgstat_get_wait_client(WaitEventClient w)
+{
+	const char *event_name = "unknown wait event";
+
+	switch (w)
+	{
+		case WAIT_EVENT_CLIENT_READ:
+			event_name = "ClientRead";
+			break;
+		case WAIT_EVENT_CLIENT_WRITE:
+			event_name = "ClientWrite";
+			break;
+		case WAIT_EVENT_SSL_OPEN_SERVER:
+			event_name = "SSLOpenServer";
+			break;
+		case WAIT_EVENT_WAL_RECEIVER_WAIT_START:
+			event_name = "WalReceiverWaitStart";
+			break;
+		case WAIT_EVENT_WAL_SENDER_WAIT_WAL:
+			event_name = "WalSenderWaitForWAL";
+			break;
+		case WAIT_EVENT_WAL_SENDER_WRITE_DATA:
+			event_name = "WalSenderWriteData";
+			break;
+		/* no default case, so that compiler will warn */
+	}
+
+	return event_name;
+}
+
+/* ----------
+ * pgstat_get_wait_ipc() -
+ *
+ * Convert WaitEventIPC to string.
+ * ----------
+ */
+static const char *
+pgstat_get_wait_ipc(WaitEventIPC w)
+{
+	const char *event_name = "unknown wait event";
+
+	switch (w)
+	{
+		case WAIT_EVENT_BGWORKER_SHUTDOWN:
+			event_name = "BgWorkerShutdown";
+			break;
+		case WAIT_EVENT_BGWORKER_STARTUP:
+			event_name = "BgWorkerStartup";
+			break;
+		case WAIT_EVENT_EXECUTE_GATHER:
+			event_name = "ExecuteGather";
+			break;
+		case WAIT_EVENT_MQ_INTERNAL:
+			event_name = "MessageQueueInternal";
+			break;
+		case WAIT_EVENT_MQ_PUT_MESSAGE:
+			event_name = "MessageQueuePutMessage";
+			break;
+		case WAIT_EVENT_MQ_RECEIVE:
+			event_name = "MessageQueueReceive";
+			break;
+		case WAIT_EVENT_MQ_SEND:
+			event_name = "MessageQueueSend";
+			break;
+		case WAIT_EVENT_PARALLEL_FINISH:
+			event_name = "ParallelFinish";
+			break;
+		case WAIT_EVENT_SAFE_SNAPSHOT:
+			event_name = "SafeSnapshot";
+			break;
+		case WAIT_EVENT_SYNC_REP:
+			event_name = "SyncRep";
+			break;
+		/* no default case, so that compiler will warn */
+	}
+
+	return event_name;
+}
+
+/* ----------
+ * pgstat_get_wait_timeout() -
+ *
+ * Convert WaitEventTimeout to string.
+ * ----------
+ */
+static const char *
+pgstat_get_wait_timeout(WaitEventTimeout w)
+{
+	const char *event_name = "unknown wait event";
+
+	switch (w)
+	{
+		case WAIT_EVENT_BASE_BACKUP_THROTTLE:
+			event_name = "BaseBackupThrottle";
+			break;
+		case WAIT_EVENT_PG_SLEEP:
+			event_name = "PgSleep";
+			break;
+		case WAIT_EVENT_RECOVERY_APPLY_DELAY:
+			event_name = "RecoveryApplyDelay";
+			break;
+		/* no default case, so that compiler will warn */
+	}
+
+	return event_name;
+}
+
+/* ----------
  * pgstat_get_backend_current_activity() -
  *
  *	Return a string representing the current activity of the backend with
@@ -3684,8 +3899,8 @@ PgstatCollectorMain(int argc, char *argv[])
 #ifndef WIN32
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
-							   pgStatSock,
-							   -1L);
+							   pgStatSock, -1L,
+							   WAIT_EVENT_PGSTAT_MAIN);
 #else
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..af71367 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -35,6 +35,7 @@
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "nodes/pg_list.h"
+#include "pgstat.h"
 #include "pgtime.h"
 #include "postmaster/fork_process.h"
 #include "postmaster/postmaster.h"
@@ -424,7 +425,8 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+							   WAIT_EVENT_SYSLOGGER_MAIN);
 
 		if (rc & WL_SOCKET_READABLE)
 		{
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 11ec56a..67dcff6 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -290,7 +290,8 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   WAIT_EVENT_WAL_WRITER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index 1eabaef..fa75930 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1364,7 +1364,8 @@ throttle(size_t increment)
 		 */
 		wait_result = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+								(long) (sleep / 1000),
+								WAIT_EVENT_BASE_BACKUP_THROTTLE);
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index b442d06..ac29f56 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -61,6 +61,7 @@
 
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/syncrep.h"
 #include "replication/walsender.h"
 #include "replication/walsender_private.h"
@@ -258,7 +259,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  WAIT_EVENT_SYNC_REP);
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..eed6eff 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -55,6 +55,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
 #include "storage/ipc.h"
@@ -486,7 +487,8 @@ WalReceiverMain(void)
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   WAIT_EVENT_WAL_RECEIVER_MAIN);
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +687,8 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  WAIT_EVENT_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c7743da..0f3ced2 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,8 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WAIT_EVENT_WAL_SENDER_WRITE_DATA);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1273,8 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WAIT_EVENT_WAL_SENDER_WAIT_WAL);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1924,7 +1926,8 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  WAIT_EVENT_WAL_SENDER_MAIN);
 		}
 	}
 	return;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 90804a3..91dc24c 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3635,9 +3635,6 @@ LockBufferForCleanup(Buffer buffer)
 		UnlockBufHdr(bufHdr, buf_state);
 		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 
-		/* Report the wait */
-		pgstat_report_wait_start(WAIT_BUFFER_PIN, 0);
-
 		/* Wait to be signaled by UnpinBuffer() */
 		if (InHotStandby)
 		{
@@ -3649,9 +3646,7 @@ LockBufferForCleanup(Buffer buffer)
 			SetStartupBufferPinWaitBufId(-1);
 		}
 		else
-			ProcWaitForSignal();
-
-		pgstat_report_wait_end();
+			ProcWaitForSignal(WAIT_BUFFER_PIN);
 
 		/*
 		 * Remove flag marking us as waiter. Normally this will not be set
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..8488f94 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -297,9 +298,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  uint32 wait_event_info)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 wait_event_info);
 }
 
 /*
@@ -316,7 +319,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, uint32 wait_event_info)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +347,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, wait_event_info);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -863,7 +866,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 uint32 wait_event_info)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -883,6 +887,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 		cur_timeout = timeout;
 	}
 
+	pgstat_report_wait_start(wait_event_info);
+
 #ifndef WIN32
 	waiting = true;
 #else
@@ -960,6 +966,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..bfb6703 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -19,6 +19,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker.h"
 #include "storage/procsignal.h"
 #include "storage/shm_mq.h"
@@ -894,7 +895,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EVENT_MQ_SEND);
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +992,7 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EVENT_MQ_RECEIVE);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1091,7 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EVENT_MQ_INTERNAL);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 547f1a8..fb887b3 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -22,6 +22,7 @@
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "storage/proc.h"
@@ -389,7 +390,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag)
 	}
 
 	/* Wait to be signaled by the release of the Relation Lock */
-	ProcWaitForSignal();
+	ProcWaitForSignal(WAIT_LOCK | locktag.locktag_type);
 
 	/*
 	 * Clear any timeout requests established above.  We assume here that the
@@ -469,7 +470,7 @@ ResolveRecoveryConflictWithBufferPin(void)
 	}
 
 	/* Wait to be signaled by UnpinBuffer() */
-	ProcWaitForSignal();
+	ProcWaitForSignal(WAIT_BUFFER_PIN);
 
 	/*
 	 * Clear any timeout requests established above.  We assume here that the
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index dba3809..71a4dd4 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -1676,7 +1676,6 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 		set_ps_display(new_status, false);
 		new_status[len] = '\0'; /* truncate off " waiting" */
 	}
-	pgstat_report_wait_start(WAIT_LOCK, locallock->tag.lock.locktag_type);
 
 	awaitedLock = locallock;
 	awaitedOwner = owner;
@@ -1724,7 +1723,6 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 		/* In this path, awaitedLock remains set until LockErrorCleanup */
 
 		/* Report change to non-waiting status */
-		pgstat_report_wait_end();
 		if (update_process_title)
 		{
 			set_ps_display(new_status, false);
@@ -1739,7 +1737,6 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 	awaitedLock = NULL;
 
 	/* Report change to non-waiting status */
-	pgstat_report_wait_end();
 	if (update_process_title)
 	{
 		set_ps_display(new_status, false);
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 9d08de7..a90b54a 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -732,9 +732,9 @@ LWLockReportWaitStart(LWLock *lock)
 	int			lockId = T_ID(lock);
 
 	if (lock->tranche == 0)
-		pgstat_report_wait_start(WAIT_LWLOCK_NAMED, (uint16) lockId);
+		pgstat_report_wait_start(WAIT_LWLOCK_NAMED | (uint16) lockId);
 	else
-		pgstat_report_wait_start(WAIT_LWLOCK_TRANCHE, lock->tranche);
+		pgstat_report_wait_start(WAIT_LWLOCK_TRANCHE | lock->tranche);
 }
 
 /*
@@ -750,7 +750,7 @@ LWLockReportWaitEnd(void)
  * Return an identifier for an LWLock based on the wait class and event.
  */
 const char *
-GetLWLockIdentifier(uint8 classId, uint16 eventId)
+GetLWLockIdentifier(uint32 classId, uint16 eventId)
 {
 	if (classId == WAIT_LWLOCK_NAMED)
 		return MainLWLockNames[eventId];
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 4064b20..24ed21b 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -192,6 +192,7 @@
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/bufmgr.h"
 #include "storage/predicate.h"
 #include "storage/predicate_internals.h"
@@ -1518,7 +1519,7 @@ GetSafeSnapshot(Snapshot origSnapshot)
 				 SxactIsROUnsafe(MySerializableXact)))
 		{
 			LWLockRelease(SerializableXactHashLock);
-			ProcWaitForSignal();
+			ProcWaitForSignal(WAIT_EVENT_SAFE_SNAPSHOT);
 			LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
 		}
 		MySerializableXact->flags &= ~SXACT_FLAG_DEFERRABLE_WAITING;
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 33e7023..dd76094 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -39,6 +39,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/autovacuum.h"
 #include "replication/slot.h"
 #include "replication/syncrep.h"
@@ -1212,7 +1213,8 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0,
+					  WAIT_LOCK | locallock->tag.lock.locktag_type);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1722,9 +1724,9 @@ CheckDeadLockAlert(void)
  * wait again if not.
  */
 void
-ProcWaitForSignal(void)
+ProcWaitForSignal(uint32 wait_event_info)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, wait_event_info);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..0da051a 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -29,6 +29,7 @@
 #include "common/keywords.h"
 #include "funcapi.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "parser/scansup.h"
 #include "postmaster/syslogger.h"
 #include "rewrite/rewriteHandler.h"
@@ -560,7 +561,8 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms,
+						 WAIT_EVENT_PG_SLEEP);
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 0c98c59..b530c01 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -715,15 +715,91 @@ typedef enum BackendState
  * Wait Classes
  * ----------
  */
-typedef enum WaitClass
+#define WAIT_LWLOCK_NAMED			0x01000000U
+#define WAIT_LWLOCK_TRANCHE			0x02000000U
+#define WAIT_LOCK					0x03000000U
+#define WAIT_BUFFER_PIN				0x04000000U
+#define WAIT_ACTIVITY				0x05000000U
+#define WAIT_CLIENT					0x06000000U
+#define WAIT_EXTENSION				0x07000000U
+#define WAIT_IPC					0x08000000U
+#define WAIT_TIMEOUT				0x09000000U
+
+/* ----------
+ * Wait Events - Activity
+ *
+ * Use this category when a process is waiting because it has no work to do,
+ * unless the "Client" or "Timeout" category describes the situation better.
+ * Typically, this should only be used for background processes.
+ * ----------
+ */
+typedef enum
 {
-	WAIT_UNDEFINED,
-	WAIT_LWLOCK_NAMED,
-	WAIT_LWLOCK_TRANCHE,
-	WAIT_LOCK,
-	WAIT_BUFFER_PIN
-}	WaitClass;
+	WAIT_EVENT_ARCHIVER_MAIN = WAIT_ACTIVITY,
+	WAIT_EVENT_AUTOVACUUM_MAIN,
+	WAIT_EVENT_BGWRITER_HIBERNATE,
+	WAIT_EVENT_BGWRITER_MAIN,
+	WAIT_EVENT_CHECKPOINTER_MAIN,
+	WAIT_EVENT_PGSTAT_MAIN,
+	WAIT_EVENT_RECOVERY_WAL_ALL,
+	WAIT_EVENT_RECOVERY_WAL_STREAM,
+	WAIT_EVENT_SYSLOGGER_MAIN,
+	WAIT_EVENT_WAL_RECEIVER_MAIN,
+	WAIT_EVENT_WAL_SENDER_MAIN,
+	WAIT_EVENT_WAL_WRITER_MAIN,
+} WaitEventActivity;
 
+/* ----------
+ * Wait Events - Client
+ *
+ * Use this category when a process is waiting to send data to or receive data
+ * from the frontend process to which it is connected.  This is never used for
+ * a background process, which has no client connection.
+ * ----------
+ */
+typedef enum
+{
+	WAIT_EVENT_CLIENT_READ = WAIT_CLIENT,
+	WAIT_EVENT_CLIENT_WRITE,
+	WAIT_EVENT_SSL_OPEN_SERVER,
+	WAIT_EVENT_WAL_RECEIVER_WAIT_START,
+	WAIT_EVENT_WAL_SENDER_WAIT_WAL,
+	WAIT_EVENT_WAL_SENDER_WRITE_DATA,
+} WaitEventClient;
+
+/* ----------
+ * Wait Events - IPC
+ *
+ * Use this category when a process cannot complete the work it is doing because
+ * it is waiting for a notification from another process.
+ * ----------
+ */
+typedef enum
+{
+	WAIT_EVENT_BGWORKER_SHUTDOWN = WAIT_IPC,
+	WAIT_EVENT_BGWORKER_STARTUP,
+	WAIT_EVENT_EXECUTE_GATHER,
+	WAIT_EVENT_MQ_INTERNAL,
+	WAIT_EVENT_MQ_PUT_MESSAGE,
+	WAIT_EVENT_MQ_RECEIVE,
+	WAIT_EVENT_MQ_SEND,
+	WAIT_EVENT_PARALLEL_FINISH,
+	WAIT_EVENT_SAFE_SNAPSHOT,
+	WAIT_EVENT_SYNC_REP
+} WaitEventIPC;
+
+/* ----------
+ * Wait Events - Timeout
+ *
+ * Use this category when a process is waiting for a timeout to expire.
+ * ----------
+ */
+typedef enum
+{
+	WAIT_EVENT_BASE_BACKUP_THROTTLE = WAIT_TIMEOUT,
+	WAIT_EVENT_PG_SLEEP,
+	WAIT_EVENT_RECOVERY_APPLY_DELAY
+} WaitEventTimeout;
 
 /* ----------
  * Command type for progress reporting purposes
@@ -1018,23 +1094,18 @@ extern void pgstat_initstats(Relation rel);
  * ----------
  */
 static inline void
-pgstat_report_wait_start(uint8 classId, uint16 eventId)
+pgstat_report_wait_start(uint32 wait_event_info)
 {
 	volatile PGPROC *proc = MyProc;
-	uint32		wait_event_val;
 
 	if (!pgstat_track_activities || !proc)
 		return;
 
-	wait_event_val = classId;
-	wait_event_val <<= 24;
-	wait_event_val |= eventId;
-
 	/*
 	 * Since this is a four-byte field which is always read and written as
 	 * four-bytes, updates are atomic.
 	 */
-	proc->wait_event_info = wait_event_val;
+	proc->wait_event_info = wait_event_info;
 }
 
 /* ----------
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..e96e88f 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -155,10 +155,13 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int WaitEventSetWait(WaitEventSet *set, long timeout,
+				 WaitEvent *occurred_events, int nevents,
+				 uint32 wait_event_info);
+extern int WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  uint32 wait_event_info);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, uint32 wait_event_info);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 18931eb..9a2d869 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -184,7 +184,7 @@ extern Size LWLockShmemSize(void);
 extern void CreateLWLocks(void);
 extern void InitLWLockAccess(void);
 
-extern const char *GetLWLockIdentifier(uint8 classId, uint16 eventId);
+extern const char *GetLWLockIdentifier(uint32 classId, uint16 eventId);
 
 /*
  * Extensions (or core code) can obtain an LWLocks by calling
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index f576f05..7dc8dac 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -291,7 +291,7 @@ extern void CheckDeadLockAlert(void);
 extern bool IsWaitingForLock(void);
 extern void LockErrorCleanup(void);
 
-extern void ProcWaitForSignal(void);
+extern void ProcWaitForSignal(uint32 wait_event_info);
 extern void ProcSendSignal(int pid);
 
 extern void BecomeLockGroupLeader(void);
#52Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#51)
1 attachment(s)
Re: Tracking wait event for latches

On Tue, Oct 4, 2016 at 1:55 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Mon, Oct 3, 2016 at 3:30 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

[ new patch ]

I think this is unnecessarily awkward for callers; the attached
version takes a different approach which I think will be more
convenient. The attached version also (1) moves a lot more of the
logic from latch.c/h to pgstat.c/h, which I think is more appropriate;
(2) more thoroughly separates the wait events by class; (3) renames
SecureRead/SecureWrite to ClientRead/ClientWrite (whether to also
rename the C functions is an interesting question, but not the most
pressing one IMHO), (4) creates a real wait event for GetSafeSnapshot
and removes the unnecessary and overly generic ProcSleep and
ProcSignal wait events, and (5) incorporates a bit of copy editing.

OK with that.

I've tested that this seems to work in basic cases, but more testing
is surely welcome. If there are no major objections, I will commit
this version.

In pgstat_get_wait_event_type you are forgetting WAIT_IPC.

+        <row>
+         <entry morerows="10"><literal>IPC</></entry>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
Here this should be morerows=9. You removed two entries, and added one
with SafeSnapshot.

The rest looks good to me. Thanks for the feedback and the time!
--
Michael

Attachments:

wait-event-v14.patchapplication/x-download; name=wait-event-v14.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 8ca1c1c..9badfe6 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -17,6 +17,7 @@
 #include "access/xact.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
@@ -496,7 +497,7 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L);
+								   -1L, WAIT_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index f400785..3de489e 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -679,6 +679,42 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
           buffer in question.
          </para>
         </listitem>
+        <listitem>
+         <para>
+          <literal>Activity</>: The server process is idle.  This is used by
+          system processes waiting for activity in their main processing loop.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Extension</>: The server process is waiting for activity
+          in an extension module.  This category is useful for modules to
+          track custom waiting points.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Client</>: The server process is waiting for some activity
+          on a socket from user applications, and that the server expects
+          something to happen that is independent from its internal processes.
+          <literal>wait_event</> will identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>IPC</>: The server process is waiting for some activity
+          from another process in the server.  <literal>wait_event</> will
+          identify the specific wait point.
+         </para>
+        </listitem>
+        <listitem>
+         <para>
+          <literal>Timeout</>: The server process is waiting for a timeout
+          to expire.  <literal>wait_event</> will identify the specific wait
+          point.
+         </para>
+        </listitem>
        </itemizedlist>
       </entry>
      </row>
@@ -1085,6 +1121,139 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
          <entry><literal>BufferPin</></entry>
          <entry>Waiting to acquire a pin on a buffer.</entry>
         </row>
+        <row>
+         <entry morerows="11"><literal>Activity</></entry>
+         <entry><literal>ArchiverMain</></entry>
+         <entry>Waiting in main loop of the archiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>AutoVacuumMain</></entry>
+         <entry>Waiting in main loop of autovacuum launcher process.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterHibernate</></entry>
+         <entry>Waiting in background writer process, hibernating.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWriterMain</></entry>
+         <entry>Waiting in main loop of background writer process background worker.</entry>
+        </row>
+        <row>
+         <entry><literal>CheckpointerMain</></entry>
+         <entry>Waiting in main loop of checkpointer process.</entry>
+        </row>
+        <row>
+         <entry><literal>PgStatMain</></entry>
+         <entry>Waiting in main loop of the statistics collector process.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalAll</></entry>
+         <entry>Waiting for WAL from any kind of source (local, archive or stream) at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryWalStream</></entry>
+         <entry>Waiting for WAL from a stream at recovery.</entry>
+        </row>
+        <row>
+         <entry><literal>SysLoggerMain</></entry>
+         <entry>Waiting in main loop of syslogger process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverMain</></entry>
+         <entry>Waiting in main loop of WAL receiver process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderMain</></entry>
+         <entry>Waiting in main loop of WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalWriterMain</></entry>
+         <entry>Waiting in main loop of WAL writer process.</entry>
+        </row>
+        <row>
+         <entry morerows="5"><literal>Client</></entry>
+         <entry><literal>ClientRead</></entry>
+         <entry>Waiting to read data from the client.</entry>
+        </row>
+        <row>
+         <entry><literal>ClientWrite</></entry>
+         <entry>Waiting to write data from the client.</entry>
+        </row>
+        <row>
+         <entry><literal>SSLOpenServer</></entry>
+         <entry>Waiting for SSL while attempting connection.</entry>
+        </row>
+        <row>
+         <entry><literal>WalReceiverWaitStart</></entry>
+         <entry>Waiting for startup process to send initial data for streaming replication.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWaitForWAL</></entry>
+         <entry>Waiting for WAL to be flushed in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>WalSenderWriteData</></entry>
+         <entry>Waiting for any activity when processing replies from WAL receiver in WAL sender process.</entry>
+        </row>
+        <row>
+         <entry><literal>Extension</></entry>
+         <entry><literal>Extension</></entry>
+         <entry>Waiting in an extension.</entry>
+        </row>
+        <row>
+         <entry morerows="9"><literal>IPC</></entry>
+         <entry><literal>BgWorkerShutdown</></entry>
+         <entry>Waiting for background worker to shut down.</entry>
+        </row>
+        <row>
+         <entry><literal>BgWorkerStartup</></entry>
+         <entry>Waiting for background worker to start up.</entry>
+        </row>
+        <row>
+         <entry><literal>ExecuteGather</></entry>
+         <entry>Waiting for activity from child process when executing <literal>Gather</> node.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueInternal</></entry>
+         <entry>Waiting for other process to be attached in shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueuePutMessage</></entry>
+         <entry>Waiting to write a protoocol message to a shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueReceive</></entry>
+         <entry>Waiting to receive bytes from a shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>MessageQueueSend</></entry>
+         <entry>Waiting to send bytes to a shared message queue.</entry>
+        </row>
+        <row>
+         <entry><literal>ParallelFinish</></entry>
+         <entry>Waiting for parallel workers to finish computing.</entry>
+        </row>
+        <row>
+         <entry><literal>SafeSnapshot</></entry>
+         <entry>Waiting for a snapshot for a <literal>READ ONLY DEFERRABLE</> transaction.</entry>
+        </row>
+        <row>
+         <entry><literal>SyncRep</></entry>
+         <entry>Waiting for confirmation from remote server during synchronous replication.</entry>
+        </row>
+        <row>
+         <entry morerows="2"><literal>Timeout</></entry>
+         <entry><literal>BaseBackupThrottle</></entry>
+         <entry>Waiting during base backup when throttling activity.</entry>
+        </row>
+        <row>
+         <entry><literal>PgSleep</></entry>
+         <entry>Waiting in process that called <function>pg_sleep</>.</entry>
+        </row>
+        <row>
+         <entry><literal>RecoveryApplyDelay</></entry>
+         <entry>Waiting to apply WAL at recovery because it is delayed.</entry>
+        </row>
       </tbody>
      </tgroup>
     </table>
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index cde0ed3..59dc394 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -24,6 +24,7 @@
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
 #include "optimizer/planmain.h"
+#include "pgstat.h"
 #include "storage/ipc.h"
 #include "storage/sinval.h"
 #include "storage/spin.h"
@@ -540,7 +541,8 @@ WaitForParallelWorkersToFinish(ParallelContext *pcxt)
 		if (!anyone_alive)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, -1,
+				  WAIT_EVENT_PARALLEL_FINISH);
 		ResetLatch(&MyProc->procLatch);
 	}
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index c1b9a97..08c87f9 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5827,7 +5827,8 @@ recoveryApplyDelay(XLogReaderState *record)
 
 		WaitLatch(&XLogCtl->recoveryWakeupLatch,
 				  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-				  secs * 1000L + microsecs / 1000);
+				  secs * 1000L + microsecs / 1000,
+				  WAIT_EVENT_RECOVERY_APPLY_DELAY);
 	}
 	return true;
 }
@@ -11387,7 +11388,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 
 						WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								  wait_time);
+								  wait_time, WAIT_EVENT_RECOVERY_WAL_STREAM);
 						ResetLatch(&XLogCtl->recoveryWakeupLatch);
 						now = GetCurrentTimestamp();
 					}
@@ -11550,7 +11551,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 					 */
 					WaitLatch(&XLogCtl->recoveryWakeupLatch,
 							  WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							  5000L);
+							  5000L, WAIT_EVENT_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
 					break;
 				}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..880ca62 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -38,6 +38,7 @@
 #include "executor/nodeSubplan.h"
 #include "executor/tqueue.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
 
@@ -387,7 +388,7 @@ gather_readnext(GatherState *gatherstate)
 				return NULL;
 
 			/* Nothing to do except wait for developments. */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EVENT_EXECUTE_GATHER);
 			ResetLatch(MyLatch);
 			nvisited = 0;
 		}
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index fedb02c..668f217 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -60,6 +60,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/latch.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
@@ -419,7 +420,8 @@ aloop:
 				else
 					waitfor = WL_SOCKET_WRITEABLE;
 
-				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
+				WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0,
+								  WAIT_EVENT_SSL_OPEN_SERVER);
 				goto aloop;
 			case SSL_ERROR_SYSCALL:
 				if (r < 0)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index cdd07d5..b267507 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -33,6 +33,7 @@
 
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 #include "storage/ipc.h"
@@ -146,7 +147,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WAIT_EVENT_CLIENT_READ);
 
 		/*
 		 * If the postmaster has died, it's not safe to continue running,
@@ -247,7 +249,8 @@ retry:
 
 		ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL);
 
-		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1);
+		WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1,
+						 WAIT_EVENT_CLIENT_WRITE);
 
 		/* See comments in secure_read. */
 		if (event.events & WL_POSTMASTER_DEATH)
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index bfe66c6..f93ccae 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -17,6 +17,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqmq.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 
@@ -171,7 +172,8 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 		if (result != SHM_MQ_WOULD_BLOCK)
 			break;
 
-		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0,
+				  WAIT_EVENT_MQ_PUT_MESSAGE);
 		ResetLatch(&MyProc->procLatch);
 		CHECK_FOR_INTERRUPTS();
 	}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1a92ca1..e3a6911 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -598,7 +598,8 @@ AutoVacLauncherMain(int argc, char *argv[])
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));
+					   (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
+					   WAIT_EVENT_AUTOVACUUM_MAIN);
 
 		ResetLatch(MyLatch);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 699c934..028a9ee 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -18,6 +18,7 @@
 #include "libpq/pqsignal.h"
 #include "postmaster/bgworker_internals.h"
 #include "postmaster/postmaster.h"
+#include "pgstat.h"
 #include "storage/barrier.h"
 #include "storage/dsm.h"
 #include "storage/ipc.h"
@@ -969,7 +970,8 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
 			break;
 
 		rc = WaitLatch(MyLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WAIT_EVENT_BGWORKER_STARTUP);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
@@ -1008,7 +1010,8 @@ WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
 			break;
 
 		rc = WaitLatch(&MyProc->procLatch,
-					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+					   WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+					   WAIT_EVENT_BGWORKER_SHUTDOWN);
 
 		if (rc & WL_POSTMASTER_DEATH)
 		{
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 1002034..c3f3356 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -345,7 +345,7 @@ BackgroundWriterMain(void)
 		 */
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   BgWriterDelay /* ms */ );
+					   BgWriterDelay /* ms */, WAIT_EVENT_BGWRITER_MAIN);
 
 		/*
 		 * If no latch event and BgBufferSync says nothing's happening, extend
@@ -372,7 +372,8 @@ BackgroundWriterMain(void)
 			/* Sleep ... */
 			rc = WaitLatch(MyLatch,
 						   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-						   BgWriterDelay * HIBERNATE_FACTOR);
+						   BgWriterDelay * HIBERNATE_FACTOR,
+						   WAIT_EVENT_BGWRITER_HIBERNATE);
 			/* Reset the notification request in case we timed out */
 			StrategyNotifyBgWriter(-1);
 		}
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index d702a48..397267c 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -556,7 +556,8 @@ CheckpointerMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout * 1000L /* convert to ms */ );
+					   cur_timeout * 1000L /* convert to ms */,
+					   WAIT_EVENT_CHECKPOINTER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1aa6466..62783d9 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -390,7 +390,8 @@ pgarch_MainLoop(void)
 
 				rc = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-							   timeout * 1000L);
+							   timeout * 1000L,
+							   WAIT_EVENT_ARCHIVER_MAIN);
 				if (rc & WL_TIMEOUT)
 					wakened = true;
 			}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 96578dc..8c9d06f 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -276,6 +276,11 @@ static PgStat_TableStatus *get_tabstat_entry(Oid rel_id, bool isshared);
 
 static void pgstat_setup_memcxt(void);
 
+static const char *pgstat_get_wait_activity(WaitEventActivity w);
+static const char *pgstat_get_wait_client(WaitEventClient w);
+static const char *pgstat_get_wait_ipc(WaitEventIPC w);
+static const char *pgstat_get_wait_timeout(WaitEventTimeout w);
+
 static void pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype);
 static void pgstat_send(void *msg, int len);
 
@@ -3131,15 +3136,14 @@ pgstat_read_current_status(void)
 const char *
 pgstat_get_wait_event_type(uint32 wait_event_info)
 {
-	uint8		classId;
+	uint32		classId;
 	const char *event_type;
 
 	/* report process as not waiting. */
 	if (wait_event_info == 0)
 		return NULL;
 
-	wait_event_info = wait_event_info >> 24;
-	classId = wait_event_info & 0XFF;
+	classId = wait_event_info & 0xFF000000;
 
 	switch (classId)
 	{
@@ -3155,6 +3159,21 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+			event_type = "Activity";
+			break;
+		case WAIT_CLIENT:
+			event_type = "Client";
+			break;
+		case WAIT_EXTENSION:
+			event_type = "Extension";
+			break;
+		case WAIT_IPC:
+			event_type = "IPC";
+			break;
+		case WAIT_TIMEOUT:
+			event_type = "Timeout";
+			break;
 		default:
 			event_type = "???";
 			break;
@@ -3172,7 +3191,7 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 const char *
 pgstat_get_wait_event(uint32 wait_event_info)
 {
-	uint8		classId;
+	uint32		classId;
 	uint16		eventId;
 	const char *event_name;
 
@@ -3180,9 +3199,8 @@ pgstat_get_wait_event(uint32 wait_event_info)
 	if (wait_event_info == 0)
 		return NULL;
 
-	eventId = wait_event_info & ((1 << 24) - 1);
-	wait_event_info = wait_event_info >> 24;
-	classId = wait_event_info & 0XFF;
+	classId = wait_event_info & 0xFF000000;
+	eventId = wait_event_info & 0x0000FFFF;
 
 	switch (classId)
 	{
@@ -3196,6 +3214,37 @@ pgstat_get_wait_event(uint32 wait_event_info)
 		case WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
+		case WAIT_ACTIVITY:
+			{
+				WaitEventActivity	w = (WaitEventActivity) wait_event_info;
+
+				event_name = pgstat_get_wait_activity(w);
+				break;
+			}
+		case WAIT_CLIENT:
+			{
+				WaitEventClient	w = (WaitEventClient) wait_event_info;
+
+				event_name = pgstat_get_wait_client(w);
+				break;
+			}
+		case WAIT_EXTENSION:
+			event_name = "Extension";
+			break;
+		case WAIT_IPC:
+			{
+				WaitEventIPC	w = (WaitEventIPC) wait_event_info;
+
+				event_name = pgstat_get_wait_ipc(w);
+				break;
+			}
+		case WAIT_TIMEOUT:
+			{
+				WaitEventTimeout	w = (WaitEventTimeout) wait_event_info;
+
+				event_name = pgstat_get_wait_timeout(w);
+				break;
+			}
 		default:
 			event_name = "unknown wait event";
 			break;
@@ -3205,6 +3254,175 @@ pgstat_get_wait_event(uint32 wait_event_info)
 }
 
 /* ----------
+ * pgstat_get_wait_activity() -
+ *
+ * Convert WaitEventActivity to string.
+ * ----------
+ */
+static const char *
+pgstat_get_wait_activity(WaitEventActivity w)
+{
+	const char *event_name = "unknown wait event";
+
+	switch (w)
+	{
+		case WAIT_EVENT_ARCHIVER_MAIN:
+			event_name = "ArchiverMain";
+			break;
+		case WAIT_EVENT_AUTOVACUUM_MAIN:
+			event_name = "AutoVacuumMain";
+			break;
+		case WAIT_EVENT_BGWRITER_HIBERNATE:
+			event_name = "BgWriterHibernate";
+			break;
+		case WAIT_EVENT_BGWRITER_MAIN:
+			event_name = "BgWriterMain";
+			break;
+		case WAIT_EVENT_CHECKPOINTER_MAIN:
+			event_name = "CheckpointerMain";
+			break;
+		case WAIT_EVENT_PGSTAT_MAIN:
+			event_name = "PgStatMain";
+			break;
+		case WAIT_EVENT_RECOVERY_WAL_ALL:
+			event_name = "RecoveryWalAll";
+			break;
+		case WAIT_EVENT_RECOVERY_WAL_STREAM:
+			event_name = "RecoveryWalStream";
+			break;
+		case WAIT_EVENT_SYSLOGGER_MAIN:
+			event_name = "SysLoggerMain";
+			break;
+		case WAIT_EVENT_WAL_RECEIVER_MAIN:
+			event_name = "WalReceiverMain";
+			break;
+		case WAIT_EVENT_WAL_SENDER_MAIN:
+			event_name = "WalSenderMain";
+			break;
+		case WAIT_EVENT_WAL_WRITER_MAIN:
+			event_name = "WalWriterMain";
+			break;
+		/* no default case, so that compiler will warn */
+	}
+
+	return event_name;
+}
+
+/* ----------
+ * pgstat_get_wait_client() -
+ *
+ * Convert WaitEventClient to string.
+ * ----------
+ */
+static const char *
+pgstat_get_wait_client(WaitEventClient w)
+{
+	const char *event_name = "unknown wait event";
+
+	switch (w)
+	{
+		case WAIT_EVENT_CLIENT_READ:
+			event_name = "ClientRead";
+			break;
+		case WAIT_EVENT_CLIENT_WRITE:
+			event_name = "ClientWrite";
+			break;
+		case WAIT_EVENT_SSL_OPEN_SERVER:
+			event_name = "SSLOpenServer";
+			break;
+		case WAIT_EVENT_WAL_RECEIVER_WAIT_START:
+			event_name = "WalReceiverWaitStart";
+			break;
+		case WAIT_EVENT_WAL_SENDER_WAIT_WAL:
+			event_name = "WalSenderWaitForWAL";
+			break;
+		case WAIT_EVENT_WAL_SENDER_WRITE_DATA:
+			event_name = "WalSenderWriteData";
+			break;
+		/* no default case, so that compiler will warn */
+	}
+
+	return event_name;
+}
+
+/* ----------
+ * pgstat_get_wait_ipc() -
+ *
+ * Convert WaitEventIPC to string.
+ * ----------
+ */
+static const char *
+pgstat_get_wait_ipc(WaitEventIPC w)
+{
+	const char *event_name = "unknown wait event";
+
+	switch (w)
+	{
+		case WAIT_EVENT_BGWORKER_SHUTDOWN:
+			event_name = "BgWorkerShutdown";
+			break;
+		case WAIT_EVENT_BGWORKER_STARTUP:
+			event_name = "BgWorkerStartup";
+			break;
+		case WAIT_EVENT_EXECUTE_GATHER:
+			event_name = "ExecuteGather";
+			break;
+		case WAIT_EVENT_MQ_INTERNAL:
+			event_name = "MessageQueueInternal";
+			break;
+		case WAIT_EVENT_MQ_PUT_MESSAGE:
+			event_name = "MessageQueuePutMessage";
+			break;
+		case WAIT_EVENT_MQ_RECEIVE:
+			event_name = "MessageQueueReceive";
+			break;
+		case WAIT_EVENT_MQ_SEND:
+			event_name = "MessageQueueSend";
+			break;
+		case WAIT_EVENT_PARALLEL_FINISH:
+			event_name = "ParallelFinish";
+			break;
+		case WAIT_EVENT_SAFE_SNAPSHOT:
+			event_name = "SafeSnapshot";
+			break;
+		case WAIT_EVENT_SYNC_REP:
+			event_name = "SyncRep";
+			break;
+		/* no default case, so that compiler will warn */
+	}
+
+	return event_name;
+}
+
+/* ----------
+ * pgstat_get_wait_timeout() -
+ *
+ * Convert WaitEventTimeout to string.
+ * ----------
+ */
+static const char *
+pgstat_get_wait_timeout(WaitEventTimeout w)
+{
+	const char *event_name = "unknown wait event";
+
+	switch (w)
+	{
+		case WAIT_EVENT_BASE_BACKUP_THROTTLE:
+			event_name = "BaseBackupThrottle";
+			break;
+		case WAIT_EVENT_PG_SLEEP:
+			event_name = "PgSleep";
+			break;
+		case WAIT_EVENT_RECOVERY_APPLY_DELAY:
+			event_name = "RecoveryApplyDelay";
+			break;
+		/* no default case, so that compiler will warn */
+	}
+
+	return event_name;
+}
+
+/* ----------
  * pgstat_get_backend_current_activity() -
  *
  *	Return a string representing the current activity of the backend with
@@ -3684,8 +3902,8 @@ PgstatCollectorMain(int argc, char *argv[])
 #ifndef WIN32
 		wr = WaitLatchOrSocket(MyLatch,
 					 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
-							   pgStatSock,
-							   -1L);
+							   pgStatSock, -1L,
+							   WAIT_EVENT_PGSTAT_MAIN);
 #else
 
 		/*
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index e7e488a..af71367 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -35,6 +35,7 @@
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "nodes/pg_list.h"
+#include "pgstat.h"
 #include "pgtime.h"
 #include "postmaster/fork_process.h"
 #include "postmaster/postmaster.h"
@@ -424,7 +425,8 @@ SysLoggerMain(int argc, char *argv[])
 		rc = WaitLatchOrSocket(MyLatch,
 							   WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
 							   syslogPipe[0],
-							   cur_timeout);
+							   cur_timeout,
+							   WAIT_EVENT_SYSLOGGER_MAIN);
 
 		if (rc & WL_SOCKET_READABLE)
 		{
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 11ec56a..67dcff6 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -290,7 +290,8 @@ WalWriterMain(void)
 
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-					   cur_timeout);
+					   cur_timeout,
+					   WAIT_EVENT_WAL_WRITER_MAIN);
 
 		/*
 		 * Emergency bailout if postmaster has died.  This is to avoid the
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index 1eabaef..fa75930 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -1364,7 +1364,8 @@ throttle(size_t increment)
 		 */
 		wait_result = WaitLatch(MyLatch,
 							 WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
-								(long) (sleep / 1000));
+								(long) (sleep / 1000),
+								WAIT_EVENT_BASE_BACKUP_THROTTLE);
 
 		if (wait_result & WL_LATCH_SET)
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index b442d06..ac29f56 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -61,6 +61,7 @@
 
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/syncrep.h"
 #include "replication/walsender.h"
 #include "replication/walsender_private.h"
@@ -258,7 +259,8 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 * Wait on latch.  Any condition that should wake us up will set the
 		 * latch, so no need for timeout.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
+		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1,
+				  WAIT_EVENT_SYNC_REP);
 	}
 
 	/*
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 413ee3a..eed6eff 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -55,6 +55,7 @@
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
 #include "storage/ipc.h"
@@ -486,7 +487,8 @@ WalReceiverMain(void)
 								   WL_POSTMASTER_DEATH | WL_SOCKET_READABLE |
 									   WL_TIMEOUT | WL_LATCH_SET,
 									   wait_fd,
-									   NAPTIME_PER_CYCLE);
+									   NAPTIME_PER_CYCLE,
+									   WAIT_EVENT_WAL_RECEIVER_MAIN);
 				if (rc & WL_LATCH_SET)
 				{
 					ResetLatch(&walrcv->latch);
@@ -685,7 +687,8 @@ WalRcvWaitForStartPosition(XLogRecPtr *startpoint, TimeLineID *startpointTLI)
 		}
 		SpinLockRelease(&walrcv->mutex);
 
-		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0);
+		WaitLatch(&walrcv->latch, WL_LATCH_SET | WL_POSTMASTER_DEATH, 0,
+				  WAIT_EVENT_WAL_RECEIVER_WAIT_START);
 	}
 
 	if (update_process_title)
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index c7743da..0f3ced2 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1146,7 +1146,8 @@ WalSndWriteData(LogicalDecodingContext *ctx, XLogRecPtr lsn, TransactionId xid,
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WAIT_EVENT_WAL_SENDER_WRITE_DATA);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1272,7 +1273,8 @@ WalSndWaitForWal(XLogRecPtr loc)
 
 		/* Sleep until something happens or we time out */
 		WaitLatchOrSocket(MyLatch, wakeEvents,
-						  MyProcPort->sock, sleeptime);
+						  MyProcPort->sock, sleeptime,
+						  WAIT_EVENT_WAL_SENDER_WAIT_WAL);
 	}
 
 	/* reactivate latch so WalSndLoop knows to continue */
@@ -1924,7 +1926,8 @@ WalSndLoop(WalSndSendDataCallback send_data)
 
 			/* Sleep until something happens or we time out */
 			WaitLatchOrSocket(MyLatch, wakeEvents,
-							  MyProcPort->sock, sleeptime);
+							  MyProcPort->sock, sleeptime,
+							  WAIT_EVENT_WAL_SENDER_MAIN);
 		}
 	}
 	return;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 90804a3..91dc24c 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3635,9 +3635,6 @@ LockBufferForCleanup(Buffer buffer)
 		UnlockBufHdr(bufHdr, buf_state);
 		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 
-		/* Report the wait */
-		pgstat_report_wait_start(WAIT_BUFFER_PIN, 0);
-
 		/* Wait to be signaled by UnpinBuffer() */
 		if (InHotStandby)
 		{
@@ -3649,9 +3646,7 @@ LockBufferForCleanup(Buffer buffer)
 			SetStartupBufferPinWaitBufId(-1);
 		}
 		else
-			ProcWaitForSignal();
-
-		pgstat_report_wait_end();
+			ProcWaitForSignal(WAIT_BUFFER_PIN);
 
 		/*
 		 * Remove flag marking us as waiter. Normally this will not be set
diff --git a/src/backend/storage/ipc/latch.c b/src/backend/storage/ipc/latch.c
index 9def8a1..8488f94 100644
--- a/src/backend/storage/ipc/latch.c
+++ b/src/backend/storage/ipc/latch.c
@@ -55,6 +55,7 @@
 #endif
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "portability/instr_time.h"
 #include "postmaster/postmaster.h"
 #include "storage/barrier.h"
@@ -297,9 +298,11 @@ DisownLatch(volatile Latch *latch)
  * we return all of them in one call, but we will return at least one.
  */
 int
-WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
+WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  uint32 wait_event_info)
 {
-	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout);
+	return WaitLatchOrSocket(latch, wakeEvents, PGINVALID_SOCKET, timeout,
+							 wait_event_info);
 }
 
 /*
@@ -316,7 +319,7 @@ WaitLatch(volatile Latch *latch, int wakeEvents, long timeout)
  */
 int
 WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
-				  long timeout)
+				  long timeout, uint32 wait_event_info)
 {
 	int			ret = 0;
 	int			rc;
@@ -344,7 +347,7 @@ WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock,
 		AddWaitEventToSet(set, ev, sock, NULL, NULL);
 	}
 
-	rc = WaitEventSetWait(set, timeout, &event, 1);
+	rc = WaitEventSetWait(set, timeout, &event, 1, wait_event_info);
 
 	if (rc == 0)
 		ret |= WL_TIMEOUT;
@@ -863,7 +866,8 @@ WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
  */
 int
 WaitEventSetWait(WaitEventSet *set, long timeout,
-				 WaitEvent *occurred_events, int nevents)
+				 WaitEvent *occurred_events, int nevents,
+				 uint32 wait_event_info)
 {
 	int			returned_events = 0;
 	instr_time	start_time;
@@ -883,6 +887,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 		cur_timeout = timeout;
 	}
 
+	pgstat_report_wait_start(wait_event_info);
+
 #ifndef WIN32
 	waiting = true;
 #else
@@ -960,6 +966,8 @@ WaitEventSetWait(WaitEventSet *set, long timeout,
 	waiting = false;
 #endif
 
+	pgstat_report_wait_end();
+
 	return returned_events;
 }
 
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 5b32782..bfb6703 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -19,6 +19,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker.h"
 #include "storage/procsignal.h"
 #include "storage/shm_mq.h"
@@ -894,7 +895,7 @@ shm_mq_send_bytes(shm_mq_handle *mqh, Size nbytes, const void *data,
 			 * at top of loop, because setting an already-set latch is much
 			 * cheaper than setting one that has been reset.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EVENT_MQ_SEND);
 
 			/* Reset the latch so we don't spin. */
 			ResetLatch(MyLatch);
@@ -991,7 +992,7 @@ shm_mq_receive_bytes(shm_mq *mq, Size bytes_needed, bool nowait,
 		 * loop, because setting an already-set latch is much cheaper than
 		 * setting one that has been reset.
 		 */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EVENT_MQ_RECEIVE);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
@@ -1090,7 +1091,7 @@ shm_mq_wait_internal(volatile shm_mq *mq, PGPROC *volatile * ptr,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EVENT_MQ_INTERNAL);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 547f1a8..fb887b3 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -22,6 +22,7 @@
 #include "access/xlog.h"
 #include "access/xloginsert.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "storage/proc.h"
@@ -389,7 +390,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag)
 	}
 
 	/* Wait to be signaled by the release of the Relation Lock */
-	ProcWaitForSignal();
+	ProcWaitForSignal(WAIT_LOCK | locktag.locktag_type);
 
 	/*
 	 * Clear any timeout requests established above.  We assume here that the
@@ -469,7 +470,7 @@ ResolveRecoveryConflictWithBufferPin(void)
 	}
 
 	/* Wait to be signaled by UnpinBuffer() */
-	ProcWaitForSignal();
+	ProcWaitForSignal(WAIT_BUFFER_PIN);
 
 	/*
 	 * Clear any timeout requests established above.  We assume here that the
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index dba3809..71a4dd4 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -1676,7 +1676,6 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 		set_ps_display(new_status, false);
 		new_status[len] = '\0'; /* truncate off " waiting" */
 	}
-	pgstat_report_wait_start(WAIT_LOCK, locallock->tag.lock.locktag_type);
 
 	awaitedLock = locallock;
 	awaitedOwner = owner;
@@ -1724,7 +1723,6 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 		/* In this path, awaitedLock remains set until LockErrorCleanup */
 
 		/* Report change to non-waiting status */
-		pgstat_report_wait_end();
 		if (update_process_title)
 		{
 			set_ps_display(new_status, false);
@@ -1739,7 +1737,6 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 	awaitedLock = NULL;
 
 	/* Report change to non-waiting status */
-	pgstat_report_wait_end();
 	if (update_process_title)
 	{
 		set_ps_display(new_status, false);
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 9d08de7..a90b54a 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -732,9 +732,9 @@ LWLockReportWaitStart(LWLock *lock)
 	int			lockId = T_ID(lock);
 
 	if (lock->tranche == 0)
-		pgstat_report_wait_start(WAIT_LWLOCK_NAMED, (uint16) lockId);
+		pgstat_report_wait_start(WAIT_LWLOCK_NAMED | (uint16) lockId);
 	else
-		pgstat_report_wait_start(WAIT_LWLOCK_TRANCHE, lock->tranche);
+		pgstat_report_wait_start(WAIT_LWLOCK_TRANCHE | lock->tranche);
 }
 
 /*
@@ -750,7 +750,7 @@ LWLockReportWaitEnd(void)
  * Return an identifier for an LWLock based on the wait class and event.
  */
 const char *
-GetLWLockIdentifier(uint8 classId, uint16 eventId)
+GetLWLockIdentifier(uint32 classId, uint16 eventId)
 {
 	if (classId == WAIT_LWLOCK_NAMED)
 		return MainLWLockNames[eventId];
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 4064b20..24ed21b 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -192,6 +192,7 @@
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/bufmgr.h"
 #include "storage/predicate.h"
 #include "storage/predicate_internals.h"
@@ -1518,7 +1519,7 @@ GetSafeSnapshot(Snapshot origSnapshot)
 				 SxactIsROUnsafe(MySerializableXact)))
 		{
 			LWLockRelease(SerializableXactHashLock);
-			ProcWaitForSignal();
+			ProcWaitForSignal(WAIT_EVENT_SAFE_SNAPSHOT);
 			LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
 		}
 		MySerializableXact->flags &= ~SXACT_FLAG_DEFERRABLE_WAITING;
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 33e7023..dd76094 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -39,6 +39,7 @@
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/autovacuum.h"
 #include "replication/slot.h"
 #include "replication/syncrep.h"
@@ -1212,7 +1213,8 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		}
 		else
 		{
-			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0,
+					  WAIT_LOCK | locallock->tag.lock.locktag_type);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
@@ -1722,9 +1724,9 @@ CheckDeadLockAlert(void)
  * wait again if not.
  */
 void
-ProcWaitForSignal(void)
+ProcWaitForSignal(uint32 wait_event_info)
 {
-	WaitLatch(MyLatch, WL_LATCH_SET, 0);
+	WaitLatch(MyLatch, WL_LATCH_SET, 0, wait_event_info);
 	ResetLatch(MyLatch);
 	CHECK_FOR_INTERRUPTS();
 }
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 5e705e9..0da051a 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -29,6 +29,7 @@
 #include "common/keywords.h"
 #include "funcapi.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "parser/scansup.h"
 #include "postmaster/syslogger.h"
 #include "rewrite/rewriteHandler.h"
@@ -560,7 +561,8 @@ pg_sleep(PG_FUNCTION_ARGS)
 
 		(void) WaitLatch(MyLatch,
 						 WL_LATCH_SET | WL_TIMEOUT,
-						 delay_ms);
+						 delay_ms,
+						 WAIT_EVENT_PG_SLEEP);
 		ResetLatch(MyLatch);
 	}
 
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 0c98c59..b530c01 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -715,15 +715,91 @@ typedef enum BackendState
  * Wait Classes
  * ----------
  */
-typedef enum WaitClass
+#define WAIT_LWLOCK_NAMED			0x01000000U
+#define WAIT_LWLOCK_TRANCHE			0x02000000U
+#define WAIT_LOCK					0x03000000U
+#define WAIT_BUFFER_PIN				0x04000000U
+#define WAIT_ACTIVITY				0x05000000U
+#define WAIT_CLIENT					0x06000000U
+#define WAIT_EXTENSION				0x07000000U
+#define WAIT_IPC					0x08000000U
+#define WAIT_TIMEOUT				0x09000000U
+
+/* ----------
+ * Wait Events - Activity
+ *
+ * Use this category when a process is waiting because it has no work to do,
+ * unless the "Client" or "Timeout" category describes the situation better.
+ * Typically, this should only be used for background processes.
+ * ----------
+ */
+typedef enum
 {
-	WAIT_UNDEFINED,
-	WAIT_LWLOCK_NAMED,
-	WAIT_LWLOCK_TRANCHE,
-	WAIT_LOCK,
-	WAIT_BUFFER_PIN
-}	WaitClass;
+	WAIT_EVENT_ARCHIVER_MAIN = WAIT_ACTIVITY,
+	WAIT_EVENT_AUTOVACUUM_MAIN,
+	WAIT_EVENT_BGWRITER_HIBERNATE,
+	WAIT_EVENT_BGWRITER_MAIN,
+	WAIT_EVENT_CHECKPOINTER_MAIN,
+	WAIT_EVENT_PGSTAT_MAIN,
+	WAIT_EVENT_RECOVERY_WAL_ALL,
+	WAIT_EVENT_RECOVERY_WAL_STREAM,
+	WAIT_EVENT_SYSLOGGER_MAIN,
+	WAIT_EVENT_WAL_RECEIVER_MAIN,
+	WAIT_EVENT_WAL_SENDER_MAIN,
+	WAIT_EVENT_WAL_WRITER_MAIN,
+} WaitEventActivity;
 
+/* ----------
+ * Wait Events - Client
+ *
+ * Use this category when a process is waiting to send data to or receive data
+ * from the frontend process to which it is connected.  This is never used for
+ * a background process, which has no client connection.
+ * ----------
+ */
+typedef enum
+{
+	WAIT_EVENT_CLIENT_READ = WAIT_CLIENT,
+	WAIT_EVENT_CLIENT_WRITE,
+	WAIT_EVENT_SSL_OPEN_SERVER,
+	WAIT_EVENT_WAL_RECEIVER_WAIT_START,
+	WAIT_EVENT_WAL_SENDER_WAIT_WAL,
+	WAIT_EVENT_WAL_SENDER_WRITE_DATA,
+} WaitEventClient;
+
+/* ----------
+ * Wait Events - IPC
+ *
+ * Use this category when a process cannot complete the work it is doing because
+ * it is waiting for a notification from another process.
+ * ----------
+ */
+typedef enum
+{
+	WAIT_EVENT_BGWORKER_SHUTDOWN = WAIT_IPC,
+	WAIT_EVENT_BGWORKER_STARTUP,
+	WAIT_EVENT_EXECUTE_GATHER,
+	WAIT_EVENT_MQ_INTERNAL,
+	WAIT_EVENT_MQ_PUT_MESSAGE,
+	WAIT_EVENT_MQ_RECEIVE,
+	WAIT_EVENT_MQ_SEND,
+	WAIT_EVENT_PARALLEL_FINISH,
+	WAIT_EVENT_SAFE_SNAPSHOT,
+	WAIT_EVENT_SYNC_REP
+} WaitEventIPC;
+
+/* ----------
+ * Wait Events - Timeout
+ *
+ * Use this category when a process is waiting for a timeout to expire.
+ * ----------
+ */
+typedef enum
+{
+	WAIT_EVENT_BASE_BACKUP_THROTTLE = WAIT_TIMEOUT,
+	WAIT_EVENT_PG_SLEEP,
+	WAIT_EVENT_RECOVERY_APPLY_DELAY
+} WaitEventTimeout;
 
 /* ----------
  * Command type for progress reporting purposes
@@ -1018,23 +1094,18 @@ extern void pgstat_initstats(Relation rel);
  * ----------
  */
 static inline void
-pgstat_report_wait_start(uint8 classId, uint16 eventId)
+pgstat_report_wait_start(uint32 wait_event_info)
 {
 	volatile PGPROC *proc = MyProc;
-	uint32		wait_event_val;
 
 	if (!pgstat_track_activities || !proc)
 		return;
 
-	wait_event_val = classId;
-	wait_event_val <<= 24;
-	wait_event_val |= eventId;
-
 	/*
 	 * Since this is a four-byte field which is always read and written as
 	 * four-bytes, updates are atomic.
 	 */
-	proc->wait_event_info = wait_event_val;
+	proc->wait_event_info = wait_event_info;
 }
 
 /* ----------
diff --git a/src/include/storage/latch.h b/src/include/storage/latch.h
index 5179ecc..e96e88f 100644
--- a/src/include/storage/latch.h
+++ b/src/include/storage/latch.h
@@ -155,10 +155,13 @@ extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd,
 				  Latch *latch, void *user_data);
 extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
 
-extern int	WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents);
-extern int	WaitLatch(volatile Latch *latch, int wakeEvents, long timeout);
+extern int WaitEventSetWait(WaitEventSet *set, long timeout,
+				 WaitEvent *occurred_events, int nevents,
+				 uint32 wait_event_info);
+extern int WaitLatch(volatile Latch *latch, int wakeEvents, long timeout,
+		  uint32 wait_event_info);
 extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents,
-				  pgsocket sock, long timeout);
+				  pgsocket sock, long timeout, uint32 wait_event_info);
 
 /*
  * Unix implementation uses SIGUSR1 for inter-process signaling.
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 18931eb..9a2d869 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -184,7 +184,7 @@ extern Size LWLockShmemSize(void);
 extern void CreateLWLocks(void);
 extern void InitLWLockAccess(void);
 
-extern const char *GetLWLockIdentifier(uint8 classId, uint16 eventId);
+extern const char *GetLWLockIdentifier(uint32 classId, uint16 eventId);
 
 /*
  * Extensions (or core code) can obtain an LWLocks by calling
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index f576f05..7dc8dac 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -291,7 +291,7 @@ extern void CheckDeadLockAlert(void);
 extern bool IsWaitingForLock(void);
 extern void LockErrorCleanup(void);
 
-extern void ProcWaitForSignal(void);
+extern void ProcWaitForSignal(uint32 wait_event_info);
 extern void ProcSendSignal(int pid);
 
 extern void BecomeLockGroupLeader(void);
#53Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#52)
Re: Tracking wait event for latches

On Mon, Oct 3, 2016 at 8:43 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

The rest looks good to me. Thanks for the feedback and the time!

Thanks for the fixes. I committed this with an additional compile
fix, but the buildfarm turned up a few more problems that my 'make
check-world' didn't find. Hopefully those are fixed now, but we'll
see.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#54Thomas Munro
thomas.munro@enterprisedb.com
In reply to: Robert Haas (#53)
1 attachment(s)
Re: Tracking wait event for latches

On Wed, Oct 5, 2016 at 4:59 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Mon, Oct 3, 2016 at 8:43 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

The rest looks good to me. Thanks for the feedback and the time!

Thanks for the fixes. I committed this with an additional compile
fix, but the buildfarm turned up a few more problems that my 'make
check-world' didn't find. Hopefully those are fixed now, but we'll
see.

Nitpicking: the includes in bgworker.c weren't sorted properly, and
then this patch added "pgstat.h" in the wrong position. See attached
suggestion.

--
Thomas Munro
http://www.enterprisedb.com

Attachments:

nitpicking.patchapplication/octet-stream; name=nitpicking.patchDownload
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 028a9ee..3a2929a 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -14,11 +14,11 @@
 
 #include <unistd.h>
 
-#include "miscadmin.h"
 #include "libpq/pqsignal.h"
+#include "miscadmin.h"
+#include "pgstat.h"
 #include "postmaster/bgworker_internals.h"
 #include "postmaster/postmaster.h"
-#include "pgstat.h"
 #include "storage/barrier.h"
 #include "storage/dsm.h"
 #include "storage/ipc.h"
#55Michael Paquier
michael.paquier@gmail.com
In reply to: Thomas Munro (#54)
1 attachment(s)
Re: Tracking wait event for latches

On Wed, Oct 5, 2016 at 4:28 AM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

On Wed, Oct 5, 2016 at 4:59 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Mon, Oct 3, 2016 at 8:43 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

The rest looks good to me. Thanks for the feedback and the time!

Thanks for the fixes. I committed this...

Yeh!

... with an additional compile
fix, but the buildfarm turned up a few more problems that my 'make
check-world' didn't find. Hopefully those are fixed now, but we'll
see.

I saw that after waking up... As usual the buildfarm is catching up
many of the things I missed..

Nitpicking: the includes in bgworker.c weren't sorted properly, and
then this patch added "pgstat.h" in the wrong position. See attached
suggestion.

Yes, that should be fixed.

And for the rest, sorry for the delay. Timezones...

More seriously, the Windows animals have been complaining about
pg_sleep() crashing the system:
SELECT pg_sleep(0.1);
! server closed the connection unexpectedly
! This probably means the server terminated abnormally
! before or while processing the request.
! connection to server was lost

And I think that the answer to this crash is in WaitForSingleObject(),
where the macro WAIT_TIMEOUT is already defined, so there is an
overlap with the new declarations in pgstat.h:
https://msdn.microsoft.com/en-us/library/aa450988.aspx
This is also generating a bunch of build warnings now that I compile
HEAD on Windows. Regression tests are not crashing here, but I am
getting a failure in stats.sql and pg_sleep is broken. I swear I
tested that at some point and did not see a crash or those warnings...
But well what's done is done.

It seems to me that a correct answer would be to rename this class ID.
But instead I'd suggest to append the prefix PG_* to all the class
events like in the attached, that passes make-check, contrib-check,
modules-check and builds without warnings on Windows. A more simple
fix would be just to rename WAIT_TIMEOUT to something else but
appending PG_ looks better in the long term.
--
Michael

Attachments:

we-msvc-fixes.patchapplication/x-download; name=we-msvc-fixes.patchDownload
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 9badfe6..bcdddc2 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -497,7 +497,7 @@ pgfdw_get_result(PGconn *conn, const char *query)
 			wc = WaitLatchOrSocket(MyLatch,
 								   WL_LATCH_SET | WL_SOCKET_READABLE,
 								   PQsocket(conn),
-								   -1L, WAIT_EXTENSION);
+								   -1L, PG_WAIT_EXTENSION);
 			ResetLatch(MyLatch);
 
 			CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 5c6cb6b..a9efee8 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3147,31 +3147,31 @@ pgstat_get_wait_event_type(uint32 wait_event_info)
 
 	switch (classId)
 	{
-		case WAIT_LWLOCK_NAMED:
+		case PG_WAIT_LWLOCK_NAMED:
 			event_type = "LWLockNamed";
 			break;
-		case WAIT_LWLOCK_TRANCHE:
+		case PG_WAIT_LWLOCK_TRANCHE:
 			event_type = "LWLockTranche";
 			break;
-		case WAIT_LOCK:
+		case PG_WAIT_LOCK:
 			event_type = "Lock";
 			break;
-		case WAIT_BUFFER_PIN:
+		case PG_WAIT_BUFFER_PIN:
 			event_type = "BufferPin";
 			break;
-		case WAIT_ACTIVITY:
+		case PG_WAIT_ACTIVITY:
 			event_type = "Activity";
 			break;
-		case WAIT_CLIENT:
+		case PG_WAIT_CLIENT:
 			event_type = "Client";
 			break;
-		case WAIT_EXTENSION:
+		case PG_WAIT_EXTENSION:
 			event_type = "Extension";
 			break;
-		case WAIT_IPC:
+		case PG_WAIT_IPC:
 			event_type = "IPC";
 			break;
-		case WAIT_TIMEOUT:
+		case PG_WAIT_TIMEOUT:
 			event_type = "Timeout";
 			break;
 		default:
@@ -3204,41 +3204,41 @@ pgstat_get_wait_event(uint32 wait_event_info)
 
 	switch (classId)
 	{
-		case WAIT_LWLOCK_NAMED:
-		case WAIT_LWLOCK_TRANCHE:
+		case PG_WAIT_LWLOCK_NAMED:
+		case PG_WAIT_LWLOCK_TRANCHE:
 			event_name = GetLWLockIdentifier(classId, eventId);
 			break;
-		case WAIT_LOCK:
+		case PG_WAIT_LOCK:
 			event_name = GetLockNameFromTagType(eventId);
 			break;
-		case WAIT_BUFFER_PIN:
+		case PG_WAIT_BUFFER_PIN:
 			event_name = "BufferPin";
 			break;
-		case WAIT_ACTIVITY:
+		case PG_WAIT_ACTIVITY:
 			{
 				WaitEventActivity	w = (WaitEventActivity) wait_event_info;
 
 				event_name = pgstat_get_wait_activity(w);
 				break;
 			}
-		case WAIT_CLIENT:
+		case PG_WAIT_CLIENT:
 			{
 				WaitEventClient	w = (WaitEventClient) wait_event_info;
 
 				event_name = pgstat_get_wait_client(w);
 				break;
 			}
-		case WAIT_EXTENSION:
+		case PG_WAIT_EXTENSION:
 			event_name = "Extension";
 			break;
-		case WAIT_IPC:
+		case PG_WAIT_IPC:
 			{
 				WaitEventIPC	w = (WaitEventIPC) wait_event_info;
 
 				event_name = pgstat_get_wait_ipc(w);
 				break;
 			}
-		case WAIT_TIMEOUT:
+		case PG_WAIT_TIMEOUT:
 			{
 				WaitEventTimeout	w = (WaitEventTimeout) wait_event_info;
 
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 91dc24c..2b63cd3 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3646,7 +3646,7 @@ LockBufferForCleanup(Buffer buffer)
 			SetStartupBufferPinWaitBufId(-1);
 		}
 		else
-			ProcWaitForSignal(WAIT_BUFFER_PIN);
+			ProcWaitForSignal(PG_WAIT_BUFFER_PIN);
 
 		/*
 		 * Remove flag marking us as waiter. Normally this will not be set
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index fb887b3..875dcec 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -390,7 +390,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag)
 	}
 
 	/* Wait to be signaled by the release of the Relation Lock */
-	ProcWaitForSignal(WAIT_LOCK | locktag.locktag_type);
+	ProcWaitForSignal(PG_WAIT_LOCK | locktag.locktag_type);
 
 	/*
 	 * Clear any timeout requests established above.  We assume here that the
@@ -470,7 +470,7 @@ ResolveRecoveryConflictWithBufferPin(void)
 	}
 
 	/* Wait to be signaled by UnpinBuffer() */
-	ProcWaitForSignal(WAIT_BUFFER_PIN);
+	ProcWaitForSignal(PG_WAIT_BUFFER_PIN);
 
 	/*
 	 * Clear any timeout requests established above.  We assume here that the
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index a90b54a..9c6862f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -732,9 +732,9 @@ LWLockReportWaitStart(LWLock *lock)
 	int			lockId = T_ID(lock);
 
 	if (lock->tranche == 0)
-		pgstat_report_wait_start(WAIT_LWLOCK_NAMED | (uint16) lockId);
+		pgstat_report_wait_start(PG_WAIT_LWLOCK_NAMED | (uint16) lockId);
 	else
-		pgstat_report_wait_start(WAIT_LWLOCK_TRANCHE | lock->tranche);
+		pgstat_report_wait_start(PG_WAIT_LWLOCK_TRANCHE | lock->tranche);
 }
 
 /*
@@ -752,10 +752,10 @@ LWLockReportWaitEnd(void)
 const char *
 GetLWLockIdentifier(uint32 classId, uint16 eventId)
 {
-	if (classId == WAIT_LWLOCK_NAMED)
+	if (classId == PG_WAIT_LWLOCK_NAMED)
 		return MainLWLockNames[eventId];
 
-	Assert(classId == WAIT_LWLOCK_TRANCHE);
+	Assert(classId == PG_WAIT_LWLOCK_TRANCHE);
 
 	/*
 	 * It is quite possible that user has registered tranche in one of the
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index dd76094..b201631 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -1214,7 +1214,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 		else
 		{
 			WaitLatch(MyLatch, WL_LATCH_SET, 0,
-					  WAIT_LOCK | locallock->tag.lock.locktag_type);
+					  PG_WAIT_LOCK | locallock->tag.lock.locktag_type);
 			ResetLatch(MyLatch);
 			/* check for deadlocks first, as that's probably log-worthy */
 			if (got_deadlock_timeout)
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 27be549..1c9bf13 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -715,15 +715,15 @@ typedef enum BackendState
  * Wait Classes
  * ----------
  */
-#define WAIT_LWLOCK_NAMED			0x01000000U
-#define WAIT_LWLOCK_TRANCHE			0x02000000U
-#define WAIT_LOCK					0x03000000U
-#define WAIT_BUFFER_PIN				0x04000000U
-#define WAIT_ACTIVITY				0x05000000U
-#define WAIT_CLIENT					0x06000000U
-#define WAIT_EXTENSION				0x07000000U
-#define WAIT_IPC					0x08000000U
-#define WAIT_TIMEOUT				0x09000000U
+#define PG_WAIT_LWLOCK_NAMED		0x01000000U
+#define PG_WAIT_LWLOCK_TRANCHE		0x02000000U
+#define PG_WAIT_LOCK				0x03000000U
+#define PG_WAIT_BUFFER_PIN			0x04000000U
+#define PG_WAIT_ACTIVITY			0x05000000U
+#define PG_WAIT_CLIENT				0x06000000U
+#define PG_WAIT_EXTENSION			0x07000000U
+#define PG_WAIT_IPC					0x08000000U
+#define PG_WAIT_TIMEOUT				0x09000000U
 
 /* ----------
  * Wait Events - Activity
@@ -735,7 +735,7 @@ typedef enum BackendState
  */
 typedef enum
 {
-	WAIT_EVENT_ARCHIVER_MAIN = WAIT_ACTIVITY,
+	WAIT_EVENT_ARCHIVER_MAIN = PG_WAIT_ACTIVITY,
 	WAIT_EVENT_AUTOVACUUM_MAIN,
 	WAIT_EVENT_BGWRITER_HIBERNATE,
 	WAIT_EVENT_BGWRITER_MAIN,
@@ -759,7 +759,7 @@ typedef enum
  */
 typedef enum
 {
-	WAIT_EVENT_CLIENT_READ = WAIT_CLIENT,
+	WAIT_EVENT_CLIENT_READ = PG_WAIT_CLIENT,
 	WAIT_EVENT_CLIENT_WRITE,
 	WAIT_EVENT_SSL_OPEN_SERVER,
 	WAIT_EVENT_WAL_RECEIVER_WAIT_START,
@@ -776,7 +776,7 @@ typedef enum
  */
 typedef enum
 {
-	WAIT_EVENT_BGWORKER_SHUTDOWN = WAIT_IPC,
+	WAIT_EVENT_BGWORKER_SHUTDOWN = PG_WAIT_IPC,
 	WAIT_EVENT_BGWORKER_STARTUP,
 	WAIT_EVENT_EXECUTE_GATHER,
 	WAIT_EVENT_MQ_INTERNAL,
@@ -796,7 +796,7 @@ typedef enum
  */
 typedef enum
 {
-	WAIT_EVENT_BASE_BACKUP_THROTTLE = WAIT_TIMEOUT,
+	WAIT_EVENT_BASE_BACKUP_THROTTLE = PG_WAIT_TIMEOUT,
 	WAIT_EVENT_PG_SLEEP,
 	WAIT_EVENT_RECOVERY_APPLY_DELAY
 } WaitEventTimeout;
diff --git a/src/test/modules/test_shm_mq/setup.c b/src/test/modules/test_shm_mq/setup.c
index cb86f3c..5cfffe4 100644
--- a/src/test/modules/test_shm_mq/setup.c
+++ b/src/test/modules/test_shm_mq/setup.c
@@ -280,7 +280,7 @@ wait_for_workers_to_become_ready(worker_state *wstate,
 		}
 
 		/* Wait to be signalled. */
-		WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EXTENSION);
+		WaitLatch(MyLatch, WL_LATCH_SET, 0, PG_WAIT_EXTENSION);
 
 		/* Reset the latch so we don't spin. */
 		ResetLatch(MyLatch);
diff --git a/src/test/modules/test_shm_mq/test.c b/src/test/modules/test_shm_mq/test.c
index bf11137..b0f3d3d 100644
--- a/src/test/modules/test_shm_mq/test.c
+++ b/src/test/modules/test_shm_mq/test.c
@@ -231,7 +231,7 @@ test_shm_mq_pipelined(PG_FUNCTION_ARGS)
 			 * have read or written data and therefore there may now be work
 			 * for us to do.
 			 */
-			WaitLatch(MyLatch, WL_LATCH_SET, 0, WAIT_EXTENSION);
+			WaitLatch(MyLatch, WL_LATCH_SET, 0, PG_WAIT_EXTENSION);
 			ResetLatch(MyLatch);
 			CHECK_FOR_INTERRUPTS();
 		}
diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c
index f8395bf..5f49549 100644
--- a/src/test/modules/worker_spi/worker_spi.c
+++ b/src/test/modules/worker_spi/worker_spi.c
@@ -228,7 +228,7 @@ worker_spi_main(Datum main_arg)
 		rc = WaitLatch(MyLatch,
 					   WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
 					   worker_spi_naptime * 1000L,
-					   WAIT_EXTENSION);
+					   PG_WAIT_EXTENSION);
 		ResetLatch(MyLatch);
 
 		/* emergency bailout if postmaster has died */
#56Robert Haas
robertmhaas@gmail.com
In reply to: Thomas Munro (#54)
Re: Tracking wait event for latches

On Tue, Oct 4, 2016 at 3:28 PM, Thomas Munro
<thomas.munro@enterprisedb.com> wrote:

Nitpicking: the includes in bgworker.c weren't sorted properly, and
then this patch added "pgstat.h" in the wrong position. See attached
suggestion.

Committed.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#57Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#55)
Re: Tracking wait event for latches

On Tue, Oct 4, 2016 at 5:07 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

More seriously, the Windows animals have been complaining about
pg_sleep() crashing the system:
SELECT pg_sleep(0.1);
! server closed the connection unexpectedly
! This probably means the server terminated abnormally
! before or while processing the request.
! connection to server was lost

And I think that the answer to this crash is in WaitForSingleObject(),
where the macro WAIT_TIMEOUT is already defined, so there is an
overlap with the new declarations in pgstat.h:
https://msdn.microsoft.com/en-us/library/aa450988.aspx
This is also generating a bunch of build warnings now that I compile
HEAD on Windows. Regression tests are not crashing here, but I am
getting a failure in stats.sql and pg_sleep is broken. I swear I
tested that at some point and did not see a crash or those warnings...
But well what's done is done.

It seems to me that a correct answer would be to rename this class ID.
But instead I'd suggest to append the prefix PG_* to all the class
events like in the attached, that passes make-check, contrib-check,
modules-check and builds without warnings on Windows. A more simple
fix would be just to rename WAIT_TIMEOUT to something else but
appending PG_ looks better in the long term.

Committed.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#58Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#57)
Re: Tracking wait event for latches

On Wed, Oct 5, 2016 at 9:25 PM, Robert Haas <robertmhaas@gmail.com> wrote:

On Tue, Oct 4, 2016 at 5:07 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

More seriously, the Windows animals have been complaining about
pg_sleep() crashing the system:
SELECT pg_sleep(0.1);
! server closed the connection unexpectedly
! This probably means the server terminated abnormally
! before or while processing the request.
! connection to server was lost

And I think that the answer to this crash is in WaitForSingleObject(),
where the macro WAIT_TIMEOUT is already defined, so there is an
overlap with the new declarations in pgstat.h:
https://msdn.microsoft.com/en-us/library/aa450988.aspx
This is also generating a bunch of build warnings now that I compile
HEAD on Windows. Regression tests are not crashing here, but I am
getting a failure in stats.sql and pg_sleep is broken. I swear I
tested that at some point and did not see a crash or those warnings...
But well what's done is done.

It seems to me that a correct answer would be to rename this class ID.
But instead I'd suggest to append the prefix PG_* to all the class
events like in the attached, that passes make-check, contrib-check,
modules-check and builds without warnings on Windows. A more simple
fix would be just to rename WAIT_TIMEOUT to something else but
appending PG_ looks better in the long term.

Committed.

Thanks.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers