MINOR: debug: Add an option that causes random allocation failures.

When compiling with DEBUG_FAIL_ALLOC, add a new option, tune.fail-alloc,
that gives the percentage of chances an allocation fails.
This is useful to check that allocation failures are always handled
gracefully.
This commit is contained in:
Olivier Houchard 2019-01-29 15:20:16 +01:00 committed by Willy Tarreau
parent 9c9da5ee89
commit dc21ff778b
3 changed files with 81 additions and 1 deletions

View File

@ -1648,6 +1648,12 @@ tune.lua.service-timeout <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 <number>
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

View File

@ -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

View File

@ -16,6 +16,7 @@
#include <types/global.h>
#include <types/stats.h>
#include <common/cfgparse.h>
#include <common/config.h>
#include <common/debug.h>
#include <common/hathreads.h>
@ -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