@@ -1,4 +1,39 @@
2016-01-10 John Baldwin <jhb@FreeBSD.org>
+
+ * configure.ac: Check for support for LWP names on FreeBSD.
+ * fbsd-nat.c [PT_LWPINFO] New variable debug_fbsd_lwp.
+ [TDP_RFPPWAIT || HAVE_STRUCT_PTRACE_LWPINFO_PL_TDNAME]
+ (fbsd_fetch_kinfo_proc): Move function earlier.
+ [PT_LWPINFO] (fbsd_thread_alive): New function.
+ [PT_LWPINFO] (fbsd_pid_to_str): New function.
+ [HAVE_STRUCT_PTRACE_LWPINFO_PL_TDNAME] (fbsd_thread_name): New function.
+ [PT_LWP_EVENTS] (fbsd_switch_to_threaded): New function.
+ [PT_LWP_EVENTS] (fbsd_enable_lwp_events): New function.
+ [!PT_LWP_EVENTS] (fbsd_add_threads): New function.
+ [PT_LWPINFO] (fbsd_update_thread_list): New function.
+ [PT_LWPINFO] New variable super_resume.
+ [PT_LWPINFO] (resume_one_thread_cb): New function.
+ [PT_LWPINFO] (resume_all_threads_cb): New function.
+ [PT_LWPINFO] (fbsd_resume): New function.
+ (fbsd_wait): Include lwp in returned ptid.
+ [PT_LWP_EVENTS] Handle LWP events.
+ (fbsd_post_startup_inferior) [PT_LWP_EVENTS]: Enable LWP events.
+ (fbsd_post_attach) [PT_LWP_EVENTS]: Enable LWP events.
+ Add threads for existing processes.
+ (fbsd_nat_add_target) [PT_LWPINFO]: Set "to_thread_alive" to
+ "fbsd_thread_alive".
+ Set "to_pid_to_str" to "fbsd_pid_to_str".
+ [HAVE_STRUCT_PTRACE_LWPINFO_PL_TDNAME]: Set "to_thread_name" to
+ "fbsd_thread_name".
+ [PT_LWPINFO]: Set "to_update_thread_list" to "fbsd_update_thread_list".
+ Set "to_has_thread_control" to "tc_schedlock".
+ Set "to_resume" to "fbsd_resume".
+ (_initialize_fbsd_nat): New function.
+ * configure: Regenerate.
+ * config.in: Regenerate.
+
+2016-01-10 John Baldwin <jhb@FreeBSD.org>
+
* amd64bsd-nat.c (get_ptrace_pid): New function.
(amd64bsd_fetch_inferior_registers): Use new function.
(amd64bsd_store_inferior_registers): Use new function.
@@ -450,6 +450,9 @@
/* Define to 1 if your system has struct lwp. */
#undef HAVE_STRUCT_LWP
+/* Define to 1 if `struct ptrace_lwpinfo' is a member of `pl_tdname'. */
+#undef HAVE_STRUCT_PTRACE_LWPINFO_PL_TDNAME
+
/* Define to 1 if your system has struct reg in <machine/reg.h>. */
#undef HAVE_STRUCT_REG
@@ -12964,6 +12964,22 @@ $as_echo "#define HAVE_PT_GETXMMREGS 1" >>confdefs.h
fi
+# See if <sys/ptrace.h> supports LWP names on FreeBSD
+# Older FreeBSD versions don't have the pl_tdname member of
+# `struct ptrace_lwpinfo'.
+ac_fn_c_check_member "$LINENO" "struct ptrace_lwpinfo" "pl_tdname" "ac_cv_member_struct_ptrace_lwpinfo_pl_tdname" "#include <sys/ptrace.h>
+"
+if test "x$ac_cv_member_struct_ptrace_lwpinfo_pl_tdname" = x""yes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_PTRACE_LWPINFO_PL_TDNAME 1
+_ACEOF
+
+
+fi
+
+
+
# Detect which type of /proc is in use, such as for Solaris.
if test "${target}" = "${host}"; then
@@ -1510,6 +1510,13 @@ if test $gdb_cv_have_pt_getxmmregs = yes; then
[Define if sys/ptrace.h defines the PT_GETXMMREGS request.])
fi
+# See if <sys/ptrace.h> supports LWP names on FreeBSD
+# Older FreeBSD versions don't have the pl_tdname member of
+# `struct ptrace_lwpinfo'.
+AC_CHECK_MEMBERS([struct ptrace_lwpinfo.pl_tdname], [], [],
+ [#include <sys/ptrace.h>])
+
+
# Detect which type of /proc is in use, such as for Solaris.
if test "${target}" = "${host}"; then
@@ -22,6 +22,7 @@
#include "inferior.h"
#include "regcache.h"
#include "regset.h"
+#include "gdbcmd.h"
#include "gdbthread.h"
#include "gdb_wait.h"
#include <sys/types.h>
@@ -204,11 +205,338 @@ fbsd_find_memory_regions (struct target_ops *self,
#endif
#ifdef PT_LWPINFO
+static int debug_fbsd_lwp;
+
static ptid_t (*super_wait) (struct target_ops *,
ptid_t,
struct target_waitstatus *,
int);
+#if defined(TDP_RFPPWAIT) || defined(HAVE_STRUCT_PTRACE_LWPINFO_PL_TDNAME)
+/* Fetch the external variant of the kernel's internal process
+ structure for the process PID into KP. */
+
+static void
+fbsd_fetch_kinfo_proc (pid_t pid, struct kinfo_proc *kp)
+{
+ size_t len;
+ int mib[4];
+
+ len = sizeof *kp;
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = pid;
+ if (sysctl (mib, 4, kp, &len, NULL, 0) == -1)
+ perror_with_name (("sysctl"));
+}
+#endif
+
+/*
+ FreeBSD's first thread support was via a "reentrant" version of libc
+ (libc_r) that first shipped in 2.2.7. This library multiplexed all
+ of the threads in a process onto a single kernel thread. This
+ library is supported via the bsd-uthread target.
+
+ FreeBSD 5.1 introduced two new threading libraries that made use of
+ multiple kernel threads. The first (libkse) scheduled M user
+ threads onto N (<= M) kernel threads (LWPs). The second (libthr)
+ bound each user thread to a dedicated kernel thread. libkse shipped
+ as the default threading library (libpthread).
+
+ FreeBSD 5.3 added a libthread_db to abstract the interface across
+ the various thread libraries (libc_r, libkse, and libthr).
+
+ FreeBSD 7.0 switched the default threading library from from libkse
+ to libpthread and removed libc_r.
+
+ FreeBSD 8.0 removed libkse and the in-kernel support for it. The
+ only threading library supported by 8.0 and later is libthr which
+ ties each user thread directly to an LWP. To simplify the
+ implementation, this target only supports LWP-backed threads using
+ ptrace directly rather than libthread_db.
+*/
+
+/* Return true if PTID is still active in the inferior. */
+
+static int
+fbsd_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+ if (ptid_lwp_p (ptid))
+ {
+ struct ptrace_lwpinfo pl;
+
+ if (ptrace (PT_LWPINFO, ptid_get_lwp (ptid), (caddr_t)&pl, sizeof pl)
+ == -1)
+ return 0;
+#ifdef PL_FLAG_EXITED
+ if (pl.pl_flags & PL_FLAG_EXITED)
+ return 0;
+#endif
+ }
+
+ return 1;
+}
+
+/* Convert PTID to a string. Returns the string in a static
+ buffer. */
+
+static char *
+fbsd_pid_to_str (struct target_ops *ops, ptid_t ptid)
+{
+ lwpid_t lwp;
+
+ lwp = ptid_get_lwp (ptid);
+ if (lwp != 0)
+ {
+ static char buf[64];
+ int pid = ptid_get_pid (ptid);
+
+ xsnprintf (buf, sizeof buf, "process %d, LWP %d", pid, lwp);
+ return buf;
+ }
+
+ return normal_pid_to_str (ptid);
+}
+
+#ifdef HAVE_STRUCT_PTRACE_LWPINFO_PL_TDNAME
+static char *
+fbsd_thread_name (struct target_ops *self, struct thread_info *thr)
+{
+ struct ptrace_lwpinfo pl;
+ struct kinfo_proc kp;
+ int pid = ptid_get_pid (thr->ptid);
+ long lwp = ptid_get_lwp (thr->ptid);
+ static char buf[64];
+
+ fbsd_fetch_kinfo_proc (pid, &kp);
+ if (ptrace (PT_LWPINFO, lwp, (caddr_t)&pl, sizeof pl) == -1)
+ perror_with_name (("ptrace"));
+ if (strcmp (kp.ki_comm, pl.pl_tdname) == 0)
+ return NULL;
+ xsnprintf (buf, sizeof buf, "%s", pl.pl_tdname);
+ return buf;
+}
+#endif
+
+#ifdef PT_LWP_EVENTS
+static void
+fbsd_switch_to_threaded (pid_t pid)
+{
+ struct cleanup *cleanup;
+ lwpid_t *lwps;
+ struct ptrace_lwpinfo *pl;
+ int i, nlwps;
+
+ nlwps = ptrace (PT_GETNUMLWPS, pid, NULL, 0);
+ if (nlwps == -1)
+ perror_with_name (("ptrace"));
+
+ lwps = XCNEWVEC (lwpid_t, nwlps);
+ cleanup = make_cleanup (xfree, lwps);
+
+ nlwps = ptrace (PT_GETLWPLIST, pid, (caddr_t)lwps, nlwps);
+ if (nlwps == -1)
+ perror_with_name (("ptrace"));
+ pl = XCNEWVEC (struct ptrace_lwpinfo, nlwps);
+ make_cleanup (xfree, pl);
+ for (i = 0; i < nlwps; i++)
+ {
+ if (ptrace (PT_LWPINFO, lwps[i], (caddr_t)&pl[i], sizeof pl[i]) == -1)
+ perror_with_name (("ptrace"));
+ }
+
+ /* Choose a candidate thread for the main thread. Prefer the first
+ non-BORN and non-EXITED thread. If all threads are newborns, use
+ the first non-EXITED thread. */
+ for (i = 0; i < nlwps; i++)
+ {
+ ptid_t ptid = ptid_build (pid, lwps[i], 0);
+
+ if ((pl[i].pl_flags & (PL_FLAG_BORN | PL_FLAG_EXITED)) != 0)
+ continue;
+
+ if (debug_fbsd_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "FLWP: using LWP %u as main thread\n",
+ lwps[i]);
+ gdb_assert (!in_thread_list (ptid));
+ thread_change_ptid (inferior_ptid, ptid);
+ break;
+ }
+ for (i = 0; i < nlwps; i++)
+ {
+ ptid_t ptid = ptid_build (pid, lwps[i], 0);
+
+ if (pl[i].pl_flags & PL_FLAG_EXITED)
+ continue;
+
+ if (!ptid_lwp_p (inferior_ptid))
+ {
+ if (debug_fbsd_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "FLWP: using LWP %u as main thread\n",
+ lwps[i]);
+ gdb_assert (!in_thread_list (ptid));
+ thread_change_ptid (inferior_ptid, ptid);
+ }
+ else if (!in_thread_list (ptid))
+ {
+ if (debug_fbsd_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "FLWP: adding thread for LWP %u\n",
+ lwps[i]);
+ add_thread (ptid);
+ }
+ }
+
+ do_cleanups (cleanup);
+}
+
+static void
+fbsd_enable_lwp_events (pid_t pid)
+{
+ if (ptrace (PT_LWP_EVENTS, pid, (PTRACE_TYPE_ARG3)0, 1) == -1)
+ perror_with_name (("ptrace"));
+}
+#else
+static void
+fbsd_add_threads (pid_t pid, int nlwps)
+{
+ struct cleanup *cleanup;
+ lwpid_t *lwps;
+ int i;
+
+ lwps = XCNEWVEC (lwpid_t, nlwps);
+ cleanup = make_cleanup (xfree, lwps);
+
+ nlwps = ptrace (PT_GETLWPLIST, pid, (caddr_t)lwps, nlwps);
+ if (nlwps == -1)
+ perror_with_name (("ptrace"));
+
+ for (i = 0; i < nlwps; i++)
+ {
+ ptid_t ptid = ptid_build (pid, lwps[i], 0);
+
+ /* If this inferior is not using LWP ptids, use the first LWP as
+ the main thread. */
+ if (!ptid_lwp_p (inferior_ptid))
+ {
+ if (debug_fbsd_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "FLWP: using LWP %u as main thread\n",
+ lwps[i]);
+ gdb_assert (!in_thread_list (ptid));
+ thread_change_ptid (inferior_ptid, ptid);
+ }
+ else if (!in_thread_list (ptid))
+ {
+ if (debug_fbsd_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "FLWP: adding thread for LWP %u\n",
+ lwps[i]);
+ gdb_assert (ptid_lwp_p (inferior_ptid));
+ add_thread (ptid);
+ }
+ }
+ do_cleanups (cleanup);
+}
+#endif
+
+/* Implement the to_update_thread_list target method for this
+ target. */
+
+static void
+fbsd_update_thread_list (struct target_ops *ops)
+{
+#ifdef PT_LWP_EVENTS
+ /* With support for thread events, threads are added/deleted from the
+ list as events are reported, so just try deleting exited threads. */
+ delete_exited_threads ();
+#else
+ int nlwps;
+ int pid = ptid_get_pid (inferior_ptid);
+
+ prune_threads();
+
+ nlwps = ptrace (PT_GETNUMLWPS, pid, NULL, 0);
+ if (nlwps == -1)
+ perror_with_name (("ptrace"));
+
+ /* Leave single-threaded processes with a non-threaded ptid alone. */
+ if (nlwps == 1 && !ptid_lwp_p (inferior_ptid))
+ return;
+
+ fbsd_add_threads (pid, nlwps);
+#endif
+}
+
+static void (*super_resume) (struct target_ops *,
+ ptid_t,
+ int,
+ enum gdb_signal);
+
+static int
+resume_one_thread_cb(struct thread_info *tp, void *data)
+{
+ ptid_t *ptid = data;
+ int request;
+
+ if (ptid_get_pid (tp->ptid) != ptid_get_pid (*ptid))
+ return 0;
+
+ if (ptid_get_lwp (tp->ptid) == ptid_get_lwp (*ptid))
+ request = PT_RESUME;
+ else
+ request = PT_SUSPEND;
+
+ if (ptrace (request, ptid_get_lwp (tp->ptid), (caddr_t)0, 0) == -1)
+ perror_with_name (("ptrace"));
+ return 0;
+}
+
+static int
+resume_all_threads_cb(struct thread_info *tp, void *data)
+{
+ ptid_t *filter = data;
+
+ if (!ptid_match (tp->ptid, *filter))
+ return 0;
+
+ /* Ignore single-threaded processes. */
+ if (!ptid_lwp_p (tp->ptid))
+ return 0;
+
+ if (ptrace (PT_RESUME, ptid_get_lwp (tp->ptid), (caddr_t)0, 0) == -1)
+ perror_with_name (("ptrace"));
+ return 0;
+}
+
+static void
+fbsd_resume (struct target_ops *ops,
+ ptid_t ptid, int step, enum gdb_signal signo)
+{
+
+ if (debug_fbsd_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "FLWP: fbsd_resume for ptid (%d, %ld, %ld)\n",
+ ptid_get_pid (ptid), ptid_get_lwp (ptid),
+ ptid_get_tid (ptid));
+ if (ptid_lwp_p (ptid))
+ {
+ /* If ptid is a specific LWP, suspend all other LWPs in the process. */
+ iterate_over_threads (resume_one_thread_cb, &ptid);
+ }
+ else
+ {
+ /* If ptid is a wildcard, resume all matching threads (they won't run
+ until the process is continued however). */
+ iterate_over_threads (resume_all_threads_cb, &ptid);
+ ptid = inferior_ptid;
+ }
+ super_resume (ops, ptid, step, signo);
+}
+
#ifdef TDP_RFPPWAIT
/*
To catch fork events, PT_FOLLOW_FORK is set on every traced process
@@ -287,24 +615,6 @@ fbsd_is_child_pending (pid_t pid)
}
return 0;
}
-
-/* Fetch the external variant of the kernel's internal process
- structure for the process PID into KP. */
-
-static void
-fbsd_fetch_kinfo_proc (pid_t pid, struct kinfo_proc *kp)
-{
- size_t len;
- int mib[4];
-
- len = sizeof *kp;
- mib[0] = CTL_KERN;
- mib[1] = KERN_PROC;
- mib[2] = KERN_PROC_PID;
- mib[3] = pid;
- if (sysctl (mib, 4, kp, &len, NULL, 0) == -1)
- perror_with_name (("sysctl"));
-}
#endif
/* Wait for the child specified by PTID to do something. Return the
@@ -330,6 +640,8 @@ fbsd_wait (struct target_ops *ops,
pid = ptid_get_pid (wptid);
if (ptrace (PT_LWPINFO, pid, (caddr_t)&pl, sizeof pl) == -1)
perror_with_name (("ptrace"));
+ if (!in_thread_list (wptid))
+ wptid = ptid_build (pid, pl.pl_lwpid, 0);
#ifdef TDP_RFPPWAIT
if (pl.pl_flags & PL_FLAG_FORKED)
@@ -384,6 +696,52 @@ fbsd_wait (struct target_ops *ops,
return wptid;
}
#endif
+
+#ifdef PT_LWP_EVENTS
+ if (pl.pl_flags & PL_FLAG_BORN)
+ {
+ if (ptid_lwp_p (inferior_ptid))
+ {
+ ptid_t ptid = ptid_build (pid, pl.pl_lwpid, 0);
+
+ /* If a process stops with multiple newborn threads,
+ the first thread birth event will add all of the
+ pending threads in fbsd_switch_to_threaded.
+ However, each thread will still report a newborn
+ stop. */
+ if (!in_thread_list (ptid))
+ {
+ if (debug_fbsd_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "FLWP: adding thread for LWP %u\n",
+ pl.pl_lwpid);
+ gdb_assert(!in_thread_list (ptid));
+ add_thread (ptid);
+ }
+ }
+ else
+ fbsd_switch_to_threaded (pid);
+ ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+ return ptid_build (pid, pl.pl_lwpid, 0);
+ }
+ if (pl.pl_flags & PL_FLAG_EXITED)
+ {
+ ptid_t ptid = ptid_build (pid, pl.pl_lwpid, 0);
+
+ gdb_assert (in_thread_list (ptid));
+ if (debug_fbsd_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "FLWP: deleting thread for LWP %u\n",
+ pl.pl_lwpid);
+ if (print_thread_events)
+ printf_unfiltered (_("[%s exited]\n"), target_pid_to_str
+ (ptid));
+ delete_thread (ptid);
+ if (ptrace (PT_CONTINUE, pid, (PTRACE_TYPE_ARG3)1, 0) == -1)
+ perror_with_name (("ptrace"));
+ continue;
+ }
+#endif
}
return wptid;
}
@@ -449,13 +807,19 @@ fbsd_enable_follow_fork (pid_t pid)
if (ptrace (PT_FOLLOW_FORK, pid, (PTRACE_TYPE_ARG3)0, 1) == -1)
perror_with_name (("ptrace"));
}
+#endif
/* Implement the "to_post_startup_inferior" target_ops method. */
static void
fbsd_post_startup_inferior (struct target_ops *self, ptid_t pid)
{
+#ifdef TDP_RFPPWAIT
fbsd_enable_follow_fork (ptid_get_pid (pid));
+#endif
+#ifdef PT_LWP_EVENTS
+ fbsd_enable_lwp_events (ptid_get_pid (pid));
+#endif
}
/* Implement the "to_post_attach" target_ops method. */
@@ -463,9 +827,28 @@ fbsd_post_startup_inferior (struct target_ops *self, ptid_t pid)
static void
fbsd_post_attach (struct target_ops *self, int pid)
{
+ int nlwps;
+
+#ifdef TDP_RFPPWAIT
fbsd_enable_follow_fork (pid);
-}
#endif
+#ifdef PT_LWP_EVENTS
+ fbsd_enable_lwp_events (pid);
+#endif
+
+ /* Add threads for other LWPs when attaching to a threaded process. */
+ nlwps = ptrace (PT_GETNUMLWPS, pid, NULL, 0);
+ if (nlwps == -1)
+ perror_with_name (("ptrace"));
+ if (nlwps > 1)
+ {
+#ifdef PT_LWP_EVENTS
+ fbsd_switch_to_threaded (pid);
+#else
+ fbsd_add_threads (pid, nlwps);
+#endif
+ }
+}
#ifdef PL_FLAG_EXEC
/* If the FreeBSD kernel supports PL_FLAG_EXEC, then traced processes
@@ -491,16 +874,25 @@ fbsd_nat_add_target (struct target_ops *t)
t->to_pid_to_exec_file = fbsd_pid_to_exec_file;
t->to_find_memory_regions = fbsd_find_memory_regions;
#ifdef PT_LWPINFO
+ t->to_thread_alive = fbsd_thread_alive;
+ t->to_pid_to_str = fbsd_pid_to_str;
+#ifdef HAVE_STRUCT_PTRACE_LWPINFO_PL_TDNAME
+ t->to_thread_name = fbsd_thread_name;
+#endif
+ t->to_update_thread_list = fbsd_update_thread_list;
+ t->to_has_thread_control = tc_schedlock;
+ super_resume = t->to_resume;
+ t->to_resume = fbsd_resume;
super_wait = t->to_wait;
t->to_wait = fbsd_wait;
+ t->to_post_startup_inferior = fbsd_post_startup_inferior;
+ t->to_post_attach = fbsd_post_attach;
#ifdef TDP_RFPPWAIT
t->to_follow_fork = fbsd_follow_fork;
t->to_insert_fork_catchpoint = fbsd_insert_fork_catchpoint;
t->to_remove_fork_catchpoint = fbsd_remove_fork_catchpoint;
t->to_insert_vfork_catchpoint = fbsd_insert_vfork_catchpoint;
t->to_remove_vfork_catchpoint = fbsd_remove_vfork_catchpoint;
- t->to_post_startup_inferior = fbsd_post_startup_inferior;
- t->to_post_attach = fbsd_post_attach;
#endif
#ifdef PL_FLAG_EXEC
t->to_insert_exec_catchpoint = fbsd_insert_exec_catchpoint;
@@ -509,3 +901,21 @@ fbsd_nat_add_target (struct target_ops *t)
#endif
add_target (t);
}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_fbsd_nat;
+
+void
+_initialize_fbsd_nat (void)
+{
+#ifdef PT_LWPINFO
+ add_setshow_boolean_cmd ("fbsd-lwp", class_maintenance,
+ &debug_fbsd_lwp, _("\
+Set debugging of FreeBSD lwp module."), _("\
+Show debugging of FreeBSD lwp module."), _("\
+Enables printf debugging output."),
+ NULL,
+ NULL,
+ &setdebuglist, &showdebuglist);
+#endif
+}