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 : #include <zebra.h>
18 :
19 : #include <stdlib.h>
20 : #ifdef HAVE_MALLOC_H
21 : #include <malloc.h>
22 : #endif
23 : #ifdef HAVE_MALLOC_NP_H
24 : #include <malloc_np.h>
25 : #endif
26 : #ifdef HAVE_MALLOC_MALLOC_H
27 : #include <malloc/malloc.h>
28 : #endif
29 :
30 : #include "memory.h"
31 : #include "log.h"
32 : #include "libfrr_trace.h"
33 :
34 : static struct memgroup *mg_first = NULL;
35 : struct memgroup **mg_insert = &mg_first;
36 :
37 27 : DEFINE_MGROUP(LIB, "libfrr");
38 27 : DEFINE_MTYPE(LIB, TMP, "Temporary memory");
39 27 : DEFINE_MTYPE(LIB, BITFIELD, "Bitfield memory");
40 :
41 495955 : static inline void mt_count_alloc(struct memtype *mt, size_t size, void *ptr)
42 : {
43 495955 : size_t current;
44 495955 : size_t oldsize;
45 :
46 495955 : current = 1 + atomic_fetch_add_explicit(&mt->n_alloc, 1,
47 : memory_order_relaxed);
48 :
49 495955 : oldsize = atomic_load_explicit(&mt->n_max, memory_order_relaxed);
50 495955 : if (current > oldsize)
51 : /* note that this may fail, but approximation is sufficient */
52 274516 : atomic_compare_exchange_weak_explicit(&mt->n_max, &oldsize,
53 : current,
54 : memory_order_relaxed,
55 : memory_order_relaxed);
56 :
57 495956 : oldsize = atomic_load_explicit(&mt->size, memory_order_relaxed);
58 495956 : if (oldsize == 0)
59 561 : oldsize = atomic_exchange_explicit(&mt->size, size,
60 : memory_order_relaxed);
61 495956 : if (oldsize != 0 && oldsize != size && oldsize != SIZE_VAR)
62 153 : atomic_store_explicit(&mt->size, SIZE_VAR,
63 : memory_order_relaxed);
64 :
65 : #ifdef HAVE_MALLOC_USABLE_SIZE
66 495956 : size_t mallocsz = malloc_usable_size(ptr);
67 :
68 495956 : current = mallocsz + atomic_fetch_add_explicit(&mt->total, mallocsz,
69 : memory_order_relaxed);
70 495956 : oldsize = atomic_load_explicit(&mt->max_size, memory_order_relaxed);
71 495956 : if (current > oldsize)
72 : /* note that this may fail, but approximation is sufficient */
73 276757 : atomic_compare_exchange_weak_explicit(&mt->max_size, &oldsize,
74 : current,
75 : memory_order_relaxed,
76 : memory_order_relaxed);
77 : #endif
78 495956 : }
79 :
80 498977 : static inline void mt_count_free(struct memtype *mt, void *ptr)
81 : {
82 498977 : frrtrace(2, frr_libfrr, memfree, mt, ptr);
83 :
84 498976 : assert(mt->n_alloc);
85 498976 : atomic_fetch_sub_explicit(&mt->n_alloc, 1, memory_order_relaxed);
86 :
87 : #ifdef HAVE_MALLOC_USABLE_SIZE
88 498976 : size_t mallocsz = malloc_usable_size(ptr);
89 :
90 498977 : atomic_fetch_sub_explicit(&mt->total, mallocsz, memory_order_relaxed);
91 : #endif
92 498977 : }
93 :
94 495955 : static inline void *mt_checkalloc(struct memtype *mt, void *ptr, size_t size)
95 : {
96 495955 : frrtrace(3, frr_libfrr, memalloc, mt, ptr, size);
97 :
98 495955 : if (__builtin_expect(ptr == NULL, 0)) {
99 0 : if (size) {
100 : /* malloc(0) is allowed to return NULL */
101 0 : memory_oom(size, mt->name);
102 : }
103 : return NULL;
104 : }
105 495955 : mt_count_alloc(mt, size, ptr);
106 495955 : return ptr;
107 : }
108 :
109 8990 : void *qmalloc(struct memtype *mt, size_t size)
110 : {
111 8990 : return mt_checkalloc(mt, malloc(size), size);
112 : }
113 :
114 356575 : void *qcalloc(struct memtype *mt, size_t size)
115 : {
116 356575 : return mt_checkalloc(mt, calloc(size, 1), size);
117 : }
118 :
119 35594 : void *qrealloc(struct memtype *mt, void *ptr, size_t size)
120 : {
121 35594 : if (ptr)
122 35540 : mt_count_free(mt, ptr);
123 35594 : return mt_checkalloc(mt, ptr ? realloc(ptr, size) : malloc(size), size);
124 : }
125 :
126 94797 : void *qstrdup(struct memtype *mt, const char *str)
127 : {
128 94797 : return str ? mt_checkalloc(mt, strdup(str), strlen(str) + 1) : NULL;
129 : }
130 :
131 0 : void qcountfree(struct memtype *mt, void *ptr)
132 : {
133 0 : if (ptr)
134 0 : mt_count_free(mt, ptr);
135 0 : }
136 :
137 616471 : void qfree(struct memtype *mt, void *ptr)
138 : {
139 616471 : if (ptr)
140 463437 : mt_count_free(mt, ptr);
141 616471 : free(ptr);
142 616471 : }
143 :
144 12 : int qmem_walk(qmem_walk_fn *func, void *arg)
145 : {
146 12 : struct memgroup *mg;
147 12 : struct memtype *mt;
148 12 : int rv;
149 :
150 66 : for (mg = mg_first; mg; mg = mg->next) {
151 54 : if ((rv = func(arg, mg, NULL)))
152 0 : return rv;
153 2208 : for (mt = mg->types; mt; mt = mt->next)
154 2154 : if ((rv = func(arg, mg, mt)))
155 0 : return rv;
156 : }
157 : return 0;
158 : }
159 :
160 : struct exit_dump_args {
161 : FILE *fp;
162 : const char *prefix;
163 : int error;
164 : struct memgroup *mg_printed;
165 : };
166 :
167 2208 : static int qmem_exit_walker(void *arg, struct memgroup *mg, struct memtype *mt)
168 : {
169 2208 : struct exit_dump_args *eda = arg;
170 2208 : char size[32];
171 :
172 2208 : if (!mt || !mt->n_alloc)
173 2118 : return 0;
174 90 : if (!mg->active_at_exit)
175 54 : eda->error++;
176 :
177 90 : if (eda->mg_printed != mg) {
178 24 : if (eda->fp != stderr)
179 9 : fprintf(eda->fp, "%s: showing active allocations in memory group %s",
180 : eda->prefix, mg->name);
181 15 : else if (mg->active_at_exit)
182 9 : zlog_debug("%s: showing active allocations in memory group %s",
183 : eda->prefix, mg->name);
184 : else
185 6 : zlog_warn("%s: showing active allocations in memory group %s",
186 : eda->prefix, mg->name);
187 24 : eda->mg_printed = mg;
188 : }
189 :
190 90 : snprintf(size, sizeof(size), "%10zu", mt->size);
191 90 : if (eda->fp != stderr)
192 72 : fprintf(eda->fp, "%s: memstats: %-30s: %6zu * %s",
193 36 : eda->prefix, mt->name, mt->n_alloc,
194 36 : mt->size == SIZE_VAR ? "(variably sized)" : size);
195 54 : else if (mg->active_at_exit)
196 27 : zlog_debug("%s: memstats: %-30s: %6zu * %s",
197 : eda->prefix, mt->name, mt->n_alloc,
198 : mt->size == SIZE_VAR ? "(variably sized)" : size);
199 : else
200 39 : zlog_warn("%s: memstats: %-30s: %6zu * %s",
201 : eda->prefix, mt->name, mt->n_alloc,
202 : mt->size == SIZE_VAR ? "(variably sized)" : size);
203 : return 0;
204 : }
205 :
206 12 : int log_memstats(FILE *fp, const char *prefix)
207 : {
208 12 : struct exit_dump_args eda = {.fp = fp, .prefix = prefix, .error = 0};
209 12 : qmem_walk(qmem_exit_walker, &eda);
210 12 : return eda.error;
211 : }
|