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