4659d5be2527e3d92c82d1f874f35bcca4fdaca6
[asterisk/asterisk.git] / main / astmm.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Memory Management
22  *
23  * \author Mark Spencer <markster@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 #ifdef __AST_DEBUG_MALLOC
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include "asterisk/paths.h"     /* use ast_config_AST_LOG_DIR */
33 #include <stddef.h>
34 #include <time.h>
35
36 #include "asterisk/cli.h"
37 #include "asterisk/lock.h"
38 #include "asterisk/strings.h"
39 #include "asterisk/unaligned.h"
40
41 #define SOME_PRIME 563
42
43 enum func_type {
44         FUNC_CALLOC = 1,
45         FUNC_MALLOC,
46         FUNC_REALLOC,
47         FUNC_STRDUP,
48         FUNC_STRNDUP,
49         FUNC_VASPRINTF,
50         FUNC_ASPRINTF
51 };
52
53 /* Undefine all our macros */
54 #undef malloc
55 #undef calloc
56 #undef realloc
57 #undef strdup
58 #undef strndup
59 #undef free
60 #undef vasprintf
61 #undef asprintf
62
63 #define FENCE_MAGIC 0xdeadbeef
64
65 static FILE *mmlog;
66
67 /* NOTE: Be EXTREMELY careful with modifying this structure; the total size of this structure
68    must result in 'automatic' alignment so that the 'fence' field lands exactly at the end of
69    the structure in memory (and thus immediately before the allocated region the fence is
70    supposed to be used to monitor). In other words, we cannot allow the compiler to insert
71    any padding between this structure and anything following it, so add up the sizes of all the
72    fields and compare to sizeof(struct ast_region)... if they don't match, then the compiler
73    is padding the structure and either the fields need to be rearranged to eliminate internal
74    padding, or a dummy field will need to be inserted before the 'fence' field to push it to
75    the end of the actual space it will consume. Note that this must be checked for both 32-bit
76    and 64-bit platforms, as the sizes of pointers and 'size_t' differ on these platforms.
77 */
78
79 static struct ast_region {
80         struct ast_region *next;
81         size_t len;
82         char file[64];
83         char func[40];
84         unsigned int lineno;
85         enum func_type which;
86         unsigned int cache;             /* region was allocated as part of a cache pool */
87         unsigned int fence;
88         unsigned char data[0];
89 } *regions[SOME_PRIME];
90
91 #define HASH(a) \
92         (((unsigned long)(a)) % SOME_PRIME)
93
94 /*! Tracking this mutex will cause infinite recursion, as the mutex tracking
95  *  code allocates memory */
96 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
97
98 #define astmm_log(...)                               \
99         do {                                         \
100                 fprintf(stderr, __VA_ARGS__);        \
101                 if (mmlog) {                         \
102                         fprintf(mmlog, __VA_ARGS__); \
103                         fflush(mmlog);               \
104                 }                                    \
105         } while (0)
106
107 static inline void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
108 {
109         struct ast_region *reg;
110         void *ptr = NULL;
111         unsigned int *fence;
112         int hash;
113
114         if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
115                 astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
116                           "at line %d of %s\n", (int) size, func, lineno, file);
117         }
118
119         ast_copy_string(reg->file, file, sizeof(reg->file));
120         ast_copy_string(reg->func, func, sizeof(reg->func));
121         reg->lineno = lineno;
122         reg->len = size;
123         reg->which = which;
124         reg->cache = cache;
125         ptr = reg->data;
126         hash = HASH(ptr);
127         reg->fence = FENCE_MAGIC;
128         fence = (ptr + reg->len);
129         put_unaligned_uint32(fence, FENCE_MAGIC);
130
131         ast_mutex_lock(&reglock);
132         reg->next = regions[hash];
133         regions[hash] = reg;
134         ast_mutex_unlock(&reglock);
135
136         return ptr;
137 }
138
139 static inline size_t __ast_sizeof_region(void *ptr)
140 {
141         int hash = HASH(ptr);
142         struct ast_region *reg;
143         size_t len = 0;
144         
145         ast_mutex_lock(&reglock);
146         for (reg = regions[hash]; reg; reg = reg->next) {
147                 if (reg->data == ptr) {
148                         len = reg->len;
149                         break;
150                 }
151         }
152         ast_mutex_unlock(&reglock);
153
154         return len;
155 }
156
157 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
158 {
159         int hash;
160         struct ast_region *reg, *prev = NULL;
161         unsigned int *fence;
162
163         if (!ptr)
164                 return;
165
166         hash = HASH(ptr);
167
168         ast_mutex_lock(&reglock);
169         for (reg = regions[hash]; reg; reg = reg->next) {
170                 if (reg->data == ptr) {
171                         if (prev)
172                                 prev->next = reg->next;
173                         else
174                                 regions[hash] = reg->next;
175                         break;
176                 }
177                 prev = reg;
178         }
179         ast_mutex_unlock(&reglock);
180
181         if (reg) {
182                 fence = (unsigned int *)(reg->data + reg->len);
183                 if (reg->fence != FENCE_MAGIC) {
184                         astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
185                                 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
186                 }
187                 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
188                         astmm_log("WARNING: High fence violation at %p, in %s of %s, "
189                                 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
190                 }
191                 free(reg);
192         } else {
193                 astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",       
194                         ptr, func, file, lineno);
195         }
196 }
197
198 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
199 {
200         void *ptr;
201
202         if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0))) 
203                 memset(ptr, 0, size * nmemb);
204
205         return ptr;
206 }
207
208 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
209 {
210         void *ptr;
211
212         if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1))) 
213                 memset(ptr, 0, size * nmemb);
214
215         return ptr;
216 }
217
218 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func) 
219 {
220         return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
221 }
222
223 void __ast_free(void *ptr, const char *file, int lineno, const char *func) 
224 {
225         __ast_free_region(ptr, file, lineno, func);
226 }
227
228 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func) 
229 {
230         void *tmp;
231         size_t len = 0;
232
233         if (ptr && !(len = __ast_sizeof_region(ptr))) {
234                 astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
235                         "line %d\n", ptr, func, file, lineno);
236                 return NULL;
237         }
238
239         if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
240                 return NULL;
241
242         if (len > size)
243                 len = size;
244         if (ptr) {
245                 memcpy(tmp, ptr, len);
246                 __ast_free_region(ptr, file, lineno, func);
247         }
248         
249         return tmp;
250 }
251
252 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func) 
253 {
254         size_t len;
255         void *ptr;
256
257         if (!s)
258                 return NULL;
259
260         len = strlen(s) + 1;
261         if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
262                 strcpy(ptr, s);
263
264         return ptr;
265 }
266
267 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func) 
268 {
269         size_t len;
270         void *ptr;
271
272         if (!s)
273                 return NULL;
274
275         len = strlen(s) + 1;
276         if (len > n)
277                 len = n;
278         if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
279                 strcpy(ptr, s);
280
281         return ptr;
282 }
283
284 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
285 {
286         int size;
287         va_list ap, ap2;
288         char s;
289
290         *strp = NULL;
291         va_start(ap, fmt);
292         va_copy(ap2, ap);
293         size = vsnprintf(&s, 1, fmt, ap2);
294         va_end(ap2);
295         if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
296                 va_end(ap);
297                 return -1;
298         }
299         vsnprintf(*strp, size + 1, fmt, ap);
300         va_end(ap);
301
302         return size;
303 }
304
305 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func) 
306 {
307         int size;
308         va_list ap2;
309         char s;
310
311         *strp = NULL;
312         va_copy(ap2, ap);
313         size = vsnprintf(&s, 1, fmt, ap2);
314         va_end(ap2);
315         if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
316                 va_end(ap);
317                 return -1;
318         }
319         vsnprintf(*strp, size + 1, fmt, ap);
320
321         return size;
322 }
323
324 static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
325 {
326         char *fn = NULL;
327         struct ast_region *reg;
328         unsigned int x;
329         unsigned int len = 0;
330         unsigned int cache_len = 0;
331         unsigned int count = 0;
332         unsigned int *fence;
333
334         switch (cmd) {
335         case CLI_INIT:
336                 e->command = "memory show allocations";
337                 e->usage =
338                         "Usage: memory show allocations [<file>]\n"
339                         "       Dumps a list of all segments of allocated memory, optionally\n"
340                         "       limited to those from a specific file\n";
341                 return NULL;
342         case CLI_GENERATE:
343                 return NULL;
344         }
345
346
347         if (a->argc > 3)
348                 fn = a->argv[3];
349
350         ast_mutex_lock(&reglock);
351         for (x = 0; x < SOME_PRIME; x++) {
352                 for (reg = regions[x]; reg; reg = reg->next) {
353                         if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
354                                 fence = (unsigned int *)(reg->data + reg->len);
355                                 if (reg->fence != FENCE_MAGIC) {
356                                         astmm_log("WARNING: Low fence violation at %p, "
357                                                 "in %s of %s, line %d\n", reg->data, 
358                                                 reg->func, reg->file, reg->lineno);
359                                 }
360                                 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
361                                         astmm_log("WARNING: High fence violation at %p, in %s of %s, "
362                                                 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
363                                 }
364                         }
365                         if (!fn || !strcasecmp(fn, reg->file)) {
366                                 ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n", 
367                                         (int) reg->len, reg->cache ? " (cache)" : "", 
368                                         reg->func, reg->lineno, reg->file);
369                                 len += reg->len;
370                                 if (reg->cache)
371                                         cache_len += reg->len;
372                                 count++;
373                         }
374                 }
375         }
376         ast_mutex_unlock(&reglock);
377         
378         if (cache_len)
379                 ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
380         else
381                 ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
382         
383         return CLI_SUCCESS;
384 }
385
386 static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
387 {
388         char *fn = NULL;
389         int x;
390         struct ast_region *reg;
391         unsigned int len = 0;
392         unsigned int cache_len = 0;
393         int count = 0;
394         struct file_summary {
395                 char fn[80];
396                 int len;
397                 int cache_len;
398                 int count;
399                 struct file_summary *next;
400         } *list = NULL, *cur;
401         
402         switch (cmd) {
403         case CLI_INIT:
404                 e->command = "memory show summary";
405                 e->usage =
406                         "Usage: memory show summary [<file>]\n"
407                         "       Summarizes heap memory allocations by file, or optionally\n"
408                         "by function, if a file is specified\n";
409                 return NULL;
410         case CLI_GENERATE:
411                 return NULL;
412         }
413
414         if (a->argc > 3) 
415                 fn = a->argv[3];
416
417         ast_mutex_lock(&reglock);
418         for (x = 0; x < SOME_PRIME; x++) {
419                 for (reg = regions[x]; reg; reg = reg->next) {
420                         if (fn && strcasecmp(fn, reg->file))
421                                 continue;
422
423                         for (cur = list; cur; cur = cur->next) {
424                                 if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
425                                         break;
426                         }
427                         if (!cur) {
428                                 cur = alloca(sizeof(*cur));
429                                 memset(cur, 0, sizeof(*cur));
430                                 ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
431                                 cur->next = list;
432                                 list = cur;
433                         }
434
435                         cur->len += reg->len;
436                         if (reg->cache)
437                                 cur->cache_len += reg->len;
438                         cur->count++;
439                 }
440         }
441         ast_mutex_unlock(&reglock);
442         
443         /* Dump the whole list */
444         for (cur = list; cur; cur = cur->next) {
445                 len += cur->len;
446                 cache_len += cur->cache_len;
447                 count += cur->count;
448                 if (cur->cache_len) {
449                         if (fn) {
450                                 ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n", 
451                                         cur->len, cur->cache_len, cur->count, cur->fn, fn);
452                         } else {
453                                 ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n", 
454                                         cur->len, cur->cache_len, cur->count, cur->fn);
455                         }
456                 } else {
457                         if (fn) {
458                                 ast_cli(a->fd, "%10d bytes in %d allocations in function '%s' of '%s'\n", 
459                                         cur->len, cur->count, cur->fn, fn);
460                         } else {
461                                 ast_cli(a->fd, "%10d bytes in %d allocations in file '%s'\n", 
462                                         cur->len, cur->count, cur->fn);
463                         }
464                 }
465         }
466
467         if (cache_len)
468                 ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
469         else
470                 ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
471
472         return CLI_SUCCESS;
473 }
474
475 static struct ast_cli_entry cli_memory[] = {
476         AST_CLI_DEFINE(handle_memory_show, "Display outstanding memory allocations"),
477         AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
478 };
479
480 void __ast_mm_init(void)
481 {
482         char filename[PATH_MAX];
483         size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
484
485         if (pad) {
486                 ast_log(LOG_ERROR, "struct ast_region has %d bytes of padding! This must be eliminated for low-fence checking to work properly!\n", (int) pad);
487         }
488
489         ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
490         
491         snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
492         
493         ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
494         
495         if ((mmlog = fopen(filename, "a+"))) {
496                 fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
497                 fflush(mmlog);
498         }
499 }
500
501 #endif