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