Split backend status and progress related functionality out of pgstat.c.
authorAndres Freund <andres@anarazel.de>
Sat, 3 Apr 2021 18:42:52 +0000 (11:42 -0700)
committerAndres Freund <andres@anarazel.de>
Sat, 3 Apr 2021 18:42:52 +0000 (11:42 -0700)
Backend status (supporting pg_stat_activity) and command
progress (supporting pg_stat_progress*) related code is largely
independent from the rest of pgstat.[ch] (supporting views like
pg_stat_all_tables that accumulate data over time). See also
a333476b925.

This commit doesn't rename the function names to make the distinction
from the rest of pgstat_ clearer - that'd be more invasive and not
clearly beneficial. If we were to decide to do such a rename at some
point, it's better done separately from moving the code as well.

Robert's review was of an earlier version.

Reviewed-By: Robert Haas <robertmhaas@gmail.com>
Discussion: https://postgr.es/m/20210316195440.twxmlov24rr2nxrg@alap3.anarazel.de

src/backend/bootstrap/bootstrap.c
src/backend/postmaster/pgstat.c
src/backend/utils/activity/Makefile
src/backend/utils/activity/backend_progress.c [new file with mode: 0644]
src/backend/utils/activity/backend_status.c [new file with mode: 0644]
src/backend/utils/init/postinit.c
src/backend/utils/misc/guc.c
src/include/commands/progress.h
src/include/pgstat.h
src/include/utils/backend_progress.h [new file with mode: 0644]
src/include/utils/backend_status.h [new file with mode: 0644]

index dce7088e6bbb25fcb5edd712aec686392f684998..174727b501d06f0926975e4babae1fa2bd24fbe2 100644 (file)
@@ -407,8 +407,11 @@ AuxiliaryProcessMain(int argc, char *argv[])
                 */
                CreateAuxProcessResourceOwner();
 
-               /* Initialize backend status information */
+               /* Initialize statistics reporting */
                pgstat_initialize();
+
+               /* Initialize backend status information */
+               pgstat_beinit();
                pgstat_bestart();
 
                /* register a before-shutdown callback for LWLock cleanup */
index 498d6ee12368aef2ea20eb77ab66898cfad1ef8d..4c4b07206819922ab3b2c6069109e53fe98c9bff 100644 (file)
@@ -46,7 +46,6 @@
 #include "libpq/pqsignal.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
-#include "pg_trace.h"
 #include "pgstat.h"
 #include "postmaster/autovacuum.h"
 #include "postmaster/fork_process.h"
@@ -63,8 +62,6 @@
 #include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/procsignal.h"
-#include "storage/sinvaladt.h"
-#include "utils/ascii.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
 #define PGSTAT_FUNCTION_HASH_SIZE      512
 
 
-/* ----------
- * Total number of backends including auxiliary
- *
- * We reserve a slot for each possible BackendId, plus one for each
- * possible auxiliary process type.  (This scheme assumes there is not
- * more than one of any auxiliary process type at a time.) MaxBackends
- * includes autovacuum workers and background workers as well.
- * ----------
- */
-#define NumBackendStatSlots (MaxBackends + NUM_AUXPROCTYPES)
-
-
 /* ----------
  * GUC parameters
  * ----------
  */
-bool           pgstat_track_activities = false;
 bool           pgstat_track_counts = false;
 int                    pgstat_track_functions = TRACK_FUNC_OFF;
-int                    pgstat_track_activity_query_size = 1024;
 
 /* ----------
  * Built from GUC parameter
@@ -259,8 +242,8 @@ static int  pgStatXactCommit = 0;
 static int     pgStatXactRollback = 0;
 PgStat_Counter pgStatBlockReadTime = 0;
 PgStat_Counter pgStatBlockWriteTime = 0;
-static PgStat_Counter pgStatActiveTime = 0;
-static PgStat_Counter pgStatTransactionIdleTime = 0;
+PgStat_Counter pgStatActiveTime = 0;
+PgStat_Counter pgStatTransactionIdleTime = 0;
 SessionEndType pgStatSessionEndCause = DISCONNECT_NORMAL;
 
 /* Record that's written to 2PC state file when pgstat state is persisted */
@@ -283,12 +266,6 @@ typedef struct TwoPhasePgStatRecord
 static MemoryContext pgStatLocalContext = NULL;
 static HTAB *pgStatDBHash = NULL;
 
-/* Status for backends including auxiliary */
-static LocalPgBackendStatus *localBackendStatusTable = NULL;
-
-/* Total number of backends including auxiliary */
-static int     localNumBackends = 0;
-
 /*
  * Cluster wide statistics, kept in the stats collector.
  * Contains statistics that are not collected per database
@@ -325,7 +302,6 @@ static pid_t pgstat_forkexec(void);
 #endif
 
 NON_EXEC_STATIC void PgstatCollectorMain(int argc, char *argv[]) pg_attribute_noreturn();
-static void pgstat_beshutdown_hook(int code, Datum arg);
 
 static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid, bool create);
 static PgStat_StatTabEntry *pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry,
@@ -335,7 +311,6 @@ static void pgstat_write_db_statsfile(PgStat_StatDBEntry *dbentry, bool permanen
 static HTAB *pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep);
 static void pgstat_read_db_statsfile(Oid databaseid, HTAB *tabhash, HTAB *funchash, bool permanent);
 static void backend_read_statsfile(void);
-static void pgstat_read_current_status(void);
 
 static bool pgstat_write_statsfile_needed(void);
 static bool pgstat_db_requested(Oid databaseid);
@@ -2757,65 +2732,6 @@ pgstat_fetch_stat_funcentry(Oid func_id)
 }
 
 
-/* ----------
- * pgstat_fetch_stat_beentry() -
- *
- *     Support function for the SQL-callable pgstat* functions. Returns
- *     our local copy of the current-activity entry for one backend.
- *
- *     NB: caller is responsible for a check if the user is permitted to see
- *     this info (especially the querystring).
- * ----------
- */
-PgBackendStatus *
-pgstat_fetch_stat_beentry(int beid)
-{
-       pgstat_read_current_status();
-
-       if (beid < 1 || beid > localNumBackends)
-               return NULL;
-
-       return &localBackendStatusTable[beid - 1].backendStatus;
-}
-
-
-/* ----------
- * pgstat_fetch_stat_local_beentry() -
- *
- *     Like pgstat_fetch_stat_beentry() 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_beentry(int beid)
-{
-       pgstat_read_current_status();
-
-       if (beid < 1 || beid > localNumBackends)
-               return NULL;
-
-       return &localBackendStatusTable[beid - 1];
-}
-
-
-/* ----------
- * pgstat_fetch_stat_numbackends() -
- *
- *     Support function for the SQL-callable pgstat* functions. Returns
- *     the maximum current backend id.
- * ----------
- */
-int
-pgstat_fetch_stat_numbackends(void)
-{
-       pgstat_read_current_status();
-
-       return localNumBackends;
-}
-
 /*
  * ---------
  * pgstat_fetch_stat_archiver() -
@@ -2899,421 +2815,16 @@ pgstat_fetch_replslot(int *nslots_p)
        return replSlotStats;
 }
 
-/* ------------------------------------------------------------
- * Functions for management of the shared-memory PgBackendStatus array
- * ------------------------------------------------------------
- */
-
-static PgBackendStatus *BackendStatusArray = NULL;
-static PgBackendStatus *MyBEEntry = NULL;
-static char *BackendAppnameBuffer = NULL;
-static char *BackendClientHostnameBuffer = NULL;
-static char *BackendActivityBuffer = NULL;
-static Size BackendActivityBufferSize = 0;
-#ifdef USE_SSL
-static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;
-#endif
-#ifdef ENABLE_GSS
-static PgBackendGSSStatus *BackendGssStatusBuffer = NULL;
-#endif
-
-
-/*
- * Report shared-memory space needed by CreateSharedBackendStatus.
- */
-Size
-BackendStatusShmemSize(void)
-{
-       Size            size;
-
-       /* BackendStatusArray: */
-       size = mul_size(sizeof(PgBackendStatus), NumBackendStatSlots);
-       /* BackendAppnameBuffer: */
-       size = add_size(size,
-                                       mul_size(NAMEDATALEN, NumBackendStatSlots));
-       /* BackendClientHostnameBuffer: */
-       size = add_size(size,
-                                       mul_size(NAMEDATALEN, NumBackendStatSlots));
-       /* BackendActivityBuffer: */
-       size = add_size(size,
-                                       mul_size(pgstat_track_activity_query_size, NumBackendStatSlots));
-#ifdef USE_SSL
-       /* BackendSslStatusBuffer: */
-       size = add_size(size,
-                                       mul_size(sizeof(PgBackendSSLStatus), NumBackendStatSlots));
-#endif
-#ifdef ENABLE_GSS
-       /* BackendGssStatusBuffer: */
-       size = add_size(size,
-                                       mul_size(sizeof(PgBackendGSSStatus), NumBackendStatSlots));
-#endif
-       return size;
-}
-
-/*
- * Initialize the shared status array and several string buffers
- * during postmaster startup.
- */
-void
-CreateSharedBackendStatus(void)
-{
-       Size            size;
-       bool            found;
-       int                     i;
-       char       *buffer;
-
-       /* Create or attach to the shared array */
-       size = mul_size(sizeof(PgBackendStatus), NumBackendStatSlots);
-       BackendStatusArray = (PgBackendStatus *)
-               ShmemInitStruct("Backend Status Array", size, &found);
-
-       if (!found)
-       {
-               /*
-                * We're the first - initialize.
-                */
-               MemSet(BackendStatusArray, 0, size);
-       }
-
-       /* Create or attach to the shared appname buffer */
-       size = mul_size(NAMEDATALEN, NumBackendStatSlots);
-       BackendAppnameBuffer = (char *)
-               ShmemInitStruct("Backend Application Name Buffer", size, &found);
-
-       if (!found)
-       {
-               MemSet(BackendAppnameBuffer, 0, size);
-
-               /* Initialize st_appname pointers. */
-               buffer = BackendAppnameBuffer;
-               for (i = 0; i < NumBackendStatSlots; i++)
-               {
-                       BackendStatusArray[i].st_appname = buffer;
-                       buffer += NAMEDATALEN;
-               }
-       }
-
-       /* Create or attach to the shared client hostname buffer */
-       size = mul_size(NAMEDATALEN, NumBackendStatSlots);
-       BackendClientHostnameBuffer = (char *)
-               ShmemInitStruct("Backend Client Host Name Buffer", size, &found);
-
-       if (!found)
-       {
-               MemSet(BackendClientHostnameBuffer, 0, size);
-
-               /* Initialize st_clienthostname pointers. */
-               buffer = BackendClientHostnameBuffer;
-               for (i = 0; i < NumBackendStatSlots; i++)
-               {
-                       BackendStatusArray[i].st_clienthostname = buffer;
-                       buffer += NAMEDATALEN;
-               }
-       }
-
-       /* Create or attach to the shared activity buffer */
-       BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
-                                                                                NumBackendStatSlots);
-       BackendActivityBuffer = (char *)
-               ShmemInitStruct("Backend Activity Buffer",
-                                               BackendActivityBufferSize,
-                                               &found);
-
-       if (!found)
-       {
-               MemSet(BackendActivityBuffer, 0, BackendActivityBufferSize);
-
-               /* Initialize st_activity pointers. */
-               buffer = BackendActivityBuffer;
-               for (i = 0; i < NumBackendStatSlots; i++)
-               {
-                       BackendStatusArray[i].st_activity_raw = buffer;
-                       buffer += pgstat_track_activity_query_size;
-               }
-       }
-
-#ifdef USE_SSL
-       /* Create or attach to the shared SSL status buffer */
-       size = mul_size(sizeof(PgBackendSSLStatus), NumBackendStatSlots);
-       BackendSslStatusBuffer = (PgBackendSSLStatus *)
-               ShmemInitStruct("Backend SSL Status Buffer", size, &found);
-
-       if (!found)
-       {
-               PgBackendSSLStatus *ptr;
-
-               MemSet(BackendSslStatusBuffer, 0, size);
-
-               /* Initialize st_sslstatus pointers. */
-               ptr = BackendSslStatusBuffer;
-               for (i = 0; i < NumBackendStatSlots; i++)
-               {
-                       BackendStatusArray[i].st_sslstatus = ptr;
-                       ptr++;
-               }
-       }
-#endif
-
-#ifdef ENABLE_GSS
-       /* Create or attach to the shared GSSAPI status buffer */
-       size = mul_size(sizeof(PgBackendGSSStatus), NumBackendStatSlots);
-       BackendGssStatusBuffer = (PgBackendGSSStatus *)
-               ShmemInitStruct("Backend GSS Status Buffer", size, &found);
-
-       if (!found)
-       {
-               PgBackendGSSStatus *ptr;
-
-               MemSet(BackendGssStatusBuffer, 0, size);
-
-               /* Initialize st_gssstatus pointers. */
-               ptr = BackendGssStatusBuffer;
-               for (i = 0; i < NumBackendStatSlots; i++)
-               {
-                       BackendStatusArray[i].st_gssstatus = ptr;
-                       ptr++;
-               }
-       }
-#endif
-}
-
-
-/* ----------
- * pgstat_initialize() -
- *
- *     Initialize pgstats state, and set up our on-proc-exit hook.
- *     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.
- * ----------
- */
-void
-pgstat_initialize(void)
-{
-       /* Initialize MyBEEntry */
-       if (MyBackendId != InvalidBackendId)
-       {
-               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 + AuxBackendType + 1 as the index of the slot for an
-                * auxiliary process.
-                */
-               MyBEEntry = &BackendStatusArray[MaxBackends + MyAuxProcType];
-       }
-
-       /*
-        * Initialize prevWalUsage with pgWalUsage so that pgstat_report_wal() can
-        * calculate how much pgWalUsage counters are increased by substracting
-        * prevWalUsage from pgWalUsage.
-        */
-       prevWalUsage = pgWalUsage;
-
-       /* Set up a process-exit hook to clean up */
-       on_shmem_exit(pgstat_beshutdown_hook, 0);
-}
-
-/* ----------
- * pgstat_bestart() -
- *
- *     Initialize this backend's entry in the PgBackendStatus array.
- *     Called from InitPostgres.
- *
- *     Apart from auxiliary processes, MyBackendId, MyDatabaseId,
- *     session userid, and application_name must be set for a
- *     backend (hence, this cannot be combined with pgstat_initialize).
- *     Note also that we must be inside a transaction if this isn't an aux
- *     process, as we may need to do encoding conversion on some strings.
- * ----------
- */
-void
-pgstat_bestart(void)
-{
-       volatile PgBackendStatus *vbeentry = MyBEEntry;
-       PgBackendStatus lbeentry;
-#ifdef USE_SSL
-       PgBackendSSLStatus lsslstatus;
-#endif
-#ifdef ENABLE_GSS
-       PgBackendGSSStatus lgssstatus;
-#endif
-
-       /* pgstats state must be initialized from pgstat_initialize() */
-       Assert(vbeentry != NULL);
-
-       /*
-        * To minimize the time spent modifying the PgBackendStatus entry, and
-        * avoid risk of errors inside the critical section, we first copy the
-        * shared-memory struct to a local variable, then modify the data in the
-        * local variable, then copy the local variable back to shared memory.
-        * Only the last step has to be inside the critical section.
-        *
-        * Most of the data we copy from shared memory is just going to be
-        * overwritten, but the struct's not so large that it's worth the
-        * maintenance hassle to copy only the needful fields.
-        */
-       memcpy(&lbeentry,
-                  unvolatize(PgBackendStatus *, vbeentry),
-                  sizeof(PgBackendStatus));
-
-       /* These structs can just start from zeroes each time, though */
-#ifdef USE_SSL
-       memset(&lsslstatus, 0, sizeof(lsslstatus));
-#endif
-#ifdef ENABLE_GSS
-       memset(&lgssstatus, 0, sizeof(lgssstatus));
-#endif
-
-       /*
-        * Now fill in all the fields of lbeentry, except for strings that are
-        * out-of-line data.  Those have to be handled separately, below.
-        */
-       lbeentry.st_procpid = MyProcPid;
-       lbeentry.st_backendType = MyBackendType;
-       lbeentry.st_proc_start_timestamp = MyStartTimestamp;
-       lbeentry.st_activity_start_timestamp = 0;
-       lbeentry.st_state_start_timestamp = 0;
-       lbeentry.st_xact_start_timestamp = 0;
-       lbeentry.st_databaseid = MyDatabaseId;
-
-       /* We have userid for client-backends, wal-sender and bgworker processes */
-       if (lbeentry.st_backendType == B_BACKEND
-               || lbeentry.st_backendType == B_WAL_SENDER
-               || lbeentry.st_backendType == B_BG_WORKER)
-               lbeentry.st_userid = GetSessionUserId();
-       else
-               lbeentry.st_userid = InvalidOid;
-
-       /*
-        * We may not have a MyProcPort (eg, if this is the autovacuum process).
-        * If so, use all-zeroes client address, which is dealt with specially in
-        * pg_stat_get_backend_client_addr and pg_stat_get_backend_client_port.
-        */
-       if (MyProcPort)
-               memcpy(&lbeentry.st_clientaddr, &MyProcPort->raddr,
-                          sizeof(lbeentry.st_clientaddr));
-       else
-               MemSet(&lbeentry.st_clientaddr, 0, sizeof(lbeentry.st_clientaddr));
-
-#ifdef USE_SSL
-       if (MyProcPort && MyProcPort->ssl_in_use)
-       {
-               lbeentry.st_ssl = true;
-               lsslstatus.ssl_bits = be_tls_get_cipher_bits(MyProcPort);
-               strlcpy(lsslstatus.ssl_version, be_tls_get_version(MyProcPort), NAMEDATALEN);
-               strlcpy(lsslstatus.ssl_cipher, be_tls_get_cipher(MyProcPort), NAMEDATALEN);
-               be_tls_get_peer_subject_name(MyProcPort, lsslstatus.ssl_client_dn, NAMEDATALEN);
-               be_tls_get_peer_serial(MyProcPort, lsslstatus.ssl_client_serial, NAMEDATALEN);
-               be_tls_get_peer_issuer_name(MyProcPort, lsslstatus.ssl_issuer_dn, NAMEDATALEN);
-       }
-       else
-       {
-               lbeentry.st_ssl = false;
-       }
-#else
-       lbeentry.st_ssl = false;
-#endif
-
-#ifdef ENABLE_GSS
-       if (MyProcPort && MyProcPort->gss != NULL)
-       {
-               const char *princ = be_gssapi_get_princ(MyProcPort);
-
-               lbeentry.st_gss = true;
-               lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort);
-               lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort);
-               if (princ)
-                       strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN);
-       }
-       else
-       {
-               lbeentry.st_gss = false;
-       }
-#else
-       lbeentry.st_gss = false;
-#endif
-
-       lbeentry.st_state = STATE_UNDEFINED;
-       lbeentry.st_progress_command = PROGRESS_COMMAND_INVALID;
-       lbeentry.st_progress_command_target = InvalidOid;
-
-       /*
-        * we don't zero st_progress_param here to save cycles; nobody should
-        * examine it until st_progress_command has been set to something other
-        * than PROGRESS_COMMAND_INVALID
-        */
-
-       /*
-        * We're ready to enter the critical section that fills the shared-memory
-        * status entry.  We follow the protocol of bumping st_changecount before
-        * and after; and make sure it's even afterwards.  We use a volatile
-        * pointer here to ensure the compiler doesn't try to get cute.
-        */
-       PGSTAT_BEGIN_WRITE_ACTIVITY(vbeentry);
-
-       /* make sure we'll memcpy the same st_changecount back */
-       lbeentry.st_changecount = vbeentry->st_changecount;
-
-       memcpy(unvolatize(PgBackendStatus *, vbeentry),
-                  &lbeentry,
-                  sizeof(PgBackendStatus));
-
-       /*
-        * We can write the out-of-line strings and structs using the pointers
-        * that are in lbeentry; this saves some de-volatilizing messiness.
-        */
-       lbeentry.st_appname[0] = '\0';
-       if (MyProcPort && MyProcPort->remote_hostname)
-               strlcpy(lbeentry.st_clienthostname, MyProcPort->remote_hostname,
-                               NAMEDATALEN);
-       else
-               lbeentry.st_clienthostname[0] = '\0';
-       lbeentry.st_activity_raw[0] = '\0';
-       /* Also make sure the last byte in each string area is always 0 */
-       lbeentry.st_appname[NAMEDATALEN - 1] = '\0';
-       lbeentry.st_clienthostname[NAMEDATALEN - 1] = '\0';
-       lbeentry.st_activity_raw[pgstat_track_activity_query_size - 1] = '\0';
-
-#ifdef USE_SSL
-       memcpy(lbeentry.st_sslstatus, &lsslstatus, sizeof(PgBackendSSLStatus));
-#endif
-#ifdef ENABLE_GSS
-       memcpy(lbeentry.st_gssstatus, &lgssstatus, sizeof(PgBackendGSSStatus));
-#endif
-
-       PGSTAT_END_WRITE_ACTIVITY(vbeentry);
-
-       /* Update app name to current GUC setting */
-       if (application_name)
-               pgstat_report_appname(application_name);
-}
-
 /*
  * Shut down a single backend's statistics reporting at process exit.
  *
  * Flush any remaining statistics counts out to the collector.
  * Without this, operations triggered during backend exit (such as
  * temp table deletions) won't be counted.
- *
- * Lastly, clear out our entry in the PgBackendStatus array.
  */
 static void
