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 2934 : BackendStatusShmemSize(void)
85 : {
86 : Size size;
87 :
88 : /* BackendStatusArray: */
89 2934 : size = mul_size(sizeof(PgBackendStatus), NumBackendStatSlots);
90 : /* BackendAppnameBuffer: */
91 2934 : size = add_size(size,
92 2934 : mul_size(NAMEDATALEN, NumBackendStatSlots));
93 : /* BackendClientHostnameBuffer: */
94 2934 : size = add_size(size,
95 2934 : mul_size(NAMEDATALEN, NumBackendStatSlots));
96 : /* BackendActivityBuffer: */
97 2934 : size = add_size(size,
98 2934 : mul_size(pgstat_track_activity_query_size, NumBackendStatSlots));
99 : #ifdef USE_SSL
100 : /* BackendSslStatusBuffer: */
101 2934 : size = add_size(size,
102 2934 : 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 2934 : return size;
110 : }
111 :
112 : /*
113 : * Initialize the shared status array and several string buffers
114 : * during postmaster startup.
115 : */
116 : void
117 1562 : 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 1562 : size = mul_size(sizeof(PgBackendStatus), NumBackendStatSlots);
126 1562 : BackendStatusArray = (PgBackendStatus *)
127 1562 : ShmemInitStruct("Backend Status Array", size, &found);
128 :
129 1562 : if (!found)
130 : {
131 : /*
132 : * We're the first - initialize.
133 : */
134 1562 : MemSet(BackendStatusArray, 0, size);
135 : }
136 :
137 : /* Create or attach to the shared appname buffer */
138 1562 : size = mul_size(NAMEDATALEN, NumBackendStatSlots);
139 1562 : BackendAppnameBuffer = (char *)
140 1562 : ShmemInitStruct("Backend Application Name Buffer", size, &found);
141 :
142 1562 : if (!found)
143 : {
144 1562 : MemSet(BackendAppnameBuffer, 0, size);
145 :
146 : /* Initialize st_appname pointers. */
147 1562 : buffer = BackendAppnameBuffer;
148 141164 : for (i = 0; i < NumBackendStatSlots; i++)
149 : {
150 139602 : BackendStatusArray[i].st_appname = buffer;
151 139602 : buffer += NAMEDATALEN;
152 : }
153 : }
154 :
155 : /* Create or attach to the shared client hostname buffer */
156 1562 : size = mul_size(NAMEDATALEN, NumBackendStatSlots);
157 1562 : BackendClientHostnameBuffer = (char *)
158 1562 : ShmemInitStruct("Backend Client Host Name Buffer", size, &found);
159 :
160 1562 : if (!found)
161 : {
162 1562 : MemSet(BackendClientHostnameBuffer, 0, size);
163 :
164 : /* Initialize st_clienthostname pointers. */
165 1562 : buffer = BackendClientHostnameBuffer;
166 141164 : for (i = 0; i < NumBackendStatSlots; i++)
167 : {
168 139602 : BackendStatusArray[i].st_clienthostname = buffer;
169 139602 : buffer += NAMEDATALEN;
170 : }
171 : }
172 :
173 : /* Create or attach to the shared activity buffer */
174 3124 : BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
175 1562 : NumBackendStatSlots);
176 1562 : BackendActivityBuffer = (char *)
177 1562 : ShmemInitStruct("Backend Activity Buffer",
178 : BackendActivityBufferSize,
179 : &found);
180 :
181 1562 : if (!found)
182 : {
183 1562 : MemSet(BackendActivityBuffer, 0, BackendActivityBufferSize);
184 :
185 : /* Initialize st_activity pointers. */
186 1562 : buffer = BackendActivityBuffer;
187 141164 : for (i = 0; i < NumBackendStatSlots; i++)
188 : {
189 139602 : BackendStatusArray[i].st_activity_raw = buffer;
190 139602 : buffer += pgstat_track_activity_query_size;
191 : }
192 : }
193 :
194 : #ifdef USE_SSL
195 : /* Create or attach to the shared SSL status buffer */
196 1562 : size = mul_size(sizeof(PgBackendSSLStatus), NumBackendStatSlots);
197 1562 : BackendSslStatusBuffer = (PgBackendSSLStatus *)
198 1562 : ShmemInitStruct("Backend SSL Status Buffer", size, &found);
199 :
200 1562 : if (!found)
201 : {
202 : PgBackendSSLStatus *ptr;
203 :
204 1562 : MemSet(BackendSslStatusBuffer, 0, size);
205 :
206 : /* Initialize st_sslstatus pointers. */
207 1562 : ptr = BackendSslStatusBuffer;
208 141164 : for (i = 0; i < NumBackendStatSlots; i++)
209 : {
210 139602 : BackendStatusArray[i].st_sslstatus = ptr;
211 139602 : 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 1562 : }
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 27782 : pgstat_beinit(void)
250 : {
251 : /* Initialize MyBEEntry */
252 27782 : if (MyBackendId != InvalidBackendId)
253 : {
254 : Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends);
255 24066 : 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 3716 : MyBEEntry = &BackendStatusArray[MaxBackends + MyAuxProcType];
271 : }
272 :
273 : /* Set up a process-exit hook to clean up */
274 27782 : on_shmem_exit(pgstat_beshutdown_hook, 0);
275 27782 : }
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 27556 : pgstat_bestart(void)
293 : {
294 27556 : 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 27556 : memcpy(&lbeentry,
318 27556 : unvolatize(PgBackendStatus *, vbeentry),
319 : sizeof(PgBackendStatus));
320 :
321 : /* These structs can just start from zeroes each time, though */
322 : #ifdef USE_SSL
323 27556 : 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 27556 : lbeentry.st_procpid = MyProcPid;
334 27556 : lbeentry.st_backendType = MyBackendType;
335 27556 : lbeentry.st_proc_start_timestamp = MyStartTimestamp;
336 27556 : lbeentry.st_activity_start_timestamp = 0;
337 27556 : lbeentry.st_state_start_timestamp = 0;
338 27556 : lbeentry.st_xact_start_timestamp = 0;
339 27556 : lbeentry.st_databaseid = MyDatabaseId;
340 :
341 : /* We have userid for client-backends, wal-sender and bgworker processes */
342 27556 : if (lbeentry.st_backendType == B_BACKEND
343 10060 : || lbeentry.st_backendType == B_WAL_SENDER
344 8336 : || lbeentry.st_backendType == B_BG_WORKER)
345 23132 : lbeentry.st_userid = GetSessionUserId();
346 : else
347 4424 : 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 27556 : if (MyProcPort)
355 19220 : memcpy(&lbeentry.st_clientaddr, &MyProcPort->raddr,
356 : sizeof(lbeentry.st_clientaddr));
357 : else
358 150048 : MemSet(&lbeentry.st_clientaddr, 0, sizeof(lbeentry.st_clientaddr));
359 :
360 : #ifdef USE_SSL
361 27556 : 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 27402 : 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 27556 : lbeentry.st_gss = false;
397 : #endif
398 :
399 27556 : lbeentry.st_state = STATE_UNDEFINED;
400 27556 : lbeentry.st_progress_command = PROGRESS_COMMAND_INVALID;
401 27556 : lbeentry.st_progress_command_target = InvalidOid;
402 27556 : 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 27556 : PGSTAT_BEGIN_WRITE_ACTIVITY(vbeentry);
417 :
418 : /* make sure we'll memcpy the same st_changecount back */
419 27556 : lbeentry.st_changecount = vbeentry->st_changecount;
420 :
421 27556 : 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 27556 : lbeentry.st_appname[0] = '\0';
430 27556 : if (MyProcPort && MyProcPort->remote_hostname)
431 154 : strlcpy(lbeentry.st_clienthostname, MyProcPort->remote_hostname,
432 : NAMEDATALEN);
433 : else
434 27402 : lbeentry.st_clienthostname[0] = '\0';
435 27556 : lbeentry.st_activity_raw[0] = '\0';
436 : /* Also make sure the last byte in each string area is always 0 */
437 27556 : lbeentry.st_appname[NAMEDATALEN - 1] = '\0';
438 27556 : lbeentry.st_clienthostname[NAMEDATALEN - 1] = '\0';
439 27556 : lbeentry.st_activity_raw[pgstat_track_activity_query_size - 1] = '\0';
440 :
441 : #ifdef USE_SSL
442 27556 : 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 27556 : PGSTAT_END_WRITE_ACTIVITY(vbeentry);
449 :
450 : /* Update app name to current GUC setting */
451 27556 : if (application_name)
452 27556 : pgstat_report_appname(application_name);
453 27556 : }
454 :
455 : /*
456 : * Clear out our entry in the PgBackendStatus array.
457 : */
458 : static void
459 27782 : pgstat_beshutdown_hook(int code, Datum arg)
460 : {
461 27782 : 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 27782 : PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
469 :
470 27782 : beentry->st_procpid = 0; /* mark invalid */
471 :
472 27782 : PGSTAT_END_WRITE_ACTIVITY(beentry);
473 :
474 : /* so that functions can check if backend_status.c is up via MyBEEntry */
475 27782 : MyBEEntry = NULL;
476 27782 : }
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 515298 : pgstat_clear_backend_activity_snapshot(void)
487 : {
488 : /* Release memory, if any was allocated */
489 515298 : if (backendStatusSnapContext)
490 : {
491 1186 : MemoryContextDelete(backendStatusSnapContext);
492 1186 : backendStatusSnapContext = NULL;
493 : }
494 :
495 : /* Reset variables */
496 515298 : localBackendStatusTable = NULL;
497 515298 : localNumBackends = 0;
498 515298 : }
499 :
500 : static void
501 1186 : pgstat_setup_backend_status_context(void)
502 : {
503 1186 : if (!backendStatusSnapContext)
504 1186 : backendStatusSnapContext = AllocSetContextCreate(TopMemoryContext,
505 : "Backend Status Snapshot",
506 : ALLOCSET_SMALL_SIZES);
507 1186 : }
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 1168534 : pgstat_report_activity(BackendState state, const char *cmd_str)
523 : {
524 1168534 : volatile PgBackendStatus *beentry = MyBEEntry;
525 : TimestampTz start_timestamp;
526 : TimestampTz current_timestamp;
527 1168534 : int len = 0;
528 :
529 : TRACE_POSTGRESQL_STATEMENT_STATUS(cmd_str);
530 :
531 1168534 : if (!beentry)
532 0 : return;
533 :
534 1168534 : 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 1168534 : start_timestamp = GetCurrentStatementStartTimestamp();
564 1168534 : 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 585454 : len = Min(strlen(cmd_str), pgstat_track_activity_query_size - 1);
572 : }
573 1168534 : current_timestamp = GetCurrentTimestamp();
574 :
575 : /*
576 : * If the state has changed from "active" or "idle in transaction",
577 : * calculate the duration.
578 : */
579 1168534 : if ((beentry->st_state == STATE_RUNNING ||
580 583632 : beentry->st_state == STATE_FASTPATH ||
581 581548 : beentry->st_state == STATE_IDLEINTRANSACTION ||
582 458802 : beentry->st_state == STATE_IDLEINTRANSACTION_ABORTED) &&
583 711414 : state != beentry->st_state)
584 : {
585 : long secs;
586 : int usecs;
587 :
588 682620 : TimestampDifference(beentry->st_state_start_timestamp,
589 : current_timestamp,
590 : &secs, &usecs);
591 :
592 682620 : if (beentry->st_state == STATE_RUNNING ||
593 126496 : beentry->st_state == STATE_FASTPATH)
594 558208 : pgstat_count_conn_active_time((PgStat_Counter) secs * 1000000 + usecs);
595 : else
596 124412 : pgstat_count_conn_txn_idle_time((PgStat_Counter) secs * 1000000 + usecs);
597 : }
598 :
599 : /*
600 : * Now update the status entry
601 : */
602 1168534 : PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
603 :
604 1168534 : beentry->st_state = state;
605 1168534 : 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 1168534 : if (state == STATE_RUNNING)
613 588038 : beentry->st_query_id = UINT64CONST(0);
614 :
615 1168534 : if (cmd_str != NULL)
616 : {
617 585454 : memcpy((char *) beentry->st_activity_raw, cmd_str, len);
618 585454 : beentry->st_activity_raw[len] = '\0';
619 585454 : beentry->st_activity_start_timestamp = start_timestamp;
620 : }
621 :
622 1168534 : 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 1813390 : pgstat_report_query_id(uint64 query_id, bool force)
633 : {
634 1813390 : volatile PgBackendStatus *beentry = MyBEEntry;
635 :
636 : /*
637 : * if track_activities is disabled, st_query_id should already have been
638 : * reset
639 : */
640 1813390 : 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 1813390 : if (beentry->st_query_id != 0 && !force)
652 103646 : 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 1709744 : PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
660 1709744 : beentry->st_query_id = query_id;
661 1709744 : 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 51878 : pgstat_report_appname(const char *appname)
673 : {
674 51878 : volatile PgBackendStatus *beentry = MyBEEntry;
675 : int len;
676 :
677 51878 : if (!beentry)
678 1624 : return;
679 :
680 : /* This should be unnecessary if GUC did its job, but be safe */
681 50254 : 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 50254 : PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
689 :
690 50254 : memcpy((char *) beentry->st_appname, appname, len);
691 50254 : beentry->st_appname[len] = '\0';
692 :
693 50254 : 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 1028964 : pgstat_report_xact_timestamp(TimestampTz tstamp)
702 : {
703 1028964 : volatile PgBackendStatus *beentry = MyBEEntry;
704 :
705 1028964 : 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 1028964 : PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
714 :
715 1028964 : beentry->st_xact_start_timestamp = tstamp;
716 :
717 1028964 : 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 10084 : 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 10084 : if (localBackendStatusTable)
745 8898 : return; /* already done */
746 :
747 1186 : 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 1186 : MemoryContextAlloc(backendStatusSnapContext,
759 1186 : sizeof(LocalPgBackendStatus) * NumBackendStatSlots);
760 : localappname = (char *)
761 1186 : MemoryContextAlloc(backendStatusSnapContext,
762 1186 : NAMEDATALEN * NumBackendStatSlots);
763 : localclienthostname = (char *)
764 1186 : MemoryContextAlloc(backendStatusSnapContext,
765 1186 : NAMEDATALEN * NumBackendStatSlots);
766 : localactivity = (char *)
767 1186 : MemoryContextAllocHuge(backendStatusSnapContext,
768 1186 : (Size) pgstat_track_activity_query_size *
769 1186 : (Size) NumBackendStatSlots);
770 : #ifdef USE_SSL
771 : localsslstatus = (PgBackendSSLStatus *)
772 1186 : MemoryContextAlloc(backendStatusSnapContext,
773 1186 : sizeof(PgBackendSSLStatus) * NumBackendStatSlots);
774 : #endif
775 : #ifdef ENABLE_GSS
776 : localgssstatus = (PgBackendGSSStatus *)
777 : MemoryContextAlloc(backendStatusSnapContext,
778 : sizeof(PgBackendGSSStatus) * NumBackendStatSlots);
779 : #endif
780 :
781 1186 : localNumBackends = 0;
782 :
783 1186 : beentry = BackendStatusArray;
784 1186 : localentry = localtable;
785 54742 : for (i = 1; i <= NumBackendStatSlots; i++)
786 : {
787 : /*
788 : * Follow the protocol of retrying if st_changecount changes while we
789 : * copy the entry, or if it's odd. (The check for odd is needed to
790 : * cover the case where we are able to completely copy the entry while
791 : * the source backend is between increment steps.) We use a volatile
792 : * pointer here to ensure the compiler doesn't try to get cute.
793 : */
794 : for (;;)
795 0 : {
796 : int before_changecount;
797 : int after_changecount;
798 :
799 53556 : pgstat_begin_read_activity(beentry, before_changecount);
800 :
801 53556 : localentry->backendStatus.st_procpid = beentry->st_procpid;
802 : /* Skip all the data-copying work if entry is not in use */
803 53556 : if (localentry->backendStatus.st_procpid > 0)
804 : {
805 8730 : memcpy(&localentry->backendStatus, unvolatize(PgBackendStatus *, beentry), sizeof(PgBackendStatus));
806 :
807 : /*
808 : * For each PgBackendStatus field that is a pointer, copy the
809 : * pointed-to data, then adjust the local copy of the pointer
810 : * field to point at the local copy of the data.
811 : *
812 : * strcpy is safe even if the string is modified concurrently,
813 : * because there's always a \0 at the end of the buffer.
814 : */
815 8730 : strcpy(localappname, (char *) beentry->st_appname);
816 8730 : localentry->backendStatus.st_appname = localappname;
817 8730 : strcpy(localclienthostname, (char *) beentry->st_clienthostname);
818 8730 : localentry->backendStatus.st_clienthostname = localclienthostname;
819 8730 : strcpy(localactivity, (char *) beentry->st_activity_raw);
820 8730 : localentry->backendStatus.st_activity_raw = localactivity;
821 : #ifdef USE_SSL
822 8730 : if (beentry->st_ssl)
823 : {
824 14 : memcpy(localsslstatus, beentry->st_sslstatus, sizeof(PgBackendSSLStatus));
825 14 : localentry->backendStatus.st_sslstatus = localsslstatus;
826 : }
827 : #endif
828 : #ifdef ENABLE_GSS
829 : if (beentry->st_gss)
830 : {
831 : memcpy(localgssstatus, beentry->st_gssstatus, sizeof(PgBackendGSSStatus));
832 : localentry->backendStatus.st_gssstatus = localgssstatus;
833 : }
834 : #endif
835 : }
836 :
837 53556 : pgstat_end_read_activity(beentry, after_changecount);
838 :
839 53556 : if (pgstat_read_activity_complete(before_changecount,
840 : after_changecount))
841 53556 : break;
842 :
843 : /* Make sure we can break out of loop if stuck... */
844 0 : CHECK_FOR_INTERRUPTS();
845 : }
846 :
847 : /* Only valid entries get included into the local array */
848 53556 : if (localentry->backendStatus.st_procpid > 0)
849 : {
850 : /*
851 : * The BackendStatusArray index is exactly the BackendId of the
852 : * source backend. Note that this means localBackendStatusTable
853 : * is in order by backend_id. pgstat_get_beentry_by_backend_id()
854 : * depends on that.
855 : */
856 8730 : localentry->backend_id = i;
857 8730 : BackendIdGetTransactionIds(i,
858 : &localentry->backend_xid,
859 : &localentry->backend_xmin,
860 : &localentry->backend_subxact_count,
861 : &localentry->backend_subxact_overflowed);
862 :
863 8730 : localentry++;
864 8730 : localappname += NAMEDATALEN;
865 8730 : localclienthostname += NAMEDATALEN;
866 8730 : localactivity += pgstat_track_activity_query_size;
867 : #ifdef USE_SSL
868 8730 : localsslstatus++;
869 : #endif
870 : #ifdef ENABLE_GSS
871 : localgssstatus++;
872 : #endif
873 8730 : localNumBackends++;
874 : }
875 :
876 53556 : beentry++;
877 : }
878 :
879 : /* Set the pointer only after completion of a valid table */
880 1186 : localBackendStatusTable = localtable;
881 : }
882 :
883 :
884 : /* ----------
885 : * pgstat_get_backend_current_activity() -
886 : *
887 : * Return a string representing the current activity of the backend with
888 : * the specified PID. This looks directly at the BackendStatusArray,
889 : * and so will provide current information regardless of the age of our
890 : * transaction's snapshot of the status array.
891 : *
892 : * It is the caller's responsibility to invoke this only for backends whose
893 : * state is expected to remain stable while the result is in use. The
894 : * only current use is in deadlock reporting, where we can expect that
895 : * the target backend is blocked on a lock. (There are corner cases
896 : * where the target's wait could get aborted while we are looking at it,
897 : * but the very worst consequence is to return a pointer to a string
898 : * that's been changed, so we won't worry too much.)
899 : *
900 : * Note: return strings for special cases match pg_stat_get_backend_activity.
901 : * ----------
902 : */
903 : const char *
904 34 : pgstat_get_backend_current_activity(int pid, bool checkUser)
905 : {
906 : PgBackendStatus *beentry;
907 : int i;
908 :
909 34 : beentry = BackendStatusArray;
910 190 : for (i = 1; i <= MaxBackends; i++)
911 : {
912 : /*
913 : * Although we expect the target backend's entry to be stable, that
914 : * doesn't imply that anyone else's is. To avoid identifying the
915 : * wrong backend, while we check for a match to the desired PID we
916 : * must follow the protocol of retrying if st_changecount changes
917 : * while we examine the entry, or if it's odd. (This might be
918 : * unnecessary, since fetching or storing an int is almost certainly
919 : * atomic, but let's play it safe.) We use a volatile pointer here to
920 : * ensure the compiler doesn't try to get cute.
921 : */
922 190 : volatile PgBackendStatus *vbeentry = beentry;
923 : bool found;
924 :
925 : for (;;)
926 0 : {
927 : int before_changecount;
928 : int after_changecount;
929 :
930 190 : pgstat_begin_read_activity(vbeentry, before_changecount);
931 :
932 190 : found = (vbeentry->st_procpid == pid);
933 :
934 190 : pgstat_end_read_activity(vbeentry, after_changecount);
935 :
936 190 : if (pgstat_read_activity_complete(before_changecount,
937 : after_changecount))
938 190 : break;
939 :
940 : /* Make sure we can break out of loop if stuck... */
941 0 : CHECK_FOR_INTERRUPTS();
942 : }
943 :
944 190 : if (found)
945 : {
946 : /* Now it is safe to use the non-volatile pointer */
947 34 : if (checkUser && !superuser() && beentry->st_userid != GetUserId())
948 0 : return "<insufficient privilege>";
949 34 : else if (*(beentry->st_activity_raw) == '\0')
950 10 : return "<command string not enabled>";
951 : else
952 : {
953 : /* this'll leak a bit of memory, but that seems acceptable */
954 24 : return pgstat_clip_activity(beentry->st_activity_raw);
955 : }
956 : }
957 :
958 156 : beentry++;
959 : }
960 :
961 : /* If we get here, caller is in error ... */
962 0 : return "<backend information not available>";
963 : }
964 :
965 : /* ----------
966 : * pgstat_get_crashed_backend_activity() -
967 : *
968 : * Return a string representing the current activity of the backend with
969 : * the specified PID. Like the function above, but reads shared memory with
970 : * the expectation that it may be corrupt. On success, copy the string
971 : * into the "buffer" argument and return that pointer. On failure,
972 : * return NULL.
973 : *
974 : * This function is only intended to be used by the postmaster to report the
975 : * query that crashed a backend. In particular, no attempt is made to
976 : * follow the correct concurrency protocol when accessing the
977 : * BackendStatusArray. But that's OK, in the worst case we'll return a
978 : * corrupted message. We also must take care not to trip on ereport(ERROR).
979 : * ----------
980 : */
981 : const char *
982 1692 : pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen)
983 : {
984 : volatile PgBackendStatus *beentry;
985 : int i;
986 :
987 1692 : beentry = BackendStatusArray;
988 :
989 : /*
990 : * We probably shouldn't get here before shared memory has been set up,
991 : * but be safe.
992 : */
993 1692 : if (beentry == NULL || BackendActivityBuffer == NULL)
994 0 : return NULL;
995 :
996 153404 : for (i = 1; i <= MaxBackends; i++)
997 : {
998 151822 : if (beentry->st_procpid == pid)
999 : {
1000 : /* Read pointer just once, so it can't change after validation */
1001 110 : const char *activity = beentry->st_activity_raw;
1002 : const char *activity_last;
1003 :
1004 : /*
1005 : * We mustn't access activity string before we verify that it
1006 : * falls within the BackendActivityBuffer. To make sure that the
1007 : * entire string including its ending is contained within the
1008 : * buffer, subtract one activity length from the buffer size.
1009 : */
1010 110 : activity_last = BackendActivityBuffer + BackendActivityBufferSize
1011 110 : - pgstat_track_activity_query_size;
1012 :
1013 110 : if (activity < BackendActivityBuffer ||
1014 : activity > activity_last)
1015 0 : return NULL;
1016 :
1017 : /* If no string available, no point in a report */
1018 110 : if (activity[0] == '\0')
1019 0 : return NULL;
1020 :
1021 : /*
1022 : * Copy only ASCII-safe characters so we don't run into encoding
1023 : * problems when reporting the message; and be sure not to run off
1024 : * the end of memory. As only ASCII characters are reported, it
1025 : * doesn't seem necessary to perform multibyte aware clipping.
1026 : */
1027 110 : ascii_safe_strlcpy(buffer, activity,
1028 110 : Min(buflen, pgstat_track_activity_query_size));
1029 :
1030 110 : return buffer;
1031 : }
1032 :
1033 151712 : beentry++;
1034 : }
1035 :
1036 : /* PID not found */
1037 1582 : return NULL;
1038 : }
1039 :
1040 : /* ----------
1041 : * pgstat_get_my_query_id() -
1042 : *
1043 : * Return current backend's query identifier.
1044 : */
1045 : uint64
1046 724 : pgstat_get_my_query_id(void)
1047 : {
1048 724 : if (!MyBEEntry)
1049 28 : return 0;
1050 :
1051 : /*
1052 : * There's no need for a lock around pgstat_begin_read_activity /
1053 : * pgstat_end_read_activity here as it's only called from
1054 : * pg_stat_get_activity which is already protected, or from the same
1055 : * backend which means that there won't be concurrent writes.
1056 : */
1057 696 : return MyBEEntry->st_query_id;
1058 : }
1059 :
1060 : /* ----------
1061 : * cmp_lbestatus
1062 : *
1063 : * Comparison function for bsearch() on an array of LocalPgBackendStatus.
1064 : * The backend_id field is used to compare the arguments.
1065 : * ----------
1066 : */
1067 : static int
1068 246 : cmp_lbestatus(const void *a, const void *b)
1069 : {
1070 246 : const LocalPgBackendStatus *lbestatus1 = (const LocalPgBackendStatus *) a;
1071 246 : const LocalPgBackendStatus *lbestatus2 = (const LocalPgBackendStatus *) b;
1072 :
1073 246 : return lbestatus1->backend_id - lbestatus2->backend_id;
1074 : }
1075 :
1076 : /* ----------
1077 : * pgstat_get_beentry_by_backend_id() -
1078 : *
1079 : * Support function for the SQL-callable pgstat* functions. Returns
1080 : * our local copy of the current-activity entry for one backend,
1081 : * or NULL if the given beid doesn't identify any known session.
1082 : *
1083 : * The beid argument is the BackendId of the desired session
1084 : * (note that this is unlike pgstat_get_local_beentry_by_index()).
1085 : *
1086 : * NB: caller is responsible for a check if the user is permitted to see
1087 : * this info (especially the querystring).
1088 : * ----------
1089 : */
1090 : PgBackendStatus *
1091 78 : pgstat_get_beentry_by_backend_id(BackendId beid)
1092 : {
1093 78 : LocalPgBackendStatus *ret = pgstat_get_local_beentry_by_backend_id(beid);
1094 :
1095 78 : if (ret)
1096 78 : return &ret->backendStatus;
1097 :
1098 0 : return NULL;
1099 : }
1100 :
1101 :
1102 : /* ----------
1103 : * pgstat_get_local_beentry_by_backend_id() -
1104 : *
1105 : * Like pgstat_get_beentry_by_backend_id() but with locally computed additions
1106 : * (like xid and xmin values of the backend)
1107 : *
1108 : * The beid argument is the BackendId of the desired session
1109 : * (note that this is unlike pgstat_get_local_beentry_by_index()).
1110 : *
1111 : * NB: caller is responsible for checking if the user is permitted to see this
1112 : * info (especially the querystring).
1113 : * ----------
1114 : */
1115 : LocalPgBackendStatus *
1116 78 : pgstat_get_local_beentry_by_backend_id(BackendId beid)
1117 : {
1118 : LocalPgBackendStatus key;
1119 :
1120 78 : pgstat_read_current_status();
1121 :
1122 : /*
1123 : * Since the localBackendStatusTable is in order by backend_id, we can use
1124 : * bsearch() to search it efficiently.
1125 : */
1126 78 : key.backend_id = beid;
1127 78 : return bsearch(&key, localBackendStatusTable, localNumBackends,
1128 : sizeof(LocalPgBackendStatus), cmp_lbestatus);
1129 : }
1130 :
1131 :
1132 : /* ----------
1133 : * pgstat_get_local_beentry_by_index() -
1134 : *
1135 : * Like pgstat_get_beentry_by_backend_id() but with locally computed additions
1136 : * (like xid and xmin values of the backend)
1137 : *
1138 : * The idx argument is a 1-based index in the localBackendStatusTable
1139 : * (note that this is unlike pgstat_get_beentry_by_backend_id()).
1140 : * Returns NULL if the argument is out of range (no current caller does that).
1141 : *
1142 : * NB: caller is responsible for a check if the user is permitted to see
1143 : * this info (especially the querystring).
1144 : * ----------
1145 : */
1146 : LocalPgBackendStatus *
1147 8740 : pgstat_get_local_beentry_by_index(int idx)
1148 : {
1149 8740 : pgstat_read_current_status();
1150 :
1151 8740 : if (idx < 1 || idx > localNumBackends)
1152 0 : return NULL;
1153 :
1154 8740 : return &localBackendStatusTable[idx - 1];
1155 : }
1156 :
1157 :
1158 : /* ----------
1159 : * pgstat_fetch_stat_numbackends() -
1160 : *
1161 : * Support function for the SQL-callable pgstat* functions. Returns
1162 : * the number of sessions known in the localBackendStatusTable, i.e.
1163 : * the maximum 1-based index to pass to pgstat_get_local_beentry_by_index().
1164 : * ----------
1165 : */
1166 : int
1167 1266 : pgstat_fetch_stat_numbackends(void)
1168 : {
1169 1266 : pgstat_read_current_status();
1170 :
1171 1266 : return localNumBackends;
1172 : }
1173 :
1174 : /*
1175 : * Convert a potentially unsafely truncated activity string (see
1176 : * PgBackendStatus.st_activity_raw's documentation) into a correctly truncated
1177 : * one.
1178 : *
1179 : * The returned string is allocated in the caller's memory context and may be
1180 : * freed.
1181 : */
1182 : char *
1183 8484 : pgstat_clip_activity(const char *raw_activity)
1184 : {
1185 : char *activity;
1186 : int rawlen;
1187 : int cliplen;
1188 :
1189 : /*
1190 : * Some callers, like pgstat_get_backend_current_activity(), do not
1191 : * guarantee that the buffer isn't concurrently modified. We try to take
1192 : * care that the buffer is always terminated by a NUL byte regardless, but
1193 : * let's still be paranoid about the string's length. In those cases the
1194 : * underlying buffer is guaranteed to be pgstat_track_activity_query_size
1195 : * large.
1196 : */
1197 8484 : activity = pnstrdup(raw_activity, pgstat_track_activity_query_size - 1);
1198 :
1199 : /* now double-guaranteed to be NUL terminated */
1200 8484 : rawlen = strlen(activity);
1201 :
1202 : /*
1203 : * All supported server-encodings make it possible to determine the length
1204 : * of a multi-byte character from its first byte (this is not the case for
1205 : * client encodings, see GB18030). As st_activity is always stored using
1206 : * server encoding, this allows us to perform multi-byte aware truncation,
1207 : * even if the string earlier was truncated in the middle of a multi-byte
1208 : * character.
1209 : */
1210 8484 : cliplen = pg_mbcliplen(activity, rawlen,
1211 : pgstat_track_activity_query_size - 1);
1212 :
1213 8484 : activity[cliplen] = '\0';
1214 :
1215 8484 : return activity;
1216 : }
|