From: noxiouz Date: Fri, 13 Mar 2026 00:36:08 +0000 (+0000) Subject: coredump: capture crashing thread ID and name X-Git-Tag: v261-rc1~823 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4c7af7d9d1cd40e2f36a87787ce2fab32400bf89;p=thirdparty%2Fsystemd.git coredump: capture crashing thread ID and name Add %I (TID in initial PID namespace) to the core_pattern, so the kernel passes the crashing thread's TID to systemd-coredump. Use it to read the thread's comm name from /proc//comm and log both as new journal fields: COREDUMP_TID= — TID of the crashing thread COREDUMP_THREAD_NAME= — comm name of the crashing thread These fields are also stored as xattrs on external coredump files (user.coredump.tid, user.coredump.thread_name) and displayed by coredumpctl info alongside the PID line. For single-threaded processes the TID equals the PID and thread_name equals comm; for multi-threaded programs with named worker threads (pthread_setname_np / PR_SET_NAME) this identifies which thread crashed without needing to open the coredump file itself. The new fields are optional in the socket forwarding path, so older systemd-coredump senders are handled gracefully. Co-developed-by: Claude --- diff --git a/src/coredump/coredump-context.c b/src/coredump/coredump-context.c index 921cfe5de76..574d3ecd529 100644 --- a/src/coredump/coredump-context.c +++ b/src/coredump/coredump-context.c @@ -32,10 +32,12 @@ static const char * const metadata_field_table[_META_MAX] = { [META_ARGV_HOSTNAME] = "COREDUMP_HOSTNAME=", [META_ARGV_DUMPABLE] = "COREDUMP_DUMPABLE=", [META_ARGV_PIDFD] = "COREDUMP_BY_PIDFD=", + [META_ARGV_TID] = "COREDUMP_TID=", [META_COMM] = "COREDUMP_COMM=", [META_EXE] = "COREDUMP_EXE=", [META_UNIT] = "COREDUMP_UNIT=", [META_PROC_AUXV] = "COREDUMP_PROC_AUXV=", + [META_THREAD_NAME] = "COREDUMP_THREAD_NAME=", }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(metadata_field, MetadataField); @@ -49,6 +51,7 @@ void coredump_context_done(CoredumpContext *context) { free(context->exe); free(context->unit); free(context->auxv); + free(context->thread_name); safe_close(context->mount_tree_fd); iovw_done_free(&context->iovw); safe_close(context->input_fd); @@ -228,6 +231,12 @@ int coredump_context_build_iovw(CoredumpContext *context) { if (r < 0) return log_error_errno(r, "Failed to add COREDUMP_COMM= field: %m"); + if (context->tid > 0) + (void) iovw_put_string_fieldf(&context->iovw, "COREDUMP_TID=", PID_FMT, context->tid); + + if (context->thread_name) + (void) iovw_put_string_field(&context->iovw, "COREDUMP_THREAD_NAME=", context->thread_name); + if (context->exe) (void) iovw_put_string_field(&context->iovw, "COREDUMP_EXE=", context->exe); @@ -339,6 +348,12 @@ static int coredump_context_parse_from_procfs(CoredumpContext *context) { if (r < 0) return log_error_errno(r, "Failed to get COMM: %m"); + if (context->tid > 0) { + r = pid_get_comm(context->tid, &context->thread_name); + if (r < 0) + log_warning_errno(r, "Failed to get comm for thread "PID_FMT", ignoring: %m", context->tid); + } + r = get_process_exe(pid, &context->exe); if (r < 0) log_warning_errno(r, "Failed to get EXE, ignoring: %m"); @@ -465,6 +480,12 @@ static int context_parse_one(CoredumpContext *context, MetadataField meta, bool context->got_pidfd = 1; return 0; } + case META_ARGV_TID: + r = parse_pid(s, &context->tid); + if (r < 0) + log_warning_errno(r, "Failed to parse TID \"%s\", ignoring: %m", s); + return 0; + case META_COMM: return free_and_strdup_warn(&context->comm, s); @@ -474,6 +495,9 @@ static int context_parse_one(CoredumpContext *context, MetadataField meta, bool case META_UNIT: return free_and_strdup_warn(&context->unit, s); + case META_THREAD_NAME: + return free_and_strdup_warn(&context->thread_name, s); + case META_PROC_AUXV: { char *t = memdup_suffix0(s, size); if (!t) diff --git a/src/coredump/coredump-context.h b/src/coredump/coredump-context.h index f2d44c141c6..7fedfde2adb 100644 --- a/src/coredump/coredump-context.h +++ b/src/coredump/coredump-context.h @@ -23,6 +23,7 @@ typedef enum MetadataField { META_ARGV_HOSTNAME = _META_ARGV_REQUIRED, /* %h: hostname */ META_ARGV_DUMPABLE, /* %d: as set by the kernel */ META_ARGV_PIDFD, /* %F: pidfd of the process, since v6.16 */ + META_ARGV_TID, /* %I: TID of the crashing thread, as seen in the initial pid namespace */ /* If new fields are added, they should be added here, to maintain compatibility * with callers which don't know about the new fields. */ _META_ARGV_MAX, @@ -40,6 +41,7 @@ typedef enum MetadataField { META_EXE, META_UNIT, META_PROC_AUXV, + META_THREAD_NAME, _META_MAX, _META_INVALID = -EINVAL, } MetadataField; @@ -53,11 +55,13 @@ struct CoredumpContext { uint64_t rlimit; /* META_ARGV_RLIMIT */ char *hostname; /* META_ARGV_HOSTNAME */ unsigned dumpable; /* META_ARGV_DUMPABLE */ + pid_t tid; /* META_ARGV_TID */ char *comm; /* META_COMM */ char *exe; /* META_EXE */ char *unit; /* META_UNIT */ char *auxv; /* META_PROC_AUXV */ size_t auxv_size; /* META_PROC_AUXV */ + char *thread_name; /* META_THREAD_NAME */ bool got_pidfd; /* META_ARGV_PIDFD */ bool same_pidns; bool forwarded; diff --git a/src/coredump/coredump-submit.c b/src/coredump/coredump-submit.c index 9134697d5b8..6cfbda0f46b 100644 --- a/src/coredump/coredump-submit.c +++ b/src/coredump/coredump-submit.c @@ -201,6 +201,10 @@ static int fix_xattr(int fd, const CoredumpContext *context) { RET_GATHER(r, fix_xattr_one(fd, "user.coredump.hostname", context->hostname)); RET_GATHER(r, fix_xattr_one(fd, "user.coredump.comm", context->comm)); RET_GATHER(r, fix_xattr_one(fd, "user.coredump.exe", context->exe)); + if (context->tid > 0) { + RET_GATHER(r, fix_xattr_format(fd, "user.coredump.tid", PID_FMT, context->tid)); + RET_GATHER(r, fix_xattr_one(fd, "user.coredump.thread_name", context->thread_name)); + } return r; } diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 64f7ae99a10..618a464fcc9 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -633,7 +633,8 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { *slice = NULL, *cgroup = NULL, *owner_uid = NULL, *message = NULL, *timestamp = NULL, *filename = NULL, *truncated = NULL, *coredump = NULL, - *pkgmeta_name = NULL, *pkgmeta_version = NULL, *pkgmeta_json = NULL; + *pkgmeta_name = NULL, *pkgmeta_version = NULL, *pkgmeta_json = NULL, + *tid = NULL, *thread_name = NULL; const void *d; size_t l; bool normal_coredump; @@ -667,6 +668,8 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { RETRIEVE(d, l, "COREDUMP_PACKAGE_NAME", pkgmeta_name); RETRIEVE(d, l, "COREDUMP_PACKAGE_VERSION", pkgmeta_version); RETRIEVE(d, l, "COREDUMP_PACKAGE_JSON", pkgmeta_json); + RETRIEVE(d, l, "COREDUMP_TID", tid); + RETRIEVE(d, l, "COREDUMP_THREAD_NAME", thread_name); RETRIEVE(d, l, "_BOOT_ID", boot_id); RETRIEVE(d, l, "_MACHINE_ID", machine_id); RETRIEVE(d, l, "MESSAGE", message); @@ -686,6 +689,13 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { " PID: %s%s%s\n", ansi_highlight(), strna(pid), ansi_normal()); + if (tid) { + if (thread_name) + fprintf(file, " TID: %s (%s)\n", tid, thread_name); + else + fprintf(file, " TID: %s\n", tid); + } + if (uid) { uid_t n; diff --git a/sysctl.d/50-coredump.conf.in b/sysctl.d/50-coredump.conf.in index fe8f7670b06..0e6f370f473 100644 --- a/sysctl.d/50-coredump.conf.in +++ b/sysctl.d/50-coredump.conf.in @@ -13,7 +13,7 @@ # the core dump. # # See systemd-coredump(8) and core(5). -kernel.core_pattern=|{{LIBEXECDIR}}/systemd-coredump %P %u %g %s %t %c %h %d %F +kernel.core_pattern=|{{LIBEXECDIR}}/systemd-coredump %P %u %g %s %t %c %h %d %F %I # Allow 16 coredumps to be dispatched in parallel by the kernel. # We collect metadata from /proc/%P/, and thus need to make sure the crashed diff --git a/test/units/TEST-87-AUX-UTILS-VM.coredump.sh b/test/units/TEST-87-AUX-UTILS-VM.coredump.sh index b3f702a2b0d..b3a3de76960 100755 --- a/test/units/TEST-87-AUX-UTILS-VM.coredump.sh +++ b/test/units/TEST-87-AUX-UTILS-VM.coredump.sh @@ -150,6 +150,11 @@ coredumpctl info foo bar baz "${CORE_TEST_BIN##*/}" coredumpctl info COREDUMP_EXE="$CORE_TEST_BIN" coredumpctl info COREDUMP_EXE=aaaaa COREDUMP_EXE= COREDUMP_EXE="$CORE_TEST_BIN" +# Check that COREDUMP_TID= is present and displayed by coredumpctl info +coredumpctl info "$CORE_TEST_BIN" | grep "TID:" >/dev/null +# Check the field is queryable in the journal +coredumpctl -F COREDUMP_TID + coredumpctl debug --debugger=/bin/true "$CORE_TEST_BIN" SYSTEMD_DEBUGGER=/bin/true coredumpctl debug "$CORE_TEST_BIN" coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_BIN##*/}"