/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2006, Digium, Inc. * * Mark Spencer * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact * any of the maintainers of this project for assistance; * the project provides a web site, mailing lists and IRC * channels for your use. * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. */ /*! \file * * \brief Memory Management * * \author Mark Spencer */ #ifdef __AST_DEBUG_MALLOC #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include #include #include #include "asterisk/cli.h" #include "asterisk/logger.h" #include "asterisk/options.h" #include "asterisk/lock.h" #include "asterisk/strings.h" #include "asterisk/unaligned.h" #define SOME_PRIME 563 enum func_type { FUNC_CALLOC = 1, FUNC_MALLOC, FUNC_REALLOC, FUNC_STRDUP, FUNC_STRNDUP, FUNC_VASPRINTF, FUNC_ASPRINTF }; /* Undefine all our macros */ #undef malloc #undef calloc #undef realloc #undef strdup #undef strndup #undef free #undef vasprintf #undef asprintf #define FENCE_MAGIC 0xdeadbeef static FILE *mmlog; static struct ast_region { struct ast_region *next; char file[40]; char func[40]; unsigned int lineno; enum func_type which; unsigned int cache; /* region was allocated as part of a cache pool */ size_t len; unsigned int fence; unsigned char data[0]; } *regions[SOME_PRIME]; #define HASH(a) \ (((unsigned long)(a)) % SOME_PRIME) AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock); #define astmm_log(...) \ do { \ fprintf(stderr, __VA_ARGS__); \ if (mmlog) { \ fprintf(mmlog, __VA_ARGS__); \ fflush(mmlog); \ } \ } while (0) 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) { struct ast_region *reg; void *ptr = NULL; unsigned int *fence; int hash; if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) { astmm_log("Memory Allocation Failure - '%d' bytes in function %s " "at line %d of %s\n", (int) size, func, lineno, file); } ast_copy_string(reg->file, file, sizeof(reg->file)); ast_copy_string(reg->func, func, sizeof(reg->func)); reg->lineno = lineno; reg->len = size; reg->which = which; reg->cache = cache; ptr = reg->data; hash = HASH(ptr); reg->fence = FENCE_MAGIC; fence = (ptr + reg->len); put_unaligned_uint32(fence, FENCE_MAGIC); ast_mutex_lock(®lock); reg->next = regions[hash]; regions[hash] = reg; ast_mutex_unlock(®lock); return ptr; } static inline size_t __ast_sizeof_region(void *ptr) { int hash = HASH(ptr); struct ast_region *reg; size_t len = 0; ast_mutex_lock(®lock); for (reg = regions[hash]; reg; reg = reg->next) { if (reg->data == ptr) { len = reg->len; break; } } ast_mutex_unlock(®lock); return len; } static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func) { int hash = HASH(ptr); struct ast_region *reg, *prev = NULL; unsigned int *fence; ast_mutex_lock(®lock); for (reg = regions[hash]; reg; reg = reg->next) { if (reg->data == ptr) { if (prev) prev->next = reg->next; else regions[hash] = reg->next; break; } prev = reg; } ast_mutex_unlock(®lock); if (reg) { fence = (unsigned int *)(reg->data + reg->len); if (reg->fence != FENCE_MAGIC) { astmm_log("WARNING: Low fence violation at %p, in %s of %s, " "line %d\n", reg->data, reg->func, reg->file, reg->lineno); } if (get_unaligned_uint32(fence) != FENCE_MAGIC) { astmm_log("WARNING: High fence violation at %p, in %s of %s, " "line %d\n", reg->data, reg->func, reg->file, reg->lineno); } free(reg); } else { astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n", ptr, func, file, lineno); } } void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func) { void *ptr; if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0))) memset(ptr, 0, size * nmemb); return ptr; } void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func) { void *ptr; if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1))) memset(ptr, 0, size * nmemb); return ptr; } void *__ast_malloc(size_t size, const char *file, int lineno, const char *func) { return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0); } void __ast_free(void *ptr, const char *file, int lineno, const char *func) { __ast_free_region(ptr, file, lineno, func); } void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func) { void *tmp; size_t len = 0; if (ptr && !(len = __ast_sizeof_region(ptr))) { astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, " "line %d\n", ptr, func, file, lineno); return NULL; } if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0))) return NULL; if (len > size) len = size; if (ptr) { memcpy(tmp, ptr, len); __ast_free_region(ptr, file, lineno, func); } return tmp; } char *__ast_strdup(const char *s, const char *file, int lineno, const char *func) { size_t len; void *ptr; if (!s) return NULL; len = strlen(s) + 1; if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0))) strcpy(ptr, s); return ptr; } char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func) { size_t len; void *ptr; if (!s) return NULL; len = strlen(s) + 1; if (len > n) len = n; if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0))) strcpy(ptr, s); return ptr; } int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...) { int size; va_list ap, ap2; char s; *strp = NULL; va_start(ap, fmt); va_copy(ap2, ap); size = vsnprintf(&s, 1, fmt, ap2); va_end(ap2); if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) { va_end(ap); return -1; } vsnprintf(*strp, size + 1, fmt, ap); va_end(ap); return size; } int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func) { int size; va_list ap2; char s; *strp = NULL; va_copy(ap2, ap); size = vsnprintf(&s, 1, fmt, ap2); va_end(ap2); if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) { va_end(ap); return -1; } vsnprintf(*strp, size + 1, fmt, ap); return size; } static int handle_show_memory(int fd, int argc, char *argv[]) { char *fn = NULL; struct ast_region *reg; unsigned int x; unsigned int len = 0; unsigned int cache_len = 0; unsigned int count = 0; unsigned int *fence; if (argc > 3) fn = argv[3]; ast_mutex_lock(®lock); for (x = 0; x < SOME_PRIME; x++) { for (reg = regions[x]; reg; reg = reg->next) { if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) { fence = (unsigned int *)(reg->data + reg->len); if (reg->fence != FENCE_MAGIC) { astmm_log("WARNING: Low fence violation at %p, " "in %s of %s, line %d\n", reg->data, reg->func, reg->file, reg->lineno); } if (get_unaligned_uint32(fence) != FENCE_MAGIC) { astmm_log("WARNING: High fence violation at %p, in %s of %s, " "line %d\n", reg->data, reg->func, reg->file, reg->lineno); } } if (!fn || !strcasecmp(fn, reg->file)) { ast_cli(fd, "%10d bytes allocated%s in %20s at line %5d of %s\n", (int) reg->len, reg->cache ? " (cache)" : "", reg->func, reg->lineno, reg->file); len += reg->len; if (reg->cache) cache_len += reg->len; count++; } } } ast_mutex_unlock(®lock); if (cache_len) ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count); else ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count); return RESULT_SUCCESS; } static int handle_show_memory_summary(int fd, int argc, char *argv[]) { char *fn = NULL; int x; struct ast_region *reg; unsigned int len = 0; unsigned int cache_len = 0; int count = 0; struct file_summary { char fn[80]; int len; int cache_len; int count; struct file_summary *next; } *list = NULL, *cur; if (argc > 3) fn = argv[3]; ast_mutex_lock(®lock); for (x = 0; x < SOME_PRIME; x++) { for (reg = regions[x]; reg; reg = reg->next) { if (fn && strcasecmp(fn, reg->file)) continue; for (cur = list; cur; cur = cur->next) { if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func))) break; } if (!cur) { cur = alloca(sizeof(*cur)); memset(cur, 0, sizeof(*cur)); ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn)); cur->next = list; list = cur; } cur->len += reg->len; if (reg->cache) cur->cache_len += reg->len; cur->count++; } } ast_mutex_unlock(®lock); /* Dump the whole list */ for (cur = list; cur; cur = cur->next) { len += cur->len; cache_len += cur->cache_len; count += cur->count; if (cur->cache_len) { if (fn) { ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n", cur->len, cur->cache_len, cur->count, cur->fn, fn); } else { ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n", cur->len, cur->cache_len, cur->count, cur->fn); } } else { if (fn) { ast_cli(fd, "%10d bytes in %d allocations in function '%s' of '%s'\n", cur->len, cur->count, cur->fn, fn); } else { ast_cli(fd, "%10d bytes in %d allocations in file '%s'\n", cur->len, cur->count, cur->fn); } } } if (cache_len) ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count); else ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count); return RESULT_SUCCESS; } static char show_memory_help[] = "Usage: memory show allocations []\n" " Dumps a list of all segments of allocated memory, optionally\n" "limited to those from a specific file\n"; static char show_memory_summary_help[] = "Usage: memory show summary []\n" " Summarizes heap memory allocations by file, or optionally\n" "by function, if a file is specified\n"; static struct ast_cli_entry cli_show_memory_allocations_deprecated = { { "show", "memory", "allocations", NULL }, handle_show_memory, NULL, NULL }; static struct ast_cli_entry cli_show_memory_summary_deprecated = { { "show", "memory", "summary", NULL }, handle_show_memory_summary, NULL, NULL }; static struct ast_cli_entry cli_memory[] = { { { "memory", "show", "allocations", NULL }, handle_show_memory, "Display outstanding memory allocations", show_memory_help, NULL, &cli_show_memory_allocations_deprecated }, { { "memory", "show", "summary", NULL }, handle_show_memory_summary, "Summarize outstanding memory allocations", show_memory_summary_help, NULL, &cli_show_memory_summary_deprecated }, }; void __ast_mm_init(void) { char filename[PATH_MAX]; ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry)); snprintf(filename, sizeof(filename), "%s/mmlog", (char *)ast_config_AST_LOG_DIR); if (option_verbose) ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename); if ((mmlog = fopen(filename, "a+"))) { fprintf(mmlog, "%ld - New session\n", (long)time(NULL)); fflush(mmlog); } } #endif