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