Wed Jan 27 20:02:04 2016

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

Generated on 27 Jan 2016 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1