From 1e47895422410a9de9200c7dc17f767d85c29752 Mon Sep 17 00:00:00 2001
From: Kuntal Ghosh <kuntal.ghosh@enterprisedb.com>
Date: Wed, 15 Feb 2017 15:34:18 +0530
Subject: [PATCH] Infra to expose non-backend processes in pg_stat_get_activity

This patch implements the infrastructure required to expose
non-backend processes in pg_stat_activity. BackendStatusArray
is extended to store auxiliary processes as well. Backends use slots
indexed in the range from 1 to MaxBackends (inclusive), so we use
MaxBackends + AuxProcType + 1 as the index of the slot for an
auxiliary process.
---
 src/backend/postmaster/pgstat.c     | 304 ++++++++++++++++++++++++++++++------
 src/backend/storage/lmgr/proc.c     |  27 ++++
 src/backend/utils/adt/pgstatfuncs.c | 120 ++++++++------
 src/backend/utils/init/postinit.c   |   4 +-
 src/include/pgstat.h                |  35 ++++-
 src/include/storage/proc.h          |   1 +
 6 files changed, 392 insertions(+), 99 deletions(-)

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 7176cf1..069e390 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -50,6 +50,7 @@
 #include "postmaster/autovacuum.h"
 #include "postmaster/fork_process.h"
 #include "postmaster/postmaster.h"
+#include "replication/walsender.h"
 #include "storage/backendid.h"
 #include "storage/dsm.h"
 #include "storage/fd.h"
@@ -212,7 +213,17 @@ typedef struct TwoPhasePgStatRecord
  */
 static MemoryContext pgStatLocalContext = NULL;
 static HTAB *pgStatDBHash = NULL;
-static LocalPgBackendStatus *localBackendStatusTable = NULL;
+
+/* Status for backends and auxiliary processes */
+static LocalPgBackendStatus *localProcStatusTable = NULL;
+
+/* Total number of processes including backends and auxiliary */
+static int	localNumProcs = 0;
+
+/* Index to backends in localProcStatusTable */
+static int *localBackendStatusIndex = NULL;
+
+/* Total number of backend processes */
 static int	localNumBackends = 0;
 
 /*
@@ -2404,7 +2415,29 @@ pgstat_fetch_stat_beentry(int beid)
 	if (beid < 1 || beid > localNumBackends)
 		return NULL;
 
-	return &localBackendStatusTable[beid - 1].backendStatus;
+	return &localProcStatusTable[localBackendStatusIndex[beid - 1]].backendStatus;
+}
+
+
+/* ----------
+ * pgstat_fetch_stat_procentry() -
+ *
+ *	Support function for the SQL-callable pgstat* functions. Returns
+ *	our local copy of the current-activity entry for one process.
+ *
+ *	NB: caller is responsible for a check if the user is permitted to see
+ *	this info (especially the querystring).
+ * ----------
+ */
+PgBackendStatus *
+pgstat_fetch_stat_procentry(int procid)
+{
+	pgstat_read_current_status();
+
+	if (procid < 1 || procid > localNumProcs)
+		return NULL;
+
+	return &localProcStatusTable[procid - 1].backendStatus;
 }
 
 
@@ -2426,7 +2459,29 @@ pgstat_fetch_stat_local_beentry(int beid)
 	if (beid < 1 || beid > localNumBackends)
 		return NULL;
 
-	return &localBackendStatusTable[beid - 1];
+	return &localProcStatusTable[localBackendStatusIndex[beid - 1]];
+}
+
+
+/* ----------
+ * pgstat_fetch_stat_local_procentry() -
+ *
+ *	Like pgstat_fetch_stat_procentry() but with locally computed additions (like
+ *	xid and xmin values of the backend)
+ *
+ *	NB: caller is responsible for a check if the user is permitted to see
+ *	this info (especially the querystring).
+ * ----------
+ */
+LocalPgBackendStatus *
+pgstat_fetch_stat_local_procentry(int procid)
+{
+	pgstat_read_current_status();
+
+	if (procid < 1 || procid > localNumProcs)
+		return NULL;
+
+	return &localProcStatusTable[procid - 1];
 }
 
 
@@ -2445,6 +2500,22 @@ pgstat_fetch_stat_numbackends(void)
 	return localNumBackends;
 }
 
+
+/* ----------
+ * pgstat_fetch_stat_numprocs() -
+ *
+ *	Support function for the SQL-callable pgstat* functions. Returns
+ *	the maximum current process id.
+ * ----------
+ */
+int
+pgstat_fetch_stat_numprocs(void)
+{
+	pgstat_read_current_status();
+
+	return localNumProcs;
+}
+
 /*
  * ---------
  * pgstat_fetch_stat_archiver() -
@@ -2504,20 +2575,20 @@ BackendStatusShmemSize(void)
 	Size		size;
 
 	/* BackendStatusArray: */
