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