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