Line data Source code
1 : /*
2 : * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc.
3 : *
4 : * Permission to use, copy, modify, and distribute this software for any
5 : * purpose with or without fee is hereby granted, provided that the above
6 : * copyright notice and this permission notice appear in all copies.
7 : *
8 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 : */
16 :
17 : #ifndef _QUAGGA_MEMORY_H
18 : #define _QUAGGA_MEMORY_H
19 :
20 : #include <stdbool.h>
21 : #include <stdlib.h>
22 : #include <stdio.h>
23 : #include <frratomic.h>
24 : #include "compiler.h"
25 :
26 : #ifdef __cplusplus
27 : extern "C" {
28 : #endif
29 :
30 : #if defined(HAVE_MALLOC_SIZE) && !defined(HAVE_MALLOC_USABLE_SIZE)
31 : #define malloc_usable_size(x) malloc_size(x)
32 : #define HAVE_MALLOC_USABLE_SIZE
33 : #endif
34 :
35 : #define SIZE_VAR ~0UL
36 : struct memtype {
37 : struct memtype *next, **ref;
38 : const char *name;
39 : atomic_size_t n_alloc;
40 : atomic_size_t n_max;
41 : atomic_size_t size;
42 : #ifdef HAVE_MALLOC_USABLE_SIZE
43 : atomic_size_t total;
44 : atomic_size_t max_size;
45 : #endif
46 : };
47 :
48 : struct memgroup {
49 : struct memgroup *next, **ref;
50 : struct memtype *types, **insert;
51 : const char *name;
52 : /* ignore group on dumping memleaks at exit */
53 : bool active_at_exit;
54 : };
55 :
56 : /* macro usage:
57 : *
58 : * mydaemon.h
59 : * DECLARE_MGROUP(MYDAEMON);
60 : * DECLARE_MTYPE(MYDAEMON_COMMON);
61 : *
62 : * mydaemon.c
63 : * DEFINE_MGROUP(MYDAEMON, "my daemon memory");
64 : * DEFINE_MTYPE(MYDAEMON, MYDAEMON_COMMON,
65 : * "this mtype is used in multiple files in mydaemon");
66 : * foo = qmalloc(MTYPE_MYDAEMON_COMMON, sizeof(*foo))
67 : *
68 : * mydaemon_io.c
69 : * bar = qmalloc(MTYPE_MYDAEMON_COMMON, sizeof(*bar))
70 : *
71 : * DEFINE_MTYPE_STATIC(MYDAEMON, MYDAEMON_IO,
72 : * "this mtype is used only in this file");
73 : * baz = qmalloc(MTYPE_MYDAEMON_IO, sizeof(*baz))
74 : *
75 : * Note: Naming conventions (MGROUP_ and MTYPE_ prefixes are enforced
76 : * by not having these as part of the macro arguments)
77 : * Note: MTYPE_* are symbols to the compiler (of type struct memtype *),
78 : * but MGROUP_* aren't.
79 : */
80 :
81 : #define DECLARE_MGROUP(name) extern struct memgroup _mg_##name
82 : #define _DEFINE_MGROUP(mname, desc, ...) \
83 : struct memgroup _mg_##mname \
84 : __attribute__((section(".data.mgroups"))) = { \
85 : .name = desc, \
86 : .types = NULL, \
87 : .next = NULL, \
88 : .insert = NULL, \
89 : .ref = NULL, \
90 : __VA_ARGS__ \
91 : }; \
92 : static void _mginit_##mname(void) __attribute__((_CONSTRUCTOR(1000))); \
93 : static void _mginit_##mname(void) \
94 : { \
95 : extern struct memgroup **mg_insert; \
96 : _mg_##mname.ref = mg_insert; \
97 : *mg_insert = &_mg_##mname; \
98 : mg_insert = &_mg_##mname.next; \
99 : } \
100 : static void _mgfini_##mname(void) __attribute__((_DESTRUCTOR(1000))); \
101 : static void _mgfini_##mname(void) \
102 : { \
103 : if (_mg_##mname.next) \
104 : _mg_##mname.next->ref = _mg_##mname.ref; \
105 : *_mg_##mname.ref = _mg_##mname.next; \
106 : } \
107 : MACRO_REQUIRE_SEMICOLON() /* end */
108 :
109 : #define DEFINE_MGROUP(mname, desc) \
110 : _DEFINE_MGROUP(mname, desc, )
111 : #define DEFINE_MGROUP_ACTIVEATEXIT(mname, desc) \
112 : _DEFINE_MGROUP(mname, desc, .active_at_exit = true)
113 :
114 : #define DECLARE_MTYPE(name) \
115 : extern struct memtype MTYPE_##name[1] \
116 : /* end */
117 :
118 : #define DEFINE_MTYPE_ATTR(group, mname, attr, desc) \
119 : attr struct memtype MTYPE_##mname[1] \
120 : __attribute__((section(".data.mtypes"))) = { { \
121 : .name = desc, \
122 : .next = NULL, \
123 : .n_alloc = 0, \
124 : .size = 0, \
125 : .ref = NULL, \
126 : } }; \
127 : static void _mtinit_##mname(void) __attribute__((_CONSTRUCTOR(1001))); \
128 : static void _mtinit_##mname(void) \
129 : { \
130 : if (_mg_##group.insert == NULL) \
131 : _mg_##group.insert = &_mg_##group.types; \
132 : MTYPE_##mname->ref = _mg_##group.insert; \
133 : *_mg_##group.insert = MTYPE_##mname; \
134 : _mg_##group.insert = &MTYPE_##mname->next; \
135 : } \
136 : static void _mtfini_##mname(void) __attribute__((_DESTRUCTOR(1001))); \
137 : static void _mtfini_##mname(void) \
138 : { \
139 : if (MTYPE_##mname->next) \
140 : MTYPE_##mname->next->ref = MTYPE_##mname->ref; \
141 : *MTYPE_##mname->ref = MTYPE_##mname->next; \
142 : } \
143 : MACRO_REQUIRE_SEMICOLON() /* end */
144 :
145 : #define DEFINE_MTYPE(group, name, desc) \
146 : DEFINE_MTYPE_ATTR(group, name, , desc) \
147 : /* end */
148 :
149 : #define DEFINE_MTYPE_STATIC(group, name, desc) \
150 : DEFINE_MTYPE_ATTR(group, name, static, desc) \
151 : /* end */
152 :
153 : DECLARE_MGROUP(LIB);
154 : DECLARE_MTYPE(TMP);
155 :
156 :
157 : extern void *qmalloc(struct memtype *mt, size_t size)
158 : __attribute__((malloc, _ALLOC_SIZE(2), nonnull(1) _RET_NONNULL));
159 : extern void *qcalloc(struct memtype *mt, size_t size)
160 : __attribute__((malloc, _ALLOC_SIZE(2), nonnull(1) _RET_NONNULL));
161 : extern void *qrealloc(struct memtype *mt, void *ptr, size_t size)
162 : __attribute__((_ALLOC_SIZE(3), nonnull(1) _RET_NONNULL));
163 : extern void *qstrdup(struct memtype *mt, const char *str)
164 : __attribute__((malloc, nonnull(1) _RET_NONNULL));
165 : extern void qcountfree(struct memtype *mt, void *ptr)
166 : __attribute__((nonnull(1)));
167 : extern void qfree(struct memtype *mt, void *ptr) __attribute__((nonnull(1)));
168 :
169 : #define XMALLOC(mtype, size) qmalloc(mtype, size)
170 : #define XCALLOC(mtype, size) qcalloc(mtype, size)
171 : #define XREALLOC(mtype, ptr, size) qrealloc(mtype, ptr, size)
172 : #define XSTRDUP(mtype, str) qstrdup(mtype, str)
173 : #define XCOUNTFREE(mtype, ptr) qcountfree(mtype, ptr)
174 : #define XFREE(mtype, ptr) \
175 : do { \
176 : qfree(mtype, ptr); \
177 : ptr = NULL; \
178 : } while (0)
179 :
180 0 : static inline size_t mtype_stats_alloc(struct memtype *mt)
181 : {
182 0 : return mt->n_alloc;
183 : }
184 :
185 : /* NB: calls are ordered by memgroup; and there is a call with mt == NULL for
186 : * each memgroup (so that a header can be printed, and empty memgroups show)
187 : *
188 : * return value: 0: continue, !0: abort walk. qmem_walk will return the
189 : * last value from qmem_walk_fn. */
190 : typedef int qmem_walk_fn(void *arg, struct memgroup *mg, struct memtype *mt);
191 : extern int qmem_walk(qmem_walk_fn *func, void *arg);
192 : extern int log_memstats(FILE *fp, const char *);
193 : #define log_memstats_stderr(prefix) log_memstats(stderr, prefix)
194 :
195 : extern __attribute__((__noreturn__)) void memory_oom(size_t size,
196 : const char *name);
197 :
198 : #ifdef __cplusplus
199 : }
200 : #endif
201 :
202 : #endif /* _QUAGGA_MEMORY_H */
|