make MALLOC_DEBUG build work properly (issue #4970 with additional changes)
[asterisk/asterisk.git] / astmm.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Memory Management
5  * 
6  * Copyright (C) 2002-2005, Mark Spencer
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #ifdef __AST_DEBUG_MALLOC
15
16 #include <malloc.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <time.h>
20
21 #include "asterisk.h"
22
23 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
24
25 #include "asterisk/cli.h"
26 #include "asterisk/logger.h"
27 #include "asterisk/options.h"
28 #include "asterisk/lock.h"
29 #include "asterisk/strings.h"
30
31 #define SOME_PRIME 563
32
33 #define FUNC_CALLOC     1
34 #define FUNC_MALLOC     2
35 #define FUNC_REALLOC    3
36 #define FUNC_STRDUP     4
37 #define FUNC_STRNDUP    5
38 #define FUNC_VASPRINTF  6
39
40 /* Undefine all our macros */
41 #undef malloc
42 #undef calloc
43 #undef realloc
44 #undef strdup
45 #undef strndup
46 #undef free
47 #undef vasprintf
48
49 static FILE *mmlog;
50
51 static struct ast_region {
52         struct ast_region *next;
53         char file[40];
54         char func[40];
55         int lineno;
56         int which;
57         size_t len;
58         unsigned char data[0];
59 } *regions[SOME_PRIME];
60
61 #define HASH(a) \
62         (((unsigned long)(a)) % SOME_PRIME)
63         
64 AST_MUTEX_DEFINE_STATIC(reglock);
65 AST_MUTEX_DEFINE_STATIC(showmemorylock);
66
67 static inline void *__ast_alloc_region(size_t size, int which, const char *file, int lineno, const char *func)
68 {
69         struct ast_region *reg;
70         void *ptr = NULL;
71         int hash;
72         reg = malloc(size + sizeof(struct ast_region));
73         ast_mutex_lock(&reglock);
74         if (reg) {
75                 ast_copy_string(reg->file, file, sizeof(reg->file));
76                 reg->file[sizeof(reg->file) - 1] = '\0';
77                 ast_copy_string(reg->func, func, sizeof(reg->func));
78                 reg->func[sizeof(reg->func) - 1] = '\0';
79                 reg->lineno = lineno;
80                 reg->len = size;
81                 reg->which = which;
82                 ptr = reg->data;
83                 hash = HASH(ptr);
84                 reg->next = regions[hash];
85                 regions[hash] = reg;
86         }
87         ast_mutex_unlock(&reglock);
88         if (!reg) {
89                 fprintf(stderr, "Out of memory :(\n");
90                 if (mmlog) {
91                         fprintf(mmlog, "%ld - Out of memory\n", time(NULL));
92                         fflush(mmlog);
93                 }
94         }
95         return ptr;
96 }
97
98 static inline size_t __ast_sizeof_region(void *ptr)
99 {
100         int hash = HASH(ptr);
101         struct ast_region *reg;
102         size_t len = 0;
103         
104         ast_mutex_lock(&reglock);
105         reg = regions[hash];
106         while (reg) {
107                 if (reg->data == ptr) {
108                         len = reg->len;
109                         break;
110                 }
111                 reg = reg->next;
112         }
113         ast_mutex_unlock(&reglock);
114         return len;
115 }
116
117 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
118 {
119         int hash = HASH(ptr);
120         struct ast_region *reg, *prev = NULL;
121         ast_mutex_lock(&reglock);
122         reg = regions[hash];
123         while (reg) {
124                 if (reg->data == ptr) {
125                         if (prev) {
126                                 prev->next = reg->next;
127                         } else {
128                                 regions[hash] = reg->next;
129                         }
130                         break;
131                 }
132                 prev = reg;
133                 reg = reg->next;
134         }
135         ast_mutex_unlock(&reglock);
136         if (reg) {
137                 free(reg);
138         } else {
139                 fprintf(stderr, "WARNING: Freeing unused memory at %p, in %s of %s, line %d\n", ptr, func, file, lineno);
140                 if (mmlog) {
141                         fprintf(mmlog, "%ld - WARNING: Freeing unused memory at %p, in %s of %s, line %d\n", time(NULL), ptr, func, file, lineno);
142                         fflush(mmlog);
143                 }
144         }
145 }
146
147 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
148 {
149         void *ptr;
150         ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func);
151         if (ptr) 
152                 memset(ptr, 0, size * nmemb);
153         return ptr;
154 }
155
156 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func) 
157 {
158         return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func);
159 }
160
161 void __ast_free(void *ptr, const char *file, int lineno, const char *func) 
162 {
163         __ast_free_region(ptr, file, lineno, func);
164 }
165
166 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func) 
167 {
168         void *tmp;
169         size_t len = 0;
170         if (ptr) {
171                 len = __ast_sizeof_region(ptr);
172                 if (!len) {
173                         fprintf(stderr, "WARNING: Realloc of unalloced memory at %p, in %s of %s, line %d\n", ptr, func, file, lineno);
174                         if (mmlog) {
175                                 fprintf(mmlog, "%ld - WARNING: Realloc of unalloced memory at %p, in %s of %s, line %d\n", time(NULL), ptr, func, file, lineno);
176                                 fflush(mmlog);
177                         }
178                         return NULL;
179                 }
180         }
181         tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func);
182         if (tmp) {
183                 if (len > size)
184                         len = size;
185                 if (ptr) {
186                         memcpy(tmp, ptr, len);
187                         __ast_free_region(ptr, file, lineno, func);
188                 }
189         }
190         return tmp;
191 }
192
193 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func) 
194 {
195         size_t len;
196         void *ptr;
197         if (!s)
198                 return NULL;
199         len = strlen(s) + 1;
200         ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func);
201         if (ptr)
202                 strcpy(ptr, s);
203         return ptr;
204 }
205
206 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func) 
207 {
208         size_t len;
209         void *ptr;
210         if (!s)
211                 return NULL;
212         len = strlen(s) + 1;
213         if (len > n)
214                 len = n;
215         ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func);
216         if (ptr)
217                 strcpy(ptr, s);
218         return ptr;
219 }
220
221 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func) 
222 {
223         int n, size = strlen(fmt) + 1;
224         if ((*strp = __ast_alloc_region(size, FUNC_VASPRINTF, file, lineno, func)) == NULL)
225                 return -1; 
226         for (;;) {
227                 n = vsnprintf(*strp, size, fmt, ap);
228                 if (n > -1 && n < size)
229                         return n;
230                 if (n > -1) {   /* glibc 2.1 */
231                         size = n+1;
232                 } else {        /* glibc 2.0 */
233                         size *= 2;
234                 }
235                 if ((*strp = __ast_realloc(*strp, size, file, lineno, func)) == NULL)
236                         return -1;
237         }
238 }
239
240 static int handle_show_memory(int fd, int argc, char *argv[])
241 {
242         char *fn = NULL;
243         int x;
244         struct ast_region *reg;
245         unsigned int len = 0;
246         int count = 0;
247         if (argc > 3) 
248                 fn = argv[3];
249
250         /* try to lock applications list ... */
251         ast_mutex_lock(&showmemorylock);
252
253         for (x = 0; x < SOME_PRIME; x++) {
254                 reg = regions[x];
255                 while (reg) {
256                         if (!fn || !strcasecmp(fn, reg->file)) {
257                                 ast_cli(fd, "%10d bytes allocated in %20s at line %5d of %s\n", (int) reg->len, reg->func, reg->lineno, reg->file);
258                                 len += reg->len;
259                                 count++;
260                         }
261                         reg = reg->next;
262                 }
263         }
264         ast_cli(fd, "%d bytes allocated %d units total\n", len, count);
265         ast_mutex_unlock(&showmemorylock);
266         return RESULT_SUCCESS;
267 }
268
269 struct file_summary {
270         char fn[80];
271         int len;
272         int count;
273         struct file_summary *next;
274 };
275
276 static int handle_show_memory_summary(int fd, int argc, char *argv[])
277 {
278         char *fn = NULL;
279         int x;
280         struct ast_region *reg;
281         unsigned int len = 0;
282         int count = 0;
283         struct file_summary *list = NULL, *cur;
284         
285         if (argc > 3) 
286                 fn = argv[3];
287
288         /* try to lock applications list ... */
289         ast_mutex_lock(&reglock);
290
291         for (x = 0; x < SOME_PRIME; x++) {
292                 reg = regions[x];
293                 while (reg) {
294                         if (!fn || !strcasecmp(fn, reg->file)) {
295                                 cur = list;
296                                 while (cur) {
297                                         if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
298                                                 break;
299                                         cur = cur->next;
300                                 }
301                                 if (!cur) {
302                                         cur = alloca(sizeof(struct file_summary));
303                                         memset(cur, 0, sizeof(struct file_summary));
304                                         ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
305                                         cur->next = list;
306                                         list = cur;
307                                 }
308                                 cur->len += reg->len;
309                                 cur->count++;
310                         }
311                         reg = reg->next;
312                 }
313         }
314         ast_mutex_unlock(&reglock);
315         
316         /* Dump the whole list */
317         while (list) {
318                 cur = list;
319                 len += list->len;
320                 count += list->count;
321                 if (fn) {
322                         ast_cli(fd, "%10d bytes in %5d allocations in function '%s' of '%s'\n", list->len, list->count, list->fn, fn);
323                 } else {
324                         ast_cli(fd, "%10d bytes in %5d allocations in file '%s'\n", list->len, list->count, list->fn);
325                 }
326                 list = list->next;
327 #if 0
328                 free(cur);
329 #endif          
330         }
331         ast_cli(fd, "%d bytes allocated %d units total\n", len, count);
332         return RESULT_SUCCESS;
333 }
334
335 static char show_memory_help[] = 
336 "Usage: show memory allocations [<file>]\n"
337 "       Dumps a list of all segments of allocated memory, optionally\n"
338 "limited to those from a specific file\n";
339
340 static char show_memory_summary_help[] = 
341 "Usage: show memory summary [<file>]\n"
342 "       Summarizes heap memory allocations by file, or optionally\n"
343 "by function, if a file is specified\n";
344
345 static struct ast_cli_entry show_memory_allocations_cli = 
346         { { "show", "memory", "allocations", NULL }, 
347         handle_show_memory, "Display outstanding memory allocations",
348         show_memory_help };
349
350 static struct ast_cli_entry show_memory_summary_cli = 
351         { { "show", "memory", "summary", NULL }, 
352         handle_show_memory_summary, "Summarize outstanding memory allocations",
353         show_memory_summary_help };
354
355 void __ast_mm_init(void)
356 {
357         char filename[80] = "";
358         ast_cli_register(&show_memory_allocations_cli);
359         ast_cli_register(&show_memory_summary_cli);
360         
361         snprintf(filename, sizeof(filename), "%s/mmlog", (char *)ast_config_AST_LOG_DIR);
362         mmlog = fopen(filename, "a+");
363         if (option_verbose)
364                 ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename);
365         if (mmlog) {
366                 fprintf(mmlog, "%ld - New session\n", time(NULL));
367                 fflush(mmlog);
368         }
369 }
370
371 #endif