diff options
Diffstat (limited to 'src/backend/postmaster/pgstat.c')
-rw-r--r-- | src/backend/postmaster/pgstat.c | 1125 |
1 files changed, 21 insertions, 1104 deletions
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 498d6ee1236..4c4b0720681 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -46,7 +46,6 @@ #include "libpq/pqsignal.h" #include "mb/pg_wchar.h" #include "miscadmin.h" -#include "pg_trace.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "postmaster/fork_process.h" @@ -63,8 +62,6 @@ #include "storage/pg_shmem.h" #include "storage/proc.h" #include "storage/procsignal.h" -#include "storage/sinvaladt.h" -#include "utils/ascii.h" #include "utils/guc.h" #include "utils/memutils.h" #include "utils/ps_status.h" @@ -109,25 +106,11 @@ /* ---------- - * Total number of backends including auxiliary - * - * We reserve a slot for each possible BackendId, plus one for each - * possible auxiliary process type. (This scheme assumes there is not - * more than one of any auxiliary process type at a time.) MaxBackends - * includes autovacuum workers and background workers as well. - * ---------- - */ -#define NumBackendStatSlots (MaxBackends + NUM_AUXPROCTYPES) - - -/* ---------- * GUC parameters * ---------- */ -bool pgstat_track_activities = false; bool pgstat_track_counts = false; int pgstat_track_functions = TRACK_FUNC_OFF; -int pgstat_track_activity_query_size = 1024; /* ---------- * Built from GUC parameter @@ -259,8 +242,8 @@ static int pgStatXactCommit = 0; static int pgStatXactRollback = 0; PgStat_Counter pgStatBlockReadTime = 0; PgStat_Counter pgStatBlockWriteTime = 0; -static PgStat_Counter pgStatActiveTime = 0; -static PgStat_Counter pgStatTransactionIdleTime = 0; +PgStat_Counter pgStatActiveTime = 0; +PgStat_Counter pgStatTransactionIdleTime = 0; SessionEndType pgStatSessionEndCause = DISCONNECT_NORMAL; /* Record that's written to 2PC state file when pgstat state is persisted */ @@ -283,12 +266,6 @@ typedef struct TwoPhasePgStatRecord static MemoryContext pgStatLocalContext = NULL; static HTAB *pgStatDBHash = NULL; -/* Status for backends including auxiliary */ -static LocalPgBackendStatus *localBackendStatusTable = NULL; - -/* Total number of backends including auxiliary */ -static int localNumBackends = 0; - /* * Cluster wide statistics, kept in the stats collector. * Contains statistics that are not collected per database @@ -325,7 +302,6 @@ static pid_t pgstat_forkexec(void); #endif NON_EXEC_STATIC void PgstatCollectorMain(int argc, char *argv[]) pg_attribute_noreturn(); -static void pgstat_beshutdown_hook(int code, Datum arg); static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid, bool create); static PgStat_StatTabEntry *pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry, @@ -335,7 +311,6 @@ static void pgstat_write_db_statsfile(PgStat_StatDBEntry *dbentry, bool permanen static HTAB *pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep); static void pgstat_read_db_statsfile(Oid databaseid, HTAB *tabhash, HTAB *funchash, bool permanent); static void backend_read_statsfile(void); -static void pgstat_read_current_status(void); static bool pgstat_write_statsfile_needed(void); static bool pgstat_db_requested(Oid databaseid); @@ -2757,65 +2732,6 @@ pgstat_fetch_stat_funcentry(Oid func_id) } -/* ---------- - * pgstat_fetch_stat_beentry() - - * - * Support function for the SQL-callable pgstat* functions. Returns - * our local copy of the current-activity entry for one backend. - * - * NB: caller is responsible for a check if the user is permitted to see - * this info (especially the querystring). - * ---------- - */ -PgBackendStatus * -pgstat_fetch_stat_beentry(int beid) -{ - pgstat_read_current_status(); - - if (beid < 1 || beid > localNumBackends) - return NULL; - - return &localBackendStatusTable[beid - 1].backendStatus; -} - - -/* ---------- - * pgstat_fetch_stat_local_beentry() - - * - * Like pgstat_fetch_stat_beentry() but with locally computed additions (like - * xid and xmin values of the backend) - * - * NB: caller is responsible for a check if the user is permitted to see - * this info (especially the querystring). - * ---------- - */ -LocalPgBackendStatus * -pgstat_fetch_stat_local_beentry(int beid) -{ - pgstat_read_current_status(); - - if (beid < 1 || beid > localNumBackends) - return NULL; - - return &localBackendStatusTable[beid - 1]; -} - - -/* ---------- - * pgstat_fetch_stat_numbackends() - - * - * Support function for the SQL-callable pgstat* functions. Returns - * the maximum current backend id. - * ---------- - */ -int -pgstat_fetch_stat_numbackends(void) -{ - pgstat_read_current_status(); - - return localNumBackends; -} - /* * --------- * pgstat_fetch_stat_archiver() - @@ -2899,421 +2815,16 @@ pgstat_fetch_replslot(int *nslots_p) return replSlotStats; } -/* ------------------------------------------------------------ - * Functions for management of the shared-memory PgBackendStatus array - * ------------------------------------------------------------ - */ - -static PgBackendStatus *BackendStatusArray = NULL; -static PgBackendStatus *MyBEEntry = NULL; -static char *BackendAppnameBuffer = NULL; -static char *BackendClientHostnameBuffer = NULL; -static char *BackendActivityBuffer = NULL; -static Size BackendActivityBufferSize = 0; -#ifdef USE_SSL -static PgBackendSSLStatus *BackendSslStatusBuffer = NULL; -#endif -#ifdef ENABLE_GSS -static PgBackendGSSStatus *BackendGssStatusBuffer = NULL; -#endif - - -/* - * Report shared-memory space needed by CreateSharedBackendStatus. - */ -Size -BackendStatusShmemSize(void) -{ - Size size; - - /* BackendStatusArray: */ - size = mul_size(sizeof(PgBackendStatus), NumBackendStatSlots); - /* BackendAppnameBuffer: */ - size = add_size(size, - mul_size(NAMEDATALEN, NumBackendStatSlots)); - /* BackendClientHostnameBuffer: */ - size = add_size(size, - mul_size(NAMEDATALEN, NumBackendStatSlots)); - /* BackendActivityBuffer: */ - size = add_size(size, - mul_size(pgstat_track_activity_query_size, NumBackendStatSlots)); -#ifdef USE_SSL - /* BackendSslStatusBuffer: */ - size = add_size(size, - mul_size(sizeof(PgBackendSSLStatus), NumBackendStatSlots)); -#endif -#ifdef ENABLE_GSS - /* BackendGssStatusBuffer: */ - size = add_size(size, - mul_size(sizeof(PgBackendGSSStatus), NumBackendStatSlots)); -#endif - return size; -} - -/* - * Initialize the shared status array and several string buffers - * during postmaster startup. - */ -void -CreateSharedBackendStatus(void) -{ - Size size; - bool found; - int i; - char *buffer; - - /* Create or attach to the shared array */ - size = mul_size(sizeof(PgBackendStatus), NumBackendStatSlots); - BackendStatusArray = (PgBackendStatus *) - ShmemInitStruct("Backend Status Array", size, &found); - - if (!found) - { - /* - * We're the first - initialize. - */ - MemSet(BackendStatusArray, 0, size); - } - - /* Create or attach to the shared appname buffer */ - size = mul_size(NAMEDATALEN, NumBackendStatSlots); - BackendAppnameBuffer = (char *) - ShmemInitStruct("Backend Application Name Buffer", size, &found); - - if (!found) - { - MemSet(BackendAppnameBuffer, 0, size); - - /* Initialize st_appname pointers. */ - buffer = BackendAppnameBuffer; - for (i = 0; i < NumBackendStatSlots; i++) - { - BackendStatusArray[i].st_appname = buffer; - buffer += NAMEDATALEN; - } - } - - /* Create or attach to the shared client hostname buffer */ - size = mul_size(NAMEDATALEN, NumBackendStatSlots); - BackendClientHostnameBuffer = (char *) - ShmemInitStruct("Backend Client Host Name Buffer", size, &found); - - if (!found) - { - MemSet(BackendClientHostnameBuffer, 0, size); - - /* Initialize st_clienthostname pointers. */ - buffer = BackendClientHostnameBuffer; - for (i = 0; i < NumBackendStatSlots; i++) - { - BackendStatusArray[i].st_clienthostname = buffer; - buffer += NAMEDATALEN; - } - } - - /* Create or attach to the shared activity buffer */ - BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size, - NumBackendStatSlots); - BackendActivityBuffer = (char *) - ShmemInitStruct("Backend Activity Buffer", - BackendActivityBufferSize, - &found); - - if (!found) - { - MemSet(BackendActivityBuffer, 0, BackendActivityBufferSize); - - /* Initialize st_activity pointers. */ - buffer = BackendActivityBuffer; - for (i = 0; i < NumBackendStatSlots; i++) - { - BackendStatusArray[i].st_activity_raw = buffer; - buffer += pgstat_track_activity_query_size; - } - } - -#ifdef USE_SSL - /* Create or attach to the shared SSL status buffer */ - size = mul_size(sizeof(PgBackendSSLStatus), NumBackendStatSlots); - BackendSslStatusBuffer = (PgBackendSSLStatus *) - ShmemInitStruct("Backend SSL Status Buffer", size, &found); - - if (!found) - { - PgBackendSSLStatus *ptr; - - MemSet(BackendSslStatusBuffer, 0, size); - - /* Initialize st_sslstatus pointers. */ - ptr = BackendSslStatusBuffer; - for (i = 0; i < NumBackendStatSlots; i++) - { - BackendStatusArray[i].st_sslstatus = ptr; - ptr++; - } - } -#endif - -#ifdef ENABLE_GSS - /* Create or attach to the shared GSSAPI status buffer */ - size = mul_size(sizeof(PgBackendGSSStatus), NumBackendStatSlots); - BackendGssStatusBuffer = (PgBackendGSSStatus *) - ShmemInitStruct("Backend GSS Status Buffer", size, &found); - - if (!found) - { - PgBackendGSSStatus *ptr; - - MemSet(BackendGssStatusBuffer, 0, size); - - /* Initialize st_gssstatus pointers. */ - ptr = BackendGssStatusBuffer; - for (i = 0; i < NumBackendStatSlots; i++) - { - BackendStatusArray[i].st_gssstatus = ptr; - ptr++; - } - } -#endif -} - - -/* ---------- - * pgstat_initialize() - - * - * Initialize pgstats state, and set up our on-proc-exit hook. - * Called from InitPostgres and AuxiliaryProcessMain. For auxiliary process, - * MyBackendId is invalid. Otherwise, MyBackendId must be set, - * but we must not have started any transaction yet (since the - * exit hook must run after the last transaction exit). - * NOTE: MyDatabaseId isn't set yet; so the shutdown hook has to be careful. - * ---------- - */ -void -pgstat_initialize(void) -{ - /* Initialize MyBEEntry */ - if (MyBackendId != InvalidBackendId) - { - Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends); - MyBEEntry = &BackendStatusArray[MyBackendId - 1]; - } - else - { - /* Must be an auxiliary process */ - Assert(MyAuxProcType != NotAnAuxProcess); - - /* - * Assign the MyBEEntry for an auxiliary process. Since it doesn't - * have a BackendId, the slot is statically allocated based on the - * auxiliary process type (MyAuxProcType). Backends use slots indexed - * in the range from 1 to MaxBackends (inclusive), so we use - * MaxBackends + AuxBackendType + 1 as the index of the slot for an - * auxiliary process. - */ - MyBEEntry = &BackendStatusArray[MaxBackends + MyAuxProcType]; - } - - /* - * Initialize prevWalUsage with pgWalUsage so that pgstat_report_wal() can - * calculate how much pgWalUsage counters are increased by substracting - * prevWalUsage from pgWalUsage. - */ - prevWalUsage = pgWalUsage; - - /* Set up a process-exit hook to clean up */ - on_shmem_exit(pgstat_beshutdown_hook, 0); -} - -/* ---------- - * pgstat_bestart() - - * - * Initialize this backend's entry in the PgBackendStatus array. - * Called from InitPostgres. - * - * Apart from auxiliary processes, MyBackendId, MyDatabaseId, - * session userid, and application_name must be set for a - * backend (hence, this cannot be combined with pgstat_initialize). - * Note also that we must be inside a transaction if this isn't an aux - * process, as we may need to do encoding conversion on some strings. - * ---------- - */ -void -pgstat_bestart(void) -{ - volatile PgBackendStatus *vbeentry = MyBEEntry; - PgBackendStatus lbeentry; -#ifdef USE_SSL - PgBackendSSLStatus lsslstatus; -#endif -#ifdef ENABLE_GSS - PgBackendGSSStatus lgssstatus; -#endif - - /* pgstats state must be initialized from pgstat_initialize() */ - Assert(vbeentry != NULL); - - /* - * To minimize the time spent modifying the PgBackendStatus entry, and - * avoid risk of errors inside the critical section, we first copy the - * shared-memory struct to a local variable, then modify the data in the - * local variable, then copy the local variable back to shared memory. - * Only the last step has to be inside the critical section. - * - * Most of the data we copy from shared memory is just going to be - * overwritten, but the struct's not so large that it's worth the - * maintenance hassle to copy only the needful fields. - */ - memcpy(&lbeentry, - unvolatize(PgBackendStatus *, vbeentry), - sizeof(PgBackendStatus)); - - /* These structs can just start from zeroes each time, though */ -#ifdef USE_SSL - memset(&lsslstatus, 0, sizeof(lsslstatus)); -#endif -#ifdef ENABLE_GSS - memset(&lgssstatus, 0, sizeof(lgssstatus)); -#endif - - /* - * Now fill in all the fields of lbeentry, except for strings that are - * out-of-line data. Those have to be handled separately, below. - */ - lbeentry.st_procpid = MyProcPid; - lbeentry.st_backendType = MyBackendType; - lbeentry.st_proc_start_timestamp = MyStartTimestamp; - lbeentry.st_activity_start_timestamp = 0; - lbeentry.st_state_start_timestamp = 0; - lbeentry.st_xact_start_timestamp = 0; - lbeentry.st_databaseid = MyDatabaseId; - - /* We have userid for client-backends, wal-sender and bgworker processes */ - if (lbeentry.st_backendType == B_BACKEND - || lbeentry.st_backendType == B_WAL_SENDER - || lbeentry.st_backendType == B_BG_WORKER) - lbeentry.st_userid = GetSessionUserId(); - else - lbeentry.st_userid = InvalidOid; - - /* - * We may not have a MyProcPort (eg, if this is the autovacuum process). - * If so, use all-zeroes client address, which is dealt with specially in - * pg_stat_get_backend_client_addr and pg_stat_get_backend_client_port. - */ - if (MyProcPort) - memcpy(&lbeentry.st_clientaddr, &MyProcPort->raddr, - sizeof(lbeentry.st_clientaddr)); - else - MemSet(&lbeentry.st_clientaddr, 0, sizeof(lbeentry.st_clientaddr)); - -#ifdef USE_SSL - if (MyProcPort && MyProcPort->ssl_in_use) - { - lbeentry.st_ssl = true; - lsslstatus.ssl_bits = be_tls_get_cipher_bits(MyProcPort); - strlcpy(lsslstatus.ssl_version, be_tls_get_version(MyProcPort), NAMEDATALEN); - strlcpy(lsslstatus.ssl_cipher, be_tls_get_cipher(MyProcPort), NAMEDATALEN); - be_tls_get_peer_subject_name(MyProcPort, lsslstatus.ssl_client_dn, NAMEDATALEN); - be_tls_get_peer_serial(MyProcPort, lsslstatus.ssl_client_serial, NAMEDATALEN); - be_tls_get_peer_issuer_name(MyProcPort, lsslstatus.ssl_issuer_dn, NAMEDATALEN); - } - else - { - lbeentry.st_ssl = false; - } -#else - lbeentry.st_ssl = false; -#endif - -#ifdef ENABLE_GSS - if (MyProcPort && MyProcPort->gss != NULL) - { - const char *princ = be_gssapi_get_princ(MyProcPort); - - lbeentry.st_gss = true; - lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort); - lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort); - if (princ) - strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN); - } - else - { - lbeentry.st_gss = false; - } -#else - lbeentry.st_gss = false; -#endif - - lbeentry.st_state = STATE_UNDEFINED; - lbeentry.st_progress_command = PROGRESS_COMMAND_INVALID; - lbeentry.st_progress_command_target = InvalidOid; - - /* - * we don't zero st_progress_param here to save cycles; nobody should - * examine it until st_progress_command has been set to something other - * than PROGRESS_COMMAND_INVALID - */ - - /* - * We're ready to enter the critical section that fills the shared-memory - * status entry. We follow the protocol of bumping st_changecount before - * and after; and make sure it's even afterwards. We use a volatile - * pointer here to ensure the compiler doesn't try to get cute. - */ - PGSTAT_BEGIN_WRITE_ACTIVITY(vbeentry); - - /* make sure we'll memcpy the same st_changecount back */ - lbeentry.st_changecount = vbeentry->st_changecount; - - memcpy(unvolatize(PgBackendStatus *, vbeentry), - &lbeentry, - sizeof(PgBackendStatus)); - - /* - * We can write the out-of-line strings and structs using the pointers - * that are in lbeentry; this saves some de-volatilizing messiness. - */ - lbeentry.st_appname[0] = '\0'; - if (MyProcPort && MyProcPort->remote_hostname) - strlcpy(lbeentry.st_clienthostname, MyProcPort->remote_hostname, - NAMEDATALEN); - else - lbeentry.st_clienthostname[0] = '\0'; - lbeentry.st_activity_raw[0] = '\0'; - /* Also make sure the last byte in each string area is always 0 */ - lbeentry.st_appname[NAMEDATALEN - 1] = '\0'; - lbeentry.st_clienthostname[NAMEDATALEN - 1] = '\0'; - lbeentry.st_activity_raw[pgstat_track_activity_query_size - 1] = '\0'; - -#ifdef USE_SSL - memcpy(lbeentry.st_sslstatus, &lsslstatus, sizeof(PgBackendSSLStatus)); -#endif -#ifdef ENABLE_GSS - memcpy(lbeentry.st_gssstatus, &lgssstatus, sizeof(PgBackendGSSStatus)); -#endif - - PGSTAT_END_WRITE_ACTIVITY(vbeentry); - - /* Update app name to current GUC setting */ - if (application_name) - pgstat_report_appname(application_name); -} - /* * Shut down a single backend's statistics reporting at process exit. * * Flush any remaining statistics counts out to the collector. * Without this, operations triggered during backend exit (such as * temp table deletions) won't be counted. - * - * Lastly, clear out our entry in the PgBackendStatus array. */ static void -pgstat_beshutdown_hook(int code, Datum arg) +pgstat_shutdown_hook(int code, Datum arg) { - volatile PgBackendStatus *beentry = MyBEEntry; - /* * If we got as far as discovering our own database ID, we can report what * we did to the collector. Otherwise, we'd be sending an invalid @@ -3322,584 +2833,29 @@ pgstat_beshutdown_hook(int code, Datum arg) */ if (OidIsValid(MyDatabaseId)) pgstat_report_stat(true); - - /* - * Clear my status entry, following the protocol of bumping st_changecount - * before and after. We use a volatile pointer here to ensure the - * compiler doesn't try to get cute. - */ - PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - - beentry->st_procpid = 0; /* mark invalid */ - - PGSTAT_END_WRITE_ACTIVITY(beentry); } - /* ---------- - * pgstat_report_activity() - - * - * Called from tcop/postgres.c to report what the backend is actually doing - * (but note cmd_str can be NULL for certain cases). - * - * All updates of the status entry follow the protocol of bumping - * st_changecount before and after. We use a volatile pointer here to - * ensure the compiler doesn't try to get cute. - * ---------- - */ -void -pgstat_report_activity(BackendState state, const char *cmd_str) -{ - volatile PgBackendStatus *beentry = MyBEEntry; - TimestampTz start_timestamp; - TimestampTz current_timestamp; - int len = 0; - - TRACE_POSTGRESQL_STATEMENT_STATUS(cmd_str); - - if (!beentry) - return; - - if (!pgstat_track_activities) - { - if (beentry->st_state != STATE_DISABLED) - { - volatile PGPROC *proc = MyProc; - - /* - * track_activities is disabled, but we last reported a - * non-disabled state. As our final update, change the state and - * clear fields we will not be updating anymore. - */ - PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - beentry->st_state = STATE_DISABLED; - beentry->st_state_start_timestamp = 0; - beentry->st_activity_raw[0] = '\0'; - beentry->st_activity_start_timestamp = 0; - /* st_xact_start_timestamp and wait_event_info are also disabled */ - beentry->st_xact_start_timestamp = 0; - proc->wait_event_info = 0; - PGSTAT_END_WRITE_ACTIVITY(beentry); - } - return; - } - - /* - * To minimize the time spent modifying the entry, and avoid risk of - * errors inside the critical section, fetch all the needed data first. - */ - start_timestamp = GetCurrentStatementStartTimestamp(); - if (cmd_str != NULL) - { - /* - * Compute length of to-be-stored string unaware of multi-byte - * characters. For speed reasons that'll get corrected on read, rather - * than computed every write. - */ - len = Min(strlen(cmd_str), pgstat_track_activity_query_size - 1); - } - current_timestamp = GetCurrentTimestamp(); - - /* - * If the state has changed from "active" or "idle in transaction", - * calculate the duration. - */ - if ((beentry->st_state == STATE_RUNNING || - beentry->st_state == STATE_FASTPATH || - beentry->st_state == STATE_IDLEINTRANSACTION || - beentry->st_state == STATE_IDLEINTRANSACTION_ABORTED) && - state != beentry->st_state) - { - long secs; - int usecs; - - TimestampDifference(beentry->st_state_start_timestamp, - current_timestamp, - &secs, &usecs); - - if (beentry->st_state == STATE_RUNNING || - beentry->st_state == STATE_FASTPATH) - pgStatActiveTime += secs * 1000000 + usecs; - else - pgStatTransactionIdleTime += secs * 1000000 + usecs; - } - - /* - * Now update the status entry - */ - PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - - beentry->st_state = state; - beentry->st_state_start_timestamp = current_timestamp; - - if (cmd_str != NULL) - { - memcpy((char *) beentry->st_activity_raw, cmd_str, len); - beentry->st_activity_raw[len] = '\0'; - beentry->st_activity_start_timestamp = start_timestamp; - } - - PGSTAT_END_WRITE_ACTIVITY(beentry); -} - -/*----------- - * pgstat_progress_start_command() - - * - * Set st_progress_command (and st_progress_command_target) in own backend - * entry. Also, zero-initialize st_progress_param array. - *----------- - */ -void -pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid) -{ - volatile PgBackendStatus *beentry = MyBEEntry; - - if (!beentry || !pgstat_track_activities) - return; - - PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - beentry->st_progress_command = cmdtype; - beentry->st_progress_command_target = relid; - MemSet(&beentry->st_progress_param, 0, sizeof(beentry->st_progress_param)); - PGSTAT_END_WRITE_ACTIVITY(beentry); -} - -/*----------- - * pgstat_progress_update_param() - - * - * Update index'th member in st_progress_param[] of own backend entry. - *----------- - */ -void -pgstat_progress_update_param(int index, int64 val) -{ - volatile PgBackendStatus *beentry = MyBEEntry; - - Assert(index >= 0 && index < PGSTAT_NUM_PROGRESS_PARAM); - - if (!beentry || !pgstat_track_activities) - return; - - PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - beentry->st_progress_param[index] = val; - PGSTAT_END_WRITE_ACTIVITY(beentry); -} - -/*----------- - * pgstat_progress_update_multi_param() - - * - * Update multiple members in st_progress_param[] of own backend entry. - * This is atomic; readers won't see intermediate states. - *----------- - */ -void -pgstat_progress_update_multi_param(int nparam, const int *index, - const int64 *val) -{ - volatile PgBackendStatus *beentry = MyBEEntry; - int i; - - if (!beentry || !pgstat_track_activities || nparam == 0) - return; - - PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - - for (i = 0; i < nparam; ++i) - { - Assert(index[i] >= 0 && index[i] < PGSTAT_NUM_PROGRESS_PARAM); - - beentry->st_progress_param[index[i]] = val[i]; - } - - PGSTAT_END_WRITE_ACTIVITY(beentry); -} - -/*----------- - * pgstat_progress_end_command() - + * pgstat_initialize() - * - * Reset st_progress_command (and st_progress_command_target) in own backend - * entry. This signals the end of the command. - *----------- - */ -void -pgstat_progress_end_command(void) -{ - volatile PgBackendStatus *beentry = MyBEEntry; - - if (!beentry || !pgstat_track_activities) - return; - - if (beentry->st_progress_command == PROGRESS_COMMAND_INVALID) - return; - - PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - beentry->st_progress_command = PROGRESS_COMMAND_INVALID; - beentry->st_progress_command_target = InvalidOid; - PGSTAT_END_WRITE_ACTIVITY(beentry); -} - -/* ---------- - * pgstat_report_appname() - + * Initialize pgstats state, and set up our on-proc-exit hook. + * Called from InitPostgres and AuxiliaryProcessMain. * - * Called to update our application name. + * NOTE: MyDatabaseId isn't set yet; so the shutdown hook has to be careful. * ---------- */ void -pgstat_report_appname(const char *appname) -{ - volatile PgBackendStatus *beentry = MyBEEntry; - int len; - - if (!beentry) - return; - - /* This should be unnecessary if GUC did its job, but be safe */ - len = pg_mbcliplen(appname, strlen(appname), NAMEDATALEN - 1); - - /* - * Update my status entry, following the protocol of bumping - * st_changecount before and after. We use a volatile pointer here to - * ensure the compiler doesn't try to get cute. - */ - PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - - memcpy((char *) beentry->st_appname, appname, len); - beentry->st_appname[len] = '\0'; - - PGSTAT_END_WRITE_ACTIVITY(beentry); -} - -/* - * Report current transaction start timestamp as the specified value. - * Zero means there is no active transaction. - */ -void -pgstat_report_xact_timestamp(TimestampTz tstamp) -{ - volatile PgBackendStatus *beentry = MyBEEntry; - - if (!pgstat_track_activities || !beentry) - return; - - /* - * Update my status entry, following the protocol of bumping - * st_changecount before and after. We use a volatile pointer here to - * ensure the compiler doesn't try to get cute. - */ - PGSTAT_BEGIN_WRITE_ACTIVITY(beentry); - - beentry->st_xact_start_timestamp = tstamp; - - PGSTAT_END_WRITE_ACTIVITY(beentry); -} - -/* ---------- - * pgstat_read_current_status() - - * - * Copy the current contents of the PgBackendStatus array to local memory, - * if not already done in this transaction. - * ---------- - */ -static void -pgstat_read_current_status(void) -{ - volatile PgBackendStatus *beentry; - LocalPgBackendStatus *localtable; - LocalPgBackendStatus *localentry; - char *localappname, - *localclienthostname, - *localactivity; -#ifdef USE_SSL - PgBackendSSLStatus *localsslstatus; -#endif -#ifdef ENABLE_GSS - PgBackendGSSStatus *localgssstatus; -#endif - int i; - - Assert(!pgStatRunningInCollector); - if (localBackendStatusTable) - return; /* already done */ - - pgstat_setup_memcxt(); - - /* - * Allocate storage for local copy of state data. We can presume that - * none of these requests overflow size_t, because we already calculated - * the same values using mul_size during shmem setup. However, with - * probably-silly values of pgstat_track_activity_query_size and - * max_connections, the localactivity buffer could exceed 1GB, so use - * "huge" allocation for that one. - */ - localtable = (LocalPgBackendStatus *) - MemoryContextAlloc(pgStatLocalContext, - sizeof(LocalPgBackendStatus) * NumBackendStatSlots); - localappname = (char *) - MemoryContextAlloc(pgStatLocalContext, - NAMEDATALEN * NumBackendStatSlots); - localclienthostname = (char *) - MemoryContextAlloc(pgStatLocalContext, - NAMEDATALEN * NumBackendStatSlots); - localactivity = (char *) - MemoryContextAllocHuge(pgStatLocalContext, - pgstat_track_activity_query_size * NumBackendStatSlots); -#ifdef USE_SSL - localsslstatus = (PgBackendSSLStatus *) - MemoryContextAlloc(pgStatLocalContext, - sizeof(PgBackendSSLStatus) * NumBackendStatSlots); -#endif -#ifdef ENABLE_GSS - localgssstatus = (PgBackendGSSStatus *) - MemoryContextAlloc(pgStatLocalContext, - sizeof(PgBackendGSSStatus) * NumBackendStatSlots); -#endif - - localNumBackends = 0; - - beentry = BackendStatusArray; - localentry = localtable; - for (i = 1; i <= NumBackendStatSlots; i++) - { - /* - * Follow the protocol of retrying if st_changecount changes while we - * copy the entry, or if it's odd. (The check for odd is needed to - * cover the case where we are able to completely copy the entry while - * the source backend is between increment steps.) We use a volatile - * pointer here to ensure the compiler doesn't try to get cute. - */ - for (;;) - { - int before_changecount; - int after_changecount; - - pgstat_begin_read_activity(beentry, before_changecount); - - localentry->backendStatus.st_procpid = beentry->st_procpid; - /* Skip all the data-copying work if entry is not in use */ - if (localentry->backendStatus.st_procpid > 0) - { - memcpy(&localentry->backendStatus, unvolatize(PgBackendStatus *, beentry), sizeof(PgBackendStatus)); - - /* - * For each PgBackendStatus field that is a pointer, copy the - * pointed-to data, then adjust the local copy of the pointer - * field to point at the local copy of the data. - * - * strcpy is safe even if the string is modified concurrently, - * because there's always a \0 at the end of the buffer. - */ - strcpy(localappname, (char *) beentry->st_appname); - localentry->backendStatus.st_appname = localappname; - strcpy(localclienthostname, (char *) beentry->st_clienthostname); - localentry->backendStatus.st_clienthostname = localclienthostname; - strcpy(localactivity, (char *) beentry->st_activity_raw); - localentry->backendStatus.st_activity_raw = localactivity; -#ifdef USE_SSL - if (beentry->st_ssl) - { - memcpy(localsslstatus, beentry->st_sslstatus, sizeof(PgBackendSSLStatus)); - localentry->backendStatus.st_sslstatus = localsslstatus; - } -#endif -#ifdef ENABLE_GSS - if (beentry->st_gss) - { - memcpy(localgssstatus, beentry->st_gssstatus, sizeof(PgBackendGSSStatus)); - localentry->backendStatus.st_gssstatus = localgssstatus; - } -#endif - } - - pgstat_end_read_activity(beentry, after_changecount); - - if (pgstat_read_activity_complete(before_changecount, - after_changecount)) - break; - - /* Make sure we can break out of loop if stuck... */ - CHECK_FOR_INTERRUPTS(); - } - - beentry++; - /* Only valid entries get included into the local array */ - if (localentry->backendStatus.st_procpid > 0) - { - BackendIdGetTransactionIds(i, - &localentry->backend_xid, - &localentry->backend_xmin); - - localentry++; - localappname += NAMEDATALEN; - localclienthostname += NAMEDATALEN; - localactivity += pgstat_track_activity_query_size; -#ifdef USE_SSL - localsslstatus++; -#endif -#ifdef ENABLE_GSS - localgssstatus++; -#endif - localNumBackends++; - } - } - - /* Set the pointer only after completion of a valid table */ - localBackendStatusTable = localtable; -} - -/* ---------- - * pgstat_get_backend_current_activity() - - * - * Return a string representing the current activity of the backend with - * the specified PID. This looks directly at the BackendStatusArray, - * and so will provide current information regardless of the age of our - * transaction's snapshot of the status array. - * - * It is the caller's responsibility to invoke this only for backends whose - * state is expected to remain stable while the result is in use. The - * only current use is in deadlock reporting, where we can expect that - * the target backend is blocked on a lock. (There are corner cases - * where the target's wait could get aborted while we are looking at it, - * but the very worst consequence is to return a pointer to a string - * that's been changed, so we won't worry too much.) - * - * Note: return strings for special cases match pg_stat_get_backend_activity. - * ---------- - */ -const char * -pgstat_get_backend_current_activity(int pid, bool checkUser) -{ - PgBackendStatus *beentry; - int i; - - beentry = BackendStatusArray; - for (i = 1; i <= MaxBackends; i++) - { - /* - * Although we expect the target backend's entry to be stable, that - * doesn't imply that anyone else's is. To avoid identifying the - * wrong backend, while we check for a match to the desired PID we - * must follow the protocol of retrying if st_changecount changes - * while we examine the entry, or if it's odd. (This might be - * unnecessary, since fetching or storing an int is almost certainly - * atomic, but let's play it safe.) We use a volatile pointer here to - * ensure the compiler doesn't try to get cute. - */ - volatile PgBackendStatus *vbeentry = beentry; - bool found; - - for (;;) - { - int before_changecount; - int after_changecount; - - pgstat_begin_read_activity(vbeentry, before_changecount); - - found = (vbeentry->st_procpid == pid); - - pgstat_end_read_activity(vbeentry, after_changecount); - - if (pgstat_read_activity_complete(before_changecount, - after_changecount)) - break; - - /* Make sure we can break out of loop if stuck... */ - CHECK_FOR_INTERRUPTS(); - } - - if (found) - { - /* Now it is safe to use the non-volatile pointer */ - if (checkUser && !superuser() && beentry->st_userid != GetUserId()) - return "<insufficient privilege>"; - else if (*(beentry->st_activity_raw) == '\0') - return "<command string not enabled>"; - else - { - /* this'll leak a bit of memory, but that seems acceptable */ - return pgstat_clip_activity(beentry->st_activity_raw); - } - } - - beentry++; - } - - /* If we get here, caller is in error ... */ - return "<backend information not available>"; -} - -/* ---------- - * pgstat_get_crashed_backend_activity() - - * - * Return a string representing the current activity of the backend with - * the specified PID. Like the function above, but reads shared memory with - * the expectation that it may be corrupt. On success, copy the string - * into the "buffer" argument and return that pointer. On failure, - * return NULL. - * - * This function is only intended to be used by the postmaster to report the - * query that crashed a backend. In particular, no attempt is made to - * follow the correct concurrency protocol when accessing the - * BackendStatusArray. But that's OK, in the worst case we'll return a - * corrupted message. We also must take care not to trip on ereport(ERROR). - * ---------- - */ -const char * -pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen) +pgstat_initialize(void) { - volatile PgBackendStatus *beentry; - int i; - - beentry = BackendStatusArray; - /* - * We probably shouldn't get here before shared memory has been set up, - * but be safe. + * Initialize prevWalUsage with pgWalUsage so that pgstat_report_wal() can + * calculate how much pgWalUsage counters are increased by substracting + * prevWalUsage from pgWalUsage. */ - if (beentry == NULL || BackendActivityBuffer == NULL) - return NULL; - - for (i = 1; i <= MaxBackends; i++) - { - if (beentry->st_procpid == pid) - { - /* Read pointer just once, so it can't change after validation */ - const char *activity = beentry->st_activity_raw; - const char *activity_last; - - /* - * We mustn't access activity string before we verify that it - * falls within the BackendActivityBuffer. To make sure that the - * entire string including its ending is contained within the - * buffer, subtract one activity length from the buffer size. - */ - activity_last = BackendActivityBuffer + BackendActivityBufferSize - - pgstat_track_activity_query_size; - - if (activity < BackendActivityBuffer || - activity > activity_last) - return NULL; - - /* If no string available, no point in a report */ - if (activity[0] == '\0') - return NULL; - - /* - * Copy only ASCII-safe characters so we don't run into encoding - * problems when reporting the message; and be sure not to run off - * the end of memory. As only ASCII characters are reported, it - * doesn't seem necessary to perform multibyte aware clipping. - */ - ascii_safe_strlcpy(buffer, activity, - Min(buflen, pgstat_track_activity_query_size)); - - return buffer; - } - - beentry++; - } + prevWalUsage = pgWalUsage; - /* PID not found */ - return NULL; + /* Set up a process-exit hook to clean up */ + on_shmem_exit(pgstat_shutdown_hook, 0); } /* ------------------------------------------------------------ @@ -5656,10 +4612,15 @@ pgstat_clear_snapshot(void) /* Reset variables */ pgStatLocalContext = NULL; pgStatDBHash = NULL; - localBackendStatusTable = NULL; - localNumBackends = 0; replSlotStats = NULL; nReplSlotStats = 0; + + /* + * Historically the backend_status.c facilities lived in this file, and + * were reset with the same function. For now keep it that way, and + * forward the reset request. + */ + pgstat_clear_backend_activity_snapshot(); } @@ -6594,50 +5555,6 @@ pgstat_db_requested(Oid databaseid) return false; } -/* - * Convert a potentially unsafely truncated activity string (see - * PgBackendStatus.st_activity_raw's documentation) into a correctly truncated - * one. - * - * The returned string is allocated in the caller's memory context and may be - * freed. - */ -char * -pgstat_clip_activity(const char *raw_activity) -{ - char *activity; - int rawlen; - int cliplen; - - /* - * Some callers, like pgstat_get_backend_current_activity(), do not - * guarantee that the buffer isn't concurrently modified. We try to take - * care that the buffer is always terminated by a NUL byte regardless, but - * let's still be paranoid about the string's length. In those cases the - * underlying buffer is guaranteed to be pgstat_track_activity_query_size - * large. - */ - activity = pnstrdup(raw_activity, pgstat_track_activity_query_size - 1); - - /* now double-guaranteed to be NUL terminated */ - rawlen = strlen(activity); - - /* - * All supported server-encodings make it possible to determine the length - * of a multi-byte character from its first byte (this is not the case for - * client encodings, see GB18030). As st_activity is always stored using - * server encoding, this allows us to perform multi-byte aware truncation, - * even if the string earlier was truncated in the middle of a multi-byte - * character. - */ - cliplen = pg_mbcliplen(activity, rawlen, - pgstat_track_activity_query_size - 1); - - activity[cliplen] = '\0'; - - return activity; -} - /* ---------- * pgstat_replslot_index * |