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