*/
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 */
#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"
#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
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 */
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
#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,
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);
}
-/* ----------
- * 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() -
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
*/
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);
}
/* ------------------------------------------------------------
/* 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();
}
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
*
include $(top_builddir)/src/Makefile.global
OBJS = \
+ backend_progress.o \
+ backend_status.o \
wait_event.o
include $(top_srcdir)/src/backend/common.mk
--- /dev/null
+/* ----------
+ * 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);
+}
--- /dev/null
+/* ----------
+ * 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;
+}
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.
#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"
*
* 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.
#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 */
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.
* 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;
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()
*/
* 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);
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) \
(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);
*/
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);
--- /dev/null
+/* ----------
+ * 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 */
--- /dev/null
+/* ----------
+ * 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 */