configuration. See also "server-state-base" and "show servers state",
"load-server-state-from-file" and "server-state-file-name"
-set-dumpable [ on | off ]
+set-dumpable [ on | off | libs ]
+ This option helps choose the core dump behavior in case of process crash.
+ Available options are:
+
+ - on this enables core dumping at the process level if it was
+ previously disabled.
+
+ - off this disables a previously enabled core dumping.
+
+ - libs this enables core dumping with an embedded copy of the binaries and
+ libraries that are required for debugging. This may be requested by
+ developers. In this case haproxy will try to load the libraries it
+ depends on into memory and keep them preciously. If the process
+ crashes, they will be dumped into the core so there is no need for
+ retrieving them from the file system anymore and no risk that they
+ do not match the core. This takes a few megabytes to a few tens of
+ megabytes of additional RAM, so it is better not to use it on small
+ systems.
+
This option is better left disabled by default and enabled only upon a
developer's request. By default it is disabled. Without argument, it defaults
to "on". If it has been enabled, it may still be forcibly disabled by prefixing
#define GTUNE_DISABLE_H2_WEBSOCKET (1<<21)
#define GTUNE_DISABLE_ACTIVE_CLOSE (1<<22)
#define GTUNE_QUICK_EXIT (1<<23)
-/* (1<<24) unused */
+#define GTUNE_COLLECT_LIBS (1<<24)
/* (1<<25) unused */
#define GTUNE_USE_FAST_FWD (1<<26)
#define GTUNE_LISTENER_MQ_FAIR (1<<27)
extern int fileless_mode;
extern struct cfgfile fileless_cfg;
+/* storage for collected libs */
+extern void *lib_storage;
+extern size_t lib_size;
+
struct proxy;
struct server;
int main(int argc, char **argv);
void *get_sym_curr_addr(const char *name);
void *get_sym_next_addr(const char *name);
int dump_libs(struct buffer *output, int with_addr);
+void collect_libs(void);
/* Note that this may result in opening libgcc() on first call, so it may need
* to have been called once before chrooting.
}
if (!*args[1] || strcmp(args[1], "on") == 0)
global.tune.options |= GTUNE_SET_DUMPABLE;
+ else if (strcmp(args[1], "libs") == 0)
+ global.tune.options |= GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS;
else if (strcmp(args[1], "off") == 0)
global.tune.options &= ~GTUNE_SET_DUMPABLE;
else {
unsigned int experimental_directives_allowed = 0;
unsigned int deprecated_directives_allowed = 0;
+/* mapped storage for collected libs */
+void *lib_storage = NULL;
+size_t lib_size = 0;
+
int check_kw_experimental(struct cfg_keyword *kw, const char *file, int linenum,
char **errmsg)
{
chunk_appendf(&trash, "TARGET='%s'", pm_target_opts);
post_mortem_add_component("haproxy", haproxy_version, cc, cflags, opts, argv[0]);
+
+ if ((global.tune.options & (GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS)) ==
+ (GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS))
+ collect_libs();
}
/* This is a third part of the late init sequence, where we register signals for
dl_iterate_phdr(dl_dump_libs_cb, &ctx);
return output->data != old_data;
}
+
+/* the private <data> we pass below is a dump context initialized like this */
+struct dl_collect_ctx {
+ char *storage;
+ size_t size;
+ char *prefix;
+ int pos;
+};
+
+static int dl_collect_libs_cb(struct dl_phdr_info *info, size_t size, void *data)
+{
+ struct dl_collect_ctx *ctx = data;
+ const char *fname;
+
+ if (!info || !info->dlpi_name)
+ goto leave;
+
+ if (!*info->dlpi_name)
+ fname = get_exec_path();
+ else if (strchr(info->dlpi_name, '/'))
+ fname = info->dlpi_name;
+ else
+ /* else it's a VDSO or similar and we're not interested */
+ goto leave;
+
+ load_file_into_tar(&ctx->storage, &ctx->size, ctx->prefix, fname, NULL, "haproxy-libs-dump");
+ leave:
+ /* increment the object's number */
+ ctx->pos++;
+ return 0;
+}
+
+/* dumps lib names and optionally address ranges */
+void collect_libs(void)
+{
+ struct dl_collect_ctx ctx = { .storage = NULL, .size = 0, .pos = 0 };
+ ulong pagesize = sysconf(_SC_PAGESIZE);
+ char dir_name[16];
+ size_t new_size;
+ void *page;
+
+ /* prepend a directory named after the starting pid */
+ snprintf(dir_name, sizeof(dir_name), "core-%u", getpid());
+ ctx.prefix = dir_name;
+
+ /* callbacks will (re-)allocate ctx->storage */
+ dl_iterate_phdr(dl_collect_libs_cb, &ctx);
+
+ /* now that the archive is complete, we need to close it by appending
+ * two empty 512B blocks. We'll also place it aligned in an isolated
+ * mapped area so that it uses its own segment in a core dump for
+ * easier locating. In order to do this, we'll allocate two extra
+ * pages and will punch holes around.
+ */
+ new_size = (ctx.size + 2*512 + 2*pagesize + pagesize - 1) & -pagesize;
+ page = mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ if (page != MAP_FAILED) {
+ /* punch holes around that won't go into the core */
+ mprotect(page, pagesize, PROT_NONE);
+ mprotect(page + new_size - pagesize, pagesize, PROT_NONE);
+ new_size -= 2*pagesize;
+ page += pagesize;
+ /* copy and make read-only */
+ memcpy(page, ctx.storage, ctx.size);
+ mprotect(page, lib_size, PROT_READ);
+ vma_set_name(page, new_size, "archive", "boot-libs");
+
+ lib_storage = page;
+ lib_size = new_size;
+ }
+
+ /* don't need the temporary storage anymore */
+ ha_free(&ctx.storage);
+}
# else // no DL_ITERATE_PHDR
# error "No dump_libs() function for this platform"
# endif
return 0;
}
+/* unsupported platform: do not collect anything */
+void collect_libs(void)
+{
+}
+
#endif // HA_HAVE_DUMP_LIBS
/*