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