Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * mcxtfuncs.c
4 : * Functions to show backend memory context.
5 : *
6 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/utils/adt/mcxtfuncs.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 :
16 : #include "postgres.h"
17 :
18 : #include "funcapi.h"
19 : #include "mb/pg_wchar.h"
20 : #include "miscadmin.h"
21 : #include "access/twophase.h"
22 : #include "catalog/pg_authid_d.h"
23 : #include "storage/proc.h"
24 : #include "storage/procarray.h"
25 : #include "utils/acl.h"
26 : #include "utils/array.h"
27 : #include "utils/builtins.h"
28 : #include "utils/hsearch.h"
29 : #include "utils/memutils.h"
30 : #include "utils/wait_event_types.h"
31 :
32 : /* ----------
33 : * The max bytes for showing identifiers of MemoryContext.
34 : * ----------
35 : */
36 : #define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE 1024
37 : struct MemoryStatsBackendState *memCxtState = NULL;
38 : struct MemoryStatsCtl *memCxtArea = NULL;
39 :
40 : /*
41 : * int_list_to_array
42 : * Convert an IntList to an array of INT4OIDs.
43 : */
44 : static Datum
45 3162 : int_list_to_array(const List *list)
46 : {
47 : Datum *datum_array;
48 : int length;
49 : ArrayType *result_array;
50 :
51 3162 : length = list_length(list);
52 3162 : datum_array = (Datum *) palloc(length * sizeof(Datum));
53 :
54 15666 : foreach_int(i, list)
55 9342 : datum_array[foreach_current_index(i)] = Int32GetDatum(i);
56 :
57 3162 : result_array = construct_array_builtin(datum_array, length, INT4OID);
58 :
59 3162 : return PointerGetDatum(result_array);
60 : }
61 :
62 : /*
63 : * PutMemoryContextsStatsTupleStore
64 : * Add details for the given MemoryContext to 'tupstore'.
65 : */
66 : static void
67 3162 : PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
68 : TupleDesc tupdesc, MemoryContext context,
69 : HTAB *context_id_lookup)
70 : {
71 : #define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS 10
72 :
73 : Datum values[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
74 : bool nulls[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
75 : MemoryContextCounters stat;
76 3162 : List *path = NIL;
77 : const char *name;
78 : const char *ident;
79 : const char *type;
80 :
81 : Assert(MemoryContextIsValid(context));
82 :
83 : /*
84 : * Figure out the transient context_id of this context and each of its
85 : * ancestors.
86 : */
87 12504 : for (MemoryContext cur = context; cur != NULL; cur = cur->parent)
88 : {
89 : MemoryStatsContextId *entry;
90 : bool found;
91 :
92 9342 : entry = hash_search(context_id_lookup, &cur, HASH_FIND, &found);
93 :
94 9342 : if (!found)
95 0 : elog(ERROR, "hash table corrupted");
96 9342 : path = lcons_int(entry->context_id, path);
97 : }
98 :
99 : /* Examine the context itself */
100 3162 : memset(&stat, 0, sizeof(stat));
101 3162 : (*context->methods->stats) (context, NULL, NULL, &stat, true);
102 :
103 3162 : memset(values, 0, sizeof(values));
104 3162 : memset(nulls, 0, sizeof(nulls));
105 :
106 3162 : name = context->name;
107 3162 : ident = context->ident;
108 :
109 : /*
110 : * To be consistent with logging output, we label dynahash contexts with
111 : * just the hash table name as with MemoryContextStatsPrint().
112 : */
113 3162 : if (ident && strcmp(name, "dynahash") == 0)
114 : {
115 300 : name = ident;
116 300 : ident = NULL;
117 : }
118 :
119 3162 : if (name)
120 3162 : values[0] = CStringGetTextDatum(name);
121 : else
122 0 : nulls[0] = true;
123 :
124 3162 : if (ident)
125 : {
126 2286 : int idlen = strlen(ident);
127 : char clipped_ident[MEMORY_CONTEXT_IDENT_DISPLAY_SIZE];
128 :
129 : /*
130 : * Some identifiers such as SQL query string can be very long,
131 : * truncate oversize identifiers.
132 : */
133 2286 : if (idlen >= MEMORY_CONTEXT_IDENT_DISPLAY_SIZE)
134 0 : idlen = pg_mbcliplen(ident, idlen, MEMORY_CONTEXT_IDENT_DISPLAY_SIZE - 1);
135 :
136 2286 : memcpy(clipped_ident, ident, idlen);
137 2286 : clipped_ident[idlen] = '\0';
138 2286 : values[1] = CStringGetTextDatum(clipped_ident);
139 : }
140 : else
141 876 : nulls[1] = true;
142 :
143 3162 : type = ContextTypeToString(context->type);
144 :
145 3162 : values[2] = CStringGetTextDatum(type);
146 3162 : values[3] = Int32GetDatum(list_length(path)); /* level */
147 3162 : values[4] = int_list_to_array(path);
148 3162 : values[5] = Int64GetDatum(stat.totalspace);
149 3162 : values[6] = Int64GetDatum(stat.nblocks);
150 3162 : values[7] = Int64GetDatum(stat.freespace);
151 3162 : values[8] = Int64GetDatum(stat.freechunks);
152 3162 : values[9] = Int64GetDatum(stat.totalspace - stat.freespace);
153 :
154 3162 : tuplestore_putvalues(tupstore, tupdesc, values, nulls);
155 3162 : list_free(path);
156 3162 : }
157 :
158 : /*
159 : * ContextTypeToString
160 : * Returns a textual representation of a context type
161 : *
162 : * This should cover the same types as MemoryContextIsValid.
163 : */
164 : const char *
165 4280 : ContextTypeToString(NodeTag type)
166 : {
167 : const char *context_type;
168 :
169 4280 : switch (type)
170 : {
171 4238 : case T_AllocSetContext:
172 4238 : context_type = "AllocSet";
173 4238 : break;
174 36 : case T_GenerationContext:
175 36 : context_type = "Generation";
176 36 : break;
177 0 : case T_SlabContext:
178 0 : context_type = "Slab";
179 0 : break;
180 6 : case T_BumpContext:
181 6 : context_type = "Bump";
182 6 : break;
183 0 : default:
184 0 : context_type = "???";
185 0 : break;
186 : }
187 4280 : return context_type;
188 : }
189 :
190 : /*
191 : * pg_get_backend_memory_contexts
192 : * SQL SRF showing backend memory context.
193 : */
194 : Datum
195 24 : pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
196 : {
197 24 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
198 : int context_id;
199 : List *contexts;
200 : HASHCTL ctl;
201 : HTAB *context_id_lookup;
202 :
203 24 : ctl.keysize = sizeof(MemoryContext);
204 24 : ctl.entrysize = sizeof(MemoryStatsContextId);
205 24 : ctl.hcxt = CurrentMemoryContext;
206 :
207 24 : context_id_lookup = hash_create("pg_get_backend_memory_contexts",
208 : 256,
209 : &ctl,
210 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
211 :
212 24 : InitMaterializedSRF(fcinfo, 0);
213 :
214 : /*
215 : * Here we use a non-recursive algorithm to visit all MemoryContexts
216 : * starting with TopMemoryContext. The reason we avoid using a recursive
217 : * algorithm is because we want to assign the context_id breadth-first.
218 : * I.e. all contexts at level 1 are assigned IDs before contexts at level
219 : * 2. Because contexts closer to TopMemoryContext are less likely to
220 : * change, this makes the assigned context_id more stable. Otherwise, if
221 : * the first child of TopMemoryContext obtained an additional grandchild,
222 : * the context_id for the second child of TopMemoryContext would change.
223 : */
224 24 : contexts = list_make1(TopMemoryContext);
225 :
226 : /* TopMemoryContext will always have a context_id of 1 */
227 24 : context_id = 1;
228 :
229 3210 : foreach_ptr(MemoryContextData, cur, contexts)
230 : {
231 : MemoryStatsContextId *entry;
232 : bool found;
233 :
234 : /*
235 : * Record the context_id that we've assigned to each MemoryContext.
236 : * PutMemoryContextsStatsTupleStore needs this to populate the "path"
237 : * column with the parent context_ids.
238 : */
239 3162 : entry = (MemoryStatsContextId *) hash_search(context_id_lookup, &cur,
240 : HASH_ENTER, &found);
241 3162 : entry->context_id = context_id++;
242 : Assert(!found);
243 :
244 3162 : PutMemoryContextsStatsTupleStore(rsinfo->setResult,
245 : rsinfo->setDesc,
246 : cur,
247 : context_id_lookup);
248 :
249 : /*
250 : * Append all children onto the contexts list so they're processed by
251 : * subsequent iterations.
252 : */
253 6300 : for (MemoryContext c = cur->firstchild; c != NULL; c = c->nextchild)
254 3138 : contexts = lappend(contexts, c);
255 : }
256 :
257 24 : hash_destroy(context_id_lookup);
258 :
259 24 : return (Datum) 0;
260 : }
261 :
262 : /*
263 : * pg_log_backend_memory_contexts
264 : * Signal a backend or an auxiliary process to log its memory contexts.
265 : *
266 : * By default, only superusers are allowed to signal to log the memory
267 : * contexts because allowing any users to issue this request at an unbounded
268 : * rate would cause lots of log messages and which can lead to denial of
269 : * service. Additional roles can be permitted with GRANT.
270 : *
271 : * On receipt of this signal, a backend or an auxiliary process sets the flag
272 : * in the signal handler, which causes the next CHECK_FOR_INTERRUPTS()
273 : * or process-specific interrupt handler to log the memory contexts.
274 : */
275 : Datum
276 18 : pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
277 : {
278 18 : int pid = PG_GETARG_INT32(0);
279 : PGPROC *proc;
280 18 : ProcNumber procNumber = INVALID_PROC_NUMBER;
281 :
282 : /*
283 : * See if the process with given pid is a backend or an auxiliary process.
284 : */
285 18 : proc = BackendPidGetProc(pid);
286 18 : if (proc == NULL)
287 6 : proc = AuxiliaryPidGetProc(pid);
288 :
289 : /*
290 : * BackendPidGetProc() and AuxiliaryPidGetProc() return NULL if the pid
291 : * isn't valid; but by the time we reach kill(), a process for which we
292 : * get a valid proc here might have terminated on its own. There's no way
293 : * to acquire a lock on an arbitrary process to prevent that. But since
294 : * this mechanism is usually used to debug a backend or an auxiliary
295 : * process running and consuming lots of memory, that it might end on its
296 : * own first and its memory contexts are not logged is not a problem.
297 : */
298 18 : if (proc == NULL)
299 : {
300 : /*
301 : * This is just a warning so a loop-through-resultset will not abort
302 : * if one backend terminated on its own during the run.
303 : */
304 0 : ereport(WARNING,
305 : (errmsg("PID %d is not a PostgreSQL server process", pid)));
306 0 : PG_RETURN_BOOL(false);
307 : }
308 :
309 18 : procNumber = GetNumberFromPGProc(proc);
310 18 : if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, procNumber) < 0)
311 : {
312 : /* Again, just a warning to allow loops */
313 0 : ereport(WARNING,
314 : (errmsg("could not send signal to process %d: %m", pid)));
315 0 : PG_RETURN_BOOL(false);
316 : }
317 :
318 18 : PG_RETURN_BOOL(true);
319 : }
320 :
321 : /*
322 : * pg_get_process_memory_contexts
323 : * Signal a backend or an auxiliary process to send its memory contexts,
324 : * wait for the results and display them.
325 : *
326 : * By default, only superusers or users with ROLE_PG_READ_ALL_STATS are allowed
327 : * to signal a process to return the memory contexts. This is because allowing
328 : * any users to issue this request at an unbounded rate would cause lots of
329 : * requests to be sent, which can lead to denial of service. Additional roles
330 : * can be permitted with GRANT.
331 : *
332 : * On receipt of this signal, a backend or an auxiliary process sets the flag
333 : * in the signal handler, which causes the next CHECK_FOR_INTERRUPTS()
334 : * or process-specific interrupt handler to copy the memory context details
335 : * to a dynamic shared memory space.
336 : *
337 : * We have defined a limit on DSA memory that could be allocated per process -
338 : * if the process has more memory contexts than what can fit in the allocated
339 : * size, the excess contexts are summarized and represented as cumulative total
340 : * at the end of the buffer.
341 : *
342 : * After sending the signal, wait on a condition variable. The publishing
343 : * backend, after copying the data to shared memory, sends signal on that
344 : * condition variable. There is one condition variable per publishing backend.
345 : * Once the condition variable is signalled, check if the latest memory context
346 : * information is available and display.
347 : *
348 : * If the publishing backend does not respond before the condition variable
349 : * times out, which is set to MEMSTATS_WAIT_TIMEOUT, retry given that there is
350 : * time left within the timeout specified by the user, before giving up and
351 : * returning previously published statistics, if any. If no previous statistics
352 : * exist, return NULL.
353 : */
354 : #define MEMSTATS_WAIT_TIMEOUT 100
355 : Datum
356 12 : pg_get_process_memory_contexts(PG_FUNCTION_ARGS)
357 : {
358 12 : int pid = PG_GETARG_INT32(0);
359 12 : bool summary = PG_GETARG_BOOL(1);
360 12 : double timeout = PG_GETARG_FLOAT8(2);
361 : PGPROC *proc;
362 12 : ProcNumber procNumber = INVALID_PROC_NUMBER;
363 12 : bool proc_is_aux = false;
364 12 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
365 : MemoryStatsEntry *memcxt_info;
366 : TimestampTz start_timestamp;
367 :
368 : /*
369 : * See if the process with given pid is a backend or an auxiliary process
370 : * and remember the type for when we requery the process later.
371 : */
372 12 : proc = BackendPidGetProc(pid);
373 12 : if (proc == NULL)
374 : {
375 6 : proc = AuxiliaryPidGetProc(pid);
376 6 : proc_is_aux = true;
377 : }
378 :
379 : /*
380 : * BackendPidGetProc() and AuxiliaryPidGetProc() return NULL if the pid
381 : * isn't valid; this is however not a problem and leave with a WARNING.
382 : * See comment in pg_log_backend_memory_contexts for a discussion on this.
383 : */
384 12 : if (proc == NULL)
385 : {
386 : /*
387 : * This is just a warning so a loop-through-resultset will not abort
388 : * if one backend terminated on its own during the run.
389 : */
390 0 : ereport(WARNING,
391 : errmsg("PID %d is not a PostgreSQL server process", pid));
392 0 : PG_RETURN_NULL();
393 : }
394 :
395 12 : InitMaterializedSRF(fcinfo, 0);
396 :
397 12 : procNumber = GetNumberFromPGProc(proc);
398 :
399 12 : LWLockAcquire(&memCxtState[procNumber].lw_lock, LW_EXCLUSIVE);
400 12 : memCxtState[procNumber].summary = summary;
401 12 : LWLockRelease(&memCxtState[procNumber].lw_lock);
402 :
403 12 : start_timestamp = GetCurrentTimestamp();
404 :
405 : /*
406 : * Send a signal to a PostgreSQL process, informing it we want it to
407 : * produce information about its memory contexts.
408 : */
409 12 : if (SendProcSignal(pid, PROCSIG_GET_MEMORY_CONTEXT, procNumber) < 0)
410 : {
411 0 : ereport(WARNING,
412 : errmsg("could not send signal to process %d: %m", pid));
413 0 : PG_RETURN_NULL();
414 : }
415 :
416 : /*
417 : * Even if the proc has published statistics, the may not be due to the
418 : * current request, but previously published stats. Check if the stats
419 : * are updated by comparing the timestamp, if the stats are newer than our
420 : * previously recorded timestamp from before sending the procsignal, they
421 : * must by definition be updated. Wait for the timeout specified by the
422 : * user, following which display old statistics if available or return
423 : * NULL.
424 : */
425 : while (1)
426 20 : {
427 : long msecs;
428 :
429 : /*
430 : * We expect to come out of sleep when the requested process has
431 : * finished publishing the statistics, verified using the valid DSA
432 : * pointer.
433 : *
434 : * Make sure that the information belongs to pid we requested
435 : * information for, Otherwise loop back and wait for the server
436 : * process to finish publishing statistics.
437 : */
438 32 : LWLockAcquire(&memCxtState[procNumber].lw_lock, LW_EXCLUSIVE);
439 :
440 : /*
441 : * Note in procnumber.h file says that a procNumber can be re-used for
442 : * a different backend immediately after a backend exits. In case an
443 : * old process' data was there and not updated by the current process
444 : * in the slot identified by the procNumber, the pid of the requested
445 : * process and the proc_id might not match.
446 : */
447 32 : if (memCxtState[procNumber].proc_id == pid)
448 : {
449 : /*
450 : * Break if the latest stats have been read, indicated by
451 : * statistics timestamp being newer than the current request
452 : * timestamp.
453 : */
454 12 : msecs = TimestampDifferenceMilliseconds(start_timestamp,
455 12 : memCxtState[procNumber].stats_timestamp);
456 :
457 12 : if (DsaPointerIsValid(memCxtState[procNumber].memstats_dsa_pointer)
458 12 : && msecs > 0)
459 12 : break;
460 : }
461 20 : LWLockRelease(&memCxtState[procNumber].lw_lock);
462 :
463 : /*
464 : * Recheck the state of the backend before sleeping on the condition
465 : * variable to ensure the process is still alive. Only check the
466 : * relevant process type based on the earlier PID check.
467 : */
468 20 : if (proc_is_aux)
469 8 : proc = AuxiliaryPidGetProc(pid);
470 : else
471 12 : proc = BackendPidGetProc(pid);
472 :
473 : /*
474 : * The process ending during memory context processing is not an
475 : * error.
476 : */
477 20 : if (proc == NULL)
478 : {
479 0 : ereport(WARNING,
480 : errmsg("PID %d is no longer a PostgreSQL server process",
481 : pid));
482 0 : PG_RETURN_NULL();
483 : }
484 :
485 20 : msecs = TimestampDifferenceMilliseconds(start_timestamp, GetCurrentTimestamp());
486 :
487 : /*
488 : * If we haven't already exceeded the timeout value, sleep for the
489 : * remainder of the timeout on the condition variable.
490 : */
491 20 : if (msecs > 0 && msecs < (timeout * 1000))
492 : {
493 : /*
494 : * Wait for the timeout as defined by the user. If no updated
495 : * statistics are available within the allowed time then display
496 : * previously published statistics if there are any. If no
497 : * previous statistics are available then return NULL. The timer
498 : * is defined in milliseconds since that's what the condition
499 : * variable sleep uses.
500 : */
501 20 : if (ConditionVariableTimedSleep(&memCxtState[procNumber].memcxt_cv,
502 20 : ((timeout * 1000) - msecs), WAIT_EVENT_MEM_CXT_PUBLISH))
503 : {
504 0 : LWLockAcquire(&memCxtState[procNumber].lw_lock, LW_EXCLUSIVE);
505 : /* Displaying previously published statistics if available */
506 0 : if (DsaPointerIsValid(memCxtState[procNumber].memstats_dsa_pointer))
507 0 : break;
508 : else
509 : {
510 0 : LWLockRelease(&memCxtState[procNumber].lw_lock);
511 0 : PG_RETURN_NULL();
512 : }
513 : }
514 : }
515 : else
516 : {
517 0 : LWLockAcquire(&memCxtState[procNumber].lw_lock, LW_EXCLUSIVE);
518 : /* Displaying previously published statistics if available */
519 0 : if (DsaPointerIsValid(memCxtState[procNumber].memstats_dsa_pointer))
520 0 : break;
521 : else
522 : {
523 0 : LWLockRelease(&memCxtState[procNumber].lw_lock);
524 0 : PG_RETURN_NULL();
525 : }
526 : }
527 : }
528 :
529 : /*
530 : * We should only reach here with a valid DSA handle, either containing
531 : * updated statistics or previously published statistics (identified by
532 : * the timestamp.
533 : */
534 : Assert(memCxtArea->memstats_dsa_handle != DSA_HANDLE_INVALID);
535 : /* Attach to the dsa area if we have not already done so */
536 12 : if (MemoryStatsDsaArea == NULL)
537 : {
538 6 : MemoryContext oldcontext = CurrentMemoryContext;
539 :
540 6 : MemoryContextSwitchTo(TopMemoryContext);
541 6 : MemoryStatsDsaArea = dsa_attach(memCxtArea->memstats_dsa_handle);
542 6 : MemoryContextSwitchTo(oldcontext);
543 6 : dsa_pin_mapping(MemoryStatsDsaArea);
544 : }
545 :
546 : /*
547 : * Backend has finished publishing the stats, project them.
548 : */
549 : memcxt_info = (MemoryStatsEntry *)
550 12 : dsa_get_address(MemoryStatsDsaArea, memCxtState[procNumber].memstats_dsa_pointer);
551 :
552 : #define PG_GET_PROCESS_MEMORY_CONTEXTS_COLS 12
553 1130 : for (int i = 0; i < memCxtState[procNumber].total_stats; i++)
554 : {
555 : ArrayType *path_array;
556 : int path_length;
557 : Datum values[PG_GET_PROCESS_MEMORY_CONTEXTS_COLS];
558 : bool nulls[PG_GET_PROCESS_MEMORY_CONTEXTS_COLS];
559 : char *name;
560 : char *ident;
561 1118 : Datum *path_datum = NULL;
562 1118 : int *path_int = NULL;
563 :
564 1118 : memset(values, 0, sizeof(values));
565 1118 : memset(nulls, 0, sizeof(nulls));
566 :
567 1118 : if (DsaPointerIsValid(memcxt_info[i].name))
568 : {
569 1118 : name = (char *) dsa_get_address(MemoryStatsDsaArea, memcxt_info[i].name);
570 1118 : values[0] = CStringGetTextDatum(name);
571 : }
572 : else
573 0 : nulls[0] = true;
574 :
575 1118 : if (DsaPointerIsValid(memcxt_info[i].ident))
576 : {
577 726 : ident = (char *) dsa_get_address(MemoryStatsDsaArea, memcxt_info[i].ident);
578 726 : values[1] = CStringGetTextDatum(ident);
579 : }
580 : else
581 392 : nulls[1] = true;
582 :
583 1118 : values[2] = CStringGetTextDatum(ContextTypeToString(memcxt_info[i].type));
584 :
585 1118 : path_length = memcxt_info[i].path_length;
586 1118 : path_datum = (Datum *) palloc(path_length * sizeof(Datum));
587 1118 : if (DsaPointerIsValid(memcxt_info[i].path))
588 : {
589 1118 : path_int = (int *) dsa_get_address(MemoryStatsDsaArea, memcxt_info[i].path);
590 4488 : for (int j = 0; j < path_length; j++)
591 3370 : path_datum[j] = Int32GetDatum(path_int[j]);
592 1118 : path_array = construct_array_builtin(path_datum, path_length, INT4OID);
593 1118 : values[3] = PointerGetDatum(path_array);
594 : }
595 : else
596 0 : nulls[3] = true;
597 :
598 1118 : values[4] = Int32GetDatum(memcxt_info[i].levels);
599 1118 : values[5] = Int64GetDatum(memcxt_info[i].totalspace);
600 1118 : values[6] = Int64GetDatum(memcxt_info[i].nblocks);
601 1118 : values[7] = Int64GetDatum(memcxt_info[i].freespace);
602 1118 : values[8] = Int64GetDatum(memcxt_info[i].freechunks);
603 2236 : values[9] = Int64GetDatum(memcxt_info[i].totalspace -
604 1118 : memcxt_info[i].freespace);
605 1118 : values[10] = Int32GetDatum(memcxt_info[i].num_agg_stats);
606 1118 : values[11] = TimestampTzGetDatum(memCxtState[procNumber].stats_timestamp);
607 :
608 1118 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
609 : values, nulls);
610 : }
611 12 : LWLockRelease(&memCxtState[procNumber].lw_lock);
612 :
613 12 : ConditionVariableCancelSleep();
614 :
615 12 : PG_RETURN_NULL();
616 : }
617 :
618 : Size
619 3906 : MemoryContextReportingShmemSize(void)
620 : {
621 3906 : Size sz = 0;
622 3906 : Size TotalProcs = 0;
623 :
624 3906 : TotalProcs = add_size(TotalProcs, NUM_AUXILIARY_PROCS);
625 3906 : TotalProcs = add_size(TotalProcs, MaxBackends);
626 3906 : sz = add_size(sz, mul_size(TotalProcs, sizeof(MemoryStatsBackendState)));
627 :
628 3906 : sz = add_size(sz, sizeof(MemoryStatsCtl));
629 :
630 3906 : return sz;
631 : }
632 :
633 : /*
634 : * Initialize shared memory for displaying memory context statistics
635 : */
636 : void
637 2100 : MemoryContextReportingShmemInit(void)
638 : {
639 : bool found;
640 :
641 2100 : memCxtArea = (MemoryStatsCtl *)
642 2100 : ShmemInitStruct("MemoryStatsCtl",
643 : sizeof(MemoryStatsCtl), &found);
644 :
645 2100 : if (!found)
646 : {
647 2100 : LWLockInitialize(&memCxtArea->lw_lock, LWTRANCHE_MEMORY_CONTEXT_REPORTING_STATE);
648 2100 : memCxtArea->memstats_dsa_handle = DSA_HANDLE_INVALID;
649 : }
650 :
651 2100 : memCxtState = (MemoryStatsBackendState *)
652 2100 : ShmemInitStruct("MemoryStatsBackendState",
653 2100 : ((MaxBackends + NUM_AUXILIARY_PROCS) * sizeof(MemoryStatsBackendState)),
654 : &found);
655 :
656 2100 : if (found)
657 0 : return;
658 :
659 279486 : for (int i = 0; i < (MaxBackends + NUM_AUXILIARY_PROCS); i++)
660 : {
661 277386 : ConditionVariableInit(&memCxtState[i].memcxt_cv);
662 277386 : LWLockInitialize(&memCxtState[i].lw_lock, LWTRANCHE_MEMORY_CONTEXT_REPORTING_PROC);
663 277386 : memCxtState[i].memstats_dsa_pointer = InvalidDsaPointer;
664 : }
665 : }
|