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