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