1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc.
*/
#ifndef _QUAGGA_MEMORY_H
#define _QUAGGA_MEMORY_H
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <frratomic.h>
#include "compiler.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SIZE_VAR ~0UL
struct memtype {
struct memtype *next, **ref;
const char *name;
atomic_size_t n_alloc;
atomic_size_t n_max;
atomic_size_t size;
atomic_size_t total;
atomic_size_t max_size;
};
struct memgroup {
struct memgroup *next, **ref;
struct memtype *types, **insert;
const char *name;
/* ignore group on dumping memleaks at exit */
bool active_at_exit;
};
/* macro usage:
*
* mydaemon.h
* DECLARE_MGROUP(MYDAEMON);
* DECLARE_MTYPE(MYDAEMON_COMMON);
*
* mydaemon.c
* DEFINE_MGROUP(MYDAEMON, "my daemon memory");
* DEFINE_MTYPE(MYDAEMON, MYDAEMON_COMMON,
* "this mtype is used in multiple files in mydaemon");
* foo = qmalloc(MTYPE_MYDAEMON_COMMON, sizeof(*foo))
*
* mydaemon_io.c
* bar = qmalloc(MTYPE_MYDAEMON_COMMON, sizeof(*bar))
*
* DEFINE_MTYPE_STATIC(MYDAEMON, MYDAEMON_IO,
* "this mtype is used only in this file");
* baz = qmalloc(MTYPE_MYDAEMON_IO, sizeof(*baz))
*
* Note: Naming conventions (MGROUP_ and MTYPE_ prefixes are enforced
* by not having these as part of the macro arguments)
* Note: MTYPE_* are symbols to the compiler (of type struct memtype *),
* but MGROUP_* aren't.
*/
/* clang-format off */
#define DECLARE_MGROUP(name) extern struct memgroup _mg_##name
#define _DEFINE_MGROUP(mname, desc, ...) \
struct memgroup _mg_##mname _DATA_SECTION("mgroups") = { \
.name = desc, \
.types = NULL, \
.next = NULL, \
.insert = NULL, \
.ref = NULL, \
__VA_ARGS__ \
}; \
static void _mginit_##mname(void) __attribute__((_CONSTRUCTOR(1000))); \
static void _mginit_##mname(void) \
{ \
extern struct memgroup **mg_insert; \
_mg_##mname.ref = mg_insert; \
*mg_insert = &_mg_##mname; \
mg_insert = &_mg_##mname.next; \
} \
static void _mgfini_##mname(void) __attribute__((_DESTRUCTOR(1000))); \
static void _mgfini_##mname(void) \
{ \
if (_mg_##mname.next) \
_mg_##mname.next->ref = _mg_##mname.ref; \
*_mg_##mname.ref = _mg_##mname.next; \
} \
MACRO_REQUIRE_SEMICOLON() /* end */
#define DEFINE_MGROUP(mname, desc) \
_DEFINE_MGROUP(mname, desc, )
#define DEFINE_MGROUP_ACTIVEATEXIT(mname, desc) \
_DEFINE_MGROUP(mname, desc, .active_at_exit = true)
#define DECLARE_MTYPE(name) \
extern struct memtype MTYPE_##name[1] \
/* end */
#define DEFINE_MTYPE_ATTR(group, mname, attr, desc) \
attr struct memtype MTYPE_##mname[1] _DATA_SECTION("mtypes") = { { \
.name = desc, \
.next = NULL, \
.n_alloc = 0, \
.size = 0, \
.ref = NULL, \
} }; \
static void _mtinit_##mname(void) __attribute__((_CONSTRUCTOR(1001))); \
static void _mtinit_##mname(void) \
{ \
if (_mg_##group.insert == NULL) \
_mg_##group.insert = &_mg_##group.types; \
MTYPE_##mname->ref = _mg_##group.insert; \
*_mg_##group.insert = MTYPE_##mname; \
_mg_##group.insert = &MTYPE_##mname->next; \
} \
static void _mtfini_##mname(void) __attribute__((_DESTRUCTOR(1001))); \
static void _mtfini_##mname(void) \
{ \
if (MTYPE_##mname->next) \
MTYPE_##mname->next->ref = MTYPE_##mname->ref; \
*MTYPE_##mname->ref = MTYPE_##mname->next; \
} \
MACRO_REQUIRE_SEMICOLON() /* end */
#define DEFINE_MTYPE(group, name, desc) \
DEFINE_MTYPE_ATTR(group, name, , desc) \
/* end */
#define DEFINE_MTYPE_STATIC(group, name, desc) \
DEFINE_MTYPE_ATTR(group, name, static, desc) \
/* end */
/* clang-format on */
DECLARE_MGROUP(LIB);
DECLARE_MTYPE(TMP);
DECLARE_MTYPE(TMP_TTABLE);
extern void *qmalloc(struct memtype *mt, size_t size)
__attribute__((malloc, _ALLOC_SIZE(2), nonnull(1) _RET_NONNULL));
extern void *qcalloc(struct memtype *mt, size_t size)
__attribute__((malloc, _ALLOC_SIZE(2), nonnull(1) _RET_NONNULL));
extern void *qrealloc(struct memtype *mt, void *ptr, size_t size)
__attribute__((_ALLOC_SIZE(3), nonnull(1) _RET_NONNULL));
extern void *qstrdup(struct memtype *mt, const char *str);
extern void qcountfree(struct memtype *mt, void *ptr)
__attribute__((nonnull(1)));
extern void qfree(struct memtype *mt, void *ptr) __attribute__((nonnull(1)));
#define XMALLOC(mtype, size) qmalloc(mtype, size)
#define XCALLOC(mtype, size) qcalloc(mtype, size)
#define XREALLOC(mtype, ptr, size) qrealloc(mtype, ptr, size)
#define XSTRDUP(mtype, str) qstrdup(mtype, str)
#define XCOUNTFREE(mtype, ptr) qcountfree(mtype, ptr)
#define XFREE(mtype, ptr) \
do { \
qfree(mtype, ptr); \
ptr = NULL; \
} while (0)
static inline size_t mtype_stats_alloc(struct memtype *mt)
{
return mt->n_alloc;
}
/* NB: calls are ordered by memgroup; and there is a call with mt == NULL for
* each memgroup (so that a header can be printed, and empty memgroups show)
*
* return value: 0: continue, !0: abort walk. qmem_walk will return the
* last value from qmem_walk_fn. */
typedef int qmem_walk_fn(void *arg, struct memgroup *mg, struct memtype *mt);
extern int qmem_walk(qmem_walk_fn *func, void *arg);
extern int log_memstats(const char *daemon_name, bool enabled);
extern FRR_NORETURN void memory_oom(size_t size, const char *name);
#ifdef __cplusplus
}
#endif
#endif /* _QUAGGA_MEMORY_H */
|