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