bind9/lib/isc/include/isc/mem.h
Aydın Mercan 197de93bdc
Forward declare mallocx in isc/mem.h
cmocka.h and jemalloc.h/malloc_np.h has conflicting macro definitions.
While fixing them with push_macro for only malloc is done below, we only
need the non-standard mallocx interface which is easy to just define by
ourselves.
2024-01-18 09:34:36 +01:00

552 lines
15 KiB
C

/*
* 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.
*/
#pragma once
/*! \file isc/mem.h */
#include <stdbool.h>
#include <stdio.h>
#include <isc/attributes.h>
#include <isc/lang.h>
#include <isc/mutex.h>
#include <isc/overflow.h>
#include <isc/types.h>
#include <isc/urcu.h>
ISC_LANG_BEGINDECLS
/*%
* Define ISC_MEM_TRACKLINES=1 to turn on detailed tracing of memory
* allocation and freeing by file and line number.
*/
#ifndef ISC_MEM_TRACKLINES
#define ISC_MEM_TRACKLINES 0
#endif /* ifndef ISC_MEM_TRACKLINES */
extern unsigned int isc_mem_debugging;
extern unsigned int isc_mem_defaultflags;
/*@{*/
#define ISC_MEM_DEBUGTRACE 0x00000001U
#define ISC_MEM_DEBUGRECORD 0x00000002U
#define ISC_MEM_DEBUGUSAGE 0x00000004U
#define ISC_MEM_DEBUGALL \
(ISC_MEM_DEBUGTRACE | ISC_MEM_DEBUGRECORD | ISC_MEM_DEBUGUSAGE)
/*!<
* The variable isc_mem_debugging holds a set of flags for
* turning certain memory debugging options on or off at
* runtime. It is initialized to the value ISC_MEM_DEGBUGGING,
* which is 0 by default but may be overridden at compile time.
* The following flags can be specified:
*
* \li #ISC_MEM_DEBUGTRACE
* Log each allocation and free to isc_lctx.
*
* \li #ISC_MEM_DEBUGRECORD
* Remember each allocation, and match them up on free.
* Crash if a free doesn't match an allocation.
*
* \li #ISC_MEM_DEBUGUSAGE
* Every time the memory usage is greater (lower) than hi_water
* (lo_water) mark, print the current inuse memory.
*/
/*@}*/
#if ISC_MEM_TRACKLINES
#define _ISC_MEM_FILELINE , __FILE__, __LINE__
#define _ISC_MEM_FLARG , const char *, unsigned int
#else /* if ISC_MEM_TRACKLINES */
#define _ISC_MEM_FILELINE
#define _ISC_MEM_FLARG
#endif /* if ISC_MEM_TRACKLINES */
/*
* Flags for isc_mem_create() calls.
*/
#define ISC_MEMFLAG_RESERVED1 0x00000001 /* reserved, obsoleted, don't use */
#define ISC_MEMFLAG_RESERVED2 0x00000002 /* reserved, obsoleted, don't use */
#define ISC_MEMFLAG_FILL \
0x00000004 /* fill with pattern after alloc and frees */
/*%
* Define ISC_MEM_DEFAULTFILL=1 to turn filling the memory with pattern
* after alloc and free.
*/
#if ISC_MEM_DEFAULTFILL
#define ISC_MEMFLAG_DEFAULT ISC_MEMFLAG_FILL
#else /* if !ISC_MEM_USE_INTERNAL_MALLOC */
#define ISC_MEMFLAG_DEFAULT 0
#endif /* if !ISC_MEM_USE_INTERNAL_MALLOC */
/*%
* isc_mem_putanddetach() is a convenience function for use where you
* have a structure with an attached memory context.
*
* Given:
*
* \code
* struct {
* ...
* isc_mem_t *mctx;
* ...
* } *ptr;
*
* isc_mem_t *mctx;
*
* isc_mem_putanddetach(&ptr->mctx, ptr, sizeof(*ptr));
* \endcode
*
* is the equivalent of:
*
* \code
* mctx = NULL;
* isc_mem_attach(ptr->mctx, &mctx);
* isc_mem_detach(&ptr->mctx);
* isc_mem_put(mctx, ptr, sizeof(*ptr));
* isc_mem_detach(&mctx);
* \endcode
*/
/*%
* These functions are actually implemented in isc__mem_<function>
* (two underscores). The single-underscore macros are used to pass
* __FILE__ and __LINE__, and in the case of the put functions, to
* set the pointer being freed to NULL in the calling function.
*/
/*%
* The definitions of the macros have been pulled directly from jemalloc.h
* and checked for consistency in mem.c.
*
*\li ISC__MEM_ZERO - fill the memory with zeroes before returning
*/
#define ISC__MEM_ZERO ((int)0x40)
#define isc_mem_get(c, s) isc__mem_get((c), (s), 0 _ISC_MEM_FILELINE)
#define isc_mem_cget(c, n, s) \
isc__mem_get((c), ISC_CHECKED_MUL((n), (s)), \
ISC__MEM_ZERO _ISC_MEM_FILELINE)
#define isc_mem_reget(c, p, o, n) \
isc__mem_reget((c), (p), (o), (n), 0 _ISC_MEM_FILELINE)
#define isc_mem_creget(c, p, o, n, s) \
isc__mem_reget((c), (p), ISC_CHECKED_MUL((o), (s)), \
ISC_CHECKED_MUL((n), (s)), \
ISC__MEM_ZERO _ISC_MEM_FILELINE)
#define isc_mem_allocate(c, s) isc__mem_allocate((c), (s), 0 _ISC_MEM_FILELINE)
#define isc_mem_callocate(c, n, s) \
isc__mem_allocate((c), ISC_CHECKED_MUL((n), (s)), \
ISC__MEM_ZERO _ISC_MEM_FILELINE)
#define isc_mem_reallocate(c, p, s) \
isc__mem_reallocate((c), (p), (s), 0 _ISC_MEM_FILELINE)
#define isc_mem_strdup(c, p) isc__mem_strdup((c), (p)_ISC_MEM_FILELINE)
#define isc_mem_strndup(c, p, l) \
isc__mem_strndup((c), (p), (l)_ISC_MEM_FILELINE)
#define isc_mempool_get(c) isc__mempool_get((c)_ISC_MEM_FILELINE)
#define isc_mem_put(c, p, s) \
do { \
isc__mem_put((c), (p), (s), 0 _ISC_MEM_FILELINE); \
(p) = NULL; \
} while (0)
#define isc_mem_cput(c, p, n, s) \
do { \
isc__mem_put((c), (p), ISC_CHECKED_MUL((n), (s)), \
ISC__MEM_ZERO _ISC_MEM_FILELINE); \
(p) = NULL; \
} while (0)
#define isc_mem_putanddetach(c, p, s) \
do { \
isc__mem_putanddetach((c), (p), (s), 0 _ISC_MEM_FILELINE); \
(p) = NULL; \
} while (0)
#define isc_mem_free(c, p) \
do { \
isc__mem_free((c), (p), 0 _ISC_MEM_FILELINE); \
(p) = NULL; \
} while (0)
#define isc_mempool_put(c, p) \
do { \
isc__mempool_put((c), (p)_ISC_MEM_FILELINE); \
(p) = NULL; \
} while (0)
/*@{*/
/*
* This is a little hack to help with dynamic link order,
* see https://github.com/jemalloc/jemalloc/issues/2566
* for more information.
*/
#if HAVE_JEMALLOC
/*
* cmocka.h has confliction definitions with the jemalloc header but we only
* need the mallocx symbol from jemalloc.
*/
void *
mallocx(size_t size, int flags);
extern volatile void *isc__mem_malloc;
#define isc_mem_create(cp) \
{ \
isc__mem_create((cp)_ISC_MEM_FILELINE); \
isc__mem_malloc = mallocx; \
ISC_INSIST(CMM_ACCESS_ONCE(isc__mem_malloc) != NULL); \
}
#else
#define isc_mem_create(cp) isc__mem_create((cp)_ISC_MEM_FILELINE)
#endif
void
isc__mem_create(isc_mem_t **_ISC_MEM_FLARG);
/*!<
* \brief Create a memory context.
*
* Requires:
* mctxp != NULL && *mctxp == NULL */
/*@}*/
#define isc_mem_create_arena(cp) isc__mem_create_arena((cp)_ISC_MEM_FILELINE)
void
isc__mem_create_arena(isc_mem_t **_ISC_MEM_FLARG);
/*!<
* \brief Create a memory context that routs all its operations to a
* dedicated jemalloc arena (when available). When jemalloc is not
* available, the function is, effectively, an alias to
* isc_mem_create().
*
* Requires:
* mctxp != NULL && *mctxp == NULL */
/*@}*/
isc_result_t
isc_mem_arena_set_muzzy_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms);
isc_result_t
isc_mem_arena_set_dirty_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms);
/*!<
* \brief These two functions set the given parameters on the
* jemalloc arena associated with the memory context (if there is
* one). When jemalloc is not available, these are no-op.
*
* NOTE: The "muzzy_decay_ms" and "dirty_decay_ms" are the most common
* parameters to adjust when the defaults do not work well (per the
* official jemalloc tuning guide:
* https://github.com/jemalloc/jemalloc/blob/dev/TUNING.md).
*
* Requires:
* mctx - a valid memory context.
*/
/*@}*/
void
isc_mem_attach(isc_mem_t *, isc_mem_t **);
/*@{*/
void
isc_mem_attach(isc_mem_t *, isc_mem_t **);
#define isc_mem_detach(cp) isc__mem_detach((cp)_ISC_MEM_FILELINE)
void
isc__mem_detach(isc_mem_t **_ISC_MEM_FLARG);
/*!<
* \brief Attach to / detach from a memory context.
*
* This is intended for applications that use multiple memory contexts
* in such a way that it is not obvious when the last allocations from
* a given context has been freed and destroying the context is safe.
*
* Most applications do not need to call these functions as they can
* simply create a single memory context at the beginning of main()
* and destroy it at the end of main(), thereby guaranteeing that it
* is not destroyed while there are outstanding allocations.
*/
/*@}*/
#define isc_mem_destroy(cp) isc__mem_destroy((cp)_ISC_MEM_FILELINE)
void
isc__mem_destroy(isc_mem_t **_ISC_MEM_FLARG);
/*%<
* Destroy a memory context.
*/
void
isc_mem_stats(isc_mem_t *mctx, FILE *out);
/*%<
* Print memory usage statistics for 'mctx' on the stream 'out'.
*/
void
isc_mem_setdestroycheck(isc_mem_t *mctx, bool on);
/*%<
* If 'on' is true, 'mctx' will check for memory leaks when
* destroyed and abort the program if any are present.
*/
size_t
isc_mem_inuse(isc_mem_t *mctx);
/*%<
* Get an estimate of the amount of memory in use in 'mctx', in bytes.
* This includes quantization overhead, but does not include memory
* allocated from the system but not yet used.
*/
bool
isc_mem_isovermem(isc_mem_t *mctx);
/*%<
* Return true iff the memory context is in "over memory" state, i.e.,
* a hiwater mark has been set and the used amount of memory has exceeds
* the mark.
*/
void
isc_mem_clearwater(isc_mem_t *mctx);
void
isc_mem_setwater(isc_mem_t *mctx, size_t hiwater, size_t lowater);
/*%<
* Set high and low water marks for this memory context.
*
* When the memory usage of 'mctx' exceeds 'hiwater', the overmem condition
* will be met and isc_mem_isovermem() will return true.
*
* If the 'hiwater' and 'lowater' is set to 0, the high- and low-water
* processing are disabled for this memory context.
*
* There's a convenient function isc_mem_clearwater().
*
* Requires:
*\li 'hiwater' >= 'lowater'
*/
void
isc_mem_checkdestroyed(FILE *file);
/*%<
* Check that all memory contexts have been destroyed.
* Prints out those that have not been.
* Fatally fails if there are still active contexts.
*/
unsigned int
isc_mem_references(isc_mem_t *ctx);
/*%<
* Return the current reference count.
*/
void
isc_mem_setname(isc_mem_t *ctx, const char *name);
/*%<
* Name 'ctx'.
*
* Notes:
*
*\li Only the first 15 characters of 'name' will be copied.
*
* Requires:
*
*\li 'ctx' is a valid ctx.
*/
const char *
isc_mem_getname(isc_mem_t *ctx);
/*%<
* Get the name of 'ctx', as previously set using isc_mem_setname().
*
* Requires:
*\li 'ctx' is a valid ctx.
*
* Returns:
*\li A non-NULL pointer to a null-terminated string.
* If the ctx has not been named, the string is
* empty.
*/
#ifdef HAVE_LIBXML2
int
isc_mem_renderxml(void *writer0);
/*%<
* Render all contexts' statistics and status in XML for writer.
*/
#endif /* HAVE_LIBXML2 */
#ifdef HAVE_JSON_C
isc_result_t
isc_mem_renderjson(void *memobj0);
/*%<
* Render all contexts' statistics and status in JSON.
*/
#endif /* HAVE_JSON_C */
/*
* Memory pools
*/
#define isc_mempool_create(c, s, mp) \
isc__mempool_create((c), (s), (mp)_ISC_MEM_FILELINE)
void
isc__mempool_create(isc_mem_t *restrict mctx, const size_t element_size,
isc_mempool_t **mpctxp _ISC_MEM_FLARG);
/*%<
* Create a memory pool.
*
* Requires:
*\li mctx is a valid memory context.
*\li size > 0
*\li mpctxp != NULL and *mpctxp == NULL
*
* Defaults:
*\li freemax = 1
*\li fillcount = 1
*
* Returns:
*\li #ISC_R_NOMEMORY -- not enough memory to create pool
*\li #ISC_R_SUCCESS -- all is well.
*/
#define isc_mempool_destroy(mp) isc__mempool_destroy((mp)_ISC_MEM_FILELINE)
void
isc__mempool_destroy(isc_mempool_t **restrict mpctxp _ISC_MEM_FLARG);
/*%<
* Destroy a memory pool.
*
* Requires:
*\li mpctxp != NULL && *mpctxp is a valid pool.
*\li The pool has no un"put" allocations outstanding
*/
void
isc_mempool_setname(isc_mempool_t *restrict mpctx, const char *name);
/*%<
* Associate a name with a memory pool. At most 15 characters may be
*used.
*
* Requires:
*\li mpctx is a valid pool.
*\li name != NULL;
*/
/*
* The following functions get/set various parameters. Note that due to
* the unlocked nature of pools these are potentially random values
*unless the imposed externally provided locking protocols are followed.
*
* Also note that the quota limits will not always take immediate
* effect.
*
* All functions require (in addition to other requirements):
* mpctx is a valid memory pool
*/
unsigned int
isc_mempool_getfreemax(isc_mempool_t *restrict mpctx);
/*%<
* Returns the maximum allowed size of the free list.
*/
void
isc_mempool_setfreemax(isc_mempool_t *restrict mpctx, const unsigned int limit);
/*%<
* Sets the maximum allowed size of the free list.
*/
unsigned int
isc_mempool_getfreecount(isc_mempool_t *restrict mpctx);
/*%<
* Returns current size of the free list.
*/
unsigned int
isc_mempool_getallocated(isc_mempool_t *restrict mpctx);
/*%<
* Returns the number of items allocated from this pool.
*/
unsigned int
isc_mempool_getfillcount(isc_mempool_t *restrict mpctx);
/*%<
* Returns the number of items allocated as a block from the parent
* memory context when the free list is empty.
*/
void
isc_mempool_setfillcount(isc_mempool_t *restrict mpctx,
const unsigned int limit);
/*%<
* Sets the fillcount.
*
* Additional requirements:
*\li limit > 0
*/
#if defined(UNIT_TESTING) && defined(malloc)
/*
* cmocka.h redefined malloc as a macro, we #undef it
* to avoid replacing ISC_ATTR_MALLOC with garbage.
*/
#pragma push_macro("malloc")
#undef malloc
#define POP_MALLOC_MACRO 1
#endif
/*
* Pseudo-private functions for use via macros. Do not call directly.
*/
void
isc__mem_putanddetach(isc_mem_t **, void *, size_t, int _ISC_MEM_FLARG);
void
isc__mem_put(isc_mem_t *, void *, size_t, int _ISC_MEM_FLARG);
void
isc__mem_free(isc_mem_t *, void *, int _ISC_MEM_FLARG);
ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mem_put, 2)
void *
isc__mem_get(isc_mem_t *, size_t, int _ISC_MEM_FLARG);
ISC_ATTR_DEALLOCATOR_IDX(isc__mem_put, 2)
void *
isc__mem_reget(isc_mem_t *, void *, size_t, size_t, int _ISC_MEM_FLARG);
ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mem_free, 2)
void *
isc__mem_allocate(isc_mem_t *, size_t, int _ISC_MEM_FLARG);
ISC_ATTR_DEALLOCATOR_IDX(isc__mem_free, 2)
void *
isc__mem_reallocate(isc_mem_t *, void *, size_t, int _ISC_MEM_FLARG);
ISC_ATTR_RETURNS_NONNULL
ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mem_free, 2)
char *
isc__mem_strdup(isc_mem_t *, const char *_ISC_MEM_FLARG);
ISC_ATTR_RETURNS_NONNULL
ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mem_free, 2)
char *
isc__mem_strndup(isc_mem_t *, const char *, size_t _ISC_MEM_FLARG);
ISC_ATTR_MALLOC_DEALLOCATOR_IDX(isc__mempool_put, 2)
void *
isc__mempool_get(isc_mempool_t *_ISC_MEM_FLARG);
void
isc__mempool_put(isc_mempool_t *, void *_ISC_MEM_FLARG);
#ifdef POP_MALLOC_MACRO
/*
* Restore cmocka.h macro for malloc.
*/
#pragma pop_macro("malloc")
#endif
ISC_LANG_ENDDECLS