diff --git a/doc/configuration.txt b/doc/configuration.txt index adc6efa25..fe5eb2507 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1648,6 +1648,12 @@ tune.lua.service-timeout counts only the pure Lua runtime. If the Lua does a sleep, the sleep is not taken in account. The default timeout is 4s. +tune.fail-alloc + If compiled with DEBUG_FAIL_ALLOC, gives the percentage of chances an + allocation attempt fails. Must be between 0 (no failure) and 100 (no + success). This is useful to debug and make sure memory failures are handled + gracefully. + tune.maxaccept Sets the maximum number of consecutive connections a process may accept in a row before switching to other work. In single process mode, higher numbers diff --git a/include/common/config.h b/include/common/config.h index 6fd85f893..55ecd5901 100644 --- a/include/common/config.h +++ b/include/common/config.h @@ -51,7 +51,7 @@ /* On architectures supporting threads and double-word CAS, we can implement * lock-less memory pools. This isn't supported for debugging modes however. */ -#if defined(USE_THREAD) && defined(HA_HAVE_CAS_DW) && !defined(DEBUG_NO_LOCKLESS_POOLS) && !defined(DEBUG_UAF) +#if defined(USE_THREAD) && defined(HA_HAVE_CAS_DW) && !defined(DEBUG_NO_LOCKLESS_POOLS) && !defined(DEBUG_UAF) && !defined(DEBUG_FAIL_ALLOC) #define CONFIG_HAP_LOCKLESS_POOLS #ifndef CONFIG_HAP_POOL_CACHE_SIZE #define CONFIG_HAP_POOL_CACHE_SIZE 524288 diff --git a/src/memory.c b/src/memory.c index 6cafd8b23..04259a539 100644 --- a/src/memory.c +++ b/src/memory.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,11 @@ THREAD_LOCAL size_t pool_cache_count = 0; /* #cache objects */ static struct list pools = LIST_HEAD_INIT(pools); int mem_poison_byte = -1; +#ifdef DEBUG_FAIL_ALLOC +static int mem_fail_rate = 0; +static int mem_should_fail(const struct pool_head *); +#endif + /* Try to find an existing shared pool with the same characteristics and * returns it, otherwise creates this one. NULL is returned if no memory * is available for a new creation. Two flags are supported : @@ -301,6 +307,10 @@ void *__pool_refill_alloc(struct pool_head *pool, unsigned int avail) void *ptr = NULL; int failed = 0; +#ifdef DEBUG_FAIL_ALLOC + if (mem_should_fail(pool)) + return NULL; +#endif /* stop point */ avail += pool->used; @@ -562,6 +572,70 @@ static struct cli_kw_list cli_kws = {{ },{ INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws); +#ifdef DEBUG_FAIL_ALLOC +#define MEM_FAIL_MAX_CHAR 32 +#define MEM_FAIL_MAX_STR 128 +static int mem_fail_cur_idx; +static char mem_fail_str[MEM_FAIL_MAX_CHAR * MEM_FAIL_MAX_STR]; +__decl_hathreads(static HA_SPINLOCK_T mem_fail_lock); + +int mem_should_fail(const struct pool_head *pool) +{ + int ret; + int n; + + if (mem_fail_rate > 0 && !(global.mode & MODE_STARTING)) { + int randnb = random() % 100; + + if (mem_fail_rate > randnb) + ret = 1; + else + ret = 0; + } + HA_SPIN_LOCK(START_LOCK, &mem_fail_lock); + n = snprintf(&mem_fail_str[mem_fail_cur_idx * MEM_FAIL_MAX_CHAR], + MEM_FAIL_MAX_CHAR - 2, + "%d %.18s %d %d", mem_fail_cur_idx, pool->name, ret, tid); + while (n < MEM_FAIL_MAX_CHAR - 1) + mem_fail_str[mem_fail_cur_idx * MEM_FAIL_MAX_CHAR + n++] = ' '; + if (mem_fail_cur_idx < MEM_FAIL_MAX_STR - 1) + mem_fail_str[mem_fail_cur_idx * MEM_FAIL_MAX_CHAR + n] = '\n'; + else + mem_fail_str[mem_fail_cur_idx * MEM_FAIL_MAX_CHAR + n] = 0; + mem_fail_cur_idx++; + if (mem_fail_cur_idx == MEM_FAIL_MAX_STR) + mem_fail_cur_idx = 0; + HA_SPIN_UNLOCK(START_LOCK, &mem_fail_lock); + return ret; + +} + +/* config parser for global "tune.fail-alloc" */ +static int mem_parse_global_fail_alloc(char **args, int section_type, struct proxy *curpx, + struct proxy *defpx, const char *file, int line, + char **err) +{ + if (too_many_args(1, args, err, NULL)) + return -1; + mem_fail_rate = atoi(args[1]); + if (mem_fail_rate < 0 || mem_fail_rate > 100) { + memprintf(err, "'%s' expects a numeric value between 0 and 100.", args[0]); + return -1; + } + return 0; +} +#endif + +/* register global config keywords */ +static struct cfg_kw_list mem_cfg_kws = {ILH, { +#ifdef DEBUG_FAIL_ALLOC + { CFG_GLOBAL, "tune.fail-alloc", mem_parse_global_fail_alloc }, +#endif + { 0, NULL, NULL } +}}; + +INITCALL1(STG_REGISTER, cfg_register_keywords, &mem_cfg_kws); + /* * Local variables: * c-indent-level: 8