MINOR: pools: support setting debugging options using -dM

The 9 currently available debugging options may now be checked, set, or
cleared using -dM. The directive now takes a comma-delimited list of
options after the optional poisonning byte. With "help", the list of
available options is displayed with a short help and their current
status.

The management doc was updated.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index b516add..0c62b56 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -2531,10 +2531,10 @@
   this value. The default value is 1.
 
 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.
+  If compiled with DEBUG_FAIL_ALLOC or started with "-dMfail", 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.fd.edge-triggered { on | off }  [ EXPERIMENTAL ]
   Enables ('on') or disables ('off') the edge-triggered polling mode for FDs
diff --git a/doc/management.txt b/doc/management.txt
index e7f3962..e3a4abc 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -227,7 +227,8 @@
 
        ./haproxy -W -q -c -dL -f foo.cfg | tar -T - -hzcf archive.tgz
 
-  -dM[<byte>] : forces memory poisoning, which means that each and every
+  -dM[<byte>[,]][help|options,...] : forces memory poisoning, and/or changes
+    memory other debugging options. Memory poisonning means that each and every
     memory region allocated with malloc() or pool_alloc() will be filled with
     <byte> before being passed to the caller. When <byte> is not specified, it
     defaults to 0x50 ('P'). While this slightly slows down operations, it is
@@ -235,7 +236,74 @@
     the code that cause random crashes. Note that -dM0 has the effect of
     turning any malloc() into a calloc(). In any case if a bug appears or
     disappears when using this option it means there is a bug in haproxy, so
-    please report it.
+    please report it. A number of other options are available either alone or
+    after a comma following the byte. The special option "help" will list the
+    currently supported options and their current value. Each debugging option
+    may be forced on or off. The most optimal options are usually chosen at
+    build time based on the operating system and do not need to be adjusted,
+    unless suggested by a developer. Supported debugging options include
+    (set/clear):
+      - fail / no-fail:
+        This enables randomly failing memory allocations, in conjunction with
+        the global "tune.fail-alloc" setting. This is used to detect missing
+        error checks in the code.
+
+      - no-merge / merge:
+        By default, pools of very similar sizes are merged, resulting in more
+        efficiency, but this complicates the analysis of certain memory dumps.
+        This option allows to disable this mechanism, and may slightly increase
+        the memory usage.
+
+      - cold-first / hot-first:
+        In order to optimize the CPU cache hit ratio, by default the most
+        recently released objects ("hot") are recycled for new allocations.
+        But doing so also complicates analysis of memory dumps and may hide
+        use-after-free bugs. This option allows to instead pick the coldest
+        objects first, which may result in a slight increase of CPU usage.
+
+      - integrity / no-integrity:
+        When this option is enabled, memory integrity checks are enabled on
+        the allocated area to verify that it hasn't been modified since it was
+        last released. This works best with "no-merge", "cold-first" and "tag".
+        Enabling this option will slightly increase the CPU usage.
+
+      - no-global / global:
+        Depending on the operating system, a process-wide global memory cache
+        may be enabled if it is estimated that the standard allocator is too
+        slow or inefficient with threads. This option allows to forcefully
+        disable it or enable it. Disabling it may result in a CPU usage
+        increase with inefficient allocators. Enabling it may result in a
+        higher memory usage with efficient allocators.
+
+      - no-cache / cache:
+        Each thread uses a very fast local object cache for allocations, which
+        is always enabled by default. This option allows to disable it. Since
+        the global cache also passes via the local caches, this will
+        effectively result in disabling all caches and allocating directly from
+        the default allocator. This may result in a significant increase of CPU
+        usage, but may also result in small memory savings on tiny systems.
+
+      - caller / no-caller:
+        Enabling this option reserves some extra space in each allocated object
+        to store the address of the last caller that allocated or released it.
+        This helps developers go back in time when analysing memory dumps and
+        to guess how something unexpected happened.
+
+      - tag / no-tag:
+        Enabling this option reserves some extra space in each allocated object
+        to store a tag that allows to detect bugs such as double-free, freeing
+        an invalid object, and buffer overflows. It offers much stronger
+        reliability guarantees at the expense of 4 or 8 extra bytes per
+        allocation. It usually is the first step to detect memory corruption.
+
+      - poison / no-poison:
+        Enabling this option will fill allocated objects with a fixed pattern
+        that will make sure that some accidental values such as 0 will not be
+        present if a newly added field was mistakenly forgotten in an
+        initialization routine. Such bugs tend to rarely reproduce, especially
+        when pools are not merged. This is normally enabled by directly passing
+        the byte's value to -dM but using this option allows to disable/enable
+        use of a previously set value.
 
   -dS : disable use of the splice() system call. It is equivalent to the
     "global" section's "nosplice" keyword. This may be used when splice() is
