xref: /netbsd-src/external/mpl/bind/dist/lib/isc/include/isc/mem.h (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: mem.h,v 1.12 2025/01/26 16:25:41 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 #pragma once
17 
18 /*! \file isc/mem.h */
19 
20 #include <stdbool.h>
21 #include <stdio.h>
22 
23 #include <isc/attributes.h>
24 #include <isc/lang.h>
25 #include <isc/mutex.h>
26 #include <isc/overflow.h>
27 #include <isc/types.h>
28 #include <isc/urcu.h>
29 
30 ISC_LANG_BEGINDECLS
31 
32 /*%
33  * Define ISC_MEM_TRACKLINES=1 to turn on detailed tracing of memory
34  * allocation and freeing by file and line number.
35  */
36 #ifndef ISC_MEM_TRACKLINES
37 #define ISC_MEM_TRACKLINES 0
38 #endif /* ifndef ISC_MEM_TRACKLINES */
39 
40 extern unsigned int isc_mem_debugging;
41 extern unsigned int isc_mem_defaultflags;
42 
43 /*@{*/
44 #define ISC_MEM_DEBUGTRACE  0x00000001U
45 #define ISC_MEM_DEBUGRECORD 0x00000002U
46 #define ISC_MEM_DEBUGUSAGE  0x00000004U
47 #define ISC_MEM_DEBUGALL \
48 	(ISC_MEM_DEBUGTRACE | ISC_MEM_DEBUGRECORD | ISC_MEM_DEBUGUSAGE)
49 /*!<
50  * The variable isc_mem_debugging holds a set of flags for
51  * turning certain memory debugging options on or off at
52  * runtime.  It is initialized to the value ISC_MEM_DEGBUGGING,
53  * which is 0 by default but may be overridden at compile time.
54  * The following flags can be specified:
55  *
56  * \li #ISC_MEM_DEBUGTRACE
57  *	Log each allocation and free to isc_lctx.
58  *
59  * \li #ISC_MEM_DEBUGRECORD
60  *	Remember each allocation, and match them up on free.
61  *	Crash if a free doesn't match an allocation.
62  *
63  * \li #ISC_MEM_DEBUGUSAGE
64  *	Every time the memory usage is greater (lower) than hi_water
65  *	(lo_water) mark, print the current inuse memory.
66  */
67 /*@}*/
68 
69 #if ISC_MEM_TRACKLINES
70 #define _ISC_MEM_FILELINE , __FILE__, __LINE__
71 #define _ISC_MEM_FLARG	  , const char *, unsigned int
72 #else /* if ISC_MEM_TRACKLINES */
73 #define _ISC_MEM_FILELINE
74 #define _ISC_MEM_FLARG
75 #endif /* if ISC_MEM_TRACKLINES */
76 
77 /*
78  * Flags for isc_mem_create() calls.
79  */
80 #define ISC_MEMFLAG_RESERVED1 0x00000001 /* reserved, obsoleted, don't use */
81 #define ISC_MEMFLAG_RESERVED2 0x00000002 /* reserved, obsoleted, don't use */
82 #define ISC_MEMFLAG_FILL \
83 	0x00000004 /* fill with pattern after alloc and frees */
84 
85 /*%
86  * Define ISC_MEM_DEFAULTFILL=1 to turn filling the memory with pattern
87  * after alloc and free.
88  */
89 #if ISC_MEM_DEFAULTFILL
90 #define ISC_MEMFLAG_DEFAULT ISC_MEMFLAG_FILL
91 #else /* if !ISC_MEM_USE_INTERNAL_MALLOC */
92 #define ISC_MEMFLAG_DEFAULT 0
93 #endif /* if !ISC_MEM_USE_INTERNAL_MALLOC */
94 
95 /*%
96  * isc_mem_putanddetach() is a convenience function for use where you
97  * have a structure with an attached memory context.
98  *
99  * Given:
100  *
101  * \code
102  * struct {
103  *	...
104  *	isc_mem_t *mctx;
105  *	...
106  * } *ptr;
107  *
108  * isc_mem_t *mctx;
109  *
110  * isc_mem_putanddetach(&ptr->mctx, ptr, sizeof(*ptr));
111  * \endcode
112  *
113  * is the equivalent of:
114  *
115  * \code
116  * mctx = NULL;
117  * isc_mem_attach(ptr->mctx, &mctx);
118  * isc_mem_detach(&ptr->mctx);
119  * isc_mem_put(mctx, ptr, sizeof(*ptr));
120  * isc_mem_detach(&mctx);
121  * \endcode
122  */
123 
124 /*%
125  * These functions are actually implemented in isc__mem_<function>
126  * (two underscores). The single-underscore macros are used to pass
127  * __FILE__ and __LINE__, and in the case of the put functions, to
128  * set the pointer being freed to NULL in the calling function.
129  */
130 
131 /*%
132  * The definitions of the macros have been pulled directly from jemalloc.h
133  * and checked for consistency in mem.c.
134  *
135  *\li	ISC__MEM_ZERO - fill the memory with zeroes before returning
136  */
137 
138 #define ISC__MEM_ZERO ((int)0x40)
139 
140 #define isc_mem_get(c, s) isc__mem_get((c), (s), 0 _ISC_MEM_FILELINE)
141 #define isc_mem_cget(c, n, s)                        \
142 	isc__mem_get((c), ISC_CHECKED_MUL((n), (s)), \
143 		     ISC__MEM_ZERO _ISC_MEM_FILELINE)
144 #define isc_mem_reget(c, p, o, n) \
145 	isc__mem_reget((c), (p), (o), (n), 0 _ISC_MEM_FILELINE)
146 #define isc_mem_creget(c, p, o, n, s)                       \
147 	isc__mem_reget((c), (p), ISC_CHECKED_MUL((o), (s)), \
148 		       ISC_CHECKED_MUL((n), (s)),           \
149 		       ISC__MEM_ZERO _ISC_MEM_FILELINE)
150 #define isc_mem_allocate(c, s) isc__mem_allocate((c), (s), 0 _ISC_MEM_FILELINE)
151 #define isc_mem_callocate(c, n, s)                        \
152 	isc__mem_allocate((c), ISC_CHECKED_MUL((n), (s)), \
153 			  ISC__MEM_ZERO _ISC_MEM_FILELINE)
154 #define isc_mem_reallocate(c, p, s) \
155 	isc__mem_reallocate((c), (p), (s), 0 _ISC_MEM_FILELINE)
156 #define isc_mem_strdup(c, p) isc__mem_strdup((c), (p)_ISC_MEM_FILELINE)
157 #define isc_mem_strndup(c, p, l) \
158 	isc__mem_strndup((c), (p), (l)_ISC_MEM_FILELINE)
159 #define isc_mempool_get(c) isc__mempool_get((c)_ISC_MEM_FILELINE)
160 
161 #define isc_mem_put(c, p, s)                                      \
162 	do {                                                      \
163 		isc__mem_put((c), (p), (s), 0 _ISC_MEM_FILELINE); \
164 		(p) = NULL;                                       \
165 	} while (0)
166 #define isc_mem_cput(c, p, n, s)                                  \
167 	do {                                                      \
168 		isc__mem_put((c), (p), ISC_CHECKED_MUL((n), (s)), \
169 			     ISC__MEM_ZERO _ISC_MEM_FILELINE);    \
170 		(p) = NULL;                                       \
171 	} while (0)
172 #define isc_mem_putanddetach(c, p, s)                                      \
173 	do {                                                               \
174 		isc__mem_putanddetach((c), (p), (s), 0 _ISC_MEM_FILELINE); \
175 		(p) = NULL;                                                \
176 	} while (0)
177 #define isc_mem_free(c, p)                                    \
178 	do {                                                  \
179 		isc__mem_free((c), (p), 0 _ISC_MEM_FILELINE); \
180 		(p) = NULL;                                   \
181 	} while (0)
182 #define isc_mempool_put(c, p)                                \
183 	do {                                                 \
184 		isc__mempool_put((c), (p)_ISC_MEM_FILELINE); \
185 		(p) = NULL;                                  \
186 	} while (0)
187 
188 /*@{*/
189 /*
190  * This is a little hack to help with dynamic link order,
191  * see https://github.com/jemalloc/jemalloc/issues/2566
192  * for more information.
193  */
194 #if HAVE_JEMALLOC
195 
196 /*
197  * cmocka.h has confliction definitions with the jemalloc header but we only
198  * need the mallocx symbol from jemalloc.
199  */
200 void *
201 mallocx(size_t size, int flags);
202 
203 extern volatile void *isc__mem_malloc;
204 
205 #define isc_mem_create(cp)                                            \
206 	{                                                             \
207 		isc__mem_create((cp)_ISC_MEM_FILELINE);               \
208 		isc__mem_malloc = mallocx;                            \
209 		ISC_INSIST(CMM_ACCESS_ONCE(isc__mem_malloc) != NULL); \
210 	}
211 #else
212 #define isc_mem_create(cp) isc__mem_create((cp)_ISC_MEM_FILELINE)
213 #endif
214 void
215 isc__mem_create(isc_mem_t **_ISC_MEM_FLARG);
216 
217 /*!<
218  * \brief Create a memory context.
219  *
220  * Requires:
221  * mctxp != NULL && *mctxp == NULL */
222 /*@}*/
223 
224 #define isc_mem_create_arena(cp) isc__mem_create_arena((cp)_ISC_MEM_FILELINE)
225 void
226 isc__mem_create_arena(isc_mem_t **_ISC_MEM_FLARG);
227 /*!<
228  * \brief Create a memory context that routs all its operations to a
229  * dedicated jemalloc arena (when available). When jemalloc is not
230  * available, the function is, effectively, an alias to
231  * isc_mem_create().
232  *
233  * Requires:
234  * mctxp != NULL && *mctxp == NULL */
235 /*@}*/
236 
237 isc_result_t
238 isc_mem_arena_set_muzzy_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms);
239 
240 isc_result_t
241 isc_mem_arena_set_dirty_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms);
242 /*!<
243  * \brief These two functions set the given parameters on the
244  * jemalloc arena associated with the memory context (if there is
245  * one). When jemalloc is not available, these are no-op.
246  *
247  * NOTE: The "muzzy_decay_ms" and "dirty_decay_ms" are the most common
248  * parameters to adjust when the defaults do not work well (per the
249  * official jemalloc tuning guide:
250  * https://github.com/jemalloc/jemalloc/blob/dev/TUNING.md).
251  *
252  * Requires:
253  * mctx - a valid memory context.
254  */
255 /*@}*/
256 
257 void
258 isc_mem_attach(isc_mem_t *, isc_mem_t **);
259 
260 /*@{*/
261 void
262 isc_mem_attach(isc_mem_t *, isc_mem_t **);
263 #define isc_mem_detach(cp) isc__mem_detach((cp)_ISC_MEM_FILELINE)
264 void
265 isc__mem_detach(isc_mem_t **_ISC_MEM_FLARG);
266 /*!<
267  * \brief Attach to / detach from a memory context.
268  *
269  * This is intended for applications that use multiple memory contexts
270  * in such a way that it is not obvious when the last allocations from
271  * a given context has been freed and destroying the context is safe.
272  *
273  * Most applications do not need to call these functions as they can
274  * simply create a single memory context at the beginning of main()
275  * and destroy it at the end of main(), thereby guaranteeing that it
276  * is not destroyed while there are outstanding allocations.
277  */
278 /*@}*/
279 
280 #define isc_mem_destroy(cp) isc__mem_destroy((cp)_ISC_MEM_FILELINE)
281 void
282 isc__mem_destroy(isc_mem_t **_ISC_MEM_FLARG);
283 /*%<
284  * Destroy a memory context.
285  */
286 
287 void
288 isc_mem_stats(isc_mem_t *mctx, FILE *out);
289 /*%<
290  * Print memory usage statistics for 'mctx' on the stream 'out'.
291  */
292 
293 void
294 isc_mem_setdestroycheck(isc_mem_t *mctx, bool on);
295 /*%<
296  * If 'on' is true, 'mctx' will check for memory leaks when
297  * destroyed and abort the program if any are present.
298  */
299 
300 size_t
301 isc_mem_inuse(isc_mem_t *mctx);
302 /*%<
303  * Get an estimate of the amount of memory in use in 'mctx', in bytes.
304  * This includes quantization overhead, but does not include memory
305  * allocated from the system but not yet used.
306  */
307 
308 bool
309 isc_mem_isovermem(isc_mem_t *mctx);
310 /*%<
311  * Return true iff the memory context is in "over memory" state, i.e.,
312  * a hiwater mark has been set and the used amount of memory has exceeds
313  * the mark.
314  */
315 
316 void
317 isc_mem_clearwater(isc_mem_t *mctx);
318 void
319 isc_mem_setwater(isc_mem_t *mctx, size_t hiwater, size_t lowater);
320 /*%<
321  * Set high and low water marks for this memory context.
322  *
323  * When the memory usage of 'mctx' exceeds 'hiwater', the overmem condition
324  * will be met and isc_mem_isovermem() will return true.
325  *
326  * If the 'hiwater' and 'lowater' is set to 0, the high- and low-water
327  * processing are disabled for this memory context.
328  *
329  * There's a convenient function isc_mem_clearwater().
330  *
331  * Requires:
332  *\li	'hiwater' >= 'lowater'
333  */
334 
335 void
336 isc_mem_checkdestroyed(FILE *file);
337 /*%<
338  * Check that all memory contexts have been destroyed.
339  * Prints out those that have not been.
340  * Fatally fails if there are still active contexts.
341  */
342 
343 unsigned int
344 isc_mem_references(isc_mem_t *ctx);
345 /*%<
346  * Return the current reference count.
347  */
348 
349 void
350 isc_mem_setname(isc_mem_t *ctx, const char *name);
351 /*%<
352  * Name 'ctx'.
353  *
354  * Notes:
355  *
356  *\li	Only the first 15 characters of 'name' will be copied.
357  *
358  * Requires:
359  *
360  *\li	'ctx' is a valid ctx.
361  */
362 
363 const char *
364 isc_mem_getname(isc_mem_t *ctx);
365 /*%<
366  * Get the name of 'ctx', as previously set using isc_mem_setname().
367  *
368  * Requires:
369  *\li	'ctx' is a valid ctx.
370  *
371  * Returns:
372  *\li	A non-NULL pointer to a null-terminated string.
373  * 	If the ctx has not been named, the string is
374  * 	empty.
375  */
376 
377 #ifdef HAVE_LIBXML2
378 int
379 isc_mem_renderxml(void *writer0);
380 /*%<
381  * Render all contexts' statistics and status in XML for writer.
382  */
383 #endif /* HAVE_LIBXML2 */
384 
385 #ifdef HAVE_JSON_C
386 isc_result_t
387 isc_mem_renderjson(void *memobj0);
388 /*%<
389  * Render all contexts' statistics and status in JSON.
390  */
391 #endif /* HAVE_JSON_C */
392 
393 /*
394  * Memory pools
395  */
396 
397 #define isc_mempool_create(c, s, mp) \
398 	isc__mempool_create((c), (s), (mp)_ISC_MEM_FILELINE)
399 void
400 isc__mempool_create(isc_mem_t *restrict mctx, const size_t element_size,
401 		    isc_mempool_t **mpctxp _ISC_MEM_FLARG);
402 /*%<
403  * Create a memory pool.
404  *
405  * Requires:
406  *\li	mctx is a valid memory context.
407  *\li	size > 0
408  *\li	mpctxp != NULL and *mpctxp == NULL
409  *
410  * Defaults:
411  *\li	freemax = 1
412  *\li	fillcount = 1
413  *
414  * Returns:
415  *\li	#ISC_R_NOMEMORY		-- not enough memory to create pool
416  *\li	#ISC_R_SUCCESS		-- all is well.
417  */
418 
419 #define isc_mempool_destroy(mp) isc__mempool_destroy((mp)_ISC_MEM_FILELINE)
420 void
421 isc__mempool_destroy(isc_mempool_t **restrict mpctxp _ISC_MEM_FLARG);
422 /*%<
423  * Destroy a memory pool.
424  *
425  * Requires:
426  *\li	mpctxp != NULL && *mpctxp is a valid pool.
427  *\li	The pool has no un"put" allocations outstanding
428  */
429 
430 void
431 isc_mempool_setname(isc_mempool_t *restrict mpctx, const char *name);
432 /*%<
433  * Associate a name with a memory pool.  At most 15 characters may be
434  *used.
435  *
436  * Requires:
437  *\li	mpctx is a valid pool.
438  *\li	name != NULL;
439  */
440 
441 /*
442  * The following functions get/set various parameters.  Note that due to
443  * the unlocked nature of pools these are potentially random values
444  *unless the imposed externally provided locking protocols are followed.
445  *
446  * Also note that the quota limits will not always take immediate
447  * effect.
448  *
449  * All functions require (in addition to other requirements):
450  *	mpctx is a valid memory pool
451  */
452 
453 unsigned int
454 isc_mempool_getfreemax(isc_mempool_t *restrict mpctx);
455 /*%<
456  * Returns the maximum allowed size of the free list.
457  */
458 
459 void
460 isc_mempool_setfreemax(isc_mempool_t *restrict mpctx, const unsigned int limit);
461 /*%<
462  * Sets the maximum allowed size of the free list.
463  */
464 
465 unsigned int
466 isc_mempool_getfreecount(isc_mempool_t *restrict mpctx);
467 /*%<
468  * Returns current size of the free list.
469  */
470 
471 unsigned int
472 isc_mempool_getallocated(isc_mempool_t *restrict mpctx);
473 /*%<
474  * Returns the number of items allocated from this pool.
475  */
476 
477 unsigned int
478 isc_mempool_getfillcount(isc_mempool_t *restrict mpctx);
479 /*%<
480  * Returns the number of items allocated as a block from the parent
481  * memory context when the free list is empty.
482  */
483 
484 void
485 isc_mempool_setfillcount(isc_mempool_t *restrict mpctx,
486 			 const unsigned int limit);
487 /*%<
488  * Sets the fillcount.
489  *
490  * Additional requirements:
491  *\li	limit > 0
492  */
493 
494 #if defined(UNIT_TESTING) && defined(malloc)
495 /*
496  * cmocka.h redefined malloc as a macro, we #undef it
497  * to avoid replacing ISC_ATTR_MALLOC with garbage.
498  */
499 #pragma push_macro("malloc")
500 #undef malloc
501 #define POP_MALLOC_MACRO 1
502 #endif
503 
504 /*
505  * Pseudo-private functions for use via macros.  Do not call directly.
506  */
507 void
508 isc__mem_putanddetach(isc_mem_t **, void *, size_t, int _ISC_MEM_FLARG);
509 void
510 isc__mem_put(isc_mem_t *, void *, size_t, int _ISC_MEM_FLARG);
511 void
512 isc__mem_free(isc_mem_t *, void *, int _ISC_MEM_FLARG);
513 
514 ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mem_put, 2)
515 void *
516 isc__mem_get(isc_mem_t *, size_t, int _ISC_MEM_FLARG);
517 
518 ISC_ATTR_DEALLOCATOR_IDX(isc__mem_put, 2)
519 void *
520 isc__mem_reget(isc_mem_t *, void *, size_t, size_t, int _ISC_MEM_FLARG);
521 
522 ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mem_free, 2)
523 void *
524 isc__mem_allocate(isc_mem_t *, size_t, int _ISC_MEM_FLARG);
525 
526 ISC_ATTR_DEALLOCATOR_IDX(isc__mem_free, 2)
527 void *
528 isc__mem_reallocate(isc_mem_t *, void *, size_t, int _ISC_MEM_FLARG);
529 
530 ISC_ATTR_RETURNS_NONNULL
531 ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mem_free, 2)
532 char *
533 isc__mem_strdup(isc_mem_t *, const char *_ISC_MEM_FLARG);
534 
535 ISC_ATTR_RETURNS_NONNULL
536 ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mem_free, 2)
537 char *
538 isc__mem_strndup(isc_mem_t *, const char *, size_t _ISC_MEM_FLARG);
539 
540 ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mempool_put, 2)
541 void *
542 isc__mempool_get(isc_mempool_t *_ISC_MEM_FLARG);
543 
544 void
545 isc__mempool_put(isc_mempool_t *, void *_ISC_MEM_FLARG);
546 
547 #ifdef POP_MALLOC_MACRO
548 /*
549  * Restore cmocka.h macro for malloc.
550  */
551 #pragma pop_macro("malloc")
552 #endif
553 
554 ISC_LANG_ENDDECLS
555