If malloc returns NULL, we need to return NULL immediately or
[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                 return NULL;
118         }
119
120         ast_copy_string(reg->file, file, sizeof(reg->file));
121         ast_copy_string(reg->func, func, sizeof(reg->func));
122         reg->lineno = lineno;
123         reg->len = size;
124         reg->which = which;
125         reg->cache = cache;
126         ptr = reg->data;
127         hash = HASH(ptr);
128         reg->fence = FENCE_MAGIC;
129         fence = (ptr + reg->len);
130         put_unaligned_uint32(fence, FENCE_MAGIC);
131
132         ast_mutex_lock(&reglock);
133         reg->next = regions[hash];
134         regions[hash] = reg;
135         ast_mutex_unlock(&reglock);
136
137         return ptr;
138 }
139
140 static inline size_t __ast_sizeof_region(void *ptr)
141 {
142         int hash = HASH(ptr);
143         struct ast_region *reg;
144         size_t len = 0;
145         
146         ast_mutex_lock(&reglock);
147         for (reg = regions[hash]; reg; reg = reg->next) {
148                 if (reg->data == ptr) {
149                         len = reg->len;
150                         break;
151                 }
152         }
153         ast_mutex_unlock(&reglock);
154
155         return len;
156 }
157
158 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
159 {
160         int hash;
161         struct ast_region *reg, *prev = NULL;
162         unsigned int *fence;
163
164         if (!ptr)
165                 return;
166
167         hash = HASH(ptr);
168
169         ast_mutex_lock(&reglock);
170         for (reg = regions[hash]; reg; reg = reg->next) {
171                 if (reg->data == ptr) {
172                         if (prev)
173                                 prev->next = reg->next;
174                         else
175                                 regions[hash] = reg->next;
176                         break;
177                 }
178                 prev = reg;
179         }
180         ast_mutex_unlock(&reglock);
181
182         if (reg) {
183                 fence = (unsigned int *)(reg->data + reg->len);
184                 if (reg->fence != FENCE_MAGIC) {
185                         astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
186                                 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
187                 }
188                 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
189                         astmm_log("WARNING: High fence violation at %p, in %s of %s, "
190                                 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
191                 }
192                 free(reg);
193         } else {
194                 astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",       
195                         ptr, func, file, lineno);
196         }
197 }
198
199 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
200 {
201         void *ptr;
202
203         if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0))) 
204                 memset(ptr, 0, size * nmemb);
205
206         return ptr;
207 }
208
209 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
210 {
211         void *ptr;
212
213         if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1))) 
214                 memset(ptr, 0, size * nmemb);
215
216         return ptr;
217 }
218
219 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func) 
220 {
221         return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
222 }
223
224 void __ast_free(void *ptr, const char *file, int lineno, const char *func) 
225 {
226         __ast_free_region(ptr, file, lineno, func);
227 }
228
229 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func) 
230 {
231         void *tmp;
232         size_t len = 0;
233
234         if (ptr && !(len = __ast_sizeof_region(ptr))) {
235                 astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
236                         "line %d\n", ptr, func, file, lineno);
237                 return NULL;
238         }
239
240         if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
241                 return NULL;
242
243         if (len > size)
244                 len = size;
245         if (ptr) {
246                 memcpy(tmp, ptr, len);
247                 __ast_free_region(ptr, file, lineno, func);
248         }
249         
250         return tmp;
251 }
252
253 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func) 
254 {
255         size_t len;
256         void *ptr;
257
258         if (!s)
259                 return NULL;
260
261         len = strlen(s) + 1;
262         if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
263                 strcpy(ptr, s);
264
265         return ptr;
266 }
267
268 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func) 
269 {
270         size_t len;
271         void *ptr;
272
273         if (!s)
274                 return NULL;
275
276         len = strlen(s) + 1;
277         if (len > n)
278                 len = n;
279         if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
280                 strcpy(ptr, s);
281
282         return ptr;
283 }
284
285 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
286 {
287         int size;
288         va_list ap, ap2;
289         char s;
290
291         *strp = NULL;
292         va_start(ap, fmt);
293         va_copy(ap2, ap);
294         size = vsnprintf(&s, 1, fmt, ap2);
295         va_end(ap2);
296         if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
297                 va_end(ap);
298                 return -1;
299         }
300         vsnprintf(*strp, size + 1, fmt, ap);
301         va_end(ap);
302
303         return size;
304 }
305
306 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func) 
307 {
308         int size;
309         va_list ap2;
310         char s;
311
312         *strp = NULL;
313         va_copy(ap2, ap);
314         size = vsnprintf(&s, 1, fmt, ap2);
315         va_end(ap2);
316         if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
317                 va_end(ap);
318                 return -1;
319         }
320         vsnprintf(*strp, size + 1, fmt, ap);
321
322         return size;
323 }
324
325 static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
326 {
327         char *fn = NULL;
328         struct ast_region *reg;
329         unsigned int x;
330         unsigned int len = 0;
331         unsigned int cache_len = 0;
332         unsigned int count = 0;
333         unsigned int *fence;
334
335         switch (cmd) {
336         case CLI_INIT:
337                 e->command = "memory show allocations";
338                 e->usage =
339                         "Usage: memory show allocations [<file>]\n"
340                         "       Dumps a list of all segments of allocated memory, optionally\n"
341                         "       limited to those from a specific file\n";
342                 return NULL;
343         case CLI_GENERATE:
344                 return NULL;
345         }
346
347
348         if (a->argc > 3)
349                 fn = a->argv[3];
350
351         ast_mutex_lock(&reglock);
352         for (x = 0; x < SOME_PRIME; x++) {
353                 for (reg = regions[x]; reg; reg = reg->next) {
354                         if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
355                                 fence = (unsigned int *)(reg->data + reg->len);
356                                 if (reg->fence != FENCE_MAGIC) {
357                                         astmm_log("WARNING: Low fence violation at %p, "
358                                                 "in %s of %s, line %d\n", reg->data, 
359                                                 reg->func, reg->file, reg->lineno);
360                                 }
361                                 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
362                                         astmm_log("WARNING: High fence violation at %p, in %s of %s, "
363                                                 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
364                                 }
365                         }
366                         if (!fn || !strcasecmp(fn, reg->file)) {
367                                 ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n", 
368                                         (int) reg->len, reg->cache ? " (cache)" : "", 
369                                         reg->func, reg->lineno, reg->file);
370                                 len += reg->len;
371                                 if (reg->cache)
372                                         cache_len += reg->len;
373                                 count++;
374                         }
375                 }
376         }
377         ast_mutex_unlock(&reglock);
378         
379         if (cache_len)
380                 ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
381         else
382                 ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
383         
384         return CLI_SUCCESS;
385 }
386
387 static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
388 {
389         char *fn = NULL;
390         int x;
391         struct ast_region *reg;
392         unsigned int len = 0;
393         unsigned int cache_len = 0;
394         int count = 0;
395         struct file_summary {
396                 char fn[80];
397                 int len;
398                 int cache_len;
399                 int count;
400                 struct file_summary *next;
401         } *list = NULL, *cur;
402         
403         switch (cmd) {
404         case CLI_INIT:
405                 e->command = "memory show summary";
406                 e->usage =
407                         "Usage: memory show summary [<file>]\n"
408                         "       Summarizes heap memory allocations by file, or optionally\n"
409                         "by function, if a file is specified\n";
410                 return NULL;
411         case CLI_GENERATE:
412                 return NULL;
413         }
414
415         if (a->argc > 3) 
416                 fn = a->argv[3];
417
418         ast_mutex_lock(&reglock);
419         for (x = 0; x < SOME_PRIME; x++) {
420                 for (reg = regions[x]; reg; reg = reg->next) {
421                         if (fn && strcasecmp(fn, reg->file))
422                                 continue;
423
424                         for (cur = list; cur; cur = cur->next) {
425                                 if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
426                                         break;
427                         }
428                         if (!cur) {
429                                 cur = alloca(sizeof(*cur));
430                                 memset(cur, 0, sizeof(*cur));
431                                 ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
432                                 cur->next = list;
433                                 list = cur;
434                         }
435
436                         cur->len += reg->len;
437                         if (reg->cache)
438                                 cur->cache_len += reg->len;
439                         cur->count++;
440                 }
441         }
442         ast_mutex_unlock(&reglock);
443         
444         /* Dump the whole list */
445         for (cur = list; cur; cur = cur->next) {
446                 len += cur->len;
447                 cache_len += cur->cache_len;
448                 count += cur->count;
449                 if (cur->cache_len) {
450                         if (fn) {
451                                 ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n", 
452                                         cur->len, cur->cache_len, cur->count, cur->fn, fn);
453                         } else {
454                                 ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n", 
455                                         cur->len, cur->cache_len, cur->count, cur->fn);
456                         }
457                 } else {
458                         if (fn) {
459                                 ast_cli(a->fd, "%10d bytes in %d allocations in function '%s' of '%s'\n", 
460                                         cur->len, cur->count, cur->fn, fn);
461                         } else {
462                                 ast_cli(a->fd, "%10d bytes in %d allocations in file '%s'\n", 
463                                         cur->len, cur->count, cur->fn);
464                         }
465                 }
466         }
467
468         if (cache_len)
469                 ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
470         else
471                 ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
472
473         return CLI_SUCCESS;
474 }
475
476 static struct ast_cli_entry cli_memory[] = {
477         AST_CLI_DEFINE(handle_memory_show, "Display outstanding memory allocations"),
478         AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
479 };
480
481 void __ast_mm_init(void)
482 {
483         char filename[PATH_MAX];
484         size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
485
486         if (pad) {
487                 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);
488         }
489
490         ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
491         
492         snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
493         
494         ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
495         
496         if ((mmlog = fopen(filename, "a+"))) {
497                 fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
498                 fflush(mmlog);
499         }
500 }
501
502 #endif