diff options
-rw-r--r-- | include/asterisk/utils.h | 19 | ||||
-rw-r--r-- | main/pbx.c | 78 | ||||
-rw-r--r-- | main/utils.c | 2 |
3 files changed, 98 insertions, 1 deletions
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h index 184850905..599beba54 100644 --- a/include/asterisk/utils.h +++ b/include/asterisk/utils.h @@ -25,6 +25,7 @@ #include "asterisk/network.h" +#include <execinfo.h> #include <time.h> /* we want to override localtime_r */ #include <unistd.h> #include <string.h> @@ -483,8 +484,24 @@ static void ast_free_ptr(void *ptr) #ifndef __AST_DEBUG_MALLOC +/* This buffer is in static memory. We never intend to read it, + * nor do we care about multiple threads writing to it at the + * same time. We only want to know if we're recursing too deep + * already. 60 entries should be more than enough. Function call + * depth rarely exceeds 20 or so. */ +#define _AST_MEM_BACKTRACE_BUFLEN 60 +extern void *_ast_mem_backtrace_buffer[_AST_MEM_BACKTRACE_BUFLEN]; + #define MALLOC_FAILURE_MSG \ - ast_log(LOG_ERROR, "Memory Allocation Failure in function %s at line %d of %s\n", func, lineno, file); + { \ + /* Ok, this sucks. But if we're already out of mem, we don't \ + * want the logger to create infinite recursion (and a crash). */ \ + if (backtrace(_ast_mem_backtrace_buffer, _AST_MEM_BACKTRACE_BUFLEN) < \ + _AST_MEM_BACKTRACE_BUFLEN) { \ + ast_log(LOG_ERROR, "Memory Allocation Failure in function %s at line %d of %s\n", func, lineno, file); \ + } \ + } + /*! * \brief A wrapper for malloc() * diff --git a/main/pbx.c b/main/pbx.c index 5000bf47b..730b526fe 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -7281,6 +7281,81 @@ static char *handle_show_switches(struct ast_cli_entry *e, int cmd, struct ast_c return CLI_SUCCESS; } +#if 0 +/* This code can be used to test if the system survives running out of memory. + * It might be an idea to put this in only if ENABLE_AUTODESTRUCT_TESTS is enabled. + * + * If you want to test this, these Linux sysctl flags might be appropriate: + * vm.overcommit_memory = 2 + * vm.swappiness = 0 + * + * <@Corydon76-home> I envision 'core eat disk space' and 'core eat file descriptors' now + * <@mjordan> egads + * <@mjordan> it's literally the 'big red' auto-destruct button + * <@mjordan> if you were wondering who even builds such a thing.... well, now you know + * ... + * <@Corydon76-home> What about if they lived only if you defined TEST_FRAMEWORK? Shouldn't have those on production machines + * <@mjordan> I think accompanied with an update to one of our README files that "no, really, TEST_FRAMEWORK isn't for you", I'd be fine + */ +static char *handle_eat_memory(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + void **blocks; + int blocks_pos = 0; + const int blocks_max = 50000; + long long int allocated = 0; + int sizes[] = { + 100 * 1024 * 1024, + 100 * 1024, + 2 * 1024, + 400, + 0 + }; + int i; + + switch (cmd) { + case CLI_INIT: + /* To do: add method to free memory again? 5 minutes? */ + e->command = "core eat memory"; + e->usage = + "Usage: core eat memory\n" + " Eats all available memory so you can test if the system survives\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + blocks = ast_malloc(sizeof(void*) * blocks_max); + if (!blocks) { + ast_log(LOG_ERROR, "Already out of mem?\n"); + return CLI_SUCCESS; + } + + for (i = 0; sizes[i]; ++i) { + int alloc_size = sizes[i]; + ast_log(LOG_WARNING, "Allocating %d sized blocks (got %d blocks already)\n", alloc_size, blocks_pos); + while (1) { + void *block; + if (blocks_pos >= blocks_max) { + ast_log(LOG_ERROR, "Memory buffer too small? Run me again :)\n"); + break; + } + + block = ast_malloc(alloc_size); + if (!block) { + break; + } + + blocks[blocks_pos++] = block; + allocated += alloc_size; + } + } + + /* No freeing of the mem! */ + ast_log(LOG_WARNING, "Allocated %lld bytes total!\n", allocated); + return CLI_SUCCESS; +} +#endif + static char *handle_show_applications(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ast_app *aa; @@ -8162,6 +8237,9 @@ static char *handle_unset_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, * CLI entries for upper commands ... */ static struct ast_cli_entry pbx_cli[] = { +#if 0 + AST_CLI_DEFINE(handle_eat_memory, "Eats all available memory"), +#endif AST_CLI_DEFINE(handle_show_applications, "Shows registered dialplan applications"), AST_CLI_DEFINE(handle_show_functions, "Shows registered dialplan functions"), AST_CLI_DEFINE(handle_show_switches, "Show alternative switches"), diff --git a/main/utils.c b/main/utils.c index 1f0528556..ed0e4bbef 100644 --- a/main/utils.c +++ b/main/utils.c @@ -74,6 +74,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") static char base64[64]; static char b2a[256]; +void *_ast_mem_backtrace_buffer[_AST_MEM_BACKTRACE_BUFLEN]; + AST_THREADSTORAGE(inet_ntoa_buf); #if !defined(HAVE_GETHOSTBYNAME_R_5) && !defined(HAVE_GETHOSTBYNAME_R_6) |