-pgstat_beshutdown_hook(int code, Datum arg)
+pgstat_shutdown_hook(int code, Datum arg)
 {
-       volatile PgBackendStatus *beentry = MyBEEntry;
-
        /*
         * If we got as far as discovering our own database ID, we can report what
         * we did to the collector.  Otherwise, we'd be sending an invalid
@@ -3322,584 +2833,29 @@ pgstat_beshutdown_hook(int code, Datum arg)
         */
        if (OidIsValid(MyDatabaseId))
                pgstat_report_stat(true);
-
-       /*
-        * Clear my status entry, following the protocol of bumping st_changecount
-        * before and after.  We use a volatile pointer here to ensure the
-        * compiler doesn't try to get cute.
-        */
-       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
-
-       beentry->st_procpid = 0;        /* mark invalid */
-
-       PGSTAT_END_WRITE_ACTIVITY(beentry);
 }
 
-
 /* ----------
- * pgstat_report_activity() -
- *
- *     Called from tcop/postgres.c to report what the backend is actually doing
- *     (but note cmd_str can be NULL for certain cases).
- *
- * All updates of the status entry follow the protocol of bumping
- * st_changecount before and after.  We use a volatile pointer here to
- * ensure the compiler doesn't try to get cute.
- * ----------
- */
-void
-pgstat_report_activity(BackendState state, const char *cmd_str)
-{
-       volatile PgBackendStatus *beentry = MyBEEntry;
-       TimestampTz start_timestamp;
-       TimestampTz current_timestamp;
-       int                     len = 0;
-
-       TRACE_POSTGRESQL_STATEMENT_STATUS(cmd_str);
-
-       if (!beentry)
-               return;
-
-       if (!pgstat_track_activities)
-       {
-               if (beentry->st_state != STATE_DISABLED)
-               {
-                       volatile PGPROC *proc = MyProc;
-
-                       /*
-                        * track_activities is disabled, but we last reported a
-                        * non-disabled state.  As our final update, change the state and
-                        * clear fields we will not be updating anymore.
-                        */
-                       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
-                       beentry->st_state = STATE_DISABLED;
-                       beentry->st_state_start_timestamp = 0;
-                       beentry->st_activity_raw[0] = '\0';
-                       beentry->st_activity_start_timestamp = 0;
-                       /* st_xact_start_timestamp and wait_event_info are also disabled */
-                       beentry->st_xact_start_timestamp = 0;
-                       proc->wait_event_info = 0;
-                       PGSTAT_END_WRITE_ACTIVITY(beentry);
-               }
-               return;
-       }
-
-       /*
-        * To minimize the time spent modifying the entry, and avoid risk of
-        * errors inside the critical section, fetch all the needed data first.
-        */
-       start_timestamp = GetCurrentStatementStartTimestamp();
-       if (cmd_str != NULL)
-       {
-               /*
-                * Compute length of to-be-stored string unaware of multi-byte
-                * characters. For speed reasons that'll get corrected on read, rather
-                * than computed every write.
-                */
-               len = Min(strlen(cmd_str), pgstat_track_activity_query_size - 1);
-       }
-       current_timestamp = GetCurrentTimestamp();
-
-       /*
-        * If the state has changed from "active" or "idle in transaction",
-        * calculate the duration.
-        */
-       if ((beentry->st_state == STATE_RUNNING ||
-                beentry->st_state == STATE_FASTPATH ||
-                beentry->st_state == STATE_IDLEINTRANSACTION ||
-                beentry->st_state == STATE_IDLEINTRANSACTION_ABORTED) &&
-               state != beentry->st_state)
-       {
-               long            secs;
-               int                     usecs;
-
-               TimestampDifference(beentry->st_state_start_timestamp,
-                                                       current_timestamp,
-                                                       &secs, &usecs);
-
-               if (beentry->st_state == STATE_RUNNING ||
-                       beentry->st_state == STATE_FASTPATH)
-                       pgStatActiveTime += secs * 1000000 + usecs;
-               else
-                       pgStatTransactionIdleTime += secs * 1000000 + usecs;
-       }
-
-       /*
-        * Now update the status entry
-        */
-       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
-
-       beentry->st_state = state;
-       beentry->st_state_start_timestamp = current_timestamp;
-
-       if (cmd_str != NULL)
-       {
-               memcpy((char *) beentry->st_activity_raw, cmd_str, len);
-               beentry->st_activity_raw[len] = '\0';
-               beentry->st_activity_start_timestamp = start_timestamp;
-       }
-
-       PGSTAT_END_WRITE_ACTIVITY(beentry);
-}
-
-/*-----------
- * pgstat_progress_start_command() -
- *
- * Set st_progress_command (and st_progress_command_target) in own backend
- * entry.  Also, zero-initialize st_progress_param array.
- *-----------
- */
-void
-pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
-{
-       volatile PgBackendStatus *beentry = MyBEEntry;
-
-       if (!beentry || !pgstat_track_activities)
-               return;
-
-       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
-       beentry->st_progress_command = cmdtype;
-       beentry->st_progress_command_target = relid;
-       MemSet(&beentry->st_progress_param, 0, sizeof(beentry->st_progress_param));
-       PGSTAT_END_WRITE_ACTIVITY(beentry);
-}
-
-/*-----------
- * pgstat_progress_update_param() -
- *
- * Update index'th member in st_progress_param[] of own backend entry.
- *-----------
- */
-void
-pgstat_progress_update_param(int index, int64 val)
-{
-       volatile PgBackendStatus *beentry = MyBEEntry;
-
-       Assert(index >= 0 && index < PGSTAT_NUM_PROGRESS_PARAM);
-
-       if (!beentry || !pgstat_track_activities)
-               return;
-
-       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
-       beentry->st_progress_param[index] = val;
-       PGSTAT_END_WRITE_ACTIVITY(beentry);
-}
-
-/*-----------
- * pgstat_progress_update_multi_param() -
- *
- * Update multiple members in st_progress_param[] of own backend entry.
- * This is atomic; readers won't see intermediate states.
- *-----------
- */
-void
-pgstat_progress_update_multi_param(int nparam, const int *index,
-                                                                  const int64 *val)
-{
-       volatile PgBackendStatus *beentry = MyBEEntry;
-       int                     i;
-
-       if (!beentry || !pgstat_track_activities || nparam == 0)
-               return;
-
-       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
-
-       for (i = 0; i < nparam; ++i)
-       {
-               Assert(index[i] >= 0 && index[i] < PGSTAT_NUM_PROGRESS_PARAM);
-
-               beentry->st_progress_param[index[i]] = val[i];
-       }
-
-       PGSTAT_END_WRITE_ACTIVITY(beentry);
-}
-
-/*-----------
- * pgstat_progress_end_command() -
+ * pgstat_initialize() -
  *
- * Reset st_progress_command (and st_progress_command_target) in own backend
- * entry.  This signals the end of the command.
- *-----------
- */
-void
-pgstat_progress_end_command(void)
-{
-       volatile PgBackendStatus *beentry = MyBEEntry;
-
-       if (!beentry || !pgstat_track_activities)
-               return;
-
-       if (beentry->st_progress_command == PROGRESS_COMMAND_INVALID)
-               return;
-
-       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
-       beentry->st_progress_command = PROGRESS_COMMAND_INVALID;
-       beentry->st_progress_command_target = InvalidOid;
-       PGSTAT_END_WRITE_ACTIVITY(beentry);
-}
-
-/* ----------
- * pgstat_report_appname() -
+ *     Initialize pgstats state, and set up our on-proc-exit hook.
+ *     Called from InitPostgres and AuxiliaryProcessMain.
  *
- *     Called to update our application name.
+ *     NOTE: MyDatabaseId isn't set yet; so the shutdown hook has to be careful.
  * ----------
  */
 void
