LCOV - code coverage report
Current view: top level - src/backend/utils/activity - backend_status.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 286 312 91.7 %
Date: 2023-12-02 14:10:25 Functions: 21 21 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* ----------
       2             :  * backend_status.c
       3             :  *    Backend status reporting infrastructure.
       4             :  *
       5             :  * Copyright (c) 2001-2023, PostgreSQL Global Development Group
       6             :  *
       7             :  *
       8             :  * IDENTIFICATION
       9             :  *    src/backend/utils/activity/backend_status.c
      10             :  * ----------
      11             :  */
      12             : #include "postgres.h"
      13             : 
      14             : #include "access/xact.h"
      15             : #include "libpq/libpq.h"
      16             : #include "miscadmin.h"
      17             : #include "pg_trace.h"
      18             : #include "pgstat.h"
      19             : #include "port/atomics.h"     /* for memory barriers */
      20             : #include "storage/ipc.h"
      21             : #include "storage/proc.h"     /* for MyProc */
      22             : #include "storage/sinvaladt.h"
      23             : #include "utils/ascii.h"
      24             : #include "utils/backend_status.h"
      25             : #include "utils/guc.h"            /* for application_name */
      26             : #include "utils/memutils.h"
      27             : 
      28             : 
      29             : /* ----------
      30             :  * Total number of backends including auxiliary
      31             :  *
      32             :  * We reserve a slot for each possible BackendId, plus one for each
      33             :  * possible auxiliary process type.  (This scheme assumes there is not
      34             :  * more than one of any auxiliary process type at a time.) MaxBackends
      35             :  * includes autovacuum workers and background workers as well.
      36             :  * ----------
      37             :  */
      38             : #define NumBackendStatSlots (MaxBackends + NUM_AUXPROCTYPES)
      39             : 
      40             : 
      41             : /* ----------
      42             :  * GUC parameters
      43             :  * ----------
      44             :  */
      45             : bool        pgstat_track_activities = false;
      46             : int         pgstat_track_activity_query_size = 1024;
      47             : 
      48             : 
      49             : /* exposed so that backend_progress.c can access it */
      50             : PgBackendStatus *MyBEEntry = NULL;
      51             : 
      52             : 
      53             : static PgBackendStatus *BackendStatusArray = NULL;
      54             : static char *BackendAppnameBuffer = NULL;
      55             : static char *BackendClientHostnameBuffer = NULL;
      56             : static char *BackendActivityBuffer = NULL;
      57             : static Size BackendActivityBufferSize = 0;
      58             : #ifdef USE_SSL
      59             : static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;
      60             : #endif
      61             : #ifdef ENABLE_GSS
      62             : static PgBackendGSSStatus *BackendGssStatusBuffer = NULL;
      63             : #endif
      64             : 
      65             : 
      66             : /* Status for backends including auxiliary */
      67             : static LocalPgBackendStatus *localBackendStatusTable = NULL;
      68             : 
      69             : /* Total number of backends including auxiliary */
      70             : static int  localNumBackends = 0;
      71             : 
      72             : static MemoryContext backendStatusSnapContext;
      73             : 
      74             : 
      75             : static void pgstat_beshutdown_hook(int code, Datum arg);
      76             : static void pgstat_read_current_status(void);
      77             : static void pgstat_setup_backend_status_context(void);
      78             : 
      79             : 
      80             : /*
      81             :  * Report shared-memory space needed by CreateSharedBackendStatus.
      82             :  */
      83             : Size
      84        2934 : BackendStatusShmemSize(void)
      85             : {
      86             :     Size        size;
      87             : 
      88             :     /* BackendStatusArray: */
      89        2934 :     size = mul_size(sizeof(PgBackendStatus), NumBackendStatSlots);
      90             :     /* BackendAppnameBuffer: */
      91        2934 :     size = add_size(size,
      92        2934 :                     mul_size(NAMEDATALEN, NumBackendStatSlots));
      93             :     /* BackendClientHostnameBuffer: */
      94        2934 :     size = add_size(size,
      95        2934 :                     mul_size(NAMEDATALEN, NumBackendStatSlots));
      96             :     /* BackendActivityBuffer: */
      97        2934 :     size = add_size(size,
      98        2934 :                     mul_size(pgstat_track_activity_query_size, NumBackendStatSlots));
      99             : #ifdef USE_SSL
     100             :     /* BackendSslStatusBuffer: */
     101        2934 :     size = add_size(size,
     102        2934 :                     mul_size(sizeof(PgBackendSSLStatus), NumBackendStatSlots));
     103             : #endif
     104             : #ifdef ENABLE_GSS
     105             :     /* BackendGssStatusBuffer: */
     106             :     size = add_size(size,
     107             :                     mul_size(sizeof(PgBackendGSSStatus), NumBackendStatSlots));
     108             : #endif
     109        2934 :     return size;
     110             : }
     111             : 
     112             : /*
     113             :  * Initialize the shared status array and several string buffers
     114             :  * during postmaster startup.
     115             :  */
     116             : void
     117        1562 : CreateSharedBackendStatus(void)
     118             : {
     119             :     Size        size;
     120             :     bool        found;
     121             :     int         i;
     122             :     char       *buffer;
     123             : 
     124             :     /* Create or attach to the shared array */
     125        1562 :     size = mul_size(sizeof(PgBackendStatus), NumBackendStatSlots);
     126        1562 :     BackendStatusArray = (PgBackendStatus *)
     127        1562 :         ShmemInitStruct("Backend Status Array", size, &found);
     128             : 
     129        1562 :     if (!found)
     130             :     {
     131             :         /*
     132             :          * We're the first - initialize.
     133             :          */
     134        1562 :         MemSet(BackendStatusArray, 0, size);
     135             :     }
     136             : 
     137             :     /* Create or attach to the shared appname buffer */
     138        1562 :     size = mul_size(NAMEDATALEN, NumBackendStatSlots);
     139        1562 :     BackendAppnameBuffer = (char *)
     140        1562 :         ShmemInitStruct("Backend Application Name Buffer", size, &found);
     141             : 
     142        1562 :     if (!found)
     143             :     {
     144        1562 :         MemSet(BackendAppnameBuffer, 0, size);
     145             : 
     146             :         /* Initialize st_appname pointers. */
     147        1562 :         buffer = BackendAppnameBuffer;
     148      141164 :         for (i = 0; i < NumBackendStatSlots; i++)
     149             :         {
     150      139602 :             BackendStatusArray[i].st_appname = buffer;
     151      139602 :             buffer += NAMEDATALEN;
     152             :         }
     153             :     }
     154             : 
     155             :     /* Create or attach to the shared client hostname buffer */
     156        1562 :     size = mul_size(NAMEDATALEN, NumBackendStatSlots);
     157        1562 :     BackendClientHostnameBuffer = (char *)
     158        1562 :         ShmemInitStruct("Backend Client Host Name Buffer", size, &found);
     159             : 
     160        1562 :     if (!found)
     161             :     {
     162        1562 :         MemSet(BackendClientHostnameBuffer, 0, size);
     163             : 
     164             :         /* Initialize st_clienthostname pointers. */
     165        1562 :         buffer = BackendClientHostnameBuffer;
     166      141164 :         for (i = 0; i < NumBackendStatSlots; i++)
     167             :         {
     168      139602 :             BackendStatusArray[i].st_clienthostname = buffer;
     169      139602 :             buffer += NAMEDATALEN;
     170             :         }
     171             :     }
     172             : 
     173             :     /* Create or attach to the shared activity buffer */
     174        3124 :     BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
     175        1562 :                                          NumBackendStatSlots);
     176        1562 :     BackendActivityBuffer = (char *)
     177        1562 :         ShmemInitStruct("Backend Activity Buffer",
     178             :                         BackendActivityBufferSize,
     179             :                         &found);
     180             : 
     181        1562 :     if (!found)
     182             :     {
     183        1562 :         MemSet(BackendActivityBuffer, 0, BackendActivityBufferSize);
     184             : 
     185             :         /* Initialize st_activity pointers. */
     186        1562 :         buffer = BackendActivityBuffer;
     187      141164 :         for (i = 0; i < NumBackendStatSlots; i++)
     188             :         {
     189      139602 :             BackendStatusArray[i].st_activity_raw = buffer;
     190      139602 :             buffer += pgstat_track_activity_query_size;
     191             :         }
     192             :     }
     193             : 
     194             : #ifdef USE_SSL
     195             :     /* Create or attach to the shared SSL status buffer */
     196        1562 :     size = mul_size(sizeof(PgBackendSSLStatus), NumBackendStatSlots);
     197        1562 :     BackendSslStatusBuffer = (PgBackendSSLStatus *)
     198        1562 :         ShmemInitStruct("Backend SSL Status Buffer", size, &found);
     199             : 
     200        1562 :     if (!found)
     201             :     {
     202             :         PgBackendSSLStatus *ptr;
     203             : 
     204        1562 :         MemSet(BackendSslStatusBuffer, 0, size);
     205             : 
     206             :         /* Initialize st_sslstatus pointers. */
     207        1562 :         ptr = BackendSslStatusBuffer;
     208      141164 :         for (i = 0; i < NumBackendStatSlots; i++)
     209             :         {
     210      139602 :             BackendStatusArray[i].st_sslstatus = ptr;
     211      139602 :             ptr++;
     212             :         }
     213             :     }
     214             : #endif
     215             : 
     216             : #ifdef ENABLE_GSS
     217             :     /* Create or attach to the shared GSSAPI status buffer */
     218             :     size = mul_size(sizeof(PgBackendGSSStatus), NumBackendStatSlots);
     219             :     BackendGssStatusBuffer = (PgBackendGSSStatus *)
     220             :         ShmemInitStruct("Backend GSS Status Buffer", size, &found);
     221             : 
     222             :     if (!found)
     223             :     {
     224             :         PgBackendGSSStatus *ptr;
     225             : 
     226             :         MemSet(BackendGssStatusBuffer, 0, size);
     227             : 
     228             :         /* Initialize st_gssstatus pointers. */
     229             :         ptr = BackendGssStatusBuffer;
     230             :         for (i = 0; i < NumBackendStatSlots; i++)
     231             :         {
     232             :             BackendStatusArray[i].st_gssstatus = ptr;
     233             :             ptr++;
     234             :         }
     235             :     }
     236             : #endif
     237        1562 : }
     238             : 
     239             : /*
     240             :  * Initialize pgstats backend activity state, and set up our on-proc-exit
     241             :  * hook.  Called from InitPostgres and AuxiliaryProcessMain. For auxiliary
     242             :  * process, MyBackendId is invalid. Otherwise, MyBackendId must be set, but we
     243             :  * must not have started any transaction yet (since the exit hook must run
     244             :  * after the last transaction exit).
     245             :  *
     246             :  * NOTE: MyDatabaseId isn't set yet; so the shutdown hook has to be careful.
     247             :  */
     248             : void
     249       27782 : pgstat_beinit(void)
     250             : {
     251             :     /* Initialize MyBEEntry */
     252       27782 :     if (MyBackendId != InvalidBackendId)
     253             :     {
     254             :         Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends);
     255       24066 :         MyBEEntry = &BackendStatusArray[MyBackendId - 1];
     256             :     }
     257             :     else
     258             :     {
     259             :         /* Must be an auxiliary process */
     260             :         Assert(MyAuxProcType != NotAnAuxProcess);
     261             : 
     262             :         /*
     263             :          * Assign the MyBEEntry for an auxiliary process.  Since it doesn't
     264             :          * have a BackendId, the slot is statically allocated based on the
     265             :          * auxiliary process type (MyAuxProcType).  Backends use slots indexed
     266             :          * in the range from 1 to MaxBackends (inclusive), so we use
     267             :          * MaxBackends + AuxBackendType + 1 as the index of the slot for an
     268             :          * auxiliary process.
     269             :          */
     270        3716 :         MyBEEntry = &BackendStatusArray[MaxBackends + MyAuxProcType];
     271             :     }
     272             : 
     273             :     /* Set up a process-exit hook to clean up */
     274       27782 :     on_shmem_exit(pgstat_beshutdown_hook, 0);
     275       27782 : }
     276             : 
     277             : 
     278             : /* ----------
     279             :  * pgstat_bestart() -
     280             :  *
     281             :  *  Initialize this backend's entry in the PgBackendStatus array.
     282             :  *  Called from InitPostgres.
     283             :  *
     284             :  *  Apart from auxiliary processes, MyBackendId, MyDatabaseId,
     285             :  *  session userid, and application_name must be set for a
     286             :  *  backend (hence, this cannot be combined with pgstat_beinit).
     287             :  *  Note also that we must be inside a transaction if this isn't an aux
     288             :  *  process, as we may need to do encoding conversion on some strings.
     289             :  * ----------
     290             :  */
     291             : void
     292       27556 : pgstat_bestart(void)
     293             : {
     294       27556 :     volatile PgBackendStatus *vbeentry = MyBEEntry;
     295             :     PgBackendStatus lbeentry;
     296             : #ifdef USE_SSL
     297             :     PgBackendSSLStatus lsslstatus;
     298             : #endif
     299             : #ifdef ENABLE_GSS
     300             :     PgBackendGSSStatus lgssstatus;
     301             : #endif
     302             : 
     303             :     /* pgstats state must be initialized from pgstat_beinit() */
     304             :     Assert(vbeentry != NULL);
     305             : 
     306             :     /*
     307             :      * To minimize the time spent modifying the PgBackendStatus entry, and
     308             :      * avoid risk of errors inside the critical section, we first copy the
     309             :      * shared-memory struct to a local variable, then modify the data in the
     310             :      * local variable, then copy the local variable back to shared memory.
     311             :      * Only the last step has to be inside the critical section.
     312             :      *
     313             :      * Most of the data we copy from shared memory is just going to be
     314             :      * overwritten, but the struct's not so large that it's worth the
     315             :      * maintenance hassle to copy only the needful fields.
     316             :      */
     317       27556 :     memcpy(&lbeentry,
     318       27556 :            unvolatize(PgBackendStatus *, vbeentry),
     319             :            sizeof(PgBackendStatus));
     320             : 
     321             :     /* These structs can just start from zeroes each time, though */
     322             : #ifdef USE_SSL
     323       27556 :     memset(&lsslstatus, 0, sizeof(lsslstatus));
     324             : #endif
     325             : #ifdef ENABLE_GSS
     326             :     memset(&lgssstatus, 0, sizeof(lgssstatus));
     327             : #endif
     328             : 
     329             :     /*
     330             :      * Now fill in all the fields of lbeentry, except for strings that are
     331             :      * out-of-line data.  Those have to be handled separately, below.
     332             :      */
     333       27556 :     lbeentry.st_procpid = MyProcPid;
     334       27556 :     lbeentry.st_backendType = MyBackendType;
     335       27556 :     lbeentry.st_proc_start_timestamp = MyStartTimestamp;
     336       27556 :     lbeentry.st_activity_start_timestamp = 0;
     337       27556 :     lbeentry.st_state_start_timestamp = 0;
     338       27556 :     lbeentry.st_xact_start_timestamp = 0;
     339       27556 :     lbeentry.st_databaseid = MyDatabaseId;
     340             : 
     341             :     /* We have userid for client-backends, wal-sender and bgworker processes */
     342       27556 :     if (lbeentry.st_backendType == B_BACKEND
     343       10060 :         || lbeentry.st_backendType == B_WAL_SENDER
     344        8336 :         || lbeentry.st_backendType == B_BG_WORKER)
     345       23132 :         lbeentry.st_userid = GetSessionUserId();
     346             :     else
     347        4424 :         lbeentry.st_userid = InvalidOid;
     348             : 
     349             :     /*
     350             :      * We may not have a MyProcPort (eg, if this is the autovacuum process).
     351             :      * If so, use all-zeroes client address, which is dealt with specially in
     352             :      * pg_stat_get_backend_client_addr and pg_stat_get_backend_client_port.
     353             :      */
     354       27556 :     if (MyProcPort)
     355       19220 :         memcpy(&lbeentry.st_clientaddr, &MyProcPort->raddr,
     356             :                sizeof(lbeentry.st_clientaddr));
     357             :     else
     358      150048 :         MemSet(&lbeentry.st_clientaddr, 0, sizeof(lbeentry.st_clientaddr));
     359             : 
     360             : #ifdef USE_SSL
     361       27556 :     if (MyProcPort && MyProcPort->ssl_in_use)
     362             :     {
     363         154 :         lbeentry.st_ssl = true;
     364         154 :         lsslstatus.ssl_bits = be_tls_get_cipher_bits(MyProcPort);
     365         154 :         strlcpy(lsslstatus.ssl_version, be_tls_get_version(MyProcPort), NAMEDATALEN);
     366         154 :         strlcpy(lsslstatus.ssl_cipher, be_tls_get_cipher(MyProcPort), NAMEDATALEN);
     367         154 :         be_tls_get_peer_subject_name(MyProcPort, lsslstatus.ssl_client_dn, NAMEDATALEN);
     368         154 :         be_tls_get_peer_serial(MyProcPort, lsslstatus.ssl_client_serial, NAMEDATALEN);
     369         154 :         be_tls_get_peer_issuer_name(MyProcPort, lsslstatus.ssl_issuer_dn, NAMEDATALEN);
     370             :     }
     371             :     else
     372             :     {
     373       27402 :         lbeentry.st_ssl = false;
     374             :     }
     375             : #else
     376             :     lbeentry.st_ssl = false;
     377             : #endif
     378             : 
     379             : #ifdef ENABLE_GSS
     380             :     if (MyProcPort && MyProcPort->gss != NULL)
     381             :     {
     382             :         const char *princ = be_gssapi_get_princ(MyProcPort);
     383             : 
     384             :         lbeentry.st_gss = true;
     385             :         lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort);
     386             :         lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort);
     387             :         lgssstatus.gss_delegation = be_gssapi_get_delegation(MyProcPort);
     388             :         if (princ)
     389             :             strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN);
     390             :     }
     391             :     else
     392             :     {
     393             :         lbeentry.st_gss = false;
     394             :     }
     395             : #else
     396       27556 :     lbeentry.st_gss = false;
     397             : #endif
     398             : 
     399       27556 :     lbeentry.st_state = STATE_UNDEFINED;
     400       27556 :     lbeentry.st_progress_command = PROGRESS_COMMAND_INVALID;
     401       27556 :     lbeentry.st_progress_command_target = InvalidOid;
     402       27556 :     lbeentry.st_query_id = UINT64CONST(0);
     403             : 
     404             :     /*
     405             :      * we don't zero st_progress_param here to save cycles; nobody should
     406             :      * examine it until st_progress_command has been set to something other
     407             :      * than PROGRESS_COMMAND_INVALID
     408             :      */
     409             : 
     410             :     /*
     411             :      * We're ready to enter the critical section that fills the shared-memory
     412             :      * status entry.  We follow the protocol of bumping st_changecount before
     413             :      * and after; and make sure it's even afterwards.  We use a volatile
     414             :      * pointer here to ensure the compiler doesn't try to get cute.
     415             :      */
     416       27556 :     PGSTAT_BEGIN_WRITE_ACTIVITY(vbeentry);
     417             : 
     418             :     /* make sure we'll memcpy the same st_changecount back */
     419       27556 :     lbeentry.st_changecount = vbeentry->st_changecount;
     420             : 
     421       27556 :     memcpy(unvolatize(PgBackendStatus *, vbeentry),
     422             :            &lbeentry,
     423             :            sizeof(PgBackendStatus));
     424             : 
     425             :     /*
     426             :      * We can write the out-of-line strings and structs using the pointers
     427             :      * that are in lbeentry; this saves some de-volatilizing messiness.
     428             :      */
     429       27556 :     lbeentry.st_appname[0] = '\0';
     430       27556 :     if (MyProcPort && MyProcPort->remote_hostname)
     431         154 :         strlcpy(lbeentry.st_clienthostname, MyProcPort->remote_hostname,
     432             :                 NAMEDATALEN);
     433             :     else
     434       27402 :         lbeentry.st_clienthostname[0] = '\0';
     435       27556 :     lbeentry.st_activity_raw[0] = '\0';
     436             :     /* Also make sure the last byte in each string area is always 0 */
     437       27556 :     lbeentry.st_appname[NAMEDATALEN - 1] = '\0';
     438       27556 :     lbeentry.st_clienthostname[NAMEDATALEN - 1] = '\0';
     439       27556 :     lbeentry.st_activity_raw[pgstat_track_activity_query_size - 1] = '\0';
     440             : 
     441             : #ifdef USE_SSL
     442       27556 :     memcpy(lbeentry.st_sslstatus, &lsslstatus, sizeof(PgBackendSSLStatus));
     443             : #endif
     444             : #ifdef ENABLE_GSS
     445             :     memcpy(lbeentry.st_gssstatus, &lgssstatus, sizeof(PgBackendGSSStatus));
     446             : #endif
     447             : 
     448       27556 :     PGSTAT_END_WRITE_ACTIVITY(vbeentry);
     449             : 
     450             :     /* Update app name to current GUC setting */
     451       27556 :     if (application_name)
     452       27556 :         pgstat_report_appname(application_name);
     453       27556 : }
     454             : 
     455             : /*
     456             :  * Clear out our entry in the PgBackendStatus array.
     457             :  */
     458             : static void
     459       27782 : pgstat_beshutdown_hook(int code, Datum arg)
     460             : {
     461       27782 :     volatile PgBackendStatus *beentry = MyBEEntry;
     462             : 
     463             :     /*
     464             :      * Clear my status entry, following the protocol of bumping st_changecount
     465             :      * before and after.  We use a volatile pointer here to ensure the
     466             :      * compiler doesn't try to get cute.
     467             :      */
     468       27782 :     PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
     469             : 
     470       27782 :     beentry->st_procpid = 0; /* mark invalid */
     471             : 
     472       27782 :     PGSTAT_END_WRITE_ACTIVITY(beentry);
     473             : 
     474             :     /* so that functions can check if backend_status.c is up via MyBEEntry */
     475       27782 :     MyBEEntry = NULL;
     476       27782 : }
     477             : 
     478             : /*
     479             :  * Discard any data collected in the current transaction.  Any subsequent
     480             :  * request will cause new snapshots to be read.
     481             :  *
     482             :  * This is also invoked during transaction commit or abort to discard the
     483             :  * no-longer-wanted snapshot.
     484             :  */
     485             : void
     486      515298 : pgstat_clear_backend_activity_snapshot(void)
     487             : {
     488             :     /* Release memory, if any was allocated */
     489      515298 :     if (backendStatusSnapContext)
     490             :     {
     491        1186 :         MemoryContextDelete(backendStatusSnapContext);
     492        1186 :         backendStatusSnapContext = NULL;
     493             :     }
     494             : 
     495             :     /* Reset variables */
     496      515298 :     localBackendStatusTable = NULL;
     497      515298 :     localNumBackends = 0;
     498      515298 : }
     499             : 
     500             : static void
     501        1186 : pgstat_setup_backend_status_context(void)
     502             : {
     503        1186 :     if (!backendStatusSnapContext)
     504        1186 :         backendStatusSnapContext = AllocSetContextCreate(TopMemoryContext,
     505             :                                                          "Backend Status Snapshot",
     506             :                                                          ALLOCSET_SMALL_SIZES);
     507        1186 : }
     508             : 
     509             : 
     510             : /* ----------
     511             :  * pgstat_report_activity() -
     512             :  *
     513             :  *  Called from tcop/postgres.c to report what the backend is actually doing
     514             :  *  (but note cmd_str can be NULL for certain cases).
     515             :  *
     516             :  * All updates of the status entry follow the protocol of bumping
     517             :  * st_changecount before and after.  We use a volatile pointer here to
     518             :  * ensure the compiler doesn't try to get cute.
     519             :  * ----------
     520             :  */
     521             : void
     522     1168534 : pgstat_report_activity(BackendState state, const char *cmd_str)
     523             : {
     524     1168534 :     volatile PgBackendStatus *beentry = MyBEEntry;
     525             :     TimestampTz start_timestamp;
     526             :     TimestampTz current_timestamp;
     527     1168534 :     int         len = 0;
     528             : 
     529             :     TRACE_POSTGRESQL_STATEMENT_STATUS(cmd_str);
     530             : 
     531     1168534 :     if (!beentry)
     532           0 :         return;
     533             : 
     534     1168534 :     if (!pgstat_track_activities)
     535             :     {
     536           0 :         if (beentry->st_state != STATE_DISABLED)
     537             :         {
     538           0 :             volatile PGPROC *proc = MyProc;
     539             : 
     540             :             /*
     541             :              * track_activities is disabled, but we last reported a
     542             :              * non-disabled state.  As our final update, change the state and
     543             :              * clear fields we will not be updating anymore.
     544             :              */
     545           0 :             PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
     546           0 :             beentry->st_state = STATE_DISABLED;
     547           0 :             beentry->st_state_start_timestamp = 0;
     548           0 :             beentry->st_activity_raw[0] = '\0';
     549           0 :             beentry->st_activity_start_timestamp = 0;
     550             :             /* st_xact_start_timestamp and wait_event_info are also disabled */
     551           0 :             beentry->st_xact_start_timestamp = 0;
     552           0 :             beentry->st_query_id = UINT64CONST(0);
     553           0 :             proc->wait_event_info = 0;
     554           0 :             PGSTAT_END_WRITE_ACTIVITY(beentry);
     555             :         }
     556           0 :         return;
     557             :     }
     558             : 
     559             :     /*
     560             :      * To minimize the time spent modifying the entry, and avoid risk of
     561             :      * errors inside the critical section, fetch all the needed data first.
     562             :      */
     563     1168534 :     start_timestamp = GetCurrentStatementStartTimestamp();
     564     1168534 :     if (cmd_str != NULL)
     565             :     {
     566             :         /*
     567             :          * Compute length of to-be-stored string unaware of multi-byte
     568             :          * characters. For speed reasons that'll get corrected on read, rather
     569             :          * than computed every write.
     570             :          */
     571      585454 :         len = Min(strlen(cmd_str), pgstat_track_activity_query_size - 1);
     572             :     }
     573     1168534 :     current_timestamp = GetCurrentTimestamp();
     574             : 
     575             :     /*
     576             :      * If the state has changed from "active" or "idle in transaction",
     577             :      * calculate the duration.
     578             :      */
     579     1168534 :     if ((beentry->st_state == STATE_RUNNING ||
     580      583632 :          beentry->st_state == STATE_FASTPATH ||
     581      581548 :          beentry->st_state == STATE_IDLEINTRANSACTION ||
     582      458802 :          beentry->st_state == STATE_IDLEINTRANSACTION_ABORTED) &&
     583      711414 :         state != beentry->st_state)
     584             :     {
     585             :         long        secs;
     586             :         int         usecs;
     587             : 
     588      682620 :         TimestampDifference(beentry->st_state_start_timestamp,
     589             :                             current_timestamp,
     590             :                             &secs, &usecs);
     591             : 
     592      682620 :         if (beentry->st_state == STATE_RUNNING ||
     593      126496 :             beentry->st_state == STATE_FASTPATH)
     594      558208 :             pgstat_count_conn_active_time((PgStat_Counter) secs * 1000000 + usecs);
     595             :         else
     596      124412 :             pgstat_count_conn_txn_idle_time((PgStat_Counter) secs * 1000000 + usecs);
     597             :     }
     598             : 
     599             :     /*
     600             :      * Now update the status entry
     601             :      */
     602     1168534 :     PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
     603             : 
     604     1168534 :     beentry->st_state = state;
     605     1168534 :     beentry->st_state_start_timestamp = current_timestamp;
     606             : 
     607             :     /*
     608             :      * If a new query is started, we reset the query identifier as it'll only
     609             :      * be known after parse analysis, to avoid reporting last query's
     610             :      * identifier.
     611             :      */
     612     1168534 :     if (state == STATE_RUNNING)
     613      588038 :         beentry->st_query_id = UINT64CONST(0);
     614             : 
     615     1168534 :     if (cmd_str != NULL)
     616             :     {
     617      585454 :         memcpy((char *) beentry->st_activity_raw, cmd_str, len);
     618      585454 :         beentry->st_activity_raw[len] = '\0';
     619      585454 :         beentry->st_activity_start_timestamp = start_timestamp;
     620             :     }
     621             : 
     622     1168534 :     PGSTAT_END_WRITE_ACTIVITY(beentry);
     623             : }
     624             : 
     625             : /* --------
     626             :  * pgstat_report_query_id() -
     627             :  *
     628             :  * Called to update top-level query identifier.
     629             :  * --------
     630             :  */
     631             : void
     632     1813390 : pgstat_report_query_id(uint64 query_id, bool force)
     633             : {
     634     1813390 :     volatile PgBackendStatus *beentry = MyBEEntry;
     635             : 
     636             :     /*
     637             :      * if track_activities is disabled, st_query_id should already have been
     638             :      * reset
     639             :      */
     640     1813390 :     if (!beentry || !pgstat_track_activities)
     641           0 :         return;
     642             : 
     643             :     /*
     644             :      * We only report the top-level query identifiers.  The stored query_id is
     645             :      * reset when a backend calls pgstat_report_activity(STATE_RUNNING), or
     646             :      * with an explicit call to this function using the force flag.  If the
     647             :      * saved query identifier is not zero it means that it's not a top-level
     648             :      * command, so ignore the one provided unless it's an explicit call to
     649             :      * reset the identifier.
     650             :      */
     651     1813390 :     if (beentry->st_query_id != 0 && !force)
     652      103646 :         return;
     653             : 
     654             :     /*
     655             :      * Update my status entry, following the protocol of bumping
     656             :      * st_changecount before and after.  We use a volatile pointer here to
     657             :      * ensure the compiler doesn't try to get cute.
     658             :      */
     659     1709744 :     PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
     660     1709744 :     beentry->st_query_id = query_id;
     661     1709744 :     PGSTAT_END_WRITE_ACTIVITY(beentry);
     662             : }
     663             : 
     664             : 
     665             : /* ----------
     666             :  * pgstat_report_appname() -
     667             :  *
     668             :  *  Called to update our application name.
     669             :  * ----------
     670             :  */
     671             : void
     672       51878 : pgstat_report_appname(const char *appname)
     673             : {
     674       51878 :     volatile PgBackendStatus *beentry = MyBEEntry;
     675             :     int         len;
     676             : 
     677       51878 :     if (!beentry)
     678        1624 :         return;
     679             : 
     680             :     /* This should be unnecessary if GUC did its job, but be safe */
     681       50254 :     len = pg_mbcliplen(appname, strlen(appname), NAMEDATALEN - 1);
     682             : 
     683             :     /*
     684             :      * Update my status entry, following the protocol of bumping
     685             :      * st_changecount before and after.  We use a volatile pointer here to
     686             :      * ensure the compiler doesn't try to get cute.
     687             :      */
     688       50254 :     PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
     689             : 
     690       50254 :     memcpy((char *) beentry->st_appname, appname, len);
     691       50254 :     beentry->st_appname[len] = '\0';
     692             : 
     693       50254 :     PGSTAT_END_WRITE_ACTIVITY(beentry);
     694             : }
     695             : 
     696             : /*
     697             :  * Report current transaction start timestamp as the specified value.
     698             :  * Zero means there is no active transaction.
     699             :  */
     700             : void
     701     1028964 : pgstat_report_xact_timestamp(TimestampTz tstamp)
     702             : {
     703     1028964 :     volatile PgBackendStatus *beentry = MyBEEntry;
     704             : 
     705     1028964 :     if (!pgstat_track_activities || !beentry)
     706           0 :         return;
     707             : 
     708             :     /*
     709             :      * Update my status entry, following the protocol of bumping
     710             :      * st_changecount before and after.  We use a volatile pointer here to
     711             :      * ensure the compiler doesn't try to get cute.
     712             :      */
     713     1028964 :     PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
     714             : 
     715     1028964 :     beentry->st_xact_start_timestamp = tstamp;
     716             : 
     717     1028964 :     PGSTAT_END_WRITE_ACTIVITY(beentry);
     718             : }
     719             : 
     720             : /* ----------
     721             :  * pgstat_read_current_status() -
     722             :  *
     723             :  *  Copy the current contents of the PgBackendStatus array to local memory,
     724             :  *  if not already done in this transaction.
     725             :  * ----------
     726             :  */
     727             : static void
     728       10084 : pgstat_read_current_status(void)
     729             : {
     730             :     volatile PgBackendStatus *beentry;
     731             :     LocalPgBackendStatus *localtable;
     732             :     LocalPgBackendStatus *localentry;
     733             :     char       *localappname,
     734             :                *localclienthostname,
     735             :                *localactivity;
     736             : #ifdef USE_SSL
     737             :     PgBackendSSLStatus *localsslstatus;
     738             : #endif
     739             : #ifdef ENABLE_GSS
     740             :     PgBackendGSSStatus *localgssstatus;
     741             : #endif
     742             :     int         i;
     743             : 
     744       10084 :     if (localBackendStatusTable)
     745        8898 :         return;                 /* already done */
     746             : 
     747        1186 :     pgstat_setup_backend_status_context();
     748             : 
     749             :     /*
     750             :      * Allocate storage for local copy of state data.  We can presume that
     751             :      * none of these requests overflow size_t, because we already calculated
     752             :      * the same values using mul_size during shmem setup.  However, with
     753             :      * probably-silly values of pgstat_track_activity_query_size and
     754             :      * max_connections, the localactivity buffer could exceed 1GB, so use
     755             :      * "huge" allocation for that one.
     756             :      */
     757             :     localtable = (LocalPgBackendStatus *)
     758        1186 :         MemoryContextAlloc(backendStatusSnapContext,
     759        1186 :                            sizeof(LocalPgBackendStatus) * NumBackendStatSlots);
     760             :     localappname = (char *)
     761        1186 :         MemoryContextAlloc(backendStatusSnapContext,
     762        1186 :                            NAMEDATALEN * NumBackendStatSlots);
     763             :     localclienthostname = (char *)
     764        1186 :         MemoryContextAlloc(backendStatusSnapContext,
     765        1186 :                            NAMEDATALEN * NumBackendStatSlots);
     766             :     localactivity = (char *)
     767        1186 :         MemoryContextAllocHuge(backendStatusSnapContext,
     768        1186 :                                (Size) pgstat_track_activity_query_size *
     769        1186 :                                (Size) NumBackendStatSlots);
     770             : #ifdef USE_SSL
     771             :     localsslstatus = (PgBackendSSLStatus *)
     772        1186 :         MemoryContextAlloc(backendStatusSnapContext,
     773        1186 :                            sizeof(PgBackendSSLStatus) * NumBackendStatSlots);
     774             : #endif
     775             : #ifdef ENABLE_GSS
     776             :     localgssstatus = (PgBackendGSSStatus *)
     777             :         MemoryContextAlloc(backendStatusSnapContext,
     778             :                            sizeof(PgBackendGSSStatus) * NumBackendStatSlots);
     779             : #endif
     780             : 
     781        1186 :     localNumBackends = 0;
     782             : 
     783        1186 :     beentry = BackendStatusArray;
     784        1186 :     localentry = localtable;
     785       54742 :     for (i = 1; i <= NumBackendStatSlots; i++)
     786             :     {
     787             :         /*
     788             :          * Follow the protocol of retrying if st_changecount changes while we
     789             :          * copy the entry, or if it's odd.  (The check for odd is needed to
     790             :          * cover the case where we are able to completely copy the entry while
     791             :          * the source backend is between increment steps.)  We use a volatile
     792             :          * pointer here to ensure the compiler doesn't try to get cute.
     793             :          */
     794             :         for (;;)
     795           0 :         {
     796             :             int         before_changecount;
     797             :             int         after_changecount;
     798             : 
     799       53556 :             pgstat_begin_read_activity(beentry, before_changecount);
     800             : 
     801       53556 :             localentry->backendStatus.st_procpid = beentry->st_procpid;
     802             :             /* Skip all the data-copying work if entry is not in use */
     803       53556 :             if (localentry->backendStatus.st_procpid > 0)
     804             :             {
     805        8730 :                 memcpy(&localentry->backendStatus, unvolatize(PgBackendStatus *, beentry), sizeof(PgBackendStatus));
     806             : 
     807             :                 /*
     808             :                  * For each PgBackendStatus field that is a pointer, copy the
     809             :                  * pointed-to data, then adjust the local copy of the pointer
     810             :                  * field to point at the local copy of the data.
     811             :                  *
     812             :                  * strcpy is safe even if the string is modified concurrently,
     813             :                  * because there's always a \0 at the end of the buffer.
     814             :                  */
     815        8730 :                 strcpy(localappname, (char *) beentry->st_appname);
     816        8730 :                 localentry->backendStatus.st_appname = localappname;
     817        8730 :                 strcpy(localclienthostname, (char *) beentry->st_clienthostname);
     818        8730 :                 localentry->backendStatus.st_clienthostname = localclienthostname;
     819        8730 :                 strcpy(localactivity, (char *) beentry->st_activity_raw);
     820        8730 :                 localentry->backendStatus.st_activity_raw = localactivity;
     821             : #ifdef USE_SSL
     822        8730 :                 if (beentry->st_ssl)
     823             :                 {
     824          14 :                     memcpy(localsslstatus, beentry->st_sslstatus, sizeof(PgBackendSSLStatus));
     825          14 :                     localentry->backendStatus.st_sslstatus = localsslstatus;
     826             :                 }
     827             : #endif
     828             : #ifdef ENABLE_GSS
     829             :                 if (beentry->st_gss)
     830             :                 {
     831             :                     memcpy(localgssstatus, beentry->st_gssstatus, sizeof(PgBackendGSSStatus));
     832             :                     localentry->backendStatus.st_gssstatus = localgssstatus;
     833             :                 }
     834             : #endif
     835             :             }
     836             : 
     837       53556 :             pgstat_end_read_activity(beentry, after_changecount);
     838             : 
     839       53556 :             if (pgstat_read_activity_complete(before_changecount,
     840             :                                               after_changecount))
     841       53556 :                 break;
     842             : 
     843             :             /* Make sure we can break out of loop if stuck... */
     844           0 :             CHECK_FOR_INTERRUPTS();
     845             :         }
     846             : 
     847             :         /* Only valid entries get included into the local array */
     848       53556 :         if (localentry->backendStatus.st_procpid > 0)
     849             :         {
     850             :             /*
     851             :              * The BackendStatusArray index is exactly the BackendId of the
     852             :              * source backend.  Note that this means localBackendStatusTable
     853             :              * is in order by backend_id.  pgstat_get_beentry_by_backend_id()
     854             :              * depends on that.
     855             :              */
     856        8730 :             localentry->backend_id = i;
     857        8730 :             BackendIdGetTransactionIds(i,
     858             :                                        &localentry->backend_xid,
     859             :                                        &localentry->backend_xmin,
     860             :                                        &localentry->backend_subxact_count,
     861             :                                        &localentry->backend_subxact_overflowed);
     862             : 
     863        8730 :             localentry++;
     864        8730 :             localappname += NAMEDATALEN;
     865        8730 :             localclienthostname += NAMEDATALEN;
     866        8730 :             localactivity += pgstat_track_activity_query_size;
     867             : #ifdef USE_SSL
     868        8730 :             localsslstatus++;
     869             : #endif
     870             : #ifdef ENABLE_GSS
     871             :             localgssstatus++;
     872             : #endif
     873        8730 :             localNumBackends++;
     874             :         }
     875             : 
     876       53556 :         beentry++;
     877             :     }
     878             : 
     879             :     /* Set the pointer only after completion of a valid table */
     880        1186 :     localBackendStatusTable = localtable;
     881             : }
     882             : 
     883             : 
     884             : /* ----------
     885             :  * pgstat_get_backend_current_activity() -
     886             :  *
     887             :  *  Return a string representing the current activity of the backend with
     888             :  *  the specified PID.  This looks directly at the BackendStatusArray,
     889             :  *  and so will provide current information regardless of the age of our
     890             :  *  transaction's snapshot of the status array.
     891             :  *
     892             :  *  It is the caller's responsibility to invoke this only for backends whose
     893             :  *  state is expected to remain stable while the result is in use.  The
     894             :  *  only current use is in deadlock reporting, where we can expect that
     895             :  *  the target backend is blocked on a lock.  (There are corner cases
     896             :  *  where the target's wait could get aborted while we are looking at it,
     897             :  *  but the very worst consequence is to return a pointer to a string
     898             :  *  that's been changed, so we won't worry too much.)
     899             :  *
     900             :  *  Note: return strings for special cases match pg_stat_get_backend_activity.
     901             :  * ----------
     902             :  */
     903             : const char *
     904          34 : pgstat_get_backend_current_activity(int pid, bool checkUser)
     905             : {
     906             :     PgBackendStatus *beentry;
     907             :     int         i;
     908             : 
     909          34 :     beentry = BackendStatusArray;
     910         190 :     for (i = 1; i <= MaxBackends; i++)
     911             :     {
     912             :         /*
     913             :          * Although we expect the target backend's entry to be stable, that
     914             :          * doesn't imply that anyone else's is.  To avoid identifying the
     915             :          * wrong backend, while we check for a match to the desired PID we
     916             :          * must follow the protocol of retrying if st_changecount changes
     917             :          * while we examine the entry, or if it's odd.  (This might be
     918             :          * unnecessary, since fetching or storing an int is almost certainly
     919             :          * atomic, but let's play it safe.)  We use a volatile pointer here to
     920             :          * ensure the compiler doesn't try to get cute.
     921             :          */
     922         190 :         volatile PgBackendStatus *vbeentry = beentry;
     923             :         bool        found;
     924             : 
     925             :         for (;;)
     926           0 :         {
     927             :             int         before_changecount;
     928             :             int         after_changecount;
     929             : 
     930         190 :             pgstat_begin_read_activity(vbeentry, before_changecount);
     931             : 
     932         190 :             found = (vbeentry->st_procpid == pid);
     933             : 
     934         190 :             pgstat_end_read_activity(vbeentry, after_changecount);
     935             : 
     936         190 :             if (pgstat_read_activity_complete(before_changecount,
     937             :                                               after_changecount))
     938         190 :                 break;
     939             : 
     940             :             /* Make sure we can break out of loop if stuck... */
     941           0 :             CHECK_FOR_INTERRUPTS();
     942             :         }
     943             : 
     944         190 :         if (found)
     945             :         {
     946             :             /* Now it is safe to use the non-volatile pointer */
     947          34 :             if (checkUser && !superuser() && beentry->st_userid != GetUserId())
     948           0 :                 return "<insufficient privilege>";
     949          34 :             else if (*(beentry->st_activity_raw) == '\0')
     950          10 :                 return "<command string not enabled>";
     951             :             else
     952             :             {
     953             :                 /* this'll leak a bit of memory, but that seems acceptable */
     954          24 :                 return pgstat_clip_activity(beentry->st_activity_raw);
     955             :             }
     956             :         }
     957             : 
     958         156 :         beentry++;
     959             :     }
     960             : 
     961             :     /* If we get here, caller is in error ... */
     962           0 :     return "<backend information not available>";
     963             : }
     964             : 
     965             : /* ----------
     966             :  * pgstat_get_crashed_backend_activity() -
     967             :  *
     968             :  *  Return a string representing the current activity of the backend with
     969             :  *  the specified PID.  Like the function above, but reads shared memory with
     970             :  *  the expectation that it may be corrupt.  On success, copy the string
     971             :  *  into the "buffer" argument and return that pointer.  On failure,
     972             :  *  return NULL.
     973             :  *
     974             :  *  This function is only intended to be used by the postmaster to report the
     975             :  *  query that crashed a backend.  In particular, no attempt is made to
     976             :  *  follow the correct concurrency protocol when accessing the
     977             :  *  BackendStatusArray.  But that's OK, in the worst case we'll return a
     978             :  *  corrupted message.  We also must take care not to trip on ereport(ERROR).
     979             :  * ----------
     980             :  */
     981             : const char *
     982        1692 : pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen)
     983             : {
     984             :     volatile PgBackendStatus *beentry;
     985             :     int         i;
     986             : 
     987        1692 :     beentry = BackendStatusArray;
     988             : 
     989             :     /*
     990             :      * We probably shouldn't get here before shared memory has been set up,
     991             :      * but be safe.
     992             :      */
     993        1692 :     if (beentry == NULL || BackendActivityBuffer == NULL)
     994           0 :         return NULL;
     995             : 
     996      153404 :     for (i = 1; i <= MaxBackends; i++)
     997             :     {
     998      151822 :         if (beentry->st_procpid == pid)
     999             :         {
    1000             :             /* Read pointer just once, so it can't change after validation */
    1001         110 :             const char *activity = beentry->st_activity_raw;
    1002             :             const char *activity_last;
    1003             : 
    1004             :             /*
    1005             :              * We mustn't access activity string before we verify that it
    1006             :              * falls within the BackendActivityBuffer. To make sure that the
    1007             :              * entire string including its ending is contained within the
    1008             :              * buffer, subtract one activity length from the buffer size.
    1009             :              */
    1010         110 :             activity_last = BackendActivityBuffer + BackendActivityBufferSize
    1011         110 :                 - pgstat_track_activity_query_size;
    1012             : 
    1013         110 :             if (activity < BackendActivityBuffer ||
    1014             :                 activity > activity_last)
    1015           0 :                 return NULL;
    1016             : 
    1017             :             /* If no string available, no point in a report */
    1018         110 :             if (activity[0] == '\0')
    1019           0 :                 return NULL;
    1020             : 
    1021             :             /*
    1022             :              * Copy only ASCII-safe characters so we don't run into encoding
    1023             :              * problems when reporting the message; and be sure not to run off
    1024             :              * the end of memory.  As only ASCII characters are reported, it
    1025             :              * doesn't seem necessary to perform multibyte aware clipping.
    1026             :              */
    1027         110 :             ascii_safe_strlcpy(buffer, activity,
    1028         110 :                                Min(buflen, pgstat_track_activity_query_size));
    1029             : 
    1030         110 :             return buffer;
    1031             :         }
    1032             : 
    1033      151712 :         beentry++;
    1034             :     }
    1035             : 
    1036             :     /* PID not found */
    1037        1582 :     return NULL;
    1038             : }
    1039             : 
    1040             : /* ----------
    1041             :  * pgstat_get_my_query_id() -
    1042             :  *
    1043             :  * Return current backend's query identifier.
    1044             :  */
    1045             : uint64
    1046         724 : pgstat_get_my_query_id(void)
    1047             : {
    1048         724 :     if (!MyBEEntry)
    1049          28 :         return 0;
    1050             : 
    1051             :     /*
    1052             :      * There's no need for a lock around pgstat_begin_read_activity /
    1053             :      * pgstat_end_read_activity here as it's only called from
    1054             :      * pg_stat_get_activity which is already protected, or from the same
    1055             :      * backend which means that there won't be concurrent writes.
    1056             :      */
    1057         696 :     return MyBEEntry->st_query_id;
    1058             : }
    1059             : 
    1060             : /* ----------
    1061             :  * cmp_lbestatus
    1062             :  *
    1063             :  *  Comparison function for bsearch() on an array of LocalPgBackendStatus.
    1064             :  *  The backend_id field is used to compare the arguments.
    1065             :  * ----------
    1066             :  */
    1067             : static int
    1068         246 : cmp_lbestatus(const void *a, const void *b)
    1069             : {
    1070         246 :     const LocalPgBackendStatus *lbestatus1 = (const LocalPgBackendStatus *) a;
    1071         246 :     const LocalPgBackendStatus *lbestatus2 = (const LocalPgBackendStatus *) b;
    1072             : 
    1073         246 :     return lbestatus1->backend_id - lbestatus2->backend_id;
    1074             : }
    1075             : 
    1076             : /* ----------
    1077             :  * pgstat_get_beentry_by_backend_id() -
    1078             :  *
    1079             :  *  Support function for the SQL-callable pgstat* functions. Returns
    1080             :  *  our local copy of the current-activity entry for one backend,
    1081             :  *  or NULL if the given beid doesn't identify any known session.
    1082             :  *
    1083             :  *  The beid argument is the BackendId of the desired session
    1084             :  *  (note that this is unlike pgstat_get_local_beentry_by_index()).
    1085             :  *
    1086             :  *  NB: caller is responsible for a check if the user is permitted to see
    1087             :  *  this info (especially the querystring).
    1088             :  * ----------
    1089             :  */
    1090             : PgBackendStatus *
    1091          78 : pgstat_get_beentry_by_backend_id(BackendId beid)
    1092             : {
    1093          78 :     LocalPgBackendStatus *ret = pgstat_get_local_beentry_by_backend_id(beid);
    1094             : 
    1095          78 :     if (ret)
    1096          78 :         return &ret->backendStatus;
    1097             : 
    1098           0 :     return NULL;
    1099             : }
    1100             : 
    1101             : 
    1102             : /* ----------
    1103             :  * pgstat_get_local_beentry_by_backend_id() -
    1104             :  *
    1105             :  *  Like pgstat_get_beentry_by_backend_id() but with locally computed additions
    1106             :  *  (like xid and xmin values of the backend)
    1107             :  *
    1108             :  *  The beid argument is the BackendId of the desired session
    1109             :  *  (note that this is unlike pgstat_get_local_beentry_by_index()).
    1110             :  *
    1111             :  *  NB: caller is responsible for checking if the user is permitted to see this
    1112             :  *  info (especially the querystring).
    1113             :  * ----------
    1114             :  */
    1115             : LocalPgBackendStatus *
    1116          78 : pgstat_get_local_beentry_by_backend_id(BackendId beid)
    1117             : {
    1118             :     LocalPgBackendStatus key;
    1119             : 
    1120          78 :     pgstat_read_current_status();
    1121             : 
    1122             :     /*
    1123             :      * Since the localBackendStatusTable is in order by backend_id, we can use
    1124             :      * bsearch() to search it efficiently.
    1125             :      */
    1126          78 :     key.backend_id = beid;
    1127          78 :     return bsearch(&key, localBackendStatusTable, localNumBackends,
    1128             :                    sizeof(LocalPgBackendStatus), cmp_lbestatus);
    1129             : }
    1130             : 
    1131             : 
    1132             : /* ----------
    1133             :  * pgstat_get_local_beentry_by_index() -
    1134             :  *
    1135             :  *  Like pgstat_get_beentry_by_backend_id() but with locally computed additions
    1136             :  *  (like xid and xmin values of the backend)
    1137             :  *
    1138             :  *  The idx argument is a 1-based index in the localBackendStatusTable
    1139             :  *  (note that this is unlike pgstat_get_beentry_by_backend_id()).
    1140             :  *  Returns NULL if the argument is out of range (no current caller does that).
    1141             :  *
    1142             :  *  NB: caller is responsible for a check if the user is permitted to see
    1143             :  *  this info (especially the querystring).
    1144             :  * ----------
    1145             :  */
    1146             : LocalPgBackendStatus *
    1147        8740 : pgstat_get_local_beentry_by_index(int idx)
    1148             : {
    1149        8740 :     pgstat_read_current_status();
    1150             : 
    1151        8740 :     if (idx < 1 || idx > localNumBackends)
    1152           0 :         return NULL;
    1153             : 
    1154        8740 :     return &localBackendStatusTable[idx - 1];
    1155             : }
    1156             : 
    1157             : 
    1158             : /* ----------
    1159             :  * pgstat_fetch_stat_numbackends() -
    1160             :  *
    1161             :  *  Support function for the SQL-callable pgstat* functions. Returns
    1162             :  *  the number of sessions known in the localBackendStatusTable, i.e.
    1163             :  *  the maximum 1-based index to pass to pgstat_get_local_beentry_by_index().
    1164             :  * ----------
    1165             :  */
    1166             : int
    1167        1266 : pgstat_fetch_stat_numbackends(void)
    1168             : {
    1169        1266 :     pgstat_read_current_status();
    1170             : 
    1171        1266 :     return localNumBackends;
    1172             : }
    1173             : 
    1174             : /*
    1175             :  * Convert a potentially unsafely truncated activity string (see
    1176             :  * PgBackendStatus.st_activity_raw's documentation) into a correctly truncated
    1177             :  * one.
    1178             :  *
    1179             :  * The returned string is allocated in the caller's memory context and may be
    1180             :  * freed.
    1181             :  */
    1182             : char *
    1183        8484 : pgstat_clip_activity(const char *raw_activity)
    1184             : {
    1185             :     char       *activity;
    1186             :     int         rawlen;
    1187             :     int         cliplen;
    1188             : 
    1189             :     /*
    1190             :      * Some callers, like pgstat_get_backend_current_activity(), do not
    1191             :      * guarantee that the buffer isn't concurrently modified. We try to take
    1192             :      * care that the buffer is always terminated by a NUL byte regardless, but
    1193             :      * let's still be paranoid about the string's length. In those cases the
    1194             :      * underlying buffer is guaranteed to be pgstat_track_activity_query_size
    1195             :      * large.
    1196             :      */
    1197        8484 :     activity = pnstrdup(raw_activity, pgstat_track_activity_query_size - 1);
    1198             : 
    1199             :     /* now double-guaranteed to be NUL terminated */
    1200        8484 :     rawlen = strlen(activity);
    1201             : 
    1202             :     /*
    1203             :      * All supported server-encodings make it possible to determine the length
    1204             :      * of a multi-byte character from its first byte (this is not the case for
    1205             :      * client encodings, see GB18030). As st_activity is always stored using
    1206             :      * server encoding, this allows us to perform multi-byte aware truncation,
    1207             :      * even if the string earlier was truncated in the middle of a multi-byte
    1208             :      * character.
    1209             :      */
    1210        8484 :     cliplen = pg_mbcliplen(activity, rawlen,
    1211             :                            pgstat_track_activity_query_size - 1);
    1212             : 
    1213        8484 :     activity[cliplen] = '\0';
    1214             : 
    1215        8484 :     return activity;
    1216             : }

Generated by: LCOV version 1.14