-	size = mul_size(sizeof(PgBackendStatus), MaxBackends);
+	size = mul_size(sizeof(PgBackendStatus), NumProcStatSlots);
 	/* BackendAppnameBuffer: */
 	size = add_size(size,
-					mul_size(NAMEDATALEN, MaxBackends));
+					mul_size(NAMEDATALEN, NumProcStatSlots));
 	/* BackendClientHostnameBuffer: */
 	size = add_size(size,
-					mul_size(NAMEDATALEN, MaxBackends));
+					mul_size(NAMEDATALEN, NumProcStatSlots));
 	/* BackendActivityBuffer: */
 	size = add_size(size,
-					mul_size(pgstat_track_activity_query_size, MaxBackends));
+				mul_size(pgstat_track_activity_query_size, NumProcStatSlots));
 #ifdef USE_SSL
 	/* BackendSslStatusBuffer: */
 	size = add_size(size,
-					mul_size(sizeof(PgBackendSSLStatus), MaxBackends));
+					mul_size(sizeof(PgBackendSSLStatus), NumProcStatSlots));
 #endif
 	return size;
 }
@@ -2535,7 +2606,7 @@ CreateSharedBackendStatus(void)
 	char	   *buffer;
 
 	/* Create or attach to the shared array */
-	size = mul_size(sizeof(PgBackendStatus), MaxBackends);
+	size = mul_size(sizeof(PgBackendStatus), NumProcStatSlots);
 	BackendStatusArray = (PgBackendStatus *)
 		ShmemInitStruct("Backend Status Array", size, &found);
 
@@ -2558,7 +2629,7 @@ CreateSharedBackendStatus(void)
 
 		/* Initialize st_appname pointers. */
 		buffer = BackendAppnameBuffer;