-pgstat_report_appname(const char *appname)
-{
-       volatile PgBackendStatus *beentry = MyBEEntry;
-       int                     len;
-
-       if (!beentry)
-               return;
-
-       /* This should be unnecessary if GUC did its job, but be safe */
-       len = pg_mbcliplen(appname, strlen(appname), NAMEDATALEN - 1);
-
-       /*
-        * Update my status entry, following the protocol of bumping
-        * st_changecount before and after.  We use a volatile pointer here to
-        * ensure the compiler doesn't try to get cute.
-        */
-       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
-
-       memcpy((char *) beentry->st_appname, appname, len);
-       beentry->st_appname[len] = '\0';
-
-       PGSTAT_END_WRITE_ACTIVITY(beentry);
-}
-
-/*
- * Report current transaction start timestamp as the specified value.
- * Zero means there is no active transaction.
- */
-void
-pgstat_report_xact_timestamp(TimestampTz tstamp)
-{
-       volatile PgBackendStatus *beentry = MyBEEntry;
-
-       if (!pgstat_track_activities || !beentry)
-               return;
-
-       /*
-        * Update my status entry, following the protocol of bumping
-        * st_changecount before and after.  We use a volatile pointer here to
-        * ensure the compiler doesn't try to get cute.
-        */
-       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
-
-       beentry->st_xact_start_timestamp = tstamp;
-
-       PGSTAT_END_WRITE_ACTIVITY(beentry);
-}
-
-/* ----------
- * pgstat_read_current_status() -
- *
- *     Copy the current contents of the PgBackendStatus array to local memory,
- *     if not already done in this transaction.
- * ----------
- */
-static void
-pgstat_read_current_status(void)
-{
-       volatile PgBackendStatus *beentry;
-       LocalPgBackendStatus *localtable;
-       LocalPgBackendStatus *localentry;
-       char       *localappname,
-                          *localclienthostname,
-                          *localactivity;
-#ifdef USE_SSL
-       PgBackendSSLStatus *localsslstatus;
-#endif
-#ifdef ENABLE_GSS
-       PgBackendGSSStatus *localgssstatus;
-#endif
-       int                     i;
-
-       Assert(!pgStatRunningInCollector);
-       if (localBackendStatusTable)
-               return;                                 /* already done */
-
-       pgstat_setup_memcxt();
-
-       /*
-        * Allocate storage for local copy of state data.  We can presume that
-        * none of these requests overflow size_t, because we already calculated
-        * the same values using mul_size during shmem setup.  However, with
-        * probably-silly values of pgstat_track_activity_query_size and
-        * max_connections, the localactivity buffer could exceed 1GB, so use
-        * "huge" allocation for that one.
-        */
-       localtable = (LocalPgBackendStatus *)
-               MemoryContextAlloc(pgStatLocalContext,
-                                                  sizeof(LocalPgBackendStatus) * NumBackendStatSlots);
-       localappname = (char *)
-               MemoryContextAlloc(pgStatLocalContext,
-                                                  NAMEDATALEN * NumBackendStatSlots);
-       localclienthostname = (char *)
-               MemoryContextAlloc(pgStatLocalContext,
-                                                  NAMEDATALEN * NumBackendStatSlots);
-       localactivity = (char *)
-               MemoryContextAllocHuge(pgStatLocalContext,
-                                                          pgstat_track_activity_query_size * NumBackendStatSlots);
-#ifdef USE_SSL
-       localsslstatus = (PgBackendSSLStatus *)
-               MemoryContextAlloc(pgStatLocalContext,
-                                                  sizeof(PgBackendSSLStatus) * NumBackendStatSlots);
-#endif
-#ifdef ENABLE_GSS
-       localgssstatus = (PgBackendGSSStatus *)
-               MemoryContextAlloc(pgStatLocalContext,
-                                                  sizeof(PgBackendGSSStatus) * NumBackendStatSlots);
-#endif
-
-       localNumBackends = 0;
-
-       beentry = BackendStatusArray;
-       localentry = localtable;
-       for (i = 1; i <= NumBackendStatSlots; i++)
-       {
-               /*
-                * Follow the protocol of retrying if st_changecount changes while we
-                * copy the entry, or if it's odd.  (The check for odd is needed to
-                * cover the case where we are able to completely copy the entry while
-                * the source backend is between increment steps.)      We use a volatile
-                * pointer here to ensure the compiler doesn't try to get cute.
-                */
-               for (;;)
-               {
-                       int                     before_changecount;
-                       int                     after_changecount;
-
-                       pgstat_begin_read_activity(beentry, before_changecount);
-
-                       localentry->backendStatus.st_procpid = beentry->st_procpid;
-                       /* Skip all the data-copying work if entry is not in use */
-                       if (localentry->backendStatus.st_procpid > 0)
-                       {
-                               memcpy(&localentry->backendStatus, unvolatize(PgBackendStatus *, beentry), sizeof(PgBackendStatus));
-
-                               /*
-                                * For each PgBackendStatus field that is a pointer, copy the
-                                * pointed-to data, then adjust the local copy of the pointer
-                                * field to point at the local copy of the data.
-                                *
-                                * 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);
-                               localentry->backendStatus.st_appname = localappname;
-                               strcpy(localclienthostname, (char *) beentry->st_clienthostname);
-                               localentry->backendStatus.st_clienthostname = localclienthostname;
-                               strcpy(localactivity, (char *) beentry->st_activity_raw);
-                               localentry->backendStatus.st_activity_raw = localactivity;
-#ifdef USE_SSL
-                               if (beentry->st_ssl)
-                               {
-                                       memcpy(localsslstatus, beentry->st_sslstatus, sizeof(PgBackendSSLStatus));
-                                       localentry->backendStatus.st_sslstatus = localsslstatus;
-                               }
-#endif
-#ifdef ENABLE_GSS
-                               if (beentry->st_gss)
-                               {
-                                       memcpy(localgssstatus, beentry->st_gssstatus, sizeof(PgBackendGSSStatus));
-                                       localentry->backendStatus.st_gssstatus = localgssstatus;
-                               }
-#endif
-                       }
-
-                       pgstat_end_read_activity(beentry, after_changecount);
-
-                       if (pgstat_read_activity_complete(before_changecount,
-                                                                                         after_changecount))
-                               break;
-
-                       /* Make sure we can break out of loop if stuck... */
-                       CHECK_FOR_INTERRUPTS();
-               }
-
-               beentry++;
-               /* Only valid entries get included into the local array */
-               if (localentry->backendStatus.st_procpid > 0)
-               {
-                       BackendIdGetTransactionIds(i,
-                                                                          &localentry->backend_xid,
-                                                                          &localentry->backend_xmin);
-
-                       localentry++;
-                       localappname += NAMEDATALEN;
-                       localclienthostname += NAMEDATALEN;
-                       localactivity += pgstat_track_activity_query_size;
-#ifdef USE_SSL
-                       localsslstatus++;
-#endif
-#ifdef ENABLE_GSS
-                       localgssstatus++;
-#endif
-                       localNumBackends++;
-               }
-       }
-
-       /* Set the pointer only after completion of a valid table */
-       localBackendStatusTable = localtable;
-}
-
-/* ----------
- * pgstat_get_backend_current_activity() -
- *
- *     Return a string representing the current activity of the backend with
- *     the specified PID.  This looks directly at the BackendStatusArray,
- *     and so will provide current information regardless of the age of our
- *     transaction's snapshot of the status array.
- *
- *     It is the caller's responsibility to invoke this only for backends whose
- *     state is expected to remain stable while the result is in use.  The
- *     only current use is in deadlock reporting, where we can expect that
- *     the target backend is blocked on a lock.  (There are corner cases
- *     where the target's wait could get aborted while we are looking at it,
- *     but the very worst consequence is to return a pointer to a string
- *     that's been changed, so we won't worry too much.)
- *
- *     Note: return strings for special cases match pg_stat_get_backend_activity.
- * ----------
- */
-const char *
-pgstat_get_backend_current_activity(int pid, bool checkUser)
-{
-       PgBackendStatus *beentry;
-       int                     i;
-
-       beentry = BackendStatusArray;
-       for (i = 1; i <= MaxBackends; i++)
-       {
-               /*
-                * Although we expect the target backend's entry to be stable, that
-                * doesn't imply that anyone else's is.  To avoid identifying the
-                * wrong backend, while we check for a match to the desired PID we
-                * must follow the protocol of retrying if st_changecount changes
-                * while we examine the entry, or if it's odd.  (This might be
-                * unnecessary, since fetching or storing an int is almost certainly
-                * atomic, but let's play it safe.)  We use a volatile pointer here to
-                * ensure the compiler doesn't try to get cute.
-                */
-               volatile PgBackendStatus *vbeentry = beentry;
-               bool            found;
-
-               for (;;)
-               {
-                       int                     before_changecount;
-                       int                     after_changecount;
-
-                       pgstat_begin_read_activity(vbeentry, before_changecount);
-
-                       found = (vbeentry->st_procpid == pid);
-
-                       pgstat_end_read_activity(vbeentry, after_changecount);
-
-                       if (pgstat_read_activity_complete(before_changecount,
-                                                                                         after_changecount))
-                               break;
-
-                       /* Make sure we can break out of loop if stuck... */
-                       CHECK_FOR_INTERRUPTS();
-               }
-
-               if (found)
-               {
-                       /* Now it is safe to use the non-volatile pointer */
-                       if (checkUser && !superuser() && beentry->st_userid != GetUserId())
-                               return "<insufficient privilege>";
-                       else if (*(beentry->st_activity_raw) == '\0')
-                               return "<command string not enabled>";
-                       else
-                       {
-                               /* this'll leak a bit of memory, but that seems acceptable */
-                               return pgstat_clip_activity(beentry->st_activity_raw);
-                       }
-               }
-
-               beentry++;
-       }
-
-       /* If we get here, caller is in error ... */
-       return "<backend information not available>";
-}
-
-/* ----------
- * pgstat_get_crashed_backend_activity() -
- *
- *     Return a string representing the current activity of the backend with
- *     the specified PID.  Like the function above, but reads shared memory with
- *     the expectation that it may be corrupt.  On success, copy the string
- *     into the "buffer" argument and return that pointer.  On failure,
- *     return NULL.
- *
- *     This function is only intended to be used by the postmaster to report the
- *     query that crashed a backend.  In particular, no attempt is made to
- *     follow the correct concurrency protocol when accessing the
- *     BackendStatusArray.  But that's OK, in the worst case we'll return a
- *     corrupted message.  We also must take care not to trip on ereport(ERROR).
- * ----------
- */
-const char *
-pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen)
+pgstat_initialize(void)
 {
-       volatile PgBackendStatus *beentry;
-       int                     i;
-
-       beentry = BackendStatusArray;
-
        /*
-        * We probably shouldn't get here before shared memory has been set up,
-        * but be safe.
+        * Initialize prevWalUsage with pgWalUsage so that pgstat_report_wal() can
+        * calculate how much pgWalUsage counters are increased by substracting
+        * prevWalUsage from pgWalUsage.
         */
-       if (beentry == NULL || BackendActivityBuffer == NULL)
-               return NULL;
-
-       for (i = 1; i <= MaxBackends; i++)
-       {
-               if (beentry->st_procpid == pid)
-               {
-                       /* Read pointer just once, so it can't change after validation */
-                       const char *activity = beentry->st_activity_raw;
-                       const char *activity_last;
-
-                       /*
-                        * We mustn't access activity string before we verify that it
-                        * falls within the BackendActivityBuffer. To make sure that the
-                        * entire string including its ending is contained within the
-                        * buffer, subtract one activity length from the buffer size.
-                        */
-                       activity_last = BackendActivityBuffer + BackendActivityBufferSize
-                               - pgstat_track_activity_query_size;
-
-                       if (activity < BackendActivityBuffer ||
-                               activity > activity_last)
-                               return NULL;
-
-                       /* If no string available, no point in a report */
-                       if (activity[0] == '\0')
-                               return NULL;
-
-                       /*
-                        * Copy only ASCII-safe characters so we don't run into encoding
-                        * problems when reporting the message; and be sure not to run off
-                        * the end of memory.  As only ASCII characters are reported, it
-                        * doesn't seem necessary to perform multibyte aware clipping.
-                        */
-                       ascii_safe_strlcpy(buffer, activity,
-                                                          Min(buflen, pgstat_track_activity_query_size));
-
-                       return buffer;
-               }
-
-               beentry++;
-       }
+       prevWalUsage = pgWalUsage;
 
-       /* PID not found */
-       return NULL;
+       /* Set up a process-exit hook to clean up */
+       on_shmem_exit(pgstat_shutdown_hook, 0);
 }
 
 /* ------------------------------------------------------------
@@ -5656,10 +4612,15 @@ pgstat_clear_snapshot(void)
        /* Reset variables */
        pgStatLocalContext = NULL;
        pgStatDBHash = NULL;
