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 : }
|