PostgreSQL Source Code  git master
mcxtfuncs.c
Go to the documentation of this file.
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/array.h"
23 #include "utils/builtins.h"
24 #include "utils/hsearch.h"
25 
26 /* ----------
27  * The max bytes for showing identifiers of MemoryContext.
28  * ----------
29  */
30 #define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE 1024
31 
32 /*
33  * MemoryContextId
34  * Used for storage of transient identifiers for
35  * pg_get_backend_memory_contexts.
36  */
37 typedef struct MemoryContextId
38 {
42 
43 /*
44  * int_list_to_array
45  * Convert an IntList to an array of INT4OIDs.
46  */
47 static Datum
49 {
50  Datum *datum_array;
51  int length;
52  ArrayType *result_array;
53 
54  length = list_length(list);
55  datum_array = (Datum *) palloc(length * sizeof(Datum));
56 
58  datum_array[foreach_current_index(i)] = Int32GetDatum(i);
59 
60  result_array = construct_array_builtin(datum_array, length, INT4OID);
61 
62  return PointerGetDatum(result_array);
63 }
64 
65 /*
66  * PutMemoryContextsStatsTupleStore
67  * Add details for the given MemoryContext to 'tupstore'.
68  */
69 static void
72  HTAB *context_id_lookup)
73 {
74 #define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS 10
75 
79  List *path = NIL;
80  const char *name;
81  const char *ident;
82  const char *type;
83 
85 
86  /*
87  * Figure out the transient context_id of this context and each of its
88  * ancestors.
89  */
90  for (MemoryContext cur = context; cur != NULL; cur = cur->parent)
91  {
92  MemoryContextId *entry;
93  bool found;
94 
95  entry = hash_search(context_id_lookup, &cur, HASH_FIND, &found);
96 
97  if (!found)
98  elog(ERROR, "hash table corrupted");
99  path = lcons_int(entry->context_id, path);
100  }
101 
102  /* Examine the context itself */
103  memset(&stat, 0, sizeof(stat));
104  (*context->methods->stats) (context, NULL, NULL, &stat, true);
105 
106  memset(values, 0, sizeof(values));
107  memset(nulls, 0, sizeof(nulls));
108 
109  name = context->name;
110  ident = context->ident;
111 
112  /*
113  * To be consistent with logging output, we label dynahash contexts with
114  * just the hash table name as with MemoryContextStatsPrint().
115  */
116  if (ident && strcmp(name, "dynahash") == 0)
117  {
118  name = ident;
119  ident = NULL;
120  }
121 
122  if (name)
124  else
125  nulls[0] = true;
126 
127  if (ident)
128  {
129  int idlen = strlen(ident);
130  char clipped_ident[MEMORY_CONTEXT_IDENT_DISPLAY_SIZE];
131 
132  /*
133  * Some identifiers such as SQL query string can be very long,
134  * truncate oversize identifiers.
135  */
138 
139  memcpy(clipped_ident, ident, idlen);
140  clipped_ident[idlen] = '\0';
141  values[1] = CStringGetTextDatum(clipped_ident);
142  }
143  else
144  nulls[1] = true;
145 
146  switch (context->type)
147  {
148  case T_AllocSetContext:
149  type = "AllocSet";
150  break;
151  case T_GenerationContext:
152  type = "Generation";
153  break;
154  case T_SlabContext:
155  type = "Slab";
156  break;
157  case T_BumpContext:
158  type = "Bump";
159  break;
160  default:
161  type = "???";
162  break;
163  }
164 
166  values[3] = Int32GetDatum(list_length(path)); /* level */
167  values[4] = int_list_to_array(path);
168  values[5] = Int64GetDatum(stat.totalspace);
169  values[6] = Int64GetDatum(stat.nblocks);
170  values[7] = Int64GetDatum(stat.freespace);
171  values[8] = Int64GetDatum(stat.freechunks);
172  values[9] = Int64GetDatum(stat.totalspace - stat.freespace);
173 
174  tuplestore_putvalues(tupstore, tupdesc, values, nulls);
175  list_free(path);
176 }
177 
178 /*
179  * pg_get_backend_memory_contexts
180  * SQL SRF showing backend memory context.
181  */
182 Datum
184 {
185  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
186  int context_id;
187  List *contexts;
188  HASHCTL ctl;
189  HTAB *context_id_lookup;
190 
191  ctl.keysize = sizeof(MemoryContext);
192  ctl.entrysize = sizeof(MemoryContextId);
193  ctl.hcxt = CurrentMemoryContext;
194 
195  context_id_lookup = hash_create("pg_get_backend_memory_contexts",
196  256,
197  &ctl,
199 
200  InitMaterializedSRF(fcinfo, 0);
201 
202  /*
203  * Here we use a non-recursive algorithm to visit all MemoryContexts
204  * starting with TopMemoryContext. The reason we avoid using a recursive
205  * algorithm is because we want to assign the context_id breadth-first.
206  * I.e. all contexts at level 1 are assigned IDs before contexts at level
207  * 2. Because contexts closer to TopMemoryContext are less likely to
208  * change, this makes the assigned context_id more stable. Otherwise, if
209  * the first child of TopMemoryContext obtained an additional grandchild,
210  * the context_id for the second child of TopMemoryContext would change.
211  */
212  contexts = list_make1(TopMemoryContext);
213 
214  /* TopMemoryContext will always have a context_id of 1 */
215  context_id = 1;
216 
217  foreach_ptr(MemoryContextData, cur, contexts)
218  {
219  MemoryContextId *entry;
220  bool found;
221 
222  /*
223  * Record the context_id that we've assigned to each MemoryContext.
224  * PutMemoryContextsStatsTupleStore needs this to populate the "path"
225  * column with the parent context_ids.
226  */
227  entry = (MemoryContextId *) hash_search(context_id_lookup, &cur,
228  HASH_ENTER, &found);
229  entry->context_id = context_id++;
230  Assert(!found);
231 
233  rsinfo->setDesc,
234  cur,
235  context_id_lookup);
236 
237  /*
238  * Append all children onto the contexts list so they're processed by
239  * subsequent iterations.
240  */
241  for (MemoryContext c = cur->firstchild; c != NULL; c = c->nextchild)
242  contexts = lappend(contexts, c);
243  }
244 
245  hash_destroy(context_id_lookup);
246 
247  return (Datum) 0;
248 }
249 
250 /*
251  * pg_log_backend_memory_contexts
252  * Signal a backend or an auxiliary process to log its memory contexts.
253  *
254  * By default, only superusers are allowed to signal to log the memory
255  * contexts because allowing any users to issue this request at an unbounded
256  * rate would cause lots of log messages and which can lead to denial of
257  * service. Additional roles can be permitted with GRANT.
258  *
259  * On receipt of this signal, a backend or an auxiliary process sets the flag
260  * in the signal handler, which causes the next CHECK_FOR_INTERRUPTS()
261  * or process-specific interrupt handler to log the memory contexts.
262  */
263 Datum
265 {
266  int pid = PG_GETARG_INT32(0);
267  PGPROC *proc;
268  ProcNumber procNumber = INVALID_PROC_NUMBER;
269 
270  /*
271  * See if the process with given pid is a backend or an auxiliary process.
272  */
273  proc = BackendPidGetProc(pid);
274  if (proc == NULL)
275  proc = AuxiliaryPidGetProc(pid);
276 
277  /*
278  * BackendPidGetProc() and AuxiliaryPidGetProc() return NULL if the pid
279  * isn't valid; but by the time we reach kill(), a process for which we
280  * get a valid proc here might have terminated on its own. There's no way
281  * to acquire a lock on an arbitrary process to prevent that. But since
282  * this mechanism is usually used to debug a backend or an auxiliary
283  * process running and consuming lots of memory, that it might end on its
284  * own first and its memory contexts are not logged is not a problem.
285  */
286  if (proc == NULL)
287  {
288  /*
289  * This is just a warning so a loop-through-resultset will not abort
290  * if one backend terminated on its own during the run.
291  */
293  (errmsg("PID %d is not a PostgreSQL server process", pid)));
294  PG_RETURN_BOOL(false);
295  }
296 
297  procNumber = GetNumberFromPGProc(proc);
298  if (SendProcSignal(pid, PROCSIG_LOG_MEMORY_CONTEXT, procNumber) < 0)
299  {
300  /* Again, just a warning to allow loops */
302  (errmsg("could not send signal to process %d: %m", pid)));
303  PG_RETURN_BOOL(false);
304  }
305 
306  PG_RETURN_BOOL(true);
307 }
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
Definition: arrayfuncs.c:3381
static Datum values[MAXATTR]
Definition: bootstrap.c:150
#define CStringGetTextDatum(s)
Definition: builtins.h:97
#define Assert(condition)
Definition: c.h:858
void hash_destroy(HTAB *hashp)
Definition: dynahash.c:865
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
struct cursor * cur
Definition: ecpg.c:28
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define WARNING
Definition: elog.h:36
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1807
#define PG_GETARG_INT32(n)
Definition: fmgr.h:269
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
Definition: funcapi.c:76
@ HASH_FIND
Definition: hsearch.h:113
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
#define ident
Definition: indent_codes.h:47
int i
Definition: isn.c:73
List * lcons_int(int datum, List *list)
Definition: list.c:513
List * lappend(List *list, void *datum)
Definition: list.c:339
void list_free(List *list)
Definition: list.c:1546
int pg_mbcliplen(const char *mbstr, int len, int limit)
Definition: mbutils.c:1083
MemoryContext TopMemoryContext
Definition: mcxt.c:149
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void * palloc(Size size)
Definition: mcxt.c:1317
struct MemoryContextId MemoryContextId
static Datum int_list_to_array(const List *list)
Definition: mcxtfuncs.c:48
static void PutMemoryContextsStatsTupleStore(Tuplestorestate *tupstore, TupleDesc tupdesc, MemoryContext context, HTAB *context_id_lookup)
Definition: mcxtfuncs.c:70
Datum pg_log_backend_memory_contexts(PG_FUNCTION_ARGS)
Definition: mcxtfuncs.c:264
#define PG_GET_BACKEND_MEMORY_CONTEXTS_COLS
#define MEMORY_CONTEXT_IDENT_DISPLAY_SIZE
Definition: mcxtfuncs.c:30
Datum pg_get_backend_memory_contexts(PG_FUNCTION_ARGS)
Definition: mcxtfuncs.c:183
#define MemoryContextIsValid(context)
Definition: memnodes.h:145
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
#define list_make1(x1)
Definition: pg_list.h:212
#define foreach_ptr(type, var, lst)
Definition: pg_list.h:469
#define foreach_int(var, lst)
Definition: pg_list.h:470
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:212
char * c
#define GetNumberFromPGProc(proc)
Definition: proc.h:428
PGPROC * BackendPidGetProc(int pid)
Definition: procarray.c:3200
#define INVALID_PROC_NUMBER
Definition: procnumber.h:26
int ProcNumber
Definition: procnumber.h:24
int SendProcSignal(pid_t pid, ProcSignalReason reason, ProcNumber procNumber)
Definition: procsignal.c:281
@ PROCSIG_LOG_MEMORY_CONTEXT
Definition: procsignal.h:37
tree context
Definition: radixtree.h:1835
tree ctl
Definition: radixtree.h:1853
PGPROC * AuxiliaryPidGetProc(int pid)
Definition: proc.c:1024
Definition: dynahash.c:220
Definition: pg_list.h:54
MemoryContext context
Definition: mcxtfuncs.c:39
Definition: proc.h:157
TupleDesc setDesc
Definition: execnodes.h:343
Tuplestorestate * setResult
Definition: execnodes.h:342
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition: tuplestore.c:784
const char * type
const char * name
#define stat
Definition: win32_port.h:284