-		for (i = 0; i < MaxBackends; i++)
+		for (i = 0; i < NumProcStatSlots; i++)
 		{
 			BackendStatusArray[i].st_appname = buffer;
 			buffer += NAMEDATALEN;
@@ -2576,7 +2647,7 @@ CreateSharedBackendStatus(void)
 
 		/* Initialize st_clienthostname pointers. */
 		buffer = BackendClientHostnameBuffer;
-		for (i = 0; i < MaxBackends; i++)
+		for (i = 0; i < NumProcStatSlots; i++)
 		{
 			BackendStatusArray[i].st_clienthostname = buffer;
 			buffer += NAMEDATALEN;
@@ -2585,7 +2656,7 @@ CreateSharedBackendStatus(void)
 
 	/* Create or attach to the shared activity buffer */
 	BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
-										 MaxBackends);
+										 NumProcStatSlots);
 	BackendActivityBuffer = (char *)
 		ShmemInitStruct("Backend Activity Buffer",
 						BackendActivityBufferSize,
@@ -2597,7 +2668,7 @@ CreateSharedBackendStatus(void)
 
 		/* Initialize st_activity pointers. */
 		buffer = BackendActivityBuffer;
-		for (i = 0; i < MaxBackends; i++)
+		for (i = 0; i < NumProcStatSlots; i++)
 		{
 			BackendStatusArray[i].st_activity = buffer;
 			buffer += pgstat_track_activity_query_size;
@@ -2606,7 +2677,7 @@ CreateSharedBackendStatus(void)
 
 #ifdef USE_SSL
 	/* Create or attach to the shared SSL status buffer */
-	size = mul_size(sizeof(PgBackendSSLStatus), MaxBackends);
+	size = mul_size(sizeof(PgBackendSSLStatus), NumProcStatSlots);
 	BackendSslStatusBuffer = (PgBackendSSLStatus *)
 		ShmemInitStruct("Backend SSL Status Buffer", size, &found);
 
@@ -2618,7 +2689,7 @@ CreateSharedBackendStatus(void)
 
 		/* Initialize st_sslstatus pointers. */
 		ptr = BackendSslStatusBuffer;
-		for (i = 0; i < MaxBackends; i++)
+		for (i = 0; i < NumProcStatSlots; i++)
 		{
 			BackendStatusArray[i].st_sslstatus = ptr;
 			ptr++;
@@ -2632,7 +2703,8 @@ CreateSharedBackendStatus(void)
  * pgstat_initialize() -
  *
  *	Initialize pgstats state, and set up our on-proc-exit hook.
- *	Called from InitPostgres.  MyBackendId must be set,
+ *	Called from InitPostgres and AuxiliaryProcessMain. For auxiliary process,
+ *	MyBackendId is invalid. Otherwise, MyBackendId must be set,
  *	but we must not have started any transaction yet (since the
  *	exit hook must run after the last transaction exit).
  *	NOTE: MyDatabaseId isn't set yet; so the shutdown hook has to be careful.
@@ -2642,27 +2714,47 @@ void
 pgstat_initialize(void)
 {
 	/* Initialize MyBEEntry */
-	Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends);
-	MyBEEntry = &BackendStatusArray[MyBackendId - 1];
+	if (MyBackendId != InvalidBackendId)
+	{
+		/* Just a backend process */
+		Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends);
+		MyBEEntry = &BackendStatusArray[MyBackendId - 1];
+	}
+	else
+	{
+		/* Must be an auxiliary process */
+		Assert(MyAuxProcType != NotAnAuxProcess);
+
+		/*
+		 * Assign the MyBEEntry for an auxiliary process.  Since it doesn't
+		 * have a BackendId, the slot is statically allocated based on the
+		 * auxiliary process type (MyAuxProcType).  Backends use slots indexed
+		 * in the range from 1 to MaxBackends (inclusive), so we use
+		 * MaxBackends + AuxProcType + 1 as the index of the slot for an
+		 * auxiliary process.
+		 */
+		MyBEEntry = &BackendStatusArray[MaxBackends + MyAuxProcType];
+	}
 
 	/* Set up a process-exit hook to clean up */
 	on_shmem_exit(pgstat_beshutdown_hook, 0);
 }
 
 /* ----------
- * pgstat_bestart() -
+ * pgstat_procstart() -
+ *
+ *	Initialize this process's entry in the PgBackendStatus array.
+ *	Called from InitPostgres and AuxiliaryProcessMain.
  *
- *	Initialize this backend's entry in the PgBackendStatus array.
- *	Called from InitPostgres.
- *	MyDatabaseId, session userid, and application_name must be set
- *	(hence, this cannot be combined with pgstat_initialize).
+ *	For a backend process, MyDatabaseId, session userid,
+ *	and application_name must be set (hence, this cannot be combined
+ *	with pgstat_initialize).
  * ----------
  */
 void
-pgstat_bestart(void)
+pgstat_procstart(void)
 {
 	TimestampTz proc_start_timestamp;
-	Oid			userid;
 	SockAddr	clientaddr;
 	volatile PgBackendStatus *beentry;
 
@@ -2677,7 +2769,6 @@ pgstat_bestart(void)
 		proc_start_timestamp = MyProcPort->SessionStartTime;
 	else
 		proc_start_timestamp = GetCurrentTimestamp();
-	userid = GetSessionUserId();
 
 	/*
 	 * We may not have a MyProcPort (eg, if this is the autovacuum process).
@@ -2696,6 +2787,55 @@ pgstat_bestart(void)
 	 * cute.
 	 */
 	beentry = MyBEEntry;
+
+	if (MyBackendId != InvalidBackendId)
+	{
+		/* Must be a backend process */
+		if (IsAutoVacuumLauncherProcess())
+		{
+			/* Autovacuum Launcher */
+			beentry->st_procType = PROC_AUTOVAC_LAUNCHER;
+		}
+		else if (am_walsender)
+		{
+			/* Wal sender */
+			beentry->st_procType = PROC_WAL_SENDER;
+		}
+		else if (IsBackgroundWorker)
+		{
+			/* bgworker */
+			beentry->st_procType = PROC_BG_WORKER;
+		}
+		else
+		{
+			/* client-backend */
+			beentry->st_procType = PROC_BACKEND;
+		}
+	}
+	else
+	{
+		/* Must be an auxiliary process */
+		Assert(MyAuxProcType != NotAnAuxProcess);
+		switch (MyAuxProcType)
+		{
+			case BgWriterProcess:
+				beentry->st_procType = PROC_BG_WRITER;
+				break;
+			case CheckpointerProcess:
+				beentry->st_procType = PROC_CHECKPOINTER;
+				break;
+			case WalWriterProcess:
+				beentry->st_procType = PROC_WAL_WRITER;
+				break;
+			case WalReceiverProcess:
+				beentry->st_procType = PROC_WAL_RECEIVER;
+				break;
+			default:
+				elog(PANIC, "unrecognized process type: %d", (int) MyAuxProcType);
+				proc_exit(1);
+		}
+	}
+
 	do
 	{
 		pgstat_increment_changecount_before(beentry);
@@ -2707,8 +2847,15 @@ pgstat_bestart(void)
 	beentry->st_state_start_timestamp = 0;
 	beentry->st_xact_start_timestamp = 0;
 	beentry->st_databaseid = MyDatabaseId;
-	beentry->st_userid = userid;
+
+	/* We have userid for client-backends and wal-sender processes */
+	if (beentry->st_procType == PROC_BACKEND || beentry->st_procType == PROC_WAL_SENDER)
+		beentry->st_userid = GetSessionUserId();
+	else
+		beentry->st_userid = InvalidOid;
+
 	beentry->st_clientaddr = clientaddr;
+
 	if (MyProcPort && MyProcPort->remote_hostname)
 		strlcpy(beentry->st_clienthostname, MyProcPort->remote_hostname,
 				NAMEDATALEN);
@@ -3027,7 +3174,7 @@ pgstat_report_xact_timestamp(TimestampTz tstamp)
 static void
 pgstat_read_current_status(void)
 {
-	volatile PgBackendStatus *beentry;
+	volatile PgBackendStatus *procentry;
 	LocalPgBackendStatus *localtable;
 	LocalPgBackendStatus *localentry;
 	char	   *localappname,
@@ -3038,31 +3185,31 @@ pgstat_read_current_status(void)
 	int			i;
 
 	Assert(!pgStatRunningInCollector);
-	if (localBackendStatusTable)
+	if (localProcStatusTable && localBackendStatusIndex)
 		return;					/* already done */
 
 	pgstat_setup_memcxt();
 
 	localtable = (LocalPgBackendStatus *)
 		MemoryContextAlloc(pgStatLocalContext,
-						   sizeof(LocalPgBackendStatus) * MaxBackends);
+						   sizeof(LocalPgBackendStatus) * NumProcStatSlots);
 	localappname = (char *)
 		MemoryContextAlloc(pgStatLocalContext,
-						   NAMEDATALEN * MaxBackends);
+						   NAMEDATALEN * NumProcStatSlots);
 	localactivity = (char *)
 		MemoryContextAlloc(pgStatLocalContext,
-						   pgstat_track_activity_query_size * MaxBackends);
+					   pgstat_track_activity_query_size * NumProcStatSlots);
 #ifdef USE_SSL
 	localsslstatus = (PgBackendSSLStatus *)
 		MemoryContextAlloc(pgStatLocalContext,
-						   sizeof(PgBackendSSLStatus) * MaxBackends);
+						   sizeof(PgBackendSSLStatus) * NumProcStatSlots);
 #endif
 
