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