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

Generated on Thu Apr 16 06:27:14 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6