d26ba945c933247cb7f5ead7b246b8f03789b3c1
[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 #include "asterisk.h"
32
33 #if defined(__AST_DEBUG_MALLOC)
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include "asterisk/paths.h"     /* use ast_config_AST_LOG_DIR */
38 #include <stddef.h>
39 #include <time.h>
40
41 #include "asterisk/cli.h"
42 #include "asterisk/lock.h"
43 #include "asterisk/strings.h"
44 #include "asterisk/unaligned.h"
45
46 #define SOME_PRIME 563
47
48 enum func_type {
49         FUNC_CALLOC = 1,
50         FUNC_MALLOC,
51         FUNC_REALLOC,
52         FUNC_STRDUP,
53         FUNC_STRNDUP,
54         FUNC_VASPRINTF,
55         FUNC_ASPRINTF
56 };
57
58 /* Undefine all our macros */
59 #undef malloc
60 #undef calloc
61 #undef realloc
62 #undef strdup
63 #undef strndup
64 #undef free
65 #undef vasprintf
66 #undef asprintf
67
68 #define FENCE_MAGIC             0xdeadbeef      /*!< Allocated memory high/low fence overwrite check. */
69 #define FREED_MAGIC             0xdeaddead      /*!< Freed memory wipe filler. */
70 #define MALLOC_FILLER   0x55            /*!< Malloced memory filler.  Must not be zero. */
71
72 static FILE *mmlog;
73
74 struct ast_region {
75         struct ast_region *next;
76         size_t len;
77         unsigned int cache;             /* region was allocated as part of a cache pool */
78         unsigned int lineno;
79         enum func_type which;
80         char file[64];
81         char func[40];
82
83         /*!
84          * \brief Lower guard fence.
85          *
86          * \note Must be right before data[].
87          *
88          * \note Padding between fence and data[] is irrelevent because
89          * data[] is used to fill in the lower fence check value and not
90          * the fence member.  The fence member is to ensure that there
91          * is space reserved for the fence check value.
92          */
93         unsigned int fence;
94         /*!
95          * \brief Location of the requested malloc block to return.
96          *
97          * \note Must have the same alignment that malloc returns.
98          * i.e., It is suitably aligned for any kind of varible.
99          */
100         unsigned char data[0] __attribute__((aligned));
101 };
102
103 /*! Hash table of lists of active allocated memory regions. */
104 static struct ast_region *regions[SOME_PRIME];
105
106 /*! Number of freed regions to keep around to delay actually freeing them. */
107 #define FREED_MAX_COUNT         1500
108
109 /*! Maximum size of a minnow block */
110 #define MINNOWS_MAX_SIZE        50
111
112 struct ast_freed_regions {
113         /*! Memory regions that have been freed. */
114         struct ast_region *regions[FREED_MAX_COUNT];
115         /*! Next index into freed regions[] to use. */
116         int index;
117 };
118
119 /*! Large memory blocks that have been freed. */
120 static struct ast_freed_regions whales;
121 /*! Small memory blocks that have been freed. */
122 static struct ast_freed_regions minnows;
123
124 #define HASH(a)         (((unsigned long)(a)) % ARRAY_LEN(regions))
125
126 /*! Tracking this mutex will cause infinite recursion, as the mutex tracking
127  *  code allocates memory */
128 AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
129
130 #define astmm_log(...)                               \
131         do {                                         \
132                 fprintf(stderr, __VA_ARGS__);        \
133                 if (mmlog) {                         \
134                         fprintf(mmlog, __VA_ARGS__); \
135                         fflush(mmlog);               \
136                 }                                    \
137         } while (0)
138
139 /*!
140  * \internal
141  *
142  * \note If DO_CRASH is not defined then the function returns.
143  *
144  * \return Nothing
145  */
146 static void my_do_crash(void)
147 {
148         /*
149          * Give the logger a chance to get the message out, just in case
150          * we abort(), or Asterisk crashes due to whatever problem just
151          * happened.
152          */
153         usleep(1);
154         ast_do_crash();
155 }
156
157 static void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
158 {
159         struct ast_region *reg;
160         unsigned int *fence;
161         int hash;
162
163         if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
164                 astmm_log("Memory Allocation Failure - '%d' bytes at %s %s() line %d\n",
165                         (int) size, file, func, lineno);
166                 return NULL;
167         }
168
169         reg->len = size;
170         reg->cache = cache;
171         reg->lineno = lineno;
172         reg->which = which;
173         ast_copy_string(reg->file, file, sizeof(reg->file));
174         ast_copy_string(reg->func, func, sizeof(reg->func));
175
176         /*
177          * Init lower fence.
178          *
179          * We use the bytes just preceeding reg->data and not reg->fence
180          * because there is likely to be padding between reg->fence and
181          * reg->data for reg->data alignment.
182          */
183         fence = (unsigned int *) (reg->data - sizeof(*fence));
184         *fence = FENCE_MAGIC;
185
186         /* Init higher fence. */
187         fence = (unsigned int *) (reg->data + reg->len);
188         put_unaligned_uint32(fence, FENCE_MAGIC);
189
190         hash = HASH(reg->data);
191         ast_mutex_lock(&reglock);
192         reg->next = regions[hash];
193         regions[hash] = reg;
194         ast_mutex_unlock(&reglock);
195
196         return reg->data;
197 }
198
199 /*!
200  * \internal
201  * \brief Wipe the region payload data with a known value.
202  *
203  * \param reg Region block to be wiped.
204  *
205  * \return Nothing
206  */
207 static void region_data_wipe(struct ast_region *reg)
208 {
209         void *end;
210         unsigned int *pos;
211
212         /*
213          * Wipe the lower fence, the payload, and whatever amount of the
214          * higher fence that falls into alignment with the payload.
215          */
216         end = reg->data + reg->len;
217         for (pos = &reg->fence; (void *) pos <= end; ++pos) {
218                 *pos = FREED_MAGIC;
219         }
220 }
221
222 /*!
223  * \internal
224  * \brief Check the region payload data for memory corruption.
225  *
226  * \param reg Region block to be checked.
227  *
228  * \return Nothing
229  */
230 static void region_data_check(struct ast_region *reg)
231 {
232         void *end;
233         unsigned int *pos;
234
235         /*
236          * Check the lower fence, the payload, and whatever amount of
237          * the higher fence that falls into alignment with the payload.
238          */
239         end = reg->data + reg->len;
240         for (pos = &reg->fence; (void *) pos <= end; ++pos) {
241                 if (*pos != FREED_MAGIC) {
242                         astmm_log("WARNING: Memory corrupted after free of %p allocated at %s %s() line %d\n",
243                                 reg->data, reg->file, reg->func, reg->lineno);
244                         my_do_crash();
245                         break;
246                 }
247         }
248 }
249
250 /*!
251  * \internal
252  * \brief Flush the circular array of freed regions.
253  *
254  * \param freed Already freed region blocks storage.
255  *
256  * \return Nothing
257  */
258 static void freed_regions_flush(struct ast_freed_regions *freed)
259 {
260         int idx;
261         struct ast_region *old;
262
263         ast_mutex_lock(&reglock);
264         for (idx = 0; idx < ARRAY_LEN(freed->regions); ++idx) {
265                 old = freed->regions[idx];
266                 freed->regions[idx] = NULL;
267                 if (old) {
268                         region_data_check(old);
269                         free(old);
270                 }
271         }
272         freed->index = 0;
273         ast_mutex_unlock(&reglock);
274 }
275
276 /*!
277  * \internal
278  * \brief Delay freeing a region block.
279  *
280  * \param freed Already freed region blocks storage.
281  * \param reg Region block to be freed.
282  *
283  * \return Nothing
284  */
285 static void region_free(struct ast_freed_regions *freed, struct ast_region *reg)
286 {
287         struct ast_region *old;
288
289         region_data_wipe(reg);
290
291         ast_mutex_lock(&reglock);
292         old = freed->regions[freed->index];
293         freed->regions[freed->index] = reg;
294
295         ++freed->index;
296         if (ARRAY_LEN(freed->regions) <= freed->index) {
297                 freed->index = 0;
298         }
299         ast_mutex_unlock(&reglock);
300
301         if (old) {
302                 region_data_check(old);
303                 free(old);
304         }
305 }
306
307 /*!
308  * \internal
309  * \brief Remove a region from the active regions.
310  *
311  * \param ptr Region payload data pointer.
312  *
313  * \retval region on success.
314  * \retval NULL if not found.
315  */
316 static struct ast_region *region_remove(void *ptr)
317 {
318         int hash;
319         struct ast_region *reg;
320         struct ast_region *prev = NULL;
321
322         hash = HASH(ptr);
323
324         ast_mutex_lock(&reglock);
325         for (reg = regions[hash]; reg; reg = reg->next) {
326                 if (reg->data == ptr) {
327                         if (prev) {
328                                 prev->next = reg->next;
329                         } else {
330                                 regions[hash] = reg->next;
331                         }
332                         break;
333                 }
334                 prev = reg;
335         }
336         ast_mutex_unlock(&reglock);
337
338         return reg;
339 }
340
341 /*!
342  * \internal
343  * \brief Check the fences of a region.
344  *
345  * \param reg Region block to check.
346  *
347  * \return Nothing
348  */
349 static void region_check_fences(struct ast_region *reg)
350 {
351         unsigned int *fence;
352
353         /*
354          * We use the bytes just preceeding reg->data and not reg->fence
355          * because there is likely to be padding between reg->fence and
356          * reg->data for reg->data alignment.
357          */
358         fence = (unsigned int *) (reg->data - sizeof(*fence));
359         if (*fence != FENCE_MAGIC) {
360                 astmm_log("WARNING: Low fence violation of %p allocated at %s %s() line %d\n",
361                         reg->data, reg->file, reg->func, reg->lineno);
362                 my_do_crash();
363         }
364         fence = (unsigned int *) (reg->data + reg->len);
365         if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
366                 astmm_log("WARNING: High fence violation of %p allocated at %s %s() line %d\n",
367                         reg->data, reg->file, reg->func, reg->lineno);
368                 my_do_crash();
369         }
370 }
371
372 static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
373 {
374         struct ast_region *reg;
375
376         if (!ptr) {
377                 return;
378         }
379
380         reg = region_remove(ptr);
381         if (reg) {
382                 region_check_fences(reg);
383
384                 if (reg->len <= MINNOWS_MAX_SIZE) {
385                         region_free(&minnows, reg);
386                 } else {
387                         region_free(&whales, reg);
388                 }
389         } else {
390                 /*
391                  * This memory region is not registered.  It could be because of
392                  * a double free or the memory block was not allocated by the
393                  * malloc debug code.
394                  */
395                 astmm_log("WARNING: Freeing unregistered memory %p by %s %s() line %d\n",
396                         ptr, file, func, lineno);
397                 my_do_crash();
398         }
399 }
400
401 void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
402 {
403         void *ptr;
404
405         ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0);
406         if (ptr) {
407                 memset(ptr, 0, size * nmemb);
408         }
409
410         return ptr;
411 }
412
413 void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
414 {
415         void *ptr;
416
417         ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1);
418         if (ptr) {
419                 memset(ptr, 0, size * nmemb);
420         }
421
422         return ptr;
423 }
424
425 void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
426 {
427         void *ptr;
428
429         ptr = __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
430         if (ptr) {
431                 /* Make sure that the malloced memory is not zero. */
432                 memset(ptr, MALLOC_FILLER, size);
433         }
434
435         return ptr;
436 }
437
438 void __ast_free(void *ptr, const char *file, int lineno, const char *func)
439 {
440         __ast_free_region(ptr, file, lineno, func);
441 }
442
443 /*!
444  * \note reglock must be locked before calling.
445  */
446 static struct ast_region *region_find(void *ptr)
447 {
448         int hash;
449         struct ast_region *reg;
450
451         hash = HASH(ptr);
452         for (reg = regions[hash]; reg; reg = reg->next) {
453                 if (reg->data == ptr) {
454                         break;
455                 }
456         }
457
458         return reg;
459 }
460
461 void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
462 {
463         size_t len;
464         struct ast_region *found;
465         void *new_mem;
466
467         if (ptr) {
468                 ast_mutex_lock(&reglock);
469                 found = region_find(ptr);
470                 if (!found) {
471                         ast_mutex_unlock(&reglock);
472                         astmm_log("WARNING: Realloc of unregistered memory %p by %s %s() line %d\n",
473                                 ptr, file, func, lineno);
474                         my_do_crash();
475                         return NULL;
476                 }
477                 len = found->len;
478                 ast_mutex_unlock(&reglock);
479         } else {
480                 found = NULL;
481                 len = 0;
482         }
483
484         if (!size) {
485                 __ast_free_region(ptr, file, lineno, func);
486                 return NULL;
487         }
488
489         new_mem = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0);
490         if (new_mem) {
491                 if (found) {
492                         /* Copy the old data to the new malloced memory. */
493                         if (size <= len) {
494                                 memcpy(new_mem, ptr, size);
495                         } else {
496                                 memcpy(new_mem, ptr, len);
497                                 /* Make sure that the added memory is not zero. */
498                                 memset(new_mem + len, MALLOC_FILLER, size - len);
499                         }
500                         __ast_free_region(ptr, file, lineno, func);
501                 } else {
502                         /* Make sure that the malloced memory is not zero. */
503                         memset(new_mem, MALLOC_FILLER, size);
504                 }
505         }
506
507         return new_mem;
508 }
509
510 char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
511 {
512         size_t len;
513         void *ptr;
514
515         if (!s)
516                 return NULL;
517
518         len = strlen(s) + 1;
519         if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
520                 strcpy(ptr, s);
521
522         return ptr;
523 }
524
525 char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
526 {
527         size_t len;
528         char *ptr;
529
530         if (!s) {
531                 return NULL;
532         }
533
534         len = strnlen(s, n);
535         if ((ptr = __ast_alloc_region(len + 1, FUNC_STRNDUP, file, lineno, func, 0))) {
536                 memcpy(ptr, s, len);
537                 ptr[len] = '\0';
538         }
539
540         return ptr;
541 }
542
543 int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
544 {
545         int size;
546         va_list ap, ap2;
547         char s;
548
549         *strp = NULL;
550         va_start(ap, fmt);
551         va_copy(ap2, ap);
552         size = vsnprintf(&s, 1, fmt, ap2);
553         va_end(ap2);
554         if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
555                 va_end(ap);
556                 return -1;
557         }
558         vsnprintf(*strp, size + 1, fmt, ap);
559         va_end(ap);
560
561         return size;
562 }
563
564 int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
565 {
566         int size;
567         va_list ap2;
568         char s;
569
570         *strp = NULL;
571         va_copy(ap2, ap);
572         size = vsnprintf(&s, 1, fmt, ap2);
573         va_end(ap2);
574         if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
575                 va_end(ap);
576                 return -1;
577         }
578         vsnprintf(*strp, size + 1, fmt, ap);
579
580         return size;
581 }
582
583 static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
584 {
585         const char *fn = NULL;
586         struct ast_region *reg;
587         unsigned int x;
588         unsigned int len = 0;
589         unsigned int cache_len = 0;
590         unsigned int count = 0;
591         int check_anomalies;
592
593         switch (cmd) {
594         case CLI_INIT:
595                 e->command = "memory show allocations";
596                 e->usage =
597                         "Usage: memory show allocations [<file>|anomalies]\n"
598                         "       Dumps a list of segments of allocated memory.\n"
599                         "       Defaults to listing all memory allocations.\n"
600                         "       <file> - Restricts output to memory allocated by the file.\n"
601                         "       anomalies - Only check for fence violations.\n";
602                 return NULL;
603         case CLI_GENERATE:
604                 return NULL;
605         }
606
607         if (a->argc == 4) {
608                 fn = a->argv[3];
609         } else if (a->argc != 3) {
610                 return CLI_SHOWUSAGE;
611         }
612
613         /* Look for historical misspelled option as well. */
614         check_anomalies = fn && (!strcasecmp(fn, "anomalies") || !strcasecmp(fn, "anomolies"));
615
616         ast_mutex_lock(&reglock);
617         for (x = 0; x < ARRAY_LEN(regions); x++) {
618                 for (reg = regions[x]; reg; reg = reg->next) {
619                         if (check_anomalies) {
620                                 region_check_fences(reg);
621                         } else if (!fn || !strcasecmp(fn, reg->file)) {
622                                 region_check_fences(reg);
623
624                                 ast_cli(a->fd, "%10u bytes allocated%s by %20s() line %5u of %s\n",
625                                         (unsigned int) reg->len, reg->cache ? " (cache)" : "",
626                                         reg->func, reg->lineno, reg->file);
627
628                                 len += reg->len;
629                                 if (reg->cache) {
630                                         cache_len += reg->len;
631                                 }
632                                 count++;
633                         }
634                 }
635         }
636         ast_mutex_unlock(&reglock);
637
638         if (check_anomalies) {
639                 ast_cli(a->fd, "Anomaly check complete.\n");
640         } else if (cache_len) {
641                 ast_cli(a->fd, "%u bytes allocated (%u in caches) in %u allocations\n",
642                         len, cache_len, count);
643         } else {
644                 ast_cli(a->fd, "%u bytes allocated in %u allocations\n", len, count);
645         }
646
647         return CLI_SUCCESS;
648 }
649
650 static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
651 {
652 #define my_max(a, b) ((a) >= (b) ? (a) : (b))
653
654         const char *fn = NULL;
655         int idx;
656         int cmp;
657         struct ast_region *reg;
658         unsigned int len = 0;
659         unsigned int cache_len = 0;
660         unsigned int count = 0;
661         struct file_summary {
662                 struct file_summary *next;
663                 unsigned int len;
664                 unsigned int cache_len;
665                 unsigned int count;
666                 unsigned int lineno;
667                 char name[my_max(sizeof(reg->file), sizeof(reg->func))];
668         } *list = NULL, *cur, **prev;
669
670         switch (cmd) {
671         case CLI_INIT:
672                 e->command = "memory show summary";
673                 e->usage =
674                         "Usage: memory show summary [<file>]\n"
675                         "       Summarizes heap memory allocations by file, or optionally\n"
676                         "       by line, if a file is specified.\n";
677                 return NULL;
678         case CLI_GENERATE:
679                 return NULL;
680         }
681
682         if (a->argc == 4) {
683                 fn = a->argv[3];
684         } else if (a->argc != 3) {
685                 return CLI_SHOWUSAGE;
686         }
687
688         ast_mutex_lock(&reglock);
689         for (idx = 0; idx < ARRAY_LEN(regions); ++idx) {
690                 for (reg = regions[idx]; reg; reg = reg->next) {
691                         if (fn) {
692                                 if (strcasecmp(fn, reg->file)) {
693                                         continue;
694                                 }
695
696                                 /* Sort list by func/lineno.  Find existing or place to insert. */
697                                 for (prev = &list; (cur = *prev); prev = &cur->next) {
698                                         cmp = strcmp(cur->name, reg->func);
699                                         if (cmp < 0) {
700                                                 continue;
701                                         }
702                                         if (cmp > 0) {
703                                                 /* Insert before current */
704                                                 cur = NULL;
705                                                 break;
706                                         }
707                                         cmp = cur->lineno - reg->lineno;
708                                         if (cmp < 0) {
709                                                 continue;
710                                         }
711                                         if (cmp > 0) {
712                                                 /* Insert before current */
713                                                 cur = NULL;
714                                         }
715                                         break;
716                                 }
717                         } else {
718                                 /* Sort list by filename.  Find existing or place to insert. */
719                                 for (prev = &list; (cur = *prev); prev = &cur->next) {
720                                         cmp = strcmp(cur->name, reg->file);
721                                         if (cmp < 0) {
722                                                 continue;
723                                         }
724                                         if (cmp > 0) {
725                                                 /* Insert before current */
726                                                 cur = NULL;
727                                         }
728                                         break;
729                                 }
730                         }
731
732                         if (!cur) {
733                                 cur = ast_alloca(sizeof(*cur));
734                                 memset(cur, 0, sizeof(*cur));
735                                 cur->lineno = reg->lineno;
736                                 ast_copy_string(cur->name, fn ? reg->func : reg->file, sizeof(cur->name));
737
738                                 cur->next = *prev;
739                                 *prev = cur;
740                         }
741
742                         cur->len += reg->len;
743                         if (reg->cache) {
744                                 cur->cache_len += reg->len;
745                         }
746                         ++cur->count;
747                 }
748         }
749         ast_mutex_unlock(&reglock);
750
751         /* Dump the whole list */
752         for (cur = list; cur; cur = cur->next) {
753                 len += cur->len;
754                 cache_len += cur->cache_len;
755                 count += cur->count;
756                 if (cur->cache_len) {
757                         if (fn) {
758                                 ast_cli(a->fd, "%10u bytes (%10u cache) in %10u allocations by %20s() line %5u of %s\n",
759                                         cur->len, cur->cache_len, cur->count, cur->name, cur->lineno, fn);
760                         } else {
761                                 ast_cli(a->fd, "%10u bytes (%10u cache) in %10u allocations in file %s\n",
762                                         cur->len, cur->cache_len, cur->count, cur->name);
763                         }
764                 } else {
765                         if (fn) {
766                                 ast_cli(a->fd, "%10u bytes in %10u allocations by %20s() line %5u of %s\n",
767                                         cur->len, cur->count, cur->name, cur->lineno, fn);
768                         } else {
769                                 ast_cli(a->fd, "%10u bytes in %10u allocations in file %s\n",
770                                         cur->len, cur->count, cur->name);
771                         }
772                 }
773         }
774
775         if (cache_len) {
776                 ast_cli(a->fd, "%u bytes allocated (%u in caches) in %u allocations\n",
777                         len, cache_len, count);
778         } else {
779                 ast_cli(a->fd, "%u bytes allocated in %u allocations\n", len, count);
780         }
781
782         return CLI_SUCCESS;
783 }
784
785 static struct ast_cli_entry cli_memory[] = {
786         AST_CLI_DEFINE(handle_memory_show, "Display outstanding memory allocations"),
787         AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
788 };
789
790 /*!
791  * \internal
792  * \return Nothing
793  */
794 static void mm_atexit_final(void)
795 {
796         FILE *log;
797
798         /* Flush all delayed memory free circular arrays. */
799         freed_regions_flush(&whales);
800         freed_regions_flush(&minnows);
801
802         /* Close the log file. */
803         log = mmlog;
804         mmlog = NULL;
805         if (log) {
806                 fclose(log);
807         }
808 }
809
810 /*!
811  * \brief Initialize malloc debug phase 1.
812  *
813  * \note Must be called first thing in main().
814  *
815  * \return Nothing
816  */
817 void __ast_mm_init_phase_1(void)
818 {
819         atexit(mm_atexit_final);
820 }
821
822 /*!
823  * \internal
824  * \return Nothing
825  */
826 static void mm_atexit_ast(void)
827 {
828         ast_cli_unregister_multiple(cli_memory, ARRAY_LEN(cli_memory));
829 }
830
831 /*!
832  * \brief Initialize malloc debug phase 2.
833  *
834  * \return Nothing
835  */
836 void __ast_mm_init_phase_2(void)
837 {
838         char filename[PATH_MAX];
839
840         ast_cli_register_multiple(cli_memory, ARRAY_LEN(cli_memory));
841
842         snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
843
844         ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
845
846         mmlog = fopen(filename, "a+");
847         if (mmlog) {
848                 fprintf(mmlog, "%ld - New session\n", (long) time(NULL));
849                 fflush(mmlog);
850         } else {
851                 ast_log(LOG_ERROR, "Could not open malloc debug log file: %s\n", filename);
852         }
853
854         ast_register_atexit(mm_atexit_ast);
855 }
856
857 #endif  /* defined(__AST_DEBUG_MALLOC) */