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

Generated by: LCOV version 1.14