Thu May 14 15:12:47 2009

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: 144925 $")
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 = HASH(ptr);
00161    struct ast_region *reg, *prev = NULL;
00162    unsigned int *fence;
00163 
00164    ast_mutex_lock(&reglock);
00165    for (reg = regions[hash]; reg; reg = reg->next) {
00166       if (reg->data == ptr) {
00167          if (prev)
00168             prev->next = reg->next;
00169          else
00170             regions[hash] = reg->next;
00171          break;
00172       }
00173       prev = reg;
00174    }
00175    ast_mutex_unlock(&reglock);
00176 
00177    if (reg) {
00178       fence = (unsigned int *)(reg->data + reg->len);
00179       if (reg->fence != FENCE_MAGIC) {
00180          astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
00181             "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00182       }
00183       if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
00184          astmm_log("WARNING: High fence violation at %p, in %s of %s, "
00185             "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00186       }
00187       free(reg);
00188    } else {
00189       astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",  
00190          ptr, func, file, lineno);
00191    }
00192 }
00193 
00194 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
00195 {
00196    void *ptr;
00197 
00198    if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0))) 
00199       memset(ptr, 0, size * nmemb);
00200 
00201    return ptr;
00202 }
00203 
00204 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func) 
00205 {
00206    void *ptr;
00207 
00208    if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1))) 
00209       memset(ptr, 0, size * nmemb);
00210 
00211    return ptr;
00212 }
00213 
00214 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func) 
00215 {
00216    return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
00217 }
00218 
00219 void __ast_free(void *ptr, const char *file, int lineno, const char *func) 
00220 {
00221    __ast_free_region(ptr, file, lineno, func);
00222 }
00223 
00224 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func) 
00225 {
00226    void *tmp;
00227    size_t len = 0;
00228 
00229    if (ptr && !(len = __ast_sizeof_region(ptr))) {
00230       astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
00231          "line %d\n", ptr, func, file, lineno);
00232       return NULL;
00233    }
00234 
00235    if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
00236       return NULL;
00237 
00238    if (len > size)
00239       len = size;
00240    if (ptr) {
00241       memcpy(tmp, ptr, len);
00242       __ast_free_region(ptr, file, lineno, func);
00243    }
00244    
00245    return tmp;
00246 }
00247 
00248 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func) 
00249 {
00250    size_t len;
00251    void *ptr;
00252 
00253    if (!s)
00254       return NULL;
00255 
00256    len = strlen(s) + 1;
00257    if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
00258       strcpy(ptr, s);
00259 
00260    return ptr;
00261 }
00262 
00263 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func) 
00264 {
00265    size_t len;
00266    void *ptr;
00267 
00268    if (!s)
00269       return NULL;
00270 
00271    len = strlen(s) + 1;
00272    if (len > n)
00273       len = n;
00274    if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
00275       strcpy(ptr, s);
00276 
00277    return ptr;
00278 }
00279 
00280 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
00281 {
00282    int size;
00283    va_list ap, ap2;
00284    char s;
00285 
00286    *strp = NULL;
00287    va_start(ap, fmt);
00288    va_copy(ap2, ap);
00289    size = vsnprintf(&s, 1, fmt, ap2);
00290    va_end(ap2);
00291    if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
00292       va_end(ap);
00293       return -1;
00294    }
00295    vsnprintf(*strp, size + 1, fmt, ap);
00296    va_end(ap);
00297 
00298    return size;
00299 }
00300 
00301 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func) 
00302 {
00303    int size;
00304    va_list ap2;
00305    char s;
00306 
00307    *strp = NULL;
00308    va_copy(ap2, ap);
00309    size = vsnprintf(&s, 1, fmt, ap2);
00310    va_end(ap2);
00311    if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
00312       va_end(ap);
00313       return -1;
00314    }
00315    vsnprintf(*strp, size + 1, fmt, ap);
00316 
00317    return size;
00318 }
00319 
00320 static int handle_show_memory(int fd, int argc, char *argv[])
00321 {
00322    char *fn = NULL;
00323    struct ast_region *reg;
00324    unsigned int x;
00325    unsigned int len = 0;
00326    unsigned int cache_len = 0;
00327    unsigned int count = 0;
00328    unsigned int *fence;
00329 
00330    if (argc > 3)
00331       fn = argv[3];
00332 
00333    ast_mutex_lock(&reglock);
00334    for (x = 0; x < SOME_PRIME; x++) {
00335       for (reg = regions[x]; reg; reg = reg->next) {
00336          if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
00337             fence = (unsigned int *)(reg->data + reg->len);
00338             if (reg->fence != FENCE_MAGIC) {
00339                astmm_log("WARNING: Low fence violation at %p, "
00340                   "in %s of %s, line %d\n", reg->data, 
00341                   reg->func, reg->file, reg->lineno);
00342             }
00343             if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
00344                astmm_log("WARNING: High fence violation at %p, in %s of %s, "
00345                   "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
00346             }
00347          }
00348          if (!fn || !strcasecmp(fn, reg->file)) {
00349             ast_cli(fd, "%10d bytes allocated%s in %20s at line %5d of %s\n", 
00350                (int) reg->len, reg->cache ? " (cache)" : "", 
00351                reg->func, reg->lineno, reg->file);
00352             len += reg->len;
00353             if (reg->cache)
00354                cache_len += reg->len;
00355             count++;
00356          }
00357       }
00358    }
00359    ast_mutex_unlock(&reglock);
00360    
00361    if (cache_len)
00362       ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
00363    else
00364       ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
00365    
00366    return RESULT_SUCCESS;
00367 }
00368 
00369 static int handle_show_memory_summary(int fd, int argc, char *argv[])
00370 {
00371    char *fn = NULL;
00372    int x;
00373    struct ast_region *reg;
00374    unsigned int len = 0;
00375    unsigned int cache_len = 0;
00376    int count = 0;
00377    struct file_summary {
00378       char fn[80];
00379       int len;
00380       int cache_len;
00381       int count;
00382       struct file_summary *next;
00383    } *list = NULL, *cur;
00384    
00385    if (argc > 3) 
00386       fn = argv[3];
00387 
00388    ast_mutex_lock(&reglock);
00389    for (x = 0; x < SOME_PRIME; x++) {
00390       for (reg = regions[x]; reg; reg = reg->next) {
00391          if (fn && strcasecmp(fn, reg->file))
00392             continue;
00393 
00394          for (cur = list; cur; cur = cur->next) {
00395             if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
00396                break;
00397          }
00398          if (!cur) {
00399             cur = alloca(sizeof(*cur));
00400             memset(cur, 0, sizeof(*cur));
00401             ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
00402             cur->next = list;
00403             list = cur;
00404          }
00405 
00406          cur->len += reg->len;
00407          if (reg->cache)
00408             cur->cache_len += reg->len;
00409          cur->count++;
00410       }
00411    }
00412    ast_mutex_unlock(&reglock);
00413    
00414    /* Dump the whole list */
00415    for (cur = list; cur; cur = cur->next) {
00416       len += cur->len;
00417       cache_len += cur->cache_len;
00418       count += cur->count;
00419       if (cur->cache_len) {
00420          if (fn) {
00421             ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n", 
00422                cur->len, cur->cache_len, cur->count, cur->fn, fn);
00423          } else {
00424             ast_cli(fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n", 
00425                cur->len, cur->cache_len, cur->count, cur->fn);
00426          }
00427       } else {
00428          if (fn) {
00429             ast_cli(fd, "%10d bytes in %d allocations in function '%s' of '%s'\n", 
00430                cur->len, cur->count, cur->fn, fn);
00431          } else {
00432             ast_cli(fd, "%10d bytes in %d allocations in file '%s'\n", 
00433                cur->len, cur->count, cur->fn);
00434          }
00435       }
00436    }
00437 
00438    if (cache_len)
00439       ast_cli(fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
00440    else
00441       ast_cli(fd, "%d bytes allocated in %d allocations\n", len, count);
00442 
00443    return RESULT_SUCCESS;
00444 }
00445 
00446 static char show_memory_help[] = 
00447 "Usage: memory show allocations [<file>]\n"
00448 "       Dumps a list of all segments of allocated memory, optionally\n"
00449 "limited to those from a specific file\n";
00450 
00451 static char show_memory_summary_help[] = 
00452 "Usage: memory show summary [<file>]\n"
00453 "       Summarizes heap memory allocations by file, or optionally\n"
00454 "by function, if a file is specified\n";
00455 
00456 static struct ast_cli_entry cli_show_memory_allocations_deprecated = {
00457    { "show", "memory", "allocations", NULL },
00458    handle_show_memory, NULL,
00459    NULL };
00460 
00461 static struct ast_cli_entry cli_show_memory_summary_deprecated = {
00462    { "show", "memory", "summary", NULL },
00463    handle_show_memory_summary, NULL,
00464    NULL };
00465 
00466 static struct ast_cli_entry cli_memory[] = {
00467    { { "memory", "show", "allocations", NULL },
00468    handle_show_memory, "Display outstanding memory allocations",
00469    show_memory_help, NULL, &cli_show_memory_allocations_deprecated },
00470 
00471    { { "memory", "show", "summary", NULL },
00472    handle_show_memory_summary, "Summarize outstanding memory allocations",
00473    show_memory_summary_help, NULL, &cli_show_memory_summary_deprecated },
00474 };
00475 
00476 void __ast_mm_init(void)
00477 {
00478    char filename[PATH_MAX];
00479    size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
00480 
00481    if (pad) {
00482       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);
00483    }
00484 
00485    ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
00486    
00487    snprintf(filename, sizeof(filename), "%s/mmlog", (char *)ast_config_AST_LOG_DIR);
00488    
00489    if (option_verbose)
00490       ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename);
00491    
00492    if ((mmlog = fopen(filename, "a+"))) {
00493       fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
00494       fflush(mmlog);
00495    }
00496 }
00497 
00498 #endif

Generated on Thu May 14 15:12:47 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7