33 #if defined(__AST_DEBUG_MALLOC)
37 #include "asterisk/paths.h"
51 #define SOME_PRIME 1567
73 #define FENCE_MAGIC 0xfeedbabe
74 #define FREED_MAGIC 0xdeaddead
75 #define MALLOC_FILLER 0x55
105 unsigned char data[0] __attribute__((aligned));
109 static struct ast_region *regions[SOME_PRIME];
112 #define FREED_MAX_COUNT 1500
115 #define MINNOWS_MAX_SIZE 50
117 struct ast_freed_regions {
119 struct ast_region *regions[FREED_MAX_COUNT];
125 static struct ast_freed_regions whales;
127 static struct ast_freed_regions minnows;
133 SUMMARY_BY_LINE = (1 << 0),
135 SUMMARY_BY_FUNC = (1 << 1),
137 SUMMARY_BY_FILE = (1 << 2),
141 static enum summary_opts atexit_summary;
143 static int atexit_list;
145 #define HASH(a) (((unsigned long)(a)) % ARRAY_LEN(regions))
151 #define astmm_log(...) \
153 fprintf(stderr, __VA_ARGS__); \
155 fprintf(mmlog, __VA_ARGS__); \
167 return calloc(nmemb, size);
192 static void my_do_crash(
void)
203 static void *__ast_alloc_region(
size_t size,
const enum func_type which,
const char *file,
int lineno,
const char *func,
unsigned int cache)
205 struct ast_region *reg;
209 if (!(reg =
malloc(size +
sizeof(*reg) +
sizeof(*fence)))) {
210 astmm_log(
"Memory Allocation Failure - '%d' bytes at %s %s() line %d\n",
211 (
int) size, file, func, lineno);
217 reg->lineno = lineno;
229 fence = (
unsigned int *) (reg->data -
sizeof(*fence));
230 *fence = FENCE_MAGIC;
233 fence = (
unsigned int *) (reg->data + reg->len);
236 hash = HASH(reg->data);
253 static void region_data_wipe(
struct ast_region *reg)
262 end = reg->data + reg->len;
263 for (pos = ®->fence; (
void *) pos <= end; ++pos) {
276 static void region_data_check(
struct ast_region *reg)
285 end = reg->data + reg->len;
286 for (pos = ®->fence; (
void *) pos <= end; ++pos) {
287 if (*pos != FREED_MAGIC) {
288 astmm_log(
"WARNING: Memory corrupted after free of %p allocated at %s %s() line %d\n",
289 reg->data, reg->file, reg->func, reg->lineno);
304 static void freed_regions_flush(
struct ast_freed_regions *freed)
307 struct ast_region *old;
310 for (idx = 0; idx <
ARRAY_LEN(freed->regions); ++idx) {
311 old = freed->regions[idx];
312 freed->regions[idx] = NULL;
314 region_data_check(old);
331 static void region_free(
struct ast_freed_regions *freed,
struct ast_region *reg)
333 struct ast_region *old;
335 region_data_wipe(reg);
338 old = freed->regions[freed->index];
339 freed->regions[freed->index] = reg;
342 if (
ARRAY_LEN(freed->regions) <= freed->index) {
348 region_data_check(old);
362 static struct ast_region *region_remove(
void *ptr)
365 struct ast_region *reg;
366 struct ast_region *prev = NULL;
371 for (reg = regions[hash]; reg; reg =
AST_LIST_NEXT(reg, node)) {
372 if (reg->data == ptr) {
395 static void region_check_fences(
struct ast_region *reg)
404 fence = (
unsigned int *) (reg->data -
sizeof(*fence));
405 if (*fence != FENCE_MAGIC) {
406 astmm_log(
"WARNING: Low fence violation of %p allocated at %s %s() line %d\n",
407 reg->data, reg->file, reg->func, reg->lineno);
410 fence = (
unsigned int *) (reg->data + reg->len);
412 astmm_log(
"WARNING: High fence violation of %p allocated at %s %s() line %d\n",
413 reg->data, reg->file, reg->func, reg->lineno);
424 static void regions_check_all_fences(
void)
427 struct ast_region *reg;
430 for (idx = 0; idx <
ARRAY_LEN(regions); ++idx) {
431 for (reg = regions[idx]; reg; reg =
AST_LIST_NEXT(reg, node)) {
432 region_check_fences(reg);
438 static void __ast_free_region(
void *ptr,
const char *file,
int lineno,
const char *func)
440 struct ast_region *reg;
446 reg = region_remove(ptr);
448 region_check_fences(reg);
450 if (reg->len <= MINNOWS_MAX_SIZE) {
451 region_free(&minnows, reg);
453 region_free(&whales, reg);
461 astmm_log(
"WARNING: Freeing unregistered memory %p by %s %s() line %d\n",
462 ptr, file, func, lineno);
467 void *
__ast_calloc(
size_t nmemb,
size_t size,
const char *file,
int lineno,
const char *func)
471 ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0);
473 memset(ptr, 0, size * nmemb);
479 void *
__ast_calloc_cache(
size_t nmemb,
size_t size,
const char *file,
int lineno,
const char *func)
483 ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1);
485 memset(ptr, 0, size * nmemb);
491 void *
__ast_malloc(
size_t size,
const char *file,
int lineno,
const char *func)
495 ptr = __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
498 memset(ptr, MALLOC_FILLER, size);
504 void __ast_free(
void *ptr,
const char *file,
int lineno,
const char *func)
506 __ast_free_region(ptr, file, lineno, func);
512 static struct ast_region *region_find(
void *ptr)
515 struct ast_region *reg;
518 for (reg = regions[hash]; reg; reg =
AST_LIST_NEXT(reg, node)) {
519 if (reg->data == ptr) {
527 void *
__ast_realloc(
void *ptr,
size_t size,
const char *file,
int lineno,
const char *func)
530 struct ast_region *found;
535 found = region_find(ptr);
538 astmm_log(
"WARNING: Realloc of unregistered memory %p by %s %s() line %d\n",
539 ptr, file, func, lineno);
551 __ast_free_region(ptr, file, lineno, func);
555 new_mem = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0);
560 memcpy(new_mem, ptr, size);
562 memcpy(new_mem, ptr, len);
564 memset(new_mem + len, MALLOC_FILLER, size - len);
566 __ast_free_region(ptr, file, lineno, func);
569 memset(new_mem, MALLOC_FILLER, size);
576 char *
__ast_strdup(
const char *s,
const char *file,
int lineno,
const char *func)
585 if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
591 char *
__ast_strndup(
const char *s,
size_t n,
const char *file,
int lineno,
const char *func)
601 if ((ptr = __ast_alloc_region(len + 1, FUNC_STRNDUP, file, lineno, func, 0))) {
609 int __ast_asprintf(
const char *file,
int lineno,
const char *func,
char **strp,
const char *fmt, ...)
618 size = vsnprintf(&s, 1, fmt, ap2);
620 if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
624 vsnprintf(*strp, size + 1, fmt, ap);
630 int __ast_vasprintf(
char **strp,
const char *fmt, va_list ap,
const char *file,
int lineno,
const char *func)
638 size = vsnprintf(&s, 1, fmt, ap2);
640 if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
644 vsnprintf(*strp, size + 1, fmt, ap);
653 e->
command =
"memory atexit list";
655 "Usage: memory atexit list {on|off}\n"
656 " Enable dumping a list of still allocated memory segments at exit.\n";
660 const char *
const options[] = {
"off",
"on", NULL };
679 ast_cli(a->
fd,
"The atexit list is: %s\n", atexit_list ?
"On" :
"Off");
690 e->
command =
"memory atexit summary";
692 "Usage: memory atexit summary {off|byline|byfunc|byfile}\n"
693 " Summary of still allocated memory segments at exit options.\n"
694 " off - Disable at exit summary.\n"
695 " byline - Enable at exit summary by file line number.\n"
696 " byfunc - Enable at exit summary by function name.\n"
697 " byfile - Enable at exit summary by file.\n"
699 " Note: byline, byfunc, and byfile are cumulative enables.\n";
703 const char *
const options[] = {
"off",
"byline",
"byfunc",
"byfile", NULL };
715 atexit_summary = SUMMARY_OFF;
716 }
else if (!strcasecmp(a->
argv[3],
"byline")) {
717 atexit_summary |= SUMMARY_BY_LINE;
718 }
else if (!strcasecmp(a->
argv[3],
"byfunc")) {
719 atexit_summary |= SUMMARY_BY_FUNC;
720 }
else if (!strcasecmp(a->
argv[3],
"byfile")) {
721 atexit_summary |= SUMMARY_BY_FILE;
726 if (atexit_summary) {
728 if (atexit_summary & SUMMARY_BY_LINE) {
729 strcat(buf,
"byline");
731 if (atexit_summary & SUMMARY_BY_FUNC) {
735 strcat(buf,
"byfunc");
737 if (atexit_summary & SUMMARY_BY_FILE) {
741 strcat(buf,
"byfile");
746 ast_cli(a->
fd,
"The atexit summary is: %s\n", buf);
753 const char *fn = NULL;
754 struct ast_region *reg;
756 unsigned int len = 0;
757 unsigned int cache_len = 0;
758 unsigned int count = 0;
762 e->
command =
"memory show allocations";
764 "Usage: memory show allocations [<file>|anomalies]\n"
765 " Dumps a list of segments of allocated memory.\n"
766 " Defaults to listing all memory allocations.\n"
767 " <file> - Restricts output to memory allocated by the file.\n"
768 " anomalies - Only check for fence violations.\n";
776 }
else if (a->
argc != 3) {
781 if (fn && (!strcasecmp(fn,
"anomalies") || !strcasecmp(fn,
"anomolies"))) {
782 regions_check_all_fences();
783 ast_cli(a->
fd,
"Anomaly check complete.\n");
788 for (idx = 0; idx <
ARRAY_LEN(regions); ++idx) {
789 for (reg = regions[idx]; reg; reg =
AST_LIST_NEXT(reg, node)) {
790 if (fn && strcasecmp(fn, reg->file)) {
794 region_check_fences(reg);
796 ast_cli(a->
fd,
"%10u bytes allocated%s by %20s() line %5u of %s\n",
797 (
unsigned int) reg->len, reg->cache ?
" (cache)" :
"",
798 reg->func, reg->lineno, reg->file);
802 cache_len += reg->len;
810 ast_cli(a->
fd,
"%u bytes allocated (%u in caches) in %u allocations\n",
811 len, cache_len, count);
813 ast_cli(a->
fd,
"%u bytes allocated in %u allocations\n", len, count);
821 #define my_max(a, b) ((a) >= (b) ? (a) : (b))
823 const char *fn = NULL;
826 struct ast_region *reg;
827 unsigned int len = 0;
828 unsigned int cache_len = 0;
829 unsigned int count = 0;
830 struct file_summary {
831 struct file_summary *next;
833 unsigned int cache_len;
836 char name[my_max(
sizeof(reg->file),
sizeof(reg->func))];
837 } *list = NULL, *cur, **prev;
841 e->
command =
"memory show summary";
843 "Usage: memory show summary [<file>]\n"
844 " Summarizes heap memory allocations by file, or optionally\n"
845 " by line, if a file is specified.\n";
853 }
else if (a->
argc != 3) {
858 for (idx = 0; idx <
ARRAY_LEN(regions); ++idx) {
859 for (reg = regions[idx]; reg; reg =
AST_LIST_NEXT(reg, node)) {
861 if (strcasecmp(fn, reg->file)) {
866 for (prev = &list; (cur = *prev); prev = &cur->next) {
867 cmp = strcmp(cur->name, reg->func);
876 cmp = cur->lineno - reg->lineno;
888 for (prev = &list; (cur = *prev); prev = &cur->next) {
889 cmp = strcmp(cur->name, reg->file);
903 memset(cur, 0,
sizeof(*cur));
904 cur->lineno = reg->lineno;
905 ast_copy_string(cur->name, fn ? reg->func : reg->file,
sizeof(cur->name));
911 cur->len += reg->len;
913 cur->cache_len += reg->len;
921 for (cur = list; cur; cur = cur->next) {
923 cache_len += cur->cache_len;
925 if (cur->cache_len) {
927 ast_cli(a->
fd,
"%10u bytes (%10u cache) in %10u allocations by %20s() line %5u of %s\n",
928 cur->len, cur->cache_len, cur->count, cur->name, cur->lineno, fn);
930 ast_cli(a->
fd,
"%10u bytes (%10u cache) in %10u allocations in file %s\n",
931 cur->len, cur->cache_len, cur->count, cur->name);
935 ast_cli(a->
fd,
"%10u bytes in %10u allocations by %20s() line %5u of %s\n",
936 cur->len, cur->count, cur->name, cur->lineno, fn);
938 ast_cli(a->
fd,
"%10u bytes in %10u allocations in file %s\n",
939 cur->len, cur->count, cur->name);
945 ast_cli(a->
fd,
"%u bytes allocated (%u in caches) in %u allocations\n",
946 len, cache_len, count);
948 ast_cli(a->
fd,
"%u bytes allocated in %u allocations\n", len, count);
955 AST_CLI_DEFINE(handle_memory_atexit_list,
"Enable memory allocations not freed at exit list."),
956 AST_CLI_DEFINE(handle_memory_atexit_summary,
"Enable memory allocations not freed at exit summary."),
957 AST_CLI_DEFINE(handle_memory_show_allocations,
"Display outstanding memory allocations"),
958 AST_CLI_DEFINE(handle_memory_show_summary,
"Summarize outstanding memory allocations"),
979 static size_t mm_atexit_hash_list(
struct region_list *
list)
981 struct ast_region *reg;
986 for (idx = 0; idx <
ARRAY_LEN(regions); ++idx) {
987 while ((reg = regions[idx])) {
1007 static void mm_atexit_hash_restore(
struct region_list *list)
1009 struct ast_region *reg;
1013 hash = HASH(reg->data);
1015 regions[hash] = reg;
1030 static int mm_atexit_cmp(
struct ast_region *left,
struct ast_region *right)
1037 cmp = strcmp(left->file, right->file);
1043 cmp = left->lineno - right->lineno;
1049 cmp_size = left->len - right->len;
1058 cmp_ptr = left->data - right->data;
1079 static void mm_atexit_list_merge(
struct region_list *list,
struct region_list *sub1,
struct region_list *sub2)
1081 struct ast_region *reg;
1116 static void mm_atexit_list_split(
struct region_list *list,
struct region_list sub[],
size_t num_lists,
size_t size,
size_t *remaining)
1120 for (idx = 0; idx < num_lists; ++idx) {
1123 if (*remaining < size) {
1132 for (count = size; count--;) {
1133 struct ast_region *reg;
1150 static void mm_atexit_list_sort(
struct region_list *list,
size_t length)
1155 struct region_list sub[2] = {
1171 mm_atexit_list_split(list, sub,
ARRAY_LEN(sub), size, &remaining);
1172 mm_atexit_list_merge(&merged, &sub[0], &sub[1]);
1194 static void mm_atexit_regions_list(
struct region_list *alloced)
1196 struct ast_region *reg;
1199 astmm_log(
"%s %s() line %u: %u bytes%s at %p\n",
1200 reg->file, reg->func, reg->lineno,
1201 (
unsigned int) reg->len, reg->cache ?
" (cache)" :
"", reg->data);
1213 static void mm_atexit_regions_summary(
struct region_list *alloced)
1215 struct ast_region *reg;
1216 struct ast_region *next;
1220 unsigned int cache_len;
1221 } by_line, by_func, by_file,
total;
1225 by_line.cache_len = 0;
1229 by_func.cache_len = 0;
1233 by_file.cache_len = 0;
1237 total.cache_len = 0;
1243 by_line.len += reg->len;
1245 by_line.cache_len += reg->len;
1247 if (next && !strcmp(reg->file, next->file) && reg->lineno == next->lineno) {
1250 if (atexit_summary & SUMMARY_BY_LINE) {
1251 if (by_line.cache_len) {
1252 astmm_log(
"%10u bytes (%u in caches) in %u allocations. %s %s() line %u\n",
1253 by_line.len, by_line.cache_len, by_line.count, reg->file, reg->func, reg->lineno);
1255 astmm_log(
"%10u bytes in %5u allocations. %s %s() line %u\n",
1256 by_line.len, by_line.count, reg->file, reg->func, reg->lineno);
1260 by_func.count += by_line.count;
1261 by_func.len += by_line.len;
1262 by_func.cache_len += by_line.cache_len;
1265 by_line.cache_len = 0;
1266 if (next && !strcmp(reg->file, next->file) && !strcmp(reg->func, next->func)) {
1269 if (atexit_summary & SUMMARY_BY_FUNC) {
1270 if (by_func.cache_len) {
1271 astmm_log(
"%10u bytes (%u in caches) in %u allocations. %s %s()\n",
1272 by_func.len, by_func.cache_len, by_func.count, reg->file, reg->func);
1274 astmm_log(
"%10u bytes in %5u allocations. %s %s()\n",
1275 by_func.len, by_func.count, reg->file, reg->func);
1279 by_file.count += by_func.count;
1280 by_file.len += by_func.len;
1281 by_file.cache_len += by_func.cache_len;
1284 by_func.cache_len = 0;
1285 if (next && !strcmp(reg->file, next->file)) {
1288 if (atexit_summary & SUMMARY_BY_FILE) {
1289 if (by_file.cache_len) {
1290 astmm_log(
"%10u bytes (%u in caches) in %u allocations. %s\n",
1291 by_file.len, by_file.cache_len, by_file.count, reg->file);
1293 astmm_log(
"%10u bytes in %5u allocations. %s\n",
1294 by_file.len, by_file.count, reg->file);
1298 total.count += by_file.count;
1299 total.len += by_file.len;
1300 total.cache_len += by_file.cache_len;
1303 by_file.cache_len = 0;
1306 if (total.cache_len) {
1307 astmm_log(
"%u bytes (%u in caches) in %u allocations.\n",
1308 total.len, total.cache_len, total.count);
1310 astmm_log(
"%u bytes in %u allocations.\n", total.len, total.count);
1322 static void mm_atexit_dump(
void)
1327 length = mm_atexit_hash_list(&alloced_atexit);
1330 astmm_log(
"Exiting with all memory freed.\n");
1334 mm_atexit_list_sort(&alloced_atexit, length);
1336 astmm_log(
"Exiting with the following memory not freed:\n");
1338 mm_atexit_regions_list(&alloced_atexit);
1340 if (atexit_summary) {
1341 mm_atexit_regions_summary(&alloced_atexit);
1350 mm_atexit_hash_restore(&alloced_atexit);
1357 static void mm_atexit_final(
void)
1362 if (atexit_list || atexit_summary) {
1363 fprintf(stderr,
"Waiting 10 seconds to let other threads die.\n");
1367 regions_check_all_fences();
1370 freed_regions_flush(&whales);
1371 freed_regions_flush(&minnows);
1374 if (atexit_list || atexit_summary) {
1397 atexit(mm_atexit_final);
1404 static void mm_atexit_ast(
void)
1416 char filename[PATH_MAX];
1422 ast_verb(1,
"Asterisk Malloc Debugger Started (see %s))\n", filename);
1424 mmlog = fopen(filename,
"a+");
1426 fprintf(mmlog,
"%ld - New session\n", (
long) time(NULL));
1429 ast_log(
LOG_ERROR,
"Could not open malloc debug log file: %s\n", filename);
void ast_std_free(void *ptr)
#define AST_CLI_DEFINE(fn, txt,...)
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.
void * __ast_malloc(size_t size, const char *file, int lineno, const char *func)
String manipulation functions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
char * __ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
Time-related functions and macros.
descriptor for a cli entry.
void __ast_free(void *ptr, const char *file, int lineno, const char *func)
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
void * ast_std_realloc(void *ptr, size_t size)
static void put_unaligned_uint32(void *p, unsigned int datum)
void * __ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
#define ast_mutex_lock(a)
void * ast_std_malloc(size_t size)
void ast_cli(int fd, const char *fmt,...)
void ast_free_ptr(void *ptr)
#define ast_verb(level,...)
int __ast_vasprintf(char **strp, const char *format, va_list ap, const char *file, int lineno, const char *func)
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
void __ast_mm_init_phase_1(void)
Handle unaligned data access.
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
static unsigned int get_unaligned_uint32(const void *p)
char * __ast_strdup(const char *s, const char *file, int lineno, const char *func)
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
void * __ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
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".
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
const char * ast_config_AST_LOG_DIR
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
void * __ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
void * ast_std_calloc(size_t nmemb, size_t size)
#define AST_LIST_HEAD_NOLOCK_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD_NOLOCK.
struct ao2_container * cache
#define AST_MUTEX_DEFINE_STATIC_NOTRACKING(mutex)
void ast_do_crash(void)
Force a crash if DO_CRASH is defined.
void __ast_mm_init_phase_2(void)
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Register multiple commands.
size_t strnlen(const char *, size_t)
struct ast_cli_entry::@159 list
int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *format,...)
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
#define ast_mutex_unlock(a)
#define AST_LIST_APPEND_LIST(head, list, field)
Appends a whole list to the tail of a list.