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