-       localBackendStatusTable = NULL;
-       localNumBackends = 0;
        replSlotStats = NULL;
        nReplSlotStats = 0;
+
+       /*
+        * Historically the backend_status.c facilities lived in this file, and
+        * were reset with the same function. For now keep it that way, and
+        * forward the reset request.
+        */
+       pgstat_clear_backend_activity_snapshot();
 }
 
 
@@ -6594,50 +5555,6 @@ pgstat_db_requested(Oid databaseid)
        return false;
 }
 
-/*
- * Convert a potentially unsafely truncated activity string (see
- * PgBackendStatus.st_activity_raw's documentation) into a correctly truncated
- * one.
- *
- * The returned string is allocated in the caller's memory context and may be
- * freed.
- */
-char *
-pgstat_clip_activity(const char *raw_activity)
-{
-       char       *activity;
-       int                     rawlen;
-       int                     cliplen;
-
-       /*
-        * Some callers, like pgstat_get_backend_current_activity(), do not
-        * guarantee that the buffer isn't concurrently modified. We try to take
-        * care that the buffer is always terminated by a NUL byte regardless, but
-        * let's still be paranoid about the string's length. In those cases the
-        * underlying buffer is guaranteed to be pgstat_track_activity_query_size
-        * large.
-        */
-       activity = pnstrdup(raw_activity, pgstat_track_activity_query_size - 1);
-
-       /* now double-guaranteed to be NUL terminated */
-       rawlen = strlen(activity);
-
-       /*
-        * All supported server-encodings make it possible to determine the length
-        * of a multi-byte character from its first byte (this is not the case for
-        * client encodings, see GB18030). As st_activity is always stored using
-        * server encoding, this allows us to perform multi-byte aware truncation,
-        * even if the string earlier was truncated in the middle of a multi-byte
-        * character.
-        */
-       cliplen = pg_mbcliplen(activity, rawlen,
-                                                  pgstat_track_activity_query_size - 1);
-
-       activity[cliplen] = '\0';
-
-       return activity;
-}
-
 /* ----------
  * pgstat_replslot_index
  *
index 7cbe0cdbe819051601f88524f3dc0ecc290f39c8..59196f278d87af74ab2043d8c88be694bb7a262e 100644 (file)
@@ -14,6 +14,8 @@ top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
 OBJS = \
+       backend_progress.o \
+       backend_status.o \
        wait_event.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/activity/backend_progress.c b/src/backend/utils/activity/backend_progress.c
new file mode 100644 (file)
index 0000000..2932549
--- /dev/null
@@ -0,0 +1,112 @@
+/* ----------
+ * progress.c
+ *
+ *     Command progress reporting infrastructure.
+ *
+ *     Copyright (c) 2001-2021, PostgreSQL Global Development Group
+ *
+ *     src/backend/postmaster/progress.c
+ * ----------
+ */
+#include "postgres.h"
+
+#include "port/atomics.h" /* for memory barriers */
+#include "utils/backend_progress.h"
+#include "utils/backend_status.h"
+
+
+/*-----------
+ * pgstat_progress_start_command() -
+ *
+ * Set st_progress_command (and st_progress_command_target) in own backend
+ * entry.  Also, zero-initialize st_progress_param array.
+ *-----------
+ */
+void
+pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
+{
+       volatile PgBackendStatus *beentry = MyBEEntry;
+
+       if (!beentry || !pgstat_track_activities)
+               return;
+
+       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
+       beentry->st_progress_command = cmdtype;
+       beentry->st_progress_command_target = relid;
+       MemSet(&beentry->st_progress_param, 0, sizeof(beentry->st_progress_param));
+       PGSTAT_END_WRITE_ACTIVITY(beentry);
+}
+
+/*-----------
+ * pgstat_progress_update_param() -
+ *
+ * Update index'th member in st_progress_param[] of own backend entry.
+ *-----------
+ */
+void
+pgstat_progress_update_param(int index, int64 val)
+{
+       volatile PgBackendStatus *beentry = MyBEEntry;
+
+       Assert(index >= 0 && index < PGSTAT_NUM_PROGRESS_PARAM);
+
+       if (!beentry || !pgstat_track_activities)
+               return;
+
+       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
+       beentry->st_progress_param[index] = val;
+       PGSTAT_END_WRITE_ACTIVITY(beentry);
+}
+
+/*-----------
+ * pgstat_progress_update_multi_param() -
+ *
+ * Update multiple members in st_progress_param[] of own backend entry.
+ * This is atomic; readers won't see intermediate states.
+ *-----------
+ */
+void
+pgstat_progress_update_multi_param(int nparam, const int *index,
+                                                                  const int64 *val)
+{
+       volatile PgBackendStatus *beentry = MyBEEntry;
+       int                     i;
+
+       if (!beentry || !pgstat_track_activities || nparam == 0)
+               return;
+
+       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
+
+       for (i = 0; i < nparam; ++i)
+       {
+               Assert(index[i] >= 0 && index[i] < PGSTAT_NUM_PROGRESS_PARAM);
+
+               beentry->st_progress_param[index[i]] = val[i];
+       }
+
+       PGSTAT_END_WRITE_ACTIVITY(beentry);
+}
+
+/*-----------
+ * pgstat_progress_end_command() -
+ *
+ * Reset st_progress_command (and st_progress_command_target) in own backend
+ * entry.  This signals the end of the command.
+ *-----------
+ */
+void
+pgstat_progress_end_command(void)
+{
+       volatile PgBackendStatus *beentry = MyBEEntry;
+
+       if (!beentry || !pgstat_track_activities)
+               return;
+
+       if (beentry->st_progress_command == PROGRESS_COMMAND_INVALID)
+               return;
+
+       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
+       beentry->st_progress_command = PROGRESS_COMMAND_INVALID;
+       beentry->st_progress_command_target = InvalidOid;
+       PGSTAT_END_WRITE_ACTIVITY(beentry);
+}
diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c
new file mode 100644 (file)
index 0000000..a25ec0e
--- /dev/null
@@ -0,0 +1,1077 @@
+/* ----------
+ * backend_status.c
+ *       Backend status reporting infrastructure.
+ *
+ * Copyright (c) 2001-2021, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/postmaster/backend_status.c
+ * ----------
+ */
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "libpq/libpq.h"
+#include "miscadmin.h"
+#include "pg_trace.h"
+#include "pgstat.h"
+#include "port/atomics.h" /* for memory barriers */
+#include "storage/ipc.h"
+#include "storage/proc.h" /* for MyProc */
+#include "storage/sinvaladt.h"
+#include "utils/ascii.h"
+#include "utils/backend_status.h"
+#include "utils/guc.h" /* for application_name */
+#include "utils/memutils.h"
+
+
+/* ----------
+ * Total number of backends including auxiliary
+ *
+ * We reserve a slot for each possible BackendId, plus one for each
+ * possible auxiliary process type.  (This scheme assumes there is not
+ * more than one of any auxiliary process type at a time.) MaxBackends
+ * includes autovacuum workers and background workers as well.
+ * ----------
+ */
+#define NumBackendStatSlots (MaxBackends + NUM_AUXPROCTYPES)
+
+
+/* ----------
+ * GUC parameters
+ * ----------
+ */
+bool           pgstat_track_activities = false;
+int                    pgstat_track_activity_query_size = 1024;
+
+
+/* exposed so that progress.c can access it */
+PgBackendStatus *MyBEEntry = NULL;
+
+
+static PgBackendStatus *BackendStatusArray = NULL;
+static char *BackendAppnameBuffer = NULL;
+static char *BackendClientHostnameBuffer = NULL;
+static char *BackendActivityBuffer = NULL;
+static Size BackendActivityBufferSize = 0;
+#ifdef USE_SSL
+static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;
+#endif
+#ifdef ENABLE_GSS
+static PgBackendGSSStatus *BackendGssStatusBuffer = NULL;
+#endif
+
+
+/* Status for backends including auxiliary */
+static LocalPgBackendStatus *localBackendStatusTable = NULL;
+
+/* Total number of backends including auxiliary */
+static int     localNumBackends = 0;
+
+static MemoryContext backendStatusSnapContext;
+
+
+static void pgstat_beshutdown_hook(int code, Datum arg);
+static void pgstat_read_current_status(void);
+static void pgstat_setup_backend_status_context(void);
+
+
+/*
+ * Report shared-memory space needed by CreateSharedBackendStatus.
+ */
+Size
+BackendStatusShmemSize(void)
+{
+       Size            size;
+
+       /* BackendStatusArray: */
+       size = mul_size(sizeof(PgBackendStatus), NumBackendStatSlots);
+       /* BackendAppnameBuffer: */
+       size = add_size(size,
+                                       mul_size(NAMEDATALEN, NumBackendStatSlots));
+       /* BackendClientHostnameBuffer: */
+       size = add_size(size,
+                                       mul_size(NAMEDATALEN, NumBackendStatSlots));
+       /* BackendActivityBuffer: */
+       size = add_size(size,
+                                       mul_size(pgstat_track_activity_query_size, NumBackendStatSlots));
+#ifdef USE_SSL
+       /* BackendSslStatusBuffer: */
+       size = add_size(size,
+                                       mul_size(sizeof(PgBackendSSLStatus), NumBackendStatSlots));
+#endif
+#ifdef ENABLE_GSS
+       /* BackendGssStatusBuffer: */
+       size = add_size(size,
+                                       mul_size(sizeof(PgBackendGSSStatus), NumBackendStatSlots));
+#endif
+       return size;
+}
+
+/*
+ * Initialize the shared status array and several string buffers
+ * during postmaster startup.
+ */
+void
+CreateSharedBackendStatus(void)
+{
+       Size            size;
+       bool            found;
+       int                     i;
+       char       *buffer;
+
+       /* Create or attach to the shared array */
+       size = mul_size(sizeof(PgBackendStatus), NumBackendStatSlots);
+       BackendStatusArray = (PgBackendStatus *)
+               ShmemInitStruct("Backend Status Array", size, &found);
+
+       if (!found)
+       {
+               /*
+                * We're the first - initialize.
+                */
+               MemSet(BackendStatusArray, 0, size);
+       }
+
+       /* Create or attach to the shared appname buffer */
+       size = mul_size(NAMEDATALEN, NumBackendStatSlots);
+       BackendAppnameBuffer = (char *)
+               ShmemInitStruct("Backend Application Name Buffer", size, &found);
+
+       if (!found)
+       {
+               MemSet(BackendAppnameBuffer, 0, size);
+
+               /* Initialize st_appname pointers. */
+               buffer = BackendAppnameBuffer;
+               for (i = 0; i < NumBackendStatSlots; i++)
+               {
+                       BackendStatusArray[i].st_appname = buffer;
+                       buffer += NAMEDATALEN;
+               }
+       }
+
+       /* Create or attach to the shared client hostname buffer */
+       size = mul_size(NAMEDATALEN, NumBackendStatSlots);
+       BackendClientHostnameBuffer = (char *)
+               ShmemInitStruct("Backend Client Host Name Buffer", size, &found);
+
+       if (!found)
+       {
+               MemSet(BackendClientHostnameBuffer, 0, size);
+
+               /* Initialize st_clienthostname pointers. */
+               buffer = BackendClientHostnameBuffer;
+               for (i = 0; i < NumBackendStatSlots; i++)
+               {
+                       BackendStatusArray[i].st_clienthostname = buffer;
+                       buffer += NAMEDATALEN;
+               }
+       }
+
+       /* Create or attach to the shared activity buffer */
+       BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
+                                                                                NumBackendStatSlots);
+       BackendActivityBuffer = (char *)
+               ShmemInitStruct("Backend Activity Buffer",
+                                               BackendActivityBufferSize,
+                                               &found);
+
+       if (!found)
+       {
+               MemSet(BackendActivityBuffer, 0, BackendActivityBufferSize);
+
+               /* Initialize st_activity pointers. */
+               buffer = BackendActivityBuffer;
+               for (i = 0; i < NumBackendStatSlots; i++)
+               {
+                       BackendStatusArray[i].st_activity_raw = buffer;
+                       buffer += pgstat_track_activity_query_size;
+               }
+       }
+
+#ifdef USE_SSL
+       /* Create or attach to the shared SSL status buffer */
+       size = mul_size(sizeof(PgBackendSSLStatus), NumBackendStatSlots);
+       BackendSslStatusBuffer = (PgBackendSSLStatus *)
+               ShmemInitStruct("Backend SSL Status Buffer", size, &found);
+
+       if (!found)
+       {
+               PgBackendSSLStatus *ptr;
+
+               MemSet(BackendSslStatusBuffer, 0, size);
+
+               /* Initialize st_sslstatus pointers. */
+               ptr = BackendSslStatusBuffer;
+               for (i = 0; i < NumBackendStatSlots; i++)
+               {
+                       BackendStatusArray[i].st_sslstatus = ptr;
+                       ptr++;
+               }
+       }
+#endif
+
+#ifdef ENABLE_GSS
+       /* Create or attach to the shared GSSAPI status buffer */
+       size = mul_size(sizeof(PgBackendGSSStatus), NumBackendStatSlots);
+       BackendGssStatusBuffer = (PgBackendGSSStatus *)
+               ShmemInitStruct("Backend GSS Status Buffer", size, &found);
+
+       if (!found)
+       {
+               PgBackendGSSStatus *ptr;
+
+               MemSet(BackendGssStatusBuffer, 0, size);
+
+               /* Initialize st_gssstatus pointers. */
+               ptr = BackendGssStatusBuffer;
+               for (i = 0; i < NumBackendStatSlots; i++)
+               {
+                       BackendStatusArray[i].st_gssstatus = ptr;
+                       ptr++;
+               }
+       }
+#endif
+}
+
+/*
+ * Initialize pgstats backend activity state, and set up our on-proc-exit
+ * hook.  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.
+ */
+void
+pgstat_beinit(void)
+{
+       /* Initialize MyBEEntry */
+       if (MyBackendId != InvalidBackendId)
+       {
+               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 + AuxBackendType + 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() -
+ *
+ *     Initialize this backend's entry in the PgBackendStatus array.
+ *     Called from InitPostgres.
+ *
+ *     Apart from auxiliary processes, MyBackendId, MyDatabaseId,
+ *     session userid, and application_name must be set for a
+ *     backend (hence, this cannot be combined with pgbestat_beinit).
+ *     Note also that we must be inside a transaction if this isn't an aux
+ *     process, as we may need to do encoding conversion on some strings.
+ * ----------
+ */
+void
+pgstat_bestart(void)
+{
+       volatile PgBackendStatus *vbeentry = MyBEEntry;
+       PgBackendStatus lbeentry;
+#ifdef USE_SSL
+       PgBackendSSLStatus lsslstatus;
+#endif
+#ifdef ENABLE_GSS
+       PgBackendGSSStatus lgssstatus;
+#endif
+
+       /* pgstats state must be initialized from pgstat_beinit() */
+       Assert(vbeentry != NULL);
+
+       /*
+        * To minimize the time spent modifying the PgBackendStatus entry, and
+        * avoid risk of errors inside the critical section, we first copy the
+        * shared-memory struct to a local variable, then modify the data in the
+        * local variable, then copy the local variable back to shared memory.
+        * Only the last step has to be inside the critical section.
+        *
+        * Most of the data we copy from shared memory is just going to be
+        * overwritten, but the struct's not so large that it's worth the
+        * maintenance hassle to copy only the needful fields.
+        */
+       memcpy(&lbeentry,
+                  unvolatize(PgBackendStatus *, vbeentry),
+                  sizeof(PgBackendStatus));
+
+       /* These structs can just start from zeroes each time, though */
+#ifdef USE_SSL
+       memset(&lsslstatus, 0, sizeof(lsslstatus));
+#endif
+#ifdef ENABLE_GSS
+       memset(&lgssstatus, 0, sizeof(lgssstatus));
+#endif
+
+       /*
+        * Now fill in all the fields of lbeentry, except for strings that are
+        * out-of-line data.  Those have to be handled separately, below.
+        */
+       lbeentry.st_procpid = MyProcPid;
+       lbeentry.st_backendType = MyBackendType;
+       lbeentry.st_proc_start_timestamp = MyStartTimestamp;
+       lbeentry.st_activity_start_timestamp = 0;
+       lbeentry.st_state_start_timestamp = 0;
+       lbeentry.st_xact_start_timestamp = 0;
+       lbeentry.st_databaseid = MyDatabaseId;
+
+       /* We have userid for client-backends, wal-sender and bgworker processes */
+       if (lbeentry.st_backendType == B_BACKEND
+               || lbeentry.st_backendType == B_WAL_SENDER
+               || lbeentry.st_backendType == B_BG_WORKER)
+               lbeentry.st_userid = GetSessionUserId();
+       else
+               lbeentry.st_userid = InvalidOid;
+
+       /*
+        * We may not have a MyProcPort (eg, if this is the autovacuum process).
+        * If so, use all-zeroes client address, which is dealt with specially in
+        * pg_stat_get_backend_client_addr and pg_stat_get_backend_client_port.
+        */
+       if (MyProcPort)
+               memcpy(&lbeentry.st_clientaddr, &MyProcPort->raddr,
+                          sizeof(lbeentry.st_clientaddr));
+       else
+               MemSet(&lbeentry.st_clientaddr, 0, sizeof(lbeentry.st_clientaddr));
+
+#ifdef USE_SSL
+       if (MyProcPort && MyProcPort->ssl_in_use)
+       {
+               lbeentry.st_ssl = true;
+               lsslstatus.ssl_bits = be_tls_get_cipher_bits(MyProcPort);
+               strlcpy(lsslstatus.ssl_version, be_tls_get_version(MyProcPort), NAMEDATALEN);
+               strlcpy(lsslstatus.ssl_cipher, be_tls_get_cipher(MyProcPort), NAMEDATALEN);
+               be_tls_get_peer_subject_name(MyProcPort, lsslstatus.ssl_client_dn, NAMEDATALEN);
+               be_tls_get_peer_serial(MyProcPort, lsslstatus.ssl_client_serial, NAMEDATALEN);
+               be_tls_get_peer_issuer_name(MyProcPort, lsslstatus.ssl_issuer_dn, NAMEDATALEN);
+       }
+       else
+       {
+               lbeentry.st_ssl = false;
+       }
+#else
+       lbeentry.st_ssl = false;
+#endif
+
+#ifdef ENABLE_GSS
+       if (MyProcPort && MyProcPort->gss != NULL)
+       {
+               const char *princ = be_gssapi_get_princ(MyProcPort);
+
+               lbeentry.st_gss = true;
+               lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort);
+               lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort);
+               if (princ)
+                       strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN);
+       }
+       else
+       {
+               lbeentry.st_gss = false;
+       }
+#else
+       lbeentry.st_gss = false;
+#endif
+
+       lbeentry.st_state = STATE_UNDEFINED;
+       lbeentry.st_progress_command = PROGRESS_COMMAND_INVALID;
+       lbeentry.st_progress_command_target = InvalidOid;
+
+       /*
+        * we don't zero st_progress_param here to save cycles; nobody should
+        * examine it until st_progress_command has been set to something other
+        * than PROGRESS_COMMAND_INVALID
+        */
+
+       /*
+        * We're ready to enter the critical section that fills the shared-memory
+        * status entry.  We follow the protocol of bumping st_changecount before
+        * and after; and make sure it's even afterwards.  We use a volatile
+        * pointer here to ensure the compiler doesn't try to get cute.
+        */
+       PGSTAT_BEGIN_WRITE_ACTIVITY(vbeentry);
+
+       /* make sure we'll memcpy the same st_changecount back */
+       lbeentry.st_changecount = vbeentry->st_changecount;
+
+       memcpy(unvolatize(PgBackendStatus *, vbeentry),
+                  &lbeentry,
+                  sizeof(PgBackendStatus));
+
+       /*
+        * We can write the out-of-line strings and structs using the pointers
+        * that are in lbeentry; this saves some de-volatilizing messiness.
+        */
+       lbeentry.st_appname[0] = '\0';
+       if (MyProcPort && MyProcPort->remote_hostname)
+               strlcpy(lbeentry.st_clienthostname, MyProcPort->remote_hostname,
+                               NAMEDATALEN);
+       else
+               lbeentry.st_clienthostname[0] = '\0';
+       lbeentry.st_activity_raw[0] = '\0';
+       /* Also make sure the last byte in each string area is always 0 */
+       lbeentry.st_appname[NAMEDATALEN - 1] = '\0';
+       lbeentry.st_clienthostname[NAMEDATALEN - 1] = '\0';
+       lbeentry.st_activity_raw[pgstat_track_activity_query_size - 1] = '\0';
+
+#ifdef USE_SSL
+       memcpy(lbeentry.st_sslstatus, &lsslstatus, sizeof(PgBackendSSLStatus));
+#endif
+#ifdef ENABLE_GSS
+       memcpy(lbeentry.st_gssstatus, &lgssstatus, sizeof(PgBackendGSSStatus));
+#endif
+
+       PGSTAT_END_WRITE_ACTIVITY(vbeentry);
+
+       /* Update app name to current GUC setting */
+       if (application_name)
+               pgstat_report_appname(application_name);
+}
+
+/*
+ * Clear out our entry in the PgBackendStatus array.
+ */
+static void
+pgstat_beshutdown_hook(int code, Datum arg)
+{
+       volatile PgBackendStatus *beentry = MyBEEntry;
+
+       /*
+        * Clear my status entry, following the protocol of bumping st_changecount
+        * before and after.  We use a volatile pointer here to ensure the
+        * compiler doesn't try to get cute.
+        */
+       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
+
+       beentry->st_procpid = 0;        /* mark invalid */
+
+       PGSTAT_END_WRITE_ACTIVITY(beentry);
+}
+
+/*
+ * Discard any data collected in the current transaction.  Any subsequent
+ * request will cause new snapshots to be read.
+ *
+ * This is also invoked during transaction commit or abort to discard the
+ * no-longer-wanted snapshot.
+ */
+void
+pgstat_clear_backend_activity_snapshot(void)
+{
+       /* Release memory, if any was allocated */
+       if (backendStatusSnapContext)
+       {
+               MemoryContextDelete(backendStatusSnapContext);
+               backendStatusSnapContext = NULL;
+       }
+
+       /* Reset variables */
+       localBackendStatusTable = NULL;
+       localNumBackends = 0;
+}
+
+static void
+pgstat_setup_backend_status_context(void)
+{
+       if (!backendStatusSnapContext)
+               backendStatusSnapContext = AllocSetContextCreate(TopMemoryContext,
+                                                                                                        "Backend Status Snapshot",
+                                                                                                        ALLOCSET_SMALL_SIZES);
+}
+
+
+/* ----------
+ * pgstat_report_activity() -
+ *
+ *     Called from tcop/postgres.c to report what the backend is actually doing
+ *     (but note cmd_str can be NULL for certain cases).
+ *
+ * All updates of the status entry follow the protocol of bumping
+ * st_changecount before and after.  We use a volatile pointer here to
+ * ensure the compiler doesn't try to get cute.
+ * ----------
+ */
+void
+pgstat_report_activity(BackendState state, const char *cmd_str)
+{
+       volatile PgBackendStatus *beentry = MyBEEntry;
+       TimestampTz start_timestamp;
+       TimestampTz current_timestamp;
+       int                     len = 0;
+
+       TRACE_POSTGRESQL_STATEMENT_STATUS(cmd_str);
+
+       if (!beentry)
+               return;
+
+       if (!pgstat_track_activities)
+       {
+               if (beentry->st_state != STATE_DISABLED)
+               {
+                       volatile PGPROC *proc = MyProc;
+
+                       /*
+                        * track_activities is disabled, but we last reported a
+                        * non-disabled state.  As our final update, change the state and
+                        * clear fields we will not be updating anymore.
+                        */
+                       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
+                       beentry->st_state = STATE_DISABLED;
+                       beentry->st_state_start_timestamp = 0;
+                       beentry->st_activity_raw[0] = '\0';
+                       beentry->st_activity_start_timestamp = 0;
+                       /* st_xact_start_timestamp and wait_event_info are also disabled */
+                       beentry->st_xact_start_timestamp = 0;
+                       proc->wait_event_info = 0;
+                       PGSTAT_END_WRITE_ACTIVITY(beentry);
+               }
+               return;
+       }
+
+       /*
+        * To minimize the time spent modifying the entry, and avoid risk of
+        * errors inside the critical section, fetch all the needed data first.
+        */
+       start_timestamp = GetCurrentStatementStartTimestamp();
+       if (cmd_str != NULL)
+       {
+               /*
+                * Compute length of to-be-stored string unaware of multi-byte
+                * characters. For speed reasons that'll get corrected on read, rather
+                * than computed every write.
+                */
+               len = Min(strlen(cmd_str), pgstat_track_activity_query_size - 1);
+       }
+       current_timestamp = GetCurrentTimestamp();
+
+       /*
+        * If the state has changed from "active" or "idle in transaction",
+        * calculate the duration.
+        */
+       if ((beentry->st_state == STATE_RUNNING ||
+                beentry->st_state == STATE_FASTPATH ||
+                beentry->st_state == STATE_IDLEINTRANSACTION ||
+                beentry->st_state == STATE_IDLEINTRANSACTION_ABORTED) &&
+               state != beentry->st_state)
+       {
+               long            secs;
+               int                     usecs;
+
+               TimestampDifference(beentry->st_state_start_timestamp,
+                                                       current_timestamp,
+                                                       &secs, &usecs);
+
+               if (beentry->st_state == STATE_RUNNING ||
+                       beentry->st_state == STATE_FASTPATH)
+                       pgstat_count_conn_active_time(secs * 1000000 + usecs);
+               else
+                       pgstat_count_conn_txn_idle_time(secs * 1000000 + usecs);
+       }
+
+       /*
+        * Now update the status entry
+        */
+       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
+
+       beentry->st_state = state;
+       beentry->st_state_start_timestamp = current_timestamp;
+
+       if (cmd_str != NULL)
+       {
+               memcpy((char *) beentry->st_activity_raw, cmd_str, len);
+               beentry->st_activity_raw[len] = '\0';
+               beentry->st_activity_start_timestamp = start_timestamp;
+       }
+
+       PGSTAT_END_WRITE_ACTIVITY(beentry);
+}
+
+/* ----------
+ * pgstat_report_appname() -
+ *
+ *     Called to update our application name.
+ * ----------
+ */
+void
+pgstat_report_appname(const char *appname)
+{
+       volatile PgBackendStatus *beentry = MyBEEntry;
+       int                     len;
+
+       if (!beentry)
+               return;
+
+       /* This should be unnecessary if GUC did its job, but be safe */
+       len = pg_mbcliplen(appname, strlen(appname), NAMEDATALEN - 1);
+
+       /*
+        * Update my status entry, following the protocol of bumping
+        * st_changecount before and after.  We use a volatile pointer here to
+        * ensure the compiler doesn't try to get cute.
+        */
+       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
+
+       memcpy((char *) beentry->st_appname, appname, len);
+       beentry->st_appname[len] = '\0';
+
+       PGSTAT_END_WRITE_ACTIVITY(beentry);
+}
+
+/*
+ * Report current transaction start timestamp as the specified value.
+ * Zero means there is no active transaction.
+ */
+void
+pgstat_report_xact_timestamp(TimestampTz tstamp)
+{
+       volatile PgBackendStatus *beentry = MyBEEntry;
+
+       if (!pgstat_track_activities || !beentry)
+               return;
+
+       /*
+        * Update my status entry, following the protocol of bumping
+        * st_changecount before and after.  We use a volatile pointer here to
+        * ensure the compiler doesn't try to get cute.
+        */
+       PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
+
+       beentry->st_xact_start_timestamp = tstamp;
+
+       PGSTAT_END_WRITE_ACTIVITY(beentry);
+}
+
+/* ----------
+ * pgstat_read_current_status() -
+ *
+ *     Copy the current contents of the PgBackendStatus array to local memory,
+ *     if not already done in this transaction.
+ * ----------
+ */
+static void
+pgstat_read_current_status(void)
+{
+       volatile PgBackendStatus *beentry;
+       LocalPgBackendStatus *localtable;
+       LocalPgBackendStatus *localentry;
+       char       *localappname,
+                          *localclienthostname,
+                          *localactivity;
+#ifdef USE_SSL
+       PgBackendSSLStatus *localsslstatus;
+#endif
+#ifdef ENABLE_GSS
+       PgBackendGSSStatus *localgssstatus;
+#endif
+       int                     i;
+
+       if (localBackendStatusTable)
+               return;                                 /* already done */
+
+       pgstat_setup_backend_status_context();
+
+       /*
+        * Allocate storage for local copy of state data.  We can presume that
+        * none of these requests overflow size_t, because we already calculated
+        * the same values using mul_size during shmem setup.  However, with
+        * probably-silly values of pgstat_track_activity_query_size and
+        * max_connections, the localactivity buffer could exceed 1GB, so use
+        * "huge" allocation for that one.
+        */
+       localtable = (LocalPgBackendStatus *)
+               MemoryContextAlloc(backendStatusSnapContext,
+                                                  sizeof(LocalPgBackendStatus) * NumBackendStatSlots);
+       localappname = (char *)
+               MemoryContextAlloc(backendStatusSnapContext,
+                                                  NAMEDATALEN * NumBackendStatSlots);
+       localclienthostname = (char *)
+               MemoryContextAlloc(backendStatusSnapContext,
+                                                  NAMEDATALEN * NumBackendStatSlots);
+       localactivity = (char *)
+               MemoryContextAllocHuge(backendStatusSnapContext,
+                                                          pgstat_track_activity_query_size * NumBackendStatSlots);
+#ifdef USE_SSL
+       localsslstatus = (PgBackendSSLStatus *)
+               MemoryContextAlloc(backendStatusSnapContext,
+                                                  sizeof(PgBackendSSLStatus) * NumBackendStatSlots);
+#endif
+#ifdef ENABLE_GSS
+       localgssstatus = (PgBackendGSSStatus *)
+               MemoryContextAlloc(backendStatusSnapContext,
+                                                  sizeof(PgBackendGSSStatus) * NumBackendStatSlots);
+#endif
+
+       localNumBackends = 0;
+
+       beentry = BackendStatusArray;
+       localentry = localtable;
+       for (i = 1; i <= NumBackendStatSlots; i++)
+       {
+               /*
+                * Follow the protocol of retrying if st_changecount changes while we
+                * copy the entry, or if it's odd.  (The check for odd is needed to
+                * cover the case where we are able to completely copy the entry while
+                * the source backend is between increment steps.)      We use a volatile
+                * pointer here to ensure the compiler doesn't try to get cute.
+                */
+               for (;;)
+               {
+                       int                     before_changecount;
+                       int                     after_changecount;
+
+                       pgstat_begin_read_activity(beentry, before_changecount);
+
+                       localentry->backendStatus.st_procpid = beentry->st_procpid;
+                       /* Skip all the data-copying work if entry is not in use */
+                       if (localentry->backendStatus.st_procpid > 0)
+                       {
+                               memcpy(&localentry->backendStatus, unvolatize(PgBackendStatus *, beentry), sizeof(PgBackendStatus));
+
+                               /*
+                                * For each PgBackendStatus field that is a pointer, copy the
+                                * pointed-to data, then adjust the local copy of the pointer
+                                * field to point at the local copy of the data.
+                                *
+                                * 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);
+                               localentry->backendStatus.st_appname = localappname;
+                               strcpy(localclienthostname, (char *) beentry->st_clienthostname);
+                               localentry->backendStatus.st_clienthostname = localclienthostname;
+                               strcpy(localactivity, (char *) beentry->st_activity_raw);
+                               localentry->backendStatus.st_activity_raw = localactivity;
+#ifdef USE_SSL
+                               if (beentry->st_ssl)
+                               {
+                                       memcpy(localsslstatus, beentry->st_sslstatus, sizeof(PgBackendSSLStatus));
+                                       localentry->backendStatus.st_sslstatus = localsslstatus;
+                               }
+#endif
+#ifdef ENABLE_GSS
+                               if (beentry->st_gss)
+                               {
+                                       memcpy(localgssstatus, beentry->st_gssstatus, sizeof(PgBackendGSSStatus));
+                                       localentry->backendStatus.st_gssstatus = localgssstatus;
+                               }
+#endif
+                       }
+
+                       pgstat_end_read_activity(beentry, after_changecount);
+
+                       if (pgstat_read_activity_complete(before_changecount,
+                                                                                         after_changecount))
+                               break;
+
+                       /* Make sure we can break out of loop if stuck... */
+                       CHECK_FOR_INTERRUPTS();
+               }
+
+               beentry++;
+               /* Only valid entries get included into the local array */
+               if (localentry->backendStatus.st_procpid > 0)
+               {
+                       BackendIdGetTransactionIds(i,
+                                                                          &localentry->backend_xid,
+                                                                          &localentry->backend_xmin);
+
+                       localentry++;
+                       localappname += NAMEDATALEN;
+                       localclienthostname += NAMEDATALEN;
+                       localactivity += pgstat_track_activity_query_size;
+#ifdef USE_SSL
+                       localsslstatus++;
+#endif
+#ifdef ENABLE_GSS
+                       localgssstatus++;
+#endif
+                       localNumBackends++;
+               }
+       }
+
+       /* Set the pointer only after completion of a valid table */
+       localBackendStatusTable = localtable;
+}
+
+
+/* ----------
+ * pgstat_get_backend_current_activity() -
+ *
+ *     Return a string representing the current activity of the backend with
+ *     the specified PID.  This looks directly at the BackendStatusArray,
+ *     and so will provide current information regardless of the age of our
+ *     transaction's snapshot of the status array.
+ *
+ *     It is the caller's responsibility to invoke this only for backends whose
+ *     state is expected to remain stable while the result is in use.  The
+ *     only current use is in deadlock reporting, where we can expect that
+ *     the target backend is blocked on a lock.  (There are corner cases
+ *     where the target's wait could get aborted while we are looking at it,
+ *     but the very worst consequence is to return a pointer to a string
+ *     that's been changed, so we won't worry too much.)
+ *
+ *     Note: return strings for special cases match pg_stat_get_backend_activity.
+ * ----------
+ */
+const char *
+pgstat_get_backend_current_activity(int pid, bool checkUser)
+{
+       PgBackendStatus *beentry;
+       int                     i;
+
+       beentry = BackendStatusArray;
+       for (i = 1; i <= MaxBackends; i++)
+       {
+               /*
+                * Although we expect the target backend's entry to be stable, that
+                * doesn't imply that anyone else's is.  To avoid identifying the
+                * wrong backend, while we check for a match to the desired PID we
+                * must follow the protocol of retrying if st_changecount changes
+                * while we examine the entry, or if it's odd.  (This might be
+                * unnecessary, since fetching or storing an int is almost certainly
+                * atomic, but let's play it safe.)  We use a volatile pointer here to
+                * ensure the compiler doesn't try to get cute.
+                */
+               volatile PgBackendStatus *vbeentry = beentry;
+               bool            found;
+
+               for (;;)
+               {
+                       int                     before_changecount;
+                       int                     after_changecount;
+
+                       pgstat_begin_read_activity(vbeentry, before_changecount);
+
+                       found = (vbeentry->st_procpid == pid);
+
+                       pgstat_end_read_activity(vbeentry, after_changecount);
+
+                       if (pgstat_read_activity_complete(before_changecount,
+                                                                                         after_changecount))
+                               break;
+
+                       /* Make sure we can break out of loop if stuck... */
+                       CHECK_FOR_INTERRUPTS();
+               }
+
+               if (found)
+               {
+                       /* Now it is safe to use the non-volatile pointer */
+                       if (checkUser && !superuser() && beentry->st_userid != GetUserId())
+                               return "<insufficient privilege>";
+                       else if (*(beentry->st_activity_raw) == '\0')
+                               return "<command string not enabled>";
+                       else
+                       {
+                               /* this'll leak a bit of memory, but that seems acceptable */
+                               return pgstat_clip_activity(beentry->st_activity_raw);
+                       }
+               }
+
+               beentry++;
+       }
+
+       /* If we get here, caller is in error ... */
+       return "<backend information not available>";
+}
+
+/* ----------
+ * pgstat_get_crashed_backend_activity() -
+ *
+ *     Return a string representing the current activity of the backend with
+ *     the specified PID.  Like the function above, but reads shared memory with
+ *     the expectation that it may be corrupt.  On success, copy the string
+ *     into the "buffer" argument and return that pointer.  On failure,
+ *     return NULL.
+ *
+ *     This function is only intended to be used by the postmaster to report the
+ *     query that crashed a backend.  In particular, no attempt is made to
+ *     follow the correct concurrency protocol when accessing the
+ *     BackendStatusArray.  But that's OK, in the worst case we'll return a
+ *     corrupted message.  We also must take care not to trip on ereport(ERROR).
+ * ----------
+ */
+const char *
+pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen)
+{
+       volatile PgBackendStatus *beentry;
+       int                     i;
+
+       beentry = BackendStatusArray;
+
+       /*
+        * We probably shouldn't get here before shared memory has been set up,
+        * but be safe.
+        */
+       if (beentry == NULL || BackendActivityBuffer == NULL)
+               return NULL;
+
+       for (i = 1; i <= MaxBackends; i++)
+       {
+               if (beentry->st_procpid == pid)
+               {
+                       /* Read pointer just once, so it can't change after validation */
+                       const char *activity = beentry->st_activity_raw;
+                       const char *activity_last;
+
+                       /*
+                        * We mustn't access activity string before we verify that it
+                        * falls within the BackendActivityBuffer. To make sure that the
+                        * entire string including its ending is contained within the
+                        * buffer, subtract one activity length from the buffer size.
+                        */
+                       activity_last = BackendActivityBuffer + BackendActivityBufferSize
+                               - pgstat_track_activity_query_size;
+
+                       if (activity < BackendActivityBuffer ||
+                               activity > activity_last)
+                               return NULL;
+
+                       /* If no string available, no point in a report */
+                       if (activity[0] == '\0')
+                               return NULL;
+
+                       /*
+                        * Copy only ASCII-safe characters so we don't run into encoding
+                        * problems when reporting the message; and be sure not to run off
+                        * the end of memory.  As only ASCII characters are reported, it
+                        * doesn't seem necessary to perform multibyte aware clipping.
+                        */
+                       ascii_safe_strlcpy(buffer, activity,
+                                                          Min(buflen, pgstat_track_activity_query_size));
+
+                       return buffer;
+               }
+
+               beentry++;
+       }
+
+       /* PID not found */
+       return NULL;
+}
+
+
+/* ----------
+ * pgstat_fetch_stat_beentry() -
+ *
+ *     Support function for the SQL-callable pgstat* functions. Returns
+ *     our local copy of the current-activity entry for one backend.
+ *
+ *     NB: caller is responsible for a check if the user is permitted to see
+ *     this info (especially the querystring).
+ * ----------
+ */
+PgBackendStatus *
+pgstat_fetch_stat_beentry(int beid)
+{
+       pgstat_read_current_status();
+
+       if (beid < 1 || beid > localNumBackends)
+               return NULL;
+
+       return &localBackendStatusTable[beid - 1].backendStatus;
+}
+
+
+/* ----------
+ * pgstat_fetch_stat_local_beentry() -
+ *
+ *     Like pgstat_fetch_stat_beentry() 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_beentry(int beid)
+{
+       pgstat_read_current_status();
+
+       if (beid < 1 || beid > localNumBackends)
+               return NULL;
+
+       return &localBackendStatusTable[beid - 1];
+}
+
+
+/* ----------
+ * pgstat_fetch_stat_numbackends() -
+ *
+ *     Support function for the SQL-callable pgstat* functions. Returns
+ *     the maximum current backend id.
+ * ----------
+ */
+int
+pgstat_fetch_stat_numbackends(void)
+{
+       pgstat_read_current_status();
+
+       return localNumBackends;
+}
+
+/*
+ * Convert a potentially unsafely truncated activity string (see
+ * PgBackendStatus.st_activity_raw's documentation) into a correctly truncated
+ * one.
+ *
+ * The returned string is allocated in the caller's memory context and may be
+ * freed.
+ */
+char *
+pgstat_clip_activity(const char *raw_activity)
+{
+       char       *activity;
+       int                     rawlen;
+       int                     cliplen;
+
+       /*
+        * Some callers, like pgstat_get_backend_current_activity(), do not
+        * guarantee that the buffer isn't concurrently modified. We try to take
+        * care that the buffer is always terminated by a NUL byte regardless, but
+        * let's still be paranoid about the string's length. In those cases the
+        * underlying buffer is guaranteed to be pgstat_track_activity_query_size
+        * large.
+        */
+       activity = pnstrdup(raw_activity, pgstat_track_activity_query_size - 1);
+
+       /* now double-guaranteed to be NUL terminated */
+       rawlen = strlen(activity);
+
+       /*
+        * All supported server-encodings make it possible to determine the length
+        * of a multi-byte character from its first byte (this is not the case for
+        * client encodings, see GB18030). As st_activity is always stored using
+        * server encoding, this allows us to perform multi-byte aware truncation,
+        * even if the string earlier was truncated in the middle of a multi-byte
+        * character.
+        */
+       cliplen = pg_mbcliplen(activity, rawlen,
+                                                  pgstat_track_activity_query_size - 1);
+
+       activity[cliplen] = '\0';
+
+       return activity;
+}
index a3ec358538a6542251b6359e0a7426611c84eece..51d1bbef301ae66b90ccfcfa83d4a4a83659ba8f 100644 (file)
@@ -681,6 +681,10 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
        if (!bootstrap)
                pgstat_initialize();
 
