Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * memutils.h
4 : * This file contains declarations for memory allocation utility
5 : * functions. These are functions that are not quite widely used
6 : * enough to justify going in utils/palloc.h, but are still part
7 : * of the API of the memory management subsystem.
8 : *
9 : *
10 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
11 : * Portions Copyright (c) 1994, Regents of the University of California
12 : *
13 : * src/include/utils/memutils.h
14 : *
15 : *-------------------------------------------------------------------------
16 : */
17 : #ifndef MEMUTILS_H
18 : #define MEMUTILS_H
19 :
20 : #include "nodes/memnodes.h"
21 : #include "storage/condition_variable.h"
22 : #include "storage/lmgr.h"
23 : #include "utils/dsa.h"
24 :
25 :
26 : /*
27 : * MaxAllocSize, MaxAllocHugeSize
28 : * Quasi-arbitrary limits on size of allocations.
29 : *
30 : * Note:
31 : * There is no guarantee that smaller allocations will succeed, but
32 : * larger requests will be summarily denied.
33 : *
34 : * palloc() enforces MaxAllocSize, chosen to correspond to the limiting size
35 : * of varlena objects under TOAST. See VARSIZE_4B() and related macros in
36 : * postgres.h. Many datatypes assume that any allocatable size can be
37 : * represented in a varlena header. This limit also permits a caller to use
38 : * an "int" variable for an index into or length of an allocation. Callers
39 : * careful to avoid these hazards can access the higher limit with
40 : * MemoryContextAllocHuge(). Both limits permit code to assume that it may
41 : * compute twice an allocation's size without overflow.
42 : */
43 : #define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
44 :
45 : #define AllocSizeIsValid(size) ((Size) (size) <= MaxAllocSize)
46 :
47 : /* Must be less than SIZE_MAX */
48 : #define MaxAllocHugeSize (SIZE_MAX / 2)
49 :
50 : #define InvalidAllocSize SIZE_MAX
51 :
52 : #define AllocHugeSizeIsValid(size) ((Size) (size) <= MaxAllocHugeSize)
53 :
54 : /*
55 : * Memory Context reporting size limits.
56 : */
57 :
58 : /* Max length of context name and ident */
59 : #define MEMORY_CONTEXT_IDENT_SHMEM_SIZE 64
60 : /* Maximum size (in bytes) of DSA area per process */
61 : #define MEMORY_CONTEXT_REPORT_MAX_PER_BACKEND ((size_t) (1 * 1024 * 1024))
62 :
63 : /*
64 : * Maximum size per context. Actual size may be lower as this assumes the worst
65 : * case of deepest path and longest identifiers (name and ident, thus the
66 : * multiplication by 2). The path depth is limited to 100 like for memory
67 : * context logging.
68 : */
69 : #define MAX_MEMORY_CONTEXT_STATS_SIZE (sizeof(MemoryStatsEntry) + \
70 : (100 * sizeof(int)) + (2 * MEMORY_CONTEXT_IDENT_SHMEM_SIZE))
71 :
72 : /*
73 : * Standard top-level memory contexts.
74 : *
75 : * Only TopMemoryContext and ErrorContext are initialized by
76 : * MemoryContextInit() itself.
77 : */
78 : extern PGDLLIMPORT MemoryContext TopMemoryContext;
79 : extern PGDLLIMPORT MemoryContext ErrorContext;
80 : extern PGDLLIMPORT MemoryContext PostmasterContext;
81 : extern PGDLLIMPORT MemoryContext CacheMemoryContext;
82 : extern PGDLLIMPORT MemoryContext MessageContext;
83 : extern PGDLLIMPORT MemoryContext TopTransactionContext;
84 : extern PGDLLIMPORT MemoryContext CurTransactionContext;
85 :
86 : /* This is a transient link to the active portal's memory context: */
87 : extern PGDLLIMPORT MemoryContext PortalContext;
88 :
89 :
90 : /*
91 : * Memory-context-type-independent functions in mcxt.c
92 : */
93 : extern void MemoryContextInit(void);
94 : extern void MemoryContextReset(MemoryContext context);
95 : extern void MemoryContextDelete(MemoryContext context);
96 : extern void MemoryContextResetOnly(MemoryContext context);
97 : extern void MemoryContextResetChildren(MemoryContext context);
98 : extern void MemoryContextDeleteChildren(MemoryContext context);
99 : extern void MemoryContextSetIdentifier(MemoryContext context, const char *id);
100 : extern void MemoryContextSetParent(MemoryContext context,
101 : MemoryContext new_parent);
102 : extern MemoryContext GetMemoryChunkContext(void *pointer);
103 : extern Size GetMemoryChunkSpace(void *pointer);
104 : extern MemoryContext MemoryContextGetParent(MemoryContext context);
105 : extern bool MemoryContextIsEmpty(MemoryContext context);
106 : extern Size MemoryContextMemAllocated(MemoryContext context, bool recurse);
107 : extern void MemoryContextMemConsumed(MemoryContext context,
108 : MemoryContextCounters *consumed);
109 : extern void MemoryContextStats(MemoryContext context);
110 : extern void MemoryContextStatsDetail(MemoryContext context,
111 : int max_level, int max_children,
112 : bool print_to_stderr);
113 : extern void MemoryContextAllowInCriticalSection(MemoryContext context,
114 : bool allow);
115 :
116 : #ifdef MEMORY_CONTEXT_CHECKING
117 : extern void MemoryContextCheck(MemoryContext context);
118 : #endif
119 :
120 : /* Handy macro for copying and assigning context ID ... but note double eval */
121 : #define MemoryContextCopyAndSetIdentifier(cxt, id) \
122 : MemoryContextSetIdentifier(cxt, MemoryContextStrdup(cxt, id))
123 :
124 : extern void HandleLogMemoryContextInterrupt(void);
125 : extern void ProcessLogMemoryContextInterrupt(void);
126 :
127 : /*
128 : * Memory-context-type-specific functions
129 : */
130 :
131 : /* aset.c */
132 : extern MemoryContext AllocSetContextCreateInternal(MemoryContext parent,
133 : const char *name,
134 : Size minContextSize,
135 : Size initBlockSize,
136 : Size maxBlockSize);
137 :
138 : /*
139 : * This wrapper macro exists to check for non-constant strings used as context
140 : * names; that's no longer supported. (Use MemoryContextSetIdentifier if you
141 : * want to provide a variable identifier.)
142 : */
143 : #ifdef HAVE__BUILTIN_CONSTANT_P
144 : #define AllocSetContextCreate(parent, name, ...) \
145 : (StaticAssertExpr(__builtin_constant_p(name), \
146 : "memory context names must be constant strings"), \
147 : AllocSetContextCreateInternal(parent, name, __VA_ARGS__))
148 : #else
149 : #define AllocSetContextCreate \
150 : AllocSetContextCreateInternal
151 : #endif
152 :
153 : /* slab.c */
154 : extern MemoryContext SlabContextCreate(MemoryContext parent,
155 : const char *name,
156 : Size blockSize,
157 : Size chunkSize);
158 :
159 : /* generation.c */
160 : extern MemoryContext GenerationContextCreate(MemoryContext parent,
161 : const char *name,
162 : Size minContextSize,
163 : Size initBlockSize,
164 : Size maxBlockSize);
165 :
166 : /* bump.c */
167 : extern MemoryContext BumpContextCreate(MemoryContext parent,
168 : const char *name,
169 : Size minContextSize,
170 : Size initBlockSize,
171 : Size maxBlockSize);
172 :
173 : /*
174 : * Recommended default alloc parameters, suitable for "ordinary" contexts
175 : * that might hold quite a lot of data.
176 : */
177 : #define ALLOCSET_DEFAULT_MINSIZE 0
178 : #define ALLOCSET_DEFAULT_INITSIZE (8 * 1024)
179 : #define ALLOCSET_DEFAULT_MAXSIZE (8 * 1024 * 1024)
180 : #define ALLOCSET_DEFAULT_SIZES \
181 : ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE
182 :
183 : /*
184 : * Recommended alloc parameters for "small" contexts that are never expected
185 : * to contain much data (for example, a context to contain a query plan).
186 : */
187 : #define ALLOCSET_SMALL_MINSIZE 0
188 : #define ALLOCSET_SMALL_INITSIZE (1 * 1024)
189 : #define ALLOCSET_SMALL_MAXSIZE (8 * 1024)
190 : #define ALLOCSET_SMALL_SIZES \
191 : ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE
192 :
193 : /*
194 : * Recommended alloc parameters for contexts that should start out small,
195 : * but might sometimes grow big.
196 : */
197 : #define ALLOCSET_START_SMALL_SIZES \
198 : ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE
199 :
200 :
201 : /*
202 : * Threshold above which a request in an AllocSet context is certain to be
203 : * allocated separately (and thereby have constant allocation overhead).
204 : * Few callers should be interested in this, but tuplesort/tuplestore need
205 : * to know it.
206 : */
207 : #define ALLOCSET_SEPARATE_THRESHOLD 8192
208 :
209 : #define SLAB_DEFAULT_BLOCK_SIZE (8 * 1024)
210 : #define SLAB_LARGE_BLOCK_SIZE (8 * 1024 * 1024)
211 :
212 : /*
213 : * pg_memory_is_all_zeros
214 : *
215 : * Test if a memory region starting at "ptr" and of size "len" is full of
216 : * zeroes.
217 : *
218 : * The test is divided into multiple cases for safety reason and multiple
219 : * phases for efficiency.
220 : *
221 : * Case 1: len < sizeof(size_t) bytes, then byte-by-byte comparison.
222 : * Case 2: len < (sizeof(size_t) * 8 - 1) bytes:
223 : * - Phase 1: byte-by-byte comparison, until the pointer is aligned.
224 : * - Phase 2: size_t comparisons, with aligned pointers, up to the last
225 : * location possible.
226 : * - Phase 3: byte-by-byte comparison, until the end location.
227 : * Case 3: len >= (sizeof(size_t) * 8) bytes, same as case 2 except that an
228 : * additional phase is placed between Phase 1 and Phase 2, with
229 : * (8 * sizeof(size_t)) comparisons using bitwise OR to encourage
230 : * compilers to use SIMD instructions if available, up to the last
231 : * aligned location possible.
232 : *
233 : * Case 1 and Case 2 are mandatory to ensure that we won't read beyond the
234 : * memory area. This is portable for 32-bit and 64-bit architectures.
235 : *
236 : * Caller must ensure that "ptr" is not NULL.
237 : */
238 : static inline bool
239 1780338 : pg_memory_is_all_zeros(const void *ptr, size_t len)
240 : {
241 1780338 : const unsigned char *p = (const unsigned char *) ptr;
242 1780338 : const unsigned char *end = &p[len];
243 1780338 : const unsigned char *aligned_end = (const unsigned char *)
244 1780338 : ((uintptr_t) end & (~(sizeof(size_t) - 1)));
245 :
246 1780338 : if (len < sizeof(size_t))
247 : {
248 0 : while (p < end)
249 : {
250 0 : if (*p++ != 0)
251 0 : return false;
252 : }
253 0 : return true;
254 : }
255 :
256 : /* "len" in the [sizeof(size_t), sizeof(size_t) * 8 - 1] range */
257 1780338 : if (len < sizeof(size_t) * 8)
258 : {
259 : /* Compare bytes until the pointer "p" is aligned */
260 20588 : while (((uintptr_t) p & (sizeof(size_t) - 1)) != 0)
261 : {
262 0 : if (p == end)
263 0 : return true;
264 0 : if (*p++ != 0)
265 0 : return false;
266 : }
267 :
268 : /*
269 : * Compare remaining size_t-aligned chunks.
270 : *
271 : * There is no risk to read beyond the memory area, as "aligned_end"
272 : * cannot be higher than "end".
273 : */
274 74708 : for (; p < aligned_end; p += sizeof(size_t))
275 : {
276 65426 : if (*(size_t *) p != 0)
277 11306 : return false;
278 : }
279 :
280 : /* Compare remaining bytes until the end */
281 9282 : while (p < end)
282 : {
283 0 : if (*p++ != 0)
284 0 : return false;
285 : }
286 9282 : return true;
287 : }
288 :
289 : /* "len" in the [sizeof(size_t) * 8, inf) range */
290 :
291 : /* Compare bytes until the pointer "p" is aligned */
292 1759750 : while (((uintptr_t) p & (sizeof(size_t) - 1)) != 0)
293 : {
294 0 : if (p == end)
295 0 : return true;
296 :
297 0 : if (*p++ != 0)
298 0 : return false;
299 : }
300 :
301 : /*
302 : * Compare 8 * sizeof(size_t) chunks at once.
303 : *
304 : * For performance reasons, we manually unroll this loop and purposefully
305 : * use bitwise-ORs to combine each comparison. This prevents boolean
306 : * short-circuiting and lets the compiler know that it's safe to access
307 : * all 8 elements regardless of the result of the other comparisons. This
308 : * seems to be enough to coax a few compilers into using SIMD
309 : * instructions.
310 : */
311 3599560 : for (; p < aligned_end - (sizeof(size_t) * 7); p += sizeof(size_t) * 8)
312 : {
313 2858540 : if ((((size_t *) p)[0] != 0) | (((size_t *) p)[1] != 0) |
314 2858540 : (((size_t *) p)[2] != 0) | (((size_t *) p)[3] != 0) |
315 2858540 : (((size_t *) p)[4] != 0) | (((size_t *) p)[5] != 0) |
316 2858540 : (((size_t *) p)[6] != 0) | (((size_t *) p)[7] != 0))
317 1018730 : return false;
318 : }
319 :
320 : /*
321 : * Compare remaining size_t-aligned chunks.
322 : *
323 : * There is no risk to read beyond the memory area, as "aligned_end"
324 : * cannot be higher than "end".
325 : */
326 3597396 : for (; p < aligned_end; p += sizeof(size_t))
327 : {
328 3557026 : if (*(size_t *) p != 0)
329 700650 : return false;
330 : }
331 :
332 : /* Compare remaining bytes until the end */
333 40370 : while (p < end)
334 : {
335 0 : if (*p++ != 0)
336 0 : return false;
337 : }
338 :
339 40370 : return true;
340 : }
341 :
342 : /* Dynamic shared memory state for statistics per context */
343 : typedef struct MemoryStatsEntry
344 : {
345 : dsa_pointer name;
346 : dsa_pointer ident;
347 : dsa_pointer path;
348 : NodeTag type;
349 : int path_length;
350 : int levels;
351 : int64 totalspace;
352 : int64 nblocks;
353 : int64 freespace;
354 : int64 freechunks;
355 : int num_agg_stats;
356 : } MemoryStatsEntry;
357 :
358 : /*
359 : * Static shared memory state representing the DSA area created for memory
360 : * context statistics reporting. A single DSA area is created and used by all
361 : * the processes, each having its specific DSA allocations for sharing memory
362 : * statistics, tracked by per backend static shared memory state.
363 : */
364 : typedef struct MemoryStatsCtl
365 : {
366 : dsa_handle memstats_dsa_handle;
367 : LWLock lw_lock;
368 : } MemoryStatsCtl;
369 :
370 : /*
371 : * Per backend static shared memory state for memory context statistics
372 : * reporting.
373 : */
374 : typedef struct MemoryStatsBackendState
375 : {
376 : ConditionVariable memcxt_cv;
377 : LWLock lw_lock;
378 : int proc_id;
379 : int total_stats;
380 : bool summary;
381 : dsa_pointer memstats_dsa_pointer;
382 : TimestampTz stats_timestamp;
383 : } MemoryStatsBackendState;
384 :
385 :
386 : /*
387 : * Used for storage of transient identifiers for pg_get_backend_memory_contexts
388 : */
389 : typedef struct MemoryStatsContextId
390 : {
391 : MemoryContext context;
392 : int context_id;
393 : } MemoryStatsContextId;
394 :
395 : extern PGDLLIMPORT MemoryStatsBackendState *memCxtState;
396 : extern PGDLLIMPORT MemoryStatsCtl *memCxtArea;
397 : extern PGDLLIMPORT dsa_area *MemoryStatsDsaArea;
398 : extern void ProcessGetMemoryContextInterrupt(void);
399 : extern const char *ContextTypeToString(NodeTag type);
400 : extern void HandleGetMemoryContextInterrupt(void);
401 : extern Size MemoryContextReportingShmemSize(void);
402 : extern void MemoryContextReportingShmemInit(void);
403 : extern void AtProcExit_memstats_cleanup(int code, Datum arg);
404 : #endif /* MEMUTILS_H */
|