2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
21 * \brief Memory Management
23 * \author Mark Spencer <markster@digium.com>
26 #ifdef __AST_DEBUG_MALLOC
30 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
37 #include "asterisk/cli.h"
38 #include "asterisk/logger.h"
39 #include "asterisk/options.h"
40 #include "asterisk/lock.h"
41 #include "asterisk/strings.h"
42 #include "asterisk/unaligned.h"
44 #define SOME_PRIME 563
56 /* Undefine all our macros */
66 #define FENCE_MAGIC 0xdeadbeef
70 /* NOTE: Be EXTREMELY careful with modifying this structure; the total size of this structure
71 must result in 'automatic' alignment so that the 'fence' field lands exactly at the end of
72 the structure in memory (and thus immediately before the allocated region the fence is
73 supposed to be used to monitor). In other words, we cannot allow the compiler to insert
74 any padding between this structure and anything following it, so add up the sizes of all the
75 fields and compare to sizeof(struct ast_region)... if they don't match, then the compiler
76 is padding the structure and either the fields need to be rearranged to eliminate internal
77 padding, or a dummy field will need to be inserted before the 'fence' field to push it to
78 the end of the actual space it will consume. Note that this must be checked for both 32-bit
79 and 64-bit platforms, as the sizes of pointers and 'size_t' differ on these platforms.
82 static struct ast_region
{
83 struct ast_region
*next
;
89 unsigned int cache
; /* region was allocated as part of a cache pool */
91 unsigned char data
[0];
92 } *regions
[SOME_PRIME
];
95 (((unsigned long)(a)) % SOME_PRIME)
97 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock
);
99 #define astmm_log(...) \
101 fprintf(stderr, __VA_ARGS__); \
103 fprintf(mmlog, __VA_ARGS__); \
108 static inline void *__ast_alloc_region(size_t size
, const enum func_type which
, const char *file
, int lineno
, const char *func
, unsigned int cache
)
110 struct ast_region
*reg
;
115 if (!(reg
= malloc(size
+ sizeof(*reg
) + sizeof(*fence
)))) {
116 astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
117 "at line %d of %s\n", (int) size
, func
, lineno
, file
);
120 ast_copy_string(reg
->file
, file
, sizeof(reg
->file
));
121 ast_copy_string(reg
->func
, func
, sizeof(reg
->func
));
122 reg
->lineno
= lineno
;
128 reg
->fence
= FENCE_MAGIC
;
129 fence
= (ptr
+ reg
->len
);
130 put_unaligned_uint32(fence
, FENCE_MAGIC
);
132 ast_mutex_lock(®lock
);
133 reg
->next
= regions
[hash
];
135 ast_mutex_unlock(®lock
);
140 static inline size_t __ast_sizeof_region(void *ptr
)
142 int hash
= HASH(ptr
);
143 struct ast_region
*reg
;
146 ast_mutex_lock(®lock
);
147 for (reg
= regions
[hash
]; reg
; reg
= reg
->next
) {
148 if (reg
->data
== ptr
) {
153 ast_mutex_unlock(®lock
);
158 static void __ast_free_region(void *ptr
, const char *file
, int lineno
, const char *func
)
160 int hash
= HASH(ptr
);
161 struct ast_region
*reg
, *prev
= NULL
;
164 ast_mutex_lock(®lock
);
165 for (reg
= regions
[hash
]; reg
; reg
= reg
->next
) {
166 if (reg
->data
== ptr
) {
168 prev
->next
= reg
->next
;
170 regions
[hash
] = reg
->next
;
175 ast_mutex_unlock(®lock
);
178 fence
= (unsigned int *)(reg
->data
+ reg
->len
);
179 if (reg
->fence
!= FENCE_MAGIC
) {
180 astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
181 "line %d\n", reg
->data
, reg
->func
, reg
->file
, reg
->lineno
);
183 if (get_unaligned_uint32(fence
) != FENCE_MAGIC
) {
184 astmm_log("WARNING: High fence violation at %p, in %s of %s, "
185 "line %d\n", reg
->data
, reg
->func
, reg
->file
, reg
->lineno
);
189 astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
190 ptr
, func
, file
, lineno
);
194 void *__ast_calloc(size_t nmemb
, size_t size
, const char *file
, int lineno
, const char *func
)
198 if ((ptr
= __ast_alloc_region(size
* nmemb
, FUNC_CALLOC
, file
, lineno
, func
, 0)))
199 memset(ptr
, 0, size
* nmemb
);
204 void *__ast_calloc_cache(size_t nmemb
, size_t size
, const char *file
, int lineno
, const char *func
)
208 if ((ptr
= __ast_alloc_region(size
* nmemb
, FUNC_CALLOC
, file
, lineno
, func
, 1)))
209 memset(ptr
, 0, size
* nmemb
);
214 void *__ast_malloc(size_t size
, const char *file
, int lineno
, const char *func
)
216 return __ast_alloc_region(size
, FUNC_MALLOC
, file
, lineno
, func
, 0);
219 void __ast_free(void *ptr
, const char *file
, int lineno
, const char *func
)
221 __ast_free_region(ptr
, file
, lineno
, func
);
224 void *__ast_realloc(void *ptr
, size_t size
, const char *file
, int lineno
, const char *func
)
229 if (ptr
&& !(len
= __ast_sizeof_region(ptr
))) {
230 astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
231 "line %d\n", ptr
, func
, file
, lineno
);
235 if (!(tmp
= __ast_alloc_region(size
, FUNC_REALLOC
, file
, lineno
, func
, 0)))
241 memcpy(tmp
, ptr
, len
);
242 __ast_free_region(ptr
, file
, lineno
, func
);
248 char *__ast_strdup(const char *s
, const char *file
, int lineno
, const char *func
)
257 if ((ptr
= __ast_alloc_region(len
, FUNC_STRDUP
, file
, lineno
, func
, 0)))
263 char *__ast_strndup(const char *s
, size_t n
, const char *file
, int lineno
, const char *func
)
274 if ((ptr
= __ast_alloc_region(len
, FUNC_STRNDUP
, file
, lineno
, func
, 0)))
280 int __ast_asprintf(const char *file
, int lineno
, const char *func
, char **strp
, const char *fmt
, ...)
289 size
= vsnprintf(&s
, 1, fmt
, ap2
);
291 if (!(*strp
= __ast_alloc_region(size
+ 1, FUNC_ASPRINTF
, file
, lineno
, func
, 0))) {
295 vsnprintf(*strp
, size
+ 1, fmt
, ap
);
301 int __ast_vasprintf(char **strp
, const char *fmt
, va_list ap
, const char *file
, int lineno
, const char *func
)
309 size
= vsnprintf(&s
, 1, fmt
, ap2
);
311 if (!(*strp
= __ast_alloc_region(size
+ 1, FUNC_VASPRINTF
, file
, lineno
, func
, 0))) {
315 vsnprintf(*strp
, size
+ 1, fmt
, ap
);
320 static int handle_show_memory(int fd
, int argc
, char *argv
[])
323 struct ast_region
*reg
;
325 unsigned int len
= 0;
326 unsigned int cache_len
= 0;
327 unsigned int count
= 0;
333 ast_mutex_lock(®lock
);
334 for (x
= 0; x
< SOME_PRIME
; x
++) {
335 for (reg
= regions
[x
]; reg
; reg
= reg
->next
) {
336 if (!fn
|| !strcasecmp(fn
, reg
->file
) || !strcasecmp(fn
, "anomolies")) {
337 fence
= (unsigned int *)(reg
->data
+ reg
->len
);
338 if (reg
->fence
!= FENCE_MAGIC
) {
339 astmm_log("WARNING: Low fence violation at %p, "
340 "in %s of %s, line %d\n", reg
->data
,
341 reg
->func
, reg
->file
, reg
->lineno
);
343 if (get_unaligned_uint32(fence
) != FENCE_MAGIC
) {
344 astmm_log("WARNING: High fence violation at %p, in %s of %s, "
345 "line %d\n", reg
->data
, reg
->func
, reg
->file
, reg
->lineno
);
348 if (!fn
|| !strcasecmp(fn
, reg
->file
)) {
349 ast_cli(fd
, "%10d bytes allocated%s in %20s at line %5d of %s\n",
350 (int) reg
->len
, reg
->cache
? " (cache)" : "",
351 reg
->func
, reg
->lineno
, reg
->file
);
354 cache_len
+= reg
->len
;
359 ast_mutex_unlock(®lock
);
362 ast_cli(fd
, "%d bytes allocated (%d in caches) in %d allocations\n", len
, cache_len
, count
);
364 ast_cli(fd
, "%d bytes allocated in %d allocations\n", len
, count
);
366 return RESULT_SUCCESS
;
369 static int handle_show_memory_summary(int fd
, int argc
, char *argv
[])
373 struct ast_region
*reg
;
374 unsigned int len
= 0;
375 unsigned int cache_len
= 0;
377 struct file_summary
{
382 struct file_summary
*next
;
383 } *list
= NULL
, *cur
;
388 ast_mutex_lock(®lock
);
389 for (x
= 0; x
< SOME_PRIME
; x
++) {
390 for (reg
= regions
[x
]; reg
; reg
= reg
->next
) {
391 if (fn
&& strcasecmp(fn
, reg
->file
))
394 for (cur
= list
; cur
; cur
= cur
->next
) {
395 if ((!fn
&& !strcmp(cur
->fn
, reg
->file
)) || (fn
&& !strcmp(cur
->fn
, reg
->func
)))
399 cur
= alloca(sizeof(*cur
));
400 memset(cur
, 0, sizeof(*cur
));
401 ast_copy_string(cur
->fn
, fn
? reg
->func
: reg
->file
, sizeof(cur
->fn
));
406 cur
->len
+= reg
->len
;
408 cur
->cache_len
+= reg
->len
;
412 ast_mutex_unlock(®lock
);
414 /* Dump the whole list */
415 for (cur
= list
; cur
; cur
= cur
->next
) {
417 cache_len
+= cur
->cache_len
;
419 if (cur
->cache_len
) {
421 ast_cli(fd
, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n",
422 cur
->len
, cur
->cache_len
, cur
->count
, cur
->fn
, fn
);
424 ast_cli(fd
, "%10d bytes (%10d cache) in %d allocations in file '%s'\n",
425 cur
->len
, cur
->cache_len
, cur
->count
, cur
->fn
);
429 ast_cli(fd
, "%10d bytes in %d allocations in function '%s' of '%s'\n",
430 cur
->len
, cur
->count
, cur
->fn
, fn
);
432 ast_cli(fd
, "%10d bytes in %d allocations in file '%s'\n",
433 cur
->len
, cur
->count
, cur
->fn
);
439 ast_cli(fd
, "%d bytes allocated (%d in caches) in %d allocations\n", len
, cache_len
, count
);
441 ast_cli(fd
, "%d bytes allocated in %d allocations\n", len
, count
);
443 return RESULT_SUCCESS
;
446 static char show_memory_help
[] =
447 "Usage: memory show allocations [<file>]\n"
448 " Dumps a list of all segments of allocated memory, optionally\n"
449 "limited to those from a specific file\n";
451 static char show_memory_summary_help
[] =
452 "Usage: memory show summary [<file>]\n"
453 " Summarizes heap memory allocations by file, or optionally\n"
454 "by function, if a file is specified\n";
456 static struct ast_cli_entry cli_show_memory_allocations_deprecated
= {
457 { "show", "memory", "allocations", NULL
},
458 handle_show_memory
, NULL
,
461 static struct ast_cli_entry cli_show_memory_summary_deprecated
= {
462 { "show", "memory", "summary", NULL
},
463 handle_show_memory_summary
, NULL
,
466 static struct ast_cli_entry cli_memory
[] = {
467 { { "memory", "show", "allocations", NULL
},
468 handle_show_memory
, "Display outstanding memory allocations",
469 show_memory_help
, NULL
, &cli_show_memory_allocations_deprecated
},
471 { { "memory", "show", "summary", NULL
},
472 handle_show_memory_summary
, "Summarize outstanding memory allocations",
473 show_memory_summary_help
, NULL
, &cli_show_memory_summary_deprecated
},
476 void __ast_mm_init(void)
478 char filename
[PATH_MAX
];
479 size_t pad
= sizeof(struct ast_region
) - offsetof(struct ast_region
, data
);
482 ast_log(LOG_ERROR
, "struct ast_region has %d bytes of padding! This must be eliminated for low-fence checking to work properly!\n", (int) pad
);
485 ast_cli_register_multiple(cli_memory
, sizeof(cli_memory
) / sizeof(struct ast_cli_entry
));
487 snprintf(filename
, sizeof(filename
), "%s/mmlog", (char *)ast_config_AST_LOG_DIR
);
490 ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename
);
492 if ((mmlog
= fopen(filename
, "a+"))) {
493 fprintf(mmlog
, "%ld - New session\n", (long)time(NULL
));