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