+static void
+show_fbsd_lwp_debug (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("Debugging of FreeBSD lwp module is %s.\n"), value);
+}
+
+#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 was 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.
+
+ FreeBSD 11.0 introduced LWP event reporting via PT_LWP_EVENTS.
+*/
+
+/* 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 const 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, "LWP %d of process %d", lwp, pid);
+ return buf;
+ }
+
+ return normal_pid_to_str (ptid);
+}
+
+#ifdef HAVE_STRUCT_PTRACE_LWPINFO_PL_TDNAME
+/* Return the name assigned to a thread by an application. Returns
+ the string in a static buffer. */
+
+static const 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[sizeof pl.pl_tdname + 1];
+
+ /* Note that ptrace_lwpinfo returns the process command in pl_tdname
+ if a name has not been set explicitly. Return a NULL name in
+ that case. */
+ 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
+
+/* Enable additional event reporting on new processes.
+
+ To catch fork events, PTRACE_FORK is set on every traced process
+ to enable stops on returns from fork or vfork. Note that both the
+ parent and child will always stop, even if system call stops are
+ not enabled.
+
+ To catch LWP events, PTRACE_EVENTS is set on every traced process.
+ This enables stops on the birth for new LWPs (excluding the "main" LWP)
+ and the death of LWPs (excluding the last LWP in a process). Note
+ that unlike fork events, the LWP that creates a new LWP does not
+ report an event. */
+
+static void
+fbsd_enable_proc_events (pid_t pid)
+{
+#ifdef PT_GET_EVENT_MASK
+ int events;
+
+ if (ptrace (PT_GET_EVENT_MASK, pid, (PTRACE_TYPE_ARG3)&events,
+ sizeof (events)) == -1)
+ perror_with_name (("ptrace"));
+ events |= PTRACE_FORK | PTRACE_LWP;
+#ifdef PTRACE_VFORK
+ events |= PTRACE_VFORK;
+#endif
+ if (ptrace (PT_SET_EVENT_MASK, pid, (PTRACE_TYPE_ARG3)&events,
+ sizeof (events)) == -1)
+ perror_with_name (("ptrace"));
+#else
+#ifdef TDP_RFPPWAIT
+ if (ptrace (PT_FOLLOW_FORK, pid, (PTRACE_TYPE_ARG3)0, 1) == -1)
+ perror_with_name (("ptrace"));
+#endif
+#ifdef PT_LWP_EVENTS
+ if (ptrace (PT_LWP_EVENTS, pid, (PTRACE_TYPE_ARG3)0, 1) == -1)
+ perror_with_name (("ptrace"));
+#endif
+#endif
+}
+
+/* Add threads for any new LWPs in a process.
+
+ When LWP events are used, this function is only used to detect existing
+ threads when attaching to a process. On older systems, this function is
+ called to discover new threads each time the thread list is updated. */
+
+static void
+fbsd_add_threads (pid_t pid)
+{
+ int i, nlwps;
+
+ gdb_assert (!in_thread_list (pid_to_ptid (pid)));
+ nlwps = ptrace (PT_GETNUMLWPS, pid, NULL, 0);
+ if (nlwps == -1)
+ perror_with_name (("ptrace"));
+
+ gdb::unique_xmalloc_ptr<lwpid_t[]> lwps (XCNEWVEC (lwpid_t, nlwps));
+
+ nlwps = ptrace (PT_GETLWPLIST, pid, (caddr_t) lwps.get (), nlwps);
+ if (nlwps == -1)
+ perror_with_name (("ptrace"));
+
+ for (i = 0; i < nlwps; i++)
+ {
+ ptid_t ptid = ptid_build (pid, lwps[i], 0);
+
+ if (!in_thread_list (ptid))
+ {
+#ifdef PT_LWP_EVENTS
+ struct ptrace_lwpinfo pl;
+
+ /* Don't add exited threads. Note that this is only called
+ when attaching to a multi-threaded process. */
+ if (ptrace (PT_LWPINFO, lwps[i], (caddr_t) &pl, sizeof pl) == -1)
+ perror_with_name (("ptrace"));
+ if (pl.pl_flags & PL_FLAG_EXITED)
+ continue;
+#endif
+ if (debug_fbsd_lwp)
+ fprintf_unfiltered (gdb_stdlog,
+ "FLWP: adding thread for LWP %u\n",
+ lwps[i]);
+ add_thread (ptid);
+ }
+ }
+}
+
+/* Implement the "to_update_thread_list" target_ops method. */
+
+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
+ prune_threads ();
+
+ fbsd_add_threads (ptid_get_pid (inferior_ptid));
+#endif
+}
+