Wed Aug 18 22:33:44 2010

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: 203233 $")
00031 
00032 #include "asterisk/paths.h"   /* use ast_config_AST_LOG_DIR */
00033 #include <stddef.h>
00034 #include <time.h>
00035 
00036 #include "asterisk/cli.h"
00037 #include "asterisk/lock.h"
00038 #include "asterisk/strings.h"
00039 #include "asterisk/unaligned.h"
00040 
00041 #define SOME_PRIME 563
00042 
00043 enum func_type {
00044    FUNC_CALLOC = 1,
00045    FUNC_MALLOC,
00046    FUNC_REALLOC,
00047    FUNC_STRDUP,
00048    FUNC_STRNDUP,
00049    FUNC_VASPRINTF,
00050    FUNC_ASPRINTF
00051 };
00052 
00053 /* Undefine all our macros */
00054 #undef malloc
00055 #undef calloc
00056 #undef realloc
00057 #undef strdup
00058 #undef strndup
00059 #undef free
00060 #undef vasprintf
00061 #undef asprintf
00062 
00063 #define FENCE_MAGIC 0xdeadbeef
00064 
00065 static FILE *mmlog;
00066 
00067 /* NOTE: Be EXTREMELY careful with modifying this structure; the total size of this structure
00068    must result in 'automatic' alignment so that the 'fence' field lands exactly at the end of
00069    the structure in memory (and thus immediately before the allocated region the fence is
00070    supposed to be used to monitor). In other words, we cannot allow the compiler to insert
00071    any padding between this structure and anything following it, so add up the sizes of all the
00072    fields and compare to sizeof(struct ast_region)... if they don't match, then the compiler
00073    is padding the structure and either the fields need to be rearranged to eliminate internal
00074    padding, or a dummy field will need to be inserted before the 'fence' field to push it to
00075    the end of the actual space it will consume. Note that this must be checked for both 32-bit
00076    and 64-bit platforms, as the sizes of pointers and 'size_t' differ on these platforms.
00077 */
00078 
00079 static struct ast_region {
00080    struct ast_region *next;
00081    size_t len;
00082    char file[64];
00083    char func[40];
00084    unsigned int lineno;
00085    enum func_type which;
00086    unsigned int cache;     /* region was allocated as part of a cache pool */
00087    unsigned int fence;
00088    unsigned char data[0];
00089 } *regions[SOME_PRIME];
00090 
00091 #define HASH(a) \
00092    (((unsigned long)(a)) % SOME_PRIME)
00093 
00094 /*! Tracking this mutex will cause infinite recursion, as the mutex tracking
00095  *  code allocates memory */
00096 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
00097 
00098 #define astmm_log(...)                               \
00099    do {                                         \
00100       fprintf(stderr, __VA_ARGS__);        \
00101       if (mmlog) {                         \
00102          fprintf(mmlog, __VA_ARGS__); \
00103          fflush(mmlog);               \
00104       }                                    \
00105    } while (0)
00106 
00107 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)
00108 {
00109    struct ast_region *reg;
00110    void *ptr = NULL;
00111    unsigned int *fence;
00112    int hash;
00113 
00114    if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
00115       astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
00116            "at line %d of %s\n", (int) size, func, lineno, file);
00117       return NULL;
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 char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
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    switch (cmd) {
00337    case CLI_INIT:
00338       e->command = "memory show allocations";
00339       e->usage =
00340          "Usage: memory show allocations [<file>]\n"
00341          "       Dumps a list of all segments of allocated memory, optionally\n"
00342          "       limited to those from a specific file\n";
00343       return NULL;
00344    case CLI_GENERATE:
00345       return NULL;
00346    }
00347 
00348 
00349    if (a->argc > 3)
00350       fn = a->argv[3];
00351 
00352    ast_mutex_lock(&reglock);
00353    for (x = 0; x < SOME_PRIME; x++) {
00354       for (reg = regions[x]; reg; reg = reg->next) {
00355          if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
00356             fence = (unsigned int *)(reg->data + reg->len);
00357             if (reg->fence != FENCE_MAGIC) {
00358                astmm_log("WARNING: Low fence violation at %p, "
00359                   "in %s of %s, line %d\n", reg->data, 
00360                   reg->func, reg->file, reg->lineno);
00361             }
00362             if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
00363                astmm_log("WARNING: High fence violation at %p, in %s of %s, "
00364                   "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00365             }
00366          }
00367          if (!fn || !strcasecmp(fn, reg->file)) {
00368             ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n", 
00369                (int) reg->len, reg->cache ? " (cache)" : "", 
00370                reg->func, reg->lineno, reg->file);
00371             len += reg->len;
00372             if (reg->cache)
00373                cache_len += reg->len;
00374             count++;
00375          }
00376       }
00377    }
00378    ast_mutex_unlock(&reglock);
00379    
00380    if (cache_len)
00381       ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
00382    else
00383       ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
00384    
00385    return CLI_SUCCESS;
00386 }
00387 
00388 static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00389 {
00390    char *fn = NULL;
00391    int x;
00392    struct ast_region *reg;
00393    unsigned int len = 0;
00394    unsigned int cache_len = 0;
00395    int count = 0;
00396    struct file_summary {
00397       char fn[80];
00398       int len;
00399       int cache_len;
00400       int count;
00401       struct file_summary *next;
00402    } *list = NULL, *cur;
00403    
00404    switch (cmd) {
00405    case CLI_INIT:
00406       e->command = "memory show summary";
00407       e->usage =
00408          "Usage: memory show summary [<file>]\n"
00409          "       Summarizes heap memory allocations by file, or optionally\n"
00410          "by function, if a file is specified\n";
00411       return NULL;
00412    case CLI_GENERATE:
00413       return NULL;
00414    }
00415 
00416    if (a->argc > 3) 
00417       fn = a->argv[3];
00418 
00419    ast_mutex_lock(&reglock);
00420    for (x = 0; x < SOME_PRIME; x++) {
00421       for (reg = regions[x]; reg; reg = reg->next) {
00422          if (fn && strcasecmp(fn, reg->file))
00423             continue;
00424 
00425          for (cur = list; cur; cur = cur->next) {
00426             if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
00427                break;
00428          }
00429          if (!cur) {
00430             cur = alloca(sizeof(*cur));
00431             memset(cur, 0, sizeof(*cur));
00432             ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
00433             cur->next = list;
00434             list = cur;
00435          }
00436 
00437          cur->len += reg->len;
00438          if (reg->cache)
00439             cur->cache_len += reg->len;
00440          cur->count++;
00441       }
00442    }
00443    ast_mutex_unlock(&reglock);
00444    
00445    /* Dump the whole list */
00446    for (cur = list; cur; cur = cur->next) {
00447       len += cur->len;
00448       cache_len += cur->cache_len;
00449       count += cur->count;
00450       if (cur->cache_len) {
00451          if (fn) {
00452             ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n", 
00453                cur->len, cur->cache_len, cur->count, cur->fn, fn);
00454          } else {
00455             ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n", 
00456                cur->len, cur->cache_len, cur->count, cur->fn);
00457          }
00458       } else {
00459          if (fn) {
00460             ast_cli(a->fd, "%10d bytes in %d allocations in function '%s' of '%s'\n", 
00461                cur->len, cur->count, cur->fn, fn);
00462          } else {
00463             ast_cli(a->fd, "%10d bytes in %d allocations in file '%s'\n", 
00464                cur->len, cur->count, cur->fn);
00465          }
00466       }
00467    }
00468 
00469    if (cache_len)
00470       ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
00471    else
00472       ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
00473 
00474    return CLI_SUCCESS;
00475 }
00476 
00477 static struct ast_cli_entry cli_memory[] = {
00478    AST_CLI_DEFINE(handle_memory_show, "Display outstanding memory allocations"),
00479    AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
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", ast_config_AST_LOG_DIR);
00494    
00495    ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
00496    
00497    if ((mmlog = fopen(filename, "a+"))) {
00498       fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
00499       fflush(mmlog);
00500    }
00501 }
00502 
00503 #endif

Generated on Wed Aug 18 22:33:44 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7