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