LCOV - code coverage report
Current view: top level - src/backend/utils/adt - mcxtfuncs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 85.9 % 92 79
Test Date: 2026-07-22 12:15:41 Functions: 100.0 % 4 4
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 66.7 % 45 30

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

Generated by: LCOV version 2.0-1