Age Owner 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
956 fujii 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 :
163 peter 49 GNC 732 : Assert(MemoryContextIsValid(context));
50 :
956 fujii 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));
733 66 732 : (*context->methods->stats) (context, NULL, (void *) &level, &stat, true);
67 :
956 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
956 fujii 74 UBC 0 : nulls[0] = true;
75 :
956 fujii 76 CBC 732 : if (ident)
77 : {
697 tgl 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 : */
956 fujii 85 552 : if (idlen >= MEMORY_CONTEXT_IDENT_DISPLAY_SIZE)
956 fujii 86 UBC 0 : idlen = pg_mbcliplen(ident, idlen, MEMORY_CONTEXT_IDENT_DISPLAY_SIZE - 1);
87 :
956 fujii 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 :
173 michael 124 6 : InitMaterializedSRF(fcinfo, 0);
398 125 6 : PutMemoryContextsStatsTupleStore(rsinfo->setResult, rsinfo->setDesc,
126 : TopMemoryContext, NULL, 0);
127 :
956 fujii 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
733 145 9 : pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
146 : {
147 9 : int pid = PG_GETARG_INT32(0);
148 : PGPROC *proc;
453 149 9 : BackendId backendId = InvalidBackendId;
150 :
670 michael 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 : */
453 fujii 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 : */
733 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 : */
733 fujii 181 UBC 0 : ereport(WARNING,
182 : (errmsg("PID %d is not a PostgreSQL server process", pid)));
183 0 : PG_RETURN_BOOL(false);
184 : }
185 :
453 fujii 186 CBC 9 : if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, backendId) < 0)
187 : {
188 : /* Again, just a warning to allow loops */
733 fujii 189 UBC 0 : ereport(WARNING,
190 : (errmsg("could not send signal to process %d: %m", pid)));
191 0 : PG_RETURN_BOOL(false);
192 : }
193 :
733 fujii 194 CBC 9 : PG_RETURN_BOOL(true);
195 : }
|