Age Owner Branch data TLA 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
1327 fujii@postgresql.org 35 :CBC 738 : 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 : :
534 peter@eisentraut.org 48 [ + - - + : 738 : Assert(MemoryContextIsValid(context));
- - - - -
- ]
49 : :
1327 fujii@postgresql.org 50 : 738 : name = context->name;
51 : 738 : 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 [ + + + + ]: 738 : if (ident && strcmp(name, "dynahash") == 0)
58 : : {
59 : 63 : name = ident;
60 : 63 : ident = NULL;
61 : : }
62 : :
63 : : /* Examine the context itself */
64 : 738 : memset(&stat, 0, sizeof(stat));
1104 65 : 738 : (*context->methods->stats) (context, NULL, (void *) &level, &stat, true);
66 : :
1327 67 : 738 : memset(values, 0, sizeof(values));
68 : 738 : memset(nulls, 0, sizeof(nulls));
69 : :
70 [ + - ]: 738 : if (name)
71 : 738 : values[0] = CStringGetTextDatum(name);
72 : : else
1327 fujii@postgresql.org 73 :UBC 0 : nulls[0] = true;
74 : :
1327 fujii@postgresql.org 75 [ + + ]:CBC 738 : if (ident)
76 : : {
1068 tgl@sss.pgh.pa.us 77 : 552 : 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 : : */
1327 fujii@postgresql.org 84 [ - + ]: 552 : if (idlen >= MEMORY_CONTEXT_IDENT_DISPLAY_SIZE)
1327 fujii@postgresql.org 85 :UBC 0 : idlen = pg_mbcliplen(ident, idlen, MEMORY_CONTEXT_IDENT_DISPLAY_SIZE - 1);
86 : :
1327 fujii@postgresql.org 87 :CBC 552 : memcpy(clipped_ident, ident, idlen);
88 : 552 : clipped_ident[idlen] = '\0';
89 : 552 : values[1] = CStringGetTextDatum(clipped_ident);
90 : : }
91 : : else
92 : 186 : nulls[1] = true;
93 : :
94 [ + + ]: 738 : if (parent)
95 : 732 : values[2] = CStringGetTextDatum(parent);
96 : : else
97 : 6 : nulls[2] = true;
98 : :
99 : 738 : values[3] = Int32GetDatum(level);
100 : 738 : values[4] = Int64GetDatum(stat.totalspace);
101 : 738 : values[5] = Int64GetDatum(stat.nblocks);
102 : 738 : values[6] = Int64GetDatum(stat.freespace);
103 : 738 : values[7] = Int64GetDatum(stat.freechunks);
104 : 738 : values[8] = Int64GetDatum(stat.totalspace - stat.freespace);
105 : 738 : tuplestore_putvalues(tupstore, tupdesc, values, nulls);
106 : :
107 [ + + ]: 1470 : for (child = context->firstchild; child != NULL; child = child->nextchild)
108 : : {
109 : 732 : PutMemoryContextsStatsTupleStore(tupstore, tupdesc,
110 : : child, name, level + 1);
111 : : }
112 : 738 : }
113 : :
114 : : /*
115 : : * pg_get_backend_memory_contexts
116 : : * SQL SRF showing backend memory context.
117 : : */
118 : : Datum
119 : 6 : pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
120 : : {
121 : 6 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
122 : :
544 michael@paquier.xyz 123 : 6 : InitMaterializedSRF(fcinfo, 0);
769 124 : 6 : PutMemoryContextsStatsTupleStore(rsinfo->setResult, rsinfo->setDesc,
125 : : TopMemoryContext, NULL, 0);
126 : :
1327 fujii@postgresql.org 127 : 6 : 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
1104 144 : 9 : pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
145 : : {
146 : 9 : int pid = PG_GETARG_INT32(0);
147 : : PGPROC *proc;
42 heikki.linnakangas@i 148 :GNC 9 : ProcNumber procNumber = INVALID_PROC_NUMBER;
149 : :
150 : : /*
151 : : * See if the process with given pid is a backend or an auxiliary process.
152 : : */
153 : 9 : proc = BackendPidGetProc(pid);
154 [ + + ]: 9 : if (proc == NULL)
824 fujii@postgresql.org 155 :CBC 3 : 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 : : */
1104 166 [ - + ]: 9 : 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 : : */
1104 fujii@postgresql.org 172 [ # # ]:UBC 0 : ereport(WARNING,
173 : : (errmsg("PID %d is not a PostgreSQL server process", pid)));
174 : 0 : PG_RETURN_BOOL(false);
175 : : }
176 : :
42 heikki.linnakangas@i 177 :GNC 9 : procNumber = GetNumberFromPGProc(proc);
178 [ - + ]: 9 : if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, procNumber) < 0)
179 : : {
180 : : /* Again, just a warning to allow loops */
1104 fujii@postgresql.org 181 [ # # ]:UBC 0 : ereport(WARNING,
182 : : (errmsg("could not send signal to process %d: %m", pid)));
183 : 0 : PG_RETURN_BOOL(false);
184 : : }
185 : :
1104 fujii@postgresql.org 186 :CBC 9 : PG_RETURN_BOOL(true);
187 : : }
|