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