]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: debug: add -dA to dump an archive of all dependencies
authorWilly Tarreau <w@1wt.eu>
Fri, 29 May 2026 09:39:05 +0000 (11:39 +0200)
committerWilly Tarreau <w@1wt.eu>
Mon, 1 Jun 2026 13:01:32 +0000 (15:01 +0200)
This adds "-dA[file]" on the command line, which dumps an archive of all
dependencies detected at runtime into the designated file in tar format.
This is equivalent to "set-dumpable libs", but instead of keeping the libs
in memory, it dumps them into a file. This may be used after a core dump,
in order to provide all necessary libraries to developers to permit them
to exploit the core. This may not be available on all operating systems.

doc/management.txt
include/haproxy/global.h
include/haproxy/tools.h
src/haproxy.c
src/tools.c

index 5dae7064aaff2ba8f138d53e92c0d7856fddbce5..dbdf5311024857417da10ad731cfc176765ba1aa 100644 (file)
@@ -215,6 +215,18 @@ list of options is :
     in foreground and to show incoming and outgoing events. It must never be
     used in an init script.
 
+  -dA[file] : dump an archive of all dependencies detected at boot time in the
+    designated file in tar format, immediately after the configuration is done
+    loading. This is equivalent to "set-dumpable libs", but instead of keeping
+    the libs in memory, it dumps them into a file. This may be used after a
+    core dump, in order to provide all necessary libraries to developers to
+    permit them to exploit the core. This may not be available on all operating
+    systems. It is highly recommended to use this with the regular
+    configuration files, and optionally with "-c" when used manually, to make
+    haproxy immediately exit after the dump, without starting. Example:
+
+       $ haproxy -dA/tmp/libs.tar -c -f /etc/haproxy/haproxy.cfg
+
   -dC[key] : dump the configuration file. It is performed after the lines are
     tokenized, so comments are stripped and indenting is forced. If a non-zero
     key is specified, lines are truncated before sensitive/confidential fields,
index 1510bfe0c6d4c54ec80595b68bd7e8b917f03503..854824419e4de52f5680030dd5350980317e6807 100644 (file)
@@ -61,6 +61,7 @@ extern struct cfgfile fileless_cfg;
 /* storage for collected libs */
 extern void *lib_storage;
 extern size_t lib_size;
+extern char *lib_output_file;
 
 struct proxy;
 struct server;
index ff1ba8bc1843f8d2a523aab4b3681ffbdf2d9a99..b00c21b3b29b7a716469e21a8ece991bc365d993 100644 (file)
@@ -1155,6 +1155,7 @@ void *get_sym_next_addr(const char *name);
 int dump_libs(struct buffer *output, int with_addr);
 void collect_libs(void);
 void free_collected_libs(void);
+int copy_libs_to_file(void);
 
 /* Note that this may result in opening libgcc() on first call, so it may need
  * to have been called once before chrooting.
index b38b13d78d68803bce6e2691effbc2d68c46b085..62c3262ead28c6eb04c95369f5d6ac56431fc2ee 100644 (file)
@@ -274,6 +274,7 @@ unsigned int deprecated_directives_allowed = 0;
 /* mapped storage for collected libs */
 void *lib_storage = NULL;
 size_t lib_size = 0;
+char *lib_output_file = NULL;
 
 int check_kw_experimental(struct cfg_keyword *kw, const char *file, int linenum,
                           char **errmsg)
@@ -784,6 +785,9 @@ static void usage(char *name)
 #if defined(HA_HAVE_DUMP_LIBS)
                "        -dL dumps loaded object files after config checks\n"
 #endif
+#if defined(HA_HAVE_DUMP_LIBS) && defined(HA_HAVE_DL_ITERATE_PHDR)
+               "        -dA[file] collects libs into a tar file at <file>\n"
+#endif
 #if defined(USE_CPU_AFFINITY)
                "        -dc dumps the list of selected and evicted CPUs\n"
 #endif
@@ -1627,6 +1631,16 @@ void haproxy_init_args(int argc, char **argv)
 #if defined(HA_HAVE_DUMP_LIBS)
                        else if (*flag == 'd' && flag[1] == 'L')
                                arg_mode |= MODE_DUMP_LIBS;
+# if defined(HA_HAVE_DL_ITERATE_PHDR)
+                       else if (*flag == 'd' && flag[1] == 'A') {
+                               lib_output_file = flag + 2;
+                               if (!*lib_output_file) {
+                                       ha_alert("-dA: missing output file name\n");
+                                       exit(1);
+                               }
+                               arg_mode |= MODE_DUMP_LIBS; // stop on libs dump
+                       }
+# endif /* HA_HAVE_DL_ITERATE_PHDR */
 #endif
                        else if (*flag == 'd' && flag[1] == 'K') {
                                arg_mode |= MODE_DUMP_KWD;
@@ -2312,6 +2326,17 @@ static void step_init_2(int argc, char** argv)
 
 #if defined(HA_HAVE_DUMP_LIBS)
        if (global.mode & MODE_DUMP_LIBS && !master) {
+# if defined(HA_HAVE_DL_ITERATE_PHDR)
+               if (lib_output_file) {
+                       /* we'll dump everything to lib_output_file */
+                       if (copy_libs_to_file() < 0)
+                               deinit_and_exit(1);
+                       /* release memory if no longer needed */
+                       if ((global.tune.options & (GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS)) !=
+                           (GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS))
+                               free_collected_libs();
+               }
+# endif
                qfprintf(stdout, "List of loaded object files:\n");
                chunk_reset(&trash);
                if (dump_libs(&trash, ((arg_mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_VERBOSE)))
index b9a0da783e74c703381ae8e8c36b9b0e9064d9d6..6c3dce7c9e857b2246e4a4b7aae93c9652a0cb18 100644 (file)
@@ -6101,6 +6101,10 @@ void collect_libs(void)
        void *page;
        int i;
 
+       /* already done */
+       if (lib_storage)
+               return;
+
        /* prepend a directory named after the starting pid */
        snprintf(dir_name, sizeof(dir_name), "core-%u", getpid());
        ctx.prefix = dir_name;
@@ -6162,6 +6166,39 @@ void free_collected_libs(void)
        lib_size = 0;
 }
 
+/* Prepare the archive in RAM and copy it to a target file. Returns <0 upon error. */
+int copy_libs_to_file(void)
+{
+       ssize_t len;
+       int ret = -1;
+       int fd = -1;
+
+       fd = open(lib_output_file, O_CREAT | O_WRONLY, S_IRWXU);
+       if (fd < 0) {
+               ha_alert("Cannot create output file to dump dependencies: %s.\n", strerror(errno));
+               goto fail;
+       }
+
+       collect_libs();
+       if (!lib_storage || !lib_size) {
+               ha_alert("Failed to collect dependencies.\n");
+               goto fail;
+       }
+
+       len = write(fd, lib_storage, lib_size);
+       if (len != lib_size) {
+               ha_alert("Failed to write dependencies to output file: %s.\n", strerror(errno));
+               goto fail;
+       }
+
+       /* OK done */
+       ret = 0;
+ fail:
+       if (fd >= 0)
+               close(fd);
+       return ret;
+}
+
 # else // no DL_ITERATE_PHDR
 #  error "No dump_libs() function for this platform"
 # endif
@@ -6183,6 +6220,12 @@ void free_collected_libs(void)
 {
 }
 
+/* unsupported platform: do not copy anything */
+int copy_libs_to_file(void)
+{
+       return -1;
+}
+
 #endif // HA_HAVE_DUMP_LIBS
 
 /*