Tue Aug 20 16:34:25 2013

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 - 2012, 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  * \author Richard Mudgett <rmudgett@digium.com>
00025  */
00026 
00027 /*** MODULEINFO
00028    <support_level>core</support_level>
00029  ***/
00030 
00031 #include "asterisk.h"
00032 
00033 #if defined(__AST_DEBUG_MALLOC)
00034 
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 377398 $")
00036 
00037 #include "asterisk/paths.h"   /* use ast_config_AST_LOG_DIR */
00038 #include <stddef.h>
00039 #include <time.h>
00040 
00041 #include "asterisk/cli.h"
00042 #include "asterisk/lock.h"
00043 #include "asterisk/strings.h"
00044 #include "asterisk/unaligned.h"
00045 
00046 /*!
00047  * The larger the number the faster memory can be freed.
00048  * However, more memory then is used for the regions[] hash
00049  * table.
00050  */
00051 #define SOME_PRIME 1567
00052 
00053 enum func_type {
00054    FUNC_CALLOC = 1,
00055    FUNC_MALLOC,
00056    FUNC_REALLOC,
00057    FUNC_STRDUP,
00058    FUNC_STRNDUP,
00059    FUNC_VASPRINTF,
00060    FUNC_ASPRINTF
00061 };
00062 
00063 /* Undefine all our macros */
00064 #undef malloc
00065 #undef calloc
00066 #undef realloc
00067 #undef strdup
00068 #undef strndup
00069 #undef free
00070 #undef vasprintf
00071 #undef asprintf
00072 
00073 #define FENCE_MAGIC     0xdeadbeef  /*!< Allocated memory high/low fence overwrite check. */
00074 #define FREED_MAGIC     0xdeaddead  /*!< Freed memory wipe filler. */
00075 #define MALLOC_FILLER   0x55     /*!< Malloced memory filler.  Must not be zero. */
00076 
00077 static FILE *mmlog;
00078 
00079 struct ast_region {
00080    AST_LIST_ENTRY(ast_region) node;
00081    size_t len;
00082    unsigned int cache;     /* region was allocated as part of a cache pool */
00083    unsigned int lineno;
00084    enum func_type which;
00085    char file[64];
00086    char func[40];
00087 
00088    /*!
00089     * \brief Lower guard fence.
00090     *
00091     * \note Must be right before data[].
00092     *
00093     * \note Padding between fence and data[] is irrelevent because
00094     * data[] is used to fill in the lower fence check value and not
00095     * the fence member.  The fence member is to ensure that there
00096     * is space reserved for the fence check value.
00097     */
00098    unsigned int fence;
00099    /*!
00100     * \brief Location of the requested malloc block to return.
00101     *
00102     * \note Must have the same alignment that malloc returns.
00103     * i.e., It is suitably aligned for any kind of varible.
00104     */
00105    unsigned char data[0] __attribute__((aligned));
00106 };
00107 
00108 /*! Hash table of lists of active allocated memory regions. */
00109 static struct ast_region *regions[SOME_PRIME];
00110 
00111 /*! Number of freed regions to keep around to delay actually freeing them. */
00112 #define FREED_MAX_COUNT    1500
00113 
00114 /*! Maximum size of a minnow block */
00115 #define MINNOWS_MAX_SIZE   50
00116 
00117 struct ast_freed_regions {
00118    /*! Memory regions that have been freed. */
00119    struct ast_region *regions[FREED_MAX_COUNT];
00120    /*! Next index into freed regions[] to use. */
00121    int index;
00122 };
00123 
00124 /*! Large memory blocks that have been freed. */
00125 static struct ast_freed_regions whales;
00126 /*! Small memory blocks that have been freed. */
00127 static struct ast_freed_regions minnows;
00128 
00129 enum summary_opts {
00130    /*! No summary at exit. */
00131    SUMMARY_OFF,
00132    /*! Bit set if summary by line at exit. */
00133    SUMMARY_BY_LINE = (1 << 0),
00134    /*! Bit set if summary by function at exit. */
00135    SUMMARY_BY_FUNC = (1 << 1),
00136    /*! Bit set if summary by file at exit. */
00137    SUMMARY_BY_FILE = (1 << 2),
00138 };
00139 
00140 /*! Summary options of unfreed regions at exit. */
00141 static enum summary_opts atexit_summary;
00142 /*! Nonzero if the unfreed regions are listed at exit. */
00143 static int atexit_list;
00144 
00145 #define HASH(a)      (((unsigned long)(a)) % ARRAY_LEN(regions))
00146 
00147 /*! Tracking this mutex will cause infinite recursion, as the mutex tracking
00148  *  code allocates memory */
00149 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
00150 
00151 #define astmm_log(...)                               \
00152    do {                                         \
00153       fprintf(stderr, __VA_ARGS__);        \
00154       if (mmlog) {                         \
00155          fprintf(mmlog, __VA_ARGS__); \
00156          fflush(mmlog);               \
00157       }                                    \
00158    } while (0)
00159 
00160 /*!
00161  * \internal
00162  *
00163  * \note If DO_CRASH is not defined then the function returns.
00164  *
00165  * \return Nothing
00166  */
00167 static void my_do_crash(void)
00168 {
00169    /*
00170     * Give the logger a chance to get the message out, just in case
00171     * we abort(), or Asterisk crashes due to whatever problem just
00172     * happened.
00173     */
00174    usleep(1);
00175    ast_do_crash();
00176 }
00177 
00178 static void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
00179 {
00180    struct ast_region *reg;
00181    unsigned int *fence;
00182    int hash;
00183 
00184    if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
00185       astmm_log("Memory Allocation Failure - '%d' bytes at %s %s() line %d\n",
00186          (int) size, file, func, lineno);
00187       return NULL;
00188    }
00189 
00190    reg->len = size;
00191    reg->cache = cache;
00192    reg->lineno = lineno;
00193    reg->which = which;
00194    ast_copy_string(reg->file, file, sizeof(reg->file));
00195    ast_copy_string(reg->func, func, sizeof(reg->func));
00196 
00197    /*
00198     * Init lower fence.
00199     *
00200     * We use the bytes just preceeding reg->data and not reg->fence
00201     * because there is likely to be padding between reg->fence and
00202     * reg->data for reg->data alignment.
00203     */
00204    fence = (unsigned int *) (reg->data - sizeof(*fence));
00205    *fence = FENCE_MAGIC;
00206 
00207    /* Init higher fence. */
00208    fence = (unsigned int *) (reg->data + reg->len);
00209    put_unaligned_uint32(fence, FENCE_MAGIC);
00210 
00211    hash = HASH(reg->data);
00212    ast_mutex_lock(&reglock);
00213    AST_LIST_NEXT(reg, node) = regions[hash];
00214    regions[hash] = reg;
00215    ast_mutex_unlock(&reglock);
00216 
00217    return reg->data;
00218 }
00219 
00220 /*!
00221  * \internal
00222  * \brief Wipe the region payload data with a known value.
00223  *
00224  * \param reg Region block to be wiped.
00225  *
00226  * \return Nothing
00227  */
00228 static void region_data_wipe(struct ast_region *reg)
00229 {
00230    void *end;
00231    unsigned int *pos;
00232 
00233    /*
00234     * Wipe the lower fence, the payload, and whatever amount of the
00235     * higher fence that falls into alignment with the payload.
00236     */
00237    end = reg->data + reg->len;
00238    for (pos = &reg->fence; (void *) pos <= end; ++pos) {
00239       *pos = FREED_MAGIC;
00240    }
00241 }
00242 
00243 /*!
00244  * \internal
00245  * \brief Check the region payload data for memory corruption.
00246  *
00247  * \param reg Region block to be checked.
00248  *
00249  * \return Nothing
00250  */
00251 static void region_data_check(struct ast_region *reg)
00252 {
00253    void *end;
00254    unsigned int *pos;
00255 
00256    /*
00257     * Check the lower fence, the payload, and whatever amount of
00258     * the higher fence that falls into alignment with the payload.
00259     */
00260    end = reg->data + reg->len;
00261    for (pos = &reg->fence; (void *) pos <= end; ++pos) {
00262       if (*pos != FREED_MAGIC) {
00263          astmm_log("WARNING: Memory corrupted after free of %p allocated at %s %s() line %d\n",
00264             reg->data, reg->file, reg->func, reg->lineno);
00265          my_do_crash();
00266          break;
00267       }
00268    }
00269 }
00270 
00271 /*!
00272  * \internal
00273  * \brief Flush the circular array of freed regions.
00274  *
00275  * \param freed Already freed region blocks storage.
00276  *
00277  * \return Nothing
00278  */
00279 static void freed_regions_flush(struct ast_freed_regions *freed)
00280 {
00281    int idx;
00282    struct ast_region *old;
00283 
00284    ast_mutex_lock(&reglock);
00285    for (idx = 0; idx < ARRAY_LEN(freed->regions); ++idx) {
00286       old = freed->regions[idx];
00287       freed->regions[idx] = NULL;
00288       if (old) {
00289          region_data_check(old);
00290          free(old);
00291       }
00292    }
00293    freed->index = 0;
00294    ast_mutex_unlock(&reglock);
00295 }
00296 
00297 /*!
00298  * \internal
00299  * \brief Delay freeing a region block.
00300  *
00301  * \param freed Already freed region blocks storage.
00302  * \param reg Region block to be freed.
00303  *
00304  * \return Nothing
00305  */
00306 static void region_free(struct ast_freed_regions *freed, struct ast_region *reg)
00307 {
00308    struct ast_region *old;
00309 
00310    region_data_wipe(reg);
00311 
00312    ast_mutex_lock(&reglock);
00313    old = freed->regions[freed->index];
00314    freed->regions[freed->index] = reg;
00315 
00316    ++freed->index;
00317    if (ARRAY_LEN(freed->regions) <= freed->index) {
00318       freed->index = 0;
00319    }
00320    ast_mutex_unlock(&reglock);
00321 
00322    if (old) {
00323       region_data_check(old);
00324       free(old);
00325    }
00326 }
00327 
00328 /*!
00329  * \internal
00330  * \brief Remove a region from the active regions.
00331  *
00332  * \param ptr Region payload data pointer.
00333  *
00334  * \retval region on success.
00335  * \retval NULL if not found.
00336  */
00337 static struct ast_region *region_remove(void *ptr)
00338 {
00339    int hash;
00340    struct ast_region *reg;
00341    struct ast_region *prev = NULL;
00342 
00343    hash = HASH(ptr);
00344 
00345    ast_mutex_lock(&reglock);
00346    for (reg = regions[hash]; reg; reg = AST_LIST_NEXT(reg, node)) {
00347       if (reg->data == ptr) {
00348          if (prev) {
00349             AST_LIST_NEXT(prev, node) = AST_LIST_NEXT(reg, node);
00350          } else {
00351             regions[hash] = AST_LIST_NEXT(reg, node);
00352          }
00353          break;
00354       }
00355       prev = reg;
00356    }
00357    ast_mutex_unlock(&reglock);
00358 
00359    return reg;
00360 }
00361 
00362 /*!
00363  * \internal
00364  * \brief Check the fences of a region.
00365  *
00366  * \param reg Region block to check.
00367  *
00368  * \return Nothing
00369  */
00370 static void region_check_fences(struct ast_region *reg)
00371 {
00372    unsigned int *fence;
00373 
00374    /*
00375     * We use the bytes just preceeding reg->data and not reg->fence
00376     * because there is likely to be padding between reg->fence and
00377     * reg->data for reg->data alignment.
00378     */
00379    fence = (unsigned int *) (reg->data - sizeof(*fence));
00380    if (*fence != FENCE_MAGIC) {
00381       astmm_log("WARNING: Low fence violation of %p allocated at %s %s() line %d\n",
00382          reg->data, reg->file, reg->func, reg->lineno);
00383       my_do_crash();
00384    }
00385    fence = (unsigned int *) (reg->data + reg->len);
00386    if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
00387       astmm_log("WARNING: High fence violation of %p allocated at %s %s() line %d\n",
00388          reg->data, reg->file, reg->func, reg->lineno);
00389       my_do_crash();
00390    }
00391 }
00392 
00393 /*!
00394  * \internal
00395  * \brief Check the fences of all regions currently allocated.
00396  *
00397  * \return Nothing
00398  */
00399 static void regions_check_all_fences(void)
00400 {
00401    int idx;
00402    struct ast_region *reg;
00403 
00404    ast_mutex_lock(&reglock);
00405    for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
00406       for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
00407          region_check_fences(reg);
00408       }
00409    }
00410    ast_mutex_unlock(&reglock);
00411 }
00412 
00413 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
00414 {
00415    struct ast_region *reg;
00416 
00417    if (!ptr) {
00418       return;
00419    }
00420 
00421    reg = region_remove(ptr);
00422    if (reg) {
00423       region_check_fences(reg);
00424 
00425       if (reg->len <= MINNOWS_MAX_SIZE) {
00426          region_free(&minnows, reg);
00427       } else {
00428          region_free(&whales, reg);
00429       }
00430    } else {
00431       /*
00432        * This memory region is not registered.  It could be because of
00433        * a double free or the memory block was not allocated by the
00434        * malloc debug code.
00435        */
00436       astmm_log("WARNING: Freeing unregistered memory %p by %s %s() line %d\n",
00437          ptr, file, func, lineno);
00438       my_do_crash();
00439    }
00440 }
00441 
00442 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
00443 {
00444    void *ptr;
00445 
00446    ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0);
00447    if (ptr) {
00448       memset(ptr, 0, size * nmemb);
00449    }
00450 
00451    return ptr;
00452 }
00453 
00454 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
00455 {
00456    void *ptr;
00457 
00458    ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1);
00459    if (ptr) {
00460       memset(ptr, 0, size * nmemb);
00461    }
00462 
00463    return ptr;
00464 }
00465 
00466 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
00467 {
00468    void *ptr;
00469 
00470    ptr = __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
00471    if (ptr) {
00472       /* Make sure that the malloced memory is not zero. */
00473       memset(ptr, MALLOC_FILLER, size);
00474    }
00475 
00476    return ptr;
00477 }
00478 
00479 void __ast_free(void *ptr, const char *file, int lineno, const char *func)
00480 {
00481    __ast_free_region(ptr, file, lineno, func);
00482 }
00483 
00484 /*!
00485  * \note reglock must be locked before calling.
00486  */
00487 static struct ast_region *region_find(void *ptr)
00488 {
00489    int hash;
00490    struct ast_region *reg;
00491 
00492    hash = HASH(ptr);
00493    for (reg = regions[hash]; reg; reg = AST_LIST_NEXT(reg, node)) {
00494       if (reg->data == ptr) {
00495          break;
00496       }
00497    }
00498 
00499    return reg;
00500 }
00501 
00502 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
00503 {
00504    size_t len;
00505    struct ast_region *found;
00506    void *new_mem;
00507 
00508    if (ptr) {
00509       ast_mutex_lock(&reglock);
00510       found = region_find(ptr);
00511       if (!found) {
00512          ast_mutex_unlock(&reglock);
00513          astmm_log("WARNING: Realloc of unregistered memory %p by %s %s() line %d\n",
00514             ptr, file, func, lineno);
00515          my_do_crash();
00516          return NULL;
00517       }
00518       len = found->len;
00519       ast_mutex_unlock(&reglock);
00520    } else {
00521       found = NULL;
00522       len = 0;
00523    }
00524 
00525    if (!size) {
00526       __ast_free_region(ptr, file, lineno, func);
00527       return NULL;
00528    }
00529 
00530    new_mem = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0);
00531    if (new_mem) {
00532       if (found) {
00533          /* Copy the old data to the new malloced memory. */
00534          if (size <= len) {
00535             memcpy(new_mem, ptr, size);
00536          } else {
00537             memcpy(new_mem, ptr, len);
00538             /* Make sure that the added memory is not zero. */
00539             memset(new_mem + len, MALLOC_FILLER, size - len);
00540          }
00541          __ast_free_region(ptr, file, lineno, func);
00542       } else {
00543          /* Make sure that the malloced memory is not zero. */
00544          memset(new_mem, MALLOC_FILLER, size);
00545       }
00546    }
00547 
00548    return new_mem;
00549 }
00550 
00551 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
00552 {
00553    size_t len;
00554    void *ptr;
00555 
00556    if (!s)
00557       return NULL;
00558 
00559    len = strlen(s) + 1;
00560    if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
00561       strcpy(ptr, s);
00562 
00563    return ptr;
00564 }
00565 
00566 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
00567 {
00568    size_t len;
00569    char *ptr;
00570 
00571    if (!s) {
00572       return NULL;
00573    }
00574 
00575    len = strnlen(s, n);
00576    if ((ptr = __ast_alloc_region(len + 1, FUNC_STRNDUP, file, lineno, func, 0))) {
00577       memcpy(ptr, s, len);
00578       ptr[len] = '\0';
00579    }
00580 
00581    return ptr;
00582 }
00583 
00584 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
00585 {
00586    int size;
00587    va_list ap, ap2;
00588    char s;
00589 
00590    *strp = NULL;
00591    va_start(ap, fmt);
00592    va_copy(ap2, ap);
00593    size = vsnprintf(&s, 1, fmt, ap2);
00594    va_end(ap2);
00595    if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
00596       va_end(ap);
00597       return -1;
00598    }
00599    vsnprintf(*strp, size + 1, fmt, ap);
00600    va_end(ap);
00601 
00602    return size;
00603 }
00604 
00605 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
00606 {
00607    int size;
00608    va_list ap2;
00609    char s;
00610 
00611    *strp = NULL;
00612    va_copy(ap2, ap);
00613    size = vsnprintf(&s, 1, fmt, ap2);
00614    va_end(ap2);
00615    if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
00616       va_end(ap);
00617       return -1;
00618    }
00619    vsnprintf(*strp, size + 1, fmt, ap);
00620 
00621    return size;
00622 }
00623 
00624 static char *handle_memory_atexit_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00625 {
00626    switch (cmd) {
00627    case CLI_INIT:
00628       e->command = "memory atexit list";
00629       e->usage =
00630          "Usage: memory atexit list {on|off}\n"
00631          "       Enable dumping a list of still allocated memory segments at exit.\n";
00632       return NULL;
00633    case CLI_GENERATE:
00634       if (a->pos == 3) {
00635          const char * const options[] = { "off", "on", NULL };
00636 
00637          return ast_cli_complete(a->word, options, a->n);
00638       }
00639       return NULL;
00640    }
00641 
00642    if (a->argc != 4) {
00643       return CLI_SHOWUSAGE;
00644    }
00645 
00646    if (ast_true(a->argv[3])) {
00647       atexit_list = 1;
00648    } else if (ast_false(a->argv[3])) {
00649       atexit_list = 0;
00650    } else {
00651       return CLI_SHOWUSAGE;
00652    }
00653 
00654    ast_cli(a->fd, "The atexit list is: %s\n", atexit_list ? "On" : "Off");
00655 
00656    return CLI_SUCCESS;
00657 }
00658 
00659 static char *handle_memory_atexit_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00660 {
00661    char buf[80];
00662 
00663    switch (cmd) {
00664    case CLI_INIT:
00665       e->command = "memory atexit summary";
00666       e->usage =
00667          "Usage: memory atexit summary {off|byline|byfunc|byfile}\n"
00668          "       Summary of still allocated memory segments at exit options.\n"
00669          "       off - Disable at exit summary.\n"
00670          "       byline - Enable at exit summary by file line number.\n"
00671          "       byfunc - Enable at exit summary by function name.\n"
00672          "       byfile - Enable at exit summary by file.\n"
00673          "\n"
00674          "       Note: byline, byfunc, and byfile are cumulative enables.\n";
00675       return NULL;
00676    case CLI_GENERATE:
00677       if (a->pos == 3) {
00678          const char * const options[] = { "off", "byline", "byfunc", "byfile", NULL };
00679 
00680          return ast_cli_complete(a->word, options, a->n);
00681       }
00682       return NULL;
00683    }
00684 
00685    if (a->argc != 4) {
00686       return CLI_SHOWUSAGE;
00687    }
00688 
00689    if (ast_false(a->argv[3])) {
00690       atexit_summary = SUMMARY_OFF;
00691    } else if (!strcasecmp(a->argv[3], "byline")) {
00692       atexit_summary |= SUMMARY_BY_LINE;
00693    } else if (!strcasecmp(a->argv[3], "byfunc")) {
00694       atexit_summary |= SUMMARY_BY_FUNC;
00695    } else if (!strcasecmp(a->argv[3], "byfile")) {
00696       atexit_summary |= SUMMARY_BY_FILE;
00697    } else {
00698       return CLI_SHOWUSAGE;
00699    }
00700 
00701    if (atexit_summary) {
00702       buf[0] = '\0';
00703       if (atexit_summary & SUMMARY_BY_LINE) {
00704          strcat(buf, "byline");
00705       }
00706       if (atexit_summary & SUMMARY_BY_FUNC) {
00707          if (buf[0]) {
00708             strcat(buf, " | ");
00709          }
00710          strcat(buf, "byfunc");
00711       }
00712       if (atexit_summary & SUMMARY_BY_FILE) {
00713          if (buf[0]) {
00714             strcat(buf, " | ");
00715          }
00716          strcat(buf, "byfile");
00717       }
00718    } else {
00719       strcpy(buf, "Off");
00720    }
00721    ast_cli(a->fd, "The atexit summary is: %s\n", buf);
00722 
00723    return CLI_SUCCESS;
00724 }
00725 
00726 static char *handle_memory_show_allocations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00727 {
00728    const char *fn = NULL;
00729    struct ast_region *reg;
00730    unsigned int idx;
00731    unsigned int len = 0;
00732    unsigned int cache_len = 0;
00733    unsigned int count = 0;
00734 
00735    switch (cmd) {
00736    case CLI_INIT:
00737       e->command = "memory show allocations";
00738       e->usage =
00739          "Usage: memory show allocations [<file>|anomalies]\n"
00740          "       Dumps a list of segments of allocated memory.\n"
00741          "       Defaults to listing all memory allocations.\n"
00742          "       <file> - Restricts output to memory allocated by the file.\n"
00743          "       anomalies - Only check for fence violations.\n";
00744       return NULL;
00745    case CLI_GENERATE:
00746       return NULL;
00747    }
00748 
00749    if (a->argc == 4) {
00750       fn = a->argv[3];
00751    } else if (a->argc != 3) {
00752       return CLI_SHOWUSAGE;
00753    }
00754 
00755    /* Look for historical misspelled option as well. */
00756    if (fn && (!strcasecmp(fn, "anomalies") || !strcasecmp(fn, "anomolies"))) {
00757       regions_check_all_fences();
00758       ast_cli(a->fd, "Anomaly check complete.\n");
00759       return CLI_SUCCESS;
00760    }
00761 
00762    ast_mutex_lock(&reglock);
00763    for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
00764       for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
00765          if (fn && strcasecmp(fn, reg->file)) {
00766             continue;
00767          }
00768 
00769          region_check_fences(reg);
00770 
00771          ast_cli(a->fd, "%10u bytes allocated%s by %20s() line %5u of %s\n",
00772             (unsigned int) reg->len, reg->cache ? " (cache)" : "",
00773             reg->func, reg->lineno, reg->file);
00774 
00775          len += reg->len;
00776          if (reg->cache) {
00777             cache_len += reg->len;
00778          }
00779          ++count;
00780       }
00781    }
00782    ast_mutex_unlock(&reglock);
00783 
00784    if (cache_len) {
00785       ast_cli(a->fd, "%u bytes allocated (%u in caches) in %u allocations\n",
00786          len, cache_len, count);
00787    } else {
00788       ast_cli(a->fd, "%u bytes allocated in %u allocations\n", len, count);
00789    }
00790 
00791    return CLI_SUCCESS;
00792 }
00793 
00794 static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00795 {
00796 #define my_max(a, b) ((a) >= (b) ? (a) : (b))
00797 
00798    const char *fn = NULL;
00799    int idx;
00800    int cmp;
00801    struct ast_region *reg;
00802    unsigned int len = 0;
00803    unsigned int cache_len = 0;
00804    unsigned int count = 0;
00805    struct file_summary {
00806       struct file_summary *next;
00807       unsigned int len;
00808       unsigned int cache_len;
00809       unsigned int count;
00810       unsigned int lineno;
00811       char name[my_max(sizeof(reg->file), sizeof(reg->func))];
00812    } *list = NULL, *cur, **prev;
00813 
00814    switch (cmd) {
00815    case CLI_INIT:
00816       e->command = "memory show summary";
00817       e->usage =
00818          "Usage: memory show summary [<file>]\n"
00819          "       Summarizes heap memory allocations by file, or optionally\n"
00820          "       by line, if a file is specified.\n";
00821       return NULL;
00822    case CLI_GENERATE:
00823       return NULL;
00824    }
00825 
00826    if (a->argc == 4) {
00827       fn = a->argv[3];
00828    } else if (a->argc != 3) {
00829       return CLI_SHOWUSAGE;
00830    }
00831 
00832    ast_mutex_lock(&reglock);
00833    for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
00834       for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
00835          if (fn) {
00836             if (strcasecmp(fn, reg->file)) {
00837                continue;
00838             }
00839 
00840             /* Sort list by func/lineno.  Find existing or place to insert. */
00841             for (prev = &list; (cur = *prev); prev = &cur->next) {
00842                cmp = strcmp(cur->name, reg->func);
00843                if (cmp < 0) {
00844                   continue;
00845                }
00846                if (cmp > 0) {
00847                   /* Insert before current */
00848                   cur = NULL;
00849                   break;
00850                }
00851                cmp = cur->lineno - reg->lineno;
00852                if (cmp < 0) {
00853                   continue;
00854                }
00855                if (cmp > 0) {
00856                   /* Insert before current */
00857                   cur = NULL;
00858                }
00859                break;
00860             }
00861          } else {
00862             /* Sort list by filename.  Find existing or place to insert. */
00863             for (prev = &list; (cur = *prev); prev = &cur->next) {
00864                cmp = strcmp(cur->name, reg->file);
00865                if (cmp < 0) {
00866                   continue;
00867                }
00868                if (cmp > 0) {
00869                   /* Insert before current */
00870                   cur = NULL;
00871                }
00872                break;
00873             }
00874          }
00875 
00876          if (!cur) {
00877             cur = ast_alloca(sizeof(*cur));
00878             memset(cur, 0, sizeof(*cur));
00879             cur->lineno = reg->lineno;
00880             ast_copy_string(cur->name, fn ? reg->func : reg->file, sizeof(cur->name));
00881 
00882             cur->next = *prev;
00883             *prev = cur;
00884          }
00885 
00886          cur->len += reg->len;
00887          if (reg->cache) {
00888             cur->cache_len += reg->len;
00889          }
00890          ++cur->count;
00891       }
00892    }
00893    ast_mutex_unlock(&reglock);
00894 
00895    /* Dump the whole list */
00896    for (cur = list; cur; cur = cur->next) {
00897       len += cur->len;
00898       cache_len += cur->cache_len;
00899       count += cur->count;
00900       if (cur->cache_len) {
00901          if (fn) {
00902             ast_cli(a->fd, "%10u bytes (%10u cache) in %10u allocations by %20s() line %5u of %s\n",
00903                cur->len, cur->cache_len, cur->count, cur->name, cur->lineno, fn);
00904          } else {
00905             ast_cli(a->fd, "%10u bytes (%10u cache) in %10u allocations in file %s\n",
00906                cur->len, cur->cache_len, cur->count, cur->name);
00907          }
00908       } else {
00909          if (fn) {
00910             ast_cli(a->fd, "%10u bytes in %10u allocations by %20s() line %5u of %s\n",
00911                cur->len, cur->count, cur->name, cur->lineno, fn);
00912          } else {
00913             ast_cli(a->fd, "%10u bytes in %10u allocations in file %s\n",
00914                cur->len, cur->count, cur->name);
00915          }
00916       }
00917    }
00918 
00919    if (cache_len) {
00920       ast_cli(a->fd, "%u bytes allocated (%u in caches) in %u allocations\n",
00921          len, cache_len, count);
00922    } else {
00923       ast_cli(a->fd, "%u bytes allocated in %u allocations\n", len, count);
00924    }
00925 
00926    return CLI_SUCCESS;
00927 }
00928 
00929 static struct ast_cli_entry cli_memory[] = {
00930    AST_CLI_DEFINE(handle_memory_atexit_list, "Enable memory allocations not freed at exit list."),
00931    AST_CLI_DEFINE(handle_memory_atexit_summary, "Enable memory allocations not freed at exit summary."),
00932    AST_CLI_DEFINE(handle_memory_show_allocations, "Display outstanding memory allocations"),
00933    AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
00934 };
00935 
00936 AST_LIST_HEAD_NOLOCK(region_list, ast_region);
00937 
00938 /*!
00939  * \internal
00940  * \brief Convert the allocated regions hash table to a list.
00941  *
00942  * \param list Fill list with the allocated regions.
00943  *
00944  * \details
00945  * Take all allocated regions from the regions[] and put them
00946  * into the list.
00947  *
00948  * \note reglock must be locked before calling.
00949  *
00950  * \note This function is destructive to the regions[] lists.
00951  *
00952  * \return Length of list created.
00953  */
00954 static size_t mm_atexit_hash_list(struct region_list *list)
00955 {
00956    struct ast_region *reg;
00957    size_t total_length;
00958    int idx;
00959 
00960    total_length = 0;
00961    for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
00962       while ((reg = regions[idx])) {
00963          regions[idx] = AST_LIST_NEXT(reg, node);
00964          AST_LIST_NEXT(reg, node) = NULL;
00965          AST_LIST_INSERT_HEAD(list, reg, node);
00966          ++total_length;
00967       }
00968    }
00969    return total_length;
00970 }
00971 
00972 /*!
00973  * \internal
00974  * \brief Put the regions list into the allocated regions hash table.
00975  *
00976  * \param list List to put into the allocated regions hash table.
00977  *
00978  * \note reglock must be locked before calling.
00979  *
00980  * \return Nothing
00981  */
00982 static void mm_atexit_hash_restore(struct region_list *list)
00983 {
00984    struct ast_region *reg;
00985    int hash;
00986 
00987    while ((reg = AST_LIST_REMOVE_HEAD(list, node))) {
00988       hash = HASH(reg->data);
00989       AST_LIST_NEXT(reg, node) = regions[hash];
00990       regions[hash] = reg;
00991    }
00992 }
00993 
00994 /*!
00995  * \internal
00996  * \brief Sort regions comparision.
00997  *
00998  * \param left Region to compare.
00999  * \param right Region to compare.
01000  *
01001  * \retval <0 if left < right
01002  * \retval =0 if left == right
01003  * \retval >0 if left > right
01004  */
01005 static int mm_atexit_cmp(struct ast_region *left, struct ast_region *right)
01006 {
01007    int cmp;
01008    ptrdiff_t cmp_ptr;
01009    ssize_t cmp_size;
01010 
01011    /* Sort by filename. */
01012    cmp = strcmp(left->file, right->file);
01013    if (cmp) {
01014       return cmp;
01015    }
01016 
01017    /* Sort by line number. */
01018    cmp = left->lineno - right->lineno;
01019    if (cmp) {
01020       return cmp;
01021    }
01022 
01023    /* Sort by allocated size. */
01024    cmp_size = left->len - right->len;
01025    if (cmp_size) {
01026       if (cmp_size < 0) {
01027          return -1;
01028       }
01029       return 1;
01030    }
01031 
01032    /* Sort by allocated pointers just because. */
01033    cmp_ptr = left->data - right->data;
01034    if (cmp_ptr) {
01035       if (cmp_ptr < 0) {
01036          return -1;
01037       }
01038       return 1;
01039    }
01040 
01041    return 0;
01042 }
01043 
01044 /*!
01045  * \internal
01046  * \brief Merge the given sorted sublists into sorted order onto the end of the list.
01047  *
01048  * \param list Merge sublists onto this list.
01049  * \param sub1 First sublist to merge.
01050  * \param sub2 Second sublist to merge.
01051  *
01052  * \return Nothing
01053  */
01054 static void mm_atexit_list_merge(struct region_list *list, struct region_list *sub1, struct region_list *sub2)
01055 {
01056    struct ast_region *reg;
01057 
01058    for (;;) {
01059       if (AST_LIST_EMPTY(sub1)) {
01060          /* The remaining sublist goes onto the list. */
01061          AST_LIST_APPEND_LIST(list, sub2, node);
01062          break;
01063       }
01064       if (AST_LIST_EMPTY(sub2)) {
01065          /* The remaining sublist goes onto the list. */
01066          AST_LIST_APPEND_LIST(list, sub1, node);
01067          break;
01068       }
01069 
01070       if (mm_atexit_cmp(AST_LIST_FIRST(sub1), AST_LIST_FIRST(sub2)) <= 0) {
01071          reg = AST_LIST_REMOVE_HEAD(sub1, node);
01072       } else {
01073          reg = AST_LIST_REMOVE_HEAD(sub2, node);
01074       }
01075       AST_LIST_INSERT_TAIL(list, reg, node);
01076    }
01077 }
01078 
01079 /*!
01080  * \internal
01081  * \brief Take sublists off of the given list.
01082  *
01083  * \param list Source list to remove sublists from the beginning of list.
01084  * \param sub Array of sublists to fill. (Lists are empty on entry.)
01085  * \param num_lists Number of lists to remove from the source list.
01086  * \param size Size of the sublists to remove.
01087  * \param remaining Remaining number of elements on the source list.
01088  *
01089  * \return Nothing
01090  */
01091 static void mm_atexit_list_split(struct region_list *list, struct region_list sub[], size_t num_lists, size_t size, size_t *remaining)
01092 {
01093    int idx;
01094 
01095    for (idx = 0; idx < num_lists; ++idx) {
01096       size_t count;
01097 
01098       if (*remaining < size) {
01099          /* The remaining source list goes onto the sublist. */
01100          AST_LIST_APPEND_LIST(&sub[idx], list, node);
01101          *remaining = 0;
01102          break;
01103       }
01104 
01105       /* Take a sublist off the beginning of the source list. */
01106       *remaining -= size;
01107       for (count = size; count--;) {
01108          struct ast_region *reg;
01109 
01110          reg = AST_LIST_REMOVE_HEAD(list, node);
01111          AST_LIST_INSERT_TAIL(&sub[idx], reg, node);
01112       }
01113    }
01114 }
01115 
01116 /*!
01117  * \internal
01118  * \brief Sort the regions list using mergesort.
01119  *
01120  * \param list Allocated regions list to sort.
01121  * \param length Length of the list.
01122  *
01123  * \return Nothing
01124  */
01125 static void mm_atexit_list_sort(struct region_list *list, size_t length)
01126 {
01127    /*! Semi-sorted merged list. */
01128    struct region_list merged = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
01129    /*! Sublists to merge. (Can only merge two sublists at this time.) */
01130    struct region_list sub[2] = {
01131       AST_LIST_HEAD_NOLOCK_INIT_VALUE,
01132       AST_LIST_HEAD_NOLOCK_INIT_VALUE
01133    };
01134    /*! Sublist size. */
01135    size_t size = 1;
01136    /*! Remaining elements in the list. */
01137    size_t remaining;
01138    /*! Number of sublist merge passes to process the list. */
01139    int passes;
01140 
01141    for (;;) {
01142       remaining = length;
01143 
01144       passes = 0;
01145       while (!AST_LIST_EMPTY(list)) {
01146          mm_atexit_list_split(list, sub, ARRAY_LEN(sub), size, &remaining);
01147          mm_atexit_list_merge(&merged, &sub[0], &sub[1]);
01148          ++passes;
01149       }
01150       AST_LIST_APPEND_LIST(list, &merged, node);
01151       if (passes <= 1) {
01152          /* The list is now sorted. */
01153          break;
01154       }
01155 
01156       /* Double the sublist size to remove for next round. */
01157       size <<= 1;
01158    }
01159 }
01160 
01161 /*!
01162  * \internal
01163  * \brief List all regions currently allocated.
01164  *
01165  * \param alloced regions list.
01166  *
01167  * \return Nothing
01168  */
01169 static void mm_atexit_regions_list(struct region_list *alloced)
01170 {
01171    struct ast_region *reg;
01172 
01173    AST_LIST_TRAVERSE(alloced, reg, node) {
01174       astmm_log("%s %s() line %u: %u bytes%s at %p\n",
01175          reg->file, reg->func, reg->lineno,
01176          (unsigned int) reg->len, reg->cache ? " (cache)" : "", reg->data);
01177    }
01178 }
01179 
01180 /*!
01181  * \internal
01182  * \brief Summarize all regions currently allocated.
01183  *
01184  * \param alloced Sorted regions list.
01185  *
01186  * \return Nothing
01187  */
01188 static void mm_atexit_regions_summary(struct region_list *alloced)
01189 {
01190    struct ast_region *reg;
01191    struct ast_region *next;
01192    struct {
01193       unsigned int count;
01194       unsigned int len;
01195       unsigned int cache_len;
01196    } by_line, by_func, by_file, total;
01197 
01198    by_line.count = 0;
01199    by_line.len = 0;
01200    by_line.cache_len = 0;
01201 
01202    by_func.count = 0;
01203    by_func.len = 0;
01204    by_func.cache_len = 0;
01205 
01206    by_file.count = 0;
01207    by_file.len = 0;
01208    by_file.cache_len = 0;
01209 
01210    total.count = 0;
01211    total.len = 0;
01212    total.cache_len = 0;
01213 
01214    AST_LIST_TRAVERSE(alloced, reg, node) {
01215       next = AST_LIST_NEXT(reg, node);
01216 
01217       ++by_line.count;
01218       by_line.len += reg->len;
01219       if (reg->cache) {
01220          by_line.cache_len += reg->len;
01221       }
01222       if (next && !strcmp(reg->file, next->file) && reg->lineno == next->lineno) {
01223          continue;
01224       }
01225       if (atexit_summary & SUMMARY_BY_LINE) {
01226          if (by_line.cache_len) {
01227             astmm_log("%10u bytes (%u in caches) in %u allocations. %s %s() line %u\n",
01228                by_line.len, by_line.cache_len, by_line.count, reg->file, reg->func, reg->lineno);
01229          } else {
01230             astmm_log("%10u bytes in %5u allocations. %s %s() line %u\n",
01231                by_line.len, by_line.count, reg->file, reg->func, reg->lineno);
01232          }
01233       }
01234 
01235       by_func.count += by_line.count;
01236       by_func.len += by_line.len;
01237       by_func.cache_len += by_line.cache_len;
01238       by_line.count = 0;
01239       by_line.len = 0;
01240       by_line.cache_len = 0;
01241       if (next && !strcmp(reg->file, next->file) && !strcmp(reg->func, next->func)) {
01242          continue;
01243       }
01244       if (atexit_summary & SUMMARY_BY_FUNC) {
01245          if (by_func.cache_len) {
01246             astmm_log("%10u bytes (%u in caches) in %u allocations. %s %s()\n",
01247                by_func.len, by_func.cache_len, by_func.count, reg->file, reg->func);
01248          } else {
01249             astmm_log("%10u bytes in %5u allocations. %s %s()\n",
01250                by_func.len, by_func.count, reg->file, reg->func);
01251          }
01252       }
01253 
01254       by_file.count += by_func.count;
01255       by_file.len += by_func.len;
01256       by_file.cache_len += by_func.cache_len;
01257       by_func.count = 0;
01258       by_func.len = 0;
01259       by_func.cache_len = 0;
01260       if (next && !strcmp(reg->file, next->file)) {
01261          continue;
01262       }
01263       if (atexit_summary & SUMMARY_BY_FILE) {
01264          if (by_file.cache_len) {
01265             astmm_log("%10u bytes (%u in caches) in %u allocations. %s\n",
01266                by_file.len, by_file.cache_len, by_file.count, reg->file);
01267          } else {
01268             astmm_log("%10u bytes in %5u allocations. %s\n",
01269                by_file.len, by_file.count, reg->file);
01270          }
01271       }
01272 
01273       total.count += by_file.count;
01274       total.len += by_file.len;
01275       total.cache_len += by_file.cache_len;
01276       by_file.count = 0;
01277       by_file.len = 0;
01278       by_file.cache_len = 0;
01279    }
01280 
01281    if (total.cache_len) {
01282       astmm_log("%u bytes (%u in caches) in %u allocations.\n",
01283          total.len, total.cache_len, total.count);
01284    } else {
01285       astmm_log("%u bytes in %u allocations.\n", total.len, total.count);
01286    }
01287 }
01288 
01289 /*!
01290  * \internal
01291  * \brief Dump the memory allocations atexit.
01292  *
01293  * \note reglock must be locked before calling.
01294  *
01295  * \return Nothing
01296  */
01297 static void mm_atexit_dump(void)
01298 {
01299    struct region_list alloced_atexit = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
01300    size_t length;
01301 
01302    length = mm_atexit_hash_list(&alloced_atexit);
01303    if (!length) {
01304       /* Wow!  This is amazing! */
01305       astmm_log("Exiting with all memory freed.\n");
01306       return;
01307    }
01308 
01309    mm_atexit_list_sort(&alloced_atexit, length);
01310 
01311    astmm_log("Exiting with the following memory not freed:\n");
01312    if (atexit_list) {
01313       mm_atexit_regions_list(&alloced_atexit);
01314    }
01315    if (atexit_summary) {
01316       mm_atexit_regions_summary(&alloced_atexit);
01317    }
01318 
01319    /*
01320     * Put the alloced list back into regions[].
01321     *
01322     * We have do do this because we can get called before all other
01323     * threads have terminated.
01324     */
01325    mm_atexit_hash_restore(&alloced_atexit);
01326 }
01327 
01328 /*!
01329  * \internal
01330  * \return Nothing
01331  */
01332 static void mm_atexit_final(void)
01333 {
01334    FILE *log;
01335 
01336    /* Only wait if we want atexit allocation dumps. */
01337    if (atexit_list || atexit_summary) {
01338       fprintf(stderr, "Waiting 10 seconds to let other threads die.\n");
01339       sleep(10);
01340    }
01341 
01342    regions_check_all_fences();
01343 
01344    /* Flush all delayed memory free circular arrays. */
01345    freed_regions_flush(&whales);
01346    freed_regions_flush(&minnows);
01347 
01348    /* Peform atexit allocation dumps. */
01349    if (atexit_list || atexit_summary) {
01350       ast_mutex_lock(&reglock);
01351       mm_atexit_dump();
01352       ast_mutex_unlock(&reglock);
01353    }
01354 
01355    /* Close the log file. */
01356    log = mmlog;
01357    mmlog = NULL;
01358    if (log) {
01359       fclose(log);
01360    }
01361 }
01362 
01363 /*!
01364  * \brief Initialize malloc debug phase 1.
01365  *
01366  * \note Must be called first thing in main().
01367  *
01368  * \return Nothing
01369  */
01370 void __ast_mm_init_phase_1(void)
01371 {
01372    atexit(mm_atexit_final);
01373 }
01374 
01375 /*!
01376  * \internal
01377  * \return Nothing
01378  */
01379 static void mm_atexit_ast(void)
01380 {
01381    ast_cli_unregister_multiple(cli_memory, ARRAY_LEN(cli_memory));
01382 }
01383 
01384 /*!
01385  * \brief Initialize malloc debug phase 2.
01386  *
01387  * \return Nothing
01388  */
01389 void __ast_mm_init_phase_2(void)
01390 {
01391    char filename[PATH_MAX];
01392 
01393    ast_cli_register_multiple(cli_memory, ARRAY_LEN(cli_memory));
01394 
01395    snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
01396 
01397    ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
01398 
01399    mmlog = fopen(filename, "a+");
01400    if (mmlog) {
01401       fprintf(mmlog, "%ld - New session\n", (long) time(NULL));
01402       fflush(mmlog);
01403    } else {
01404       ast_log(LOG_ERROR, "Could not open malloc debug log file: %s\n", filename);
01405    }
01406 
01407    ast_register_atexit(mm_atexit_ast);
01408 }
01409 
01410 #endif   /* defined(__AST_DEBUG_MALLOC) */

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1