+       /* Initialize status reporting */
+       if (!bootstrap)
+               pgstat_beinit();
+
        /*
         * Load relcache entries for the shared system catalogs.  This must create
         * at least entries for pg_database and catalogs used for authentication.
index 60a9c7a2a0b5187c24b9731db462e6cd05ef3e7d..c9c9da85f3943b4cc9183094ce20483402e64c08 100644 (file)
@@ -90,6 +90,7 @@
 #include "tcop/tcopprot.h"
 #include "tsearch/ts_cache.h"
 #include "utils/acl.h"
+#include "utils/backend_status.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
 #include "utils/float.h"
index c6b139d57d3dde25ec47606131c57b86dde4a455..d7bf16368bd7ac316e47355c701d1e613b592dd0 100644 (file)
@@ -2,7 +2,7 @@
  *
  * progress.h
  *       Constants used with the progress reporting facilities defined in
- *       pgstat.h.  These are possibly interesting to extensions, so we
+ *       backend_status.h.  These are possibly interesting to extensions, so we
  *       expose them via this header file.  Note that if you update these
  *       constants, you probably also need to update the views based on them
  *       in system_views.sql.
index 3247c7b8ad0d26420c137765bd72e0ccd548773d..7cd137506e2b43560962bea0cc597722ca29bc57 100644 (file)
 #define PGSTAT_H
 
 #include "datatype/timestamp.h"
-#include "libpq/pqcomm.h"
-#include "miscadmin.h"
-#include "port/atomics.h"
 #include "portability/instr_time.h"
-#include "postmaster/pgarch.h"
+#include "postmaster/pgarch.h" /* for MAX_XFN_CHARS */
+#include "utils/backend_progress.h" /* for backward compatibility */
+#include "utils/backend_status.h" /* for backward compatibility */
 #include "utils/hsearch.h"
 #include "utils/relcache.h"
 #include "utils/wait_event.h" /* for backward compatibility */
