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