Home | History | Annotate | Line # | Download | only in isc
      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