LCOV - code coverage report
Current view: top level - src/backend/utils/adt - mcxtfuncs.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 90.7 % 118 107
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 4 4
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 63.1 % 65 41

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * mcxtfuncs.c
       4                 :             :  *        Functions to show backend memory context.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, 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 "storage/proc.h"
      21                 :             : #include "storage/procarray.h"
      22                 :             : #include "utils/array.h"
      23                 :             : #include "utils/builtins.h"
      24                 :             : #include "utils/hsearch.h"
      25                 :             : 
      26                 :             : /* ----------
      27                 :             :  * The max bytes for showing identifiers of MemoryContext.
      28                 :             :  * ----------
      29                 :             :  */
      30                 :             : #define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE       1024
      31                 :             : 
      32                 :             : /*
      33                 :             :  * MemoryContextId
      34                 :             :  *              Used for storage of transient identifiers for
      35                 :             :  *              pg_get_backend_memory_contexts.
      36                 :             :  */
      37                 :             : typedef struct MemoryContextId
      38                 :             : {
      39                 :             :         MemoryContext context;
      40                 :             :         int                     context_id;
      41                 :             : } MemoryContextId;
      42                 :             : 
      43                 :             : /*
      44                 :             :  * int_list_to_array
      45                 :             :  *              Convert an IntList to an array of INT4OIDs.
      46                 :             :  */
      47                 :             : static Datum
      48                 :         526 : int_list_to_array(const List *list)
      49                 :             : {
      50                 :         526 :         Datum      *datum_array;
      51                 :         526 :         int                     length;
      52                 :         526 :         ArrayType  *result_array;
      53                 :             : 
      54                 :         526 :         length = list_length(list);
      55                 :         526 :         datum_array = palloc_array(Datum, length);
      56                 :             : 
      57   [ +  +  +  -  :        2605 :         foreach_int(i, list)
             +  +  +  + ]
      58                 :        2079 :                 datum_array[foreach_current_index(i)] = Int32GetDatum(i);
      59                 :             : 
      60                 :         526 :         result_array = construct_array_builtin(datum_array, length, INT4OID);
      61                 :             : 
      62                 :        1052 :         return PointerGetDatum(result_array);
      63                 :         526 : }
      64                 :             : 
      65                 :             : /*
      66                 :             :  * PutMemoryContextsStatsTupleStore
      67                 :             :  *              Add details for the given MemoryContext to 'tupstore'.
      68                 :             :  */
      69                 :             : static void
      70                 :         526 : PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
      71                 :             :                                                                  TupleDesc tupdesc, MemoryContext context,
      72                 :             :                                                                  HTAB *context_id_lookup)
      73                 :             : {
      74                 :             : #define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS     10
      75                 :             : 
      76                 :         526 :         Datum           values[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
      77                 :         526 :         bool            nulls[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
      78                 :         526 :         MemoryContextCounters stat;
      79                 :         526 :         List       *path = NIL;
      80                 :         526 :         const char *name;
      81                 :         526 :         const char *ident;
      82                 :         526 :         const char *type;
      83                 :             : 
      84   [ +  -  +  +  :         526 :         Assert(MemoryContextIsValid(context));
             +  -  +  + ]
      85                 :             : 
      86                 :             :         /*
      87                 :             :          * Figure out the transient context_id of this context and each of its
      88                 :             :          * ancestors.
      89                 :             :          */
      90         [ +  + ]:        2079 :         for (MemoryContext cur = context; cur != NULL; cur = cur->parent)
      91                 :             :         {
      92                 :        1553 :                 MemoryContextId *entry;
      93                 :        1553 :                 bool            found;
      94                 :             : 
      95                 :        1553 :                 entry = hash_search(context_id_lookup, &cur, HASH_FIND, &found);
      96                 :             : 
      97         [ +  - ]:        1553 :                 if (!found)
      98   [ #  #  #  # ]:           0 :                         elog(ERROR, "hash table corrupted");
      99                 :        1553 :                 path = lcons_int(entry->context_id, path);
     100                 :        1553 :         }
     101                 :             : 
     102                 :             :         /* Examine the context itself */
     103                 :         526 :         memset(&stat, 0, sizeof(stat));
     104                 :         526 :         (*context->methods->stats) (context, NULL, NULL, &stat, true);
     105                 :             : 
     106                 :         526 :         memset(values, 0, sizeof(values));
     107                 :         526 :         memset(nulls, 0, sizeof(nulls));
     108                 :             : 
     109                 :         526 :         name = context->name;
     110                 :         526 :         ident = context->ident;
     111                 :             : 
     112                 :             :         /*
     113                 :             :          * To be consistent with logging output, we label dynahash contexts with
     114                 :             :          * just the hash table name as with MemoryContextStatsPrint().
     115                 :             :          */
     116   [ +  +  +  + ]:         526 :         if (ident && strcmp(name, "dynahash") == 0)
     117                 :             :         {
     118                 :          50 :                 name = ident;
     119                 :          50 :                 ident = NULL;
     120                 :          50 :         }
     121                 :             : 
     122         [ +  - ]:         526 :         if (name)
     123                 :         526 :                 values[0] = CStringGetTextDatum(name);
     124                 :             :         else
     125                 :           0 :                 nulls[0] = true;
     126                 :             : 
     127         [ +  + ]:         526 :         if (ident)
     128                 :             :         {
     129                 :         379 :                 int                     idlen = strlen(ident);
     130                 :         379 :                 char            clipped_ident[MEMORY_CONTEXT_IDENT_DISPLAY_SIZE];
     131                 :             : 
     132                 :             :                 /*
     133                 :             :                  * Some identifiers such as SQL query string can be very long,
     134                 :             :                  * truncate oversize identifiers.
     135                 :             :                  */
     136         [ +  - ]:         379 :                 if (idlen >= MEMORY_CONTEXT_IDENT_DISPLAY_SIZE)
     137                 :           0 :                         idlen = pg_mbcliplen(ident, idlen, MEMORY_CONTEXT_IDENT_DISPLAY_SIZE - 1);
     138                 :             : 
     139                 :         379 :                 memcpy(clipped_ident, ident, idlen);
     140                 :         379 :                 clipped_ident[idlen] = '\0';
     141                 :         379 :                 values[1] = CStringGetTextDatum(clipped_ident);
     142                 :         379 :         }
     143                 :             :         else
     144                 :         147 :                 nulls[1] = true;
     145                 :             : 
     146   [ -  +  +  -  :         526 :         switch (context->type)
                      + ]
     147                 :             :         {
     148                 :             :                 case T_AllocSetContext:
     149                 :         520 :                         type = "AllocSet";
     150                 :         520 :                         break;
     151                 :             :                 case T_GenerationContext:
     152                 :           5 :                         type = "Generation";
     153                 :           5 :                         break;
     154                 :             :                 case T_SlabContext:
     155                 :           0 :                         type = "Slab";
     156                 :           0 :                         break;
     157                 :             :                 case T_BumpContext:
     158                 :           1 :                         type = "Bump";
     159                 :           1 :                         break;
     160                 :             :                 default:
     161                 :           0 :                         type = "???";
     162                 :           0 :                         break;
     163                 :             :         }
     164                 :             : 
     165                 :         526 :         values[2] = CStringGetTextDatum(type);
     166                 :         526 :         values[3] = Int32GetDatum(list_length(path));   /* level */
     167                 :         526 :         values[4] = int_list_to_array(path);
     168                 :         526 :         values[5] = Int64GetDatum(stat.totalspace);
     169                 :         526 :         values[6] = Int64GetDatum(stat.nblocks);
     170                 :         526 :         values[7] = Int64GetDatum(stat.freespace);
     171                 :         526 :         values[8] = Int64GetDatum(stat.freechunks);
     172                 :         526 :         values[9] = Int64GetDatum(stat.totalspace - stat.freespace);
     173                 :             : 
     174                 :         526 :         tuplestore_putvalues(tupstore, tupdesc, values, nulls);
     175                 :         526 :         list_free(path);
     176                 :         526 : }
     177                 :             : 
     178                 :             : /*
     179                 :             :  * pg_get_backend_memory_contexts
     180                 :             :  *              SQL SRF showing backend memory context.
     181                 :             :  */
     182                 :             : Datum
     183                 :           4 : pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
     184                 :             : {
     185                 :           4 :         ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     186                 :           4 :         int                     context_id;
     187                 :           4 :         List       *contexts;
     188                 :           4 :         HASHCTL         ctl;
     189                 :           4 :         HTAB       *context_id_lookup;
     190                 :             : 
     191                 :           4 :         ctl.keysize = sizeof(MemoryContext);
     192                 :           4 :         ctl.entrysize = sizeof(MemoryContextId);
     193                 :           4 :         ctl.hcxt = CurrentMemoryContext;
     194                 :             : 
     195                 :           4 :         context_id_lookup = hash_create("pg_get_backend_memory_contexts",
     196                 :             :                                                                         256,
     197                 :             :                                                                         &ctl,
     198                 :             :                                                                         HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
     199                 :             : 
     200                 :           4 :         InitMaterializedSRF(fcinfo, 0);
     201                 :             : 
     202                 :             :         /*
     203                 :             :          * Here we use a non-recursive algorithm to visit all MemoryContexts
     204                 :             :          * starting with TopMemoryContext.  The reason we avoid using a recursive
     205                 :             :          * algorithm is because we want to assign the context_id breadth-first.
     206                 :             :          * I.e. all contexts at level 1 are assigned IDs before contexts at level
     207                 :             :          * 2.  Because contexts closer to TopMemoryContext are less likely to
     208                 :             :          * change, this makes the assigned context_id more stable.  Otherwise, if
     209                 :             :          * the first child of TopMemoryContext obtained an additional grandchild,
     210                 :             :          * the context_id for the second child of TopMemoryContext would change.
     211                 :             :          */
     212                 :           4 :         contexts = list_make1(TopMemoryContext);
     213                 :             : 
     214                 :             :         /* TopMemoryContext will always have a context_id of 1 */
     215                 :           4 :         context_id = 1;
     216                 :             : 
     217   [ +  +  +  -  :         534 :         foreach_ptr(MemoryContextData, cur, contexts)
             +  +  +  + ]
     218                 :             :         {
     219                 :         526 :                 MemoryContextId *entry;
     220                 :         526 :                 bool            found;
     221                 :             : 
     222                 :             :                 /*
     223                 :             :                  * Record the context_id that we've assigned to each MemoryContext.
     224                 :             :                  * PutMemoryContextsStatsTupleStore needs this to populate the "path"
     225                 :             :                  * column with the parent context_ids.
     226                 :             :                  */
     227                 :         526 :                 entry = (MemoryContextId *) hash_search(context_id_lookup, &cur,
     228                 :             :                                                                                                 HASH_ENTER, &found);
     229                 :         526 :                 entry->context_id = context_id++;
     230         [ +  - ]:         526 :                 Assert(!found);
     231                 :             : 
     232                 :        1052 :                 PutMemoryContextsStatsTupleStore(rsinfo->setResult,
     233                 :         526 :                                                                                  rsinfo->setDesc,
     234                 :         526 :                                                                                  cur,
     235                 :         526 :                                                                                  context_id_lookup);
     236                 :             : 
     237                 :             :                 /*
     238                 :             :                  * Append all children onto the contexts list so they're processed by
     239                 :             :                  * subsequent iterations.
     240                 :             :                  */
     241         [ +  + ]:        1048 :                 for (MemoryContext c = cur->firstchild; c != NULL; c = c->nextchild)
     242                 :         522 :                         contexts = lappend(contexts, c);
     243                 :         530 :         }
     244                 :             : 
     245                 :           4 :         hash_destroy(context_id_lookup);
     246                 :             : 
     247                 :           4 :         return (Datum) 0;
     248                 :           4 : }
     249                 :             : 
     250                 :             : /*
     251                 :             :  * pg_log_backend_memory_contexts
     252                 :             :  *              Signal a backend or an auxiliary process to log its memory contexts.
     253                 :             :  *
     254                 :             :  * By default, only superusers are allowed to signal to log the memory
     255                 :             :  * contexts because allowing any users to issue this request at an unbounded
     256                 :             :  * rate would cause lots of log messages and which can lead to denial of
     257                 :             :  * service. Additional roles can be permitted with GRANT.
     258                 :             :  *
     259                 :             :  * On receipt of this signal, a backend or an auxiliary process sets the flag
     260                 :             :  * in the signal handler, which causes the next CHECK_FOR_INTERRUPTS()
     261                 :             :  * or process-specific interrupt handler to log the memory contexts.
     262                 :             :  */
     263                 :             : Datum
     264                 :           3 : pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
     265                 :             : {
     266                 :           3 :         int                     pid = PG_GETARG_INT32(0);
     267                 :           3 :         PGPROC     *proc;
     268                 :           3 :         ProcNumber      procNumber = INVALID_PROC_NUMBER;
     269                 :             : 
     270                 :             :         /*
     271                 :             :          * See if the process with given pid is a backend or an auxiliary process.
     272                 :             :          */
     273                 :           3 :         proc = BackendPidGetProc(pid);
     274         [ +  + ]:           3 :         if (proc == NULL)
     275                 :           1 :                 proc = AuxiliaryPidGetProc(pid);
     276                 :             : 
     277                 :             :         /*
     278                 :             :          * BackendPidGetProc() and AuxiliaryPidGetProc() return NULL if the pid
     279                 :             :          * isn't valid; but by the time we reach kill(), a process for which we
     280                 :             :          * get a valid proc here might have terminated on its own.  There's no way
     281                 :             :          * to acquire a lock on an arbitrary process to prevent that. But since
     282                 :             :          * this mechanism is usually used to debug a backend or an auxiliary
     283                 :             :          * process running and consuming lots of memory, that it might end on its
     284                 :             :          * own first and its memory contexts are not logged is not a problem.
     285                 :             :          */
     286         [ +  - ]:           3 :         if (proc == NULL)
     287                 :             :         {
     288                 :             :                 /*
     289                 :             :                  * This is just a warning so a loop-through-resultset will not abort
     290                 :             :                  * if one backend terminated on its own during the run.
     291                 :             :                  */
     292   [ #  #  #  # ]:           0 :                 ereport(WARNING,
     293                 :             :                                 (errmsg("PID %d is not a PostgreSQL server process", pid)));
     294                 :           0 :                 PG_RETURN_BOOL(false);
     295                 :             :         }
     296                 :             : 
     297                 :           3 :         procNumber = GetNumberFromPGProc(proc);
     298         [ +  - ]:           3 :         if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, procNumber) < 0)
     299                 :             :         {
     300                 :             :                 /* Again, just a warning to allow loops */
     301   [ #  #  #  # ]:           0 :                 ereport(WARNING,
     302                 :             :                                 (errmsg("could not send signal to process %d: %m", pid)));
     303                 :           0 :                 PG_RETURN_BOOL(false);
     304                 :             :         }
     305                 :             : 
     306                 :           3 :         PG_RETURN_BOOL(true);
     307                 :           3 : }
        

Generated by: LCOV version 2.3.2-1