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