Merged revisions 78095 via svnmerge from
[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 <stdio.h>
33 #include <string.h>
34 #include <time.h>
35
36 #include "asterisk/cli.h"
37 #include "asterisk/logger.h"
38 #include "asterisk/options.h"
39 #include "asterisk/lock.h"
40 #include "asterisk/strings.h"
41 #include "asterisk/unaligned.h"
42
43 #define SOME_PRIME 563
44
45 enum func_type {
46         FUNC_CALLOC = 1,
47         FUNC_MALLOC,
48         FUNC_REALLOC,
49         FUNC_STRDUP,
50         FUNC_STRNDUP,
51         FUNC_VASPRINTF,
52         FUNC_ASPRINTF
53 };
54
55 /* Undefine all our macros */
56 #undef malloc
57 #undef calloc
58 #undef realloc
59 #undef strdup
60 #undef strndup
61 #undef free
62 #undef vasprintf
63 #undef asprintf
64
65 #define FENCE_MAGIC 0xdeadbeef
66
67 static FILE *mmlog;
68
69 static struct ast_region {
70         struct ast_region *next;
71         char file[40];
72         char func[40];
73         unsigned int lineno;
74         enum func_type which;
75         unsigned int cache;             /* region was allocated as part of a cache pool */
76         size_t len;
77         unsigned int fence;
78         unsigned char data[0];
79 } *regions[SOME_PRIME];
80
81 #define HASH(a) \
82         (((unsigned long)(a)) % SOME_PRIME)
83
84 /*! Tracking this mutex will cause infinite recursion, as the mutex tracking
85  *  code allocates memory */
86 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
87 AST_MUTEX_DEFINE_STATIC(showmemorylock);
88
89 #define astmm_log(...)                               \
90         do {                                         \
91                 fprintf(stderr, __VA_ARGS__);        \
92                 if (mmlog) {                         \
93                         fprintf(mmlog, __VA_ARGS__); \
94                         fflush(mmlog);               \
95                 }                                    \
96         } while (0)
97
98 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)
99 {
100         struct ast_region *reg;
101         void *ptr = NULL;
102         unsigned int *fence;
103         int hash;
104
105         if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
106                 astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
107                           "at line %d of %s\n", (int) size, func, lineno, file);
108         }
109
110         ast_copy_string(reg->file, file, sizeof(reg->file));
111         ast_copy_string(reg->func, func, sizeof(reg->func));
112         reg->lineno = lineno;
113         reg->len = size;
114         reg->which = which;
115         reg->cache = cache;
116         ptr = reg->data;
117         hash = HASH(ptr);
118         reg->fence = FENCE_MAGIC;
119         fence = (ptr + reg->len);
120         put_unaligned_uint32(fence, FENCE_MAGIC);
121
122         ast_mutex_lock(&reglock);
123         reg->next = regions[hash];
124         regions[hash] = reg;
125         ast_mutex_unlock(&reglock);
126
127         return ptr;
128 }
129
130 static inline size_t __ast_sizeof_region(void *ptr)
131 {
132         int hash = HASH(ptr);
133         struct ast_region *reg;
134         size_t len = 0;
135         
136         ast_mutex_lock(&reglock);
137         for (reg = regions[hash]; reg; reg = reg->next) {
138                 if (reg->data == ptr) {
139                         len = reg->len;
140                         break;
141                 }
142         }
143         ast_mutex_unlock(&reglock);
144
145         return len;
146 }
147
148 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
149 {
150         int hash = HASH(ptr);
151         struct ast_region *reg, *prev = NULL;
152         unsigned int *fence;
153
154         ast_mutex_lock(&reglock);
155         for (reg = regions[hash]; reg; reg = reg->next) {
156                 if (reg->data == ptr) {
157                         if (prev)
158                                 prev->next = reg->next;
159                         else
160                                 regions[hash] = reg->next;
161                         break;
162                 }
163                 prev = reg;
164         }
165         ast_mutex_unlock(&reglock);
166
167         if (reg) {
168                 fence = (unsigned int *)(reg->data + reg->len);
169                 if (reg->fence != FENCE_MAGIC) {
170                         astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
171                                 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
172                 }
173                 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
174                         astmm_log("WARNING: High fence violation at %p, in %s of %s, "
175                                 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
176                 }
177                 free(reg);
178         } else {
179                 astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",       
180                         ptr, func, file, lineno);
181         }
182 }
183
184 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
185 {
186         void *ptr;
187
188         if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0))) 
189                 memset(ptr, 0, size * nmemb);
190
191         return ptr;
192 }
193
194 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
195 {
196         void *ptr;
197
198         if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1))) 
199                 memset(ptr, 0, size * nmemb);
200
201         return ptr;
202 }
203
204 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func) 
205 {
206         return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
207 }
208
209 void __ast_free(void *ptr, const char *file, int lineno, const char *func) 
210 {
211         __ast_free_region(ptr, file, lineno, func);
212 }
213
214 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func) 
215 {
216         void *tmp;
217         size_t len = 0;
218
219         if (ptr && !(len = __ast_sizeof_region(ptr))) {
220                 astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
221                         "line %d\n", ptr, func, file, lineno);
222                 return NULL;
223         }
224
225         if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
226                 return NULL;
227
228         if (len > size)
229                 len = size;
230         if (ptr) {
231                 memcpy(tmp, ptr, len);
232                 __ast_free_region(ptr, file, lineno, func);
233         }
234         
235         return tmp;
236 }
237
238 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func) 
239 {
240         size_t len;
241         void *ptr;
242
243         if (!s)
244                 return NULL;
245
246         len = strlen(s) + 1;
247         if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
248                 strcpy(ptr, s);
249
250         return ptr;
251 }
252
253 char *__ast_strndup(const char *s, size_t n, 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 (len > n)
263                 len = n;
264         if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
265                 strcpy(ptr, s);
266
267         return ptr;
268 }
269
270 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
271 {
272         int size;
273         va_list ap, ap2;
274         char s;
275
276         *strp = NULL;
277         va_start(ap, fmt);
278         va_copy(ap2, ap);
279         size = vsnprintf(&s, 1, fmt, ap2);
280         va_end(ap2);
281         if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
282                 va_end(ap);
283                 return -1;
284         }
285         vsnprintf(*strp, size + 1, fmt, ap);
286         va_end(ap);
287
288         return size;
289 }
290
291 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func) 
292 {
293         int size;
294         va_list ap2;
295         char s;
296
297         *strp = NULL;
298         va_copy(ap2, ap);
299         size = vsnprintf(&s, 1, fmt, ap2);
300         va_end(ap2);
301         if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
302                 va_end(ap);
303                 return -1;
304         }
305         vsnprintf(*strp, size + 1, fmt, ap);
306
307         return size;
308 }
309
310 static int handle_show_memory(int fd, int argc, char *argv[])
311 {
312         char *fn = NULL;
313         struct ast_region *reg;
314         unsigned int x;
315         unsigned int len = 0;
316         unsigned int cache_len = 0;
317         unsigned int count = 0;
318         unsigned int *fence;
319
320         if (argc > 3)
321                 fn = argv[3];
322
323         ast_mutex_lock(&showmemorylock);
324         for (x = 0; x < SOME_PRIME; x++) {
325                 for (reg = regions[x]; reg; reg = reg->next) {
326                         if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
327                                 fence = (unsigned int *)(reg->data + reg->len);
328                                 if (reg->fence != FENCE_MAGIC) {
329                                         astmm_log("WARNING: Low fence violation at %p, "
330                                                 "in %s of %s, line %d\n", reg->data, 
331                                                 reg->func, reg->file, reg->lineno);
332                                 }
333                                 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
334                                         astmm_log("WARNING: High fence violation at %p, in %s of %s, "
335                                                 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
336                                 }
337                         }
338                         if (!fn || !strcasecmp(fn, reg->file)) {
339                                 ast_cli(fd, "%10d bytes allocated%s in %20s at line %5d of %s\n", 
340                                         (int) reg->len, reg->cache ? " (cache)" : "", 
341                                         reg->func, reg->lineno, reg->file);
342                                 len += reg->len;
343                                 if (reg->cache)
344                                         cache_len += reg->len;
345                                 count++;
346                         }
347                 }
348         }
349         ast_mutex_unlock(&showmemorylock);
350         
351         if (cache_len)
352                 ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
353         else
354                 ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
355         
356         return RESULT_SUCCESS;
357 }
358
359 static int handle_show_memory_summary(int fd, int argc, char *argv[])
360 {
361         char *fn = NULL;
362         int x;
363         struct ast_region *reg;
364         unsigned int len = 0;
365         unsigned int cache_len = 0;
366         int count = 0;
367         struct file_summary {
368                 char fn[80];
369                 int len;
370                 int cache_len;
371                 int count;
372                 struct file_summary *next;
373         } *list = NULL, *cur;
374         
375         if (argc > 3) 
376                 fn = argv[3];
377
378         ast_mutex_lock(&reglock);
379         for (x = 0; x < SOME_PRIME; x++) {
380                 for (reg = regions[x]; reg; reg = reg->next) {
381                         if (fn && strcasecmp(fn, reg->file))
382                                 continue;
383
384                         for (cur = list; cur; cur = cur->next) {
385                                 if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
386                                         break;
387                         }
388                         if (!cur) {
389                                 cur = alloca(sizeof(*cur));
390                                 memset(cur, 0, sizeof(*cur));
391                                 ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
392                                 cur->next = list;
393                                 list = cur;
394                         }
395
396                         cur->len += reg->len;
397                         if (reg->cache)
398                                 cur->cache_len += reg->len;
399                         cur->count++;
400                 }
401         }
402         ast_mutex_unlock(&reglock);
403         
404         /* Dump the whole list */
405         for (cur = list; cur; cur = cur->next) {
406                 len += cur->len;
407                 cache_len += cur->cache_len;
408                 count += cur->count;
409                 if (cur->cache_len) {
410                         if (fn) {
411                                 ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n", 
412                                         cur->len, cur->cache_len, cur->count, cur->fn, fn);
413                         } else {
414                                 ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n", 
415                                         cur->len, cur->cache_len, cur->count, cur->fn);
416                         }
417                 } else {
418                         if (fn) {
419                                 ast_cli(fd, "%10d bytes in %d allocations in function '%s' of '%s'\n", 
420                                         cur->len, cur->count, cur->fn, fn);
421                         } else {
422                                 ast_cli(fd, "%10d bytes in %d allocations in file '%s'\n", 
423                                         cur->len, cur->count, cur->fn);
424                         }
425                 }
426         }
427
428         if (cache_len)
429                 ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
430         else
431                 ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
432
433         return RESULT_SUCCESS;
434 }
435
436 static char show_memory_help[] = 
437 "Usage: memory show allocations [<file>]\n"
438 "       Dumps a list of all segments of allocated memory, optionally\n"
439 "limited to those from a specific file\n";
440
441 static char show_memory_summary_help[] = 
442 "Usage: memory show summary [<file>]\n"
443 "       Summarizes heap memory allocations by file, or optionally\n"
444 "by function, if a file is specified\n";
445
446 static struct ast_cli_entry cli_memory[] = {
447         { { "memory", "show", "allocations", NULL },
448         handle_show_memory, "Display outstanding memory allocations",
449         show_memory_help },
450
451         { { "memory", "show", "summary", NULL },
452         handle_show_memory_summary, "Summarize outstanding memory allocations",
453         show_memory_summary_help },
454 };
455
456 void __ast_mm_init(void)
457 {
458         char filename[PATH_MAX];
459
460         ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
461         
462         snprintf(filename, sizeof(filename), "%s/mmlog", (char *)ast_config_AST_LOG_DIR);
463         
464         if (option_verbose)
465                 ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename);
466         
467         if ((mmlog = fopen(filename, "a+"))) {
468                 fprintf(mmlog, "%ld - New session\n", time(NULL));
469                 fflush(mmlog);
470         }
471 }
472
473 #endif