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