-	localNumBackends = 0;
+	localNumProcs = 0;
 
-	beentry = BackendStatusArray;
+	procentry = BackendStatusArray;
 	localentry = localtable;
-	for (i = 1; i <= MaxBackends; i++)
+	for (i = 1; i <= NumProcStatSlots; i++)
 	{
 		/*
 		 * Follow the protocol of retrying if st_changecount changes while we
@@ -3076,32 +3223,32 @@ pgstat_read_current_status(void)
 			int			before_changecount;
 			int			after_changecount;
 
-			pgstat_save_changecount_before(beentry, before_changecount);
+			pgstat_save_changecount_before(procentry, before_changecount);
 
-			localentry->backendStatus.st_procpid = beentry->st_procpid;
+			localentry->backendStatus.st_procpid = procentry->st_procpid;
 			if (localentry->backendStatus.st_procpid > 0)
 			{
-				memcpy(&localentry->backendStatus, (char *) beentry, sizeof(PgBackendStatus));
+				memcpy(&localentry->backendStatus, (char *) procentry, sizeof(PgBackendStatus));
 
 				/*
 				 * strcpy is safe even if the string is modified concurrently,
 				 * because there's always a \0 at the end of the buffer.
 				 */
-				strcpy(localappname, (char *) beentry->st_appname);
+				strcpy(localappname, (char *) procentry->st_appname);
 				localentry->backendStatus.st_appname = localappname;
-				strcpy(localactivity, (char *) beentry->st_activity);
+				strcpy(localactivity, (char *) procentry->st_activity);
 				localentry->backendStatus.st_activity = localactivity;
-				localentry->backendStatus.st_ssl = beentry->st_ssl;
+				localentry->backendStatus.st_ssl = procentry->st_ssl;
 #ifdef USE_SSL
-				if (beentry->st_ssl)
+				if (procentry->st_ssl)
 				{
-					memcpy(localsslstatus, beentry->st_sslstatus, sizeof(PgBackendSSLStatus));
+					memcpy(localsslstatus, procentry->st_sslstatus, sizeof(PgBackendSSLStatus));
 					localentry->backendStatus.st_sslstatus = localsslstatus;
 				}
 #endif
 			}
 
-			pgstat_save_changecount_after(beentry, after_changecount);
+			pgstat_save_changecount_after(procentry, after_changecount);
 			if (before_changecount == after_changecount &&
 				(before_changecount & 1) == 0)
 				break;
@@ -3110,7 +3257,7 @@ pgstat_read_current_status(void)
 			CHECK_FOR_INTERRUPTS();
 		}
 
