loader: Correct overly strict startup checks.
[asterisk/asterisk.git] / main / astmm.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2012, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Memory Management
22  *
23  * \author Mark Spencer <markster@digium.com>
24  * \author Richard Mudgett <rmudgett@digium.com>
25  */
26
27 /*** MODULEINFO
28         <support_level>core</support_level>
29  ***/
30
31 #define ASTMM_LIBC ASTMM_IGNORE
32 #include "asterisk.h"
33
34 #if defined(__AST_DEBUG_MALLOC)
35
36 #include "asterisk/paths.h"     /* use ast_config_AST_LOG_DIR */
37 #include <stddef.h>
38 #include <time.h>
39
40 #include "asterisk/cli.h"
41 #include "asterisk/lock.h"
42 #include "asterisk/strings.h"
43 #include "asterisk/unaligned.h"
44 #include "asterisk/backtrace.h"
45
46 /*!
47  * The larger the number the faster memory can be freed.
48  * However, more memory then is used for the regions[] hash
49  * table.
50  */
51 #define SOME_PRIME 1567
52
53 enum func_type {
54         FUNC_CALLOC = 1,
55         FUNC_MALLOC,
56         FUNC_REALLOC,
57         FUNC_STRDUP,
58         FUNC_STRNDUP,
59         FUNC_VASPRINTF,
60         FUNC_ASPRINTF
61 };
62
63 #define FENCE_MAGIC             0xfeedbabe      /*!< Allocated memory high/low fence overwrite check. */
64 #define FREED_MAGIC             0xdeaddead      /*!< Freed memory wipe filler. */
65 #define MALLOC_FILLER   0x55            /*!< Malloced memory filler.  Must not be zero. */
66
67 static FILE *mmlog;
68
69 struct ast_region {
70         AST_LIST_ENTRY(ast_region) node;
71         struct ast_bt *bt;
72         size_t len;
73         unsigned int cache;             /* region was allocated as part of a cache pool */
74         unsigned int lineno;
75         enum func_type which;
76         char file[64];
77         char func[40];
78         /*!
79          * \brief Lower guard fence.
80          *
81          * \note Must be right before data[].
82          *
83          * \note Padding between fence and data[] is irrelevent because
84          * data[] is used to fill in the lower fence check value and not
85          * the fence member.  The fence member is to ensure that there
86          * is space reserved for the fence check value.
87          */
88         unsigned int fence;
89         /*!
90          * \brief Location of the requested malloc block to return.
91          *
92          * \note Must have the same alignment that malloc returns.
93          * i.e., It is suitably aligned for any kind of varible.
94          */
95         unsigned char data[0] __attribute__((aligned));
96 };
97
98 /*! Hash table of lists of active allocated memory regions. */
99 static struct ast_region *regions[SOME_PRIME];
100
101 /*! Number of freed regions to keep around to delay actually freeing them. */
102 #define FREED_MAX_COUNT         1500
103
104 /*! Maximum size of a minnow block */
105 #define MINNOWS_MAX_SIZE        50
106
107 struct ast_freed_regions {
108         /*! Memory regions that have been freed. */
109         struct ast_region *regions[FREED_MAX_COUNT];
110         /*! Next index into freed regions[] to use. */
111         int index;
112 };
113
114 /*! Large memory blocks that have been freed. */
115 static struct ast_freed_regions whales;
116 /*! Small memory blocks that have been freed. */
117 static struct ast_freed_regions minnows;
118
119 enum summary_opts {
120         /*! No summary at exit. */
121         SUMMARY_OFF,
122         /*! Bit set if summary by line at exit. */
123         SUMMARY_BY_LINE = (1 << 0),
124         /*! Bit set if summary by function at exit. */
125         SUMMARY_BY_FUNC = (1 << 1),
126         /*! Bit set if summary by file at exit. */
127         SUMMARY_BY_FILE = (1 << 2),
128 };
129
130 /*! Summary options of unfreed regions at exit. */
131 static enum summary_opts atexit_summary;
132 /*! Nonzero if the unfreed regions are listed at exit. */
133 static int atexit_list;
134 /*! Nonzero if the memory allocation backtrace is enabled. */
135 static int backtrace_enabled;
136
137 #define HASH(a)         (((unsigned long)(a)) % ARRAY_LEN(regions))
138
139 /*! Tracking this mutex will cause infinite recursion, as the mutex tracking
140  *  code allocates memory */
141 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
142
143 #define astmm_log(...)                               \
144         do {                                         \
145                 fprintf(stderr, __VA_ARGS__);        \
146                 if (mmlog) {                         \
147                         fprintf(mmlog, __VA_ARGS__); \
148                         fflush(mmlog);               \
149                 }                                    \
150         } while (0)
151
152 void *ast_std_malloc(size_t size)
153 {
154         return malloc(size);
155 }
156
157 void *ast_std_calloc(size_t nmemb, size_t size)
158 {
159         return calloc(nmemb, size);
160 }
161
162 void *ast_std_realloc(void *ptr, size_t size)
163 {
164         return realloc(ptr, size);
165 }
166
167 void ast_std_free(void *ptr)
168 {
169         free(ptr);
170 }
171
172 void ast_free_ptr(void *ptr)
173 {
174         ast_free(ptr);
175 }
176
177 static void print_backtrace(struct ast_bt *bt)
178 {
179         int i = 0;
180         char **strings;
181
182         if (!bt) {
183                 return;
184         }
185
186         if ((strings = ast_bt_get_symbols(bt->addresses, bt->num_frames))) {
187                 astmm_log("Memory allocation backtrace:\n");
188                 for (i = 3; i < bt->num_frames - 2; i++) {
189                         astmm_log("#%d: [%p] %s\n", i - 3, bt->addresses[i], strings[i]);
190                 }
191                 ast_std_free(strings);
192         }
193 }
194
195 /*!
196  * \internal
197  *
198  * \note If DO_CRASH is not defined then the function returns.
199  *
200  * \return Nothing
201  */
202 static void my_do_crash(void)
203 {
204         /*
205          * Give the logger a chance to get the message out, just in case
206          * we abort(), or Asterisk crashes due to whatever problem just
207          * happened.
208          */
209         usleep(1);
210         ast_do_crash();
211 }
212
213 static void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
214 {
215         struct ast_region *reg;
216         unsigned int *fence;
217         int hash;
218
219         if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
220                 astmm_log("Memory Allocation Failure - '%d' bytes at %s %s() line %d\n",
221                         (int) size, file, func, lineno);
222                 return NULL;
223         }
224
225         reg->len = size;
226         reg->cache = cache;
227         reg->lineno = lineno;
228         reg->which = which;
229         reg->bt = backtrace_enabled ? ast_bt_create() : NULL;
230         ast_copy_string(reg->file, file, sizeof(reg->file));
231         ast_copy_string(reg->func, func, sizeof(reg->func));
232
233         /*
234          * Init lower fence.
235          *
236          * We use the bytes just preceeding reg->data and not reg->fence
237          * because there is likely to be padding between reg->fence and
238          * reg->data for reg->data alignment.
239          */
240         fence = (unsigned int *) (reg->data - sizeof(*fence));
241         *fence = FENCE_MAGIC;
242
243         /* Init higher fence. */
244         fence = (unsigned int *) (reg->data + reg->len);
245         put_unaligned_uint32(fence, FENCE_MAGIC);
246
247         hash = HASH(reg->data);
248         ast_mutex_lock(&reglock);
249         AST_LIST_NEXT(reg, node) = regions[hash];
250         regions[hash] = reg;
251         ast_mutex_unlock(&reglock);
252
253         return reg->data;
254 }
255
256 /*!
257  * \internal
258  * \brief Wipe the region payload data with a known value.
259  *
260  * \param reg Region block to be wiped.
261  *
262  * \return Nothing
263  */
264 static void region_data_wipe(struct ast_region *reg)
265 {
266         void *end;
267         unsigned int *pos;
268
269         /*
270          * Wipe the lower fence, the payload, and whatever amount of the
271          * higher fence that falls into alignment with the payload.
272          */
273         end = reg->data + reg->len;
274         for (pos = &reg->fence; (void *) pos <= end; ++pos) {
275                 *pos = FREED_MAGIC;
276         }
277 }
278
279 /*!
280  * \internal
281  * \brief Check the region payload data for memory corruption.
282  *
283  * \param reg Region block to be checked.
284  *
285  * \return Nothing
286  */
287 static void region_data_check(struct ast_region *reg)
288 {
289         void *end;
290         unsigned int *pos;
291
292         /*
293          * Check the lower fence, the payload, and whatever amount of
294          * the higher fence that falls into alignment with the payload.
295          */
296         end = reg->data + reg->len;
297         for (pos = &reg->fence; (void *) pos <= end; ++pos) {
298                 if (*pos != FREED_MAGIC) {
299                         astmm_log("WARNING: Memory corrupted after free of %p allocated at %s %s() line %d\n",
300                                 reg->data, reg->file, reg->func, reg->lineno);
301                         print_backtrace(reg->bt);
302                         my_do_crash();
303                         break;
304                 }
305         }
306 }
307
308 /*!
309  * \internal
310  * \brief Flush the circular array of freed regions.
311  *
312  * \param freed Already freed region blocks storage.
313  *
314  * \return Nothing
315  */
316 static void freed_regions_flush(struct ast_freed_regions *freed)
317 {
318         int idx;
319         struct ast_region *old;
320
321         ast_mutex_lock(&reglock);
322         for (idx = 0; idx < ARRAY_LEN(freed->regions); ++idx) {
323                 old = freed->regions[idx];
324                 freed->regions[idx] = NULL;
325                 if (old) {
326                         region_data_check(old);
327                         free(old);
328                 }
329         }
330         freed->index = 0;
331         ast_mutex_unlock(&reglock);
332 }
333
334 /*!
335  * \internal
336  * \brief Delay freeing a region block.
337  *
338  * \param freed Already freed region blocks storage.
339  * \param reg Region block to be freed.
340  *
341  * \return Nothing
342  */
343 static void region_free(struct ast_freed_regions *freed, struct ast_region *reg)
344 {
345         struct ast_region *old;
346
347         region_data_wipe(reg);
348
349         ast_mutex_lock(&reglock);
350         old = freed->regions[freed->index];
351         freed->regions[freed->index] = reg;
352
353         ++freed->index;
354         if (ARRAY_LEN(freed->regions) <= freed->index) {
355                 freed->index = 0;
356         }
357         ast_mutex_unlock(&reglock);
358
359         if (old) {
360                 region_data_check(old);
361                 old->bt = ast_bt_destroy(old->bt);
362                 free(old);
363         }
364 }
365
366 /*!
367  * \internal
368  * \brief Remove a region from the active regions.
369  *
370  * \param ptr Region payload data pointer.
371  *
372  * \retval region on success.
373  * \retval NULL if not found.
374  */
375 static struct ast_region *region_remove(void *ptr)
376 {
377         int hash;
378         struct ast_region *reg;
379         struct ast_region *prev = NULL;
380
381         hash = HASH(ptr);
382
383         ast_mutex_lock(&reglock);
384         for (reg = regions[hash]; reg; reg = AST_LIST_NEXT(reg, node)) {
385                 if (reg->data == ptr) {
386                         if (prev) {
387                                 AST_LIST_NEXT(prev, node) = AST_LIST_NEXT(reg, node);
388                         } else {
389                                 regions[hash] = AST_LIST_NEXT(reg, node);
390                         }
391                         break;
392                 }
393                 prev = reg;
394         }
395         ast_mutex_unlock(&reglock);
396
397         return reg;
398 }
399
400 /*!
401  * \internal
402  * \brief Check the fences of a region.
403  *
404  * \param reg Region block to check.
405  *
406  * \return Nothing
407  */
408 static void region_check_fences(struct ast_region *reg)
409 {
410         unsigned int *fence;
411
412         /*
413          * We use the bytes just preceeding reg->data and not reg->fence
414          * because there is likely to be padding between reg->fence and
415          * reg->data for reg->data alignment.
416          */
417         fence = (unsigned int *) (reg->data - sizeof(*fence));
418         if (*fence != FENCE_MAGIC) {
419                 astmm_log("WARNING: Low fence violation of %p allocated at %s %s() line %d\n",
420                         reg->data, reg->file, reg->func, reg->lineno);
421                 print_backtrace(reg->bt);
422                 my_do_crash();
423         }
424         fence = (unsigned int *) (reg->data + reg->len);
425         if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
426                 astmm_log("WARNING: High fence violation of %p allocated at %s %s() line %d\n",
427                         reg->data, reg->file, reg->func, reg->lineno);
428                 print_backtrace(reg->bt);
429                 my_do_crash();
430         }
431 }
432
433 /*!
434  * \internal
435  * \brief Check the fences of all regions currently allocated.
436  *
437  * \return Nothing
438  */
439 static void regions_check_all_fences(void)
440 {
441         int idx;
442         struct ast_region *reg;
443
444         ast_mutex_lock(&reglock);
445         for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
446                 for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
447                         region_check_fences(reg);
448                 }
449         }
450         ast_mutex_unlock(&reglock);
451 }
452
453 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
454 {
455         struct ast_region *reg;
456
457         if (!ptr) {
458                 return;
459         }
460
461         reg = region_remove(ptr);
462         if (reg) {
463                 region_check_fences(reg);
464
465                 if (reg->len <= MINNOWS_MAX_SIZE) {
466                         region_free(&minnows, reg);
467                 } else {
468                         region_free(&whales, reg);
469                 }
470         } else {
471                 /*
472                  * This memory region is not registered.  It could be because of
473                  * a double free or the memory block was not allocated by the
474                  * malloc debug code.
475                  */
476                 astmm_log("WARNING: Freeing unregistered memory %p by %s %s() line %d\n",
477                         ptr, file, func, lineno);
478                 my_do_crash();
479         }
480 }
481
482 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
483 {
484         void *ptr;
485
486         ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0);
487         if (ptr) {
488                 memset(ptr, 0, size * nmemb);
489         }
490
491         return ptr;
492 }
493
494 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
495 {
496         void *ptr;
497
498         ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1);
499         if (ptr) {
500                 memset(ptr, 0, size * nmemb);
501         }
502
503         return ptr;
504 }
505
506 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
507 {
508         void *ptr;
509
510         ptr = __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
511         if (ptr) {
512                 /* Make sure that the malloced memory is not zero. */
513                 memset(ptr, MALLOC_FILLER, size);
514         }
515
516         return ptr;
517 }
518
519 void __ast_free(void *ptr, const char *file, int lineno, const char *func)
520 {
521         __ast_free_region(ptr, file, lineno, func);
522 }
523
524 /*!
525  * \note reglock must be locked before calling.
526  */
527 static struct ast_region *region_find(void *ptr)
528 {
529         int hash;
530         struct ast_region *reg;
531
532         hash = HASH(ptr);
533         for (reg = regions[hash]; reg; reg = AST_LIST_NEXT(reg, node)) {
534                 if (reg->data == ptr) {
535                         break;
536                 }
537         }
538
539         return reg;
540 }
541
542 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
543 {
544         size_t len;
545         struct ast_region *found;
546         void *new_mem;
547
548         if (ptr) {
549                 ast_mutex_lock(&reglock);
550                 found = region_find(ptr);
551                 if (!found) {
552                         ast_mutex_unlock(&reglock);
553                         astmm_log("WARNING: Realloc of unregistered memory %p by %s %s() line %d\n",
554                                 ptr, file, func, lineno);
555                         my_do_crash();
556                         return NULL;
557                 }
558                 len = found->len;
559                 ast_mutex_unlock(&reglock);
560         } else {
561                 found = NULL;
562                 len = 0;
563         }
564
565         if (!size) {
566                 __ast_free_region(ptr, file, lineno, func);
567                 return NULL;
568         }
569
570         new_mem = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0);
571         if (new_mem) {
572                 if (found) {
573                         /* Copy the old data to the new malloced memory. */
574                         if (size <= len) {
575                                 memcpy(new_mem, ptr, size);
576                         } else {
577                                 memcpy(new_mem, ptr, len);
578                                 /* Make sure that the added memory is not zero. */
579                                 memset(new_mem + len, MALLOC_FILLER, size - len);
580                         }
581                         __ast_free_region(ptr, file, lineno, func);
582                 } else {
583                         /* Make sure that the malloced memory is not zero. */
584                         memset(new_mem, MALLOC_FILLER, size);
585                 }
586         }
587
588         return new_mem;
589 }
590
591 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
592 {
593         size_t len;
594         void *ptr;
595
596         if (!s)
597                 return NULL;
598
599         len = strlen(s) + 1;
600         if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
601                 strcpy(ptr, s);
602
603         return ptr;
604 }
605
606 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
607 {
608         size_t len;
609         char *ptr;
610
611         if (!s) {
612                 return NULL;
613         }
614
615         len = strnlen(s, n);
616         if ((ptr = __ast_alloc_region(len + 1, FUNC_STRNDUP, file, lineno, func, 0))) {
617                 memcpy(ptr, s, len);
618                 ptr[len] = '\0';
619         }
620
621         return ptr;
622 }
623
624 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
625 {
626         int size;
627         va_list ap, ap2;
628         char s;
629
630         *strp = NULL;
631         va_start(ap, fmt);
632         va_copy(ap2, ap);
633         size = vsnprintf(&s, 1, fmt, ap2);
634         va_end(ap2);
635         if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
636                 va_end(ap);
637                 return -1;
638         }
639         vsnprintf(*strp, size + 1, fmt, ap);
640         va_end(ap);
641
642         return size;
643 }
644
645 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
646 {
647         int size;
648         va_list ap2;
649         char s;
650
651         *strp = NULL;
652         va_copy(ap2, ap);
653         size = vsnprintf(&s, 1, fmt, ap2);
654         va_end(ap2);
655         if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
656                 va_end(ap);
657                 return -1;
658         }
659         vsnprintf(*strp, size + 1, fmt, ap);
660
661         return size;
662 }
663
664 /*!
665  * \internal
666  * \brief Count the number of bytes in the specified freed region.
667  *
668  * \param freed Already freed region blocks storage.
669  *
670  * \note reglock must be locked before calling.
671  *
672  * \return Number of bytes in freed region.
673  */
674 static size_t freed_regions_size(struct ast_freed_regions *freed)
675 {
676         size_t total_len = 0;
677         int idx;
678         struct ast_region *old;
679
680         for (idx = 0; idx < ARRAY_LEN(freed->regions); ++idx) {
681                 old = freed->regions[idx];
682                 if (old) {
683                         total_len += old->len;
684                 }
685         }
686
687         return total_len;
688 }
689
690 static char *handle_memory_atexit_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
691 {
692         switch (cmd) {
693         case CLI_INIT:
694                 e->command = "memory atexit list {on|off}";
695                 e->usage =
696                         "Usage: memory atexit list {on|off}\n"
697                         "       Enable dumping a list of still allocated memory segments at exit.\n";
698                 return NULL;
699         case CLI_GENERATE:
700                 return NULL;
701         }
702
703         if (a->argc != 4) {
704                 return CLI_SHOWUSAGE;
705         }
706
707         if (ast_true(a->argv[3])) {
708                 atexit_list = 1;
709         } else if (ast_false(a->argv[3])) {
710                 atexit_list = 0;
711         } else {
712                 return CLI_SHOWUSAGE;
713         }
714
715         ast_cli(a->fd, "The atexit list is: %s\n", atexit_list ? "On" : "Off");
716
717         return CLI_SUCCESS;
718 }
719
720 static char *handle_memory_atexit_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
721 {
722         char buf[80];
723
724         switch (cmd) {
725         case CLI_INIT:
726                 e->command = "memory atexit summary {off|byline|byfunc|byfile}";
727                 e->usage =
728                         "Usage: memory atexit summary {off|byline|byfunc|byfile}\n"
729                         "       Summary of still allocated memory segments at exit options.\n"
730                         "       off - Disable at exit summary.\n"
731                         "       byline - Enable at exit summary by file line number.\n"
732                         "       byfunc - Enable at exit summary by function name.\n"
733                         "       byfile - Enable at exit summary by file.\n"
734                         "\n"
735                         "       Note: byline, byfunc, and byfile are cumulative enables.\n";
736                 return NULL;
737         case CLI_GENERATE:
738                 return NULL;
739         }
740
741         if (a->argc != 4) {
742                 return CLI_SHOWUSAGE;
743         }
744
745         if (ast_false(a->argv[3])) {
746                 atexit_summary = SUMMARY_OFF;
747         } else if (!strcasecmp(a->argv[3], "byline")) {
748                 atexit_summary |= SUMMARY_BY_LINE;
749         } else if (!strcasecmp(a->argv[3], "byfunc")) {
750                 atexit_summary |= SUMMARY_BY_FUNC;
751         } else if (!strcasecmp(a->argv[3], "byfile")) {
752                 atexit_summary |= SUMMARY_BY_FILE;
753         } else {
754                 return CLI_SHOWUSAGE;
755         }
756
757         if (atexit_summary) {
758                 buf[0] = '\0';
759                 if (atexit_summary & SUMMARY_BY_LINE) {
760                         strcat(buf, "byline");
761                 }
762                 if (atexit_summary & SUMMARY_BY_FUNC) {
763                         if (buf[0]) {
764                                 strcat(buf, " | ");
765                         }
766                         strcat(buf, "byfunc");
767                 }
768                 if (atexit_summary & SUMMARY_BY_FILE) {
769                         if (buf[0]) {
770                                 strcat(buf, " | ");
771                         }
772                         strcat(buf, "byfile");
773                 }
774         } else {
775                 strcpy(buf, "Off");
776         }
777         ast_cli(a->fd, "The atexit summary is: %s\n", buf);
778
779         return CLI_SUCCESS;
780 }
781
782 /*!
783  * \internal
784  * \brief Common summary output at the end of the memory show commands.
785  *
786  * \param fd CLI output file descriptor.
787  * \param whales_len Accumulated size of free large allocations.
788  * \param minnows_len Accumulated size of free small allocations.
789  * \param total_len Accumulated size of all current allocations.
790  * \param selected_len Accumulated size of the selected allocations.
791  * \param cache_len Accumulated size of the allocations that are part of a cache.
792  * \param count Number of selected allocations.
793  *
794  * \return Nothing
795  */
796 static void print_memory_show_common_stats(int fd,
797         unsigned int whales_len,
798         unsigned int minnows_len,
799         unsigned int total_len,
800         unsigned int selected_len,
801         unsigned int cache_len,
802         unsigned int count)
803 {
804         if (cache_len) {
805                 ast_cli(fd, "%10u bytes allocated (%u in caches) in %u selected allocations\n\n",
806                         selected_len, cache_len, count);
807         } else {
808                 ast_cli(fd, "%10u bytes allocated in %u selected allocations\n\n",
809                         selected_len, count);
810         }
811
812         ast_cli(fd, "%10u bytes in all allocations\n", total_len);
813         ast_cli(fd, "%10u bytes in deferred free large allocations\n", whales_len);
814         ast_cli(fd, "%10u bytes in deferred free small allocations\n", minnows_len);
815         ast_cli(fd, "%10u bytes in deferred free allocations\n",
816                 whales_len + minnows_len);
817         ast_cli(fd, "%10u bytes in all allocations and deferred free allocations\n",
818                 total_len + whales_len + minnows_len);
819 }
820
821 static char *handle_memory_show_allocations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
822 {
823         const char *fn = NULL;
824         struct ast_region *reg;
825         unsigned int idx;
826         unsigned int whales_len;
827         unsigned int minnows_len;
828         unsigned int total_len = 0;
829         unsigned int selected_len = 0;
830         unsigned int cache_len = 0;
831         unsigned int count = 0;
832
833         switch (cmd) {
834         case CLI_INIT:
835                 e->command = "memory show allocations";
836                 e->usage =
837                         "Usage: memory show allocations [<file>|anomalies]\n"
838                         "       Dumps a list of segments of allocated memory.\n"
839                         "       Defaults to listing all memory allocations.\n"
840                         "       <file> - Restricts output to memory allocated by the file.\n"
841                         "       anomalies - Only check for fence violations.\n";
842                 return NULL;
843         case CLI_GENERATE:
844                 return NULL;
845         }
846
847         if (a->argc == 4) {
848                 fn = a->argv[3];
849         } else if (a->argc != 3) {
850                 return CLI_SHOWUSAGE;
851         }
852
853         /* Look for historical misspelled option as well. */
854         if (fn && (!strcasecmp(fn, "anomalies") || !strcasecmp(fn, "anomolies"))) {
855                 regions_check_all_fences();
856                 ast_cli(a->fd, "Anomaly check complete.\n");
857                 return CLI_SUCCESS;
858         }
859
860         ast_mutex_lock(&reglock);
861         for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
862                 for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
863                         total_len += reg->len;
864                         if (fn && strcasecmp(fn, reg->file)) {
865                                 continue;
866                         }
867
868                         region_check_fences(reg);
869
870                         ast_cli(a->fd, "%10u bytes allocated%s by %20s() line %5u of %s\n",
871                                 (unsigned int) reg->len, reg->cache ? " (cache)" : "",
872                                 reg->func, reg->lineno, reg->file);
873
874                         selected_len += reg->len;
875                         if (reg->cache) {
876                                 cache_len += reg->len;
877                         }
878                         ++count;
879                 }
880         }
881
882         whales_len = freed_regions_size(&whales);
883         minnows_len = freed_regions_size(&minnows);
884         ast_mutex_unlock(&reglock);
885
886         print_memory_show_common_stats(a->fd,
887                 whales_len, minnows_len, total_len,
888                 selected_len, cache_len, count);
889
890         return CLI_SUCCESS;
891 }
892
893 static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
894 {
895 #define my_max(a, b) ((a) >= (b) ? (a) : (b))
896
897         const char *fn = NULL;
898         int idx;
899         int cmp;
900         struct ast_region *reg;
901         unsigned int whales_len;
902         unsigned int minnows_len;
903         unsigned int total_len = 0;
904         unsigned int selected_len = 0;
905         unsigned int cache_len = 0;
906         unsigned int count = 0;
907         struct file_summary {
908                 struct file_summary *next;
909                 unsigned int len;
910                 unsigned int cache_len;
911                 unsigned int count;
912                 unsigned int lineno;
913                 char name[my_max(sizeof(reg->file), sizeof(reg->func))];
914         } *list = NULL, *cur, **prev;
915
916         switch (cmd) {
917         case CLI_INIT:
918                 e->command = "memory show summary";
919                 e->usage =
920                         "Usage: memory show summary [<file>]\n"
921                         "       Summarizes heap memory allocations by file, or optionally\n"
922                         "       by line if a file is specified.\n";
923                 return NULL;
924         case CLI_GENERATE:
925                 return NULL;
926         }
927
928         if (a->argc == 4) {
929                 fn = a->argv[3];
930         } else if (a->argc != 3) {
931                 return CLI_SHOWUSAGE;
932         }
933
934         ast_mutex_lock(&reglock);
935         for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
936                 for (reg = regions[idx]; reg; reg = AST_LIST_NEXT(reg, node)) {
937                         total_len += reg->len;
938                         if (fn) {
939                                 if (strcasecmp(fn, reg->file)) {
940                                         continue;
941                                 }
942
943                                 /* Sort list by func/lineno.  Find existing or place to insert. */
944                                 for (prev = &list; (cur = *prev); prev = &cur->next) {
945                                         cmp = strcmp(cur->name, reg->func);
946                                         if (cmp < 0) {
947                                                 continue;
948                                         }
949                                         if (cmp > 0) {
950                                                 /* Insert before current */
951                                                 cur = NULL;
952                                                 break;
953                                         }
954                                         cmp = cur->lineno - reg->lineno;
955                                         if (cmp < 0) {
956                                                 continue;
957                                         }
958                                         if (cmp > 0) {
959                                                 /* Insert before current */
960                                                 cur = NULL;
961                                         }
962                                         break;
963                                 }
964                         } else {
965                                 /* Sort list by filename.  Find existing or place to insert. */
966                                 for (prev = &list; (cur = *prev); prev = &cur->next) {
967                                         cmp = strcmp(cur->name, reg->file);
968                                         if (cmp < 0) {
969                                                 continue;
970                                         }
971                                         if (cmp > 0) {
972                                                 /* Insert before current */
973                                                 cur = NULL;
974                                         }
975                                         break;
976                                 }
977                         }
978
979                         if (!cur) {
980                                 cur = ast_alloca(sizeof(*cur));
981                                 memset(cur, 0, sizeof(*cur));
982                                 cur->lineno = reg->lineno;
983                                 ast_copy_string(cur->name, fn ? reg->func : reg->file, sizeof(cur->name));
984
985                                 cur->next = *prev;
986                                 *prev = cur;
987                         }
988
989                         cur->len += reg->len;
990                         if (reg->cache) {
991                                 cur->cache_len += reg->len;
992                         }
993                         ++cur->count;
994                 }
995         }
996
997         whales_len = freed_regions_size(&whales);
998         minnows_len = freed_regions_size(&minnows);
999         ast_mutex_unlock(&reglock);
1000
1001         /* Dump the whole list */
1002         for (cur = list; cur; cur = cur->next) {
1003                 selected_len += cur->len;
1004                 cache_len += cur->cache_len;
1005                 count += cur->count;
1006                 if (cur->cache_len) {
1007                         if (fn) {
1008                                 ast_cli(a->fd, "%10u bytes (%10u cache) in %10u allocations by %20s() line %5u of %s\n",
1009                                         cur->len, cur->cache_len, cur->count, cur->name, cur->lineno, fn);
1010                         } else {
1011                                 ast_cli(a->fd, "%10u bytes (%10u cache) in %10u allocations in file %s\n",
1012                                         cur->len, cur->cache_len, cur->count, cur->name);
1013                         }
1014                 } else {
1015                         if (fn) {
1016                                 ast_cli(a->fd, "%10u bytes in %10u allocations by %20s() line %5u of %s\n",
1017                                         cur->len, cur->count, cur->name, cur->lineno, fn);
1018                         } else {
1019                                 ast_cli(a->fd, "%10u bytes in %10u allocations in file %s\n",
1020                                         cur->len, cur->count, cur->name);
1021                         }
1022                 }
1023         }
1024
1025         print_memory_show_common_stats(a->fd,
1026                 whales_len, minnows_len, total_len,
1027                 selected_len, cache_len, count);
1028
1029         return CLI_SUCCESS;
1030 }
1031
1032 static char *handle_memory_backtrace(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1033 {
1034         switch (cmd) {
1035         case CLI_INIT:
1036                 e->command = "memory backtrace {on|off}";
1037                 e->usage =
1038                         "Usage: memory backtrace {on|off}\n"
1039                         "       Enable dumping an allocation backtrace with memory diagnostics.\n"
1040                         "       Note that saving the backtrace data for each allocation\n"
1041                         "       can be CPU intensive.\n";
1042                 return NULL;
1043         case CLI_GENERATE:
1044                 return NULL;
1045         }
1046
1047         if (a->argc != 3) {
1048                 return CLI_SHOWUSAGE;
1049         }
1050
1051         if (ast_true(a->argv[2])) {
1052                 backtrace_enabled = 1;
1053         } else if (ast_false(a->argv[2])) {
1054                 backtrace_enabled = 0;
1055         } else {
1056                 return CLI_SHOWUSAGE;
1057         }
1058
1059         ast_cli(a->fd, "The memory backtrace is: %s\n", backtrace_enabled ? "On" : "Off");
1060
1061         return CLI_SUCCESS;
1062 }
1063
1064 static struct ast_cli_entry cli_memory[] = {
1065         AST_CLI_DEFINE(handle_memory_atexit_list, "Enable memory allocations not freed at exit list."),
1066         AST_CLI_DEFINE(handle_memory_atexit_summary, "Enable memory allocations not freed at exit summary."),
1067         AST_CLI_DEFINE(handle_memory_show_allocations, "Display outstanding memory allocations"),
1068         AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
1069         AST_CLI_DEFINE(handle_memory_backtrace, "Enable dumping an allocation backtrace with memory diagnostics."),
1070 };
1071
1072 AST_LIST_HEAD_NOLOCK(region_list, ast_region);
1073
1074 /*!
1075  * \internal
1076  * \brief Convert the allocated regions hash table to a list.
1077  *
1078  * \param list Fill list with the allocated regions.
1079  *
1080  * \details
1081  * Take all allocated regions from the regions[] and put them
1082  * into the list.
1083  *
1084  * \note reglock must be locked before calling.
1085  *
1086  * \note This function is destructive to the regions[] lists.
1087  *
1088  * \return Length of list created.
1089  */
1090 static size_t mm_atexit_hash_list(struct region_list *list)
1091 {
1092         struct ast_region *reg;
1093         size_t total_length;
1094         int idx;
1095
1096         total_length = 0;
1097         for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
1098                 while ((reg = regions[idx])) {
1099                         regions[idx] = AST_LIST_NEXT(reg, node);
1100                         AST_LIST_NEXT(reg, node) = NULL;
1101                         AST_LIST_INSERT_HEAD(list, reg, node);
1102                         ++total_length;
1103                 }
1104         }
1105         return total_length;
1106 }
1107
1108 /*!
1109  * \internal
1110  * \brief Put the regions list into the allocated regions hash table.
1111  *
1112  * \param list List to put into the allocated regions hash table.
1113  *
1114  * \note reglock must be locked before calling.
1115  *
1116  * \return Nothing
1117  */
1118 static void mm_atexit_hash_restore(struct region_list *list)
1119 {
1120         struct ast_region *reg;
1121         int hash;
1122
1123         while ((reg = AST_LIST_REMOVE_HEAD(list, node))) {
1124                 hash = HASH(reg->data);
1125                 AST_LIST_NEXT(reg, node) = regions[hash];
1126                 regions[hash] = reg;
1127         }
1128 }
1129
1130 /*!
1131  * \internal
1132  * \brief Sort regions comparision.
1133  *
1134  * \param left Region to compare.
1135  * \param right Region to compare.
1136  *
1137  * \retval <0 if left < right
1138  * \retval =0 if left == right
1139  * \retval >0 if left > right
1140  */
1141 static int mm_atexit_cmp(struct ast_region *left, struct ast_region *right)
1142 {
1143         int cmp;
1144         ptrdiff_t cmp_ptr;
1145         ssize_t cmp_size;
1146
1147         /* Sort by filename. */
1148         cmp = strcmp(left->file, right->file);
1149         if (cmp) {
1150                 return cmp;
1151         }
1152
1153         /* Sort by line number. */
1154         cmp = left->lineno - right->lineno;
1155         if (cmp) {
1156                 return cmp;
1157         }
1158
1159         /* Sort by allocated size. */
1160         cmp_size = left->len - right->len;
1161         if (cmp_size) {
1162                 if (cmp_size < 0) {
1163                         return -1;
1164                 }
1165                 return 1;
1166         }
1167
1168         /* Sort by allocated pointers just because. */
1169         cmp_ptr = left->data - right->data;
1170         if (cmp_ptr) {
1171                 if (cmp_ptr < 0) {
1172                         return -1;
1173                 }
1174                 return 1;
1175         }
1176
1177         return 0;
1178 }
1179
1180 /*!
1181  * \internal
1182  * \brief Merge the given sorted sublists into sorted order onto the end of the list.
1183  *
1184  * \param list Merge sublists onto this list.
1185  * \param sub1 First sublist to merge.
1186  * \param sub2 Second sublist to merge.
1187  *
1188  * \return Nothing
1189  */
1190 static void mm_atexit_list_merge(struct region_list *list, struct region_list *sub1, struct region_list *sub2)
1191 {
1192         struct ast_region *reg;
1193
1194         for (;;) {
1195                 if (AST_LIST_EMPTY(sub1)) {
1196                         /* The remaining sublist goes onto the list. */
1197                         AST_LIST_APPEND_LIST(list, sub2, node);
1198                         break;
1199                 }
1200                 if (AST_LIST_EMPTY(sub2)) {
1201                         /* The remaining sublist goes onto the list. */
1202                         AST_LIST_APPEND_LIST(list, sub1, node);
1203                         break;
1204                 }
1205
1206                 if (mm_atexit_cmp(AST_LIST_FIRST(sub1), AST_LIST_FIRST(sub2)) <= 0) {
1207                         reg = AST_LIST_REMOVE_HEAD(sub1, node);
1208                 } else {
1209                         reg = AST_LIST_REMOVE_HEAD(sub2, node);
1210                 }
1211                 AST_LIST_INSERT_TAIL(list, reg, node);
1212         }
1213 }
1214
1215 /*!
1216  * \internal
1217  * \brief Take sublists off of the given list.
1218  *
1219  * \param list Source list to remove sublists from the beginning of list.
1220  * \param sub Array of sublists to fill. (Lists are empty on entry.)
1221  * \param num_lists Number of lists to remove from the source list.
1222  * \param size Size of the sublists to remove.
1223  * \param remaining Remaining number of elements on the source list.
1224  *
1225  * \return Nothing
1226  */
1227 static void mm_atexit_list_split(struct region_list *list, struct region_list sub[], size_t num_lists, size_t size, size_t *remaining)
1228 {
1229         int idx;
1230
1231         for (idx = 0; idx < num_lists; ++idx) {
1232                 size_t count;
1233
1234                 if (*remaining < size) {
1235                         /* The remaining source list goes onto the sublist. */
1236                         AST_LIST_APPEND_LIST(&sub[idx], list, node);
1237                         *remaining = 0;
1238                         break;
1239                 }
1240
1241                 /* Take a sublist off the beginning of the source list. */
1242                 *remaining -= size;
1243                 for (count = size; count--;) {
1244                         struct ast_region *reg;
1245
1246                         reg = AST_LIST_REMOVE_HEAD(list, node);
1247                         AST_LIST_INSERT_TAIL(&sub[idx], reg, node);
1248                 }
1249         }
1250 }
1251
1252 /*!
1253  * \internal
1254  * \brief Sort the regions list using mergesort.
1255  *
1256  * \param list Allocated regions list to sort.
1257  * \param length Length of the list.
1258  *
1259  * \return Nothing
1260  */
1261 static void mm_atexit_list_sort(struct region_list *list, size_t length)
1262 {
1263         /*! Semi-sorted merged list. */
1264         struct region_list merged = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
1265         /*! Sublists to merge. (Can only merge two sublists at this time.) */
1266         struct region_list sub[2] = {
1267                 AST_LIST_HEAD_NOLOCK_INIT_VALUE,
1268                 AST_LIST_HEAD_NOLOCK_INIT_VALUE
1269         };
1270         /*! Sublist size. */
1271         size_t size = 1;
1272         /*! Remaining elements in the list. */
1273         size_t remaining;
1274         /*! Number of sublist merge passes to process the list. */
1275         int passes;
1276
1277         for (;;) {
1278                 remaining = length;
1279
1280                 passes = 0;
1281                 while (!AST_LIST_EMPTY(list)) {
1282                         mm_atexit_list_split(list, sub, ARRAY_LEN(sub), size, &remaining);
1283                         mm_atexit_list_merge(&merged, &sub[0], &sub[1]);
1284                         ++passes;
1285                 }
1286                 AST_LIST_APPEND_LIST(list, &merged, node);
1287                 if (passes <= 1) {
1288                         /* The list is now sorted. */
1289                         break;
1290                 }
1291
1292                 /* Double the sublist size to remove for next round. */
1293                 size <<= 1;
1294         }
1295 }
1296
1297 /*!
1298  * \internal
1299  * \brief List all regions currently allocated.
1300  *
1301  * \param alloced regions list.
1302  *
1303  * \return Nothing
1304  */
1305 static void mm_atexit_regions_list(struct region_list *alloced)
1306 {
1307         struct ast_region *reg;
1308
1309         AST_LIST_TRAVERSE(alloced, reg, node) {
1310                 astmm_log("%s %s() line %u: %u bytes%s at %p\n",
1311                         reg->file, reg->func, reg->lineno,
1312                         (unsigned int) reg->len, reg->cache ? " (cache)" : "", reg->data);
1313         }
1314 }
1315
1316 /*!
1317  * \internal
1318  * \brief Summarize all regions currently allocated.
1319  *
1320  * \param alloced Sorted regions list.
1321  *
1322  * \return Nothing
1323  */
1324 static void mm_atexit_regions_summary(struct region_list *alloced)
1325 {
1326         struct ast_region *reg;
1327         struct ast_region *next;
1328         struct {
1329                 unsigned int count;
1330                 unsigned int len;
1331                 unsigned int cache_len;
1332         } by_line, by_func, by_file, total;
1333
1334         by_line.count = 0;
1335         by_line.len = 0;
1336         by_line.cache_len = 0;
1337
1338         by_func.count = 0;
1339         by_func.len = 0;
1340         by_func.cache_len = 0;
1341
1342         by_file.count = 0;
1343         by_file.len = 0;
1344         by_file.cache_len = 0;
1345
1346         total.count = 0;
1347         total.len = 0;
1348         total.cache_len = 0;
1349
1350         AST_LIST_TRAVERSE(alloced, reg, node) {
1351                 next = AST_LIST_NEXT(reg, node);
1352
1353                 ++by_line.count;
1354                 by_line.len += reg->len;
1355                 if (reg->cache) {
1356                         by_line.cache_len += reg->len;
1357                 }
1358                 if (next && !strcmp(reg->file, next->file) && reg->lineno == next->lineno) {
1359                         continue;
1360                 }
1361                 if (atexit_summary & SUMMARY_BY_LINE) {
1362                         if (by_line.cache_len) {
1363                                 astmm_log("%10u bytes (%u in caches) in %u allocations. %s %s() line %u\n",
1364                                         by_line.len, by_line.cache_len, by_line.count, reg->file, reg->func, reg->lineno);
1365                         } else {
1366                                 astmm_log("%10u bytes in %5u allocations. %s %s() line %u\n",
1367                                         by_line.len, by_line.count, reg->file, reg->func, reg->lineno);
1368                         }
1369                 }
1370
1371                 by_func.count += by_line.count;
1372                 by_func.len += by_line.len;
1373                 by_func.cache_len += by_line.cache_len;
1374                 by_line.count = 0;
1375                 by_line.len = 0;
1376                 by_line.cache_len = 0;
1377                 if (next && !strcmp(reg->file, next->file) && !strcmp(reg->func, next->func)) {
1378                         continue;
1379                 }
1380                 if (atexit_summary & SUMMARY_BY_FUNC) {
1381                         if (by_func.cache_len) {
1382                                 astmm_log("%10u bytes (%u in caches) in %u allocations. %s %s()\n",
1383                                         by_func.len, by_func.cache_len, by_func.count, reg->file, reg->func);
1384                         } else {
1385                                 astmm_log("%10u bytes in %5u allocations. %s %s()\n",
1386                                         by_func.len, by_func.count, reg->file, reg->func);
1387                         }
1388                 }
1389
1390                 by_file.count += by_func.count;
1391                 by_file.len += by_func.len;
1392                 by_file.cache_len += by_func.cache_len;
1393                 by_func.count = 0;
1394                 by_func.len = 0;
1395                 by_func.cache_len = 0;
1396                 if (next && !strcmp(reg->file, next->file)) {
1397                         continue;
1398                 }
1399                 if (atexit_summary & SUMMARY_BY_FILE) {
1400                         if (by_file.cache_len) {
1401                                 astmm_log("%10u bytes (%u in caches) in %u allocations. %s\n",
1402                                         by_file.len, by_file.cache_len, by_file.count, reg->file);
1403                         } else {
1404                                 astmm_log("%10u bytes in %5u allocations. %s\n",
1405                                         by_file.len, by_file.count, reg->file);
1406                         }
1407                 }
1408
1409                 total.count += by_file.count;
1410                 total.len += by_file.len;
1411                 total.cache_len += by_file.cache_len;
1412                 by_file.count = 0;
1413                 by_file.len = 0;
1414                 by_file.cache_len = 0;
1415         }
1416
1417         if (total.cache_len) {
1418                 astmm_log("%u bytes (%u in caches) in %u allocations.\n",
1419                         total.len, total.cache_len, total.count);
1420         } else {
1421                 astmm_log("%u bytes in %u allocations.\n", total.len, total.count);
1422         }
1423 }
1424
1425 /*!
1426  * \internal
1427  * \brief Dump the memory allocations atexit.
1428  *
1429  * \note reglock must be locked before calling.
1430  *
1431  * \return Nothing
1432  */
1433 static void mm_atexit_dump(void)
1434 {
1435         struct region_list alloced_atexit = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
1436         size_t length;
1437
1438         length = mm_atexit_hash_list(&alloced_atexit);
1439         if (!length) {
1440                 /* Wow!  This is amazing! */
1441                 astmm_log("Exiting with all memory freed.\n");
1442                 return;
1443         }
1444
1445         mm_atexit_list_sort(&alloced_atexit, length);
1446
1447         astmm_log("Exiting with the following memory not freed:\n");
1448         if (atexit_list) {
1449                 mm_atexit_regions_list(&alloced_atexit);
1450         }
1451         if (atexit_summary) {
1452                 mm_atexit_regions_summary(&alloced_atexit);
1453         }
1454
1455         /*
1456          * Put the alloced list back into regions[].
1457          *
1458          * We have do this because we can get called before all other
1459          * threads have terminated.
1460          */
1461         mm_atexit_hash_restore(&alloced_atexit);
1462 }
1463
1464 /*!
1465  * \internal
1466  * \return Nothing
1467  */
1468 static void mm_atexit_final(void)
1469 {
1470         FILE *log;
1471
1472         /* Only wait if we want atexit allocation dumps. */
1473         if (atexit_list || atexit_summary) {
1474                 fprintf(stderr, "Waiting 10 seconds to let other threads die.\n");
1475                 sleep(10);
1476         }
1477
1478         regions_check_all_fences();
1479
1480         /* Flush all delayed memory free circular arrays. */
1481         freed_regions_flush(&whales);
1482         freed_regions_flush(&minnows);
1483
1484         /* Peform atexit allocation dumps. */
1485         if (atexit_list || atexit_summary) {
1486                 ast_mutex_lock(&reglock);
1487                 mm_atexit_dump();
1488                 ast_mutex_unlock(&reglock);
1489         }
1490
1491         /* Close the log file. */
1492         log = mmlog;
1493         mmlog = NULL;
1494         if (log) {
1495                 fclose(log);
1496         }
1497 }
1498
1499 /*!
1500  * \brief Initialize malloc debug phase 1.
1501  *
1502  * \note Must be called first thing in main().
1503  *
1504  * \return Nothing
1505  */
1506 void __ast_mm_init_phase_1(void)
1507 {
1508         atexit(mm_atexit_final);
1509 }
1510
1511 /*!
1512  * \internal
1513  * \return Nothing
1514  */
1515 static void mm_atexit_ast(void)
1516 {
1517         ast_cli_unregister_multiple(cli_memory, ARRAY_LEN(cli_memory));
1518 }
1519
1520 /*!
1521  * \brief Initialize malloc debug phase 2.
1522  *
1523  * \return Nothing
1524  */
1525 void __ast_mm_init_phase_2(void)
1526 {
1527         char filename[PATH_MAX];
1528
1529         ast_cli_register_multiple(cli_memory, ARRAY_LEN(cli_memory));
1530
1531         snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
1532
1533         ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
1534
1535         mmlog = fopen(filename, "a+");
1536         if (mmlog) {
1537                 fprintf(mmlog, "%ld - New session\n", (long) time(NULL));
1538                 fflush(mmlog);
1539         } else {
1540                 ast_log(LOG_ERROR, "Could not open malloc debug log file: %s\n", filename);
1541         }
1542
1543         ast_register_cleanup(mm_atexit_ast);
1544 }
1545
1546 #endif  /* defined(__AST_DEBUG_MALLOC) */