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

Generated by: LCOV version 1.14