Wed Nov 28 2012 04:18:03

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