LCOV - code coverage report
Current view: top level - src/backend/utils/adt - mcxtfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 181 213 85.0 %
Date: 2025-04-24 12:15:10 Functions: 8 8 100.0 %
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.14