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