Sat Apr 12 07:12:18 2008

Asterisk developer's documentation


astmm.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Memory Management
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  */
00025 
00026 #ifdef __AST_DEBUG_MALLOC
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 87373 $")
00031 
00032 #include <stdio.h>
00033 #include <string.h>
00034 #include <time.h>
00035 
00036 #include "asterisk/cli.h"
00037 #include "asterisk/logger.h"
00038 #include "asterisk/options.h"
00039 #include "asterisk/lock.h"
00040 #include "asterisk/strings.h"
00041 #include "asterisk/unaligned.h"
00042 
00043 #define SOME_PRIME 563
00044 
00045 enum func_type {
00046    FUNC_CALLOC = 1,
00047    FUNC_MALLOC,
00048    FUNC_REALLOC,
00049    FUNC_STRDUP,
00050    FUNC_STRNDUP,
00051    FUNC_VASPRINTF,
00052    FUNC_ASPRINTF
00053 };
00054 
00055 /* Undefine all our macros */
00056 #undef malloc
00057 #undef calloc
00058 #undef realloc
00059 #undef strdup
00060 #undef strndup
00061 #undef free
00062 #undef vasprintf
00063 #undef asprintf
00064 
00065 #define FENCE_MAGIC 0xdeadbeef
00066 
00067 static FILE *mmlog;
00068 
00069 static struct ast_region {
00070    struct ast_region *next;
00071    char file[40];
00072    char func[40];
00073    unsigned int lineno;
00074    enum func_type which;
00075    unsigned int cache;     /* region was allocated as part of a cache pool */
00076    size_t len;
00077    unsigned int fence;
00078    unsigned char data[0];
00079 } *regions[SOME_PRIME];
00080 
00081 #define HASH(a) \
00082    (((unsigned long)(a)) % SOME_PRIME)
00083    
00084 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
00085 
00086 #define astmm_log(...)                               \
00087    do {                                         \
00088       fprintf(stderr, __VA_ARGS__);        \
00089       if (mmlog) {                         \
00090          fprintf(mmlog, __VA_ARGS__); \
00091          fflush(mmlog);               \
00092       }                                    \
00093    } while (0)
00094 
00095 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)
00096 {
00097    struct ast_region *reg;
00098    void *ptr = NULL;
00099    unsigned int *fence;
00100    int hash;
00101 
00102    if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
00103       astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
00104            "at line %d of %s\n", (int) size, func, lineno, file);
00105    }
00106 
00107    ast_copy_string(reg->file, file, sizeof(reg->file));
00108    ast_copy_string(reg->func, func, sizeof(reg->func));
00109    reg->lineno = lineno;
00110    reg->len = size;
00111    reg->which = which;
00112    reg->cache = cache;
00113    ptr = reg->data;
00114    hash = HASH(ptr);
00115    reg->fence = FENCE_MAGIC;
00116    fence = (ptr + reg->len);
00117    put_unaligned_uint32(fence, FENCE_MAGIC);
00118 
00119    ast_mutex_lock(&reglock);
00120    reg->next = regions[hash];
00121    regions[hash] = reg;
00122    ast_mutex_unlock(&reglock);
00123 
00124    return ptr;
00125 }
00126 
00127 static inline size_t __ast_sizeof_region(void *ptr)
00128 {
00129    int hash = HASH(ptr);
00130    struct ast_region *reg;
00131    size_t len = 0;
00132    
00133    ast_mutex_lock(&reglock);
00134    for (reg = regions[hash]; reg; reg = reg->next) {
00135       if (reg->data == ptr) {
00136          len = reg->len;
00137          break;
00138       }
00139    }
00140    ast_mutex_unlock(&reglock);
00141 
00142    return len;
00143 }
00144 
00145 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
00146 {
00147    int hash = HASH(ptr);
00148    struct ast_region *reg, *prev = NULL;
00149    unsigned int *fence;
00150 
00151    ast_mutex_lock(&reglock);
00152    for (reg = regions[hash]; reg; reg = reg->next) {
00153       if (reg->data == ptr) {
00154          if (prev)
00155             prev->next = reg->next;
00156          else
00157             regions[hash] = reg->next;
00158          break;
00159       }
00160       prev = reg;
00161    }
00162    ast_mutex_unlock(&reglock);
00163 
00164    if (reg) {
00165       fence = (unsigned int *)(reg->data + reg->len);
00166       if (reg->fence != FENCE_MAGIC) {
00167          astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
00168             "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00169       }
00170       if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
00171          astmm_log("WARNING: High fence violation at %p, in %s of %s, "
00172             "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00173       }
00174       free(reg);
00175    } else {
00176       astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",  
00177          ptr, func, file, lineno);
00178    }
00179 }
00180 
00181 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
00182 {
00183    void *ptr;
00184 
00185    if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0))) 
00186       memset(ptr, 0, size * nmemb);
00187 
00188    return ptr;
00189 }
00190 
00191 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
00192 {
00193    void *ptr;
00194 
00195    if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1))) 
00196       memset(ptr, 0, size * nmemb);
00197 
00198    return ptr;
00199 }
00200 
00201 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func) 
00202 {
00203    return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
00204 }
00205 
00206 void __ast_free(void *ptr, const char *file, int lineno, const char *func) 
00207 {
00208    __ast_free_region(ptr, file, lineno, func);
00209 }
00210 
00211 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func) 
00212 {
00213    void *tmp;
00214    size_t len = 0;
00215 
00216    if (ptr && !(len = __ast_sizeof_region(ptr))) {
00217       astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
00218          "line %d\n", ptr, func, file, lineno);
00219       return NULL;
00220    }
00221 
00222    if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
00223       return NULL;
00224 
00225    if (len > size)
00226       len = size;
00227    if (ptr) {
00228       memcpy(tmp, ptr, len);
00229       __ast_free_region(ptr, file, lineno, func);
00230    }
00231    
00232    return tmp;
00233 }
00234 
00235 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func) 
00236 {
00237    size_t len;
00238    void *ptr;
00239 
00240    if (!s)
00241       return NULL;
00242 
00243    len = strlen(s) + 1;
00244    if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
00245       strcpy(ptr, s);
00246 
00247    return ptr;
00248 }
00249 
00250 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func) 
00251 {
00252    size_t len;
00253    void *ptr;
00254 
00255    if (!s)
00256       return NULL;
00257 
00258    len = strlen(s) + 1;
00259    if (len > n)
00260       len = n;
00261    if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
00262       strcpy(ptr, s);
00263 
00264    return ptr;
00265 }
00266 
00267 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
00268 {
00269    int size;
00270    va_list ap, ap2;
00271    char s;
00272 
00273    *strp = NULL;
00274    va_start(ap, fmt);
00275    va_copy(ap2, ap);
00276    size = vsnprintf(&s, 1, fmt, ap2);
00277    va_end(ap2);
00278    if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
00279       va_end(ap);
00280       return -1;
00281    }
00282    vsnprintf(*strp, size + 1, fmt, ap);
00283    va_end(ap);
00284 
00285    return size;
00286 }
00287 
00288 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func) 
00289 {
00290    int size;
00291    va_list ap2;
00292    char s;
00293 
00294    *strp = NULL;
00295    va_copy(ap2, ap);
00296    size = vsnprintf(&s, 1, fmt, ap2);
00297    va_end(ap2);
00298    if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
00299       va_end(ap);
00300       return -1;
00301    }
00302    vsnprintf(*strp, size + 1, fmt, ap);
00303 
00304    return size;
00305 }
00306 
00307 static int handle_show_memory(int fd, int argc, char *argv[])
00308 {
00309    char *fn = NULL;
00310    struct ast_region *reg;
00311    unsigned int x;
00312    unsigned int len = 0;
00313    unsigned int cache_len = 0;
00314    unsigned int count = 0;
00315    unsigned int *fence;
00316 
00317    if (argc > 3)
00318       fn = argv[3];
00319 
00320    ast_mutex_lock(&reglock);
00321    for (x = 0; x < SOME_PRIME; x++) {
00322       for (reg = regions[x]; reg; reg = reg->next) {
00323          if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
00324             fence = (unsigned int *)(reg->data + reg->len);
00325             if (reg->fence != FENCE_MAGIC) {
00326                astmm_log("WARNING: Low fence violation at %p, "
00327                   "in %s of %s, line %d\n", reg->data, 
00328                   reg->func, reg->file, reg->lineno);
00329             }
00330             if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
00331                astmm_log("WARNING: High fence violation at %p, in %s of %s, "
00332                   "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00333             }
00334          }
00335          if (!fn || !strcasecmp(fn, reg->file)) {
00336             ast_cli(fd, "%10d bytes allocated%s in %20s at line %5d of %s\n", 
00337                (int) reg->len, reg->cache ? " (cache)" : "", 
00338                reg->func, reg->lineno, reg->file);
00339             len += reg->len;
00340             if (reg->cache)
00341                cache_len += reg->len;
00342             count++;
00343          }
00344       }
00345    }
00346    ast_mutex_unlock(&reglock);
00347    
00348    if (cache_len)
00349       ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
00350    else
00351       ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
00352    
00353    return RESULT_SUCCESS;
00354 }
00355 
00356 static int handle_show_memory_summary(int fd, int argc, char *argv[])
00357 {
00358    char *fn = NULL;
00359    int x;
00360    struct ast_region *reg;
00361    unsigned int len = 0;
00362    unsigned int cache_len = 0;
00363    int count = 0;
00364    struct file_summary {
00365       char fn[80];
00366       int len;
00367       int cache_len;
00368       int count;
00369       struct file_summary *next;
00370    } *list = NULL, *cur;
00371    
00372    if (argc > 3) 
00373       fn = argv[3];
00374 
00375    ast_mutex_lock(&reglock);
00376    for (x = 0; x < SOME_PRIME; x++) {
00377       for (reg = regions[x]; reg; reg = reg->next) {
00378          if (fn && strcasecmp(fn, reg->file))
00379             continue;
00380 
00381          for (cur = list; cur; cur = cur->next) {
00382             if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
00383                break;
00384          }
00385          if (!cur) {
00386             cur = alloca(sizeof(*cur));
00387             memset(cur, 0, sizeof(*cur));
00388             ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
00389             cur->next = list;
00390             list = cur;
00391          }
00392 
00393          cur->len += reg->len;
00394          if (reg->cache)
00395             cur->cache_len += reg->len;
00396          cur->count++;
00397       }
00398    }
00399    ast_mutex_unlock(&reglock);
00400    
00401    /* Dump the whole list */
00402    for (cur = list; cur; cur = cur->next) {
00403       len += cur->len;
00404       cache_len += cur->cache_len;
00405       count += cur->count;
00406       if (cur->cache_len) {
00407          if (fn) {
00408             ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n", 
00409                cur->len, cur->cache_len, cur->count, cur->fn, fn);
00410          } else {
00411             ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n", 
00412                cur->len, cur->cache_len, cur->count, cur->fn);
00413          }
00414       } else {
00415          if (fn) {
00416             ast_cli(fd, "%10d bytes in %d allocations in function '%s' of '%s'\n", 
00417                cur->len, cur->count, cur->fn, fn);
00418          } else {
00419             ast_cli(fd, "%10d bytes in %d allocations in file '%s'\n", 
00420                cur->len, cur->count, cur->fn);
00421          }
00422       }
00423    }
00424 
00425    if (cache_len)
00426       ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
00427    else
00428       ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
00429 
00430    return RESULT_SUCCESS;
00431 }
00432 
00433 static char show_memory_help[] = 
00434 "Usage: memory show allocations [<file>]\n"
00435 "       Dumps a list of all segments of allocated memory, optionally\n"
00436 "limited to those from a specific file\n";
00437 
00438 static char show_memory_summary_help[] = 
00439 "Usage: memory show summary [<file>]\n"
00440 "       Summarizes heap memory allocations by file, or optionally\n"
00441 "by function, if a file is specified\n";
00442 
00443 static struct ast_cli_entry cli_show_memory_allocations_deprecated = {
00444    { "show", "memory", "allocations", NULL },
00445    handle_show_memory, NULL,
00446    NULL };
00447 
00448 static struct ast_cli_entry cli_show_memory_summary_deprecated = {
00449    { "show", "memory", "summary", NULL },
00450    handle_show_memory_summary, NULL,
00451    NULL };
00452 
00453 static struct ast_cli_entry cli_memory[] = {
00454    { { "memory", "show", "allocations", NULL },
00455    handle_show_memory, "Display outstanding memory allocations",
00456    show_memory_help, NULL, &cli_show_memory_allocations_deprecated },
00457 
00458    { { "memory", "show", "summary", NULL },
00459    handle_show_memory_summary, "Summarize outstanding memory allocations",
00460    show_memory_summary_help, NULL, &cli_show_memory_summary_deprecated },
00461 };
00462 
00463 void __ast_mm_init(void)
00464 {
00465    char filename[PATH_MAX];
00466 
00467    ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
00468    
00469    snprintf(filename, sizeof(filename), "%s/mmlog", (char *)ast_config_AST_LOG_DIR);
00470    
00471    if (option_verbose)
00472       ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename);
00473    
00474    if ((mmlog = fopen(filename, "a+"))) {
00475       fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
00476       fflush(mmlog);
00477    }
00478 }
00479 
00480 #endif

Generated on Sat Apr 12 07:12:18 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.5