Sat Aug 6 00:39:22 2011

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 #include "asterisk.h"
00027 
00028 #ifdef __AST_DEBUG_MALLOC
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 203230 $")
00031 
00032 #include <stdio.h>
00033 #include <string.h>
00034 #include <stddef.h>
00035 #include <time.h>
00036 
00037 #include "asterisk/cli.h"
00038 #include "asterisk/logger.h"
00039 #include "asterisk/options.h"
00040 #include "asterisk/lock.h"
00041 #include "asterisk/strings.h"
00042 #include "asterisk/unaligned.h"
00043 
00044 #define SOME_PRIME 563
00045 
00046 enum func_type {
00047    FUNC_CALLOC = 1,
00048    FUNC_MALLOC,
00049    FUNC_REALLOC,
00050    FUNC_STRDUP,
00051    FUNC_STRNDUP,
00052    FUNC_VASPRINTF,
00053    FUNC_ASPRINTF
00054 };
00055 
00056 /* Undefine all our macros */
00057 #undef malloc
00058 #undef calloc
00059 #undef realloc
00060 #undef strdup
00061 #undef strndup
00062 #undef free
00063 #undef vasprintf
00064 #undef asprintf
00065 
00066 #define FENCE_MAGIC 0xdeadbeef
00067 
00068 static FILE *mmlog;
00069 
00070 /* NOTE: Be EXTREMELY careful with modifying this structure; the total size of this structure
00071    must result in 'automatic' alignment so that the 'fence' field lands exactly at the end of
00072    the structure in memory (and thus immediately before the allocated region the fence is
00073    supposed to be used to monitor). In other words, we cannot allow the compiler to insert
00074    any padding between this structure and anything following it, so add up the sizes of all the
00075    fields and compare to sizeof(struct ast_region)... if they don't match, then the compiler
00076    is padding the structure and either the fields need to be rearranged to eliminate internal
00077    padding, or a dummy field will need to be inserted before the 'fence' field to push it to
00078    the end of the actual space it will consume. Note that this must be checked for both 32-bit
00079    and 64-bit platforms, as the sizes of pointers and 'size_t' differ on these platforms.
00080 */
00081 
00082 static struct ast_region {
00083    struct ast_region *next;
00084    size_t len;
00085    char file[40];
00086    char func[40];
00087    unsigned int lineno;
00088    enum func_type which;
00089    unsigned int cache;     /* region was allocated as part of a cache pool */
00090    unsigned int fence;
00091    unsigned char data[0];
00092 } *regions[SOME_PRIME];
00093 
00094 #define HASH(a) \
00095    (((unsigned long)(a)) % SOME_PRIME)
00096    
00097 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
00098 
00099 #define astmm_log(...)                               \
00100    do {                                         \
00101       fprintf(stderr, __VA_ARGS__);        \
00102       if (mmlog) {                         \
00103          fprintf(mmlog, __VA_ARGS__); \
00104          fflush(mmlog);               \
00105       }                                    \
00106    } while (0)
00107 
00108 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)
00109 {
00110    struct ast_region *reg;
00111    void *ptr = NULL;
00112    unsigned int *fence;
00113    int hash;
00114 
00115    if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
00116       astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
00117            "at line %d of %s\n", (int) size, func, lineno, file);
00118    }
00119 
00120    ast_copy_string(reg->file, file, sizeof(reg->file));
00121    ast_copy_string(reg->func, func, sizeof(reg->func));
00122    reg->lineno = lineno;
00123    reg->len = size;
00124    reg->which = which;
00125    reg->cache = cache;
00126    ptr = reg->data;
00127    hash = HASH(ptr);
00128    reg->fence = FENCE_MAGIC;
00129    fence = (ptr + reg->len);
00130    put_unaligned_uint32(fence, FENCE_MAGIC);
00131 
00132    ast_mutex_lock(&reglock);
00133    reg->next = regions[hash];
00134    regions[hash] = reg;
00135    ast_mutex_unlock(&reglock);
00136 
00137    return ptr;
00138 }
00139 
00140 static inline size_t __ast_sizeof_region(void *ptr)
00141 {
00142    int hash = HASH(ptr);
00143    struct ast_region *reg;
00144    size_t len = 0;
00145    
00146    ast_mutex_lock(&reglock);
00147    for (reg = regions[hash]; reg; reg = reg->next) {
00148       if (reg->data == ptr) {
00149          len = reg->len;
00150          break;
00151       }
00152    }
00153    ast_mutex_unlock(&reglock);
00154 
00155    return len;
00156 }
00157 
00158 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
00159 {
00160    int hash;
00161    struct ast_region *reg, *prev = NULL;
00162    unsigned int *fence;
00163 
00164    if (!ptr) {
00165       return;
00166    }
00167 
00168    hash = HASH(ptr);
00169 
00170    ast_mutex_lock(&reglock);
00171    for (reg = regions[hash]; reg; reg = reg->next) {
00172       if (reg->data == ptr) {
00173          if (prev)
00174             prev->next = reg->next;
00175          else
00176             regions[hash] = reg->next;
00177          break;
00178       }
00179       prev = reg;
00180    }
00181    ast_mutex_unlock(&reglock);
00182 
00183    if (reg) {
00184       fence = (unsigned int *)(reg->data + reg->len);
00185       if (reg->fence != FENCE_MAGIC) {
00186          astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
00187             "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00188       }
00189       if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
00190          astmm_log("WARNING: High fence violation at %p, in %s of %s, "
00191             "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00192       }
00193       free(reg);
00194    } else {
00195       astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",  
00196          ptr, func, file, lineno);
00197    }
00198 }
00199 
00200 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
00201 {
00202    void *ptr;
00203 
00204    if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0))) 
00205       memset(ptr, 0, size * nmemb);
00206 
00207    return ptr;
00208 }
00209 
00210 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
00211 {
00212    void *ptr;
00213 
00214    if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1))) 
00215       memset(ptr, 0, size * nmemb);
00216 
00217    return ptr;
00218 }
00219 
00220 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func) 
00221 {
00222    return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
00223 }
00224 
00225 void __ast_free(void *ptr, const char *file, int lineno, const char *func) 
00226 {
00227    __ast_free_region(ptr, file, lineno, func);
00228 }
00229 
00230 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func) 
00231 {
00232    void *tmp;
00233    size_t len = 0;
00234 
00235    if (ptr && !(len = __ast_sizeof_region(ptr))) {
00236       astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
00237          "line %d\n", ptr, func, file, lineno);
00238       return NULL;
00239    }
00240 
00241    if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
00242       return NULL;
00243 
00244    if (len > size)
00245       len = size;
00246    if (ptr) {
00247       memcpy(tmp, ptr, len);
00248       __ast_free_region(ptr, file, lineno, func);
00249    }
00250    
00251    return tmp;
00252 }
00253 
00254 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func) 
00255 {
00256    size_t len;
00257    void *ptr;
00258 
00259    if (!s)
00260       return NULL;
00261 
00262    len = strlen(s) + 1;
00263    if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
00264       strcpy(ptr, s);
00265 
00266    return ptr;
00267 }
00268 
00269 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func) 
00270 {
00271    size_t len;
00272    void *ptr;
00273 
00274    if (!s)
00275       return NULL;
00276 
00277    len = strlen(s) + 1;
00278    if (len > n)
00279       len = n;
00280    if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
00281       strcpy(ptr, s);
00282 
00283    return ptr;
00284 }
00285 
00286 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
00287 {
00288    int size;
00289    va_list ap, ap2;
00290    char s;
00291 
00292    *strp = NULL;
00293    va_start(ap, fmt);
00294    va_copy(ap2, ap);
00295    size = vsnprintf(&s, 1, fmt, ap2);
00296    va_end(ap2);
00297    if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
00298       va_end(ap);
00299       return -1;
00300    }
00301    vsnprintf(*strp, size + 1, fmt, ap);
00302    va_end(ap);
00303 
00304    return size;
00305 }
00306 
00307 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func) 
00308 {
00309    int size;
00310    va_list ap2;
00311    char s;
00312 
00313    *strp = NULL;
00314    va_copy(ap2, ap);
00315    size = vsnprintf(&s, 1, fmt, ap2);
00316    va_end(ap2);
00317    if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
00318       va_end(ap);
00319       return -1;
00320    }
00321    vsnprintf(*strp, size + 1, fmt, ap);
00322 
00323    return size;
00324 }
00325 
00326 static int handle_show_memory(int fd, int argc, char *argv[])
00327 {
00328    char *fn = NULL;
00329    struct ast_region *reg;
00330    unsigned int x;
00331    unsigned int len = 0;
00332    unsigned int cache_len = 0;
00333    unsigned int count = 0;
00334    unsigned int *fence;
00335 
00336    if (argc > 3)
00337       fn = argv[3];
00338 
00339    ast_mutex_lock(&reglock);
00340    for (x = 0; x < SOME_PRIME; x++) {
00341       for (reg = regions[x]; reg; reg = reg->next) {
00342          if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
00343             fence = (unsigned int *)(reg->data + reg->len);
00344             if (reg->fence != FENCE_MAGIC) {
00345                astmm_log("WARNING: Low fence violation at %p, "
00346                   "in %s of %s, line %d\n", reg->data, 
00347                   reg->func, reg->file, reg->lineno);
00348             }
00349             if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
00350                astmm_log("WARNING: High fence violation at %p, in %s of %s, "
00351                   "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00352             }
00353          }
00354          if (!fn || !strcasecmp(fn, reg->file)) {
00355             ast_cli(fd, "%10d bytes allocated%s in %20s at line %5d of %s\n", 
00356                (int) reg->len, reg->cache ? " (cache)" : "", 
00357                reg->func, reg->lineno, reg->file);
00358             len += reg->len;
00359             if (reg->cache)
00360                cache_len += reg->len;
00361             count++;
00362          }
00363       }
00364    }
00365    ast_mutex_unlock(&reglock);
00366    
00367    if (cache_len)
00368       ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
00369    else
00370       ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
00371    
00372    return RESULT_SUCCESS;
00373 }
00374 
00375 static int handle_show_memory_summary(int fd, int argc, char *argv[])
00376 {
00377    char *fn = NULL;
00378    int x;
00379    struct ast_region *reg;
00380    unsigned int len = 0;
00381    unsigned int cache_len = 0;
00382    int count = 0;
00383    struct file_summary {
00384       char fn[80];
00385       int len;
00386       int cache_len;
00387       int count;
00388       struct file_summary *next;
00389    } *list = NULL, *cur;
00390    
00391    if (argc > 3) 
00392       fn = argv[3];
00393 
00394    ast_mutex_lock(&reglock);
00395    for (x = 0; x < SOME_PRIME; x++) {
00396       for (reg = regions[x]; reg; reg = reg->next) {
00397          if (fn && strcasecmp(fn, reg->file))
00398             continue;
00399 
00400          for (cur = list; cur; cur = cur->next) {
00401             if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
00402                break;
00403          }
00404          if (!cur) {
00405             cur = alloca(sizeof(*cur));
00406             memset(cur, 0, sizeof(*cur));
00407             ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
00408             cur->next = list;
00409             list = cur;
00410          }
00411 
00412          cur->len += reg->len;
00413          if (reg->cache)
00414             cur->cache_len += reg->len;
00415          cur->count++;
00416       }
00417    }
00418    ast_mutex_unlock(&reglock);
00419    
00420    /* Dump the whole list */
00421    for (cur = list; cur; cur = cur->next) {
00422       len += cur->len;
00423       cache_len += cur->cache_len;
00424       count += cur->count;
00425       if (cur->cache_len) {
00426          if (fn) {
00427             ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n", 
00428                cur->len, cur->cache_len, cur->count, cur->fn, fn);
00429          } else {
00430             ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n", 
00431                cur->len, cur->cache_len, cur->count, cur->fn);
00432          }
00433       } else {
00434          if (fn) {
00435             ast_cli(fd, "%10d bytes in %d allocations in function '%s' of '%s'\n", 
00436                cur->len, cur->count, cur->fn, fn);
00437          } else {
00438             ast_cli(fd, "%10d bytes in %d allocations in file '%s'\n", 
00439                cur->len, cur->count, cur->fn);
00440          }
00441       }
00442    }
00443 
00444    if (cache_len)
00445       ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
00446    else
00447       ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
00448 
00449    return RESULT_SUCCESS;
00450 }
00451 
00452 static char show_memory_help[] = 
00453 "Usage: memory show allocations [<file>]\n"
00454 "       Dumps a list of all segments of allocated memory, optionally\n"
00455 "limited to those from a specific file\n";
00456 
00457 static char show_memory_summary_help[] = 
00458 "Usage: memory show summary [<file>]\n"
00459 "       Summarizes heap memory allocations by file, or optionally\n"
00460 "by function, if a file is specified\n";
00461 
00462 static struct ast_cli_entry cli_show_memory_allocations_deprecated = {
00463    { "show", "memory", "allocations", NULL },
00464    handle_show_memory, NULL,
00465    NULL };
00466 
00467 static struct ast_cli_entry cli_show_memory_summary_deprecated = {
00468    { "show", "memory", "summary", NULL },
00469    handle_show_memory_summary, NULL,
00470    NULL };
00471 
00472 static struct ast_cli_entry cli_memory[] = {
00473    { { "memory", "show", "allocations", NULL },
00474    handle_show_memory, "Display outstanding memory allocations",
00475    show_memory_help, NULL, &cli_show_memory_allocations_deprecated },
00476 
00477    { { "memory", "show", "summary", NULL },
00478    handle_show_memory_summary, "Summarize outstanding memory allocations",
00479    show_memory_summary_help, NULL, &cli_show_memory_summary_deprecated },
00480 };
00481 
00482 void __ast_mm_init(void)
00483 {
00484    char filename[PATH_MAX];
00485    size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
00486 
00487    if (pad) {
00488       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);
00489    }
00490 
00491    ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
00492    
00493    snprintf(filename, sizeof(filename), "%s/mmlog", (char *)ast_config_AST_LOG_DIR);
00494    
00495    if (option_verbose)
00496       ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename);
00497    
00498    if ((mmlog = fopen(filename, "a+"))) {
00499       fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
00500       fflush(mmlog);
00501    }
00502 }
00503 
00504 #endif

Generated on Sat Aug 6 00:39:22 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7