]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: debug: Add an option that causes random allocation failures.
authorOlivier Houchard <ohouchard@haproxy.com>
Tue, 29 Jan 2019 14:20:16 +0000 (15:20 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 31 Jan 2019 18:38:25 +0000 (19:38 +0100)
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.

doc/configuration.txt
include/common/config.h
src/memory.c

index adc6efa25bf913f814a208a43fefab0a39458944..fe5eb25076c7fb05881472517f42b1d810a7ce0a 100644 (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
index 6fd85f893814d0ef15e6648caee4394169274a55..55ecd59010d37a57ce9edbaa0329f94248973be9 100644 (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
index 6cafd8b238eff66e3fc10c5a82341d887c6d5b24..04259a5390801f5873c4bcfd78dbdbdb88d27ab2 100644 (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