@@ -882,262 +881,6 @@ typedef struct PgStat_ReplSlotStats
        TimestampTz stat_reset_timestamp;
 } PgStat_ReplSlotStats;
 
-/* ----------
- * Backend states
- * ----------
- */
-typedef enum BackendState
-{
-       STATE_UNDEFINED,
-       STATE_IDLE,
-       STATE_RUNNING,
-       STATE_IDLEINTRANSACTION,
-       STATE_FASTPATH,
-       STATE_IDLEINTRANSACTION_ABORTED,
-       STATE_DISABLED
-} BackendState;
-
-/* ----------
- * Command type for progress reporting purposes
- * ----------
- */
-typedef enum ProgressCommandType
-{
-       PROGRESS_COMMAND_INVALID,
-       PROGRESS_COMMAND_VACUUM,
-       PROGRESS_COMMAND_ANALYZE,
-       PROGRESS_COMMAND_CLUSTER,
-       PROGRESS_COMMAND_CREATE_INDEX,
-       PROGRESS_COMMAND_BASEBACKUP,
-       PROGRESS_COMMAND_COPY
-} ProgressCommandType;
-
-#define PGSTAT_NUM_PROGRESS_PARAM      20
-
-/* ----------
- * Shared-memory data structures
- * ----------
- */
-
-
-/*
- * PgBackendSSLStatus
- *
- * For each backend, we keep the SSL status in a separate struct, that
- * is only filled in if SSL is enabled.
- *
- * All char arrays must be null-terminated.
- */
-typedef struct PgBackendSSLStatus
-{
-       /* Information about SSL connection */
-       int                     ssl_bits;
-       char            ssl_version[NAMEDATALEN];
-       char            ssl_cipher[NAMEDATALEN];
-       char            ssl_client_dn[NAMEDATALEN];
-
-       /*
-        * serial number is max "20 octets" per RFC 5280, so this size should be
-        * fine
-        */
-       char            ssl_client_serial[NAMEDATALEN];
-
-       char            ssl_issuer_dn[NAMEDATALEN];
-} PgBackendSSLStatus;
-
-/*
- * PgBackendGSSStatus
- *
- * For each backend, we keep the GSS status in a separate struct, that
- * is only filled in if GSS is enabled.
- *
- * All char arrays must be null-terminated.
- */
-typedef struct PgBackendGSSStatus
-{
-       /* Information about GSSAPI connection */
-       char            gss_princ[NAMEDATALEN]; /* GSSAPI Principal used to auth */
-       bool            gss_auth;               /* If GSSAPI authentication was used */
-       bool            gss_enc;                /* If encryption is being used */
-
-} PgBackendGSSStatus;
-
-
-/* ----------
- * PgBackendStatus
- *
- * Each live backend maintains a PgBackendStatus struct in shared memory
- * 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 auxiliary process also maintains a PgBackendStatus struct in shared
- * memory.
- * ----------
- */
-typedef struct PgBackendStatus
-{
-       /*
-        * To avoid locking overhead, we use the following protocol: a backend
-        * increments st_changecount before modifying its entry, and again after
-        * finishing a modification.  A would-be reader should note the value of
-        * st_changecount, copy the entry into private memory, then check
-        * st_changecount again.  If the value hasn't changed, and if it's even,
-        * the copy is valid; otherwise start over.  This makes updates cheap
-        * while reads are potentially expensive, but that's the tradeoff we want.
-        *
-        * The above protocol needs memory barriers to ensure that the apparent
-        * order of execution is as it desires.  Otherwise, for example, the CPU
-        * might rearrange the code so that st_changecount is incremented twice
-        * before the modification on a machine with weak memory ordering.  Hence,
-        * use the macros defined below for manipulating st_changecount, rather
-        * than touching it directly.
-        */
-       int                     st_changecount;
-
-       /* The entry is valid iff st_procpid > 0, unused if st_procpid == 0 */
-       int                     st_procpid;
-
-       /* Type of backends */
-       BackendType st_backendType;
-
-       /* Times when current backend, transaction, and activity started */
-       TimestampTz st_proc_start_timestamp;
-       TimestampTz st_xact_start_timestamp;
-       TimestampTz st_activity_start_timestamp;
-       TimestampTz st_state_start_timestamp;
-
-       /* Database OID, owning user's OID, connection client address */
-       Oid                     st_databaseid;
-       Oid                     st_userid;
-       SockAddr        st_clientaddr;
-       char       *st_clienthostname;  /* MUST be null-terminated */
-
-       /* Information about SSL connection */
-       bool            st_ssl;
-       PgBackendSSLStatus *st_sslstatus;
-
-       /* Information about GSSAPI connection */
-       bool            st_gss;
-       PgBackendGSSStatus *st_gssstatus;
-
-       /* current state */
-       BackendState st_state;
-
-       /* application name; MUST be null-terminated */
-       char       *st_appname;
-
-       /*
-        * Current command string; MUST be null-terminated. Note that this string
-        * possibly is truncated in the middle of a multi-byte character. As
-        * activity strings are stored more frequently than read, that allows to
-        * move the cost of correct truncation to the display side. Use
-        * pgstat_clip_activity() to truncate correctly.
-        */
-       char       *st_activity_raw;
-
-       /*
-        * Command progress reporting.  Any command which wishes can advertise
-        * that it is running by setting st_progress_command,
-        * st_progress_command_target, and st_progress_param[].
-        * st_progress_command_target should be the OID of the relation which the
-        * command targets (we assume there's just one, as this is meant for
-        * utility commands), but the meaning of each element in the
-        * st_progress_param array is command-specific.
-        */
-       ProgressCommandType st_progress_command;
-       Oid                     st_progress_command_target;
-       int64           st_progress_param[PGSTAT_NUM_PROGRESS_PARAM];
-} PgBackendStatus;
-
-/*
- * Macros to load and store st_changecount with appropriate memory barriers.
- *
- * Use PGSTAT_BEGIN_WRITE_ACTIVITY() before, and PGSTAT_END_WRITE_ACTIVITY()
- * after, modifying the current process's PgBackendStatus data.  Note that,
- * since there is no mechanism for cleaning up st_changecount after an error,
- * THESE MACROS FORM A CRITICAL SECTION.  Any error between them will be
- * promoted to PANIC, causing a database restart to clean up shared memory!
- * Hence, keep the critical section as short and straight-line as possible.
- * Aside from being safer, that minimizes the window in which readers will
- * have to loop.
- *
- * Reader logic should follow this sketch:
- *
- *             for (;;)
- *             {
- *                     int before_ct, after_ct;
- *
- *                     pgstat_begin_read_activity(beentry, before_ct);
- *                     ... copy beentry data to local memory ...
- *                     pgstat_end_read_activity(beentry, after_ct);
- *                     if (pgstat_read_activity_complete(before_ct, after_ct))
- *                             break;
- *                     CHECK_FOR_INTERRUPTS();
- *             }
- *
- * For extra safety, we generally use volatile beentry pointers, although
- * the memory barriers should theoretically be sufficient.
- */
-#define PGSTAT_BEGIN_WRITE_ACTIVITY(beentry) \
-       do { \
-               START_CRIT_SECTION(); \
-               (beentry)->st_changecount++; \
-               pg_write_barrier(); \
-       } while (0)
-
-#define PGSTAT_END_WRITE_ACTIVITY(beentry) \
-       do { \
-               pg_write_barrier(); \
-               (beentry)->st_changecount++; \
-               Assert(((beentry)->st_changecount & 1) == 0); \
-               END_CRIT_SECTION(); \
-       } while (0)
-
-#define pgstat_begin_read_activity(beentry, before_changecount) \
-       do { \
-               (before_changecount) = (beentry)->st_changecount; \
-               pg_read_barrier(); \
-       } while (0)
-
-#define pgstat_end_read_activity(beentry, after_changecount) \
-       do { \
-               pg_read_barrier(); \
-               (after_changecount) = (beentry)->st_changecount; \
-       } while (0)
-
-#define pgstat_read_activity_complete(before_changecount, after_changecount) \
-       ((before_changecount) == (after_changecount) && \
-        ((before_changecount) & 1) == 0)
-
-
-/* ----------
- * LocalPgBackendStatus
- *
- * When we build the backend status array, we use LocalPgBackendStatus to be
- * able to add new values to the struct when needed without adding new fields
- * to the shared memory. It contains the backend status as a first member.
- * ----------
- */
-typedef struct LocalPgBackendStatus
-{
-       /*
-        * Local version of the backend status entry.
-        */
-       PgBackendStatus backendStatus;
-
-       /*
-        * The xid of the current transaction if available, InvalidTransactionId
-        * if not.
-        */
-       TransactionId backend_xid;
-
-       /*
-        * The xmin of the current session if available, InvalidTransactionId if
-        * not.
-        */
-       TransactionId backend_xmin;
-} LocalPgBackendStatus;
 
 /*
  * Working state needed to accumulate per-function-call timing statistics.
@@ -1160,10 +903,8 @@ typedef struct PgStat_FunctionCallUsage
  * GUC parameters
  * ----------
  */
