Home | History | Annotate | Line # | Download | only in isc
      1 /*	$NetBSD: mem.h,v 1.13 2026/08/29 14:55:19 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_mempool_get(c)   isc__mempool_get((c)_ISC_MEM_FILELINE)
    158 
    159 #define isc_mem_put(c, p, s)                                      \
    160 	do {                                                      \
    161 		isc__mem_put((c), (p), (s), 0 _ISC_MEM_FILELINE); \
    162 		(p) = NULL;                                       \
    163 	} while (0)
    164 #define isc_mem_cput(c, p, n, s)                                  \
    165 	do {                                                      \
    166 		isc__mem_put((c), (p), ISC_CHECKED_MUL((n), (s)), \
    167 			     ISC__MEM_ZERO _ISC_MEM_FILELINE);    \
    168 		(p) = NULL;                                       \
    169 	} while (0)
    170 #define isc_mem_putanddetach(c, p, s)                                      \
    171 	do {                                                               \
    172 		isc__mem_putanddetach((c), (p), (s), 0 _ISC_MEM_FILELINE); \
    173 		(p) = NULL;                                                \
    174 	} while (0)
    175 #define isc_mem_free(c, p)                                    \
    176 	do {                                                  \
    177 		isc__mem_free((c), (p), 0 _ISC_MEM_FILELINE); \
    178 		(p) = NULL;                                   \
    179 	} while (0)
    180 #define isc_mempool_put(c, p)                                \
    181 	do {                                                 \
    182 		isc__mempool_put((c), (p)_ISC_MEM_FILELINE); \
    183 		(p) = NULL;                                  \
    184 	} while (0)
    185 
    186 /*@{*/
    187 /*
    188  * This is a little hack to help with dynamic link order,
    189  * see https://github.com/jemalloc/jemalloc/issues/2566
    190  * for more information.
    191  */
    192 #if HAVE_JEMALLOC
    193 
    194 /*
    195  * cmocka.h has confliction definitions with the jemalloc header but we only
    196  * need the mallocx symbol from jemalloc.
    197  */
    198 void *
    199 mallocx(size_t size, int flags);
    200 
    201 extern volatile void *isc__mem_malloc;
    202 
    203 #define isc_mem_create(cp)                                            \
    204 	{                                                             \
    205 		isc__mem_create((cp)_ISC_MEM_FILELINE);               \
    206 		isc__mem_malloc = mallocx;                            \
    207 		ISC_INSIST(CMM_ACCESS_ONCE(isc__mem_malloc) != NULL); \
    208 	}
    209 #else
    210 #define isc_mem_create(cp) isc__mem_create((cp)_ISC_MEM_FILELINE)
    211 #endif
    212 void
    213 isc__mem_create(isc_mem_t **_ISC_MEM_FLARG);
    214 
    215 /*!<
    216  * \brief Create a memory context.
    217  *
    218  * Requires:
    219  * mctxp != NULL && *mctxp == NULL */
    220 /*@}*/
    221 
    222 #define isc_mem_create_arena(cp) isc__mem_create_arena((cp)_ISC_MEM_FILELINE)
    223 void
    224 isc__mem_create_arena(isc_mem_t **_ISC_MEM_FLARG);
    225 /*!<
    226  * \brief Create a memory context that routs all its operations to a
    227  * dedicated jemalloc arena (when available). When jemalloc is not
    228  * available, the function is, effectively, an alias to
    229  * isc_mem_create().
    230  *
    231  * Requires:
    232  * mctxp != NULL && *mctxp == NULL */
    233 /*@}*/
    234 
    235 isc_result_t
    236 isc_mem_arena_set_muzzy_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms);
    237 
    238 isc_result_t
    239 isc_mem_arena_set_dirty_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms);
    240 /*!<
    241  * \brief These two functions set the given parameters on the
    242  * jemalloc arena associated with the memory context (if there is
    243  * one). When jemalloc is not available, these are no-op.
    244  *
    245  * NOTE: The "muzzy_decay_ms" and "dirty_decay_ms" are the most common
    246  * parameters to adjust when the defaults do not work well (per the
    247  * official jemalloc tuning guide:
    248  * https://github.com/jemalloc/jemalloc/blob/dev/TUNING.md).
    249  *
    250  * Requires:
    251  * mctx - a valid memory context.
    252  */
    253 /*@}*/
    254 
    255 void
    256 isc_mem_attach(isc_mem_t *, isc_mem_t **);
    257 
    258 /*@{*/
    259 void
    260 isc_mem_attach(isc_mem_t *, isc_mem_t **);
    261 #define isc_mem_detach(cp) isc__mem_detach((cp)_ISC_MEM_FILELINE)
    262 void
    263 isc__mem_detach(isc_mem_t **_ISC_MEM_FLARG);
    264 /*!<
    265  * \brief Attach to / detach from a memory context.
    266  *
    267  * This is intended for applications that use multiple memory contexts
    268  * in such a way that it is not obvious when the last allocations from
    269  * a given context has been freed and destroying the context is safe.
    270  *
    271  * Most applications do not need to call these functions as they can
    272  * simply create a single memory context at the beginning of main()
    273  * and destroy it at the end of main(), thereby guaranteeing that it
    274  * is not destroyed while there are outstanding allocations.
    275  */
    276 /*@}*/
    277 
    278 #define isc_mem_destroy(cp) isc__mem_destroy((cp)_ISC_MEM_FILELINE)
    279 void
    280 isc__mem_destroy(isc_mem_t **_ISC_MEM_FLARG);
    281 /*%<
    282  * Destroy a memory context.
    283  */
    284 
    285 void
    286 isc_mem_stats(isc_mem_t *mctx, FILE *out);
    287 /*%<
    288  * Print memory usage statistics for 'mctx' on the stream 'out'.
    289  */
    290 
    291 void
    292 isc_mem_setdestroycheck(isc_mem_t *mctx, bool on);
    293 /*%<
    294  * If 'on' is true, 'mctx' will check for memory leaks when
    295  * destroyed and abort the program if any are present.
    296  */
    297 
    298 size_t
    299 isc_mem_inuse(isc_mem_t *mctx);
    300 /*%<
    301  * Get an estimate of the amount of memory in use in 'mctx', in bytes.
    302  * This includes quantization overhead, but does not include memory
    303  * allocated from the system but not yet used.
    304  */
    305 
    306 bool
    307 isc_mem_isovermem(isc_mem_t *mctx);
    308 /*%<
    309  * Return true iff the memory context is in "over memory" state, i.e.,
    310  * a hiwater mark has been set and the used amount of memory has exceeds
    311  * the mark.
    312  */
    313 
    314 void
    315 isc_mem_clearwater(isc_mem_t *mctx);
    316 void
    317 isc_mem_setwater(isc_mem_t *mctx, size_t hiwater, size_t lowater);
    318 /*%<
    319  * Set high and low water marks for this memory context.
    320  *
    321  * When the memory usage of 'mctx' exceeds 'hiwater', the overmem condition
    322  * will be met and isc_mem_isovermem() will return true.
    323  *
    324  * If the 'hiwater' and 'lowater' is set to 0, the high- and low-water
    325  * processing are disabled for this memory context.
    326  *
    327  * There's a convenient function isc_mem_clearwater().
    328  *
    329  * Requires:
    330  *\li	'hiwater' >= 'lowater'
    331  */
    332 
    333 void
    334 isc_mem_checkdestroyed(FILE *file);
    335 /*%<
    336  * Check that all memory contexts have been destroyed.
    337  * Prints out those that have not been.
    338  * Fatally fails if there are still active contexts.
    339  */
    340 
    341 unsigned int
    342 isc_mem_references(isc_mem_t *ctx);
    343 /*%<
    344  * Return the current reference count.
    345  */
    346 
    347 void
    348 isc_mem_setname(isc_mem_t *ctx, const char *name);
    349 /*%<
    350  * Name 'ctx'.
    351  *
    352  * Notes:
    353  *
    354  *\li	Only the first 15 characters of 'name' will be copied.
    355  *
    356  * Requires:
    357  *
    358  *\li	'ctx' is a valid ctx.
    359  */
    360 
    361 const char *
    362 isc_mem_getname(isc_mem_t *ctx);
    363 /*%<
    364  * Get the name of 'ctx', as previously set using isc_mem_setname().
    365  *
    366  * Requires:
    367  *\li	'ctx' is a valid ctx.
    368  *
    369  * Returns:
    370  *\li	A non-NULL pointer to a null-terminated string.
    371  * 	If the ctx has not been named, the string is
    372  * 	empty.
    373  */
    374 
    375 #ifdef HAVE_LIBXML2
    376 int
    377 isc_mem_renderxml(void *writer0);
    378 /*%<
    379  * Render all contexts' statistics and status in XML for writer.
    380  */
    381 #endif /* HAVE_LIBXML2 */
    382 
    383 #ifdef HAVE_JSON_C
    384 isc_result_t
    385 isc_mem_renderjson(void *memobj0);
    386 /*%<
    387  * Render all contexts' statistics and status in JSON.
    388  */
    389 #endif /* HAVE_JSON_C */
    390 
    391 /*
    392  * Memory pools
    393  */
    394 
    395 #define isc_mempool_create(c, s, mp) \
    396 	isc__mempool_create((c), (s), (mp)_ISC_MEM_FILELINE)
    397 void
    398 isc__mempool_create(isc_mem_t *restrict mctx, const size_t element_size,
    399 		    isc_mempool_t **mpctxp _ISC_MEM_FLARG);
    400 /*%<
    401  * Create a memory pool.
    402  *
    403  * Requires:
    404  *\li	mctx is a valid memory context.
    405  *\li	size > 0
    406  *\li	mpctxp != NULL and *mpctxp == NULL
    407  *
    408  * Defaults:
    409  *\li	freemax = 1
    410  *\li	fillcount = 1
    411  *
    412  * Returns:
    413  *\li	#ISC_R_NOMEMORY		-- not enough memory to create pool
    414  *\li	#ISC_R_SUCCESS		-- all is well.
    415  */
    416 
    417 #define isc_mempool_destroy(mp) isc__mempool_destroy((mp)_ISC_MEM_FILELINE)
    418 void
    419 isc__mempool_destroy(isc_mempool_t **restrict mpctxp _ISC_MEM_FLARG);
    420 /*%<
    421  * Destroy a memory pool.
    422  *
    423  * Requires:
    424  *\li	mpctxp != NULL && *mpctxp is a valid pool.
    425  *\li	The pool has no un"put" allocations outstanding
    426  */
    427 
    428 void
    429 isc_mempool_setname(isc_mempool_t *restrict mpctx, const char *name);
    430 /*%<
    431  * Associate a name with a memory pool.  At most 15 characters may be
    432  *used.
    433  *
    434  * Requires:
    435  *\li	mpctx is a valid pool.
    436  *\li	name != NULL;
    437  */
    438 
    439 /*
    440  * The following functions get/set various parameters.  Note that due to
    441  * the unlocked nature of pools these are potentially random values
    442  *unless the imposed externally provided locking protocols are followed.
    443  *
    444  * Also note that the quota limits will not always take immediate
    445  * effect.
    446  *
    447  * All functions require (in addition to other requirements):
    448  *	mpctx is a valid memory pool
    449  */
    450 
    451 unsigned int
    452 isc_mempool_getfreemax(isc_mempool_t *restrict mpctx);
    453 /*%<
    454  * Returns the maximum allowed size of the free list.
    455  */
    456 
    457 void
    458 isc_mempool_setfreemax(isc_mempool_t *restrict mpctx, const unsigned int limit);
    459 /*%<
    460  * Sets the maximum allowed size of the free list.
    461  */
    462 
    463 unsigned int
    464 isc_mempool_getfreecount(isc_mempool_t *restrict mpctx);
    465 /*%<
    466  * Returns current size of the free list.
    467  */
    468 
    469 unsigned int
    470 isc_mempool_getallocated(isc_mempool_t *restrict mpctx);
    471 /*%<
    472  * Returns the number of items allocated from this pool.
    473  */
    474 
    475 unsigned int
    476 isc_mempool_getfillcount(isc_mempool_t *restrict mpctx);
    477 /*%<
    478  * Returns the number of items allocated as a block from the parent
    479  * memory context when the free list is empty.
    480  */
    481 
    482 void
    483 isc_mempool_setfillcount(isc_mempool_t *restrict mpctx,
    484 			 const unsigned int limit);
    485 /*%<
    486  * Sets the fillcount.
    487  *
    488  * Additional requirements:
    489  *\li	limit > 0
    490  */
    491 
    492 #if defined(UNIT_TESTING) && defined(malloc)
    493 /*
    494  * cmocka.h redefined malloc as a macro, we #undef it
    495  * to avoid replacing ISC_ATTR_MALLOC with garbage.
    496  */
    497 #pragma push_macro("malloc")
    498 #undef malloc
    499 #define POP_MALLOC_MACRO 1
    500 #endif
    501 
    502 /*
    503  * Pseudo-private functions for use via macros.  Do not call directly.
    504  */
    505 void
    506 isc__mem_putanddetach(isc_mem_t **, void *, size_t, int _ISC_MEM_FLARG);
    507 void
    508 isc__mem_put(isc_mem_t *, void *, size_t, int _ISC_MEM_FLARG);
    509 void
    510 isc__mem_free(isc_mem_t *, void *, int _ISC_MEM_FLARG);
    511 
    512 ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mem_put, 2)
    513 void *
    514 isc__mem_get(isc_mem_t *, size_t, int _ISC_MEM_FLARG);
    515 
    516 ISC_ATTR_DEALLOCATOR_IDX(isc__mem_put, 2)
    517 void *
    518 isc__mem_reget(isc_mem_t *, void *, size_t, size_t, int _ISC_MEM_FLARG);
    519 
    520 ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mem_free, 2)
    521 void *
    522 isc__mem_allocate(isc_mem_t *, size_t, int _ISC_MEM_FLARG);
    523 
    524 ISC_ATTR_DEALLOCATOR_IDX(isc__mem_free, 2)
    525 void *
    526 isc__mem_reallocate(isc_mem_t *, void *, size_t, int _ISC_MEM_FLARG);
    527 
    528 ISC_ATTR_RETURNS_NONNULL
    529 ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mem_free, 2)
    530 char *
    531 isc__mem_strdup(isc_mem_t *, const char *_ISC_MEM_FLARG);
    532 
    533 ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mempool_put, 2)
    534 void *
    535 isc__mempool_get(isc_mempool_t *_ISC_MEM_FLARG);
    536 
    537 void
    538 isc__mempool_put(isc_mempool_t *, void *_ISC_MEM_FLARG);
    539 
    540 #ifdef POP_MALLOC_MACRO
    541 /*
    542  * Restore cmocka.h macro for malloc.
    543  */
    544 #pragma pop_macro("malloc")
    545 #endif
    546 
    547 ISC_LANG_ENDDECLS
    548