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