Home | History | Annotate | Line # | Download | only in isc
mem.c revision 1.15.2.1
      1 /*	$NetBSD: mem.c,v 1.15.2.1 2025/08/02 05:53:53 perseant 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 /*! \file */
     17 
     18 #include <inttypes.h>
     19 #include <limits.h>
     20 #include <stdbool.h>
     21 #include <stddef.h>
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 
     25 #include <isc/hash.h>
     26 #include <isc/magic.h>
     27 #include <isc/mem.h>
     28 #include <isc/mutex.h>
     29 #include <isc/once.h>
     30 #include <isc/os.h>
     31 #include <isc/overflow.h>
     32 #include <isc/refcount.h>
     33 #include <isc/strerr.h>
     34 #include <isc/string.h>
     35 #include <isc/types.h>
     36 #include <isc/urcu.h>
     37 #include <isc/util.h>
     38 
     39 #ifdef HAVE_LIBXML2
     40 #include <libxml/xmlwriter.h>
     41 #define ISC_XMLCHAR (const xmlChar *)
     42 #endif /* HAVE_LIBXML2 */
     43 
     44 #ifdef HAVE_JSON_C
     45 #include <json_object.h>
     46 #endif /* HAVE_JSON_C */
     47 
     48 /* On DragonFly BSD the header does not provide jemalloc API */
     49 #if defined(HAVE_MALLOC_NP_H) && !defined(__DragonFly__)
     50 #include <malloc_np.h>
     51 #define JEMALLOC_API_SUPPORTED 1
     52 #elif defined(HAVE_JEMALLOC)
     53 #include <jemalloc/jemalloc.h>
     54 #define JEMALLOC_API_SUPPORTED 1
     55 #else
     56 #include "jemalloc_shim.h"
     57 #endif
     58 
     59 #include "mem_p.h"
     60 
     61 #define MCTXLOCK(m)   LOCK(&m->lock)
     62 #define MCTXUNLOCK(m) UNLOCK(&m->lock)
     63 
     64 #ifndef ISC_MEM_DEBUGGING
     65 #define ISC_MEM_DEBUGGING 0
     66 #endif /* ifndef ISC_MEM_DEBUGGING */
     67 unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;
     68 unsigned int isc_mem_defaultflags = ISC_MEMFLAG_DEFAULT;
     69 
     70 #define ISC_MEM_ILLEGAL_ARENA (UINT_MAX)
     71 
     72 volatile void *isc__mem_malloc = mallocx;
     73 
     74 /*
     75  * Constants.
     76  */
     77 
     78 #define ZERO_ALLOCATION_SIZE sizeof(void *)
     79 #define ALIGNMENT	     8U /*%< must be a power of 2 */
     80 #define ALIGNMENT_SIZE	     sizeof(size_info)
     81 #define DEBUG_TABLE_COUNT    512U
     82 
     83 /*
     84  * Types.
     85  */
     86 #if ISC_MEM_TRACKLINES
     87 typedef struct debuglink debuglink_t;
     88 struct debuglink {
     89 	size_t dlsize;
     90 	ISC_LINK(debuglink_t) link;
     91 	const void *ptr;
     92 	size_t size;
     93 	unsigned int line;
     94 	const char file[];
     95 };
     96 
     97 typedef ISC_LIST(debuglink_t) debuglist_t;
     98 
     99 #define FLARG_PASS , file, line
    100 #define FLARG	   , const char *file, unsigned int line
    101 #else /* if ISC_MEM_TRACKLINES */
    102 #define FLARG_PASS
    103 #define FLARG
    104 #endif /* if ISC_MEM_TRACKLINES */
    105 
    106 typedef struct element element;
    107 struct element {
    108 	element *next;
    109 };
    110 
    111 #define MEM_MAGIC	 ISC_MAGIC('M', 'e', 'm', 'C')
    112 #define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC)
    113 
    114 /* List of all active memory contexts. */
    115 
    116 static ISC_LIST(isc_mem_t) contexts;
    117 
    118 static isc_once_t init_once = ISC_ONCE_INIT;
    119 static isc_once_t shut_once = ISC_ONCE_INIT;
    120 static isc_mutex_t contextslock;
    121 
    122 struct isc_mem {
    123 	unsigned int magic;
    124 	unsigned int flags;
    125 	unsigned int jemalloc_flags;
    126 	unsigned int jemalloc_arena;
    127 	unsigned int debugging;
    128 	isc_mutex_t lock;
    129 	bool checkfree;
    130 	isc_refcount_t references;
    131 	char name[16];
    132 	atomic_size_t inuse;
    133 	atomic_bool hi_called;
    134 	atomic_bool is_overmem;
    135 	atomic_size_t hi_water;
    136 	atomic_size_t lo_water;
    137 	ISC_LIST(isc_mempool_t) pools;
    138 	unsigned int poolcnt;
    139 
    140 #if ISC_MEM_TRACKLINES
    141 	debuglist_t *debuglist;
    142 	size_t debuglistcnt;
    143 #endif /* if ISC_MEM_TRACKLINES */
    144 
    145 	ISC_LINK(isc_mem_t) link;
    146 };
    147 
    148 #define MEMPOOL_MAGIC	 ISC_MAGIC('M', 'E', 'M', 'p')
    149 #define VALID_MEMPOOL(c) ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
    150 
    151 struct isc_mempool {
    152 	/* always unlocked */
    153 	unsigned int magic;
    154 	isc_mem_t *mctx;	      /*%< our memory context */
    155 	ISC_LINK(isc_mempool_t) link; /*%< next pool in this mem context */
    156 	element *items;		      /*%< low water item list */
    157 	size_t size;		      /*%< size of each item on this pool */
    158 	size_t allocated;	      /*%< # of items currently given out */
    159 	size_t freecount;	      /*%< # of items on reserved list */
    160 	size_t freemax;		      /*%< # of items allowed on free list */
    161 	size_t fillcount;	      /*%< # of items to fetch on each fill */
    162 	/*%< Stats only. */
    163 	size_t gets; /*%< # of requests to this pool */
    164 	/*%< Debugging only. */
    165 	char name[16]; /*%< printed name in stats reports */
    166 };
    167 
    168 /*
    169  * Private Inline-able.
    170  */
    171 
    172 #if !ISC_MEM_TRACKLINES
    173 #define ADD_TRACE(mctx, ptr, size, file, line)
    174 #define DELETE_TRACE(mctx, ptr, size, file, line)
    175 #define ISC_MEMFUNC_SCOPE
    176 #else /* if !ISC_MEM_TRACKLINES */
    177 #define TRACE_OR_RECORD (ISC_MEM_DEBUGTRACE | ISC_MEM_DEBUGRECORD)
    178 
    179 #define SHOULD_TRACE_OR_RECORD(mctx, ptr) \
    180 	(((mctx)->debugging & TRACE_OR_RECORD) != 0 && ptr != NULL)
    181 
    182 #define ADD_TRACE(mctx, ptr, size, file, line)                \
    183 	if (SHOULD_TRACE_OR_RECORD(mctx, ptr)) {              \
    184 		add_trace_entry(mctx, ptr, size, file, line); \
    185 	}
    186 
    187 #define DELETE_TRACE(mctx, ptr, size, file, line)                \
    188 	if (SHOULD_TRACE_OR_RECORD(mctx, ptr)) {                 \
    189 		delete_trace_entry(mctx, ptr, size, file, line); \
    190 	}
    191 
    192 static void
    193 print_active(isc_mem_t *ctx, FILE *out);
    194 #endif /* ISC_MEM_TRACKLINES */
    195 
    196 #if ISC_MEM_TRACKLINES
    197 /*!
    198  * mctx must not be locked.
    199  */
    200 static void
    201 add_trace_entry(isc_mem_t *mctx, const void *ptr, size_t size FLARG) {
    202 	debuglink_t *dl = NULL;
    203 	uint32_t hash;
    204 	uint32_t idx;
    205 
    206 	/*
    207 	 * "file" needs to be copied because it can be part of a dynamically
    208 	 * loaded plugin which would be unloaded at the time the trace is
    209 	 * dumped. Storing "file" pointer then leads to a dangling pointer
    210 	 * dereference and a crash.
    211 	 */
    212 	size_t filelen = strlen(file) + 1;
    213 	size_t dlsize = STRUCT_FLEX_SIZE(dl, file, filelen);
    214 
    215 	MCTXLOCK(mctx);
    216 
    217 	if ((mctx->debugging & ISC_MEM_DEBUGTRACE) != 0) {
    218 		fprintf(stderr, "add %p size %zu file %s line %u mctx %p\n",
    219 			ptr, size, file, line, mctx);
    220 	}
    221 
    222 	if (mctx->debuglist == NULL) {
    223 		goto unlock;
    224 	}
    225 
    226 #ifdef __COVERITY__
    227 	/*
    228 	 * Use simple conversion from pointer to hash to avoid
    229 	 * tainting 'ptr' due to byte swap in isc_hash32.
    230 	 */
    231 	hash = (uintptr_t)ptr >> 3;
    232 #else
    233 	hash = isc_hash32(&ptr, sizeof(ptr), true);
    234 #endif
    235 	idx = hash % DEBUG_TABLE_COUNT;
    236 
    237 	dl = mallocx(dlsize, mctx->jemalloc_flags);
    238 	INSIST(dl != NULL);
    239 
    240 	ISC_LINK_INIT(dl, link);
    241 	dl->ptr = ptr;
    242 	dl->size = size;
    243 	dl->line = line;
    244 	dl->dlsize = dlsize;
    245 	strlcpy((char *)dl->file, file, filelen);
    246 
    247 	ISC_LIST_PREPEND(mctx->debuglist[idx], dl, link);
    248 	mctx->debuglistcnt++;
    249 unlock:
    250 	MCTXUNLOCK(mctx);
    251 }
    252 
    253 static void
    254 delete_trace_entry(isc_mem_t *mctx, const void *ptr, size_t size,
    255 		   const char *file, unsigned int line) {
    256 	debuglink_t *dl = NULL;
    257 	uint32_t hash;
    258 	uint32_t idx;
    259 
    260 	MCTXLOCK(mctx);
    261 
    262 	if ((mctx->debugging & ISC_MEM_DEBUGTRACE) != 0) {
    263 		fprintf(stderr, "del %p size %zu file %s line %u mctx %p\n",
    264 			ptr, size, file, line, mctx);
    265 	}
    266 
    267 	if (mctx->debuglist == NULL) {
    268 		goto unlock;
    269 	}
    270 
    271 #ifdef __COVERITY__
    272 	/*
    273 	 * Use simple conversion from pointer to hash to avoid
    274 	 * tainting 'ptr' due to byte swap in isc_hash32.
    275 	 */
    276 	hash = (uintptr_t)ptr >> 3;
    277 #else
    278 	hash = isc_hash32(&ptr, sizeof(ptr), true);
    279 #endif
    280 	idx = hash % DEBUG_TABLE_COUNT;
    281 
    282 	dl = ISC_LIST_HEAD(mctx->debuglist[idx]);
    283 	while (dl != NULL) {
    284 		if (dl->ptr == ptr) {
    285 			ISC_LIST_UNLINK(mctx->debuglist[idx], dl, link);
    286 			sdallocx(dl, dl->dlsize, mctx->jemalloc_flags);
    287 			goto unlock;
    288 		}
    289 		dl = ISC_LIST_NEXT(dl, link);
    290 	}
    291 
    292 	/*
    293 	 * If we get here, we didn't find the item on the list.  We're
    294 	 * screwed.
    295 	 */
    296 	UNREACHABLE();
    297 unlock:
    298 	MCTXUNLOCK(mctx);
    299 }
    300 #endif /* ISC_MEM_TRACKLINES */
    301 
    302 #define ADJUST_ZERO_ALLOCATION_SIZE(s)    \
    303 	if (s == 0) {                     \
    304 		s = ZERO_ALLOCATION_SIZE; \
    305 	}
    306 
    307 /*!
    308  * Perform a malloc, doing memory filling and overrun detection as necessary.
    309  */
    310 static void *
    311 mem_get(isc_mem_t *ctx, size_t size, int flags) {
    312 	char *ret = NULL;
    313 
    314 	ADJUST_ZERO_ALLOCATION_SIZE(size);
    315 
    316 	ret = mallocx(size, flags | ctx->jemalloc_flags);
    317 	INSIST(ret != NULL);
    318 
    319 	if ((flags & ISC__MEM_ZERO) == 0 &&
    320 	    (ctx->flags & ISC_MEMFLAG_FILL) != 0)
    321 	{
    322 		memset(ret, 0xbe, size); /* Mnemonic for "beef". */
    323 	}
    324 
    325 	return ret;
    326 }
    327 
    328 /*!
    329  * Perform a free, doing memory filling and overrun detection as necessary.
    330  */
    331 /* coverity[+free : arg-1] */
    332 static void
    333 mem_put(isc_mem_t *ctx, void *mem, size_t size, int flags) {
    334 	ADJUST_ZERO_ALLOCATION_SIZE(size);
    335 
    336 	if ((ctx->flags & ISC_MEMFLAG_FILL) != 0) {
    337 		memset(mem, 0xde, size); /* Mnemonic for "dead". */
    338 	}
    339 	sdallocx(mem, size, flags | ctx->jemalloc_flags);
    340 }
    341 
    342 static void *
    343 mem_realloc(isc_mem_t *ctx, void *old_ptr, size_t old_size, size_t new_size,
    344 	    int flags) {
    345 	void *new_ptr = NULL;
    346 
    347 	ADJUST_ZERO_ALLOCATION_SIZE(new_size);
    348 
    349 	new_ptr = rallocx(old_ptr, new_size, flags | ctx->jemalloc_flags);
    350 	INSIST(new_ptr != NULL);
    351 
    352 	if ((flags & ISC__MEM_ZERO) == 0 &&
    353 	    (ctx->flags & ISC_MEMFLAG_FILL) != 0)
    354 	{
    355 		ssize_t diff_size = new_size - old_size;
    356 		void *diff_ptr = (uint8_t *)new_ptr + old_size;
    357 		if (diff_size > 0) {
    358 			/* Mnemonic for "beef". */
    359 			memset(diff_ptr, 0xbe, diff_size);
    360 		}
    361 	}
    362 
    363 	return new_ptr;
    364 }
    365 
    366 /*!
    367  * Update internal counters after a memory get.
    368  */
    369 static void
    370 mem_getstats(isc_mem_t *ctx, size_t size) {
    371 	atomic_fetch_add_relaxed(&ctx->inuse, size);
    372 }
    373 
    374 /*!
    375  * Update internal counters after a memory put.
    376  */
    377 static void
    378 mem_putstats(isc_mem_t *ctx, size_t size) {
    379 	atomic_size_t s = atomic_fetch_sub_relaxed(&ctx->inuse, size);
    380 	INSIST(s >= size);
    381 }
    382 
    383 /*
    384  * Private.
    385  */
    386 
    387 static bool
    388 mem_jemalloc_arena_create(unsigned int *pnew_arenano) {
    389 	REQUIRE(pnew_arenano != NULL);
    390 
    391 #ifdef JEMALLOC_API_SUPPORTED
    392 	unsigned int arenano = 0;
    393 	size_t len = sizeof(arenano);
    394 	int res = 0;
    395 
    396 	res = mallctl("arenas.create", &arenano, &len, NULL, 0);
    397 	if (res != 0) {
    398 		return false;
    399 	}
    400 
    401 	*pnew_arenano = arenano;
    402 
    403 	return true;
    404 #else
    405 	*pnew_arenano = ISC_MEM_ILLEGAL_ARENA;
    406 	return true;
    407 #endif /* JEMALLOC_API_SUPPORTED */
    408 }
    409 
    410 static bool
    411 mem_jemalloc_arena_destroy(unsigned int arenano) {
    412 #ifdef JEMALLOC_API_SUPPORTED
    413 	int res = 0;
    414 	char buf[256] = { 0 };
    415 
    416 	(void)snprintf(buf, sizeof(buf), "arena.%u.destroy", arenano);
    417 	res = mallctl(buf, NULL, NULL, NULL, 0);
    418 	if (res != 0) {
    419 		return false;
    420 	}
    421 
    422 	return true;
    423 #else
    424 	UNUSED(arenano);
    425 	return true;
    426 #endif /* JEMALLOC_API_SUPPORTED */
    427 }
    428 
    429 static void
    430 mem_initialize(void) {
    431 /*
    432  * Check if the values copied from jemalloc still match
    433  */
    434 #ifdef JEMALLOC_API_SUPPORTED
    435 	RUNTIME_CHECK(ISC__MEM_ZERO == MALLOCX_ZERO);
    436 #endif /* JEMALLOC_API_SUPPORTED */
    437 
    438 	isc_mutex_init(&contextslock);
    439 	ISC_LIST_INIT(contexts);
    440 }
    441 
    442 void
    443 isc__mem_initialize(void) {
    444 	isc_once_do(&init_once, mem_initialize);
    445 }
    446 
    447 static void
    448 mem_shutdown(void) {
    449 	bool empty;
    450 
    451 	isc__mem_checkdestroyed();
    452 
    453 	LOCK(&contextslock);
    454 	empty = ISC_LIST_EMPTY(contexts);
    455 	UNLOCK(&contextslock);
    456 
    457 	if (empty) {
    458 		isc_mutex_destroy(&contextslock);
    459 	}
    460 }
    461 
    462 void
    463 isc__mem_shutdown(void) {
    464 	isc_once_do(&shut_once, mem_shutdown);
    465 }
    466 
    467 static void
    468 mem_create(isc_mem_t **ctxp, unsigned int debugging, unsigned int flags,
    469 	   unsigned int jemalloc_flags) {
    470 	isc_mem_t *ctx = NULL;
    471 
    472 	REQUIRE(ctxp != NULL && *ctxp == NULL);
    473 
    474 	ctx = mallocx(sizeof(*ctx), jemalloc_flags);
    475 	INSIST(ctx != NULL);
    476 
    477 	*ctx = (isc_mem_t){
    478 		.magic = MEM_MAGIC,
    479 		.debugging = debugging,
    480 		.flags = flags,
    481 		.jemalloc_flags = jemalloc_flags,
    482 		.jemalloc_arena = ISC_MEM_ILLEGAL_ARENA,
    483 		.checkfree = true,
    484 	};
    485 
    486 	isc_mutex_init(&ctx->lock);
    487 	isc_refcount_init(&ctx->references, 1);
    488 
    489 	atomic_init(&ctx->inuse, 0);
    490 	atomic_init(&ctx->hi_water, 0);
    491 	atomic_init(&ctx->lo_water, 0);
    492 	atomic_init(&ctx->hi_called, false);
    493 	atomic_init(&ctx->is_overmem, false);
    494 
    495 	ISC_LIST_INIT(ctx->pools);
    496 
    497 #if ISC_MEM_TRACKLINES
    498 	if ((ctx->debugging & ISC_MEM_DEBUGRECORD) != 0) {
    499 		unsigned int i;
    500 
    501 		ctx->debuglist = mallocx(
    502 			ISC_CHECKED_MUL(DEBUG_TABLE_COUNT, sizeof(debuglist_t)),
    503 			jemalloc_flags);
    504 		INSIST(ctx->debuglist != NULL);
    505 
    506 		for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
    507 			ISC_LIST_INIT(ctx->debuglist[i]);
    508 		}
    509 	}
    510 #endif /* if ISC_MEM_TRACKLINES */
    511 
    512 	LOCK(&contextslock);
    513 	ISC_LIST_INITANDAPPEND(contexts, ctx, link);
    514 	UNLOCK(&contextslock);
    515 
    516 	*ctxp = ctx;
    517 }
    518 
    519 /*
    520  * Public.
    521  */
    522 
    523 static void
    524 destroy(isc_mem_t *ctx) {
    525 	unsigned int arena_no;
    526 	LOCK(&contextslock);
    527 	ISC_LIST_UNLINK(contexts, ctx, link);
    528 	UNLOCK(&contextslock);
    529 
    530 	ctx->magic = 0;
    531 
    532 	arena_no = ctx->jemalloc_arena;
    533 
    534 	INSIST(ISC_LIST_EMPTY(ctx->pools));
    535 
    536 #if ISC_MEM_TRACKLINES
    537 	if (ctx->debuglist != NULL) {
    538 		debuglink_t *dl;
    539 		for (size_t i = 0; i < DEBUG_TABLE_COUNT; i++) {
    540 			for (dl = ISC_LIST_HEAD(ctx->debuglist[i]); dl != NULL;
    541 			     dl = ISC_LIST_HEAD(ctx->debuglist[i]))
    542 			{
    543 				if (ctx->checkfree && dl->ptr != NULL) {
    544 					print_active(ctx, stderr);
    545 				}
    546 				INSIST(!ctx->checkfree || dl->ptr == NULL);
    547 
    548 				ISC_LIST_UNLINK(ctx->debuglist[i], dl, link);
    549 				sdallocx(dl, sizeof(*dl), ctx->jemalloc_flags);
    550 			}
    551 		}
    552 
    553 		sdallocx(
    554 			ctx->debuglist,
    555 			ISC_CHECKED_MUL(DEBUG_TABLE_COUNT, sizeof(debuglist_t)),
    556 			ctx->jemalloc_flags);
    557 	}
    558 #endif /* if ISC_MEM_TRACKLINES */
    559 
    560 	isc_mutex_destroy(&ctx->lock);
    561 
    562 	if (ctx->checkfree) {
    563 		INSIST(atomic_load(&ctx->inuse) == 0);
    564 	}
    565 	sdallocx(ctx, sizeof(*ctx), ctx->jemalloc_flags);
    566 
    567 	if (arena_no != ISC_MEM_ILLEGAL_ARENA) {
    568 		RUNTIME_CHECK(mem_jemalloc_arena_destroy(arena_no) == true);
    569 	}
    570 }
    571 
    572 void
    573 isc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) {
    574 	REQUIRE(VALID_CONTEXT(source));
    575 	REQUIRE(targetp != NULL && *targetp == NULL);
    576 
    577 	isc_refcount_increment(&source->references);
    578 
    579 	*targetp = source;
    580 }
    581 
    582 void
    583 isc__mem_detach(isc_mem_t **ctxp FLARG) {
    584 	isc_mem_t *ctx = NULL;
    585 
    586 	REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
    587 
    588 	ctx = *ctxp;
    589 	*ctxp = NULL;
    590 
    591 	if (isc_refcount_decrement(&ctx->references) == 1) {
    592 		isc_refcount_destroy(&ctx->references);
    593 #if ISC_MEM_TRACKLINES
    594 		if ((ctx->debugging & ISC_MEM_DEBUGTRACE) != 0) {
    595 			fprintf(stderr, "destroy mctx %p file %s line %u\n",
    596 				ctx, file, line);
    597 		}
    598 #endif
    599 		destroy(ctx);
    600 	}
    601 }
    602 
    603 /*
    604  * isc_mem_putanddetach() is the equivalent of:
    605  *
    606  * mctx = NULL;
    607  * isc_mem_attach(ptr->mctx, &mctx);
    608  * isc_mem_detach(&ptr->mctx);
    609  * isc_mem_put(mctx, ptr, sizeof(*ptr);
    610  * isc_mem_detach(&mctx);
    611  */
    612 
    613 void
    614 isc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size,
    615 		      int flags FLARG) {
    616 	REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
    617 	REQUIRE(ptr != NULL);
    618 	REQUIRE(size != 0);
    619 
    620 	isc_mem_t *ctx = *ctxp;
    621 	*ctxp = NULL;
    622 
    623 	isc__mem_put(ctx, ptr, size, flags FLARG_PASS);
    624 	isc__mem_detach(&ctx FLARG_PASS);
    625 }
    626 
    627 void
    628 isc__mem_destroy(isc_mem_t **ctxp FLARG) {
    629 	isc_mem_t *ctx = NULL;
    630 
    631 	/*
    632 	 * This routine provides legacy support for callers who use mctxs
    633 	 * without attaching/detaching.
    634 	 */
    635 
    636 	REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
    637 
    638 	ctx = *ctxp;
    639 	*ctxp = NULL;
    640 
    641 	rcu_barrier();
    642 
    643 #if ISC_MEM_TRACKLINES
    644 	if ((ctx->debugging & ISC_MEM_DEBUGTRACE) != 0) {
    645 		fprintf(stderr, "destroy mctx %p file %s line %u\n", ctx, file,
    646 			line);
    647 	}
    648 
    649 	if (isc_refcount_decrement(&ctx->references) > 1) {
    650 		print_active(ctx, stderr);
    651 	}
    652 #else  /* if ISC_MEM_TRACKLINES */
    653 	isc_refcount_decrementz(&ctx->references);
    654 #endif /* if ISC_MEM_TRACKLINES */
    655 	isc_refcount_destroy(&ctx->references);
    656 	destroy(ctx);
    657 
    658 	*ctxp = NULL;
    659 }
    660 
    661 void *
    662 isc__mem_get(isc_mem_t *ctx, size_t size, int flags FLARG) {
    663 	void *ptr = NULL;
    664 
    665 	REQUIRE(VALID_CONTEXT(ctx));
    666 
    667 	ptr = mem_get(ctx, size, flags);
    668 
    669 	mem_getstats(ctx, size);
    670 	ADD_TRACE(ctx, ptr, size, file, line);
    671 
    672 	return ptr;
    673 }
    674 
    675 void
    676 isc__mem_put(isc_mem_t *ctx, void *ptr, size_t size, int flags FLARG) {
    677 	REQUIRE(VALID_CONTEXT(ctx));
    678 
    679 	DELETE_TRACE(ctx, ptr, size, file, line);
    680 
    681 	mem_putstats(ctx, size);
    682 	mem_put(ctx, ptr, size, flags);
    683 }
    684 
    685 #if ISC_MEM_TRACKLINES
    686 static void
    687 print_active(isc_mem_t *mctx, FILE *out) {
    688 	if (mctx->debuglist != NULL) {
    689 		debuglink_t *dl;
    690 		unsigned int i;
    691 		bool found;
    692 
    693 		fprintf(out, "Dump of all outstanding memory "
    694 			     "allocations:\n");
    695 		found = false;
    696 		for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
    697 			dl = ISC_LIST_HEAD(mctx->debuglist[i]);
    698 
    699 			if (dl != NULL) {
    700 				found = true;
    701 			}
    702 
    703 			while (dl != NULL) {
    704 				if (dl->ptr != NULL) {
    705 					fprintf(out,
    706 						"\tptr %p size %zu "
    707 						"file %s "
    708 						"line %u\n",
    709 						dl->ptr, dl->size, dl->file,
    710 						dl->line);
    711 				}
    712 				dl = ISC_LIST_NEXT(dl, link);
    713 			}
    714 		}
    715 
    716 		if (!found) {
    717 			fprintf(out, "\tNone.\n");
    718 		}
    719 	}
    720 }
    721 #endif /* if ISC_MEM_TRACKLINES */
    722 
    723 /*
    724  * Print the stats[] on the stream "out" with suitable formatting.
    725  */
    726 void
    727 isc_mem_stats(isc_mem_t *ctx, FILE *out) {
    728 	isc_mempool_t *pool = NULL;
    729 
    730 	REQUIRE(VALID_CONTEXT(ctx));
    731 
    732 	MCTXLOCK(ctx);
    733 
    734 	/*
    735 	 * Note that since a pool can be locked now, these stats might
    736 	 * be somewhat off if the pool is in active use at the time the
    737 	 * stats are dumped.  The link fields are protected by the
    738 	 * isc_mem_t's lock, however, so walking this list and
    739 	 * extracting integers from stats fields is always safe.
    740 	 */
    741 	pool = ISC_LIST_HEAD(ctx->pools);
    742 	if (pool != NULL) {
    743 		fprintf(out, "[Pool statistics]\n");
    744 		fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %1s\n", "name",
    745 			"size", "allocated", "freecount", "freemax",
    746 			"fillcount", "gets", "L");
    747 	}
    748 	while (pool != NULL) {
    749 		fprintf(out,
    750 			"%15s %10zu %10zu %10zu %10zu %10zu %10zu %10zu %s\n",
    751 			pool->name, pool->size, (size_t)0, pool->allocated,
    752 			pool->freecount, pool->freemax, pool->fillcount,
    753 			pool->gets, "N");
    754 		pool = ISC_LIST_NEXT(pool, link);
    755 	}
    756 
    757 #if ISC_MEM_TRACKLINES
    758 	print_active(ctx, out);
    759 #endif /* if ISC_MEM_TRACKLINES */
    760 
    761 	MCTXUNLOCK(ctx);
    762 }
    763 
    764 void *
    765 isc__mem_allocate(isc_mem_t *ctx, size_t size, int flags FLARG) {
    766 	void *ptr = NULL;
    767 
    768 	REQUIRE(VALID_CONTEXT(ctx));
    769 
    770 	ptr = mem_get(ctx, size, flags);
    771 
    772 	/* Recalculate the real allocated size */
    773 	size = sallocx(ptr, flags | ctx->jemalloc_flags);
    774 
    775 	mem_getstats(ctx, size);
    776 	ADD_TRACE(ctx, ptr, size, file, line);
    777 
    778 	return ptr;
    779 }
    780 
    781 void *
    782 isc__mem_reget(isc_mem_t *ctx, void *old_ptr, size_t old_size, size_t new_size,
    783 	       int flags FLARG) {
    784 	void *new_ptr = NULL;
    785 
    786 	if (old_ptr == NULL) {
    787 		REQUIRE(old_size == 0);
    788 		new_ptr = isc__mem_get(ctx, new_size, flags FLARG_PASS);
    789 	} else if (new_size == 0) {
    790 		isc__mem_put(ctx, old_ptr, old_size, flags FLARG_PASS);
    791 	} else {
    792 		DELETE_TRACE(ctx, old_ptr, old_size, file, line);
    793 		mem_putstats(ctx, old_size);
    794 
    795 		new_ptr = mem_realloc(ctx, old_ptr, old_size, new_size, flags);
    796 
    797 		mem_getstats(ctx, new_size);
    798 		ADD_TRACE(ctx, new_ptr, new_size, file, line);
    799 
    800 		/*
    801 		 * We want to postpone the call to water in edge case
    802 		 * where the realloc will exactly hit on the boundary of
    803 		 * the water and we would call water twice.
    804 		 */
    805 	}
    806 
    807 	return new_ptr;
    808 }
    809 
    810 void *
    811 isc__mem_reallocate(isc_mem_t *ctx, void *old_ptr, size_t new_size,
    812 		    int flags FLARG) {
    813 	void *new_ptr = NULL;
    814 
    815 	REQUIRE(VALID_CONTEXT(ctx));
    816 
    817 	if (old_ptr == NULL) {
    818 		new_ptr = isc__mem_allocate(ctx, new_size, flags FLARG_PASS);
    819 	} else if (new_size == 0) {
    820 		isc__mem_free(ctx, old_ptr, flags FLARG_PASS);
    821 	} else {
    822 		size_t old_size = sallocx(old_ptr, flags | ctx->jemalloc_flags);
    823 
    824 		DELETE_TRACE(ctx, old_ptr, old_size, file, line);
    825 		mem_putstats(ctx, old_size);
    826 
    827 		new_ptr = mem_realloc(ctx, old_ptr, old_size, new_size, flags);
    828 
    829 		/* Recalculate the real allocated size */
    830 		new_size = sallocx(new_ptr, flags | ctx->jemalloc_flags);
    831 
    832 		mem_getstats(ctx, new_size);
    833 		ADD_TRACE(ctx, new_ptr, new_size, file, line);
    834 
    835 		/*
    836 		 * We want to postpone the call to water in edge case
    837 		 * where the realloc will exactly hit on the boundary of
    838 		 * the water and we would call water twice.
    839 		 */
    840 	}
    841 
    842 	return new_ptr;
    843 }
    844 
    845 void
    846 isc__mem_free(isc_mem_t *ctx, void *ptr, int flags FLARG) {
    847 	size_t size = 0;
    848 
    849 	REQUIRE(VALID_CONTEXT(ctx));
    850 	REQUIRE(ptr != NULL);
    851 
    852 	size = sallocx(ptr, flags | ctx->jemalloc_flags);
    853 
    854 	DELETE_TRACE(ctx, ptr, size, file, line);
    855 
    856 	mem_putstats(ctx, size);
    857 	mem_put(ctx, ptr, size, flags);
    858 }
    859 
    860 /*
    861  * Other useful things.
    862  */
    863 
    864 char *
    865 isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) {
    866 	size_t len;
    867 	char *ns = NULL;
    868 
    869 	REQUIRE(VALID_CONTEXT(mctx));
    870 	REQUIRE(s != NULL);
    871 
    872 	len = strlen(s) + 1;
    873 
    874 	ns = isc__mem_allocate(mctx, len, 0 FLARG_PASS);
    875 
    876 	strlcpy(ns, s, len);
    877 
    878 	return ns;
    879 }
    880 
    881 char *
    882 isc__mem_strndup(isc_mem_t *mctx, const char *s, size_t size FLARG) {
    883 	size_t len;
    884 	char *ns = NULL;
    885 
    886 	REQUIRE(VALID_CONTEXT(mctx));
    887 	REQUIRE(s != NULL);
    888 	REQUIRE(size != 0);
    889 
    890 	len = strlen(s) + 1;
    891 	if (len > size) {
    892 		len = size;
    893 	}
    894 
    895 	ns = isc__mem_allocate(mctx, len, 0 FLARG_PASS);
    896 
    897 	strlcpy(ns, s, len);
    898 
    899 	return ns;
    900 }
    901 
    902 void
    903 isc_mem_setdestroycheck(isc_mem_t *ctx, bool flag) {
    904 	REQUIRE(VALID_CONTEXT(ctx));
    905 
    906 	MCTXLOCK(ctx);
    907 
    908 	ctx->checkfree = flag;
    909 
    910 	MCTXUNLOCK(ctx);
    911 }
    912 
    913 size_t
    914 isc_mem_inuse(isc_mem_t *ctx) {
    915 	REQUIRE(VALID_CONTEXT(ctx));
    916 
    917 	return atomic_load_relaxed(&ctx->inuse);
    918 }
    919 
    920 void
    921 isc_mem_clearwater(isc_mem_t *mctx) {
    922 	isc_mem_setwater(mctx, 0, 0);
    923 }
    924 
    925 void
    926 isc_mem_setwater(isc_mem_t *ctx, size_t hiwater, size_t lowater) {
    927 	REQUIRE(VALID_CONTEXT(ctx));
    928 	REQUIRE(hiwater >= lowater);
    929 
    930 	atomic_store_release(&ctx->hi_water, hiwater);
    931 	atomic_store_release(&ctx->lo_water, lowater);
    932 
    933 	return;
    934 }
    935 
    936 bool
    937 isc_mem_isovermem(isc_mem_t *ctx) {
    938 	REQUIRE(VALID_CONTEXT(ctx));
    939 
    940 	bool is_overmem = atomic_load_relaxed(&ctx->is_overmem);
    941 
    942 	if (!is_overmem) {
    943 		/* We are not overmem, check whether we should be? */
    944 		size_t hiwater = atomic_load_relaxed(&ctx->hi_water);
    945 		if (hiwater == 0) {
    946 			return false;
    947 		}
    948 
    949 		size_t inuse = atomic_load_relaxed(&ctx->inuse);
    950 		if (inuse <= hiwater) {
    951 			return false;
    952 		}
    953 
    954 		if ((isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) {
    955 			fprintf(stderr,
    956 				"overmem mctx %p inuse %zu hi_water %zu\n", ctx,
    957 				inuse, hiwater);
    958 		}
    959 
    960 		atomic_store_relaxed(&ctx->is_overmem, true);
    961 		return true;
    962 	} else {
    963 		/* We are overmem, check whether we should not be? */
    964 		size_t lowater = atomic_load_relaxed(&ctx->lo_water);
    965 		if (lowater == 0) {
    966 			return false;
    967 		}
    968 
    969 		size_t inuse = atomic_load_relaxed(&ctx->inuse);
    970 		if (inuse >= lowater) {
    971 			return true;
    972 		}
    973 
    974 		if ((isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) {
    975 			fprintf(stderr,
    976 				"overmem mctx %p inuse %zu lo_water %zu\n", ctx,
    977 				inuse, lowater);
    978 		}
    979 		atomic_store_relaxed(&ctx->is_overmem, false);
    980 		return false;
    981 	}
    982 }
    983 
    984 void
    985 isc_mem_setname(isc_mem_t *ctx, const char *name) {
    986 	REQUIRE(VALID_CONTEXT(ctx));
    987 
    988 	LOCK(&ctx->lock);
    989 	strlcpy(ctx->name, name, sizeof(ctx->name));
    990 	UNLOCK(&ctx->lock);
    991 }
    992 
    993 const char *
    994 isc_mem_getname(isc_mem_t *ctx) {
    995 	REQUIRE(VALID_CONTEXT(ctx));
    996 
    997 	if (ctx->name[0] == 0) {
    998 		return "";
    999 	}
   1000 
   1001 	return ctx->name;
   1002 }
   1003 
   1004 /*
   1005  * Memory pool stuff
   1006  */
   1007 
   1008 void
   1009 isc__mempool_create(isc_mem_t *restrict mctx, const size_t element_size,
   1010 		    isc_mempool_t **restrict mpctxp FLARG) {
   1011 	isc_mempool_t *restrict mpctx = NULL;
   1012 	size_t size = element_size;
   1013 
   1014 	REQUIRE(VALID_CONTEXT(mctx));
   1015 	REQUIRE(size > 0U);
   1016 	REQUIRE(mpctxp != NULL && *mpctxp == NULL);
   1017 
   1018 	/*
   1019 	 * Mempools are stored as a linked list of element.
   1020 	 */
   1021 	if (size < sizeof(element)) {
   1022 		size = sizeof(element);
   1023 	}
   1024 
   1025 	/*
   1026 	 * Allocate space for this pool, initialize values, and if all
   1027 	 * works well, attach to the memory context.
   1028 	 */
   1029 	mpctx = isc_mem_get(mctx, sizeof(isc_mempool_t));
   1030 
   1031 	*mpctx = (isc_mempool_t){
   1032 		.size = size,
   1033 		.freemax = 1,
   1034 		.fillcount = 1,
   1035 	};
   1036 
   1037 #if ISC_MEM_TRACKLINES
   1038 	if ((mctx->debugging & ISC_MEM_DEBUGTRACE) != 0) {
   1039 		fprintf(stderr, "create pool %p file %s line %u mctx %p\n",
   1040 			mpctx, file, line, mctx);
   1041 	}
   1042 #endif /* ISC_MEM_TRACKLINES */
   1043 
   1044 	isc_mem_attach(mctx, &mpctx->mctx);
   1045 	mpctx->magic = MEMPOOL_MAGIC;
   1046 
   1047 	*mpctxp = (isc_mempool_t *)mpctx;
   1048 
   1049 	MCTXLOCK(mctx);
   1050 	ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link);
   1051 	mctx->poolcnt++;
   1052 	MCTXUNLOCK(mctx);
   1053 }
   1054 
   1055 void
   1056 isc_mempool_setname(isc_mempool_t *restrict mpctx, const char *name) {
   1057 	REQUIRE(VALID_MEMPOOL(mpctx));
   1058 	REQUIRE(name != NULL);
   1059 
   1060 	strlcpy(mpctx->name, name, sizeof(mpctx->name));
   1061 }
   1062 
   1063 void
   1064 isc__mempool_destroy(isc_mempool_t **restrict mpctxp FLARG) {
   1065 	isc_mempool_t *restrict mpctx = NULL;
   1066 	isc_mem_t *mctx = NULL;
   1067 	element *restrict item = NULL;
   1068 
   1069 	REQUIRE(mpctxp != NULL);
   1070 	REQUIRE(VALID_MEMPOOL(*mpctxp));
   1071 
   1072 	mpctx = *mpctxp;
   1073 	*mpctxp = NULL;
   1074 
   1075 	mctx = mpctx->mctx;
   1076 
   1077 #if ISC_MEM_TRACKLINES
   1078 	if ((mctx->debugging & ISC_MEM_DEBUGTRACE) != 0) {
   1079 		fprintf(stderr, "destroy pool %p file %s line %u mctx %p\n",
   1080 			mpctx, file, line, mctx);
   1081 	}
   1082 #endif
   1083 
   1084 	if (mpctx->allocated > 0) {
   1085 		UNEXPECTED_ERROR("mempool %s leaked memory", mpctx->name);
   1086 	}
   1087 	REQUIRE(mpctx->allocated == 0);
   1088 
   1089 	/*
   1090 	 * Return any items on the free list
   1091 	 */
   1092 	while (mpctx->items != NULL) {
   1093 		INSIST(mpctx->freecount > 0);
   1094 		mpctx->freecount--;
   1095 
   1096 		item = mpctx->items;
   1097 		mpctx->items = item->next;
   1098 
   1099 		mem_putstats(mctx, mpctx->size);
   1100 		mem_put(mctx, item, mpctx->size, 0);
   1101 	}
   1102 
   1103 	/*
   1104 	 * Remove our linked list entry from the memory context.
   1105 	 */
   1106 	MCTXLOCK(mctx);
   1107 	ISC_LIST_UNLINK(mctx->pools, mpctx, link);
   1108 	mctx->poolcnt--;
   1109 	MCTXUNLOCK(mctx);
   1110 
   1111 	mpctx->magic = 0;
   1112 
   1113 	isc_mem_putanddetach(&mpctx->mctx, mpctx, sizeof(isc_mempool_t));
   1114 }
   1115 
   1116 void *
   1117 isc__mempool_get(isc_mempool_t *restrict mpctx FLARG) {
   1118 	element *restrict item = NULL;
   1119 
   1120 	REQUIRE(VALID_MEMPOOL(mpctx));
   1121 
   1122 	mpctx->allocated++;
   1123 
   1124 	if (mpctx->items == NULL) {
   1125 		isc_mem_t *mctx = mpctx->mctx;
   1126 #if !__SANITIZE_ADDRESS__
   1127 		const size_t fillcount = mpctx->fillcount;
   1128 #else
   1129 		const size_t fillcount = 1;
   1130 #endif
   1131 		/*
   1132 		 * We need to dip into the well.  Fill up our free list.
   1133 		 */
   1134 		for (size_t i = 0; i < fillcount; i++) {
   1135 			item = mem_get(mctx, mpctx->size, 0);
   1136 			mem_getstats(mctx, mpctx->size);
   1137 			item->next = mpctx->items;
   1138 			mpctx->items = item;
   1139 			mpctx->freecount++;
   1140 		}
   1141 	}
   1142 
   1143 	INSIST(mpctx->items != NULL);
   1144 	item = mpctx->items;
   1145 
   1146 	mpctx->items = item->next;
   1147 
   1148 	INSIST(mpctx->freecount > 0);
   1149 	mpctx->freecount--;
   1150 	mpctx->gets++;
   1151 
   1152 	ADD_TRACE(mpctx->mctx, item, mpctx->size, file, line);
   1153 
   1154 	return item;
   1155 }
   1156 
   1157 /* coverity[+free : arg-1] */
   1158 void
   1159 isc__mempool_put(isc_mempool_t *restrict mpctx, void *mem FLARG) {
   1160 	element *restrict item = NULL;
   1161 
   1162 	REQUIRE(VALID_MEMPOOL(mpctx));
   1163 	REQUIRE(mem != NULL);
   1164 
   1165 	isc_mem_t *mctx = mpctx->mctx;
   1166 	const size_t freecount = mpctx->freecount;
   1167 #if !__SANITIZE_ADDRESS__
   1168 	const size_t freemax = mpctx->freemax;
   1169 #else
   1170 	const size_t freemax = 0;
   1171 #endif
   1172 
   1173 	INSIST(mpctx->allocated > 0);
   1174 	mpctx->allocated--;
   1175 
   1176 	DELETE_TRACE(mctx, mem, mpctx->size, file, line);
   1177 
   1178 	/*
   1179 	 * If our free list is full, return this to the mctx directly.
   1180 	 */
   1181 	if (freecount >= freemax) {
   1182 		mem_putstats(mctx, mpctx->size);
   1183 		mem_put(mctx, mem, mpctx->size, 0);
   1184 		return;
   1185 	}
   1186 
   1187 	/*
   1188 	 * Otherwise, attach it to our free list and bump the counter.
   1189 	 */
   1190 	item = (element *)mem;
   1191 	item->next = mpctx->items;
   1192 	mpctx->items = item;
   1193 	mpctx->freecount++;
   1194 }
   1195 
   1196 /*
   1197  * Quotas
   1198  */
   1199 
   1200 void
   1201 isc_mempool_setfreemax(isc_mempool_t *restrict mpctx,
   1202 		       const unsigned int limit) {
   1203 	REQUIRE(VALID_MEMPOOL(mpctx));
   1204 	mpctx->freemax = limit;
   1205 }
   1206 
   1207 unsigned int
   1208 isc_mempool_getfreemax(isc_mempool_t *restrict mpctx) {
   1209 	REQUIRE(VALID_MEMPOOL(mpctx));
   1210 
   1211 	return mpctx->freemax;
   1212 }
   1213 
   1214 unsigned int
   1215 isc_mempool_getfreecount(isc_mempool_t *restrict mpctx) {
   1216 	REQUIRE(VALID_MEMPOOL(mpctx));
   1217 
   1218 	return mpctx->freecount;
   1219 }
   1220 
   1221 unsigned int
   1222 isc_mempool_getallocated(isc_mempool_t *restrict mpctx) {
   1223 	REQUIRE(VALID_MEMPOOL(mpctx));
   1224 
   1225 	return mpctx->allocated;
   1226 }
   1227 
   1228 void
   1229 isc_mempool_setfillcount(isc_mempool_t *restrict mpctx,
   1230 			 unsigned int const limit) {
   1231 	REQUIRE(VALID_MEMPOOL(mpctx));
   1232 	REQUIRE(limit > 0);
   1233 
   1234 	mpctx->fillcount = limit;
   1235 }
   1236 
   1237 unsigned int
   1238 isc_mempool_getfillcount(isc_mempool_t *restrict mpctx) {
   1239 	REQUIRE(VALID_MEMPOOL(mpctx));
   1240 
   1241 	return mpctx->fillcount;
   1242 }
   1243 
   1244 /*
   1245  * Requires contextslock to be held by caller.
   1246  */
   1247 #if ISC_MEM_TRACKLINES
   1248 static void
   1249 print_contexts(FILE *file) {
   1250 	isc_mem_t *ctx;
   1251 
   1252 	for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
   1253 	     ctx = ISC_LIST_NEXT(ctx, link))
   1254 	{
   1255 		fprintf(file, "context: %p (%s): %" PRIuFAST32 " references\n",
   1256 			ctx, ctx->name[0] == 0 ? "<unknown>" : ctx->name,
   1257 			isc_refcount_current(&ctx->references));
   1258 		print_active(ctx, file);
   1259 	}
   1260 	fflush(file);
   1261 }
   1262 #endif
   1263 
   1264 static atomic_uintptr_t checkdestroyed = 0;
   1265 
   1266 void
   1267 isc_mem_checkdestroyed(FILE *file) {
   1268 	atomic_store_release(&checkdestroyed, (uintptr_t)file);
   1269 }
   1270 
   1271 void
   1272 isc__mem_checkdestroyed(void) {
   1273 	FILE *file = (FILE *)atomic_load_acquire(&checkdestroyed);
   1274 
   1275 	if (file == NULL) {
   1276 		return;
   1277 	}
   1278 
   1279 	LOCK(&contextslock);
   1280 	if (!ISC_LIST_EMPTY(contexts)) {
   1281 #if ISC_MEM_TRACKLINES
   1282 		if ((isc_mem_debugging & TRACE_OR_RECORD) != 0) {
   1283 			print_contexts(file);
   1284 		}
   1285 #endif /* if ISC_MEM_TRACKLINES */
   1286 		UNREACHABLE();
   1287 	}
   1288 	UNLOCK(&contextslock);
   1289 }
   1290 
   1291 unsigned int
   1292 isc_mem_references(isc_mem_t *ctx) {
   1293 	return isc_refcount_current(&ctx->references);
   1294 }
   1295 
   1296 #ifdef HAVE_LIBXML2
   1297 #define TRY0(a)                     \
   1298 	do {                        \
   1299 		xmlrc = (a);        \
   1300 		if (xmlrc < 0)      \
   1301 			goto error; \
   1302 	} while (0)
   1303 static int
   1304 xml_renderctx(isc_mem_t *ctx, size_t *inuse, xmlTextWriterPtr writer) {
   1305 	REQUIRE(VALID_CONTEXT(ctx));
   1306 
   1307 	int xmlrc;
   1308 
   1309 	MCTXLOCK(ctx);
   1310 
   1311 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "context"));
   1312 
   1313 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id"));
   1314 	TRY0(xmlTextWriterWriteFormatString(writer, "%p", ctx));
   1315 	TRY0(xmlTextWriterEndElement(writer)); /* id */
   1316 
   1317 	if (ctx->name[0] != 0) {
   1318 		TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
   1319 		TRY0(xmlTextWriterWriteFormatString(writer, "%s", ctx->name));
   1320 		TRY0(xmlTextWriterEndElement(writer)); /* name */
   1321 	}
   1322 
   1323 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references"));
   1324 	TRY0(xmlTextWriterWriteFormatString(
   1325 		writer, "%" PRIuFAST32,
   1326 		isc_refcount_current(&ctx->references)));
   1327 	TRY0(xmlTextWriterEndElement(writer)); /* references */
   1328 
   1329 	*inuse += isc_mem_inuse(ctx);
   1330 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse"));
   1331 	TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
   1332 					    (uint64_t)isc_mem_inuse(ctx)));
   1333 	TRY0(xmlTextWriterEndElement(writer)); /* inuse */
   1334 
   1335 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "malloced"));
   1336 	TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
   1337 					    (uint64_t)isc_mem_inuse(ctx)));
   1338 	TRY0(xmlTextWriterEndElement(writer)); /* malloced */
   1339 
   1340 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools"));
   1341 	TRY0(xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt));
   1342 	TRY0(xmlTextWriterEndElement(writer)); /* pools */
   1343 
   1344 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater"));
   1345 	TRY0(xmlTextWriterWriteFormatString(
   1346 		writer, "%" PRIu64 "",
   1347 		(uint64_t)atomic_load_relaxed(&ctx->hi_water)));
   1348 	TRY0(xmlTextWriterEndElement(writer)); /* hiwater */
   1349 
   1350 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater"));
   1351 	TRY0(xmlTextWriterWriteFormatString(
   1352 		writer, "%" PRIu64 "",
   1353 		(uint64_t)atomic_load_relaxed(&ctx->lo_water)));
   1354 	TRY0(xmlTextWriterEndElement(writer)); /* lowater */
   1355 
   1356 	TRY0(xmlTextWriterEndElement(writer)); /* context */
   1357 
   1358 error:
   1359 	MCTXUNLOCK(ctx);
   1360 
   1361 	return xmlrc;
   1362 }
   1363 
   1364 int
   1365 isc_mem_renderxml(void *writer0) {
   1366 	isc_mem_t *ctx;
   1367 	size_t inuse = 0;
   1368 	int xmlrc;
   1369 	xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0;
   1370 
   1371 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts"));
   1372 
   1373 	LOCK(&contextslock);
   1374 	for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
   1375 	     ctx = ISC_LIST_NEXT(ctx, link))
   1376 	{
   1377 		xmlrc = xml_renderctx(ctx, &inuse, writer);
   1378 		if (xmlrc < 0) {
   1379 			UNLOCK(&contextslock);
   1380 			goto error;
   1381 		}
   1382 	}
   1383 	UNLOCK(&contextslock);
   1384 
   1385 	TRY0(xmlTextWriterEndElement(writer)); /* contexts */
   1386 
   1387 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary"));
   1388 
   1389 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Malloced"));
   1390 	TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
   1391 					    (uint64_t)inuse));
   1392 	TRY0(xmlTextWriterEndElement(writer)); /* malloced */
   1393 
   1394 	TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse"));
   1395 	TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
   1396 					    (uint64_t)inuse));
   1397 	TRY0(xmlTextWriterEndElement(writer)); /* InUse */
   1398 
   1399 	TRY0(xmlTextWriterEndElement(writer)); /* summary */
   1400 error:
   1401 	return xmlrc;
   1402 }
   1403 
   1404 #endif /* HAVE_LIBXML2 */
   1405 
   1406 #ifdef HAVE_JSON_C
   1407 #define CHECKMEM(m) RUNTIME_CHECK(m != NULL)
   1408 
   1409 static isc_result_t
   1410 json_renderctx(isc_mem_t *ctx, size_t *inuse, json_object *array) {
   1411 	REQUIRE(VALID_CONTEXT(ctx));
   1412 	REQUIRE(array != NULL);
   1413 
   1414 	json_object *ctxobj, *obj;
   1415 	char buf[1024];
   1416 
   1417 	MCTXLOCK(ctx);
   1418 
   1419 	*inuse += isc_mem_inuse(ctx);
   1420 
   1421 	ctxobj = json_object_new_object();
   1422 	CHECKMEM(ctxobj);
   1423 
   1424 	snprintf(buf, sizeof(buf), "%p", ctx);
   1425 	obj = json_object_new_string(buf);
   1426 	CHECKMEM(obj);
   1427 	json_object_object_add(ctxobj, "id", obj);
   1428 
   1429 	if (ctx->name[0] != 0) {
   1430 		obj = json_object_new_string(ctx->name);
   1431 		CHECKMEM(obj);
   1432 		json_object_object_add(ctxobj, "name", obj);
   1433 	}
   1434 
   1435 	obj = json_object_new_int64(isc_refcount_current(&ctx->references));
   1436 	CHECKMEM(obj);
   1437 	json_object_object_add(ctxobj, "references", obj);
   1438 
   1439 	obj = json_object_new_int64(isc_mem_inuse(ctx));
   1440 	CHECKMEM(obj);
   1441 	json_object_object_add(ctxobj, "malloced", obj);
   1442 
   1443 	obj = json_object_new_int64(isc_mem_inuse(ctx));
   1444 	CHECKMEM(obj);
   1445 	json_object_object_add(ctxobj, "inuse", obj);
   1446 
   1447 	obj = json_object_new_int64(ctx->poolcnt);
   1448 	CHECKMEM(obj);
   1449 	json_object_object_add(ctxobj, "pools", obj);
   1450 
   1451 	obj = json_object_new_int64(atomic_load_relaxed(&ctx->hi_water));
   1452 	CHECKMEM(obj);
   1453 	json_object_object_add(ctxobj, "hiwater", obj);
   1454 
   1455 	obj = json_object_new_int64(atomic_load_relaxed(&ctx->lo_water));
   1456 	CHECKMEM(obj);
   1457 	json_object_object_add(ctxobj, "lowater", obj);
   1458 
   1459 	MCTXUNLOCK(ctx);
   1460 	json_object_array_add(array, ctxobj);
   1461 	return ISC_R_SUCCESS;
   1462 }
   1463 
   1464 isc_result_t
   1465 isc_mem_renderjson(void *memobj0) {
   1466 	isc_result_t result = ISC_R_SUCCESS;
   1467 	isc_mem_t *ctx;
   1468 	size_t inuse = 0;
   1469 	json_object *ctxarray, *obj;
   1470 	json_object *memobj = (json_object *)memobj0;
   1471 
   1472 	ctxarray = json_object_new_array();
   1473 	CHECKMEM(ctxarray);
   1474 
   1475 	LOCK(&contextslock);
   1476 	for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
   1477 	     ctx = ISC_LIST_NEXT(ctx, link))
   1478 	{
   1479 		result = json_renderctx(ctx, &inuse, ctxarray);
   1480 		if (result != ISC_R_SUCCESS) {
   1481 			UNLOCK(&contextslock);
   1482 			goto error;
   1483 		}
   1484 	}
   1485 	UNLOCK(&contextslock);
   1486 
   1487 	obj = json_object_new_int64(inuse);
   1488 	CHECKMEM(obj);
   1489 	json_object_object_add(memobj, "InUse", obj);
   1490 
   1491 	obj = json_object_new_int64(inuse);
   1492 	CHECKMEM(obj);
   1493 	json_object_object_add(memobj, "Malloced", obj);
   1494 
   1495 	json_object_object_add(memobj, "contexts", ctxarray);
   1496 	return ISC_R_SUCCESS;
   1497 
   1498 error:
   1499 	if (ctxarray != NULL) {
   1500 		json_object_put(ctxarray);
   1501 	}
   1502 	return result;
   1503 }
   1504 #endif /* HAVE_JSON_C */
   1505 
   1506 void
   1507 isc__mem_create(isc_mem_t **mctxp FLARG) {
   1508 	mem_create(mctxp, isc_mem_debugging, isc_mem_defaultflags, 0);
   1509 #if ISC_MEM_TRACKLINES
   1510 	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
   1511 		fprintf(stderr, "create mctx %p file %s line %u\n", *mctxp,
   1512 			file, line);
   1513 	}
   1514 #endif /* ISC_MEM_TRACKLINES */
   1515 }
   1516 
   1517 void
   1518 isc__mem_create_arena(isc_mem_t **mctxp FLARG) {
   1519 	unsigned int arena_no = ISC_MEM_ILLEGAL_ARENA;
   1520 
   1521 	RUNTIME_CHECK(mem_jemalloc_arena_create(&arena_no));
   1522 
   1523 	/*
   1524 	 * We use MALLOCX_TCACHE_NONE to bypass the tcache and route
   1525 	 * allocations directly to the arena. That is a recommendation
   1526 	 * from jemalloc developers:
   1527 	 *
   1528 	 * https://github.com/jemalloc/jemalloc/issues/2483#issuecomment-1698173849
   1529 	 */
   1530 	mem_create(mctxp, isc_mem_debugging, isc_mem_defaultflags,
   1531 		   arena_no == ISC_MEM_ILLEGAL_ARENA
   1532 			   ? 0
   1533 			   : MALLOCX_ARENA(arena_no) | MALLOCX_TCACHE_NONE);
   1534 	(*mctxp)->jemalloc_arena = arena_no;
   1535 #if ISC_MEM_TRACKLINES
   1536 	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
   1537 		fprintf(stderr,
   1538 			"create mctx %p file %s line %u for jemalloc arena "
   1539 			"%u\n",
   1540 			*mctxp, file, line, arena_no);
   1541 	}
   1542 #endif /* ISC_MEM_TRACKLINES */
   1543 }
   1544 
   1545 #ifdef JEMALLOC_API_SUPPORTED
   1546 static bool
   1547 jemalloc_set_ssize_value(const char *valname, ssize_t newval) {
   1548 	int ret;
   1549 
   1550 	ret = mallctl(valname, NULL, NULL, &newval, sizeof(newval));
   1551 	return ret == 0;
   1552 }
   1553 #endif /* JEMALLOC_API_SUPPORTED */
   1554 
   1555 static isc_result_t
   1556 mem_set_arena_ssize_value(isc_mem_t *mctx, const char *arena_valname,
   1557 			  const ssize_t newval) {
   1558 	REQUIRE(VALID_CONTEXT(mctx));
   1559 #ifdef JEMALLOC_API_SUPPORTED
   1560 	bool ret;
   1561 	char buf[256] = { 0 };
   1562 
   1563 	if (mctx->jemalloc_arena == ISC_MEM_ILLEGAL_ARENA) {
   1564 		return ISC_R_UNEXPECTED;
   1565 	}
   1566 
   1567 	(void)snprintf(buf, sizeof(buf), "arena.%u.%s", mctx->jemalloc_arena,
   1568 		       arena_valname);
   1569 
   1570 	ret = jemalloc_set_ssize_value(buf, newval);
   1571 
   1572 	if (!ret) {
   1573 		return ISC_R_FAILURE;
   1574 	}
   1575 
   1576 	return ISC_R_SUCCESS;
   1577 #else
   1578 	UNUSED(arena_valname);
   1579 	UNUSED(newval);
   1580 	return ISC_R_NOTIMPLEMENTED;
   1581 #endif
   1582 }
   1583 
   1584 isc_result_t
   1585 isc_mem_arena_set_muzzy_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms) {
   1586 	return mem_set_arena_ssize_value(mctx, "muzzy_decay_ms", decay_ms);
   1587 }
   1588 
   1589 isc_result_t
   1590 isc_mem_arena_set_dirty_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms) {
   1591 	return mem_set_arena_ssize_value(mctx, "dirty_decay_ms", decay_ms);
   1592 }
   1593 
   1594 void
   1595 isc__mem_printactive(isc_mem_t *ctx, FILE *file) {
   1596 #if ISC_MEM_TRACKLINES
   1597 	REQUIRE(VALID_CONTEXT(ctx));
   1598 	REQUIRE(file != NULL);
   1599 
   1600 	print_active(ctx, file);
   1601 #else  /* if ISC_MEM_TRACKLINES */
   1602 	UNUSED(ctx);
   1603 	UNUSED(file);
   1604 #endif /* if ISC_MEM_TRACKLINES */
   1605 }
   1606