Line | Count | Source |
1 | | // SPDX-License-Identifier: ISC |
2 | | /* |
3 | | * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc. |
4 | | */ |
5 | | |
6 | | #include <zebra.h> |
7 | | |
8 | | #include <stdlib.h> |
9 | | #ifdef HAVE_MALLOC_H |
10 | | #include <malloc.h> |
11 | | #endif |
12 | | #ifdef HAVE_MALLOC_NP_H |
13 | | #include <malloc_np.h> |
14 | | #endif |
15 | | #ifdef HAVE_MALLOC_MALLOC_H |
16 | | #include <malloc/malloc.h> |
17 | | #endif |
18 | | |
19 | | #include "memory.h" |
20 | | #include "log.h" |
21 | | #include "libfrr_trace.h" |
22 | | |
23 | | static struct memgroup *mg_first = NULL; |
24 | | struct memgroup **mg_insert = &mg_first; |
25 | | |
26 | | DEFINE_MGROUP(LIB, "libfrr"); |
27 | 8 | DEFINE_MTYPE(LIB, TMP, "Temporary memory"); |
28 | 8 | DEFINE_MTYPE(LIB, BITFIELD, "Bitfield memory"); |
29 | 8 | |
30 | 8 | #ifdef FUZZING |
31 | 8 | #undef HAVE_MALLOC_USABLE_SIZE |
32 | 8 | #endif |
33 | 8 | |
34 | 8 | static inline void mt_count_alloc(struct memtype *mt, size_t size, void *ptr) |
35 | 1.79M | { |
36 | 1.79M | size_t current; |
37 | 1.79M | size_t oldsize; |
38 | | |
39 | 1.79M | current = 1 + atomic_fetch_add_explicit(&mt->n_alloc, 1, |
40 | 1.79M | memory_order_relaxed); |
41 | | |
42 | 1.79M | oldsize = atomic_load_explicit(&mt->n_max, memory_order_relaxed); |
43 | 1.79M | if (current > oldsize) |
44 | | /* note that this may fail, but approximation is sufficient */ |
45 | 1.79M | atomic_compare_exchange_weak_explicit(&mt->n_max, &oldsize, |
46 | 102k | current, |
47 | 102k | memory_order_relaxed, |
48 | 102k | memory_order_relaxed); |
49 | | |
50 | 1.79M | oldsize = atomic_load_explicit(&mt->size, memory_order_relaxed); |
51 | 1.79M | if (oldsize == 0) |
52 | 199 | oldsize = atomic_exchange_explicit(&mt->size, size, |
53 | 199 | memory_order_relaxed); |
54 | 1.79M | if (oldsize != 0 && oldsize != size && oldsize != SIZE_VAR) |
55 | 1.79M | atomic_store_explicit(&mt->size, SIZE_VAR, |
56 | 59 | memory_order_relaxed); |
57 | | |
58 | | #ifdef HAVE_MALLOC_USABLE_SIZE |
59 | | size_t mallocsz = malloc_usable_size(ptr); |
60 | | |
61 | | current = mallocsz + atomic_fetch_add_explicit(&mt->total, mallocsz, |
62 | | memory_order_relaxed); |
63 | | oldsize = atomic_load_explicit(&mt->max_size, memory_order_relaxed); |
64 | | if (current > oldsize) |
65 | | /* note that this may fail, but approximation is sufficient */ |
66 | | atomic_compare_exchange_weak_explicit(&mt->max_size, &oldsize, |
67 | | current, |
68 | | memory_order_relaxed, |
69 | | memory_order_relaxed); |
70 | | #endif |
71 | 1.79M | } |
72 | | |
73 | | static inline void mt_count_free(struct memtype *mt, void *ptr) |
74 | 1.69M | { |
75 | 1.69M | frrtrace(2, frr_libfrr, memfree, mt, ptr); |
76 | | |
77 | 1.69M | assert(mt->n_alloc); |
78 | 1.69M | atomic_fetch_sub_explicit(&mt->n_alloc, 1, memory_order_relaxed); |
79 | | |
80 | | #ifdef HAVE_MALLOC_USABLE_SIZE |
81 | | size_t mallocsz = malloc_usable_size(ptr); |
82 | | |
83 | | atomic_fetch_sub_explicit(&mt->total, mallocsz, memory_order_relaxed); |
84 | | #endif |
85 | 1.69M | } |
86 | | |
87 | | static inline void *mt_checkalloc(struct memtype *mt, void *ptr, size_t size) |
88 | 1.79M | { |
89 | 1.79M | frrtrace(3, frr_libfrr, memalloc, mt, ptr, size); |
90 | | |
91 | 1.79M | if (__builtin_expect(ptr == NULL, 0)) { |
92 | 0 | if (size) { |
93 | | /* malloc(0) is allowed to return NULL */ |
94 | 0 | memory_oom(size, mt->name); |
95 | 0 | } |
96 | 0 | return NULL; |
97 | 0 | } |
98 | 1.79M | mt_count_alloc(mt, size, ptr); |
99 | 1.79M | return ptr; |
100 | 1.79M | } |
101 | | |
102 | | void *qmalloc(struct memtype *mt, size_t size) |
103 | 160k | { |
104 | 160k | return mt_checkalloc(mt, malloc(size), size); |
105 | 160k | } |
106 | | |
107 | | void *qcalloc(struct memtype *mt, size_t size) |
108 | 1.54M | { |
109 | 1.54M | return mt_checkalloc(mt, calloc(size, 1), size); |
110 | 1.54M | } |
111 | | |
112 | | void *qrealloc(struct memtype *mt, void *ptr, size_t size) |
113 | 21.8k | { |
114 | 21.8k | if (ptr) |
115 | 17.1k | mt_count_free(mt, ptr); |
116 | 21.8k | return mt_checkalloc(mt, ptr ? realloc(ptr, size) : malloc(size), size); |
117 | 21.8k | } |
118 | | |
119 | | void *qstrdup(struct memtype *mt, const char *str) |
120 | 73.5k | { |
121 | 73.5k | return str ? mt_checkalloc(mt, strdup(str), strlen(str) + 1) : NULL; |
122 | 73.5k | } |
123 | | |
124 | | void qcountfree(struct memtype *mt, void *ptr) |
125 | 0 | { |
126 | 0 | if (ptr) |
127 | 0 | mt_count_free(mt, ptr); |
128 | 0 | } |
129 | | |
130 | | void qfree(struct memtype *mt, void *ptr) |
131 | 1.68M | { |
132 | 1.68M | if (ptr) |
133 | 1.68M | mt_count_free(mt, ptr); |
134 | 1.68M | free(ptr); |
135 | 1.68M | } |
136 | | |
137 | | int qmem_walk(qmem_walk_fn *func, void *arg) |
138 | 0 | { |
139 | 0 | struct memgroup *mg; |
140 | 0 | struct memtype *mt; |
141 | 0 | int rv; |
142 | |
|
143 | 0 | for (mg = mg_first; mg; mg = mg->next) { |
144 | 0 | if ((rv = func(arg, mg, NULL))) |
145 | 0 | return rv; |
146 | 0 | for (mt = mg->types; mt; mt = mt->next) |
147 | 0 | if ((rv = func(arg, mg, mt))) |
148 | 0 | return rv; |
149 | 0 | } |
150 | 0 | return 0; |
151 | 0 | } |
152 | | |
153 | | struct exit_dump_args { |
154 | | FILE *fp; |
155 | | const char *prefix; |
156 | | int error; |
157 | | }; |
158 | | |
159 | | static int qmem_exit_walker(void *arg, struct memgroup *mg, struct memtype *mt) |
160 | 0 | { |
161 | 0 | struct exit_dump_args *eda = arg; |
162 | |
|
163 | 0 | if (!mt) { |
164 | 0 | fprintf(eda->fp, |
165 | 0 | "%s: showing active allocations in memory group %s\n", |
166 | 0 | eda->prefix, mg->name); |
167 | |
|
168 | 0 | } else if (mt->n_alloc) { |
169 | 0 | char size[32]; |
170 | 0 | if (!mg->active_at_exit) |
171 | 0 | eda->error++; |
172 | 0 | snprintf(size, sizeof(size), "%10zu", mt->size); |
173 | 0 | fprintf(eda->fp, "%s: memstats: %-30s: %6zu * %s\n", |
174 | 0 | eda->prefix, mt->name, mt->n_alloc, |
175 | 0 | mt->size == SIZE_VAR ? "(variably sized)" : size); |
176 | 0 | } |
177 | 0 | return 0; |
178 | 0 | } |
179 | | |
180 | | int log_memstats(FILE *fp, const char *prefix) |
181 | 0 | { |
182 | 0 | struct exit_dump_args eda = {.fp = fp, .prefix = prefix, .error = 0}; |
183 | 0 | qmem_walk(qmem_exit_walker, &eda); |
184 | 0 | return eda.error; |
185 | 0 | } |