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