]>
Commit | Line | Data |
---|---|---|
3b4cd783 DL |
1 | /* |
2 | * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | * DEALINGS IN THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #ifndef _QUAGGA_MEMORY_H | |
24 | #define _QUAGGA_MEMORY_H | |
25 | ||
26 | #include <stdlib.h> | |
27 | ||
28 | #define array_size(ar) (sizeof(ar) / sizeof(ar[0])) | |
29 | ||
30 | #define SIZE_VAR ~0UL | |
31 | struct memtype | |
32 | { | |
33 | struct memtype *next; | |
34 | const char *name; | |
35 | size_t n_alloc; | |
36 | size_t size; | |
37 | }; | |
38 | ||
39 | struct memgroup | |
40 | { | |
41 | struct memgroup *next; | |
42 | struct memtype *types, **insert; | |
43 | const char *name; | |
44 | }; | |
45 | ||
46 | #if defined(__clang__) | |
47 | # if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 5) | |
48 | # define _RET_NONNULL , returns_nonnull | |
49 | # endif | |
50 | # define _CONSTRUCTOR(x) constructor(x) | |
51 | #elif defined(__GNUC__) | |
52 | # if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9) | |
53 | # define _RET_NONNULL , returns_nonnull | |
54 | # endif | |
55 | # if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) | |
56 | # define _CONSTRUCTOR(x) constructor(x) | |
57 | # define _ALLOC_SIZE(x) alloc_size(x) | |
58 | # endif | |
59 | #endif | |
60 | ||
61 | #ifdef __sun | |
62 | /* Solaris doesn't do constructor priorities due to linker restrictions */ | |
63 | # undef _CONSTRUCTOR | |
64 | #endif | |
65 | ||
66 | #ifndef _RET_NONNULL | |
67 | # define _RET_NONNULL | |
68 | #endif | |
69 | #ifndef _CONSTRUCTOR | |
70 | # define _CONSTRUCTOR(x) constructor | |
71 | #endif | |
72 | #ifndef _ALLOC_SIZE | |
73 | # define _ALLOC_SIZE(x) | |
74 | #endif | |
75 | ||
76 | /* macro usage: | |
77 | * | |
78 | * mydaemon.h | |
79 | * DECLARE_MGROUP(MYDAEMON) | |
80 | * DECLARE_MTYPE(MYDAEMON_COMMON) | |
81 | * | |
82 | * mydaemon.c | |
83 | * DEFINE_MGROUP(MYDAEMON, "my daemon memory") | |
84 | * DEFINE_MTYPE(MYDAEMON, MYDAEMON_COMMON, | |
85 | * "this mtype is used in multiple files in mydaemon") | |
86 | * foo = qmalloc (MTYPE_MYDAEMON_COMMON, sizeof (*foo)) | |
87 | * | |
88 | * mydaemon_io.c | |
89 | * bar = qmalloc (MTYPE_MYDAEMON_COMMON, sizeof (*bar)) | |
90 | * | |
91 | * DEFINE_MTYPE_STATIC(MYDAEMON, MYDAEMON_IO, | |
92 | * "this mtype is used only in this file") | |
93 | * baz = qmalloc (MTYPE_MYDAEMON_IO, sizeof (*baz)) | |
94 | * | |
95 | * Note: Naming conventions (MGROUP_ and MTYPE_ prefixes are enforced | |
96 | * by not having these as part of the macro arguments) | |
97 | * Note: MTYPE_* are symbols to the compiler (of type struct memtype *), | |
98 | * but MGROUP_* aren't. | |
99 | */ | |
100 | ||
101 | #define DECLARE_MGROUP(name) \ | |
102 | extern struct memgroup _mg_##name; | |
103 | #define DEFINE_MGROUP(mname, desc) \ | |
104 | struct memgroup _mg_##mname \ | |
105 | __attribute__ ((section (".data.mgroups"))) = { \ | |
106 | .name = desc, \ | |
107 | .types = NULL, .next = NULL, .insert = NULL, \ | |
108 | }; \ | |
109 | static void _mginit_##mname (void) \ | |
110 | __attribute__ ((_CONSTRUCTOR (1000))); \ | |
111 | static void _mginit_##mname (void) \ | |
112 | { extern struct memgroup **mg_insert; \ | |
113 | *mg_insert = &_mg_##mname; \ | |
114 | mg_insert = &_mg_##mname.next; } | |
115 | ||
116 | ||
117 | #define DECLARE_MTYPE(name) \ | |
118 | extern struct memtype _mt_##name; \ | |
119 | static struct memtype * const MTYPE_ ## name = &_mt_##name; | |
120 | ||
121 | #define DEFINE_MTYPE_ATTR(group, mname, attr, desc) \ | |
122 | attr struct memtype _mt_##mname \ | |
123 | __attribute__ ((section (".data.mtypes"))) = { \ | |
124 | .name = desc, \ | |
125 | .next = NULL, .n_alloc = 0, .size = 0, \ | |
126 | }; \ | |
127 | static void _mtinit_##mname (void) \ | |
128 | __attribute__ ((_CONSTRUCTOR (1001))); \ | |
129 | static void _mtinit_##mname (void) \ | |
130 | { if (_mg_##group.insert == NULL) \ | |
131 | _mg_##group.insert = &_mg_##group.types; \ | |
132 | *_mg_##group.insert = &_mt_##mname; \ | |
133 | _mg_##group.insert = &_mt_##mname.next; } | |
134 | ||
135 | #define DEFINE_MTYPE(group, name, desc) \ | |
136 | DEFINE_MTYPE_ATTR(group, name, , desc) | |
137 | #define DEFINE_MTYPE_STATIC(group, name, desc) \ | |
138 | DEFINE_MTYPE_ATTR(group, name, static, desc) \ | |
139 | static struct memtype * const MTYPE_ ## name = &_mt_##name; | |
140 | ||
4a1ab8e4 DL |
141 | DECLARE_MGROUP(LIB) |
142 | DECLARE_MTYPE(TMP) | |
143 | ||
3b4cd783 DL |
144 | |
145 | extern void *qmalloc (struct memtype *mt, size_t size) | |
146 | __attribute__ ((malloc, _ALLOC_SIZE(2), nonnull (1) _RET_NONNULL)); | |
147 | extern void *qcalloc (struct memtype *mt, size_t size) | |
148 | __attribute__ ((malloc, _ALLOC_SIZE(2), nonnull (1) _RET_NONNULL)); | |
149 | extern void *qrealloc (struct memtype *mt, void *ptr, size_t size) | |
150 | __attribute__ ((_ALLOC_SIZE(3), nonnull (1) _RET_NONNULL)); | |
151 | extern void *qstrdup (struct memtype *mt, const char *str) | |
152 | __attribute__ ((malloc, nonnull (1) _RET_NONNULL)); | |
153 | extern void qfree (struct memtype *mt, void *ptr) | |
154 | __attribute__ ((nonnull (1))); | |
155 | ||
3b4cd783 DL |
156 | #define XMALLOC(mtype, size) qmalloc(mtype, size) |
157 | #define XCALLOC(mtype, size) qcalloc(mtype, size) | |
158 | #define XREALLOC(mtype, ptr, size) qrealloc(mtype, ptr, size) | |
159 | #define XSTRDUP(mtype, str) qstrdup(mtype, str) | |
160 | #define XFREE(mtype, ptr) do { qfree(mtype, ptr); ptr = NULL; } \ | |
161 | while (0) | |
162 | ||
163 | static inline size_t mtype_stats_alloc(struct memtype *mt) | |
164 | { | |
165 | return mt->n_alloc; | |
166 | } | |
3b4cd783 DL |
167 | |
168 | /* NB: calls are ordered by memgroup; and there is a call with mt == NULL for | |
169 | * each memgroup (so that a header can be printed, and empty memgroups show) | |
170 | * | |
171 | * return value: 0: continue, !0: abort walk. qmem_walk will return the | |
172 | * last value from qmem_walk_fn. */ | |
173 | typedef int qmem_walk_fn (void *arg, struct memgroup *mg, struct memtype *mt); | |
174 | extern int qmem_walk (qmem_walk_fn *func, void *arg); | |
175 | ||
176 | extern void memory_oom (size_t size, const char *name); | |
177 | ||
178 | #endif /* _QUAGGA_MEMORY_H */ |