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