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>
28 #ifdef __AST_DEBUG_MALLOC
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32 #include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
36 #include "asterisk/cli.h"
37 #include "asterisk/lock.h"
38 #include "asterisk/strings.h"
39 #include "asterisk/unaligned.h"
41 #define SOME_PRIME 563
53 /* Undefine all our macros */
63 #define FENCE_MAGIC 0xdeadbeef
67 /* NOTE: Be EXTREMELY careful with modifying this structure; the total size of this structure
68 must result in 'automatic' alignment so that the 'fence' field lands exactly at the end of
69 the structure in memory (and thus immediately before the allocated region the fence is
70 supposed to be used to monitor). In other words, we cannot allow the compiler to insert
71 any padding between this structure and anything following it, so add up the sizes of all the
72 fields and compare to sizeof(struct ast_region)... if they don't match, then the compiler
73 is padding the structure and either the fields need to be rearranged to eliminate internal
74 padding, or a dummy field will need to be inserted before the 'fence' field to push it to
75 the end of the actual space it will consume. Note that this must be checked for both 32-bit
76 and 64-bit platforms, as the sizes of pointers and 'size_t' differ on these platforms.
79 static struct ast_region {
80 struct ast_region *next;
86 unsigned int cache; /* region was allocated as part of a cache pool */
88 unsigned char data[0];
89 } *regions[SOME_PRIME];
92 (((unsigned long)(a)) % SOME_PRIME)
94 /*! Tracking this mutex will cause infinite recursion, as the mutex tracking
95 * code allocates memory */
96 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
98 #define astmm_log(...) \
100 fprintf(stderr, __VA_ARGS__); \
102 fprintf(mmlog, __VA_ARGS__); \
107 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)
109 struct ast_region *reg;
114 if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
115 astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
116 "at line %d of %s\n", (int) size, func, lineno, file);
119 ast_copy_string(reg->file, file, sizeof(reg->file));
120 ast_copy_string(reg->func, func, sizeof(reg->func));
121 reg->lineno = lineno;
127 reg->fence = FENCE_MAGIC;
128 fence = (ptr + reg->len);
129 put_unaligned_uint32(fence, FENCE_MAGIC);
131 ast_mutex_lock(®lock);
132 reg->next = regions[hash];
134 ast_mutex_unlock(®lock);
139 static inline size_t __ast_sizeof_region(void *ptr)
141 int hash = HASH(ptr);
142 struct ast_region *reg;
145 ast_mutex_lock(®lock);
146 for (reg = regions[hash]; reg; reg = reg->next) {
147 if (reg->data == ptr) {
152 ast_mutex_unlock(®lock);
157 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
160 struct ast_region *reg, *prev = NULL;
168 ast_mutex_lock(®lock);
169 for (reg = regions[hash]; reg; reg = reg->next) {
170 if (reg->data == ptr) {
172 prev->next = reg->next;
174 regions[hash] = reg->next;
179 ast_mutex_unlock(®lock);
182 fence = (unsigned int *)(reg->data + reg->len);
183 if (reg->fence != FENCE_MAGIC) {
184 astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
185 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
187 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
188 astmm_log("WARNING: High fence violation at %p, in %s of %s, "
189 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
193 astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
194 ptr, func, file, lineno);
198 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
202 if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0)))
203 memset(ptr, 0, size * nmemb);
208 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
212 if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1)))
213 memset(ptr, 0, size * nmemb);
218 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
220 return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
223 void __ast_free(void *ptr, const char *file, int lineno, const char *func)
225 __ast_free_region(ptr, file, lineno, func);
228 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
233 if (ptr && !(len = __ast_sizeof_region(ptr))) {
234 astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
235 "line %d\n", ptr, func, file, lineno);
239 if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
245 memcpy(tmp, ptr, len);
246 __ast_free_region(ptr, file, lineno, func);
252 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
261 if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
267 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
278 if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
284 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
293 size = vsnprintf(&s, 1, fmt, ap2);
295 if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
299 vsnprintf(*strp, size + 1, fmt, ap);
305 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
313 size = vsnprintf(&s, 1, fmt, ap2);
315 if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
319 vsnprintf(*strp, size + 1, fmt, ap);
324 static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
327 struct ast_region *reg;
329 unsigned int len = 0;
330 unsigned int cache_len = 0;
331 unsigned int count = 0;
336 e->command = "memory show allocations";
338 "Usage: memory show allocations [<file>]\n"
339 " Dumps a list of all segments of allocated memory, optionally\n"
340 " limited to those from a specific file\n";
350 ast_mutex_lock(®lock);
351 for (x = 0; x < SOME_PRIME; x++) {
352 for (reg = regions[x]; reg; reg = reg->next) {
353 if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
354 fence = (unsigned int *)(reg->data + reg->len);
355 if (reg->fence != FENCE_MAGIC) {
356 astmm_log("WARNING: Low fence violation at %p, "
357 "in %s of %s, line %d\n", reg->data,
358 reg->func, reg->file, reg->lineno);
360 if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
361 astmm_log("WARNING: High fence violation at %p, in %s of %s, "
362 "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
365 if (!fn || !strcasecmp(fn, reg->file)) {
366 ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n",
367 (int) reg->len, reg->cache ? " (cache)" : "",
368 reg->func, reg->lineno, reg->file);
371 cache_len += reg->len;
376 ast_mutex_unlock(®lock);
379 ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
381 ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
386 static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
390 struct ast_region *reg;
391 unsigned int len = 0;
392 unsigned int cache_len = 0;
394 struct file_summary {
399 struct file_summary *next;
400 } *list = NULL, *cur;
404 e->command = "memory show summary";
406 "Usage: memory show summary [<file>]\n"
407 " Summarizes heap memory allocations by file, or optionally\n"
408 "by function, if a file is specified\n";
417 ast_mutex_lock(®lock);
418 for (x = 0; x < SOME_PRIME; x++) {
419 for (reg = regions[x]; reg; reg = reg->next) {
420 if (fn && strcasecmp(fn, reg->file))
423 for (cur = list; cur; cur = cur->next) {
424 if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
428 cur = alloca(sizeof(*cur));
429 memset(cur, 0, sizeof(*cur));
430 ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
435 cur->len += reg->len;
437 cur->cache_len += reg->len;
441 ast_mutex_unlock(®lock);
443 /* Dump the whole list */
444 for (cur = list; cur; cur = cur->next) {
446 cache_len += cur->cache_len;
448 if (cur->cache_len) {
450 ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n",
451 cur->len, cur->cache_len, cur->count, cur->fn, fn);
453 ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n",
454 cur->len, cur->cache_len, cur->count, cur->fn);
458 ast_cli(a->fd, "%10d bytes in %d allocations in function '%s' of '%s'\n",
459 cur->len, cur->count, cur->fn, fn);
461 ast_cli(a->fd, "%10d bytes in %d allocations in file '%s'\n",
462 cur->len, cur->count, cur->fn);
468 ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
470 ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
475 static struct ast_cli_entry cli_memory[] = {
476 AST_CLI_DEFINE(handle_memory_show, "Display outstanding memory allocations"),
477 AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
480 void __ast_mm_init(void)
482 char filename[PATH_MAX];
483 size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
486 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);
489 ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
491 snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
493 ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
495 if ((mmlog = fopen(filename, "a+"))) {
496 fprintf(mmlog, "%ld - New session\n", (long)time(NULL));