Convert NEW_CLI to AST_CLI.
[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 char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
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         switch (cmd) {
321         case CLI_INIT:
322                 e->command = "memory show allocations";
323                 e->usage =
324                         "Usage: memory show allocations [<file>]\n"
325                         "       Dumps a list of all segments of allocated memory, optionally\n"
326                         "       limited to those from a specific file\n";
327                 break;
328         case CLI_GENERATE:
329                 return NULL;
330         }
331
332
333         if (a->argc > 3)
334                 fn = a->argv[3];
335
336         ast_mutex_lock(&showmemorylock);
337         for (x = 0; x < SOME_PRIME; x++) {
338                 for (reg = regions[x]; reg; reg = reg->next) {
339                         if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
340                                 fence = (unsigned int *)(reg->data + reg->len);
341                                 if (reg->fence != FENCE_MAGIC) {
342                                         astmm_log("WARNING: Low fence violation at %p, "
343                                                 "in %s of %s, line %d\n", reg->data, 
344                                                 reg->func, reg->file, reg->lineno);
345                                 }
346                                 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
347                                         astmm_log("WARNING: High fence violation at %p, in %s of %s, "
348                                                 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
349                                 }
350                         }
351                         if (!fn || !strcasecmp(fn, reg->file)) {
352                                 ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n", 
353                                         (int) reg->len, reg->cache ? " (cache)" : "", 
354                                         reg->func, reg->lineno, reg->file);
355                                 len += reg->len;
356                                 if (reg->cache)
357                                         cache_len += reg->len;
358                                 count++;
359                         }
360                 }
361         }
362         ast_mutex_unlock(&showmemorylock);
363         
364         if (cache_len)
365                 ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
366         else
367                 ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
368         
369         return CLI_SUCCESS;
370 }
371
372 static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
373 {
374         char *fn = NULL;
375         int x;
376         struct ast_region *reg;
377         unsigned int len = 0;
378         unsigned int cache_len = 0;
379         int count = 0;
380         struct file_summary {
381                 char fn[80];
382                 int len;
383                 int cache_len;
384                 int count;
385                 struct file_summary *next;
386         } *list = NULL, *cur;
387         
388         switch (cmd) {
389         case CLI_INIT:
390                 e->command = "memory show summary";
391                 e->usage =
392                         "Usage: memory show summary [<file>]\n"
393                         "       Summarizes heap memory allocations by file, or optionally\n"
394                         "by function, if a file is specified\n";
395                 break;
396         case CLI_GENERATE:
397                 return NULL;
398         }
399
400         if (a->argc > 3) 
401                 fn = a->argv[3];
402
403         ast_mutex_lock(&reglock);
404         for (x = 0; x < SOME_PRIME; x++) {
405                 for (reg = regions[x]; reg; reg = reg->next) {
406                         if (fn && strcasecmp(fn, reg->file))
407                                 continue;
408
409                         for (cur = list; cur; cur = cur->next) {
410                                 if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
411                                         break;
412                         }
413                         if (!cur) {
414                                 cur = alloca(sizeof(*cur));
415                                 memset(cur, 0, sizeof(*cur));
416                                 ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
417                                 cur->next = list;
418                                 list = cur;
419                         }
420
421                         cur->len += reg->len;
422                         if (reg->cache)
423                                 cur->cache_len += reg->len;
424                         cur->count++;
425                 }
426         }
427         ast_mutex_unlock(&reglock);
428         
429         /* Dump the whole list */
430         for (cur = list; cur; cur = cur->next) {
431                 len += cur->len;
432                 cache_len += cur->cache_len;
433                 count += cur->count;
434                 if (cur->cache_len) {
435                         if (fn) {
436                                 ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n", 
437                                         cur->len, cur->cache_len, cur->count, cur->fn, fn);
438                         } else {
439                                 ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n", 
440                                         cur->len, cur->cache_len, cur->count, cur->fn);
441                         }
442                 } else {
443                         if (fn) {
444                                 ast_cli(a->fd, "%10d bytes in %d allocations in function '%s' of '%s'\n", 
445                                         cur->len, cur->count, cur->fn, fn);
446                         } else {
447                                 ast_cli(a->fd, "%10d bytes in %d allocations in file '%s'\n", 
448                                         cur->len, cur->count, cur->fn);
449                         }
450                 }
451         }
452
453         if (cache_len)
454                 ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
455         else
456                 ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
457
458         return CLI_SUCCESS;
459 }
460
461 static struct ast_cli_entry cli_memory[] = {
462         AST_CLI(handle_memory_show, "Display outstanding memory allocations"),
463         AST_CLI(handle_memory_show_summary, "Summarize outstanding memory allocations"),
464 };
465
466 void __ast_mm_init(void)
467 {
468         char filename[PATH_MAX];
469
470         ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
471         
472         snprintf(filename, sizeof(filename), "%s/mmlog", (char *)ast_config_AST_LOG_DIR);
473         
474         if (option_verbose)
475                 ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename);
476         
477         if ((mmlog = fopen(filename, "a+"))) {
478                 fprintf(mmlog, "%ld - New session\n", time(NULL));
479                 fflush(mmlog);
480         }
481 }
482
483 #endif