]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: debug: opportunistically load libthread_db.so.1 with set-dumpable=libs
authorWilly Tarreau <w@1wt.eu>
Wed, 18 Mar 2026 10:17:22 +0000 (11:17 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 18 Mar 2026 14:30:39 +0000 (15:30 +0100)
When loading libs into the core dump, let's also try to load
libthread_db.so.1 that gdb usually requires. It can significantly help
decoding the threads for systems which require it, and the file is quite
small. It can appear at a few different locations and is generally next
to libpthread.so, or alternately libc, so we first look where we found
them, and fall back to a few other common places. The file is really
small, a few tens of kB usually.

src/tools.c

index 879879c472e504134538d6b6677206bfdf3217c0..bae43643a7f7f41c8894261bce481b45cf9baa6b 100644 (file)
@@ -6038,6 +6038,7 @@ struct dl_collect_ctx {
        size_t size;
        char *prefix;
        int pos;
+       char libpthread_path[PATH_MAX];
 };
 
 static int dl_collect_libs_cb(struct dl_phdr_info *info, size_t size, void *data)
@@ -6066,6 +6067,18 @@ static int dl_collect_libs_cb(struct dl_phdr_info *info, size_t size, void *data
                load_file_into_tar(&ctx->storage, &ctx->size, ctx->prefix, dbg, NULL, "haproxy-libs-dump");
        }
 
+       /* check if we're loading libpthread or libc, and if so, keep a copy of its path */
+       if (!ctx->libpthread_path[0]) {
+               const char *basename = strrchr(fname, '/');
+
+               if (basename &&
+                   (strncmp(basename, "/libpthread.so", 14) == 0 ||
+                    strncmp(basename, "/libc.so", 8) == 0)) {
+                       /* Note: this will trim the trailing slash */
+                       strncpy(ctx->libpthread_path, fname,
+                               MIN(basename - fname, sizeof(ctx->libpthread_path)));
+               }
+       }
  leave:
        /* increment the object's number */
        ctx->pos++;
@@ -6075,11 +6088,13 @@ static int dl_collect_libs_cb(struct dl_phdr_info *info, size_t size, void *data
 /* dumps lib names and optionally address ranges */
 void collect_libs(void)
 {
-       struct dl_collect_ctx ctx = { .storage = NULL, .size = 0, .pos = 0 };
+       struct dl_collect_ctx ctx = { .storage = NULL, .size = 0, .pos = 0, .libpthread_path = "" };
+       const char *libthr_paths[] = { ctx.libpthread_path, "/usr/lib64", "/lib64", "/usr/lib", "/lib", NULL };
        ulong pagesize = sysconf(_SC_PAGESIZE);
        char dir_name[16];
        size_t new_size;
        void *page;
+       int i;
 
        /* prepend a directory named after the starting pid */
        snprintf(dir_name, sizeof(dir_name), "core-%u", getpid());
@@ -6088,6 +6103,22 @@ void collect_libs(void)
        /* callbacks will (re-)allocate ctx->storage */
        dl_iterate_phdr(dl_collect_libs_cb, &ctx);
 
+       /* if we've found libpthread, there's likely a libthread_db.so.1 next
+        * to it, for use with gdb, and ctx.libpthread_path will point to it,
+        * and with it, libthr_paths[0]. Otherwise we search in a few other
+        * common paths.
+        */
+       for (i = 0; libthr_paths[i]; i++) {
+               char path[PATH_MAX];
+
+               if (!*libthr_paths[i])
+                       continue;
+
+               snprintf(path, sizeof(path), "%s/libthread_db.so.1", DISGUISE(libthr_paths[i]));
+               if (load_file_into_tar(&ctx.storage, &ctx.size, ctx.prefix, path, NULL, "haproxy-libs-dump") == 0)
+                       break;
+       }
+
        /* 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