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