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