-		beentry++;
+		procentry++;
 		/* Only valid entries get included into the local array */
 		if (localentry->backendStatus.st_procpid > 0)
 		{
@@ -3124,12 +3271,35 @@ pgstat_read_current_status(void)
 #ifdef USE_SSL
 			localsslstatus++;
 #endif
-			localNumBackends++;
+			localNumProcs++;
 		}
 	}
 
 	/* Set the pointer only after completion of a valid table */
-	localBackendStatusTable = localtable;
+	localProcStatusTable = localtable;
+
+	/*
+	 * In localBackendStatusIndex, we store the backend indices from
+	 * localProcStatusTable. We need this information since there are
+	 * user-defined functions which expects ids of backends starting from 1 to
+	 * the number of active backends. Hence, we don't want to surprise the
+	 * end-user by including auxiliary processes in the result.
+	 *
+	 * See pg_stat_get_backend_idset for further info.
+	 */
+	localBackendStatusIndex = (int *)
+		MemoryContextAlloc(pgStatLocalContext,
+						   sizeof(int) * NumProcStatSlots);
+
+	localNumBackends = 0;
+	for (i = 0; i < localNumProcs; i++)
+	{
+		if (localProcStatusTable[i].backendStatus.st_procType == PROC_BACKEND)
+		{
+			localBackendStatusIndex[localNumBackends] = i;
+			localNumBackends++;
+		}
+	}
 }
 
 /* ----------
@@ -3584,7 +3754,41 @@ pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen)
 	return NULL;
 }
 
+const char *
+pgstat_get_proctype_desc(ProcType procType)
+{
+	const char *procDesc = "unknown process type";
+
+	switch (procType)
+	{
+		case PROC_BACKEND:
+			procDesc = "client backend";
+			break;
+		case PROC_AUTOVAC_LAUNCHER:
+			procDesc = "autovacuum launcher";
+			break;
+		case PROC_WAL_SENDER:
+			procDesc = "wal sender";
+			break;
+		case PROC_BG_WORKER:
+			procDesc = "bgworker";
+			break;
+		case PROC_BG_WRITER:
+			procDesc = "writer";
+			break;
+		case PROC_CHECKPOINTER:
+			procDesc = "checkpointer";
+			break;
+		case PROC_WAL_WRITER:
+			procDesc = "wal writer";
+			break;
+		case PROC_WAL_RECEIVER:
+			procDesc = "wal receiver";
+			break;
+	}
 
+	return procDesc;
+}
 /* ------------------------------------------------------------
  * Local support functions follow
  * ------------------------------------------------------------
@@ -5048,7 +5252,9 @@ pgstat_clear_snapshot(void)
 	/* Reset variables */
 	pgStatLocalContext = NULL;
 	pgStatDBHash = NULL;
-	localBackendStatusTable = NULL;
+	localProcStatusTable = NULL;
+	localNumProcs = 0;
+	localBackendStatusIndex = NULL;
 	localNumBackends = 0;
 }
 
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 8f467be..021c9b0 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -941,6 +941,33 @@ AuxiliaryProcKill(int code, Datum arg)
 	SpinLockRelease(ProcStructLock);
 }
 