-extern PGDLLIMPORT bool pgstat_track_activities;
 extern PGDLLIMPORT bool pgstat_track_counts;
 extern PGDLLIMPORT int pgstat_track_functions;
-extern PGDLLIMPORT int pgstat_track_activity_query_size;
 extern char *pgstat_stat_directory;
 extern char *pgstat_stat_tmpname;
 extern char *pgstat_stat_filename;
@@ -1184,6 +925,14 @@ extern PgStat_MsgWal WalStats;
 extern PgStat_Counter pgStatBlockReadTime;
 extern PgStat_Counter pgStatBlockWriteTime;
 
+/*
+ * Updated by pgstat_count_conn_*_time macros, called by
+ * pgstat_report_activity().
+ */
+extern PgStat_Counter pgStatActiveTime;
+extern PgStat_Counter pgStatTransactionIdleTime;
+
+
 /*
  * Updated by the traffic cop and in errfinish()
  */
@@ -1193,9 +942,6 @@ extern SessionEndType pgStatSessionEndCause;
  * Functions called from postmaster
  * ----------
  */
-extern Size BackendStatusShmemSize(void);
-extern void CreateSharedBackendStatus(void);
-
 extern void pgstat_init(void);
 extern int     pgstat_start(void);
 extern void pgstat_reset_all(void);
@@ -1241,30 +987,13 @@ extern void pgstat_report_replslot(const char *slotname, PgStat_Counter spilltxn
 extern void pgstat_report_replslot_drop(const char *slotname);
 
 extern void pgstat_initialize(void);
-extern void pgstat_bestart(void);
-
-extern void pgstat_report_activity(BackendState state, const char *cmd_str);
-extern void pgstat_report_tempfile(size_t filesize);
-extern void pgstat_report_appname(const char *appname);
-extern void pgstat_report_xact_timestamp(TimestampTz tstamp);
-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 void pgstat_progress_start_command(ProgressCommandType cmdtype,
-                                                                                 Oid relid);
-extern void pgstat_progress_update_param(int index, int64 val);
-extern void pgstat_progress_update_multi_param(int nparam, const int *index,
-                                                                                          const int64 *val);
-extern void pgstat_progress_end_command(void);
+
 
 extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id);
 extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
 
 extern void pgstat_initstats(Relation rel);
 
