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;
}