From: kurt@intricatesoftware.com Subject: extend ptrace(2) PT_GET_THREAD_* to include thread names To: cjeker@diehard.n-r-g.com, jca@wxcvbn.org, mark.kettenis@xs4all.nl, pascal@stumpf.co, ports@openbsd.org, tech@openbsd.org Date: Wed, 10 Dec 2025 16:41:27 +0000 Instead of gdb using sysctl(2) to get thread names let's extend ptrace(2) PT_GET_THREAD_* to include thread names. This allows gdb to use ptrace for both thread names and thread is alive detection. I'm using a new define larger then _MAXCOMLEN to avoid that define and header from propagating in to ptrace.h as well. The diff for gdb to use this and remove sysctl use follows below as well. This would be committed a few days after the pthread change is committed. okay for both? Index: sys/sys/ptrace.h =================================================================== RCS file: /cvs/src/sys/sys/ptrace.h,v diff -u -p -u -r1.16 ptrace.h --- sys/sys/ptrace.h 16 Mar 2020 11:58:46 -0000 1.16 +++ sys/sys/ptrace.h 10 Dec 2025 15:56:13 -0000 @@ -82,8 +82,11 @@ typedef struct ptrace_state { #define PT_GET_THREAD_FIRST 15 #define PT_GET_THREAD_NEXT 16 +#define PT_PTS_NAMELEN 32 /* must be >= sizeof(p_name) in struct proc */ + struct ptrace_thread_state { pid_t pts_tid; + char pts_name[PT_PTS_NAMELEN]; }; #define PT_FIRSTMACH 32 /* for machine-specific requests */ Index: sys/kern/sys_process.c =================================================================== RCS file: /cvs/src/sys/kern/sys_process.c,v diff -u -p -u -r1.106 sys_process.c --- sys/kern/sys_process.c 17 Feb 2025 15:45:55 -0000 1.106 +++ sys/kern/sys_process.c 10 Dec 2025 15:56:13 -0000 @@ -605,8 +605,10 @@ ptrace_kstate(struct proc *p, int req, p if (t == NULL) pts->pts_tid = -1; - else + else { pts->pts_tid = t->p_tid + THREAD_PID_OFFSET; + strlcpy(pts->pts_name, t->p_name, sizeof(pts->pts_name)); + } return 0; } } Index: Makefile =================================================================== RCS file: /cvs/ports/devel/gdb/Makefile,v diff -u -p -u -r1.98 Makefile --- Makefile 4 Dec 2025 18:28:32 -0000 1.98 +++ Makefile 10 Dec 2025 16:35:55 -0000 @@ -2,7 +2,7 @@ COMMENT= GNU debugger CATEGORIES= devel DISTNAME= gdb-16.3 -REVISION= 0 +REVISION= 1 HOMEPAGE= https://www.gnu.org/software/gdb/ Index: patches/patch-gdb_obsd-nat_c =================================================================== RCS file: /cvs/ports/devel/gdb/patches/patch-gdb_obsd-nat_c,v diff -u -p -u -r1.1 patch-gdb_obsd-nat_c --- patches/patch-gdb_obsd-nat_c 4 Dec 2025 18:28:32 -0000 1.1 +++ patches/patch-gdb_obsd-nat_c 10 Dec 2025 16:35:55 -0000 @@ -1,15 +1,9 @@ -Add support for thread_name. +Add support for thread_name and thread_alive using ptrace. Index: gdb/obsd-nat.c --- gdb/obsd-nat.c.orig +++ gdb/obsd-nat.c -@@ -23,11 +23,13 @@ - - #include - #include -+#include - #include "gdbsupport/gdb_wait.h" - +@@ -28,6 +28,7 @@ #include "inf-ptrace.h" #include "obsd-nat.h" #include "gdbsupport/eintr.h" @@ -17,50 +11,131 @@ Index: gdb/obsd-nat.c /* OpenBSD 5.2 and later include rthreads which uses a thread model that maps userland threads directly onto kernel threads in a 1:1 -@@ -183,4 +185,69 @@ int - obsd_nat_target::remove_fork_catchpoint (int pid) - { - return 0; -+} -+ +@@ -42,34 +43,78 @@ obsd_nat_target::pid_to_str (ptid_t ptid) + return normal_pid_to_str (ptid); + } + +-void +-obsd_nat_target::update_thread_list () +/* Generic thread lister within a specified PID. The CALLBACK + parameters is a C++ function that is called for each detected thread. + When the CALLBACK function returns true, the iteration is interrupted. + -+ This function assumes internally that the queried process is stopped -+ and the number of threads does not change between two sysctl () calls. */ ++ This function assumes internally that the queried process is stopped. */ + +static bool +obsd_thread_lister (const pid_t pid, -+ gdb::function_view ++ gdb::function_view + callback) -+{ -+ int mib[6] = {CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_SHOW_THREADS, -+ pid, sizeof(struct kinfo_proc), 0}; -+ size_t size; -+ -+ if (sysctl (mib, ARRAY_SIZE (mib), NULL, &size, NULL, 0) == -1 || size == 0) -+ perror_with_name (("sysctl")); -+ -+ mib[5] = size / sizeof (struct kinfo_proc); -+ -+ gdb::unique_xmalloc_ptr ki -+ ((struct kinfo_proc *) xcalloc (mib[5], sizeof (struct kinfo_proc))); -+ -+ if (sysctl (mib, ARRAY_SIZE (mib), ki.get (), &size, NULL, 0) == -1 -+ || size == 0) -+ perror_with_name (("sysctl")); -+ -+ for (size_t i = 0; i < size / sizeof (struct kinfo_proc); i++) -+ { -+ struct kinfo_proc *l = &ki[i]; -+ if (callback (l)) + { +- pid_t pid = inferior_ptid.pid (); + struct ptrace_thread_state pts; + +- prune_threads (); +- + if (ptrace (PT_GET_THREAD_FIRST, pid, (caddr_t)&pts, sizeof pts) == -1) + perror_with_name (("ptrace")); + + while (pts.pts_tid != -1) + { +- ptid_t ptid = ptid_t (pid, pts.pts_tid, 0); +- +- if (!in_thread_list (this, ptid)) +- { +- if (inferior_ptid.lwp () == 0) +- thread_change_ptid (this, inferior_ptid, ptid); +- else +- add_thread (this, ptid); +- } +- ++ if (callback (&pts)) + return true; -+ } + if (ptrace (PT_GET_THREAD_NEXT, pid, (caddr_t)&pts, sizeof pts) == -1) + perror_with_name (("ptrace")); + } + + return false; + } + ++/* Fuction to support executing callback for each alive thread */ ++ ++static void ++for_each_thread (pid_t pid, gdb::function_view callback) ++{ ++ auto fn ++ = [=, &callback] (const struct ptrace_thread_state *pts) ++ { ++ ptid_t ptid = ptid_t (pid, pts->pts_tid, 0); ++ callback (ptid); ++ return false; ++ }; ++ ++ obsd_thread_lister (pid, fn); +} + ++/* Implement the "post_attach" target_ops method. */ ++ ++static void ++obsd_add_threads (obsd_nat_target *target, pid_t pid) ++{ ++ auto fn ++ = [&target] (ptid_t ptid) ++ { ++ if (!in_thread_list (target, ptid)) ++ { ++ if (inferior_ptid.lwp () == 0) ++ thread_change_ptid (target, inferior_ptid, ptid); ++ else ++ add_thread (target, ptid); ++ } ++ }; ++ ++ for_each_thread (pid, fn); ++} ++ ++void ++obsd_nat_target::update_thread_list () ++{ ++ pid_t pid = inferior_ptid.pid (); ++ ++ prune_threads (); ++ obsd_add_threads (this, pid); ++} ++ + /* Enable additional event reporting on a new or existing process. */ + + static void +@@ -143,6 +188,7 @@ void + obsd_nat_target::post_attach (int pid) + { + obsd_enable_proc_events (pid); ++ obsd_add_threads (this, pid); + } + + /* Implement the virtual inf_ptrace_target::post_startup_inferior method. */ +@@ -184,3 +230,49 @@ obsd_nat_target::remove_fork_catchpoint (int pid) + { + return 0; + } ++ ++/* See obsd-nat.h. */ ++ ++bool ++obsd_nat_target::thread_alive (ptid_t ptid) ++{ ++ pid_t pid = ptid.pid (); ++ ptid_t::lwp_type tid = ptid.lwp (); ++ ++ auto fn ++ = [=] (const struct ptrace_thread_state *pts) ++ { ++ return pts->pts_tid == tid; ++ }; ++ ++ return obsd_thread_lister (pid, fn); ++} ++ ++#ifdef PT_PTS_NAMELEN +/* See obsd-nat.h. */ + +const char * @@ -69,14 +144,14 @@ Index: gdb/obsd-nat.c + pid_t pid = thr->ptid.pid (); + ptid_t::lwp_type tid = thr->ptid.lwp (); + -+ static char buf[KI_MAXCOMLEN] = {}; ++ static char buf[PT_PTS_NAMELEN] = {}; + + auto fn -+ = [=] (const struct kinfo_proc *ki) ++ = [=] (const struct ptrace_thread_state *pts) + { -+ if (ki->p_tid == tid) ++ if (pts->pts_tid == tid) + { -+ xsnprintf (buf, sizeof buf, "%s", ki->p_name); ++ xsnprintf (buf, sizeof buf, "%s", pts->pts_name); + return true; + } + return false; @@ -86,4 +161,5 @@ Index: gdb/obsd-nat.c + return buf; + else + return NULL; - } ++} ++#endif Index: patches/patch-gdb_obsd-nat_h =================================================================== RCS file: /cvs/ports/devel/gdb/patches/patch-gdb_obsd-nat_h,v diff -u -p -u -r1.1 patch-gdb_obsd-nat_h --- patches/patch-gdb_obsd-nat_h 4 Dec 2025 18:28:32 -0000 1.1 +++ patches/patch-gdb_obsd-nat_h 10 Dec 2025 16:35:55 -0000 @@ -1,13 +1,16 @@ -Add support for thread_name. +Add support for thread_name and thread_alive. Index: gdb/obsd-nat.h --- gdb/obsd-nat.h.orig +++ gdb/obsd-nat.h -@@ -27,6 +27,7 @@ class obsd_nat_target : public inf_ptrace_target +@@ -27,6 +27,10 @@ class obsd_nat_target : public inf_ptrace_target /* Override some methods to support threads. */ std::string pid_to_str (ptid_t) override; void update_thread_list () override; ++ bool thread_alive (ptid_t ptid) override; ++#ifdef PT_PTS_NAMELEN + const char *thread_name (struct thread_info *thr) override; ++#endif ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override; void follow_fork (inferior *inf, ptid_t, target_waitkind, bool, bool) override;