diff --git a/src/haproxy.c b/src/haproxy.c
index 030a973..0010e90 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -564,7 +564,7 @@
 		"        [ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- <cfgfile>*]\n"
 		"        -v displays version ; -vv shows known build options.\n"
 		"        -d enters debug mode ; -db only disables background mode.\n"
-		"        -dM[<byte>] poisons memory with <byte> (defaults to 0x50)\n"
+		"        -dM[<byte>,help,...] debug memory (default: poison with <byte>/0x50)\n"
 		"        -V enters verbose mode (disables quiet mode)\n"
 		"        -D goes daemon ; -C changes to <dir> before loading files.\n"
 		"        -W master-worker mode.\n"
diff --git a/src/pool.c b/src/pool.c
index 4998f09..0e33cd3 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -62,6 +62,25 @@
 #endif
 	0;
 
+static const struct {
+	uint flg;
+	const char *set;
+	const char *clr;
+	const char *hlp;
+} dbg_options[] = {
+	/* flg,                 set,          clr,            hlp */
+	{ POOL_DBG_FAIL_ALLOC, "fail",       "no-fail",      "randomly fail allocations" },
+	{ POOL_DBG_DONT_MERGE, "no-merge",   "merge",        "disable merging of similar pools" },
+	{ POOL_DBG_COLD_FIRST, "cold-first", "hot-first",    "pick cold objects first" },
+	{ POOL_DBG_INTEGRITY,  "integrity",  "no-integrity", "enable cache integrity checks" },
+	{ POOL_DBG_NO_GLOBAL,  "no-global",  "global",       "disable global shared cache" },
+	{ POOL_DBG_NO_CACHE,   "no-cache",   "cache",        "disable thread-local cache" },
+	{ POOL_DBG_CALLER,     "caller",     "no-caller",    "save caller information in cache" },
+	{ POOL_DBG_TAG,        "tag",        "no-tag",       "add tag at end of allocated objects" },
+	{ POOL_DBG_POISON,     "poison",     "no-poison",    "poison newly allocated objects" },
+	{ 0 /* end */ }
+};
+
 static int mem_fail_rate __read_mostly = 0;
 static int using_default_allocator __read_mostly = 1;
 static int(*my_mallctl)(const char *, void *, size_t *, void *, size_t) = NULL;
@@ -902,7 +921,9 @@
  */
 int pool_parse_debugging(const char *str, char **err)
 {
+	struct ist args;
 	char *end;
+	uint new_dbg;
 	int v;
 
 
@@ -916,6 +937,55 @@
 			pool_debugging &= ~POOL_DBG_POISON;
 		str = end;
 	}
+
+	new_dbg = pool_debugging;
+
+	for (args = ist(str); istlen(args); args = istadv(istfind(args, ','), 1)) {
+		struct ist feat = iststop(args, ',');
+
+		if (!istlen(feat))
+			continue;
+
+		if (isteq(feat, ist("help"))) {
+			ha_free(err);
+			memprintf(err,
+				  "-dM alone enables memory poisonning with byte 0x50 on allocation. A numeric\n"
+				  "value may be appended immediately after -dM to use another value (0 supported).\n"
+				  "Then an optional list of comma-delimited keywords may be appended to set or\n"
+				  "clear some debugging options ('*' marks the current setting):\n\n"
+				  "    set               clear            description\n"
+				  "  -----------------+-----------------+-----------------------------------------\n");
+
+			for (v = 0; dbg_options[v].flg; v++) {
+				memprintf(err, "%s  %c %-15s|%c %-15s| %s\n",
+					  *err,
+					  (pool_debugging & dbg_options[v].flg) ? '*' : ' ',
+					  dbg_options[v].set,
+					  (pool_debugging & dbg_options[v].flg) ? ' ' : '*',
+					  dbg_options[v].clr,
+					  dbg_options[v].hlp);
+			}
+			return -1;
+		}
+
+		for (v = 0; dbg_options[v].flg; v++) {
+			if (isteq(feat, ist(dbg_options[v].set))) {
+				new_dbg |= dbg_options[v].flg;
+				break;
+			}
+			else if (isteq(feat, ist(dbg_options[v].clr))) {
+				new_dbg &= ~dbg_options[v].flg;
+				break;
+			}
+		}
+
+		if (!dbg_options[v].flg) {
+			memprintf(err, "unknown pool debugging feature <%.*s>", (int)istlen(feat), istptr(feat));
+			return -2;
+		}
+	}
+
+	pool_debugging = new_dbg;
 	return 1;
 }