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

Generated on Thu Jul 9 13:40:23 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7