make app_queue 1.2 jump compliant (issue #5580)
[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 /*! \file
20  *
21  * \brief 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 #define FENCE_MAGIC 0xdeadbeef
60
61 static FILE *mmlog;
62
63 static struct ast_region {
64         struct ast_region *next;
65         char file[40];
66         char func[40];
67         int lineno;
68         int which;
69         size_t len;
70         unsigned int fence;
71         unsigned char data[0];
72 } *regions[SOME_PRIME];
73
74 #define HASH(a) \
75         (((unsigned long)(a)) % SOME_PRIME)
76         
77 AST_MUTEX_DEFINE_STATIC(reglock);
78 AST_MUTEX_DEFINE_STATIC(showmemorylock);
79
80 static inline void *__ast_alloc_region(size_t size, int which, const char *file, int lineno, const char *func)
81 {
82         struct ast_region *reg;
83         void *ptr = NULL;
84         unsigned int *fence;
85         int hash;
86         reg = malloc(size + sizeof(struct ast_region) + sizeof(unsigned int));
87         ast_mutex_lock(&reglock);
88         if (reg) {
89                 ast_copy_string(reg->file, file, sizeof(reg->file));
90                 reg->file[sizeof(reg->file) - 1] = '\0';
91                 ast_copy_string(reg->func, func, sizeof(reg->func));
92                 reg->func[sizeof(reg->func) - 1] = '\0';
93                 reg->lineno = lineno;
94                 reg->len = size;
95                 reg->which = which;
96                 ptr = reg->data;
97                 hash = HASH(ptr);
98                 reg->next = regions[hash];
99                 regions[hash] = reg;
100                 reg->fence = FENCE_MAGIC;
101                 fence = (ptr + reg->len);
102                 *fence = FENCE_MAGIC;
103         }
104         ast_mutex_unlock(&reglock);
105         if (!reg) {
106                 fprintf(stderr, "Out of memory :(\n");
107                 if (mmlog) {
108                         fprintf(mmlog, "%ld - Out of memory\n", time(NULL));
109                         fflush(mmlog);
110                 }
111         }
112         return ptr;
113 }
114
115 static inline size_t __ast_sizeof_region(void *ptr)
116 {
117         int hash = HASH(ptr);
118         struct ast_region *reg;
119         size_t len = 0;
120         
121         ast_mutex_lock(&reglock);
122         reg = regions[hash];
123         while (reg) {
124                 if (reg->data == ptr) {
125                         len = reg->len;
126                         break;
127                 }
128                 reg = reg->next;
129         }
130         ast_mutex_unlock(&reglock);
131         return len;
132 }
133
134 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
135 {
136         int hash = HASH(ptr);
137         struct ast_region *reg, *prev = NULL;
138         unsigned int *fence;
139
140         ast_mutex_lock(&reglock);
141         reg = regions[hash];
142         while (reg) {
143                 if (reg->data == ptr) {
144                         if (prev) {
145                                 prev->next = reg->next;
146                         } else {
147                                 regions[hash] = reg->next;
148                         }
149                         break;
150                 }
151                 prev = reg;
152                 reg = reg->next;
153         }
154         ast_mutex_unlock(&reglock);
155         if (reg) {
156                 fence = (unsigned int *)(reg->data + reg->len);
157                 if (reg->fence != FENCE_MAGIC) {
158                         fprintf(stderr, "WARNING: Low fence violation at %p, in %s of %s, line %d\n", reg->data, reg->func, reg->file, reg->lineno);
159                         if (mmlog) {
160                                 fprintf(mmlog, "%ld - WARNING: Low fence violation at %p, in %s of %s, line %d\n", time(NULL), reg->data, reg->func, reg->file, reg->lineno);
161                                 fflush(mmlog);
162                         }
163                 }
164                 if (*fence != FENCE_MAGIC) {
165                         fprintf(stderr, "WARNING: High fence violation at %p, in %s of %s, line %d\n", reg->data, reg->func, reg->file, reg->lineno);
166                         if (mmlog) {
167                                 fprintf(mmlog, "%ld - WARNING: High fence violation at %p, in %s of %s, line %d\n", time(NULL), reg->data, reg->func, reg->file, reg->lineno);
168                                 fflush(mmlog);
169                         }
170                 }
171                 free(reg);
172         } else {
173                 fprintf(stderr, "WARNING: Freeing unused memory at %p, in %s of %s, line %d\n", ptr, func, file, lineno);
174                 if (mmlog) {
175                         fprintf(mmlog, "%ld - WARNING: Freeing unused memory at %p, in %s of %s, line %d\n", time(NULL), ptr, func, file, lineno);
176                         fflush(mmlog);
177                 }
178         }
179 }
180
181 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
182 {
183         void *ptr;
184         ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func);
185         if (ptr) 
186                 memset(ptr, 0, size * nmemb);
187         return ptr;
188 }
189
190 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func) 
191 {
192         return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func);
193 }
194
195 void __ast_free(void *ptr, const char *file, int lineno, const char *func) 
196 {
197         __ast_free_region(ptr, file, lineno, func);
198 }
199
200 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func) 
201 {
202         void *tmp;
203         size_t len = 0;
204         if (ptr) {
205                 len = __ast_sizeof_region(ptr);
206                 if (!len) {
207                         fprintf(stderr, "WARNING: Realloc of unalloced memory at %p, in %s of %s, line %d\n", ptr, func, file, lineno);
208                         if (mmlog) {
209                                 fprintf(mmlog, "%ld - WARNING: Realloc of unalloced memory at %p, in %s of %s, line %d\n", time(NULL), ptr, func, file, lineno);
210                                 fflush(mmlog);
211                         }
212                         return NULL;
213                 }
214         }
215         tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func);
216         if (tmp) {
217                 if (len > size)
218                         len = size;
219                 if (ptr) {
220                         memcpy(tmp, ptr, len);
221                         __ast_free_region(ptr, file, lineno, func);
222                 }
223         }
224         return tmp;
225 }
226
227 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func) 
228 {
229         size_t len;
230         void *ptr;
231         if (!s)
232                 return NULL;
233         len = strlen(s) + 1;
234         ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func);
235         if (ptr)
236                 strcpy(ptr, s);
237         return ptr;
238 }
239
240 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func) 
241 {
242         size_t len;
243         void *ptr;
244         if (!s)
245                 return NULL;
246         len = strlen(s) + 1;
247         if (len > n)
248                 len = n;
249         ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func);
250         if (ptr)
251                 strcpy(ptr, s);
252         return ptr;
253 }
254
255 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func) 
256 {
257         int n, size = strlen(fmt) + 1;
258         if ((*strp = __ast_alloc_region(size, FUNC_VASPRINTF, file, lineno, func)) == NULL)
259                 return -1; 
260         for (;;) {
261                 n = vsnprintf(*strp, size, fmt, ap);
262                 if (n > -1 && n < size)
263                         return n;
264                 if (n > -1) {   /* glibc 2.1 */
265                         size = n+1;
266                 } else {        /* glibc 2.0 */
267                         size *= 2;
268                 }
269                 if ((*strp = __ast_realloc(*strp, size, file, lineno, func)) == NULL)
270                         return -1;
271         }
272 }
273
274 static int handle_show_memory(int fd, int argc, char *argv[])
275 {
276         char *fn = NULL;
277         int x;
278         struct ast_region *reg;
279         unsigned int len = 0;
280         int count = 0;
281         unsigned int *fence;
282         if (argc > 3) 
283                 fn = argv[3];
284
285         /* try to lock applications list ... */
286         ast_mutex_lock(&showmemorylock);
287
288         for (x = 0; x < SOME_PRIME; x++) {
289                 reg = regions[x];
290                 while (reg) {
291                         if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
292                                 fence = (unsigned int *)(reg->data + reg->len);
293                                 if (reg->fence != FENCE_MAGIC) {
294                                         fprintf(stderr, "WARNING: Low fence violation at %p, in %s of %s, line %d\n", reg->data, reg->func, reg->file, reg->lineno);
295                                         if (mmlog) {
296                                                 fprintf(mmlog, "%ld - WARNING: Low fence violation at %p, in %s of %s, line %d\n", time(NULL), reg->data, reg->func, reg-> file, reg->lineno);
297                                                 fflush(mmlog);
298                                         }
299                                 }
300                                 if (*fence != FENCE_MAGIC) {
301                                         fprintf(stderr, "WARNING: High fence violation at %p, in %s of %s, line %d\n", reg->data, reg->func, reg->file, reg->lineno);
302                                         if (mmlog) {
303                                                 fprintf(mmlog, "%ld - WARNING: High fence violation at %p, in %s of %s, line %d\n", time(NULL), reg->data, reg->func, reg->file, reg->lineno);
304                                                 fflush(mmlog);
305                                         }
306                                 }
307                         }
308                         if (!fn || !strcasecmp(fn, reg->file)) {
309                                 ast_cli(fd, "%10d bytes allocated in %20s at line %5d of %s\n", (int) reg->len, reg->func, reg->lineno, reg->file);
310                                 len += reg->len;
311                                 count++;
312                         }
313                         reg = reg->next;
314                 }
315         }
316         ast_cli(fd, "%d bytes allocated %d units total\n", len, count);
317         ast_mutex_unlock(&showmemorylock);
318         return RESULT_SUCCESS;
319 }
320
321 struct file_summary {
322         char fn[80];
323         int len;
324         int count;
325         struct file_summary *next;
326 };
327
328 static int handle_show_memory_summary(int fd, int argc, char *argv[])
329 {
330         char *fn = NULL;
331         int x;
332         struct ast_region *reg;
333         unsigned int len = 0;
334         int count = 0;
335         struct file_summary *list = NULL, *cur;
336         
337         if (argc > 3) 
338                 fn = argv[3];
339
340         /* try to lock applications list ... */
341         ast_mutex_lock(&reglock);
342
343         for (x = 0; x < SOME_PRIME; x++) {
344                 reg = regions[x];
345                 while (reg) {
346                         if (!fn || !strcasecmp(fn, reg->file)) {
347                                 cur = list;
348                                 while (cur) {
349                                         if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
350                                                 break;
351                                         cur = cur->next;
352                                 }
353                                 if (!cur) {
354                                         cur = alloca(sizeof(struct file_summary));
355                                         memset(cur, 0, sizeof(struct file_summary));
356                                         ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
357                                         cur->next = list;
358                                         list = cur;
359                                 }
360                                 cur->len += reg->len;
361                                 cur->count++;
362                         }
363                         reg = reg->next;
364                 }
365         }
366         ast_mutex_unlock(&reglock);
367         
368         /* Dump the whole list */
369         while (list) {
370                 cur = list;
371                 len += list->len;
372                 count += list->count;
373                 if (fn) {
374                         ast_cli(fd, "%10d bytes in %5d allocations in function '%s' of '%s'\n", list->len, list->count, list->fn, fn);
375                 } else {
376                         ast_cli(fd, "%10d bytes in %5d allocations in file '%s'\n", list->len, list->count, list->fn);
377                 }
378                 list = list->next;
379 #if 0
380                 free(cur);
381 #endif          
382         }
383         ast_cli(fd, "%d bytes allocated %d units total\n", len, count);
384         return RESULT_SUCCESS;
385 }
386
387 static char show_memory_help[] = 
388 "Usage: show memory allocations [<file>]\n"
389 "       Dumps a list of all segments of allocated memory, optionally\n"
390 "limited to those from a specific file\n";
391
392 static char show_memory_summary_help[] = 
393 "Usage: show memory summary [<file>]\n"
394 "       Summarizes heap memory allocations by file, or optionally\n"
395 "by function, if a file is specified\n";
396
397 static struct ast_cli_entry show_memory_allocations_cli = 
398         { { "show", "memory", "allocations", NULL }, 
399         handle_show_memory, "Display outstanding memory allocations",
400         show_memory_help };
401
402 static struct ast_cli_entry show_memory_summary_cli = 
403         { { "show", "memory", "summary", NULL }, 
404         handle_show_memory_summary, "Summarize outstanding memory allocations",
405         show_memory_summary_help };
406
407 void __ast_mm_init(void)
408 {
409         char filename[80] = "";
410         ast_cli_register(&show_memory_allocations_cli);
411         ast_cli_register(&show_memory_summary_cli);
412         
413         snprintf(filename, sizeof(filename), "%s/mmlog", (char *)ast_config_AST_LOG_DIR);
414         mmlog = fopen(filename, "a+");
415         if (option_verbose)
416                 ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename);
417         if (mmlog) {
418                 fprintf(mmlog, "%ld - New session\n", time(NULL));
419                 fflush(mmlog);
420         }
421 }
422
423 #endif