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