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