Mon Oct 8 12:38:58 2012

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: 369001 $")
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    void *ptr;
00276 
00277    if (!s)
00278       return NULL;
00279 
00280    len = strlen(s) + 1;
00281    if (len > n)
00282       len = n;
00283    if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
00284       strcpy(ptr, s);
00285 
00286    return ptr;
00287 }
00288 
00289 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
00290 {
00291    int size;
00292    va_list ap, ap2;
00293    char s;
00294 
00295    *strp = NULL;
00296    va_start(ap, fmt);
00297    va_copy(ap2, ap);
00298    size = vsnprintf(&s, 1, fmt, ap2);
00299    va_end(ap2);
00300    if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
00301       va_end(ap);
00302       return -1;
00303    }
00304    vsnprintf(*strp, size + 1, fmt, ap);
00305    va_end(ap);
00306 
00307    return size;
00308 }
00309 
00310 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func) 
00311 {
00312    int size;
00313    va_list ap2;
00314    char s;
00315 
00316    *strp = NULL;
00317    va_copy(ap2, ap);
00318    size = vsnprintf(&s, 1, fmt, ap2);
00319    va_end(ap2);
00320    if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
00321       va_end(ap);
00322       return -1;
00323    }
00324    vsnprintf(*strp, size + 1, fmt, ap);
00325 
00326    return size;
00327 }
00328 
00329 static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00330 {
00331    const char *fn = NULL;
00332    struct ast_region *reg;
00333    unsigned int x;
00334    unsigned int len = 0;
00335    unsigned int cache_len = 0;
00336    unsigned int count = 0;
00337    unsigned int *fence;
00338 
00339    switch (cmd) {
00340    case CLI_INIT:
00341       e->command = "memory show allocations";
00342       e->usage =
00343          "Usage: memory show allocations [<file>]\n"
00344          "       Dumps a list of all segments of allocated memory, optionally\n"
00345          "       limited to those from a specific file\n";
00346       return NULL;
00347    case CLI_GENERATE:
00348       return NULL;
00349    }
00350 
00351 
00352    if (a->argc > 3)
00353       fn = a->argv[3];
00354 
00355    ast_mutex_lock(&reglock);
00356    for (x = 0; x < SOME_PRIME; x++) {
00357       for (reg = regions[x]; reg; reg = reg->next) {
00358          if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
00359             fence = (unsigned int *)(reg->data + reg->len);
00360             if (reg->fence != FENCE_MAGIC) {
00361                astmm_log("WARNING: Low fence violation at %p, "
00362                   "in %s of %s, line %d\n", reg->data, 
00363                   reg->func, reg->file, reg->lineno);
00364             }
00365             if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
00366                astmm_log("WARNING: High fence violation at %p, in %s of %s, "
00367                   "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00368             }
00369          }
00370          if (!fn || !strcasecmp(fn, reg->file)) {
00371             ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n", 
00372                (int) reg->len, reg->cache ? " (cache)" : "", 
00373                reg->func, reg->lineno, reg->file);
00374             len += reg->len;
00375             if (reg->cache)
00376                cache_len += reg->len;
00377             count++;
00378          }
00379       }
00380    }
00381    ast_mutex_unlock(&reglock);
00382    
00383    if (cache_len)
00384       ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
00385    else
00386       ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
00387    
00388    return CLI_SUCCESS;
00389 }
00390 
00391 static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00392 {
00393    const char *fn = NULL;
00394    int x;
00395    struct ast_region *reg;
00396    unsigned int len = 0;
00397    unsigned int cache_len = 0;
00398    int count = 0;
00399    struct file_summary {
00400       char fn[80];
00401       int len;
00402       int cache_len;
00403       int count;
00404       struct file_summary *next;
00405    } *list = NULL, *cur;
00406    
00407    switch (cmd) {
00408    case CLI_INIT:
00409       e->command = "memory show summary";
00410       e->usage =
00411          "Usage: memory show summary [<file>]\n"
00412          "       Summarizes heap memory allocations by file, or optionally\n"
00413          "by function, if a file is specified\n";
00414       return NULL;
00415    case CLI_GENERATE:
00416       return NULL;
00417    }
00418 
00419    if (a->argc > 3) 
00420       fn = a->argv[3];
00421 
00422    ast_mutex_lock(&reglock);
00423    for (x = 0; x < SOME_PRIME; x++) {
00424       for (reg = regions[x]; reg; reg = reg->next) {
00425          if (fn && strcasecmp(fn, reg->file))
00426             continue;
00427 
00428          for (cur = list; cur; cur = cur->next) {
00429             if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
00430                break;
00431          }
00432          if (!cur) {
00433             cur = alloca(sizeof(*cur));
00434             memset(cur, 0, sizeof(*cur));
00435             ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
00436             cur->next = list;
00437             list = cur;
00438          }
00439 
00440          cur->len += reg->len;
00441          if (reg->cache)
00442             cur->cache_len += reg->len;
00443          cur->count++;
00444       }
00445    }
00446    ast_mutex_unlock(&reglock);
00447    
00448    /* Dump the whole list */
00449    for (cur = list; cur; cur = cur->next) {
00450       len += cur->len;
00451       cache_len += cur->cache_len;
00452       count += cur->count;
00453       if (cur->cache_len) {
00454          if (fn) {
00455             ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n", 
00456                cur->len, cur->cache_len, cur->count, cur->fn, fn);
00457          } else {
00458             ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n", 
00459                cur->len, cur->cache_len, cur->count, cur->fn);
00460          }
00461       } else {
00462          if (fn) {
00463             ast_cli(a->fd, "%10d bytes in %d allocations in function '%s' of '%s'\n", 
00464                cur->len, cur->count, cur->fn, fn);
00465          } else {
00466             ast_cli(a->fd, "%10d bytes in %d allocations in file '%s'\n", 
00467                cur->len, cur->count, cur->fn);
00468          }
00469       }
00470    }
00471 
00472    if (cache_len)
00473       ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
00474    else
00475       ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
00476 
00477    return CLI_SUCCESS;
00478 }
00479 
00480 static struct ast_cli_entry cli_memory[] = {
00481    AST_CLI_DEFINE(handle_memory_show, "Display outstanding memory allocations"),
00482    AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
00483 };
00484 
00485 void __ast_mm_init(void)
00486 {
00487    char filename[PATH_MAX];
00488    size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
00489 
00490    if (pad) {
00491       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);
00492    }
00493 
00494    ast_cli_register_multiple(cli_memory, ARRAY_LEN(cli_memory));
00495    
00496    snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
00497    
00498    ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
00499    
00500    if ((mmlog = fopen(filename, "a+"))) {
00501       fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
00502       fflush(mmlog);
00503    }
00504 }
00505 
00506 #endif

Generated on Mon Oct 8 12:38:58 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7