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.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index adc6efa..fe5eb25 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1648,6 +1648,12 @@
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
diff --git a/include/common/config.h b/include/common/config.h
index 6fd85f8..55ecd59 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 6cafd8b..04259a5 100644
--- a/src/memory.c
+++ b/src/memory.c
@@ -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 @@
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 *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 @@
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