+/*
+ * AuxiliaryPidGetProc -- get PGPROC for an auxiliary process
+ * given its PID
+ *
+ * Returns NULL if not found.
+ */
+PGPROC *
+AuxiliaryPidGetProc(int pid)
+{
+	PGPROC	   *result;
+	int			index;
+
+	if (pid == 0)				/* never match dummy PGPROCs */
+		return NULL;
+
+	for (index = 0; index < NUM_AUXILIARY_PROCS; index++)
+	{
+		PGPROC	   *proc = &AuxiliaryProcs[index];
+
+		if (proc->pid == pid)
+		{
+			result = proc;
+			break;
+		}
+	}
+	return result;
+}
 
 /*
  * ProcQueue package: routines for putting processes to sleep
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index a987d0d..e858e4d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -20,6 +20,7 @@
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "postmaster/postmaster.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
 #include "utils/acl.h"
@@ -533,14 +534,14 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 }
 
 /*
- * Returns activity of PG backends.
+ * Returns activity of PG processes.
  */
 Datum
 pg_stat_get_activity(PG_FUNCTION_ARGS)
 {
 #define PG_STAT_GET_ACTIVITY_COLS	23
-	int			num_backends = pgstat_fetch_stat_numbackends();
-	int			curr_backend;
+	int			num_procs = pgstat_fetch_stat_numprocs();
+	int			curr_proc;
 	int			pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
 	TupleDesc	tupdesc;
@@ -574,13 +575,13 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 	MemoryContextSwitchTo(oldcontext);
 
 	/* 1-based index */
-	for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
+	for (curr_proc = 1; curr_proc <= num_procs; curr_proc++)
 	{
 		/* for each row */
 		Datum		values[PG_STAT_GET_ACTIVITY_COLS];
 		bool		nulls[PG_STAT_GET_ACTIVITY_COLS];
-		LocalPgBackendStatus *local_beentry;
-		PgBackendStatus *beentry;
+		LocalPgBackendStatus *local_procentry;
+		PgBackendStatus *procentry;
 		PGPROC	   *proc;
 		const char *wait_event_type;
 		const char *wait_event;
@@ -589,8 +590,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		MemSet(nulls, 0, sizeof(nulls));
 
 		/* Get the next one in the list */
-		local_beentry = pgstat_fetch_stat_local_beentry(curr_backend);
-		if (!local_beentry)
+		local_procentry = pgstat_fetch_stat_local_procentry(curr_proc);
+		if (!local_procentry)
 		{
 			int			i;
 
@@ -608,39 +609,48 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			continue;
 		}
 
-		beentry = &local_beentry->backendStatus;
+		procentry = &local_procentry->backendStatus;
 
 		/* If looking for specific PID, ignore all the others */
-		if (pid != -1 && beentry->st_procpid != pid)
+		if (pid != -1 && procentry->st_procpid != pid)
 			continue;
 
 		/* Values available to all callers */
-		values[0] = ObjectIdGetDatum(beentry->st_databaseid);
-		values[1] = Int32GetDatum(beentry->st_procpid);
-		values[2] = ObjectIdGetDatum(beentry->st_userid);
-		if (beentry->st_appname)
-			values[3] = CStringGetTextDatum(beentry->st_appname);
+		if (procentry->st_databaseid != InvalidOid)
+			values[0] = ObjectIdGetDatum(procentry->st_databaseid);
+		else
+			nulls[0] = true;
+
+		values[1] = Int32GetDatum(procentry->st_procpid);
+
+		if (procentry->st_userid != InvalidOid)
+			values[2] = ObjectIdGetDatum(procentry->st_userid);
+		else
+			nulls[2] = true;
+
+		if (procentry->st_appname)
+			values[3] = CStringGetTextDatum(procentry->st_appname);
 		else
 			nulls[3] = true;
 
-		if (TransactionIdIsValid(local_beentry->backend_xid))
-			values[15] = TransactionIdGetDatum(local_beentry->backend_xid);
+		if (TransactionIdIsValid(local_procentry->backend_xid))
+			values[15] = TransactionIdGetDatum(local_procentry->backend_xid);
 		else
 			nulls[15] = true;
 
-		if (TransactionIdIsValid(local_beentry->backend_xmin))
-			values[16] = TransactionIdGetDatum(local_beentry->backend_xmin);
+		if (TransactionIdIsValid(local_procentry->backend_xmin))
+			values[16] = TransactionIdGetDatum(local_procentry->backend_xmin);
 		else
 			nulls[16] = true;
 
-		if (beentry->st_ssl)
+		if (procentry->st_ssl)
 		{
 			values[17] = BoolGetDatum(true);	/* ssl */
-			values[18] = CStringGetTextDatum(beentry->st_sslstatus->ssl_version);
-			values[19] = CStringGetTextDatum(beentry->st_sslstatus->ssl_cipher);
-			values[20] = Int32GetDatum(beentry->st_sslstatus->ssl_bits);
-			values[21] = BoolGetDatum(beentry->st_sslstatus->ssl_compression);
-			values[22] = CStringGetTextDatum(beentry->st_sslstatus->ssl_clientdn);
+			values[18] = CStringGetTextDatum(procentry->st_sslstatus->ssl_version);
+			values[19] = CStringGetTextDatum(procentry->st_sslstatus->ssl_cipher);
+			values[20] = Int32GetDatum(procentry->st_sslstatus->ssl_bits);
+			values[21] = BoolGetDatum(procentry->st_sslstatus->ssl_compression);
+			values[22] = CStringGetTextDatum(procentry->st_sslstatus->ssl_clientdn);
 		}
 		else
 		{
@@ -649,11 +659,11 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 		}
 
 		/* Values only available to role member */
-		if (has_privs_of_role(GetUserId(), beentry->st_userid))
+		if (has_privs_of_role(GetUserId(), procentry->st_userid))
 		{
 			SockAddr	zero_clientaddr;
 
-			switch (beentry->st_state)
+			switch (procentry->st_state)
 			{
 				case STATE_IDLE:
 					values[4] = CStringGetTextDatum("idle");
@@ -678,9 +688,9 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 					break;
 			}
 
-			values[5] = CStringGetTextDatum(beentry->st_activity);
+			values[5] = CStringGetTextDatum(procentry->st_activity);
 
-			proc = BackendPidGetProc(beentry->st_procpid);
+			proc = BackendPidGetProc(procentry->st_procpid);
 			if (proc != NULL)
 			{
 				uint32		raw_wait_event;
@@ -690,6 +700,22 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				wait_event = pgstat_get_wait_event(raw_wait_event);
 
 			}
+			else if (procentry->st_procType != PROC_BACKEND)
+			{
+				uint32		raw_wait_event;
+
+				/*
+				 * For auxiliary process, retrieve proc info from
+				 * AuxiliaryProcs stored in shared-mem.
+				 */
+				proc = AuxiliaryPidGetProc(procentry->st_procpid);
+
+				/* Check whether this is indeed an auxiliary process */
+				Assert(proc != NULL);
+				raw_wait_event = UINT32_ACCESS_ONCE(proc->wait_event_info);
+				wait_event_type = pgstat_get_wait_event_type(raw_wait_event);
+				wait_event = pgstat_get_wait_event(raw_wait_event);
+			}
 			else
 			{
 				wait_event_type = NULL;
@@ -706,29 +732,29 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			else
 				nulls[7] = true;
 
-			if (beentry->st_xact_start_timestamp != 0)
-				values[8] = TimestampTzGetDatum(beentry->st_xact_start_timestamp);
+			if (procentry->st_xact_start_timestamp != 0)
+				values[8] = TimestampTzGetDatum(procentry->st_xact_start_timestamp);
 			else
 				nulls[8] = true;
 
-			if (beentry->st_activity_start_timestamp != 0)
-				values[9] = TimestampTzGetDatum(beentry->st_activity_start_timestamp);
+			if (procentry->st_activity_start_timestamp != 0)
+				values[9] = TimestampTzGetDatum(procentry->st_activity_start_timestamp);
 			else
 				nulls[9] = true;
 
-			if (beentry->st_proc_start_timestamp != 0)
-				values[10] = TimestampTzGetDatum(beentry->st_proc_start_timestamp);
+			if (procentry->st_proc_start_timestamp != 0)
+				values[10] = TimestampTzGetDatum(procentry->st_proc_start_timestamp);
 			else
 				nulls[10] = true;
 
-			if (beentry->st_state_start_timestamp != 0)
-				values[11] = TimestampTzGetDatum(beentry->st_state_start_timestamp);
+			if (procentry->st_state_start_timestamp != 0)
+				values[11] = TimestampTzGetDatum(procentry->st_state_start_timestamp);
 			else
 				nulls[11] = true;
 
 			/* A zeroed client addr means we don't know */
 			memset(&zero_clientaddr, 0, sizeof(zero_clientaddr));
-			if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr,
+			if (memcmp(&(procentry->st_clientaddr), &zero_clientaddr,
 					   sizeof(zero_clientaddr)) == 0)
 			{
 				nulls[12] = true;
@@ -737,9 +763,9 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			}
 			else
 			{
-				if (beentry->st_clientaddr.addr.ss_family == AF_INET
+				if (procentry->st_clientaddr.addr.ss_family == AF_INET
 #ifdef HAVE_IPV6
-					|| beentry->st_clientaddr.addr.ss_family == AF_INET6
+					|| procentry->st_clientaddr.addr.ss_family == AF_INET6
 #endif
 					)
 				{
@@ -749,19 +775,19 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 
 					remote_host[0] = '\0';
 					remote_port[0] = '\0';
-					ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr,
-											 beentry->st_clientaddr.salen,
+					ret = pg_getnameinfo_all(&procentry->st_clientaddr.addr,
+											 procentry->st_clientaddr.salen,
 											 remote_host, sizeof(remote_host),
 											 remote_port, sizeof(remote_port),
 											 NI_NUMERICHOST | NI_NUMERICSERV);
 					if (ret == 0)
 					{
-						clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host);
+						clean_ipv6_addr(procentry->st_clientaddr.addr.ss_family, remote_host);
 						values[12] = DirectFunctionCall1(inet_in,
 											   CStringGetDatum(remote_host));
-						if (beentry->st_clienthostname &&
-							beentry->st_clienthostname[0])
-							values[13] = CStringGetTextDatum(beentry->st_clienthostname);
+						if (procentry->st_clienthostname &&
+							procentry->st_clienthostname[0])
+							values[13] = CStringGetTextDatum(procentry->st_clienthostname);
 						else
 							nulls[13] = true;
 						values[14] = Int32GetDatum(atoi(remote_port));
@@ -773,7 +799,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 						nulls[14] = true;
 					}
 				}
-				else if (beentry->st_clientaddr.addr.ss_family == AF_UNIX)
+				else if (procentry->st_clientaddr.addr.ss_family == AF_UNIX)
 				{
 					/*
 					 * Unix sockets always reports NULL for host and -1 for
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 9f938f2..f8c36c0 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -809,7 +809,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		InitializeClientEncoding();
 
 		/* report this backend in the PgBackendStatus array */
-		pgstat_bestart();
+		pgstat_procstart();
 
 		/* close the transaction we started above */
 		CommitTransactionCommand();
@@ -1021,7 +1021,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 
 	/* report this backend in the PgBackendStatus array */
 	if (!bootstrap)
-		pgstat_bestart();
+		pgstat_procstart();
 
 	/* close the transaction we started above */
 	if (!bootstrap)
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index de8225b..80a1f36 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -41,6 +41,9 @@ typedef enum TrackFunctionsLevel
 	TRACK_FUNC_ALL
 }	TrackFunctionsLevel;
 
+/* Total number of PG processes */
+#define NumProcStatSlots	(MaxBackends + NUM_AUXPROCTYPES)
+
 /* ----------
  * The types of backend -> collector messages
  * ----------
@@ -696,6 +699,23 @@ typedef struct PgStat_GlobalStats
 
 
 /* ----------
+ * Process types
+ * ----------
+ */
+typedef enum ProcType
+{
+	PROC_BACKEND,
+	PROC_AUTOVAC_LAUNCHER,
+	PROC_WAL_SENDER,
+	PROC_BG_WORKER,
+	PROC_BG_WRITER,
+	PROC_CHECKPOINTER,
+	PROC_WAL_WRITER,
+	PROC_WAL_RECEIVER
+} ProcType;
+
+
+/* ----------
  * Backend states
  * ----------
  */
@@ -845,6 +865,12 @@ typedef struct PgBackendSSLStatus
  * showing its current activity.  (The structs are allocated according to
  * BackendId, but that is not critical.)  Note that the collector process
  * has no involvement in, or even access to, these structs.
+ *
+ * Each auxliliary process also maintains a PgBackendStatus struct in shared
+ * memory.
+ * XXX: PgBackendStatus should be renamed as PgProcStatus since it is used for
+ * backends as well as auxiliary process. But, to avoid massive code refactoring,
+ * we've kept it this way for now.
  * ----------
  */
 typedef struct PgBackendStatus
@@ -869,6 +895,9 @@ typedef struct PgBackendStatus
 	/* The entry is valid iff st_procpid > 0, unused if st_procpid == 0 */
 	int			st_procpid;
 
+	/* Type of process */
+	ProcType	st_procType;
+
 	/* Times when current backend, transaction, and activity started */
 	TimestampTz st_proc_start_timestamp;
 	TimestampTz st_xact_start_timestamp;
@@ -1056,7 +1085,7 @@ extern void pgstat_report_recovery_conflict(int reason);
 extern void pgstat_report_deadlock(void);
 
 extern void pgstat_initialize(void);
-extern void pgstat_bestart(void);
+extern void pgstat_procstart(void);
 
 extern void pgstat_report_activity(BackendState state, const char *cmd_str);
 extern void pgstat_report_tempfile(size_t filesize);
@@ -1067,6 +1096,7 @@ extern const char *pgstat_get_wait_event_type(uint32 wait_event_info);
 extern const char *pgstat_get_backend_current_activity(int pid, bool checkUser);
 extern const char *pgstat_get_crashed_backend_activity(int pid, char *buffer,
 									int buflen);
+extern const char *pgstat_get_proctype_desc(ProcType procType);
 
 extern void pgstat_progress_start_command(ProgressCommandType cmdtype,
 							  Oid relid);
@@ -1210,8 +1240,11 @@ extern PgStat_StatDBEntry *pgstat_fetch_stat_dbentry(Oid dbid);
 extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid relid);
 extern PgBackendStatus *pgstat_fetch_stat_beentry(int beid);
 extern LocalPgBackendStatus *pgstat_fetch_stat_local_beentry(int beid);
+extern PgBackendStatus *pgstat_fetch_stat_procentry(int procid);
+extern LocalPgBackendStatus *pgstat_fetch_stat_local_procentry(int procid);
 extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
 extern int	pgstat_fetch_stat_numbackends(void);
+extern int	pgstat_fetch_stat_numprocs(void);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 extern PgStat_GlobalStats *pgstat_fetch_global(void);
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 5f38fa6..be3bd01 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -262,6 +262,7 @@ extern PGPROC *PreparedXactProcs;
  */
 #define NUM_AUXILIARY_PROCS		4
 
+extern PGPROC *AuxiliaryPidGetProc(int pid);
 
 /* configurable options */
 extern int	DeadlockTimeout;
-- 
1.8.3.1

