LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - mcxtfuncs.c (source / functions) Coverage Total Hit UBC GNC CBC DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 88.9 % 54 48 6 1 47 1
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 3 3 1 2
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a