Wed Apr 6 11:29:40 2011

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

Generated on Wed Apr 6 11:29:40 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7