LCOV - code coverage report
Current view: top level - src/backend/utils/adt - mcxtfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 47 53 88.7 %
Date: 2024-03-29 00:11:46 Functions: 3 3 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-2024, 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/builtins.h"
      23             : 
      24             : /* ----------
      25             :  * The max bytes for showing identifiers of MemoryContext.
      26             :  * ----------
      27             :  */
      28             : #define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE   1024
      29             : 
      30             : /*
      31             :  * PutMemoryContextsStatsTupleStore
      32             :  *      One recursion level for pg_get_backend_memory_contexts.
      33             :  */
      34             : static void
      35        1476 : PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore,
      36             :                                  TupleDesc tupdesc, MemoryContext context,
      37             :                                  const char *parent, int level)
      38             : {
      39             : #define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS 9
      40             : 
      41             :     Datum       values[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
      42             :     bool        nulls[PG_GET_BACKEND_MEMORY_CONTEXTS_COLS];
      43             :     MemoryContextCounters stat;
      44             :     MemoryContext child;
      45             :     const char *name;
      46             :     const char *ident;
      47             : 
      48             :     Assert(MemoryContextIsValid(context));
      49             : 
      50        1476 :     name = context->name;
      51        1476 :     ident = context->ident;
      52             : 
      53             :     /*
      54             :      * To be consistent with logging output, we label dynahash contexts with
      55             :      * just the hash table name as with MemoryContextStatsPrint().
      56             :      */
      57        1476 :     if (ident && strcmp(name, "dynahash") == 0)
      58             :     {
      59         126 :         name = ident;
      60         126 :         ident = NULL;
      61             :     }
      62             : 
      63             :     /* Examine the context itself */
      64        1476 :     memset(&stat, 0, sizeof(stat));
      65        1476 :     (*context->methods->stats) (context, NULL, (void *) &level, &stat, true);
      66             : 
      67        1476 :     memset(values, 0, sizeof(values));
      68        1476 :     memset(nulls, 0, sizeof(nulls));
      69             : 
      70        1476 :     if (name)
      71        1476 :         values[0] = CStringGetTextDatum(name);
      72             :     else
      73           0 :         nulls[0] = true;
      74             : 
      75        1476 :     if (ident)
      76             :     {
      77        1104 :         int         idlen = strlen(ident);
      78             :         char        clipped_ident[MEMORY_CONTEXT_IDENT_DISPLAY_SIZE];
      79             : 
      80             :         /*
      81             :          * Some identifiers such as SQL query string can be very long,
      82             :          * truncate oversize identifiers.
      83             :          */
      84        1104 :         if (idlen >= MEMORY_CONTEXT_IDENT_DISPLAY_SIZE)
      85           0 :             idlen = pg_mbcliplen(ident, idlen, MEMORY_CONTEXT_IDENT_DISPLAY_SIZE - 1);
      86             : 
      87        1104 :         memcpy(clipped_ident, ident, idlen);
      88        1104 :         clipped_ident[idlen] = '\0';
      89        1104 :         values[1] = CStringGetTextDatum(clipped_ident);
      90             :     }
      91             :     else
      92         372 :         nulls[1] = true;
      93             : 
      94        1476 :     if (parent)
      95        1464 :         values[2] = CStringGetTextDatum(parent);
      96             :     else
      97          12 :         nulls[2] = true;
      98             : 
      99        1476 :     values[3] = Int32GetDatum(level);
     100        1476 :     values[4] = Int64GetDatum(stat.totalspace);
     101        1476 :     values[5] = Int64GetDatum(stat.nblocks);
     102        1476 :     values[6] = Int64GetDatum(stat.freespace);
     103        1476 :     values[7] = Int64GetDatum(stat.freechunks);
     104        1476 :     values[8] = Int64GetDatum(stat.totalspace - stat.freespace);
     105        1476 :     tuplestore_putvalues(tupstore, tupdesc, values, nulls);
     106             : 
     107        2940 :     for (child = context->firstchild; child != NULL; child = child->nextchild)
     108             :     {
     109        1464 :         PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
     110             :                                          child, name, level + 1);
     111             :     }
     112        1476 : }
     113             : 
     114             : /*
     115             :  * pg_get_backend_memory_contexts
     116             :  *      SQL SRF showing backend memory context.
     117             :  */
     118             : Datum
     119          12 : pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
     120             : {
     121          12 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     122             : 
     123          12 :     InitMaterializedSRF(fcinfo, 0);
     124          12 :     PutMemoryContextsStatsTupleStore(rsinfo->setResult, rsinfo->setDesc,
     125             :                                      TopMemoryContext, NULL, 0);
     126             : 
     127          12 :     return (Datum) 0;
     128             : }
     129             : 
     130             : /*
     131             :  * pg_log_backend_memory_contexts
     132             :  *      Signal a backend or an auxiliary process to log its memory contexts.
     133             :  *
     134             :  * By default, only superusers are allowed to signal to log the memory
     135             :  * contexts because allowing any users to issue this request at an unbounded
     136             :  * rate would cause lots of log messages and which can lead to denial of
     137             :  * service. Additional roles can be permitted with GRANT.
     138             :  *
     139             :  * On receipt of this signal, a backend or an auxiliary process sets the flag
     140             :  * in the signal handler, which causes the next CHECK_FOR_INTERRUPTS()
     141             :  * or process-specific interrupt handler to log the memory contexts.
     142             :  */
     143             : Datum
     144          18 : pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
     145             : {
     146          18 :     int         pid = PG_GETARG_INT32(0);
     147             :     PGPROC     *proc;
     148          18 :     ProcNumber  procNumber = INVALID_PROC_NUMBER;
     149             : 
     150             :     /*
     151             :      * See if the process with given pid is a backend or an auxiliary process.
     152             :      */
     153          18 :     proc = BackendPidGetProc(pid);
     154          18 :     if (proc == NULL)
     155           6 :         proc = AuxiliaryPidGetProc(pid);
     156             : 
     157             :     /*
     158             :      * BackendPidGetProc() and AuxiliaryPidGetProc() return NULL if the pid
     159             :      * isn't valid; but by the time we reach kill(), a process for which we
     160             :      * get a valid proc here might have terminated on its own.  There's no way
     161             :      * to acquire a lock on an arbitrary process to prevent that. But since
     162             :      * this mechanism is usually used to debug a backend or an auxiliary
     163             :      * process running and consuming lots of memory, that it might end on its
     164             :      * own first and its memory contexts are not logged is not a problem.
     165             :      */
     166          18 :     if (proc == NULL)
     167             :     {
     168             :         /*
     169             :          * This is just a warning so a loop-through-resultset will not abort
     170             :          * if one backend terminated on its own during the run.
     171             :          */
     172           0 :         ereport(WARNING,
     173             :                 (errmsg("PID %d is not a PostgreSQL server process", pid)));
     174           0 :         PG_RETURN_BOOL(false);
     175             :     }
     176             : 
     177          18 :     procNumber = GetNumberFromPGProc(proc);
     178          18 :     if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, procNumber) < 0)
     179             :     {
     180             :         /* Again, just a warning to allow loops */
     181           0 :         ereport(WARNING,
     182             :                 (errmsg("could not send signal to process %d: %m", pid)));
     183           0 :         PG_RETURN_BOOL(false);
     184             :     }
     185             : 
     186          18 :     PG_RETURN_BOOL(true);
     187             : }

Generated by: LCOV version 1.14