$ echo "show info json" | socat /var/run/haproxy.sock stdio | \
python -m json.tool
+show libs
+ Dump the list of loaded shared dynamic libraries and object files, on systems
+ that support it. When available, for each shared object the range of virtual
+ addresses will be indicated, the size and the path to the object. This can be
+ used for example to try to estimate what library provides a function that
+ appears in a dump. Note that on many systems, addresses will change upon each
+ restart (address space randomization), so that this list would need to be
+ retrieved upon startup if it is expected to be used to analyse a core file.
+ This command may only be issued on sockets configured for levels "operator"
+ or "admin". Note that the output format may vary between operating systems,
+ architectures and even haproxy versions, and ought not to be relied on in
+ scripts.
+
show map [[@<ver>] <map>]
Dump info about map converters. Without argument, the list of all available
maps is returned. If a <map> is specified, its contents are dumped. <map> is
/* dl_iterate_phdr() is available in GLIBC 2.2.4 and up. Let's round up to 2.3.x */
#if defined(USE_DL) && defined(__GNU_LIBRARY__) && (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)
#define HA_HAVE_DL_ITERATE_PHDR
+#define HA_HAVE_DUMP_LIBS
#endif
/* malloc_trim() can be very convenient to reclaim unused memory especially
const char *get_exec_path(void);
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);
/* Note that this may result in opening libgcc() on first call, so it may need
* to have been called once before chrooting.
return 1;
}
+#if defined(HA_HAVE_DUMP_LIBS)
+/* parse a "show libs" command. It returns 1 if it emits anything otherwise zero. */
+static int debug_parse_cli_show_libs(char **args, char *payload, struct appctx *appctx, void *private)
+{
+ if (!cli_has_level(appctx, ACCESS_LVL_OPER))
+ return 1;
+
+ chunk_reset(&trash);
+ if (dump_libs(&trash, 1))
+ return cli_msg(appctx, LOG_INFO, trash.area);
+ else
+ return 0;
+}
+#endif
+
/* dumps a state of all threads into the trash and on fd #2, then aborts. */
void ha_panic()
{
{{ "debug", "dev", "sym", NULL }, "debug dev sym <addr> : resolve symbol address", debug_parse_cli_sym, NULL, NULL, NULL, ACCESS_EXPERT },
{{ "debug", "dev", "tkill", NULL }, "debug dev tkill [thr] [sig] : send signal to thread", debug_parse_cli_tkill, NULL, NULL, NULL, ACCESS_EXPERT },
{{ "debug", "dev", "write", NULL }, "debug dev write [size] : write that many bytes in return", debug_parse_cli_write, NULL, NULL, NULL, ACCESS_EXPERT },
+#if defined(HA_HAVE_DUMP_LIBS)
+ {{ "show", "libs", NULL, NULL }, "show libs : show loaded object files and libraries", debug_parse_cli_show_libs, NULL, NULL },
+#endif
{{ "show", "threads", NULL, NULL }, "show threads : show some threads debugging information", NULL, cli_io_handler_show_threads, NULL },
{{},}
}};
return NULL;
}
+/* On systems where this is supported, let's provide a possibility to enumerate
+ * the list of object files. The output is appended to a buffer initialized by
+ * the caller, with one name per line. A trailing zero is always emitted if data
+ * are written. Only real objects are dumped (executable and .so libs). The
+ * function returns non-zero if it dumps anything. These functions do not make
+ * use of the trash so that it is possible for the caller to call them with the
+ * trash on input. The output format may be platform-specific but at least one
+ * version must emit raw object file names when argument is zero.
+ */
+#if defined(HA_HAVE_DUMP_LIBS)
+# if defined(HA_HAVE_DL_ITERATE_PHDR)
+/* the private <data> we pass below is a dump context initialized like this */
+struct dl_dump_ctx {
+ struct buffer *buf;
+ int with_addr;
+};
+
+static int dl_dump_libs_cb(struct dl_phdr_info *info, size_t size, void *data)
+{
+ struct dl_dump_ctx *ctx = data;
+ const char *fname;
+ size_t p1, p2, beg, end;
+ int idx;
+
+ 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;
+
+ if (!ctx->with_addr)
+ goto dump_name;
+
+ /* virtual addresses are relative to the load address and are per
+ * pseudo-header, so we have to scan them all to find the furthest
+ * one from the beginning. In this case we only dump entries if
+ * they have at least one section.
+ */
+ beg = ~0; end = 0;
+ for (idx = 0; idx < info->dlpi_phnum; idx++) {
+ if (!info->dlpi_phdr[idx].p_memsz)
+ continue;
+ p1 = info->dlpi_phdr[idx].p_vaddr;
+ if (p1 < beg)
+ beg = p1;
+ p2 = p1 + info->dlpi_phdr[idx].p_memsz - 1;
+ if (p2 > end)
+ end = p2;
+ }
+
+ if (!idx)
+ goto leave;
+
+ chunk_appendf(ctx->buf, "0x%012llx-0x%012llx (0x%07llx) ",
+ (ullong)info->dlpi_addr + beg,
+ (ullong)info->dlpi_addr + end,
+ (ullong)(end - beg + 1));
+ dump_name:
+ chunk_appendf(ctx->buf, "%s\n", fname);
+ leave:
+ return 0;
+}
+
+/* dumps lib names and optionally address ranges */
+int dump_libs(struct buffer *output, int with_addr)
+{
+ struct dl_dump_ctx ctx = { .buf = output, .with_addr = with_addr };
+ size_t old_data = output->data;
+
+ dl_iterate_phdr(dl_dump_libs_cb, &ctx);
+ return output->data != old_data;
+}
+# else // no DL_ITERATE_PHDR
+# error "No dump_libs() function for this platform"
+# endif
+#else // no HA_HAVE_DUMP_LIBS
+
+/* unsupported platform: do not dump anything */
+int dump_libs(struct buffer *output, int with_addr)
+{
+ return 0;
+}
+
+#endif // HA_HAVE_DUMP_LIBS
+
/*
* Allocate an array of unsigned int with <nums> as address from <str> string
* made of integer separated by dot characters.