Download raw body.
devel/gdb: implement thread_alive
On Mon, Dec 08, 2025 at 12:43:56AM +0000, Kurt Miller wrote:
> I've been trying to debug an issue with the jvm but whenever a
> thread exits that has previously been seen by gdb, I can't get
> a full list of threads with thread info. I get this error instead:
> "Couldn't get registers: No such process." For example:
>
> (gdb) info threads
> Id Target Id Frame
> 1 thread 388840 of process 15667 "" futex () at /tmp/-:3
> 2 thread 563387 of process 15667 "" futex () at /tmp/-:3
> 3 thread 536589 of process 15667 Couldn't get registers: No such process.
>
> This is because we don't provide a function override for
> thread_alive. This diff adds it (again copying NetBSD's
> implementation) and is enough to fix the above problem.
>
> However, this diff goes a step further and moves aways from using
> ptrace PT_GET_THREAD_FIRST/NEXT for finding threads and solely
> utilizes sysctl KERN_PROC_PID | KERN_PROC_SHOW_THREADS for finding
> threads. The advantage of this is that we can filter out threads
> with SIDL or SDEAD states from gdb's view of the process's threads.
>
> This should prevent a thread from being added in one of those
> states and then removed when thread_alive returns false for it.
>
> I've tested this on aarch64 with the jvm with threads being created
> and exiting while stopping periodically to check on state. I've
> checked both starting the process in gdb or attaching to the process.
> I also double checked a single threaded program still works in gdb.
>
> There are other ways to approach fixing this, like considering
> threads in SIDL or SDEAD states alive - I'm not sure if that would
> fix "Couldn't get registers: No such process." though. Another
> aproach would be to have the kernel skip threads with
> SIDL or SDEAD with ptrace PT_GET_THREAD_FIRST/NEXT.
>
> Thoughts on my current approach or okays?
I'm not super stocked about using SIDL or SDEAD outside of the kernel.
My problem with this is that I think we need to move away from a thread
state in the long run and so this would break if that is done.
Also using sysctl to grab all processes is pulling in an extra dependency
which has a similar issue but is less of a concern.
> 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 8 Dec 2025 00:11:51 -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 8 Dec 2025 00:11:51 -0000
> @@ -1,11 +1,16 @@
> -Add support for thread_name.
> +Add support for thread_name and thread_alive.
> +Use sysctl KERN_PROC_PID | KERN_PROC_SHOW_THREADS instead of
> +ptrace(PT_GET_THREAD_FIRST/NEXT) for adding threads so that we
> +can filter out threads with SIDL or SDEAD states.
>
> Index: gdb/obsd-nat.c
> --- gdb/obsd-nat.c.orig
> +++ gdb/obsd-nat.c
> -@@ -23,11 +23,13 @@
> +@@ -22,12 +22,15 @@
> + #include "target.h"
>
> #include <sys/types.h>
> ++#include <sys/proc.h>
> #include <sys/ptrace.h>
> +#include <sys/sysctl.h>
> #include "gdbsupport/gdb_wait.h"
> @@ -17,12 +22,12 @@ 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 +45,111 @@ 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.
> @@ -34,16 +39,22 @@ Index: gdb/obsd-nat.c
> +obsd_thread_lister (const pid_t pid,
> + gdb::function_view<bool (const struct kinfo_proc *)>
> + callback)
> -+{
> + {
> +- pid_t pid = inferior_ptid.pid ();
> +- struct ptrace_thread_state pts;
> + int mib[6] = {CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_SHOW_THREADS,
> + pid, sizeof(struct kinfo_proc), 0};
> + size_t size;
> -+
> +
> +- prune_threads ();
> + if (sysctl (mib, ARRAY_SIZE (mib), NULL, &size, NULL, 0) == -1 || size == 0)
> + perror_with_name (("sysctl"));
> -+
> +
> +- if (ptrace (PT_GET_THREAD_FIRST, pid, (caddr_t)&pts, sizeof pts) == -1)
> +- perror_with_name (("ptrace"));
> + mib[5] = size / sizeof (struct kinfo_proc);
> -+
> +
> +- while (pts.pts_tid != -1)
> + gdb::unique_xmalloc_ptr<struct kinfo_proc[]> ki
> + ((struct kinfo_proc *) xcalloc (mib[5], sizeof (struct kinfo_proc)));
> +
> @@ -52,13 +63,124 @@ Index: gdb/obsd-nat.c
> + perror_with_name (("sysctl"));
> +
> + for (size_t i = 0; i < size / sizeof (struct kinfo_proc); i++)
> -+ {
> + {
> +- ptid_t ptid = ptid_t (pid, pts.pts_tid, 0);
> + struct kinfo_proc *l = &ki[i];
> +
> +- if (!in_thread_list (this, ptid))
> +- {
> +- if (inferior_ptid.lwp () == 0)
> +- thread_change_ptid (this, inferior_ptid, ptid);
> +- else
> +- add_thread (this, ptid);
> +- }
> ++ /* Return true if the specified thread is alive. */
> ++ auto thr_alive
> ++ = [] (struct kinfo_proc *thr_proc)
> ++ {
> ++ switch (thr_proc->p_stat)
> ++ {
> ++ case SSLEEP:
> ++ case SRUN:
> ++ case SONPROC:
> ++ case SSTOP:
> ++ return true;
> ++ default:
> ++ return false;
> ++ }
> ++ };
> +
> +- if (ptrace (PT_GET_THREAD_NEXT, pid, (caddr_t)&pts, sizeof pts) == -1)
> +- perror_with_name (("ptrace"));
> ++ /* Ignore p_tid -1 which is the kinfo_proc for the process
> ++ also ignore embryonic or demised threads. */
> ++ if (l->p_tid == -1 || !thr_alive (l))
> ++ continue;
> ++
> + if (callback (l))
> + return true;
> -+ }
> + }
> +
> + return false;
> + }
> +
> ++/* Fuction to support executing callback for each alive thread */
> ++
> ++static void
> ++for_each_thread (pid_t pid, gdb::function_view<void (ptid_t)> callback)
> ++{
> ++ auto fn
> ++ = [=, &callback] (const struct kinfo_proc *ki)
> ++ {
> ++ ptid_t ptid = ptid_t (pid, ki->p_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 +223,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. */
> +@@ -183,4 +264,48 @@ int
> + 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 kinfo_proc *ki)
> ++ {
> ++ return ki->p_tid == tid;
> ++ };
> ++
> ++ return obsd_thread_lister (pid, fn);
> +}
> +
> +/* See obsd-nat.h. */
> 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 8 Dec 2025 00:11:51 -0000
> @@ -1,12 +1,13 @@
> -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,8 @@ 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;
> + const char *thread_name (struct thread_info *thr) override;
> ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
>
>
--
:wq Claudio
devel/gdb: implement thread_alive