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