summaryrefslogtreecommitdiffstats
path: root/lib/isc/mem.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/isc/mem.c2450
1 files changed, 2450 insertions, 0 deletions
diff --git a/lib/isc/mem.c b/lib/isc/mem.c
new file mode 100644
index 0000000..21b2d86
--- /dev/null
+++ b/lib/isc/mem.c
@@ -0,0 +1,2450 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <isc/bind9.h>
+#include <isc/hash.h>
+#include <isc/lib.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/refcount.h>
+#include <isc/strerr.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#ifdef HAVE_LIBXML2
+#include <libxml/xmlwriter.h>
+#define ISC_XMLCHAR (const xmlChar *)
+#endif /* HAVE_LIBXML2 */
+
+#ifdef HAVE_JSON_C
+#include <json_object.h>
+#endif /* HAVE_JSON_C */
+
+#include "mem_p.h"
+
+#define MCTXLOCK(m) LOCK(&m->lock)
+#define MCTXUNLOCK(m) UNLOCK(&m->lock)
+
+#ifndef ISC_MEM_DEBUGGING
+#define ISC_MEM_DEBUGGING 0
+#endif /* ifndef ISC_MEM_DEBUGGING */
+LIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;
+LIBISC_EXTERNAL_DATA unsigned int isc_mem_defaultflags = ISC_MEMFLAG_DEFAULT;
+
+/*
+ * Constants.
+ */
+
+#define DEF_MAX_SIZE 1100
+#define DEF_MEM_TARGET 4096
+#define ALIGNMENT_SIZE \
+ 8U /*%< must be a power of 2, also update lib/dns/rbt.c */
+#define NUM_BASIC_BLOCKS 64 /*%< must be > 1 */
+#define TABLE_INCREMENT 1024
+#define DEBUG_TABLE_COUNT 512U
+
+/*
+ * Types.
+ */
+typedef struct isc__mem isc__mem_t;
+typedef struct isc__mempool isc__mempool_t;
+
+#if ISC_MEM_TRACKLINES
+typedef struct debuglink debuglink_t;
+struct debuglink {
+ ISC_LINK(debuglink_t) link;
+ const void *ptr;
+ size_t size;
+ const char *file;
+ unsigned int line;
+};
+
+typedef ISC_LIST(debuglink_t) debuglist_t;
+
+#define FLARG_PASS , file, line
+#define FLARG , const char *file, unsigned int line
+#else /* if ISC_MEM_TRACKLINES */
+#define FLARG_PASS
+#define FLARG
+#endif /* if ISC_MEM_TRACKLINES */
+
+typedef struct element element;
+struct element {
+ element *next;
+};
+
+typedef struct {
+ /*!
+ * This structure must be ALIGNMENT_SIZE bytes.
+ */
+ union {
+ size_t size;
+ isc__mem_t *ctx;
+ char bytes[ALIGNMENT_SIZE];
+ } u;
+} size_info;
+
+struct stats {
+ unsigned long gets;
+ unsigned long totalgets;
+ unsigned long blocks;
+ unsigned long freefrags;
+};
+
+#define MEM_MAGIC ISC_MAGIC('M', 'e', 'm', 'C')
+#define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC)
+
+/* List of all active memory contexts. */
+
+static ISC_LIST(isc__mem_t) contexts;
+
+static isc_once_t init_once = ISC_ONCE_INIT;
+static isc_once_t shut_once = ISC_ONCE_INIT;
+static isc_mutex_t contextslock;
+
+/*%
+ * Total size of lost memory due to a bug of external library.
+ * Locked by the global lock.
+ */
+static uint64_t totallost;
+
+/*%
+ * Memory allocation and free function definitions.
+ * isc__memalloc_t must deal with memory allocation failure
+ * and must never return NULL.
+ */
+typedef void *(*isc__memalloc_t)(size_t);
+typedef void (*isc__memfree_t)(void *);
+
+struct isc__mem {
+ isc_mem_t common;
+ unsigned int flags;
+ isc_mutex_t lock;
+ isc__memalloc_t memalloc;
+ isc__memfree_t memfree;
+ size_t max_size;
+ bool checkfree;
+ struct stats *stats;
+ isc_refcount_t references;
+ char name[16];
+ void *tag;
+ size_t total;
+ size_t inuse;
+ size_t maxinuse;
+ size_t malloced;
+ size_t maxmalloced;
+ size_t hi_water;
+ size_t lo_water;
+ bool hi_called;
+ bool is_overmem;
+ isc_mem_water_t water;
+ void *water_arg;
+ ISC_LIST(isc__mempool_t) pools;
+ unsigned int poolcnt;
+
+ /* ISC_MEMFLAG_INTERNAL */
+ size_t mem_target;
+ element **freelists;
+ element *basic_blocks;
+ unsigned char **basic_table;
+ unsigned int basic_table_count;
+ unsigned int basic_table_size;
+ unsigned char *lowest;
+ unsigned char *highest;
+
+#if ISC_MEM_TRACKLINES
+ debuglist_t *debuglist;
+ size_t debuglistcnt;
+#endif /* if ISC_MEM_TRACKLINES */
+
+ ISC_LINK(isc__mem_t) link;
+};
+
+#define MEMPOOL_MAGIC ISC_MAGIC('M', 'E', 'M', 'p')
+#define VALID_MEMPOOL(c) ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
+
+struct isc__mempool {
+ /* always unlocked */
+ isc_mempool_t common; /*%< common header of mempool's */
+ isc__mem_t *mctx; /*%< our memory context */
+ ISC_LINK(isc__mempool_t) link; /*%< next pool in this mem context */
+ element *items; /*%< low water item list */
+ size_t size; /*%< size of each item on this pool */
+ unsigned int maxalloc; /*%< max number of items allowed */
+ unsigned int allocated; /*%< # of items currently given out */
+ unsigned int freecount; /*%< # of items on reserved list */
+ unsigned int freemax; /*%< # of items allowed on free list */
+ unsigned int fillcount; /*%< # of items to fetch on each fill */
+ /*%< Stats only. */
+ unsigned int gets; /*%< # of requests to this pool */
+ /*%< Debugging only. */
+#if ISC_MEMPOOL_NAMES
+ char name[16]; /*%< printed name in stats reports */
+#endif /* if ISC_MEMPOOL_NAMES */
+};
+
+/*
+ * Private Inline-able.
+ */
+
+#if !ISC_MEM_TRACKLINES
+#define ADD_TRACE(a, b, c, d, e)
+#define DELETE_TRACE(a, b, c, d, e)
+#define ISC_MEMFUNC_SCOPE
+#else /* if !ISC_MEM_TRACKLINES */
+#define TRACE_OR_RECORD (ISC_MEM_DEBUGTRACE | ISC_MEM_DEBUGRECORD)
+#define ADD_TRACE(a, b, c, d, e) \
+ do { \
+ if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0 && \
+ b != NULL)) \
+ add_trace_entry(a, b, c, d, e); \
+ } while (0)
+#define DELETE_TRACE(a, b, c, d, e) \
+ do { \
+ if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0 && \
+ b != NULL)) \
+ delete_trace_entry(a, b, c, d, e); \
+ } while (0)
+
+static void
+print_active(isc__mem_t *ctx, FILE *out);
+
+#endif /* ISC_MEM_TRACKLINES */
+
+static void *
+isc___mem_get(isc_mem_t *ctx, size_t size FLARG);
+static void
+isc___mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG);
+static void
+isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG);
+static void *
+isc___mem_allocate(isc_mem_t *ctx, size_t size FLARG);
+static void *
+isc___mem_reallocate(isc_mem_t *ctx, void *ptr, size_t size FLARG);
+static char *
+isc___mem_strdup(isc_mem_t *mctx, const char *s FLARG);
+static char *
+isc___mem_strndup(isc_mem_t *mctx0, const char *s, size_t size FLARG);
+static void
+isc___mem_free(isc_mem_t *ctx, void *ptr FLARG);
+
+static isc_memmethods_t memmethods = {
+ isc___mem_get, isc___mem_put, isc___mem_putanddetach,
+ isc___mem_allocate, isc___mem_reallocate, isc___mem_strdup,
+ isc___mem_strndup, isc___mem_free,
+};
+
+#if ISC_MEM_TRACKLINES
+/*!
+ * mctx must be locked.
+ */
+static void
+add_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size FLARG) {
+ debuglink_t *dl;
+ uint32_t hash;
+ uint32_t idx;
+
+ if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
+ fprintf(stderr, "add %p size %zu file %s line %u mctx %p\n",
+ ptr, size, file, line, mctx);
+ }
+
+ if (mctx->debuglist == NULL) {
+ return;
+ }
+
+#ifdef __COVERITY__
+ /*
+ * Use simple conversion from pointer to hash to avoid
+ * tainting 'ptr' due to byte swap in isc_hash_function.
+ */
+ hash = (uintptr_t)ptr >> 3;
+#else
+ hash = isc_hash_function(&ptr, sizeof(ptr), true);
+#endif
+ idx = hash % DEBUG_TABLE_COUNT;
+
+ dl = malloc(sizeof(debuglink_t));
+ INSIST(dl != NULL);
+ mctx->malloced += sizeof(debuglink_t);
+ if (mctx->malloced > mctx->maxmalloced) {
+ mctx->maxmalloced = mctx->malloced;
+ }
+
+ ISC_LINK_INIT(dl, link);
+ dl->ptr = ptr;
+ dl->size = size;
+ dl->file = file;
+ dl->line = line;
+
+ ISC_LIST_PREPEND(mctx->debuglist[idx], dl, link);
+ mctx->debuglistcnt++;
+}
+
+static void
+delete_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size,
+ const char *file, unsigned int line) {
+ debuglink_t *dl;
+ uint32_t hash;
+ uint32_t idx;
+
+ if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
+ fprintf(stderr, "del %p size %zu file %s line %u mctx %p\n",
+ ptr, size, file, line, mctx);
+ }
+
+ if (mctx->debuglist == NULL) {
+ return;
+ }
+
+#ifdef __COVERITY__
+ /*
+ * Use simple conversion from pointer to hash to avoid
+ * tainting 'ptr' due to byte swap in isc_hash_function.
+ */
+ hash = (uintptr_t)ptr >> 3;
+#else
+ hash = isc_hash_function(&ptr, sizeof(ptr), true);
+#endif
+ idx = hash % DEBUG_TABLE_COUNT;
+
+ dl = ISC_LIST_HEAD(mctx->debuglist[idx]);
+ while (ISC_LIKELY(dl != NULL)) {
+ if (ISC_UNLIKELY(dl->ptr == ptr)) {
+ ISC_LIST_UNLINK(mctx->debuglist[idx], dl, link);
+ mctx->malloced -= sizeof(*dl);
+ free(dl);
+ return;
+ }
+ dl = ISC_LIST_NEXT(dl, link);
+ }
+
+ /*
+ * If we get here, we didn't find the item on the list. We're
+ * screwed.
+ */
+ UNREACHABLE();
+}
+#endif /* ISC_MEM_TRACKLINES */
+
+static size_t
+rmsize(size_t size) {
+ /*
+ * round down to ALIGNMENT_SIZE
+ */
+ return (size & (~(ALIGNMENT_SIZE - 1)));
+}
+
+static size_t
+quantize(size_t size) {
+ /*!
+ * Round up the result in order to get a size big
+ * enough to satisfy the request and be aligned on ALIGNMENT_SIZE
+ * byte boundaries.
+ */
+
+ if (size == 0U) {
+ return (ALIGNMENT_SIZE);
+ }
+ return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1)));
+}
+
+static void
+more_basic_blocks(isc__mem_t *ctx) {
+ void *tmp;
+ unsigned char *curr, *next;
+ unsigned char *first, *last;
+ unsigned char **table;
+ unsigned int table_size;
+
+ /* Require: we hold the context lock. */
+
+ INSIST(ctx->basic_table_count <= ctx->basic_table_size);
+ if (ctx->basic_table_count == ctx->basic_table_size) {
+ table_size = ctx->basic_table_size + TABLE_INCREMENT;
+ table = (ctx->memalloc)(table_size * sizeof(unsigned char *));
+ ctx->malloced += table_size * sizeof(unsigned char *);
+ if (ctx->malloced > ctx->maxmalloced) {
+ ctx->maxmalloced = ctx->malloced;
+ }
+ if (ctx->basic_table_size != 0) {
+ memmove(table, ctx->basic_table,
+ ctx->basic_table_size *
+ sizeof(unsigned char *));
+ (ctx->memfree)(ctx->basic_table);
+ ctx->malloced -= ctx->basic_table_size *
+ sizeof(unsigned char *);
+ }
+ ctx->basic_table = table;
+ ctx->basic_table_size = table_size;
+ }
+
+ tmp = (ctx->memalloc)(NUM_BASIC_BLOCKS * ctx->mem_target);
+ ctx->total += NUM_BASIC_BLOCKS * ctx->mem_target;
+ ctx->basic_table[ctx->basic_table_count] = tmp;
+ ctx->basic_table_count++;
+ ctx->malloced += NUM_BASIC_BLOCKS * ctx->mem_target;
+ if (ctx->malloced > ctx->maxmalloced) {
+ ctx->maxmalloced = ctx->malloced;
+ }
+
+ curr = tmp;
+ next = curr + ctx->mem_target;
+ for (int i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
+ ((element *)curr)->next = (element *)next;
+ curr = next;
+ next += ctx->mem_target;
+ }
+ /*
+ * curr is now pointing at the last block in the
+ * array.
+ */
+ ((element *)curr)->next = NULL;
+ first = tmp;
+ last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1;
+ if (first < ctx->lowest || ctx->lowest == NULL) {
+ ctx->lowest = first;
+ }
+ if (last > ctx->highest) {
+ ctx->highest = last;
+ }
+ ctx->basic_blocks = tmp;
+}
+
+static void
+more_frags(isc__mem_t *ctx, size_t new_size) {
+ int frags;
+ size_t total_size;
+ void *tmp;
+ unsigned char *curr, *next;
+
+ /*!
+ * Try to get more fragments by chopping up a basic block.
+ */
+
+ if (ctx->basic_blocks == NULL) {
+ more_basic_blocks(ctx);
+ }
+ INSIST(ctx->basic_blocks != NULL);
+
+ total_size = ctx->mem_target;
+ tmp = ctx->basic_blocks;
+ ctx->basic_blocks = ctx->basic_blocks->next;
+ frags = (int)(total_size / new_size);
+ ctx->stats[new_size].blocks++;
+ ctx->stats[new_size].freefrags += frags;
+ /*
+ * Set up a linked-list of blocks of size
+ * "new_size".
+ */
+ curr = tmp;
+ next = curr + new_size;
+ total_size -= new_size;
+ for (int i = 0; i < (frags - 1); i++) {
+ ((element *)curr)->next = (element *)next;
+ curr = next;
+ next += new_size;
+ total_size -= new_size;
+ }
+ /*
+ * Add the remaining fragment of the basic block to a free list.
+ */
+ total_size = rmsize(total_size);
+ if (total_size > 0U) {
+ ((element *)next)->next = ctx->freelists[total_size];
+ ctx->freelists[total_size] = (element *)next;
+ ctx->stats[total_size].freefrags++;
+ }
+ /*
+ * curr is now pointing at the last block in the
+ * array.
+ */
+ ((element *)curr)->next = NULL;
+ ctx->freelists[new_size] = tmp;
+}
+
+static void *
+mem_getunlocked(isc__mem_t *ctx, size_t size) {
+ size_t new_size = quantize(size);
+ void *ret;
+
+ if (new_size >= ctx->max_size) {
+ /*
+ * memget() was called on something beyond our upper limit.
+ */
+ ret = (ctx->memalloc)(size);
+ ctx->total += size;
+ ctx->inuse += size;
+ ctx->stats[ctx->max_size].gets++;
+ ctx->stats[ctx->max_size].totalgets++;
+ ctx->malloced += size;
+ if (ctx->malloced > ctx->maxmalloced) {
+ ctx->maxmalloced = ctx->malloced;
+ }
+ /*
+ * If we don't set new_size to size, then the
+ * ISC_MEMFLAG_FILL code might write over bytes we don't
+ * own.
+ */
+ new_size = size;
+ goto done;
+ }
+ /*
+ * If there are no blocks in the free list for this size, get a chunk
+ * of memory and then break it up into "new_size"-sized blocks, adding
+ * them to the free list.
+ */
+ if (ctx->freelists[new_size] == NULL) {
+ more_frags(ctx, new_size);
+ }
+ INSIST(ctx->freelists[new_size] != NULL);
+
+ /*
+ * The free list uses the "rounded-up" size "new_size".
+ */
+
+ ret = ctx->freelists[new_size];
+ ctx->freelists[new_size] = ctx->freelists[new_size]->next;
+
+ /*
+ * The stats[] uses the _actual_ "size" requested by the
+ * caller, with the caveat (in the code above) that "size" >= the
+ * max. size (max_size) ends up getting recorded as a call to
+ * max_size.
+ */
+ ctx->stats[size].gets++;
+ ctx->stats[size].totalgets++;
+ ctx->stats[new_size].freefrags--;
+ ctx->inuse += new_size;
+
+done:
+ if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0) &&
+ ISC_LIKELY(ret != NULL))
+ {
+ memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
+ }
+
+ return (ret);
+}
+
+#if ISC_MEM_CHECKOVERRUN
+static void
+check_overrun(void *mem, size_t size, size_t new_size) {
+ unsigned char *cp;
+
+ cp = (unsigned char *)mem;
+ cp += size;
+ while (size < new_size) {
+ INSIST(*cp == 0xbe);
+ cp++;
+ size++;
+ }
+}
+#endif /* if ISC_MEM_CHECKOVERRUN */
+
+/* coverity[+free : arg-1] */
+static void
+mem_putunlocked(isc__mem_t *ctx, void *mem, size_t size) {
+ size_t new_size = quantize(size);
+
+ if (new_size >= ctx->max_size) {
+ /*
+ * memput() called on something beyond our upper limit.
+ */
+ if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0)) {
+ memset(mem, 0xde, size); /* Mnemonic for "dead". */
+ }
+
+ (ctx->memfree)(mem);
+ INSIST(ctx->stats[ctx->max_size].gets != 0U);
+ ctx->stats[ctx->max_size].gets--;
+ INSIST(size <= ctx->inuse);
+ ctx->inuse -= size;
+ ctx->malloced -= size;
+ return;
+ }
+
+ if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0)) {
+#if ISC_MEM_CHECKOVERRUN
+ check_overrun(mem, size, new_size);
+#endif /* if ISC_MEM_CHECKOVERRUN */
+ memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
+ }
+
+ /*
+ * The free list uses the "rounded-up" size "new_size".
+ */
+ ((element *)mem)->next = ctx->freelists[new_size];
+ ctx->freelists[new_size] = (element *)mem;
+
+ /*
+ * The stats[] uses the _actual_ "size" requested by the
+ * caller, with the caveat (in the code above) that "size" >= the
+ * max. size (max_size) ends up getting recorded as a call to
+ * max_size.
+ */
+ INSIST(ctx->stats[size].gets != 0U);
+ ctx->stats[size].gets--;
+ ctx->stats[new_size].freefrags++;
+ ctx->inuse -= new_size;
+}
+
+/*!
+ * Perform a malloc, doing memory filling and overrun detection as necessary.
+ */
+static void *
+mem_get(isc__mem_t *ctx, size_t size) {
+ char *ret;
+
+#if ISC_MEM_CHECKOVERRUN
+ size += 1;
+#endif /* if ISC_MEM_CHECKOVERRUN */
+ ret = (ctx->memalloc)(size);
+
+ if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0)) {
+ if (ISC_LIKELY(ret != NULL)) {
+ memset(ret, 0xbe, size); /* Mnemonic for "beef". */
+ }
+ }
+#if ISC_MEM_CHECKOVERRUN
+ else
+ {
+ if (ISC_LIKELY(ret != NULL)) {
+ ret[size - 1] = 0xbe;
+ }
+ }
+#endif /* if ISC_MEM_CHECKOVERRUN */
+
+ return (ret);
+}
+
+/*!
+ * Perform a free, doing memory filling and overrun detection as necessary.
+ */
+/* coverity[+free : arg-1] */
+static void
+mem_put(isc__mem_t *ctx, void *mem, size_t size) {
+#if ISC_MEM_CHECKOVERRUN
+ INSIST(((unsigned char *)mem)[size] == 0xbe);
+ size += 1;
+#endif /* if ISC_MEM_CHECKOVERRUN */
+ if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0)) {
+ memset(mem, 0xde, size); /* Mnemonic for "dead". */
+ }
+ (ctx->memfree)(mem);
+}
+
+/*!
+ * Update internal counters after a memory get.
+ */
+static void
+mem_getstats(isc__mem_t *ctx, size_t size) {
+ ctx->total += size;
+ ctx->inuse += size;
+
+ if (size > ctx->max_size) {
+ ctx->stats[ctx->max_size].gets++;
+ ctx->stats[ctx->max_size].totalgets++;
+ } else {
+ ctx->stats[size].gets++;
+ ctx->stats[size].totalgets++;
+ }
+
+#if ISC_MEM_CHECKOVERRUN
+ size += 1;
+#endif /* if ISC_MEM_CHECKOVERRUN */
+ ctx->malloced += size;
+ if (ctx->malloced > ctx->maxmalloced) {
+ ctx->maxmalloced = ctx->malloced;
+ }
+}
+
+/*!
+ * Update internal counters after a memory put.
+ */
+static void
+mem_putstats(isc__mem_t *ctx, void *ptr, size_t size) {
+ UNUSED(ptr);
+
+ INSIST(ctx->inuse >= size);
+ ctx->inuse -= size;
+
+ if (size > ctx->max_size) {
+ INSIST(ctx->stats[ctx->max_size].gets > 0U);
+ ctx->stats[ctx->max_size].gets--;
+ } else {
+ INSIST(ctx->stats[size].gets > 0U);
+ ctx->stats[size].gets--;
+ }
+#if ISC_MEM_CHECKOVERRUN
+ size += 1;
+#endif /* if ISC_MEM_CHECKOVERRUN */
+ ctx->malloced -= size;
+}
+
+/*
+ * Private.
+ */
+
+static void *
+default_memalloc(size_t size) {
+ void *ptr;
+
+ ptr = malloc(size);
+
+ /*
+ * If the space cannot be allocated, a null pointer is returned. If the
+ * size of the space requested is zero, the behavior is
+ * implementation-defined: either a null pointer is returned, or the
+ * behavior is as if the size were some nonzero value, except that the
+ * returned pointer shall not be used to access an object.
+ * [ISO9899 ยง 7.22.3]
+ *
+ * [ISO9899]
+ * ISO/IEC WG 9899:2011: Programming languages - C.
+ * International Organization for Standardization, Geneva,
+ * Switzerland.
+ * http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf
+ */
+
+ if (ptr == NULL && size != 0) {
+ char strbuf[ISC_STRERRORSIZE];
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ isc_error_fatal(__FILE__, __LINE__, "malloc failed: %s",
+ strbuf);
+ }
+
+ return (ptr);
+}
+
+static void
+default_memfree(void *ptr) {
+ free(ptr);
+}
+
+static void
+mem_initialize(void) {
+ isc_mutex_init(&contextslock);
+ ISC_LIST_INIT(contexts);
+ totallost = 0;
+}
+
+void
+isc__mem_initialize(void) {
+ RUNTIME_CHECK(isc_once_do(&init_once, mem_initialize) == ISC_R_SUCCESS);
+}
+
+static void
+mem_shutdown(void) {
+ isc__mem_checkdestroyed();
+
+ isc_mutex_destroy(&contextslock);
+}
+
+void
+isc__mem_shutdown(void) {
+ RUNTIME_CHECK(isc_once_do(&shut_once, mem_shutdown) == ISC_R_SUCCESS);
+}
+
+static void
+mem_create(isc_mem_t **ctxp, unsigned int flags) {
+ REQUIRE(ctxp != NULL && *ctxp == NULL);
+#if __SANITIZE_ADDRESS__
+ REQUIRE((flags & ISC_MEMFLAG_INTERNAL) == 0);
+#endif
+
+ isc__mem_t *ctx;
+
+ isc_enable_constructors();
+
+ STATIC_ASSERT((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0,
+ "wrong alignment size");
+
+ ctx = (default_memalloc)(sizeof(*ctx));
+
+ isc_mutex_init(&ctx->lock);
+
+ ctx->max_size = DEF_MAX_SIZE;
+ ctx->flags = flags;
+ isc_refcount_init(&ctx->references, 1);
+ memset(ctx->name, 0, sizeof(ctx->name));
+ ctx->tag = NULL;
+ ctx->total = 0;
+ ctx->inuse = 0;
+ ctx->maxinuse = 0;
+ ctx->malloced = sizeof(*ctx);
+ ctx->maxmalloced = sizeof(*ctx);
+ ctx->hi_water = 0;
+ ctx->lo_water = 0;
+ ctx->hi_called = false;
+ ctx->is_overmem = false;
+ ctx->water = NULL;
+ ctx->water_arg = NULL;
+ ctx->common.impmagic = MEM_MAGIC;
+ ctx->common.magic = ISCAPI_MCTX_MAGIC;
+ ctx->common.methods = (isc_memmethods_t *)&memmethods;
+ ctx->memalloc = default_memalloc;
+ ctx->memfree = default_memfree;
+ ctx->stats = NULL;
+ ctx->checkfree = true;
+#if ISC_MEM_TRACKLINES
+ ctx->debuglist = NULL;
+ ctx->debuglistcnt = 0;
+#endif /* if ISC_MEM_TRACKLINES */
+ ISC_LIST_INIT(ctx->pools);
+ ctx->poolcnt = 0;
+ ctx->freelists = NULL;
+ ctx->basic_blocks = NULL;
+ ctx->basic_table = NULL;
+ ctx->basic_table_count = 0;
+ ctx->basic_table_size = 0;
+ ctx->lowest = NULL;
+ ctx->highest = NULL;
+
+ ctx->stats =
+ (ctx->memalloc)((ctx->max_size + 1) * sizeof(struct stats));
+
+ memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats));
+ ctx->malloced += (ctx->max_size + 1) * sizeof(struct stats);
+ ctx->maxmalloced += (ctx->max_size + 1) * sizeof(struct stats);
+
+ if ((flags & ISC_MEMFLAG_INTERNAL) != 0) {
+ ctx->mem_target = DEF_MEM_TARGET;
+ ctx->freelists =
+ (ctx->memalloc)(ctx->max_size * sizeof(element *));
+ memset(ctx->freelists, 0, ctx->max_size * sizeof(element *));
+ ctx->malloced += ctx->max_size * sizeof(element *);
+ ctx->maxmalloced += ctx->max_size * sizeof(element *);
+ }
+
+#if ISC_MEM_TRACKLINES
+ if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0)) {
+ unsigned int i;
+
+ ctx->debuglist = (ctx->memalloc)(
+ (DEBUG_TABLE_COUNT * sizeof(debuglist_t)));
+ for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
+ ISC_LIST_INIT(ctx->debuglist[i]);
+ }
+ ctx->malloced += DEBUG_TABLE_COUNT * sizeof(debuglist_t);
+ ctx->maxmalloced += DEBUG_TABLE_COUNT * sizeof(debuglist_t);
+ }
+#endif /* if ISC_MEM_TRACKLINES */
+
+ LOCK(&contextslock);
+ ISC_LIST_INITANDAPPEND(contexts, ctx, link);
+ UNLOCK(&contextslock);
+
+ *ctxp = (isc_mem_t *)ctx;
+}
+
+/*
+ * Public.
+ */
+
+static void
+destroy(isc__mem_t *ctx) {
+ unsigned int i;
+
+ LOCK(&contextslock);
+ ISC_LIST_UNLINK(contexts, ctx, link);
+ totallost += ctx->inuse;
+ UNLOCK(&contextslock);
+
+ ctx->common.impmagic = 0;
+ ctx->common.magic = 0;
+
+ INSIST(ISC_LIST_EMPTY(ctx->pools));
+
+#if ISC_MEM_TRACKLINES
+ if (ISC_UNLIKELY(ctx->debuglist != NULL)) {
+ debuglink_t *dl;
+ for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
+ for (dl = ISC_LIST_HEAD(ctx->debuglist[i]); dl != NULL;
+ dl = ISC_LIST_HEAD(ctx->debuglist[i]))
+ {
+ if (ctx->checkfree && dl->ptr != NULL) {
+ print_active(ctx, stderr);
+ }
+ INSIST(!ctx->checkfree || dl->ptr == NULL);
+
+ ISC_LIST_UNLINK(ctx->debuglist[i], dl, link);
+ free(dl);
+ ctx->malloced -= sizeof(*dl);
+ }
+ }
+
+ (ctx->memfree)(ctx->debuglist);
+ ctx->malloced -= DEBUG_TABLE_COUNT * sizeof(debuglist_t);
+ }
+#endif /* if ISC_MEM_TRACKLINES */
+
+ if (ctx->checkfree) {
+ for (i = 0; i <= ctx->max_size; i++) {
+ if (ctx->stats[i].gets != 0U) {
+ fprintf(stderr,
+ "Failing assertion due to probable "
+ "leaked memory in context %p (\"%s\") "
+ "(stats[%u].gets == %lu).\n",
+ ctx, ctx->name, i, ctx->stats[i].gets);
+#if ISC_MEM_TRACKLINES
+ print_active(ctx, stderr);
+#endif /* if ISC_MEM_TRACKLINES */
+ INSIST(ctx->stats[i].gets == 0U);
+ }
+ }
+ }
+
+ (ctx->memfree)(ctx->stats);
+ ctx->malloced -= (ctx->max_size + 1) * sizeof(struct stats);
+
+ if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+ for (i = 0; i < ctx->basic_table_count; i++) {
+ (ctx->memfree)(ctx->basic_table[i]);
+ ctx->malloced -= NUM_BASIC_BLOCKS * ctx->mem_target;
+ }
+ (ctx->memfree)(ctx->freelists);
+ ctx->malloced -= ctx->max_size * sizeof(element *);
+ if (ctx->basic_table != NULL) {
+ (ctx->memfree)(ctx->basic_table);
+ ctx->malloced -= ctx->basic_table_size *
+ sizeof(unsigned char *);
+ }
+ }
+
+ isc_mutex_destroy(&ctx->lock);
+
+ ctx->malloced -= sizeof(*ctx);
+ if (ctx->checkfree) {
+ INSIST(ctx->malloced == 0);
+ }
+ (ctx->memfree)(ctx);
+}
+
+void
+isc_mem_attach(isc_mem_t *source0, isc_mem_t **targetp) {
+ REQUIRE(VALID_CONTEXT(source0));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc__mem_t *source = (isc__mem_t *)source0;
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = (isc_mem_t *)source;
+}
+
+void
+isc_mem_detach(isc_mem_t **ctxp) {
+ REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
+
+ isc__mem_t *ctx = (isc__mem_t *)*ctxp;
+ *ctxp = NULL;
+
+ if (isc_refcount_decrement(&ctx->references) == 1) {
+ isc_refcount_destroy(&ctx->references);
+ destroy(ctx);
+ }
+}
+
+/*
+ * isc_mem_putanddetach() is the equivalent of:
+ *
+ * mctx = NULL;
+ * isc_mem_attach(ptr->mctx, &mctx);
+ * isc_mem_detach(&ptr->mctx);
+ * isc_mem_put(mctx, ptr, sizeof(*ptr);
+ * isc_mem_detach(&mctx);
+ */
+
+void
+isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) {
+ REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
+ REQUIRE(ptr != NULL);
+
+ isc__mem_t *ctx = (isc__mem_t *)*ctxp;
+ *ctxp = NULL;
+
+ if (ISC_UNLIKELY((isc_mem_debugging &
+ (ISC_MEM_DEBUGSIZE | ISC_MEM_DEBUGCTX)) != 0))
+ {
+ if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
+ size_info *si = &(((size_info *)ptr)[-1]);
+ size_t oldsize = si->u.size - ALIGNMENT_SIZE;
+ if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
+ oldsize -= ALIGNMENT_SIZE;
+ }
+ INSIST(oldsize == size);
+ }
+ isc__mem_free((isc_mem_t *)ctx, ptr FLARG_PASS);
+
+ goto destroy;
+ }
+
+ MCTXLOCK(ctx);
+
+ DELETE_TRACE(ctx, ptr, size, file, line);
+
+ if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+ mem_putunlocked(ctx, ptr, size);
+ } else {
+ mem_putstats(ctx, ptr, size);
+ mem_put(ctx, ptr, size);
+ }
+ MCTXUNLOCK(ctx);
+
+destroy:
+ if (isc_refcount_decrement(&ctx->references) == 1) {
+ isc_refcount_destroy(&ctx->references);
+ destroy(ctx);
+ }
+}
+
+void
+isc_mem_destroy(isc_mem_t **ctxp) {
+ /*
+ * This routine provides legacy support for callers who use mctxs
+ * without attaching/detaching.
+ */
+
+ REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
+
+ isc__mem_t *ctx = (isc__mem_t *)*ctxp;
+
+#if ISC_MEM_TRACKLINES
+ if (isc_refcount_decrement(&ctx->references) > 1) {
+ print_active(ctx, stderr);
+ }
+#else /* if ISC_MEM_TRACKLINES */
+ isc_refcount_decrementz(&ctx->references);
+#endif /* if ISC_MEM_TRACKLINES */
+ isc_refcount_destroy(&ctx->references);
+ destroy(ctx);
+
+ *ctxp = NULL;
+}
+
+void *
+isc___mem_get(isc_mem_t *ctx0, size_t size FLARG) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+ void *ptr;
+ bool call_water = false;
+
+ if (ISC_UNLIKELY((isc_mem_debugging &
+ (ISC_MEM_DEBUGSIZE | ISC_MEM_DEBUGCTX)) != 0))
+ {
+ return (isc__mem_allocate(ctx0, size FLARG_PASS));
+ }
+
+ if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+ MCTXLOCK(ctx);
+ ptr = mem_getunlocked(ctx, size);
+ } else {
+ ptr = mem_get(ctx, size);
+ MCTXLOCK(ctx);
+ if (ptr != NULL) {
+ mem_getstats(ctx, size);
+ }
+ }
+
+ ADD_TRACE(ctx, ptr, size, file, line);
+
+ if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water) {
+ ctx->is_overmem = true;
+ if (!ctx->hi_called) {
+ call_water = true;
+ }
+ }
+ if (ctx->inuse > ctx->maxinuse) {
+ ctx->maxinuse = ctx->inuse;
+ if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
+ (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
+ {
+ fprintf(stderr, "maxinuse = %lu\n",
+ (unsigned long)ctx->inuse);
+ }
+ }
+ MCTXUNLOCK(ctx);
+
+ if (call_water && (ctx->water != NULL)) {
+ (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
+ }
+
+ return (ptr);
+}
+
+void
+isc___mem_put(isc_mem_t *ctx0, void *ptr, size_t size FLARG) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+ REQUIRE(ptr != NULL);
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+ bool call_water = false;
+ size_info *si;
+ size_t oldsize;
+
+ if (ISC_UNLIKELY((isc_mem_debugging &
+ (ISC_MEM_DEBUGSIZE | ISC_MEM_DEBUGCTX)) != 0))
+ {
+ if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
+ si = &(((size_info *)ptr)[-1]);
+ oldsize = si->u.size - ALIGNMENT_SIZE;
+ if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
+ oldsize -= ALIGNMENT_SIZE;
+ }
+ INSIST(oldsize == size);
+ }
+ isc__mem_free((isc_mem_t *)ctx, ptr FLARG_PASS);
+ return;
+ }
+
+ MCTXLOCK(ctx);
+
+ DELETE_TRACE(ctx, ptr, size, file, line);
+
+ if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+ mem_putunlocked(ctx, ptr, size);
+ } else {
+ mem_putstats(ctx, ptr, size);
+ mem_put(ctx, ptr, size);
+ }
+
+ /*
+ * The check against ctx->lo_water == 0 is for the condition
+ * when the context was pushed over hi_water but then had
+ * isc_mem_setwater() called with 0 for hi_water and lo_water.
+ */
+ if ((ctx->inuse < ctx->lo_water) || (ctx->lo_water == 0U)) {
+ ctx->is_overmem = false;
+ if (ctx->hi_called) {
+ call_water = true;
+ }
+ }
+
+ MCTXUNLOCK(ctx);
+
+ if (call_water && (ctx->water != NULL)) {
+ (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
+ }
+}
+
+void
+isc_mem_waterack(isc_mem_t *ctx0, int flag) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+
+ MCTXLOCK(ctx);
+ if (flag == ISC_MEM_LOWATER) {
+ ctx->hi_called = false;
+ } else if (flag == ISC_MEM_HIWATER) {
+ ctx->hi_called = true;
+ }
+ MCTXUNLOCK(ctx);
+}
+
+#if ISC_MEM_TRACKLINES
+static void
+print_active(isc__mem_t *mctx, FILE *out) {
+ if (mctx->debuglist != NULL) {
+ debuglink_t *dl;
+ unsigned int i;
+ bool found;
+
+ fputs("Dump of all outstanding memory allocations:\n", out);
+ found = false;
+ for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
+ dl = ISC_LIST_HEAD(mctx->debuglist[i]);
+
+ if (dl != NULL) {
+ found = true;
+ }
+
+ while (dl != NULL) {
+ if (dl->ptr != NULL) {
+ fprintf(out,
+ "\tptr %p size %zu file %s "
+ "line %u\n",
+ dl->ptr, dl->size, dl->file,
+ dl->line);
+ }
+ dl = ISC_LIST_NEXT(dl, link);
+ }
+ }
+
+ if (!found) {
+ fputs("\tNone.\n", out);
+ }
+ }
+}
+#endif /* if ISC_MEM_TRACKLINES */
+
+/*
+ * Print the stats[] on the stream "out" with suitable formatting.
+ */
+void
+isc_mem_stats(isc_mem_t *ctx0, FILE *out) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+ size_t i;
+ const struct stats *s;
+ const isc__mempool_t *pool;
+
+ MCTXLOCK(ctx);
+
+ for (i = 0; i <= ctx->max_size; i++) {
+ s = &ctx->stats[i];
+
+ if (s->totalgets == 0U && s->gets == 0U) {
+ continue;
+ }
+ fprintf(out, "%s%5lu: %11lu gets, %11lu rem",
+ (i == ctx->max_size) ? ">=" : " ", (unsigned long)i,
+ s->totalgets, s->gets);
+ if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0 &&
+ (s->blocks != 0U || s->freefrags != 0U))
+ {
+ fprintf(out, " (%lu bl, %lu ff)", s->blocks,
+ s->freefrags);
+ }
+ fputc('\n', out);
+ }
+
+ /*
+ * Note that since a pool can be locked now, these stats might be
+ * somewhat off if the pool is in active use at the time the stats
+ * are dumped. The link fields are protected by the isc_mem_t's
+ * lock, however, so walking this list and extracting integers from
+ * stats fields is always safe.
+ */
+ pool = ISC_LIST_HEAD(ctx->pools);
+ if (pool != NULL) {
+ fputs("[Pool statistics]\n", out);
+ fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %10s %1s\n",
+ "name", "size", "maxalloc", "allocated", "freecount",
+ "freemax", "fillcount", "gets", "L");
+ }
+ while (pool != NULL) {
+ fprintf(out, "%15s %10lu %10u %10u %10u %10u %10u %10u %s\n",
+#if ISC_MEMPOOL_NAMES
+ pool->name,
+#else /* if ISC_MEMPOOL_NAMES */
+ "(not tracked)",
+#endif /* if ISC_MEMPOOL_NAMES */
+ (unsigned long)pool->size, pool->maxalloc,
+ pool->allocated, pool->freecount, pool->freemax,
+ pool->fillcount, pool->gets, "N");
+ pool = ISC_LIST_NEXT(pool, link);
+ }
+
+#if ISC_MEM_TRACKLINES
+ print_active(ctx, out);
+#endif /* if ISC_MEM_TRACKLINES */
+
+ MCTXUNLOCK(ctx);
+}
+
+/*
+ * Replacements for malloc() and free() -- they implicitly remember the
+ * size of the object allocated (with some additional overhead).
+ */
+
+static void *
+mem_allocateunlocked(isc_mem_t *ctx0, size_t size) {
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+ size_info *si;
+
+ size += ALIGNMENT_SIZE;
+ if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)) {
+ size += ALIGNMENT_SIZE;
+ }
+
+ if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+ si = mem_getunlocked(ctx, size);
+ } else {
+ si = mem_get(ctx, size);
+ }
+
+ if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)) {
+ si->u.ctx = ctx;
+ si++;
+ }
+ si->u.size = size;
+ return (&si[1]);
+}
+
+void *
+isc___mem_allocate(isc_mem_t *ctx0, size_t size FLARG) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+ size_info *si;
+ bool call_water = false;
+
+ MCTXLOCK(ctx);
+ si = mem_allocateunlocked((isc_mem_t *)ctx, size);
+ if (((ctx->flags & ISC_MEMFLAG_INTERNAL) == 0)) {
+ mem_getstats(ctx, si[-1].u.size);
+ }
+
+ ADD_TRACE(ctx, si, si[-1].u.size, file, line);
+ if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
+ !ctx->is_overmem)
+ {
+ ctx->is_overmem = true;
+ }
+
+ if (ctx->hi_water != 0U && !ctx->hi_called &&
+ ctx->inuse > ctx->hi_water)
+ {
+ ctx->hi_called = true;
+ call_water = true;
+ }
+ if (ctx->inuse > ctx->maxinuse) {
+ ctx->maxinuse = ctx->inuse;
+ if (ISC_UNLIKELY(ctx->hi_water != 0U &&
+ ctx->inuse > ctx->hi_water &&
+ (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0))
+ {
+ fprintf(stderr, "maxinuse = %lu\n",
+ (unsigned long)ctx->inuse);
+ }
+ }
+ MCTXUNLOCK(ctx);
+
+ if (call_water) {
+ (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
+ }
+
+ return (si);
+}
+
+void *
+isc___mem_reallocate(isc_mem_t *ctx0, void *ptr, size_t size FLARG) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ void *new_ptr = NULL;
+ size_t oldsize, copysize;
+
+ /*
+ * This function emulates the realloc(3) standard library function:
+ * - if size > 0, allocate new memory; and if ptr is non NULL, copy
+ * as much of the old contents to the new buffer and free the old one.
+ * Note that when allocation fails the original pointer is intact;
+ * the caller must free it.
+ * - if size is 0 and ptr is non NULL, simply free the given ptr.
+ * - this function returns:
+ * pointer to the newly allocated memory, or
+ * NULL if allocation fails or doesn't happen.
+ */
+ if (size > 0U) {
+ new_ptr = isc__mem_allocate(ctx0, size FLARG_PASS);
+ if (new_ptr != NULL && ptr != NULL) {
+ oldsize = (((size_info *)ptr)[-1]).u.size;
+ INSIST(oldsize >= ALIGNMENT_SIZE);
+ oldsize -= ALIGNMENT_SIZE;
+ if (ISC_UNLIKELY((isc_mem_debugging &
+ ISC_MEM_DEBUGCTX) != 0))
+ {
+ INSIST(oldsize >= ALIGNMENT_SIZE);
+ oldsize -= ALIGNMENT_SIZE;
+ }
+ copysize = (oldsize > size) ? size : oldsize;
+ memmove(new_ptr, ptr, copysize);
+ isc__mem_free(ctx0, ptr FLARG_PASS);
+ }
+ } else if (ptr != NULL) {
+ isc__mem_free(ctx0, ptr FLARG_PASS);
+ }
+
+ return (new_ptr);
+}
+
+void
+isc___mem_free(isc_mem_t *ctx0, void *ptr FLARG) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+ REQUIRE(ptr != NULL);
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+ size_info *si;
+ size_t size;
+ bool call_water = false;
+
+ if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)) {
+ si = &(((size_info *)ptr)[-2]);
+ REQUIRE(si->u.ctx == ctx);
+ size = si[1].u.size;
+ } else {
+ si = &(((size_info *)ptr)[-1]);
+ size = si->u.size;
+ }
+
+ MCTXLOCK(ctx);
+
+ DELETE_TRACE(ctx, ptr, size, file, line);
+
+ if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+ mem_putunlocked(ctx, si, size);
+ } else {
+ mem_putstats(ctx, si, size);
+ mem_put(ctx, si, size);
+ }
+
+ /*
+ * The check against ctx->lo_water == 0 is for the condition
+ * when the context was pushed over hi_water but then had
+ * isc_mem_setwater() called with 0 for hi_water and lo_water.
+ */
+ if (ctx->is_overmem &&
+ (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U))
+ {
+ ctx->is_overmem = false;
+ }
+
+ if (ctx->hi_called &&
+ (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U))
+ {
+ ctx->hi_called = false;
+
+ if (ctx->water != NULL) {
+ call_water = true;
+ }
+ }
+ MCTXUNLOCK(ctx);
+
+ if (call_water) {
+ (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
+ }
+}
+
+/*
+ * Other useful things.
+ */
+
+char *
+isc___mem_strdup(isc_mem_t *mctx0, const char *s FLARG) {
+ REQUIRE(VALID_CONTEXT(mctx0));
+ REQUIRE(s != NULL);
+
+ isc__mem_t *mctx = (isc__mem_t *)mctx0;
+ size_t len;
+ char *ns;
+
+ len = strlen(s) + 1;
+
+ ns = isc__mem_allocate((isc_mem_t *)mctx, len FLARG_PASS);
+
+ if (ns != NULL) {
+ strlcpy(ns, s, len);
+ }
+
+ return (ns);
+}
+
+char *
+isc___mem_strndup(isc_mem_t *mctx0, const char *s, size_t size FLARG) {
+ REQUIRE(VALID_CONTEXT(mctx0));
+ REQUIRE(s != NULL);
+
+ isc__mem_t *mctx = (isc__mem_t *)mctx0;
+ size_t len;
+ char *ns;
+
+ len = strlen(s) + 1;
+ if (len > size) {
+ len = size;
+ }
+
+ ns = isc__mem_allocate((isc_mem_t *)mctx, len FLARG_PASS);
+
+ if (ns != NULL) {
+ strlcpy(ns, s, len);
+ }
+
+ return (ns);
+}
+
+void
+isc_mem_setdestroycheck(isc_mem_t *ctx0, bool flag) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+
+ MCTXLOCK(ctx);
+
+ ctx->checkfree = flag;
+
+ MCTXUNLOCK(ctx);
+}
+
+size_t
+isc_mem_inuse(isc_mem_t *ctx0) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+ size_t inuse;
+
+ MCTXLOCK(ctx);
+
+ inuse = ctx->inuse;
+
+ MCTXUNLOCK(ctx);
+
+ return (inuse);
+}
+
+size_t
+isc_mem_maxinuse(isc_mem_t *ctx0) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+ size_t maxinuse;
+
+ MCTXLOCK(ctx);
+
+ maxinuse = ctx->maxinuse;
+
+ MCTXUNLOCK(ctx);
+
+ return (maxinuse);
+}
+
+size_t
+isc_mem_total(isc_mem_t *ctx0) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+ size_t total;
+
+ MCTXLOCK(ctx);
+
+ total = ctx->total;
+
+ MCTXUNLOCK(ctx);
+
+ return (total);
+}
+
+void
+isc_mem_setwater(isc_mem_t *ctx0, isc_mem_water_t water, void *water_arg,
+ size_t hiwater, size_t lowater) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+ REQUIRE(hiwater >= lowater);
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+ bool callwater = false;
+ isc_mem_water_t oldwater;
+ void *oldwater_arg;
+
+ MCTXLOCK(ctx);
+ oldwater = ctx->water;
+ oldwater_arg = ctx->water_arg;
+ if (water == NULL) {
+ callwater = ctx->hi_called;
+ ctx->water = NULL;
+ ctx->water_arg = NULL;
+ ctx->hi_water = 0;
+ ctx->lo_water = 0;
+ } else {
+ if (ctx->hi_called &&
+ (ctx->water != water || ctx->water_arg != water_arg ||
+ ctx->inuse < lowater || lowater == 0U))
+ {
+ callwater = true;
+ }
+ ctx->water = water;
+ ctx->water_arg = water_arg;
+ ctx->hi_water = hiwater;
+ ctx->lo_water = lowater;
+ }
+ MCTXUNLOCK(ctx);
+
+ if (callwater && oldwater != NULL) {
+ (oldwater)(oldwater_arg, ISC_MEM_LOWATER);
+ }
+}
+
+ISC_NO_SANITIZE_THREAD bool
+isc_mem_isovermem(isc_mem_t *ctx0) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+
+ /*
+ * We don't bother to lock the context because 100% accuracy isn't
+ * necessary (and even if we locked the context the returned value
+ * could be different from the actual state when it's used anyway)
+ */
+ return (ctx->is_overmem);
+}
+
+void
+isc_mem_setname(isc_mem_t *ctx0, const char *name, void *tag) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+
+ LOCK(&ctx->lock);
+ strlcpy(ctx->name, name, sizeof(ctx->name));
+ ctx->tag = tag;
+ UNLOCK(&ctx->lock);
+}
+
+const char *
+isc_mem_getname(isc_mem_t *ctx0) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+
+ if (ctx->name[0] == 0) {
+ return ("");
+ }
+
+ return (ctx->name);
+}
+
+void *
+isc_mem_gettag(isc_mem_t *ctx0) {
+ REQUIRE(VALID_CONTEXT(ctx0));
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+
+ return (ctx->tag);
+}
+
+/*
+ * Memory pool stuff
+ */
+
+void
+isc_mempool_create(isc_mem_t *mctx0, size_t size, isc_mempool_t **mpctxp) {
+ REQUIRE(VALID_CONTEXT(mctx0));
+ REQUIRE(size > 0U);
+ REQUIRE(mpctxp != NULL && *mpctxp == NULL);
+
+ isc__mem_t *mctx = (isc__mem_t *)mctx0;
+ isc__mempool_t *mpctx;
+
+ /*
+ * Allocate space for this pool, initialize values, and if all works
+ * well, attach to the memory context.
+ */
+ mpctx = isc_mem_get((isc_mem_t *)mctx, sizeof(isc__mempool_t));
+
+ mpctx->common.impmagic = MEMPOOL_MAGIC;
+ mpctx->common.magic = ISCAPI_MPOOL_MAGIC;
+ mpctx->mctx = NULL;
+ isc_mem_attach((isc_mem_t *)mctx, (isc_mem_t **)&mpctx->mctx);
+ /*
+ * Mempools are stored as a linked list of element.
+ */
+ if (size < sizeof(element)) {
+ size = sizeof(element);
+ }
+ mpctx->size = size;
+ mpctx->maxalloc = UINT_MAX;
+ mpctx->allocated = 0;
+ mpctx->freecount = 0;
+ mpctx->freemax = 1;
+ mpctx->fillcount = 1;
+ mpctx->gets = 0;
+#if ISC_MEMPOOL_NAMES
+ mpctx->name[0] = 0;
+#endif /* if ISC_MEMPOOL_NAMES */
+ mpctx->items = NULL;
+
+ *mpctxp = (isc_mempool_t *)mpctx;
+
+ MCTXLOCK(mctx);
+ ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link);
+ mctx->poolcnt++;
+ MCTXUNLOCK(mctx);
+}
+
+void
+isc_mempool_setname(isc_mempool_t *mpctx0, const char *name) {
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+ REQUIRE(name != NULL);
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+
+#if ISC_MEMPOOL_NAMES
+ strlcpy(mpctx->name, name, sizeof(mpctx->name));
+#else /* if ISC_MEMPOOL_NAMES */
+ UNUSED(mpctx);
+ UNUSED(name);
+#endif /* if ISC_MEMPOOL_NAMES */
+}
+
+void
+isc_mempool_destroy(isc_mempool_t **mpctxp) {
+ REQUIRE(mpctxp != NULL);
+ REQUIRE(VALID_MEMPOOL(*mpctxp));
+
+ isc__mempool_t *mpctx;
+ isc__mem_t *mctx;
+ element *item;
+
+ mpctx = (isc__mempool_t *)*mpctxp;
+#if ISC_MEMPOOL_NAMES
+ if (mpctx->allocated > 0) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_mempool_destroy(): mempool %s "
+ "leaked memory",
+ mpctx->name);
+ }
+#endif /* if ISC_MEMPOOL_NAMES */
+ REQUIRE(mpctx->allocated == 0);
+
+ mctx = mpctx->mctx;
+
+ /*
+ * Return any items on the free list
+ */
+ MCTXLOCK(mctx);
+ while (mpctx->items != NULL) {
+ INSIST(mpctx->freecount > 0);
+ mpctx->freecount--;
+ item = mpctx->items;
+ mpctx->items = item->next;
+ mem_putstats(mctx, item, mpctx->size);
+ mem_put(mctx, item, mpctx->size);
+ }
+ MCTXUNLOCK(mctx);
+
+ /*
+ * Remove our linked list entry from the memory context.
+ */
+ MCTXLOCK(mctx);
+ ISC_LIST_UNLINK(mctx->pools, mpctx, link);
+ mctx->poolcnt--;
+ MCTXUNLOCK(mctx);
+
+ mpctx->common.impmagic = 0;
+ mpctx->common.magic = 0;
+
+ isc_mem_putanddetach((isc_mem_t **)&mpctx->mctx, mpctx,
+ sizeof(isc__mempool_t));
+
+ *mpctxp = NULL;
+}
+
+#if __SANITIZE_ADDRESS__
+void *
+isc__mempool_get(isc_mempool_t *mpctx0 FLARG) {
+ void *item = NULL;
+
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+ isc_mem_t *mctx = (isc_mem_t *)mpctx->mctx;
+
+ /*
+ * Don't let the caller go over quota
+ */
+ if (ISC_UNLIKELY(mpctx->allocated >= mpctx->maxalloc)) {
+ goto out;
+ }
+
+ item = isc__mem_get(mctx, mpctx->size FLARG_PASS);
+ mpctx->gets++;
+ mpctx->allocated++;
+
+out:
+ return (item);
+}
+
+void
+isc__mempool_put(isc_mempool_t *mpctx0, void *mem FLARG) {
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+ isc_mem_t *mctx = (isc_mem_t *)mpctx->mctx;
+
+ REQUIRE(mem != NULL);
+
+ INSIST(mpctx->allocated > 0);
+ mpctx->allocated--;
+
+ isc__mem_put(mctx, mem, mpctx->size FLARG_PASS);
+}
+
+#else /* __SANITIZE_ADDRESS__ */
+void *
+isc__mempool_get(isc_mempool_t *mpctx0 FLARG) {
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+ element *item;
+ isc__mem_t *mctx;
+ unsigned int i;
+
+ mctx = mpctx->mctx;
+
+ /*
+ * Don't let the caller go over quota
+ */
+ if (ISC_UNLIKELY(mpctx->allocated >= mpctx->maxalloc)) {
+ item = NULL;
+ goto out;
+ }
+
+ if (ISC_UNLIKELY(mpctx->items == NULL)) {
+ /*
+ * We need to dip into the well. Lock the memory context
+ * here and fill up our free list.
+ */
+ MCTXLOCK(mctx);
+ for (i = 0; i < mpctx->fillcount; i++) {
+ item = mem_get(mctx, mpctx->size);
+ mem_getstats(mctx, mpctx->size);
+ item->next = mpctx->items;
+ mpctx->items = item;
+ mpctx->freecount++;
+ }
+ MCTXUNLOCK(mctx);
+ }
+
+ /*
+ * If we didn't get any items, return NULL.
+ */
+ item = mpctx->items;
+ if (ISC_UNLIKELY(item == NULL)) {
+ goto out;
+ }
+
+ mpctx->items = item->next;
+ INSIST(mpctx->freecount > 0);
+ mpctx->freecount--;
+ mpctx->gets++;
+ mpctx->allocated++;
+
+out:
+#if ISC_MEM_TRACKLINES
+ if (ISC_UNLIKELY(((isc_mem_debugging & TRACE_OR_RECORD) != 0) &&
+ item != NULL))
+ {
+ MCTXLOCK(mctx);
+ ADD_TRACE(mctx, item, mpctx->size, file, line);
+ MCTXUNLOCK(mctx);
+ }
+#endif /* ISC_MEM_TRACKLINES */
+
+ return (item);
+}
+
+/* coverity[+free : arg-1] */
+void
+isc__mempool_put(isc_mempool_t *mpctx0, void *mem FLARG) {
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+ REQUIRE(mem != NULL);
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+ isc__mem_t *mctx = mpctx->mctx;
+ element *item;
+
+ INSIST(mpctx->allocated > 0);
+ mpctx->allocated--;
+
+#if ISC_MEM_TRACKLINES
+ if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0)) {
+ MCTXLOCK(mctx);
+ DELETE_TRACE(mctx, mem, mpctx->size, file, line);
+ MCTXUNLOCK(mctx);
+ }
+#endif /* ISC_MEM_TRACKLINES */
+
+ /*
+ * If our free list is full, return this to the mctx directly.
+ */
+ if (mpctx->freecount >= mpctx->freemax) {
+ MCTXLOCK(mctx);
+ mem_putstats(mctx, mem, mpctx->size);
+ mem_put(mctx, mem, mpctx->size);
+ MCTXUNLOCK(mctx);
+ return;
+ }
+
+ /*
+ * Otherwise, attach it to our free list and bump the counter.
+ */
+ mpctx->freecount++;
+ item = (element *)mem;
+ item->next = mpctx->items;
+ mpctx->items = item;
+}
+
+#endif /* __SANITIZE_ADDRESS__ */
+
+/*
+ * Quotas
+ */
+
+void
+isc_mempool_setfreemax(isc_mempool_t *mpctx0, unsigned int limit) {
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+
+ mpctx->freemax = limit;
+}
+
+unsigned int
+isc_mempool_getfreemax(isc_mempool_t *mpctx0) {
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+
+ return (mpctx->freemax);
+}
+
+unsigned int
+isc_mempool_getfreecount(isc_mempool_t *mpctx0) {
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+
+ return (mpctx->freecount);
+}
+
+void
+isc_mempool_setmaxalloc(isc_mempool_t *mpctx0, unsigned int limit) {
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+ REQUIRE(limit > 0);
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+
+ mpctx->maxalloc = limit;
+}
+
+unsigned int
+isc_mempool_getmaxalloc(isc_mempool_t *mpctx0) {
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+
+ return (mpctx->maxalloc);
+}
+
+unsigned int
+isc_mempool_getallocated(isc_mempool_t *mpctx0) {
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+
+ return (mpctx->allocated);
+}
+
+void
+isc_mempool_setfillcount(isc_mempool_t *mpctx0, unsigned int limit) {
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+ REQUIRE(limit > 0);
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+
+ mpctx->fillcount = limit;
+}
+
+unsigned int
+isc_mempool_getfillcount(isc_mempool_t *mpctx0) {
+ REQUIRE(VALID_MEMPOOL(mpctx0));
+
+ isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
+
+ return (mpctx->fillcount);
+}
+
+/*
+ * Requires contextslock to be held by caller.
+ */
+static void
+print_contexts(FILE *file) {
+ isc__mem_t *ctx;
+
+ for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
+ ctx = ISC_LIST_NEXT(ctx, link))
+ {
+ fprintf(file, "context: %p (%s): %" PRIuFAST32 " references\n",
+ ctx, ctx->name[0] == 0 ? "<unknown>" : ctx->name,
+ isc_refcount_current(&ctx->references));
+ print_active(ctx, file);
+ }
+ fflush(file);
+}
+
+static atomic_uintptr_t checkdestroyed = 0;
+
+void
+isc_mem_checkdestroyed(FILE *file) {
+ atomic_store_release(&checkdestroyed, (uintptr_t)file);
+}
+
+void
+isc__mem_checkdestroyed(void) {
+ FILE *file = (FILE *)atomic_load_acquire(&checkdestroyed);
+
+ if (file == NULL) {
+ return;
+ }
+
+ LOCK(&contextslock);
+ if (!ISC_LIST_EMPTY(contexts)) {
+#if ISC_MEM_TRACKLINES
+ if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0)) {
+ print_contexts(file);
+ }
+#endif /* if ISC_MEM_TRACKLINES */
+ UNREACHABLE();
+ }
+ UNLOCK(&contextslock);
+}
+
+unsigned int
+isc_mem_references(isc_mem_t *ctx0) {
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+ return (isc_refcount_current(&ctx->references));
+}
+
+typedef struct summarystat {
+ uint64_t total;
+ uint64_t inuse;
+ uint64_t malloced;
+ uint64_t blocksize;
+ uint64_t contextsize;
+} summarystat_t;
+
+#ifdef HAVE_LIBXML2
+#define TRY0(a) \
+ do { \
+ xmlrc = (a); \
+ if (xmlrc < 0) \
+ goto error; \
+ } while (0)
+static int
+xml_renderctx(isc__mem_t *ctx, summarystat_t *summary,
+ xmlTextWriterPtr writer) {
+ REQUIRE(VALID_CONTEXT(ctx));
+
+ int xmlrc;
+
+ MCTXLOCK(ctx);
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "context"));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%p", ctx));
+ TRY0(xmlTextWriterEndElement(writer)); /* id */
+
+ if (ctx->name[0] != 0) {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%s", ctx->name));
+ TRY0(xmlTextWriterEndElement(writer)); /* name */
+ }
+
+ summary->contextsize += sizeof(*ctx) +
+ (ctx->max_size + 1) * sizeof(struct stats) +
+ ctx->max_size * sizeof(element *) +
+ ctx->basic_table_count * sizeof(char *);
+#if ISC_MEM_TRACKLINES
+ if (ctx->debuglist != NULL) {
+ summary->contextsize += DEBUG_TABLE_COUNT *
+ sizeof(debuglist_t) +
+ ctx->debuglistcnt * sizeof(debuglink_t);
+ }
+#endif /* if ISC_MEM_TRACKLINES */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references"));
+ TRY0(xmlTextWriterWriteFormatString(
+ writer, "%" PRIuFAST32,
+ isc_refcount_current(&ctx->references)));
+ TRY0(xmlTextWriterEndElement(writer)); /* references */
+
+ summary->total += ctx->total;
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "total"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ (uint64_t)ctx->total));
+ TRY0(xmlTextWriterEndElement(writer)); /* total */
+
+ summary->inuse += ctx->inuse;
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ (uint64_t)ctx->inuse));
+ TRY0(xmlTextWriterEndElement(writer)); /* inuse */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxinuse"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ (uint64_t)ctx->maxinuse));
+ TRY0(xmlTextWriterEndElement(writer)); /* maxinuse */
+
+ summary->malloced += ctx->malloced;
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "malloced"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ (uint64_t)ctx->malloced));
+ TRY0(xmlTextWriterEndElement(writer)); /* malloced */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxmalloced"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ (uint64_t)ctx->maxmalloced));
+ TRY0(xmlTextWriterEndElement(writer)); /* maxmalloced */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "blocksize"));
+ if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+ summary->blocksize += ctx->basic_table_count *
+ NUM_BASIC_BLOCKS * ctx->mem_target;
+ TRY0(xmlTextWriterWriteFormatString(
+ writer, "%" PRIu64 "",
+ (uint64_t)ctx->basic_table_count * NUM_BASIC_BLOCKS *
+ ctx->mem_target));
+ } else {
+ TRY0(xmlTextWriterWriteFormatString(writer, "%s", "-"));
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* blocksize */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt));
+ TRY0(xmlTextWriterEndElement(writer)); /* pools */
+ summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ (uint64_t)ctx->hi_water));
+ TRY0(xmlTextWriterEndElement(writer)); /* hiwater */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ (uint64_t)ctx->lo_water));
+ TRY0(xmlTextWriterEndElement(writer)); /* lowater */
+
+ TRY0(xmlTextWriterEndElement(writer)); /* context */
+
+error:
+ MCTXUNLOCK(ctx);
+
+ return (xmlrc);
+}
+
+int
+isc_mem_renderxml(void *writer0) {
+ isc__mem_t *ctx;
+ summarystat_t summary;
+ uint64_t lost;
+ int xmlrc;
+ xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0;
+
+ memset(&summary, 0, sizeof(summary));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts"));
+
+ LOCK(&contextslock);
+ lost = totallost;
+ for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
+ ctx = ISC_LIST_NEXT(ctx, link))
+ {
+ xmlrc = xml_renderctx(ctx, &summary, writer);
+ if (xmlrc < 0) {
+ UNLOCK(&contextslock);
+ goto error;
+ }
+ }
+ UNLOCK(&contextslock);
+
+ TRY0(xmlTextWriterEndElement(writer)); /* contexts */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary"));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "TotalUse"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ summary.total));
+ TRY0(xmlTextWriterEndElement(writer)); /* TotalUse */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ summary.inuse));
+ TRY0(xmlTextWriterEndElement(writer)); /* InUse */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Malloced"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ summary.malloced));
+ TRY0(xmlTextWriterEndElement(writer)); /* InUse */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "BlockSize"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ summary.blocksize));
+ TRY0(xmlTextWriterEndElement(writer)); /* BlockSize */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ContextSize"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
+ summary.contextsize));
+ TRY0(xmlTextWriterEndElement(writer)); /* ContextSize */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Lost"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", lost));
+ TRY0(xmlTextWriterEndElement(writer)); /* Lost */
+
+ TRY0(xmlTextWriterEndElement(writer)); /* summary */
+error:
+ return (xmlrc);
+}
+
+#endif /* HAVE_LIBXML2 */
+
+#ifdef HAVE_JSON_C
+#define CHECKMEM(m) RUNTIME_CHECK(m != NULL)
+
+static isc_result_t
+json_renderctx(isc__mem_t *ctx, summarystat_t *summary, json_object *array) {
+ REQUIRE(VALID_CONTEXT(ctx));
+ REQUIRE(summary != NULL);
+ REQUIRE(array != NULL);
+
+ json_object *ctxobj, *obj;
+ char buf[1024];
+
+ MCTXLOCK(ctx);
+
+ summary->contextsize += sizeof(*ctx) +
+ (ctx->max_size + 1) * sizeof(struct stats) +
+ ctx->max_size * sizeof(element *) +
+ ctx->basic_table_count * sizeof(char *);
+ summary->total += ctx->total;
+ summary->inuse += ctx->inuse;
+ summary->malloced += ctx->malloced;
+ if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+ summary->blocksize += ctx->basic_table_count *
+ NUM_BASIC_BLOCKS * ctx->mem_target;
+ }
+#if ISC_MEM_TRACKLINES
+ if (ctx->debuglist != NULL) {
+ summary->contextsize += DEBUG_TABLE_COUNT *
+ sizeof(debuglist_t) +
+ ctx->debuglistcnt * sizeof(debuglink_t);
+ }
+#endif /* if ISC_MEM_TRACKLINES */
+
+ ctxobj = json_object_new_object();
+ CHECKMEM(ctxobj);
+
+ snprintf(buf, sizeof(buf), "%p", ctx);
+ obj = json_object_new_string(buf);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "id", obj);
+
+ if (ctx->name[0] != 0) {
+ obj = json_object_new_string(ctx->name);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "name", obj);
+ }
+
+ obj = json_object_new_int64(isc_refcount_current(&ctx->references));
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "references", obj);
+
+ obj = json_object_new_int64(ctx->total);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "total", obj);
+
+ obj = json_object_new_int64(ctx->inuse);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "inuse", obj);
+
+ obj = json_object_new_int64(ctx->maxinuse);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "maxinuse", obj);
+
+ obj = json_object_new_int64(ctx->malloced);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "malloced", obj);
+
+ obj = json_object_new_int64(ctx->maxmalloced);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "maxmalloced", obj);
+
+ if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+ uint64_t blocksize;
+ blocksize = ctx->basic_table_count * NUM_BASIC_BLOCKS *
+ ctx->mem_target;
+ obj = json_object_new_int64(blocksize);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "blocksize", obj);
+ }
+
+ obj = json_object_new_int64(ctx->poolcnt);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "pools", obj);
+
+ summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
+
+ obj = json_object_new_int64(ctx->hi_water);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "hiwater", obj);
+
+ obj = json_object_new_int64(ctx->lo_water);
+ CHECKMEM(obj);
+ json_object_object_add(ctxobj, "lowater", obj);
+
+ MCTXUNLOCK(ctx);
+ json_object_array_add(array, ctxobj);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_mem_renderjson(void *memobj0) {
+ isc_result_t result = ISC_R_SUCCESS;
+ isc__mem_t *ctx;
+ summarystat_t summary;
+ uint64_t lost;
+ json_object *ctxarray, *obj;
+ json_object *memobj = (json_object *)memobj0;
+
+ memset(&summary, 0, sizeof(summary));
+
+ ctxarray = json_object_new_array();
+ CHECKMEM(ctxarray);
+
+ LOCK(&contextslock);
+ lost = totallost;
+ for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
+ ctx = ISC_LIST_NEXT(ctx, link))
+ {
+ result = json_renderctx(ctx, &summary, ctxarray);
+ if (result != ISC_R_SUCCESS) {
+ UNLOCK(&contextslock);
+ goto error;
+ }
+ }
+ UNLOCK(&contextslock);
+
+ obj = json_object_new_int64(summary.total);
+ CHECKMEM(obj);
+ json_object_object_add(memobj, "TotalUse", obj);
+
+ obj = json_object_new_int64(summary.inuse);
+ CHECKMEM(obj);
+ json_object_object_add(memobj, "InUse", obj);
+
+ obj = json_object_new_int64(summary.malloced);
+ CHECKMEM(obj);
+ json_object_object_add(memobj, "Malloced", obj);
+
+ obj = json_object_new_int64(summary.blocksize);
+ CHECKMEM(obj);
+ json_object_object_add(memobj, "BlockSize", obj);
+
+ obj = json_object_new_int64(summary.contextsize);
+ CHECKMEM(obj);
+ json_object_object_add(memobj, "ContextSize", obj);
+
+ obj = json_object_new_int64(lost);
+ CHECKMEM(obj);
+ json_object_object_add(memobj, "Lost", obj);
+
+ json_object_object_add(memobj, "contexts", ctxarray);
+ return (ISC_R_SUCCESS);
+
+error:
+ if (ctxarray != NULL) {
+ json_object_put(ctxarray);
+ }
+ return (result);
+}
+#endif /* HAVE_JSON_C */
+
+void
+isc_mem_create(isc_mem_t **mctxp) {
+ mem_create(mctxp, isc_mem_defaultflags);
+}
+
+void *
+isc__mem_get(isc_mem_t *mctx, size_t size FLARG) {
+ REQUIRE(ISCAPI_MCTX_VALID(mctx));
+
+ return (mctx->methods->memget(mctx, size FLARG_PASS));
+}
+
+void
+isc__mem_put(isc_mem_t *mctx, void *ptr, size_t size FLARG) {
+ REQUIRE(ISCAPI_MCTX_VALID(mctx));
+
+ mctx->methods->memput(mctx, ptr, size FLARG_PASS);
+}
+
+void
+isc__mem_putanddetach(isc_mem_t **mctxp, void *ptr, size_t size FLARG) {
+ REQUIRE(mctxp != NULL && ISCAPI_MCTX_VALID(*mctxp));
+
+ (*mctxp)->methods->memputanddetach(mctxp, ptr, size FLARG_PASS);
+}
+
+void *
+isc__mem_allocate(isc_mem_t *mctx, size_t size FLARG) {
+ REQUIRE(ISCAPI_MCTX_VALID(mctx));
+
+ return (mctx->methods->memallocate(mctx, size FLARG_PASS));
+}
+
+void *
+isc__mem_reallocate(isc_mem_t *mctx, void *ptr, size_t size FLARG) {
+ REQUIRE(ISCAPI_MCTX_VALID(mctx));
+
+ return (mctx->methods->memreallocate(mctx, ptr, size FLARG_PASS));
+}
+
+char *
+isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) {
+ REQUIRE(ISCAPI_MCTX_VALID(mctx));
+
+ return (mctx->methods->memstrdup(mctx, s FLARG_PASS));
+}
+
+char *
+isc__mem_strndup(isc_mem_t *mctx, const char *s, size_t size FLARG) {
+ REQUIRE(ISCAPI_MCTX_VALID(mctx));
+
+ return (mctx->methods->memstrndup(mctx, s, size FLARG_PASS));
+}
+
+void
+isc__mem_free(isc_mem_t *mctx, void *ptr FLARG) {
+ REQUIRE(ISCAPI_MCTX_VALID(mctx));
+
+ mctx->methods->memfree(mctx, ptr FLARG_PASS);
+}
+
+void
+isc__mem_printactive(isc_mem_t *ctx0, FILE *file) {
+#if ISC_MEM_TRACKLINES
+ REQUIRE(VALID_CONTEXT(ctx0));
+ REQUIRE(file != NULL);
+
+ isc__mem_t *ctx = (isc__mem_t *)ctx0;
+
+ print_active(ctx, file);
+#else /* if ISC_MEM_TRACKLINES */
+ UNUSED(ctx0);
+ UNUSED(file);
+#endif /* if ISC_MEM_TRACKLINES */
+}