-extern char *pgstat_clip_activity(const char *raw_activity);
-
 /* nontransactional event counts are simple enough to inline */
 
 #define pgstat_count_heap_scan(rel)                                                                    \
@@ -1306,6 +1035,10 @@ extern char *pgstat_clip_activity(const char *raw_activity);
        (pgStatBlockReadTime += (n))
 #define pgstat_count_buffer_write_time(n)                                                      \
        (pgStatBlockWriteTime += (n))
+#define pgstat_count_conn_active_time(n)                                                       \
+       (pgStatActiveTime += (n))
+#define pgstat_count_conn_txn_idle_time(n)                                                     \
+       (pgStatTransactionIdleTime += (n))
 
 extern void pgstat_count_heap_insert(Relation rel, PgStat_Counter n);
 extern void pgstat_count_heap_update(Relation rel, bool hot);
@@ -1342,10 +1075,7 @@ extern bool pgstat_send_wal(bool force);
  */
 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 PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid);
-extern int     pgstat_fetch_stat_numbackends(void);
 extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void);
 extern PgStat_GlobalStats *pgstat_fetch_global(void);
 extern PgStat_WalStats *pgstat_fetch_stat_wal(void);
diff --git a/src/include/utils/backend_progress.h b/src/include/utils/backend_progress.h
new file mode 100644 (file)
index 0000000..1714fa0
--- /dev/null
@@ -0,0 +1,44 @@
+/* ----------
+ * backend_progress.h
+ *       Command progress reporting definition.
+ *
+ * Note that this file provides the infrastructure for storing a single
+ * backend's command progress counters, without ascribing meaning to the
+ * individual fields. See commands/progress.h and system_views.sql for that.
+ *
+ * Copyright (c) 2001-2021, PostgreSQL Global Development Group
+ *
+ * src/include/utils/backend_progress.h
+ * ----------
+ */
+#ifndef BACKEND_PROGRESS_H
+#define BACKEND_PROGRESS_H
+
+
+/* ----------
+ * Command type for progress reporting purposes
+ * ----------
+ */
+typedef enum ProgressCommandType
+{
+       PROGRESS_COMMAND_INVALID,
+       PROGRESS_COMMAND_VACUUM,
+       PROGRESS_COMMAND_ANALYZE,
+       PROGRESS_COMMAND_CLUSTER,
+       PROGRESS_COMMAND_CREATE_INDEX,
+       PROGRESS_COMMAND_BASEBACKUP,
+       PROGRESS_COMMAND_COPY
+} ProgressCommandType;
+
+#define PGSTAT_NUM_PROGRESS_PARAM      20
+
+
+extern void pgstat_progress_start_command(ProgressCommandType cmdtype,
+                                                                                 Oid relid);
+extern void pgstat_progress_update_param(int index, int64 val);
+extern void pgstat_progress_update_multi_param(int nparam, const int *index,
+                                                                                          const int64 *val);
+extern void pgstat_progress_end_command(void);
+
+
+#endif /* BACKEND_PROGRESS_H */
diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h
new file mode 100644 (file)
index 0000000..3fd7370
--- /dev/null
@@ -0,0 +1,316 @@
+/* ----------
+ * backend_status.h
+ *       Definitions related to backend status reporting
+ *
+ * Copyright (c) 2001-2021, PostgreSQL Global Development Group
+ *
+ * src/include/utils/backend_status.h
+ * ----------
+ */
+#ifndef BACKEND_STATUS_H
+#define BACKEND_STATUS_H
+
+#include "datatype/timestamp.h"
+#include "libpq/pqcomm.h"
+#include "miscadmin.h" /* for BackendType */
+#include "utils/backend_progress.h"
+
+
+/* ----------
+ * Backend states
+ * ----------
+ */
+typedef enum BackendState
+{
+       STATE_UNDEFINED,
+       STATE_IDLE,
+       STATE_RUNNING,
+       STATE_IDLEINTRANSACTION,
+       STATE_FASTPATH,
+       STATE_IDLEINTRANSACTION_ABORTED,
+       STATE_DISABLED
+} BackendState;
+
+
+/* ----------
+ * Shared-memory data structures
+ * ----------
+ */
+
+/*
+ * PgBackendSSLStatus
+ *
+ * For each backend, we keep the SSL status in a separate struct, that
+ * is only filled in if SSL is enabled.
+ *
+ * All char arrays must be null-terminated.
+ */
+typedef struct PgBackendSSLStatus
+{
+       /* Information about SSL connection */
+       int                     ssl_bits;
+       char            ssl_version[NAMEDATALEN];
+       char            ssl_cipher[NAMEDATALEN];
+       char            ssl_client_dn[NAMEDATALEN];
+
+       /*
+        * serial number is max "20 octets" per RFC 5280, so this size should be
+        * fine
+        */
+       char            ssl_client_serial[NAMEDATALEN];
+
+       char            ssl_issuer_dn[NAMEDATALEN];
+} PgBackendSSLStatus;
+
+/*
+ * PgBackendGSSStatus
+ *
+ * For each backend, we keep the GSS status in a separate struct, that
+ * is only filled in if GSS is enabled.
+ *
+ * All char arrays must be null-terminated.
+ */
+typedef struct PgBackendGSSStatus
+{
+       /* Information about GSSAPI connection */
+       char            gss_princ[NAMEDATALEN]; /* GSSAPI Principal used to auth */
+       bool            gss_auth;               /* If GSSAPI authentication was used */
+       bool            gss_enc;                /* If encryption is being used */
+
+} PgBackendGSSStatus;
+
+
+/* ----------
+ * PgBackendStatus
+ *
+ * Each live backend maintains a PgBackendStatus struct in shared memory
+ * 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 auxiliary process also maintains a PgBackendStatus struct in shared
+ * memory.
+ * ----------
+ */
+typedef struct PgBackendStatus
+{
+       /*
+        * To avoid locking overhead, we use the following protocol: a backend
+        * increments st_changecount before modifying its entry, and again after
+        * finishing a modification.  A would-be reader should note the value of
+        * st_changecount, copy the entry into private memory, then check
+        * st_changecount again.  If the value hasn't changed, and if it's even,
+        * the copy is valid; otherwise start over.  This makes updates cheap
+        * while reads are potentially expensive, but that's the tradeoff we want.
+        *
+        * The above protocol needs memory barriers to ensure that the apparent
+        * order of execution is as it desires.  Otherwise, for example, the CPU
+        * might rearrange the code so that st_changecount is incremented twice
+        * before the modification on a machine with weak memory ordering.  Hence,
+        * use the macros defined below for manipulating st_changecount, rather
+        * than touching it directly.
+        */
+       int                     st_changecount;
+
+       /* The entry is valid iff st_procpid > 0, unused if st_procpid == 0 */
+       int                     st_procpid;
+
+       /* Type of backends */
+       BackendType st_backendType;
+
+       /* Times when current backend, transaction, and activity started */
+       TimestampTz st_proc_start_timestamp;
+       TimestampTz st_xact_start_timestamp;
+       TimestampTz st_activity_start_timestamp;
+       TimestampTz st_state_start_timestamp;
+
+       /* Database OID, owning user's OID, connection client address */
+       Oid                     st_databaseid;
+       Oid                     st_userid;
+       SockAddr        st_clientaddr;
+       char       *st_clienthostname;  /* MUST be null-terminated */
+
+       /* Information about SSL connection */
+       bool            st_ssl;
+       PgBackendSSLStatus *st_sslstatus;
+
+       /* Information about GSSAPI connection */
+       bool            st_gss;
+       PgBackendGSSStatus *st_gssstatus;
+
+       /* current state */
+       BackendState st_state;
+
+       /* application name; MUST be null-terminated */
+       char       *st_appname;
+
+       /*
+        * Current command string; MUST be null-terminated. Note that this string
+        * possibly is truncated in the middle of a multi-byte character. As
+        * activity strings are stored more frequently than read, that allows to
+        * move the cost of correct truncation to the display side. Use
+        * pgstat_clip_activity() to truncate correctly.
+        */
+       char       *st_activity_raw;
+
+       /*
+        * Command progress reporting.  Any command which wishes can advertise
+        * that it is running by setting st_progress_command,
+        * st_progress_command_target, and st_progress_param[].
+        * st_progress_command_target should be the OID of the relation which the
+        * command targets (we assume there's just one, as this is meant for
+        * utility commands), but the meaning of each element in the
+        * st_progress_param array is command-specific.
+        */
+       ProgressCommandType st_progress_command;
+       Oid                     st_progress_command_target;
+       int64           st_progress_param[PGSTAT_NUM_PROGRESS_PARAM];
+} PgBackendStatus;
+
+
+/*
+ * Macros to load and store st_changecount with appropriate memory barriers.
+ *
+ * Use PGSTAT_BEGIN_WRITE_ACTIVITY() before, and PGSTAT_END_WRITE_ACTIVITY()
+ * after, modifying the current process's PgBackendStatus data.  Note that,
+ * since there is no mechanism for cleaning up st_changecount after an error,
+ * THESE MACROS FORM A CRITICAL SECTION.  Any error between them will be
+ * promoted to PANIC, causing a database restart to clean up shared memory!
+ * Hence, keep the critical section as short and straight-line as possible.
+ * Aside from being safer, that minimizes the window in which readers will
+ * have to loop.
+ *
+ * Reader logic should follow this sketch:
+ *
+ *             for (;;)
+ *             {
+ *                     int before_ct, after_ct;
+ *
+ *                     pgstat_begin_read_activity(beentry, before_ct);
+ *                     ... copy beentry data to local memory ...
+ *                     pgstat_end_read_activity(beentry, after_ct);
+ *                     if (pgstat_read_activity_complete(before_ct, after_ct))
+ *                             break;
+ *                     CHECK_FOR_INTERRUPTS();
+ *             }
+ *
+ * For extra safety, we generally use volatile beentry pointers, although
+ * the memory barriers should theoretically be sufficient.
+ */
+#define PGSTAT_BEGIN_WRITE_ACTIVITY(beentry) \
+       do { \
+               START_CRIT_SECTION(); \
+               (beentry)->st_changecount++; \
+               pg_write_barrier(); \
+       } while (0)
+
+#define PGSTAT_END_WRITE_ACTIVITY(beentry) \
+       do { \
+               pg_write_barrier(); \
+               (beentry)->st_changecount++; \
+               Assert(((beentry)->st_changecount & 1) == 0); \
+               END_CRIT_SECTION(); \
+       } while (0)
+
+#define pgstat_begin_read_activity(beentry, before_changecount) \
+       do { \
+               (before_changecount) = (beentry)->st_changecount; \
+               pg_read_barrier(); \
+       } while (0)
+
+#define pgstat_end_read_activity(beentry, after_changecount) \
+       do { \
+               pg_read_barrier(); \
+               (after_changecount) = (beentry)->st_changecount; \
+       } while (0)
+
+#define pgstat_read_activity_complete(before_changecount, after_changecount) \
+       ((before_changecount) == (after_changecount) && \
+        ((before_changecount) & 1) == 0)
+
+
+/* ----------
+ * LocalPgBackendStatus
+ *
+ * When we build the backend status array, we use LocalPgBackendStatus to be
+ * able to add new values to the struct when needed without adding new fields
+ * to the shared memory. It contains the backend status as a first member.
+ * ----------
+ */
+typedef struct LocalPgBackendStatus
+{
+       /*
+        * Local version of the backend status entry.
+        */
+       PgBackendStatus backendStatus;
+
+       /*
+        * The xid of the current transaction if available, InvalidTransactionId
+        * if not.
+        */
+       TransactionId backend_xid;
+
+       /*
+        * The xmin of the current session if available, InvalidTransactionId if
+        * not.
+        */
+       TransactionId backend_xmin;
+} LocalPgBackendStatus;
+
+
+/* ----------
+ * GUC parameters
+ * ----------
+ */
+extern PGDLLIMPORT bool pgstat_track_activities;
+extern PGDLLIMPORT int pgstat_track_activity_query_size;
+
+
+/* ----------
+ * Other global variables
+ * ----------
+ */
+extern PGDLLIMPORT  PgBackendStatus *MyBEEntry;
+
+
+/* ----------
+ * Functions called from postmaster
+ * ----------
+ */
+extern Size BackendStatusShmemSize(void);
+extern void CreateSharedBackendStatus(void);
+
+
+/* ----------
+ * Functions called from backends
+ * ----------
+ */
+
+/* Initialization functions */
+extern void pgstat_beinit(void);
+extern void pgstat_bestart(void);
+
+extern void pgstat_clear_backend_activity_snapshot(void);
+
+/* Activity reporting functions */
+extern void pgstat_report_activity(BackendState state, const char *cmd_str);
+extern void pgstat_report_tempfile(size_t filesize);
+extern void pgstat_report_appname(const char *appname);
+extern void pgstat_report_xact_timestamp(TimestampTz tstamp);
+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);
+
+
+/* ----------
+ * Support functions for the SQL-callable functions to
+ * generate the pgstat* views.
+ * ----------
+ */
+extern int     pgstat_fetch_stat_numbackends(void);
+extern PgBackendStatus *pgstat_fetch_stat_beentry(int beid);
+extern LocalPgBackendStatus *pgstat_fetch_stat_local_beentry(int beid);
+extern char *pgstat_clip_activity(const char *raw_activity);
+
+
+#endif /* BACKEND_STATUS_H */