Add MALLOC_DEBUG enhancements.

* Makes malloc() behave like calloc().  It will return a memory block
filled with 0x55.  A nonzero value.

* Makes free() fill the released memory block and boundary fence's with
0xdeaddead.  Any pointer use after free is going to have a pointer
pointing to 0xdeaddead.  The 0xdeaddead pointer is usually an invalid
memory address so a crash is expected.

* Puts the freed memory block into a circular array so it is not reused
immediately.

* When the circular array rotates out a memory block to the heap it checks
that the memory has not been altered from 0xdeaddead.

* Made the astmm_log message wording better.

* Made crash if the DO_CRASH menuselect option is enabled and something is
found.

* Fixed a potential alignment issue on 64 bit systems.
struct ast_region.data[] should now be aligned correctly for all
platforms.

* Extracted region_check_fences() from __ast_free_region() and
handle_memory_show().

* Updated handle_memory_show() CLI usage help.

Review: https://reviewboard.asterisk.org/r/2182/
........

Merged revisions 376029 from http://svn.asterisk.org/svn/asterisk/branches/1.8


git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/10@376030 65c4cc65-6c06-0410-ace0-fbb531ad65f3
10
Richard Mudgett 13 years ago
parent fc222734d0
commit 23ae02216b

@ -65,7 +65,8 @@ int __ast_asprintf(const char *file, int lineno, const char *func, char **strp,
__attribute__((format(printf, 5, 6)));
int __ast_vasprintf(char **strp, const char *format, va_list ap, const char *file, int lineno, const char *func)
__attribute__((format(printf, 2, 0)));
void __ast_mm_init(void);
void __ast_mm_init_phase_1(void);
void __ast_mm_init_phase_2(void);
/* Provide our own definitions */

@ -721,32 +721,27 @@ int ast_parse_digest(const char *digest, struct ast_http_digest *d, int request,
#ifdef AST_DEVMODE
void __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function);
#define ast_assert(a) _ast_assert(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
static void force_inline _ast_assert(int condition, const char *condition_str,
const char *file, int line, const char *function)
static void force_inline _ast_assert(int condition, const char *condition_str, const char *file, int line, const char *function)
{
if (__builtin_expect(!condition, 1)) {
/* Attempt to put it into the logger, but hope that at least someone saw the
* message on stderr ... */
ast_log(__LOG_ERROR, file, line, function, "FRACK!, Failed assertion %s (%d)\n",
condition_str, condition);
fprintf(stderr, "FRACK!, Failed assertion %s (%d) at line %d in %s of %s\n",
condition_str, condition, line, function, file);
/* Give the logger a chance to get the message out, just in case we abort(), or
* Asterisk crashes due to whatever problem just happened after we exit ast_assert(). */
usleep(1);
#ifdef DO_CRASH
abort();
/* Just in case abort() doesn't work or something else super silly,
* and for Qwell's amusement. */
*((int*)0)=0;
#endif
__ast_assert_failed(condition, condition_str, file, line, function);
}
}
#else
#define ast_assert(a)
#endif
/*!
* \brief Force a crash if DO_CRASH is defined.
*
* \note If DO_CRASH is not defined then the function returns.
*
* \return Nothing
*/
void ast_do_crash(void);
#include "asterisk/strings.h"
/*!

@ -3352,6 +3352,10 @@ int main(int argc, char *argv[])
int moduleresult; /*!< Result from the module load subsystem */
struct rlimit l;
#if defined(__AST_DEBUG_MALLOC)
__ast_mm_init_phase_1();
#endif /* defined(__AST_DEBUG_MALLOC) */
/* Remember original args for restart */
if (argc > ARRAY_LEN(_argv) - 1) {
fprintf(stderr, "Truncating argument size to %d\n", (int)ARRAY_LEN(_argv) - 1);
@ -4022,9 +4026,9 @@ int main(int argc, char *argv[])
pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
#ifdef __AST_DEBUG_MALLOC
__ast_mm_init();
#endif
#if defined(__AST_DEBUG_MALLOC)
__ast_mm_init_phase_2();
#endif /* defined(__AST_DEBUG_MALLOC) */
ast_lastreloadtime = ast_startuptime = ast_tvnow();
ast_cli_register_multiple(cli_asterisk, ARRAY_LEN(cli_asterisk));

@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
* Copyright (C) 1999 - 2012, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
@ -21,6 +21,7 @@
* \brief Memory Management
*
* \author Mark Spencer <markster@digium.com>
* \author Richard Mudgett <rmudgett@digium.com>
*/
/*** MODULEINFO
@ -29,7 +30,7 @@
#include "asterisk.h"
#ifdef __AST_DEBUG_MALLOC
#if defined(__AST_DEBUG_MALLOC)
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
@ -64,36 +65,63 @@ enum func_type {
#undef vasprintf
#undef asprintf
#define FENCE_MAGIC 0xdeadbeef
#define FENCE_MAGIC 0xdeadbeef /*!< Allocated memory high/low fence overwrite check. */
#define FREED_MAGIC 0xdeaddead /*!< Freed memory wipe filler. */
#define MALLOC_FILLER 0x55 /*!< Malloced memory filler. Must not be zero. */
static FILE *mmlog;
/* NOTE: Be EXTREMELY careful with modifying this structure; the total size of this structure
must result in 'automatic' alignment so that the 'fence' field lands exactly at the end of
the structure in memory (and thus immediately before the allocated region the fence is
supposed to be used to monitor). In other words, we cannot allow the compiler to insert
any padding between this structure and anything following it, so add up the sizes of all the
fields and compare to sizeof(struct ast_region)... if they don't match, then the compiler
is padding the structure and either the fields need to be rearranged to eliminate internal
padding, or a dummy field will need to be inserted before the 'fence' field to push it to
the end of the actual space it will consume. Note that this must be checked for both 32-bit
and 64-bit platforms, as the sizes of pointers and 'size_t' differ on these platforms.
*/
static struct ast_region {
struct ast_region {
struct ast_region *next;
size_t len;
char file[64];
char func[40];
unsigned int cache; /* region was allocated as part of a cache pool */
unsigned int lineno;
enum func_type which;
unsigned int cache; /* region was allocated as part of a cache pool */
char file[64];
char func[40];
/*!
* \brief Lower guard fence.
*
* \note Must be right before data[].
*
* \note Padding between fence and data[] is irrelevent because
* data[] is used to fill in the lower fence check value and not
* the fence member. The fence member is to ensure that there
* is space reserved for the fence check value.
*/
unsigned int fence;
unsigned char data[0];
} *regions[SOME_PRIME];
/*!
* \brief Location of the requested malloc block to return.
*
* \note Must have the same alignment that malloc returns.
* i.e., It is suitably aligned for any kind of varible.
*/
unsigned char data[0] __attribute__((aligned));
};
/*! Hash table of lists of active allocated memory regions. */
static struct ast_region *regions[SOME_PRIME];
#define HASH(a) \
(((unsigned long)(a)) % SOME_PRIME)
/*! Number of freed regions to keep around to delay actually freeing them. */
#define FREED_MAX_COUNT 1500
/*! Maximum size of a minnow block */
#define MINNOWS_MAX_SIZE 50
struct ast_freed_regions {
/*! Memory regions that have been freed. */
struct ast_region *regions[FREED_MAX_COUNT];
/*! Next index into freed regions[] to use. */
int index;
};
/*! Large memory blocks that have been freed. */
static struct ast_freed_regions whales;
/*! Small memory blocks that have been freed. */
static struct ast_freed_regions minnows;
#define HASH(a) (((unsigned long)(a)) % ARRAY_LEN(regions))
/*! Tracking this mutex will cause infinite recursion, as the mutex tracking
* code allocates memory */
@ -108,153 +136,378 @@ AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
} \
} while (0)
static inline void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
/*!
* \internal
*
* \note If DO_CRASH is not defined then the function returns.
*
* \return Nothing
*/
static void my_do_crash(void)
{
/*
* Give the logger a chance to get the message out, just in case
* we abort(), or Asterisk crashes due to whatever problem just
* happened.
*/
usleep(1);
ast_do_crash();
}
static void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
{
struct ast_region *reg;
void *ptr = NULL;
unsigned int *fence;
int hash;
if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
"at line %d of %s\n", (int) size, func, lineno, file);
astmm_log("Memory Allocation Failure - '%d' bytes at %s %s() line %d\n",
(int) size, file, func, lineno);
return NULL;
}
ast_copy_string(reg->file, file, sizeof(reg->file));
ast_copy_string(reg->func, func, sizeof(reg->func));
reg->lineno = lineno;
reg->len = size;
reg->which = which;
reg->cache = cache;
ptr = reg->data;
hash = HASH(ptr);
reg->fence = FENCE_MAGIC;
fence = (ptr + reg->len);
reg->lineno = lineno;
reg->which = which;
ast_copy_string(reg->file, file, sizeof(reg->file));
ast_copy_string(reg->func, func, sizeof(reg->func));
/*
* Init lower fence.
*
* We use the bytes just preceeding reg->data and not reg->fence
* because there is likely to be padding between reg->fence and
* reg->data for reg->data alignment.
*/
fence = (unsigned int *) (reg->data - sizeof(*fence));
*fence = FENCE_MAGIC;
/* Init higher fence. */
fence = (unsigned int *) (reg->data + reg->len);
put_unaligned_uint32(fence, FENCE_MAGIC);
hash = HASH(reg->data);
ast_mutex_lock(&reglock);
reg->next = regions[hash];
regions[hash] = reg;
ast_mutex_unlock(&reglock);
return ptr;
return reg->data;
}
static inline size_t __ast_sizeof_region(void *ptr)
/*!
* \internal
* \brief Wipe the region payload data with a known value.
*
* \param reg Region block to be wiped.
*
* \return Nothing
*/
static void region_data_wipe(struct ast_region *reg)
{
int hash = HASH(ptr);
struct ast_region *reg;
size_t len = 0;
ast_mutex_lock(&reglock);
for (reg = regions[hash]; reg; reg = reg->next) {
if (reg->data == ptr) {
len = reg->len;
void *end;
unsigned int *pos;
/*
* Wipe the lower fence, the payload, and whatever amount of the
* higher fence that falls into alignment with the payload.
*/
end = reg->data + reg->len;
for (pos = &reg->fence; (void *) pos <= end; ++pos) {
*pos = FREED_MAGIC;
}
}
/*!
* \internal
* \brief Check the region payload data for memory corruption.
*
* \param reg Region block to be checked.
*
* \return Nothing
*/
static void region_data_check(struct ast_region *reg)
{
void *end;
unsigned int *pos;
/*
* Check the lower fence, the payload, and whatever amount of
* the higher fence that falls into alignment with the payload.
*/
end = reg->data + reg->len;
for (pos = &reg->fence; (void *) pos <= end; ++pos) {
if (*pos != FREED_MAGIC) {
astmm_log("WARNING: Memory corrupted after free of %p allocated at %s %s() line %d\n",
reg->data, reg->file, reg->func, reg->lineno);
my_do_crash();
break;
}
}
}
/*!
* \internal
* \brief Flush the circular array of freed regions.
*
* \param freed Already freed region blocks storage.
*
* \return Nothing
*/
static void freed_regions_flush(struct ast_freed_regions *freed)
{
int idx;
struct ast_region *old;
ast_mutex_lock(&reglock);
for (idx = 0; idx < ARRAY_LEN(freed->regions); ++idx) {
old = freed->regions[idx];
freed->regions[idx] = NULL;
if (old) {
region_data_check(old);
free(old);
}
}
freed->index = 0;
ast_mutex_unlock(&reglock);
}
/*!
* \internal
* \brief Delay freeing a region block.
*
* \param freed Already freed region blocks storage.
* \param reg Region block to be freed.
*
* \return Nothing
*/
static void region_free(struct ast_freed_regions *freed, struct ast_region *reg)
{
struct ast_region *old;
return len;
region_data_wipe(reg);
ast_mutex_lock(&reglock);
old = freed->regions[freed->index];
freed->regions[freed->index] = reg;
++freed->index;
if (ARRAY_LEN(freed->regions) <= freed->index) {
freed->index = 0;
}
ast_mutex_unlock(&reglock);
if (old) {
region_data_check(old);
free(old);
}
}
static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
/*!
* \internal
* \brief Remove a region from the active regions.
*
* \param ptr Region payload data pointer.
*
* \retval region on success.
* \retval NULL if not found.
*/
static struct ast_region *region_remove(void *ptr)
{
int hash;
struct ast_region *reg, *prev = NULL;
unsigned int *fence;
if (!ptr)
return;
struct ast_region *reg;
struct ast_region *prev = NULL;
hash = HASH(ptr);
ast_mutex_lock(&reglock);
for (reg = regions[hash]; reg; reg = reg->next) {
if (reg->data == ptr) {
if (prev)
if (prev) {
prev->next = reg->next;
else
} else {
regions[hash] = reg->next;
}
break;
}
prev = reg;
}
ast_mutex_unlock(&reglock);
return reg;
}
/*!
* \internal
* \brief Check the fences of a region.
*
* \param reg Region block to check.
*
* \return Nothing
*/
static void region_check_fences(struct ast_region *reg)
{
unsigned int *fence;
/*
* We use the bytes just preceeding reg->data and not reg->fence
* because there is likely to be padding between reg->fence and
* reg->data for reg->data alignment.
*/
fence = (unsigned int *) (reg->data - sizeof(*fence));
if (*fence != FENCE_MAGIC) {
astmm_log("WARNING: Low fence violation of %p allocated at %s %s() line %d\n",
reg->data, reg->file, reg->func, reg->lineno);
my_do_crash();
}
fence = (unsigned int *) (reg->data + reg->len);
if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
astmm_log("WARNING: High fence violation of %p allocated at %s %s() line %d\n",
reg->data, reg->file, reg->func, reg->lineno);
my_do_crash();
}
}
static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
{
struct ast_region *reg;
if (!ptr) {
return;
}
reg = region_remove(ptr);
if (reg) {
fence = (unsigned int *)(reg->data + reg->len);
if (reg->fence != FENCE_MAGIC) {
astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
"line %d\n", reg->data, reg->func, reg->file, reg->lineno);
}
if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
astmm_log("WARNING: High fence violation at %p, in %s of %s, "
"line %d\n", reg->data, reg->func, reg->file, reg->lineno);
region_check_fences(reg);
if (reg->len <= MINNOWS_MAX_SIZE) {
region_free(&minnows, reg);
} else {
region_free(&whales, reg);
}
free(reg);
} else {
astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
ptr, func, file, lineno);
/*
* This memory region is not registered. It could be because of
* a double free or the memory block was not allocated by the
* malloc debug code.
*/
astmm_log("WARNING: Freeing unregistered memory %p by %s %s() line %d\n",
ptr, file, func, lineno);
my_do_crash();
}
}
void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
{
void *ptr;
if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0)))
ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0);
if (ptr) {
memset(ptr, 0, size * nmemb);
}
return ptr;
}
void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
{
void *ptr;
if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1)))
ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1);
if (ptr) {
memset(ptr, 0, size * nmemb);
}
return ptr;
}
void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
{
return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
void *ptr;
ptr = __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
if (ptr) {
/* Make sure that the malloced memory is not zero. */
memset(ptr, MALLOC_FILLER, size);
}
return ptr;
}
void __ast_free(void *ptr, const char *file, int lineno, const char *func)
void __ast_free(void *ptr, const char *file, int lineno, const char *func)
{
__ast_free_region(ptr, file, lineno, func);
}
void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
/*!
* \note reglock must be locked before calling.
*/
static struct ast_region *region_find(void *ptr)
{
void *tmp;
size_t len = 0;
int hash;
struct ast_region *reg;
if (ptr && !(len = __ast_sizeof_region(ptr))) {
astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
"line %d\n", ptr, func, file, lineno);
return NULL;
hash = HASH(ptr);
for (reg = regions[hash]; reg; reg = reg->next) {
if (reg->data == ptr) {
break;
}
}
if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
return NULL;
return reg;
}
void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
{
size_t len;
struct ast_region *found;
void *new_mem;
if (len > size)
len = size;
if (ptr) {
memcpy(tmp, ptr, len);
ast_mutex_lock(&reglock);
found = region_find(ptr);
if (!found) {
ast_mutex_unlock(&reglock);
astmm_log("WARNING: Realloc of unregistered memory %p by %s %s() line %d\n",
ptr, file, func, lineno);
my_do_crash();
return NULL;
}
len = found->len;
ast_mutex_unlock(&reglock);
} else {
found = NULL;
len = 0;
}
if (!size) {
__ast_free_region(ptr, file, lineno, func);
return NULL;
}
return tmp;
new_mem = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0);
if (new_mem) {
if (found) {
/* Copy the old data to the new malloced memory. */
if (size <= len) {
memcpy(new_mem, ptr, size);
} else {
memcpy(new_mem, ptr, len);
/* Make sure that the added memory is not zero. */
memset(new_mem + len, MALLOC_FILLER, size - len);
}
__ast_free_region(ptr, file, lineno, func);
} else {
/* Make sure that the malloced memory is not zero. */
memset(new_mem, MALLOC_FILLER, size);
}
}
return new_mem;
}
char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
{
size_t len;
void *ptr;
@ -269,7 +522,7 @@ char *__ast_strdup(const char *s, const char *file, int lineno, const char *func
return ptr;
}
char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
{
size_t len;
char *ptr;
@ -308,7 +561,7 @@ int __ast_asprintf(const char *file, int lineno, const char *func, char **strp,
return size;
}
int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
{
int size;
va_list ap2;
@ -335,15 +588,16 @@ static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli
unsigned int len = 0;
unsigned int cache_len = 0;
unsigned int count = 0;
unsigned int *fence;
switch (cmd) {
case CLI_INIT:
e->command = "memory show allocations";
e->usage =
"Usage: memory show allocations [<file>]\n"
" Dumps a list of all segments of allocated memory, optionally\n"
" limited to those from a specific file\n";
"Usage: memory show allocations [<file>|anomolies]\n"
" Dumps a list of segments of allocated memory.\n"
" Defaults to listing all memory allocations.\n"
" <file> - Restricts output to memory allocated by the file.\n"
" anomolies - Only check for fence violations.\n";
return NULL;
case CLI_GENERATE:
return NULL;
@ -354,23 +608,14 @@ static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli
fn = a->argv[3];
ast_mutex_lock(&reglock);
for (x = 0; x < SOME_PRIME; x++) {
for (x = 0; x < ARRAY_LEN(regions); x++) {
for (reg = regions[x]; reg; reg = reg->next) {
if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
fence = (unsigned int *)(reg->data + reg->len);
if (reg->fence != FENCE_MAGIC) {
astmm_log("WARNING: Low fence violation at %p, "
"in %s of %s, line %d\n", reg->data,
reg->func, reg->file, reg->lineno);
}
if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
astmm_log("WARNING: High fence violation at %p, in %s of %s, "
"line %d\n", reg->data, reg->func, reg->file, reg->lineno);
}
region_check_fences(reg);
}
if (!fn || !strcasecmp(fn, reg->file)) {
ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n",
(int) reg->len, reg->cache ? " (cache)" : "",
ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n",
(int) reg->len, reg->cache ? " (cache)" : "",
reg->func, reg->lineno, reg->file);
len += reg->len;
if (reg->cache)
@ -380,12 +625,12 @@ static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli
}
}
ast_mutex_unlock(&reglock);
if (cache_len)
ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
else
ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
return CLI_SUCCESS;
}
@ -404,24 +649,24 @@ static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct
int count;
struct file_summary *next;
} *list = NULL, *cur;
switch (cmd) {
case CLI_INIT:
e->command = "memory show summary";
e->usage =
"Usage: memory show summary [<file>]\n"
" Summarizes heap memory allocations by file, or optionally\n"
"by function, if a file is specified\n";
" by function, if a file is specified.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc > 3)
if (a->argc > 3)
fn = a->argv[3];
ast_mutex_lock(&reglock);
for (x = 0; x < SOME_PRIME; x++) {
for (x = 0; x < ARRAY_LEN(regions); x++) {
for (reg = regions[x]; reg; reg = reg->next) {
if (fn && strcasecmp(fn, reg->file))
continue;
@ -445,7 +690,7 @@ static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct
}
}
ast_mutex_unlock(&reglock);
/* Dump the whole list */
for (cur = list; cur; cur = cur->next) {
len += cur->len;
@ -453,18 +698,18 @@ static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct
count += cur->count;
if (cur->cache_len) {
if (fn) {
ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n",
ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n",
cur->len, cur->cache_len, cur->count, cur->fn, fn);
} else {
ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n",
ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n",
cur->len, cur->cache_len, cur->count, cur->fn);
}
} else {
if (fn) {
ast_cli(a->fd, "%10d bytes in %d allocations in function '%s' of '%s'\n",
ast_cli(a->fd, "%10d bytes in %d allocations in function '%s' of '%s'\n",
cur->len, cur->count, cur->fn, fn);
} else {
ast_cli(a->fd, "%10d bytes in %d allocations in file '%s'\n",
ast_cli(a->fd, "%10d bytes in %d allocations in file '%s'\n",
cur->len, cur->count, cur->fn);
}
}
@ -483,25 +728,71 @@ static struct ast_cli_entry cli_memory[] = {
AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
};
void __ast_mm_init(void)
/*!
* \internal
* \return Nothing
*/
static void mm_atexit_final(void)
{
char filename[PATH_MAX];
size_t pad = sizeof(struct ast_region) - offsetof(struct ast_region, data);
FILE *log;
if (pad) {
ast_log(LOG_ERROR, "struct ast_region has %d bytes of padding! This must be eliminated for low-fence checking to work properly!\n", (int) pad);
/* Flush all delayed memory free circular arrays. */
freed_regions_flush(&whales);
freed_regions_flush(&minnows);
/* Close the log file. */
log = mmlog;
mmlog = NULL;
if (log) {
fclose(log);
}
}
/*!
* \brief Initialize malloc debug phase 1.
*
* \note Must be called first thing in main().
*
* \return Nothing
*/
void __ast_mm_init_phase_1(void)
{
atexit(mm_atexit_final);
}
/*!
* \internal
* \return Nothing
*/
static void mm_atexit_ast(void)
{
ast_cli_unregister_multiple(cli_memory, ARRAY_LEN(cli_memory));
}
/*!
* \brief Initialize malloc debug phase 2.
*
* \return Nothing
*/
void __ast_mm_init_phase_2(void)
{
char filename[PATH_MAX];
ast_cli_register_multiple(cli_memory, ARRAY_LEN(cli_memory));
snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
ast_verb(1, "Asterisk Malloc Debugger Started (see %s))\n", filename);
if ((mmlog = fopen(filename, "a+"))) {
fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
mmlog = fopen(filename, "a+");
if (mmlog) {
fprintf(mmlog, "%ld - New session\n", (long) time(NULL));
fflush(mmlog);
} else {
ast_log(LOG_ERROR, "Could not open malloc debug log file: %s\n", filename);
}
ast_register_atexit(mm_atexit_ast);
}
#endif
#endif /* defined(__AST_DEBUG_MALLOC) */

@ -2178,3 +2178,35 @@ char *ast_utils_which(const char *binary, char *fullpath, size_t fullpath_size)
return NULL;
}
void ast_do_crash(void)
{
#if defined(DO_CRASH)
abort();
/*
* Just in case abort() doesn't work or something else super
* silly, and for Qwell's amusement.
*/
*((int *) 0) = 0;
#endif /* defined(DO_CRASH) */
}
#if defined(AST_DEVMODE)
void __ast_assert_failed(int condition, const char *condition_str, const char *file, int line, const char *function)
{
/*
* Attempt to put it into the logger, but hope that at least
* someone saw the message on stderr ...
*/
ast_log(__LOG_ERROR, file, line, function, "FRACK!, Failed assertion %s (%d)\n",
condition_str, condition);
fprintf(stderr, "FRACK!, Failed assertion %s (%d) at line %d in %s of %s\n",
condition_str, condition, line, function, file);
/*
* Give the logger a chance to get the message out, just in case
* we abort(), or Asterisk crashes due to whatever problem just
* happened after we exit ast_assert().
*/
usleep(1);
ast_do_crash();
}
#endif /* defined(AST_DEVMODE) */

Loading…
Cancel
Save