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