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