LCOV - code coverage report
Current view: top level - src/backend/utils/adt - mcxtfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 47 53 88.7 %
Date: 2023-05-31 03:12:18 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-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        1464 : 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             :     Assert(MemoryContextIsValid(context));
      50             : 
      51        1464 :     name = context->name;
      52        1464 :     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        1464 :     if (ident && strcmp(name, "dynahash") == 0)
      59             :     {
      60         126 :         name = ident;
      61         126 :         ident = NULL;
      62             :     }
      63             : 
      64             :     /* Examine the context itself */
      65        1464 :     memset(&stat, 0, sizeof(stat));
      66        1464 :     (*context->methods->stats) (context, NULL, (void *) &level, &stat, true);
      67             : 
      68        1464 :     memset(values, 0, sizeof(values));
      69        1464 :     memset(nulls, 0, sizeof(nulls));
      70             : 
      71        1464 :     if (name)
      72        1464 :         values[0] = CStringGetTextDatum(name);
      73             :     else
      74           0 :         nulls[0] = true;
      75             : 
      76        1464 :     if (ident)
      77             :     {
      78        1104 :         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        1104 :         if (idlen >= MEMORY_CONTEXT_IDENT_DISPLAY_SIZE)
      86           0 :             idlen = pg_mbcliplen(ident, idlen, MEMORY_CONTEXT_IDENT_DISPLAY_SIZE - 1);
      87             : 
      88        1104 :         memcpy(clipped_ident, ident, idlen);
      89        1104 :         clipped_ident[idlen] = '\0';
      90        1104 :         values[1] = CStringGetTextDatum(clipped_ident);
      91             :     }
      92             :     else
      93         360 :         nulls[1] = true;
      94             : 
      95        1464 :     if (parent)
      96        1452 :         values[2] = CStringGetTextDatum(parent);
      97             :     else
      98          12 :         nulls[2] = true;
      99             : 
     100        1464 :     values[3] = Int32GetDatum(level);
     101        1464 :     values[4] = Int64GetDatum(stat.totalspace);
     102        1464 :     values[5] = Int64GetDatum(stat.nblocks);
     103        1464 :     values[6] = Int64GetDatum(stat.freespace);
     104        1464 :     values[7] = Int64GetDatum(stat.freechunks);
     105        1464 :     values[8] = Int64GetDatum(stat.totalspace - stat.freespace);
     106        1464 :     tuplestore_putvalues(tupstore, tupdesc, values, nulls);
     107             : 
     108        2916 :     for (child = context->firstchild; child != NULL; child = child->nextchild)
     109             :     {
     110        1452 :         PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
     111             :                                          child, name, level + 1);
     112             :     }
     113        1464 : }
     114             : 
     115             : /*
     116             :  * pg_get_backend_memory_contexts
     117             :  *      SQL SRF showing backend memory context.
     118             :  */
     119             : Datum
     120          12 : pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
     121             : {
     122          12 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     123             : 
     124          12 :     InitMaterializedSRF(fcinfo, 0);
     125          12 :     PutMemoryContextsStatsTupleStore(rsinfo->setResult, rsinfo->setDesc,
     126             :                                      TopMemoryContext, NULL, 0);
     127             : 
     128          12 :     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          18 : pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
     146             : {
     147          18 :     int         pid = PG_GETARG_INT32(0);
     148             :     PGPROC     *proc;
     149          18 :     BackendId   backendId = InvalidBackendId;
     150             : 
     151          18 :     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          18 :     if (proc != NULL)
     162          12 :         backendId = proc->backendId;
     163             :     else
     164           6 :         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          18 :     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           0 :         ereport(WARNING,
     182             :                 (errmsg("PID %d is not a PostgreSQL server process", pid)));
     183           0 :         PG_RETURN_BOOL(false);
     184             :     }
     185             : 
     186          18 :     if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
     187             :     {
     188             :         /* Again, just a warning to allow loops */
     189           0 :         ereport(WARNING,
     190             :                 (errmsg("could not send signal to process %d: %m", pid)));
     191           0 :         PG_RETURN_BOOL(false);
     192             :     }
     193             : 
     194          18 :     PG_RETURN_BOOL(true);
     195             : }

Generated by: LCOV version 1.14