public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [RFC] Thread debug support for NetBSD 5
@ 2010-04-20 15:49 Paul Koning
  2010-04-22 15:36 ` Paul Koning
  0 siblings, 1 reply; 16+ messages in thread
From: Paul Koning @ 2010-04-20 15:49 UTC (permalink / raw)
  To: gdb-patches

This patch adds thread debug support for NetBSD 5.0.  Unlike older
NetBSDs, in that version threads are seen by the kernel and exposed
via ptrace() machinery.  The patch makes some small changes in
existing files, mainly to move some code into NetBSD specific files to
isolate the changes, and it adds a new file nbsd-thread.c.  The
machinery in that file is loosely based on existing examples in
dec-thread.c and linux-nat.c.

Tested on NetBSD on i386 and mipsel platforms.  I did not make changes
in target specific code for other platforms because I don't have them;
the changes should be pretty obvious given what is changed in
mipsnbsd-nat.c. 

I don't have write privs in GDB as far as I know, but all the
paperwork is in place.

	  paul

2010-04-20  Paul Koning  <paul_koning@dell.com>

	* i386bsd-nat.c (i386bsd_supply_gregset, i386bsd_collect_gregset):
	Make global.
	* i386bsd-nat.h: Ditto.
	* i386nbsd-nat.c: Include inferior.h, i387-tdep.h, sys/ptrace.h,
	machine/reg.h.
	(i386nbsd_fetch_inferior_registers,
	i386nbsd_store_inferior_registers): New.
	* mipsnbsd-nat.c (mipsnbsd_fetch_inferior_registers,
	mipsnbsd_store_inferior_registers): Pass thread ID to ptrace().
	* nbsd-thread.c: New file.
	* config/i386/nbsdelf.mh: Add nbsd-thread.o.
	* config/mips/nbsd.mh: Add nbsd-thread.o.

Index: gdb/i386bsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/i386bsd-nat.c,v
retrieving revision 1.43
diff -u -r1.43 i386bsd-nat.c
--- gdb/i386bsd-nat.c	1 Jan 2010 07:31:36 -0000	1.43
+++ gdb/i386bsd-nat.c	20 Apr 2010 15:37:49 -0000
@@ -88,7 +88,7 @@
 
 /* Supply the general-purpose registers in GREGS, to REGCACHE.  */
 
-static void
+void
 i386bsd_supply_gregset (struct regcache *regcache, const void *gregs)
 {
   const char *regs = gregs;
@@ -107,7 +107,7 @@
    GREGS.  If REGNUM is -1, collect and store all appropriate
    registers.  */
 
-static void
+void
 i386bsd_collect_gregset (const struct regcache *regcache,
 			 void *gregs, int regnum)
 {
Index: gdb/i386bsd-nat.h
===================================================================
RCS file: /cvs/src/src/gdb/i386bsd-nat.h,v
retrieving revision 1.8
diff -u -r1.8 i386bsd-nat.h
--- gdb/i386bsd-nat.h	1 Jan 2010 07:31:36 -0000	1.8
+++ gdb/i386bsd-nat.h	20 Apr 2010 15:37:49 -0000
@@ -35,4 +35,11 @@
 
 extern unsigned long i386bsd_dr_get_status (void);
 
+extern void i386bsd_supply_gregset (struct regcache *regcache, 
+                                    const void *gregs);
+
+extern void i386bsd_collect_gregset (const struct regcache *regcache,
+                                     void *gregs, int regnum);
+
+
 #endif /* i386bsd-nat.h */
Index: gdb/i386nbsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/i386nbsd-nat.c,v
retrieving revision 1.22
diff -u -r1.22 i386nbsd-nat.c
--- gdb/i386nbsd-nat.c	1 Jan 2010 07:31:36 -0000	1.22
+++ gdb/i386nbsd-nat.c	20 Apr 2010 15:37:49 -0000
@@ -19,16 +19,20 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "inferior.h"
 #include "gdbcore.h"
 #include "regcache.h"
 #include "target.h"
 
 #include "i386-tdep.h"
+#include "i387-tdep.h"
 #include "i386bsd-nat.h"
 
 /* Support for debugging kernel virtual memory images.  */
 
 #include <sys/types.h>
+#include <sys/ptrace.h>
+#include <machine/reg.h>
 #include <machine/frame.h>
 #include <machine/pcb.h>
 
@@ -71,6 +75,131 @@
 
   return 1;
 }
+
+/* Macro to determine if a register is fetched with PT_GETREGS.  */
+#define GETREGS_SUPPLIES(regnum) \
+  ((0 <= (regnum) && (regnum) <= 15))
+
+#ifdef HAVE_PT_GETXMMREGS
+/* Set to 1 if the kernel supports PT_GETXMMREGS.  Initialized to -1
+   so that we try PT_GETXMMREGS the first time around.  */
+static int have_ptrace_xmmregs = -1;
+#endif
+\f
+
+/* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
+   for all registers (including the floating point registers).  */
+
+static void
+i386nbsd_fetch_inferior_registers (struct target_ops *ops,
+				   struct regcache *regcache, int regnum)
+{
+  if (regnum == -1 || GETREGS_SUPPLIES (regnum))
+    {
+      struct reg regs;
+
+      if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
+	perror_with_name (_("Couldn't get registers"));
+
+      i386bsd_supply_gregset (regcache, &regs);
+      if (regnum != -1)
+	return;
+    }
+
+  if (regnum == -1 || regnum >= I386_ST0_REGNUM)
+    {
+      struct fpreg fpregs;
+#ifdef HAVE_PT_GETXMMREGS
+      char xmmregs[512];
+
+      if (have_ptrace_xmmregs != 0
+	  && ptrace(PT_GETXMMREGS, PIDGET (inferior_ptid),
+		    (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == 0)
+	{
+	  have_ptrace_xmmregs = 1;
+	  i387_supply_fxsave (regcache, -1, xmmregs);
+	}
+      else
+	{
+          if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
+	    perror_with_name (_("Couldn't get floating point status"));
+
+	  i387_supply_fsave (regcache, -1, &fpregs);
+	}
+#else
+      if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
+	perror_with_name (_("Couldn't get floating point status"));
+
+      i387_supply_fsave (regcache, -1, &fpregs);
+#endif
+    }
+}
+
+/* Store register REGNUM back into the inferior.  If REGNUM is -1, do
+   this for all registers (including the floating point registers).  */
+
+static void
+i386nbsd_store_inferior_registers (struct target_ops *ops,
+				   struct regcache *regcache, int regnum)
+{
+  if (regnum == -1 || GETREGS_SUPPLIES (regnum))
+    {
+      struct reg regs;
+
+      if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
+                  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
+        perror_with_name (_("Couldn't get registers"));
+
+      i386bsd_collect_gregset (regcache, &regs, regnum);
+
+      if (ptrace (PT_SETREGS, PIDGET (inferior_ptid),
+	          (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
+        perror_with_name (_("Couldn't write registers"));
+
+      if (regnum != -1)
+	return;
+    }
+
+  if (regnum == -1 || regnum >= I386_ST0_REGNUM)
+    {
+      struct fpreg fpregs;
+#ifdef HAVE_PT_GETXMMREGS
+      char xmmregs[512];
+
+      if (have_ptrace_xmmregs != 0
+	  && ptrace(PT_GETXMMREGS, PIDGET (inferior_ptid),
+		    (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == 0)
+	{
+	  have_ptrace_xmmregs = 1;
+
+	  i387_collect_fxsave (regcache, regnum, xmmregs);
+
+	  if (ptrace (PT_SETXMMREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == -1)
+            perror_with_name (_("Couldn't write XMM registers"));
+	}
+      else
+	{
+	  have_ptrace_xmmregs = 0;
+#endif
+          if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
+	    perror_with_name (_("Couldn't get floating point status"));
+
+          i387_collect_fsave (regcache, regnum, &fpregs);
+
+          if (ptrace (PT_SETFPREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
+	    perror_with_name (_("Couldn't write floating point status"));
+#ifdef HAVE_PT_GETXMMREGS
+        }
+#endif
+    }
+}
+
 \f
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
@@ -84,6 +213,8 @@
   /* Add some extra features to the common *BSD/i386 target.  */
   t = i386bsd_target ();
   t->to_pid_to_exec_file = nbsd_pid_to_exec_file;
+  t->to_fetch_registers = i386nbsd_fetch_inferior_registers;
+  t->to_store_registers = i386nbsd_store_inferior_registers;
   add_target (t);
  
   /* Support debugging kernel virtual memory images.  */
Index: gdb/mipsnbsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/mipsnbsd-nat.c,v
retrieving revision 1.17
diff -u -r1.17 mipsnbsd-nat.c
--- gdb/mipsnbsd-nat.c	1 Jan 2010 07:31:37 -0000	1.17
+++ gdb/mipsnbsd-nat.c	20 Apr 2010 15:37:49 -0000
@@ -49,7 +49,7 @@
       struct reg regs;
 
       if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get registers"));
       
       mipsnbsd_supply_reg (regcache, (char *) &regs, regno);
@@ -62,7 +62,7 @@
       struct fpreg fpregs;
 
       if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get floating point status"));
 
       mipsnbsd_supply_fpreg (regcache, (char *) &fpregs, regno);
@@ -79,13 +79,13 @@
       struct reg regs;
 
       if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get registers"));
 
       mipsnbsd_fill_reg (regcache, (char *) &regs, regno);
 
       if (ptrace (PT_SETREGS, PIDGET (inferior_ptid), 
-		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't write registers"));
 
       if (regno != -1)
@@ -97,13 +97,13 @@
       struct fpreg fpregs; 
 
       if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get floating point status"));
 
       mipsnbsd_fill_fpreg (regcache, (char *) &fpregs, regno);
 
       if (ptrace (PT_SETFPREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't write floating point status"));
     }
 }
Index: gdb/nbsd-thread.c
===================================================================
RCS file: gdb/nbsd-thread.c
diff -N gdb/nbsd-thread.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdb/nbsd-thread.c	20 Apr 2010 15:37:49 -0000
@@ -0,0 +1,695 @@
+/* Threads support for NetBSD 5.0.
+
+   Copyright (C) 2010 Free Software Foundation, Inc.
+
+   Contributed by Paul Koning, Dell, inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "observer.h"
+#include "target.h"
+#include "inferior.h"
+#include "gdbthread.h"
+#include "regcache.h"
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+
+/* Data structure used to track NetBSD thread state.  There is a 
+   vector of these, in ascending order of LWP ID.  */
+
+struct lwp_info
+{
+  /* The process ID of the LWP.  This is a combination of the process
+     ID and the LWP ID.  */
+  ptid_t ptid;
+  
+  /* Non-zero if we this LWP was reported as having been signalled
+     by the PT_LWPINFO ptrace() call.  */
+  int signalled;
+  
+  /* The waitstatus for this LWP's last event.  */
+  struct target_waitstatus waitstatus;
+};
+
+/* The lwp_info buffer and its control variables.  */
+static struct lwp_info *lwp_buffer;
+static int lwp_count;
+static int lwp_bufsize;
+
+/* Count of signals still pending delivery to GDB.  These are threads
+   that were found to be stopped and not breakpoints.  For threads that
+   hit a breakpoint, we simply push back the thread so it will hit the
+   break again (if it isn't removed before then) but for other signals,
+   for example faults, the signal remains pending, the "to_resume" that
+   resumes the whole process is skipped, and then the "to_wait" returns
+   the information about one of the pending signals instead.  */
+static int pending_sigs;
+
+/* The LWP ID of the thread being stepped, or 0 if none.  */
+static int step_lwpid;
+
+/* Flag to indicate whether last resume was a resume all threads or
+   a resume single thread.  */
+static int resume_all;
+
+/* Non-zero if the netbsd-thread layer is active.  */
+static int nbsd_thread_active = 0;
+
+/* The netbsd-thread target_ops structure.  */
+static struct target_ops nbsd_thread_ops;
+
+int debug_nbsd_thread;
+static void
+show_debug_nbsd_thread (struct ui_file *file, int from_tty,
+		      struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Debugging of NetBSD thread module is %s.\n"),
+		    value);
+}
+
+
+/* Activate thread support if appropriate.  Do nothing if thread
+   support is already active.  */
+
+static void
+enable_nbsd_thread (void)
+{
+  struct minimal_symbol *msym;
+  void* caller_context;
+  int status;
+
+  /* If already active, nothing more to do.  */
+  if (nbsd_thread_active)
+    return;
+
+  if (lwp_buffer != NULL)
+    {
+      xfree (lwp_buffer);
+      lwp_buffer = NULL;
+      lwp_count = lwp_bufsize = 0;
+    }
+  
+  push_target (&nbsd_thread_ops);
+  nbsd_thread_active = 1;
+}
+
+/* Deactivate thread support.  Do nothing is thread support is
+   already inactive.  */
+
+static void
+disable_nbsd_thread (void)
+{
+  if (!nbsd_thread_active)
+    return;
+
+  unpush_target (&nbsd_thread_ops);
+  nbsd_thread_active = 0;
+}
+
+/* Update our lwp_info buffer, and tell GDB about adds or deletes.
+
+   By doing the thread add and thread delete operations here as we
+   learn about threads, we allow users to run thread-specific commands
+   without needing to run "info threads" first. 
+
+   The argument is a pointer to the waitstatus struct, which
+   is copied into the waitstatus for the thread we find as the signalled
+   thread.  */
+
+static void
+update_lwpbuf (struct target_waitstatus *status)
+{
+  int pi;
+  lwpid_t lwp_id, sig_lwpid;
+  struct ptrace_lwpinfo pt_info;
+  ptid_t ptid;
+  
+  /* Accumulate an array of NetBSD threads, in descending order of LWP id.
+
+     The reason for using descending order is that this is the order
+     in which LWPs are returned by the ptrace() PT_LWPINFO function,
+     because it returns them in the order in which they exist in the
+     p_lwps list for the process, and new entries are assigned ascending
+     LWP IDs and are added to the head of that list.  */
+
+  lwp_id = sig_lwpid = 0;
+  pi = 0;
+  
+  while (1)
+    {
+      pt_info.pl_lwpid = lwp_id;
+      errno = 0;
+      if (ptrace (PT_LWPINFO, PIDGET (inferior_ptid),
+		  (PTRACE_TYPE_ARG3) &pt_info, sizeof (pt_info)) == -1)
+	{
+	  if (errno == ESRCH)
+	    break;
+	  else if (errno != 0)
+	    perror_with_name (_("Couldn't get thread information"));
+	}
+
+      if (debug_nbsd_thread)
+	fprintf_unfiltered (gdb_stdlog,
+			    "NTUP: lwpinfo on %d returns lwp %d, pl_event %d\n",
+			    lwp_id, pt_info.pl_lwpid, pt_info.pl_event);
+	  
+      /* Retrieve the LWP ID that was found.  This also sets the ID to
+	 start from the next time around the loop.  */
+      lwp_id = pt_info.pl_lwpid;
+
+      /* LWP id 0 is end of list.  */
+      if (lwp_id == 0)
+	break;
+      
+      /* If the LWP we found has an ID less than the ID of the current
+	 buffer entry, then the current buffer entry is a deleted thread.
+	 Tell GDB about its demise, then remove it from the buffer. 
+	  
+	 We have to do this in a loop until we run out of threads
+	 to be removed.  */
+      while (pi < lwp_count && lwp_id < TIDGET (lwp_buffer[pi].ptid))
+	{
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTUP: thread ptid %d,%ld has disappeared\n",
+				PIDGET (lwp_buffer[pi].ptid),
+				TIDGET (lwp_buffer[pi].ptid));
+
+	  /* Tell GDB.  */
+	  delete_thread (lwp_buffer[pi].ptid);
+
+	  /* Remove the deleted entry.  */
+	  if (pi < lwp_count)
+	    memmove (lwp_buffer + pi + 1, lwp_buffer + pi, 
+		     (lwp_count - pi) * sizeof (struct lwp_info));
+	  lwp_count--;
+	}
+
+      /* If we're now at the end of the current buffer, or the LWP found
+	 has an LWP ID greater than the current entry in the buffer, this
+	 is a new thread.  Allocate more buffer space if need be,
+	 make room for this entry, and store it.  Then tell GDB about
+	 the new thread.  */
+      if (pi >= lwp_count || lwp_id > TIDGET (lwp_buffer[pi].ptid))
+	{
+	  /* Allocate more space, if we need it.  */
+	  if (lwp_count == lwp_bufsize)
+	    {
+	      if (lwp_bufsize)
+		lwp_bufsize *= 2;
+	      else
+		lwp_bufsize = 1;
+	      lwp_buffer = (struct lwp_info *) xrealloc (lwp_buffer, 
+							 lwp_bufsize * sizeof (struct lwp_info));
+	    }
+
+	  /* Push current and later entries, if any, over.  */
+	  if (pi < lwp_count)
+	    memmove (lwp_buffer + pi + 1, lwp_buffer + pi, 
+		     (lwp_count - pi) * sizeof (struct lwp_info));
+
+	  /* Update the count of LWPs.  */
+	  lwp_count++;
+
+	  /* Initialize the new entry.  */
+	  lwp_buffer[pi].ptid = MERGEPID (PIDGET (inferior_ptid), lwp_id);
+	  if (pt_info.pl_event == PL_EVENT_SIGNAL)
+	    {
+	      lwp_buffer[pi].signalled = 1;
+	      lwp_buffer[pi].waitstatus = *status;
+	      sig_lwpid = lwp_id;
+	    }
+	  else
+	    lwp_buffer[pi].signalled = 0;
+
+	  /* Advance the LWP buffer pointer.  */
+	  pi++;
+	  
+	  /* Tell GDB about the new thread.  */
+	  if (lwp_count == 1)
+	    {
+	      /* See if GDB still has TID zero, if so set the TID.  */
+	      if (TIDGET (inferior_ptid) == 0)
+		{
+		  ptid = MERGEPID (PIDGET (inferior_ptid), lwp_id);
+		  thread_change_ptid (inferior_ptid, ptid);
+		  if (debug_nbsd_thread)
+		    fprintf_unfiltered (gdb_stdlog,
+					"NTUP: setting main thread ptid to %d,%ld\n",
+					PIDGET (ptid), TIDGET (ptid));
+		}
+	    }
+	  else
+	    {
+	      /* New thread but not the first, add it to GDB.  */
+	      if (debug_nbsd_thread)
+		fprintf_unfiltered (gdb_stdlog,
+				    "NTUP: adding new thread ptid %d,%d\n",
+				    PIDGET (inferior_ptid), lwp_id);
+	      add_thread (MERGEPID (PIDGET (inferior_ptid), lwp_id));
+	    }
+	}
+      else
+	{
+	  /* Found an existing thread.  Update its status in the buffer.
+	     Note that we clear the signalled flag if this is the first
+	     call and this thread wasn't the signalled thread, but we 
+	     leave it alone on subsequent calls.  That way the subsequent
+	     calls will accumulate the set of signalled threads.  */
+	  if (pt_info.pl_event == PL_EVENT_SIGNAL)
+	    {
+	      lwp_buffer[pi].signalled = 1;
+	      lwp_buffer[pi].waitstatus = *status;
+	      sig_lwpid = lwp_id;
+	    }
+	  
+	  /* Advance the LWP buffer pointer.  */
+	  pi++;
+	}
+    }
+  if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTUP: signalled thread lwpid is %d\n", sig_lwpid);
+
+}
+
+/* The "to_detach" method of the nbsd_thread_ops.  */
+
+static void
+nbsd_thread_detach (struct target_ops *ops, char *args, int from_tty)
+{   
+  struct target_ops *beneath = find_target_beneath (ops);
+
+  disable_nbsd_thread ();
+  beneath->to_detach (beneath, args, from_tty);
+}
+
+/* Resume execution of thread PTID, or all threads if PTID is -1.  If
+   STEP is nonzero, single-step it.  If SIGNAL is nonzero, give it
+   that signal.  */
+
+static void
+nbsd_thread_resume (struct target_ops *ops,
+		    ptid_t ptid, int step, enum target_signal signal)
+{
+  pid_t pid;
+  int request;
+  
+  /* A specific PTID means `step only this process id'.  */
+  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
+    {
+      resume_all = 1;
+      ptid = inferior_ptid;
+    }
+  else
+    resume_all = 0;
+  
+  pid = ptid_get_pid (ptid);
+
+  if (catch_syscall_enabled () > 0)
+    request = PT_SYSCALL;
+  else
+    request = PT_CONTINUE;
+
+  if (step)
+    {
+      /* If this system does not support PT_STEP, a higher level
+         function will have called single_step() to transmute the step
+         request into a continue request (by setting breakpoints on
+         all possible successor instructions), so we don't have to
+         worry about that here.  */
+      request = PT_STEP;
+    }
+
+  /* An address of (PTRACE_TYPE_ARG3)1 tells ptrace to continue from
+     where it was.  If GDB wanted it to start some other way, we have
+     already written a new program counter value to the child.  */
+  errno = 0;
+  if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTR: %s ptid %d,%ld, %s, signal %d\n",
+			(step ? "stepping" : "resuming"),
+			PIDGET (ptid), TIDGET (ptid),
+			(resume_all ? "all threads" : "single thread"),
+			signal);
+
+  /* Assume not stepping some LWP ID.  */
+  step_lwpid = 0;
+  if (step)
+    {
+      step_lwpid = TIDGET (ptid);
+      if (step_lwpid < 0)
+	step_lwpid = -step_lwpid;
+    }	
+  
+  if (resume_all)
+    {
+      if (pending_sigs > 0)
+	{
+	  /* We have pending signals from the previous wait still 
+	     needing to be delivered.  So don't resume the process,
+	     instead take no action and we'll deliver one of those
+	     pending signals at the next wait.  */
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTR: ptid %d,%ld has %d pending signals, skipping resume\n",
+				PIDGET (ptid), TIDGET (ptid), pending_sigs);
+	  return;
+	}
+      if (step)
+	ptrace (request, pid, (PTRACE_TYPE_ARG3)1, TIDGET (ptid));
+      else
+	ptrace (request, pid, (PTRACE_TYPE_ARG3)1,
+		target_signal_to_host (signal));
+    }
+  else
+    ptrace (request, pid, (PTRACE_TYPE_ARG3)1, -TIDGET (ptid));
+  
+  if (errno != 0)
+    perror_with_name (("ptrace"));
+}
+
+/* Wait for the child specified by PTID to do something.  Return the
+   process ID of the child, or MINUS_ONE_PTID in case of error; store
+   the status in *OURSTATUS.  */
+
+static ptid_t
+nbsd_thread_wait2 (struct target_ops *ops, ptid_t ptid,
+		   struct target_waitstatus *ourstatus, int options)
+{
+  pid_t pid;
+  int status, save_errno;
+
+  do
+    {
+      set_sigint_trap ();
+
+      do
+	{
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTW2: waiting for ptid %d,%ld, opt %d\n",
+				PIDGET (ptid), TIDGET (ptid), options);
+
+	  pid = waitpid (ptid_get_pid (ptid), &status, options);
+	  save_errno = errno;
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTW2: waitpid errno is %d, pid %d, status %x\n",
+				save_errno, pid, status);
+	}
+      while (pid == -1 && save_errno == EINTR);
+
+      clear_sigint_trap ();
+
+      /* If nothing found in the no wait case, report that.  */
+      if (options == WNOHANG && pid == 0)
+	return pid_to_ptid (-1);
+      
+      if (pid == -1)
+	{
+	  fprintf_unfiltered (gdb_stderr,
+			      _("Child process unexpectedly missing: %s.\n"),
+			      safe_strerror (save_errno));
+
+	  /* Claim it exited with unknown signal.  */
+	  ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+	  ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
+	  return inferior_ptid;
+	}
+
+      /* Ignore terminated detached child processes.  */
+      if (!WIFSTOPPED (status) && pid != ptid_get_pid (inferior_ptid))
+	pid = -1;
+    }
+  while (pid == -1);
+
+  store_waitstatus (ourstatus, status);
+  return pid_to_ptid (pid);
+}
+
+static int
+nbsd_thread_cancel_breakpoint (struct lwp_info *lp)
+{
+  /* Arrange for a breakpoint to be hit again later.  We don't keep
+     the SIGTRAP status and don't forward the SIGTRAP signal to the
+     LWP.  We will handle the current event, eventually we will resume
+     this LWP, and this breakpoint will trap again.
+
+     If we do not do this, then we run the risk that the user will
+     delete or disable the breakpoint, but the LWP will have already
+     tripped on it.  */
+
+  struct regcache *regcache = get_thread_regcache (lp->ptid);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  CORE_ADDR pc;
+
+  pc = regcache_read_pc (regcache) - gdbarch_decr_pc_after_break (gdbarch);
+  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
+    {
+      if (debug_nbsd_thread)
+	fprintf_unfiltered (gdb_stdlog,
+			    "NTCB: Push back breakpoint for ptid %d,%ld\n",
+			    PIDGET (lp->ptid), TIDGET (lp->ptid));
+
+      /* Back up the PC if necessary.  */
+      if (gdbarch_decr_pc_after_break (gdbarch))
+	regcache_write_pc (regcache, pc);
+
+      /* We no longer have a pending signal for this thread.  */
+      lp->signalled = 0;
+    }
+  return 0;
+}
+
+/* The "to_wait" method of the nbsd_thread_ops.  */
+
+static ptid_t
+nbsd_thread_wait (struct target_ops *ops,
+		 ptid_t ptid, struct target_waitstatus *status, int options)
+{
+  ptid_t active_ptid;
+  struct lwp_info *sel_thread;
+  int sig_threads, i;
+  struct target_waitstatus tstatus;
+
+  /* If there were pending signals and a resume all threads was done,
+     the process wasn't actually resumed so don't wait on it.  Just
+     go on to pick a thread to report on.  */
+  if (!(pending_sigs > 0 && resume_all))
+    {
+      ptid = nbsd_thread_wait2 (ops, ptid, &tstatus, 0);
+
+      /* Default status returned is the one we just got.  */
+      *status = tstatus;
+      
+      /* The ptid returned by the target beneath us is the ptid of the process.
+	 We need to find which thread is currently active and return 
+	 its ptid.  */
+      update_lwpbuf (&tstatus);
+  
+      /* Loop checking for additional threads that are waiting, and gather
+	 up their status.  */
+      while (1)
+	{
+	  ptid = nbsd_thread_wait2 (ops, ptid, &tstatus, WNOHANG);
+	  if (PIDGET (ptid) == -1)
+	    break;
+	  update_lwpbuf (&tstatus);
+	}
+    }
+  
+  /* Find a suitable signalled thread.  Pick the stepped one, if there
+     is one; otherwise pick a random one.  */
+  sel_thread = NULL;
+  sig_threads = 0;
+  
+  for (i = 0; i < lwp_count; i++)
+    {
+      if (lwp_buffer[i].signalled)
+	{
+	  sig_threads++;
+	  if (TIDGET (lwp_buffer[i].ptid) == step_lwpid)
+	    {
+	      /* If there is a stepped thread, pick that one.  */
+	      sel_thread = &lwp_buffer[i];
+	      if (debug_nbsd_thread)
+		fprintf_unfiltered (gdb_stdlog,
+				    "NTW: Picking ptid %d,%ld because it is stepped\n",
+				    PIDGET (sel_thread->ptid),
+				    TIDGET (sel_thread->ptid));
+	      break;
+	    }
+	  /* Randomly pick this one or keep the previous choice,
+	     such that all of the signalled threads have an equal
+	     probability of being picked.  */
+	  if (sel_thread == NULL || 
+	      (((double) rand ()) / (RAND_MAX + 1.0)) < (1.0 / sig_threads))
+	    {
+	      sel_thread = &lwp_buffer[i];
+	      if (debug_nbsd_thread)
+		fprintf_unfiltered (gdb_stdlog,
+				    "NTW: Picking ptid %d,%ld out of %d\n",
+				    PIDGET (sel_thread->ptid),
+				    TIDGET (sel_thread->ptid), sig_threads);
+	    }
+	}
+    }
+  
+  /* Scan the LWP table again.  For each signalled LWP other than the
+     chosen one, back it up to the breakpoint if it was stopped by a
+     breakpoint and mark it as not signalled (it will re-break next
+     time we run the whole process).  Other LWPs (those with signals
+     other than breakpoint stop) are counted but not backed up; if we
+     find any of those then those will be delivered next.  */
+  pending_sigs = 0;
+  if (sig_threads > 1)
+    {
+      for (i = 0; i < lwp_count; i++)
+	{
+	  /* Skip the selected LWP.  */
+	  if (&lwp_buffer[i] == sel_thread)
+	    continue;
+	  
+	  if (lwp_buffer[i].signalled)
+	    {
+	      if (WIFSTOPPED (lwp_buffer[i].waitstatus))
+		{
+		  if (!nbsd_thread_cancel_breakpoint (&lwp_buffer[i]))
+		    pending_sigs++;
+		}
+	      else
+		pending_sigs++;
+	    }
+	}
+    }
+  
+  ptid = inferior_ptid;
+  if (sel_thread != NULL)
+    {
+      ptid =  MERGEPID (PIDGET (ptid), TIDGET (sel_thread->ptid));
+      *status = tstatus;
+      
+      /* The signal for this thread is now being reported, so clear
+	 the flag that says it hasn't been reported yet.  */
+      sel_thread->signalled = 0;
+    }
+  else if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTW: no signalled thread\n");
+
+  if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTW: returning ptid %d,%ld\n",
+			PIDGET (ptid), TIDGET (ptid));
+
+  return ptid;
+}
+
+/* The "to_mourn_inferior" method of the nbsd_thread_ops.  */
+
+static void
+nbsd_thread_mourn_inferior (struct target_ops *ops)
+{
+  int status;
+
+  /* Wait just one more time to collect the inferior's exit status.
+     Do not check whether this succeeds though, since we may be
+     dealing with a process that we attached to.  Such a process will
+     only report its exit status to its original parent.  */
+  waitpid (ptid_get_pid (inferior_ptid), &status, WNOHANG);
+
+  generic_mourn_inferior ();
+
+  if (!have_inferiors ())
+    unpush_target (ops);
+}
+
+
+/* The "to_thread_alive" method of the nbsd_thread_ops.  */
+static int
+nbsd_thread_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+  /* The thread list maintained by GDB is up to date, since we update
+     it everytime we stop.   So check this list.  */
+  return in_thread_list (ptid);
+}
+
+/* The "to_pid_to_str" method of the nbsd_thread_ops.  */
+
+static char *
+nbsd_thread_pid_to_str (struct target_ops *ops, ptid_t ptid)
+{
+  if (TIDGET (ptid) == 0)
+    {
+      struct target_ops *beneath = find_target_beneath (ops);
+
+      return beneath->to_pid_to_str (beneath, ptid);
+    }
+  return xstrprintf (_("Thread %ld"), TIDGET (ptid));
+}
+
+/* A "new-objfile" observer.  Used to activate/deactivate netbsd-thread
+   support.  */
+
+static void
+nbsd_thread_new_objfile_observer (struct objfile *objfile)
+{
+  if (objfile != NULL)
+     enable_nbsd_thread ();
+  else
+     disable_nbsd_thread ();
+}
+
+static void
+init_nbsd_thread_ops (void)
+{
+  nbsd_thread_ops.to_shortname          = "netbsd-threads";
+  nbsd_thread_ops.to_longname           = _("NetBSD threads support");
+  nbsd_thread_ops.to_doc                = _("NetBSD threads support");
+  nbsd_thread_ops.to_detach             = nbsd_thread_detach;
+  nbsd_thread_ops.to_resume             = nbsd_thread_resume;
+  nbsd_thread_ops.to_wait               = nbsd_thread_wait;
+  nbsd_thread_ops.to_mourn_inferior     = nbsd_thread_mourn_inferior;
+  nbsd_thread_ops.to_thread_alive       = nbsd_thread_thread_alive;
+  nbsd_thread_ops.to_pid_to_str         = nbsd_thread_pid_to_str;
+  nbsd_thread_ops.to_stratum            = thread_stratum;
+  nbsd_thread_ops.to_magic              = OPS_MAGIC;
+}
+
+void
+_initialize_nbsd_thread (void)
+{
+  init_nbsd_thread_ops ();
+  add_target (&nbsd_thread_ops);
+
+  add_setshow_zinteger_cmd ("nbsd-thread", class_maintenance,
+			    &debug_nbsd_thread, _("\
+Set debugging of NetBSD thread module."), _("\
+Show debugging of NetBSD thread module."), _("\
+Enables printf debugging output."),
+			    NULL,
+			    show_debug_nbsd_thread,
+			    &setdebuglist, &showdebuglist);
+
+  observer_attach_new_objfile (nbsd_thread_new_objfile_observer);
+}
Index: gdb/config/i386/nbsdelf.mh
===================================================================
RCS file: /cvs/src/src/gdb/config/i386/nbsdelf.mh,v
retrieving revision 1.24
diff -u -r1.24 nbsdelf.mh
--- gdb/config/i386/nbsdelf.mh	17 Dec 2006 13:30:44 -0000	1.24
+++ gdb/config/i386/nbsdelf.mh	20 Apr 2010 15:37:49 -0000
@@ -1,5 +1,5 @@
 # Host: NetBSD/i386 ELF
 NATDEPFILES= fork-child.o inf-ptrace.o \
-	nbsd-nat.o i386bsd-nat.o i386nbsd-nat.o bsd-kvm.o
+	nbsd-nat.o nbsd-thread.o i386bsd-nat.o i386nbsd-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
Index: gdb/config/mips/nbsd.mh
===================================================================
RCS file: /cvs/src/src/gdb/config/mips/nbsd.mh,v
retrieving revision 1.3
diff -u -r1.3 nbsd.mh
--- gdb/config/mips/nbsd.mh	31 Oct 2004 20:47:55 -0000	1.3
+++ gdb/config/mips/nbsd.mh	20 Apr 2010 15:37:49 -0000
@@ -1,2 +1,2 @@
 # Host: NetBSD/mips
-NATDEPFILES= fork-child.o inf-ptrace.o mipsnbsd-nat.o
+NATDEPFILES= fork-child.o inf-ptrace.o nbsd-thread.o mipsnbsd-nat.o

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] Thread debug support for NetBSD 5
  2010-04-20 15:49 [RFC] Thread debug support for NetBSD 5 Paul Koning
@ 2010-04-22 15:36 ` Paul Koning
  2010-04-29 14:49   ` [PING] " Paul Koning
  0 siblings, 1 reply; 16+ messages in thread
From: Paul Koning @ 2010-04-22 15:36 UTC (permalink / raw)
  To: gdb-patches

Any comments?

Attached is a slightly updated version of the patch I sent earlier.
The change affects a few lines in the new file (nbsd-thread.c) to cure
an infinite loop if the child process exits.  Credit to Antti Kantee
for help with this.

    paul

Excerpt of message (sent 20 April 2010) by Paul Koning:
> This patch adds thread debug support for NetBSD 5.0.  Unlike older
> NetBSDs, in that version threads are seen by the kernel and exposed
> via ptrace() machinery.  The patch makes some small changes in
> existing files, mainly to move some code into NetBSD specific files to
> isolate the changes, and it adds a new file nbsd-thread.c.  The
> machinery in that file is loosely based on existing examples in
> dec-thread.c and linux-nat.c.
> 
> Tested on NetBSD on i386 and mipsel platforms.  I did not make changes
> in target specific code for other platforms because I don't have them;
> the changes should be pretty obvious given what is changed in
> mipsnbsd-nat.c. 
> 
> I don't have write privs in GDB as far as I know, but all the
> paperwork is in place.
> 
> 	  paul
2010-04-22  Paul Koning  <paul_koning@dell.com>

	* i386bsd-nat.c (i386bsd_supply_gregset, i386bsd_collect_gregset):
	Make global.
	* i386bsd-nat.h: Ditto.
	* i386nbsd-nat.c: Include inferior.h, i387-tdep.h, sys/ptrace.h,
	machine/reg.h.
	(i386nbsd_fetch_inferior_registers,
	i386nbsd_store_inferior_registers): New.
	* mipsnbsd-nat.c (mipsnbsd_fetch_inferior_registers,
	mipsnbsd_store_inferior_registers): Pass thread ID to ptrace().
	* nbsd-thread.c: New file.
	* config/i386/nbsdelf.mh: Add nbsd-thread.o.
	* config/mips/nbsd.mh: Add nbsd-thread.o.

? gdb/changelog-entry
Index: gdb/i386bsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/i386bsd-nat.c,v
retrieving revision 1.43
diff -u -p -r1.43 i386bsd-nat.c
--- gdb/i386bsd-nat.c	1 Jan 2010 07:31:36 -0000	1.43
+++ gdb/i386bsd-nat.c	22 Apr 2010 15:21:55 -0000
@@ -88,7 +88,7 @@ static int have_ptrace_xmmregs = -1;
 
 /* Supply the general-purpose registers in GREGS, to REGCACHE.  */
 
-static void
+void
 i386bsd_supply_gregset (struct regcache *regcache, const void *gregs)
 {
   const char *regs = gregs;
@@ -107,7 +107,7 @@ i386bsd_supply_gregset (struct regcache 
    GREGS.  If REGNUM is -1, collect and store all appropriate
    registers.  */
 
-static void
+void
 i386bsd_collect_gregset (const struct regcache *regcache,
 			 void *gregs, int regnum)
 {
Index: gdb/i386bsd-nat.h
===================================================================
RCS file: /cvs/src/src/gdb/i386bsd-nat.h,v
retrieving revision 1.8
diff -u -p -r1.8 i386bsd-nat.h
--- gdb/i386bsd-nat.h	1 Jan 2010 07:31:36 -0000	1.8
+++ gdb/i386bsd-nat.h	22 Apr 2010 15:21:55 -0000
@@ -35,4 +35,11 @@ extern void i386bsd_dr_reset_addr (int r
 
 extern unsigned long i386bsd_dr_get_status (void);
 
+extern void i386bsd_supply_gregset (struct regcache *regcache, 
+                                    const void *gregs);
+
+extern void i386bsd_collect_gregset (const struct regcache *regcache,
+                                     void *gregs, int regnum);
+
+
 #endif /* i386bsd-nat.h */
Index: gdb/i386nbsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/i386nbsd-nat.c,v
retrieving revision 1.22
diff -u -p -r1.22 i386nbsd-nat.c
--- gdb/i386nbsd-nat.c	1 Jan 2010 07:31:36 -0000	1.22
+++ gdb/i386nbsd-nat.c	22 Apr 2010 15:21:55 -0000
@@ -19,16 +19,20 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "inferior.h"
 #include "gdbcore.h"
 #include "regcache.h"
 #include "target.h"
 
 #include "i386-tdep.h"
+#include "i387-tdep.h"
 #include "i386bsd-nat.h"
 
 /* Support for debugging kernel virtual memory images.  */
 
 #include <sys/types.h>
+#include <sys/ptrace.h>
+#include <machine/reg.h>
 #include <machine/frame.h>
 #include <machine/pcb.h>
 
@@ -71,6 +75,131 @@ i386nbsd_supply_pcb (struct regcache *re
 
   return 1;
 }
+
+/* Macro to determine if a register is fetched with PT_GETREGS.  */
+#define GETREGS_SUPPLIES(regnum) \
+  ((0 <= (regnum) && (regnum) <= 15))
+
+#ifdef HAVE_PT_GETXMMREGS
+/* Set to 1 if the kernel supports PT_GETXMMREGS.  Initialized to -1
+   so that we try PT_GETXMMREGS the first time around.  */
+static int have_ptrace_xmmregs = -1;
+#endif
+\f
+
+/* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
+   for all registers (including the floating point registers).  */
+
+static void
+i386nbsd_fetch_inferior_registers (struct target_ops *ops,
+				   struct regcache *regcache, int regnum)
+{
+  if (regnum == -1 || GETREGS_SUPPLIES (regnum))
+    {
+      struct reg regs;
+
+      if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
+	perror_with_name (_("Couldn't get registers"));
+
+      i386bsd_supply_gregset (regcache, &regs);
+      if (regnum != -1)
+	return;
+    }
+
+  if (regnum == -1 || regnum >= I386_ST0_REGNUM)
+    {
+      struct fpreg fpregs;
+#ifdef HAVE_PT_GETXMMREGS
+      char xmmregs[512];
+
+      if (have_ptrace_xmmregs != 0
+	  && ptrace(PT_GETXMMREGS, PIDGET (inferior_ptid),
+		    (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == 0)
+	{
+	  have_ptrace_xmmregs = 1;
+	  i387_supply_fxsave (regcache, -1, xmmregs);
+	}
+      else
+	{
+          if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
+	    perror_with_name (_("Couldn't get floating point status"));
+
+	  i387_supply_fsave (regcache, -1, &fpregs);
+	}
+#else
+      if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
+	perror_with_name (_("Couldn't get floating point status"));
+
+      i387_supply_fsave (regcache, -1, &fpregs);
+#endif
+    }
+}
+
+/* Store register REGNUM back into the inferior.  If REGNUM is -1, do
+   this for all registers (including the floating point registers).  */
+
+static void
+i386nbsd_store_inferior_registers (struct target_ops *ops,
+				   struct regcache *regcache, int regnum)
+{
+  if (regnum == -1 || GETREGS_SUPPLIES (regnum))
+    {
+      struct reg regs;
+
+      if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
+                  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
+        perror_with_name (_("Couldn't get registers"));
+
+      i386bsd_collect_gregset (regcache, &regs, regnum);
+
+      if (ptrace (PT_SETREGS, PIDGET (inferior_ptid),
+	          (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
+        perror_with_name (_("Couldn't write registers"));
+
+      if (regnum != -1)
+	return;
+    }
+
+  if (regnum == -1 || regnum >= I386_ST0_REGNUM)
+    {
+      struct fpreg fpregs;
+#ifdef HAVE_PT_GETXMMREGS
+      char xmmregs[512];
+
+      if (have_ptrace_xmmregs != 0
+	  && ptrace(PT_GETXMMREGS, PIDGET (inferior_ptid),
+		    (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == 0)
+	{
+	  have_ptrace_xmmregs = 1;
+
+	  i387_collect_fxsave (regcache, regnum, xmmregs);
+
+	  if (ptrace (PT_SETXMMREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == -1)
+            perror_with_name (_("Couldn't write XMM registers"));
+	}
+      else
+	{
+	  have_ptrace_xmmregs = 0;
+#endif
+          if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
+	    perror_with_name (_("Couldn't get floating point status"));
+
+          i387_collect_fsave (regcache, regnum, &fpregs);
+
+          if (ptrace (PT_SETFPREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
+	    perror_with_name (_("Couldn't write floating point status"));
+#ifdef HAVE_PT_GETXMMREGS
+        }
+#endif
+    }
+}
+
 \f
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
@@ -84,6 +213,8 @@ _initialize_i386nbsd_nat (void)
   /* Add some extra features to the common *BSD/i386 target.  */
   t = i386bsd_target ();
   t->to_pid_to_exec_file = nbsd_pid_to_exec_file;
+  t->to_fetch_registers = i386nbsd_fetch_inferior_registers;
+  t->to_store_registers = i386nbsd_store_inferior_registers;
   add_target (t);
  
   /* Support debugging kernel virtual memory images.  */
Index: gdb/mipsnbsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/mipsnbsd-nat.c,v
retrieving revision 1.17
diff -u -p -r1.17 mipsnbsd-nat.c
--- gdb/mipsnbsd-nat.c	1 Jan 2010 07:31:37 -0000	1.17
+++ gdb/mipsnbsd-nat.c	22 Apr 2010 15:21:55 -0000
@@ -49,7 +49,7 @@ mipsnbsd_fetch_inferior_registers (struc
       struct reg regs;
 
       if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get registers"));
       
       mipsnbsd_supply_reg (regcache, (char *) &regs, regno);
@@ -62,7 +62,7 @@ mipsnbsd_fetch_inferior_registers (struc
       struct fpreg fpregs;
 
       if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get floating point status"));
 
       mipsnbsd_supply_fpreg (regcache, (char *) &fpregs, regno);
@@ -79,13 +79,13 @@ mipsnbsd_store_inferior_registers (struc
       struct reg regs;
 
       if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get registers"));
 
       mipsnbsd_fill_reg (regcache, (char *) &regs, regno);
 
       if (ptrace (PT_SETREGS, PIDGET (inferior_ptid), 
-		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't write registers"));
 
       if (regno != -1)
@@ -97,13 +97,13 @@ mipsnbsd_store_inferior_registers (struc
       struct fpreg fpregs; 
 
       if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get floating point status"));
 
       mipsnbsd_fill_fpreg (regcache, (char *) &fpregs, regno);
 
       if (ptrace (PT_SETFPREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't write floating point status"));
     }
 }
Index: gdb/nbsd-thread.c
===================================================================
RCS file: gdb/nbsd-thread.c
diff -N gdb/nbsd-thread.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdb/nbsd-thread.c	22 Apr 2010 15:21:55 -0000
@@ -0,0 +1,698 @@
+/* Threads support for NetBSD 5.0.
+
+   Copyright (C) 2010 Free Software Foundation, Inc.
+
+   Contributed by Paul Koning, Dell, inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "observer.h"
+#include "target.h"
+#include "inferior.h"
+#include "gdbthread.h"
+#include "regcache.h"
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+
+/* Data structure used to track NetBSD thread state.  There is a 
+   vector of these, in ascending order of LWP ID.  */
+
+struct lwp_info
+{
+  /* The process ID of the LWP.  This is a combination of the process
+     ID and the LWP ID.  */
+  ptid_t ptid;
+  
+  /* Non-zero if we this LWP was reported as having been signalled
+     by the PT_LWPINFO ptrace() call.  */
+  int signalled;
+  
+  /* The waitstatus for this LWP's last event.  */
+  struct target_waitstatus waitstatus;
+};
+
+/* The lwp_info buffer and its control variables.  */
+static struct lwp_info *lwp_buffer;
+static int lwp_count;
+static int lwp_bufsize;
+
+/* Count of signals still pending delivery to GDB.  These are threads
+   that were found to be stopped and not breakpoints.  For threads that
+   hit a breakpoint, we simply push back the thread so it will hit the
+   break again (if it isn't removed before then) but for other signals,
+   for example faults, the signal remains pending, the "to_resume" that
+   resumes the whole process is skipped, and then the "to_wait" returns
+   the information about one of the pending signals instead.  */
+static int pending_sigs;
+
+/* The LWP ID of the thread being stepped, or 0 if none.  */
+static int step_lwpid;
+
+/* Flag to indicate whether last resume was a resume all threads or
+   a resume single thread.  */
+static int resume_all;
+
+/* Non-zero if the netbsd-thread layer is active.  */
+static int nbsd_thread_active = 0;
+
+/* The netbsd-thread target_ops structure.  */
+static struct target_ops nbsd_thread_ops;
+
+int debug_nbsd_thread;
+static void
+show_debug_nbsd_thread (struct ui_file *file, int from_tty,
+		      struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Debugging of NetBSD thread module is %s.\n"),
+		    value);
+}
+
+
+/* Activate thread support if appropriate.  Do nothing if thread
+   support is already active.  */
+
+static void
+enable_nbsd_thread (void)
+{
+  struct minimal_symbol *msym;
+  void* caller_context;
+  int status;
+
+  /* If already active, nothing more to do.  */
+  if (nbsd_thread_active)
+    return;
+
+  if (lwp_buffer != NULL)
+    {
+      xfree (lwp_buffer);
+      lwp_buffer = NULL;
+      lwp_count = lwp_bufsize = 0;
+    }
+  
+  push_target (&nbsd_thread_ops);
+  nbsd_thread_active = 1;
+}
+
+/* Deactivate thread support.  Do nothing is thread support is
+   already inactive.  */
+
+static void
+disable_nbsd_thread (void)
+{
+  if (!nbsd_thread_active)
+    return;
+
+  unpush_target (&nbsd_thread_ops);
+  nbsd_thread_active = 0;
+}
+
+/* Update our lwp_info buffer, and tell GDB about adds or deletes.
+
+   By doing the thread add and thread delete operations here as we
+   learn about threads, we allow users to run thread-specific commands
+   without needing to run "info threads" first. 
+
+   The argument is a pointer to the waitstatus struct, which
+   is copied into the waitstatus for the thread we find as the signalled
+   thread.  */
+
+static void
+update_lwpbuf (struct target_waitstatus *status)
+{
+  int pi;
+  lwpid_t lwp_id, sig_lwpid;
+  struct ptrace_lwpinfo pt_info;
+  ptid_t ptid;
+  
+  /* Accumulate an array of NetBSD threads, in descending order of LWP id.
+
+     The reason for using descending order is that this is the order
+     in which LWPs are returned by the ptrace() PT_LWPINFO function,
+     because it returns them in the order in which they exist in the
+     p_lwps list for the process, and new entries are assigned ascending
+     LWP IDs and are added to the head of that list.  */
+
+  lwp_id = sig_lwpid = 0;
+  pi = 0;
+  
+  while (1)
+    {
+      pt_info.pl_lwpid = lwp_id;
+      errno = 0;
+      if (ptrace (PT_LWPINFO, PIDGET (inferior_ptid),
+		  (PTRACE_TYPE_ARG3) &pt_info, sizeof (pt_info)) == -1)
+	{
+	  if (errno == ESRCH)
+	    break;
+	  else if (errno != 0)
+	    perror_with_name (_("Couldn't get thread information"));
+	}
+
+      if (debug_nbsd_thread)
+	fprintf_unfiltered (gdb_stdlog,
+			    "NTUP: lwpinfo on %d returns lwp %d, pl_event %d\n",
+			    lwp_id, pt_info.pl_lwpid, pt_info.pl_event);
+	  
+      /* Retrieve the LWP ID that was found.  This also sets the ID to
+	 start from the next time around the loop.  */
+      lwp_id = pt_info.pl_lwpid;
+
+      /* LWP id 0 is end of list.  */
+      if (lwp_id == 0)
+	break;
+      
+      /* If the LWP we found has an ID less than the ID of the current
+	 buffer entry, then the current buffer entry is a deleted thread.
+	 Tell GDB about its demise, then remove it from the buffer. 
+	  
+	 We have to do this in a loop until we run out of threads
+	 to be removed.  */
+      while (pi < lwp_count && lwp_id < TIDGET (lwp_buffer[pi].ptid))
+	{
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTUP: thread ptid %d,%ld has disappeared\n",
+				PIDGET (lwp_buffer[pi].ptid),
+				TIDGET (lwp_buffer[pi].ptid));
+
+	  /* Tell GDB.  */
+	  delete_thread (lwp_buffer[pi].ptid);
+
+	  /* Remove the deleted entry.  */
+	  if (pi < lwp_count)
+	    memmove (lwp_buffer + pi + 1, lwp_buffer + pi, 
+		     (lwp_count - pi) * sizeof (struct lwp_info));
+	  lwp_count--;
+	}
+
+      /* If we're now at the end of the current buffer, or the LWP found
+	 has an LWP ID greater than the current entry in the buffer, this
+	 is a new thread.  Allocate more buffer space if need be,
+	 make room for this entry, and store it.  Then tell GDB about
+	 the new thread.  */
+      if (pi >= lwp_count || lwp_id > TIDGET (lwp_buffer[pi].ptid))
+	{
+	  /* Allocate more space, if we need it.  */
+	  if (lwp_count == lwp_bufsize)
+	    {
+	      if (lwp_bufsize)
+		lwp_bufsize *= 2;
+	      else
+		lwp_bufsize = 1;
+	      lwp_buffer = (struct lwp_info *) xrealloc (lwp_buffer, 
+							 lwp_bufsize * sizeof (struct lwp_info));
+	    }
+
+	  /* Push current and later entries, if any, over.  */
+	  if (pi < lwp_count)
+	    memmove (lwp_buffer + pi + 1, lwp_buffer + pi, 
+		     (lwp_count - pi) * sizeof (struct lwp_info));
+
+	  /* Update the count of LWPs.  */
+	  lwp_count++;
+
+	  /* Initialize the new entry.  */
+	  lwp_buffer[pi].ptid = MERGEPID (PIDGET (inferior_ptid), lwp_id);
+	  if (pt_info.pl_event == PL_EVENT_SIGNAL)
+	    {
+	      lwp_buffer[pi].signalled = 1;
+	      lwp_buffer[pi].waitstatus = *status;
+	      sig_lwpid = lwp_id;
+	    }
+	  else
+	    lwp_buffer[pi].signalled = 0;
+
+	  /* Advance the LWP buffer pointer.  */
+	  pi++;
+	  
+	  /* Tell GDB about the new thread.  */
+	  if (lwp_count == 1)
+	    {
+	      /* See if GDB still has TID zero, if so set the TID.  */
+	      if (TIDGET (inferior_ptid) == 0)
+		{
+		  ptid = MERGEPID (PIDGET (inferior_ptid), lwp_id);
+		  thread_change_ptid (inferior_ptid, ptid);
+		  if (debug_nbsd_thread)
+		    fprintf_unfiltered (gdb_stdlog,
+					"NTUP: setting main thread ptid to %d,%ld\n",
+					PIDGET (ptid), TIDGET (ptid));
+		}
+	    }
+	  else
+	    {
+	      /* New thread but not the first, add it to GDB.  */
+	      if (debug_nbsd_thread)
+		fprintf_unfiltered (gdb_stdlog,
+				    "NTUP: adding new thread ptid %d,%d\n",
+				    PIDGET (inferior_ptid), lwp_id);
+	      add_thread (MERGEPID (PIDGET (inferior_ptid), lwp_id));
+	    }
+	}
+      else
+	{
+	  /* Found an existing thread.  Update its status in the buffer.
+	     Note that we clear the signalled flag if this is the first
+	     call and this thread wasn't the signalled thread, but we 
+	     leave it alone on subsequent calls.  That way the subsequent
+	     calls will accumulate the set of signalled threads.  */
+	  if (pt_info.pl_event == PL_EVENT_SIGNAL)
+	    {
+	      lwp_buffer[pi].signalled = 1;
+	      lwp_buffer[pi].waitstatus = *status;
+	      sig_lwpid = lwp_id;
+	    }
+	  
+	  /* Advance the LWP buffer pointer.  */
+	  pi++;
+	}
+    }
+  if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTUP: signalled thread lwpid is %d\n", sig_lwpid);
+
+}
+
+/* The "to_detach" method of the nbsd_thread_ops.  */
+
+static void
+nbsd_thread_detach (struct target_ops *ops, char *args, int from_tty)
+{   
+  struct target_ops *beneath = find_target_beneath (ops);
+
+  disable_nbsd_thread ();
+  beneath->to_detach (beneath, args, from_tty);
+}
+
+/* Resume execution of thread PTID, or all threads if PTID is -1.  If
+   STEP is nonzero, single-step it.  If SIGNAL is nonzero, give it
+   that signal.  */
+
+static void
+nbsd_thread_resume (struct target_ops *ops,
+		    ptid_t ptid, int step, enum target_signal signal)
+{
+  pid_t pid;
+  int request;
+  
+  /* A specific PTID means `step only this process id'.  */
+  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
+    {
+      resume_all = 1;
+      ptid = inferior_ptid;
+    }
+  else
+    resume_all = 0;
+  
+  pid = ptid_get_pid (ptid);
+
+  if (catch_syscall_enabled () > 0)
+    request = PT_SYSCALL;
+  else
+    request = PT_CONTINUE;
+
+  if (step)
+    {
+      /* If this system does not support PT_STEP, a higher level
+         function will have called single_step() to transmute the step
+         request into a continue request (by setting breakpoints on
+         all possible successor instructions), so we don't have to
+         worry about that here.  */
+      request = PT_STEP;
+    }
+
+  /* An address of (PTRACE_TYPE_ARG3)1 tells ptrace to continue from
+     where it was.  If GDB wanted it to start some other way, we have
+     already written a new program counter value to the child.  */
+  errno = 0;
+  if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTR: %s ptid %d,%ld, %s, signal %d\n",
+			(step ? "stepping" : "resuming"),
+			PIDGET (ptid), TIDGET (ptid),
+			(resume_all ? "all threads" : "single thread"),
+			signal);
+
+  /* Assume not stepping some LWP ID.  */
+  step_lwpid = 0;
+  if (step)
+    {
+      step_lwpid = TIDGET (ptid);
+      if (step_lwpid < 0)
+	step_lwpid = -step_lwpid;
+    }	
+  
+  if (resume_all)
+    {
+      if (pending_sigs > 0)
+	{
+	  /* We have pending signals from the previous wait still 
+	     needing to be delivered.  So don't resume the process,
+	     instead take no action and we'll deliver one of those
+	     pending signals at the next wait.  */
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTR: ptid %d,%ld has %d pending signals, skipping resume\n",
+				PIDGET (ptid), TIDGET (ptid), pending_sigs);
+	  return;
+	}
+      if (step)
+	ptrace (request, pid, (PTRACE_TYPE_ARG3)1, TIDGET (ptid));
+      else
+	ptrace (request, pid, (PTRACE_TYPE_ARG3)1,
+		target_signal_to_host (signal));
+    }
+  else
+    ptrace (request, pid, (PTRACE_TYPE_ARG3)1, -TIDGET (ptid));
+  
+  if (errno != 0)
+    perror_with_name (("ptrace"));
+}
+
+/* Wait for the child specified by PTID to do something.  Return the
+   process ID of the child, or MINUS_ONE_PTID in case of error; store
+   the status in *OURSTATUS.  */
+
+static ptid_t
+nbsd_thread_wait2 (struct target_ops *ops, ptid_t ptid,
+		   struct target_waitstatus *ourstatus, int options)
+{
+  pid_t pid;
+  int status, save_errno;
+
+  do
+    {
+      set_sigint_trap ();
+
+      do
+	{
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTW2: waiting for ptid %d,%ld, opt %d\n",
+				PIDGET (ptid), TIDGET (ptid), options);
+
+	  pid = waitpid (ptid_get_pid (ptid), &status, options);
+	  save_errno = errno;
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTW2: waitpid errno is %d, pid %d, status %x\n",
+				save_errno, pid, status);
+	}
+      while (pid == -1 && save_errno == EINTR);
+
+      clear_sigint_trap ();
+
+      /* If nothing found in the no wait case, report that.  */
+      if (options == WNOHANG && pid == 0)
+	return pid_to_ptid (-1);
+      
+      if (pid == -1)
+	{
+	  fprintf_unfiltered (gdb_stderr,
+			      _("Child process unexpectedly missing: %s.\n"),
+			      safe_strerror (save_errno));
+
+	  /* If first wait, claim it exited with unknown signal; 
+	     else claim there is nothing left to wait for.  */
+	  if (options == WNOHANG)
+	    return pid_to_ptid (-1);
+	  ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+	  ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
+	  return inferior_ptid;
+	}
+
+      /* Ignore terminated detached child processes.  */
+      if (!WIFSTOPPED (status) && pid != ptid_get_pid (inferior_ptid))
+	pid = -1;
+    }
+  while (pid == -1);
+
+  store_waitstatus (ourstatus, status);
+  return pid_to_ptid (pid);
+}
+
+static int
+nbsd_thread_cancel_breakpoint (struct lwp_info *lp)
+{
+  /* Arrange for a breakpoint to be hit again later.  We don't keep
+     the SIGTRAP status and don't forward the SIGTRAP signal to the
+     LWP.  We will handle the current event, eventually we will resume
+     this LWP, and this breakpoint will trap again.
+
+     If we do not do this, then we run the risk that the user will
+     delete or disable the breakpoint, but the LWP will have already
+     tripped on it.  */
+
+  struct regcache *regcache = get_thread_regcache (lp->ptid);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  CORE_ADDR pc;
+
+  pc = regcache_read_pc (regcache) - gdbarch_decr_pc_after_break (gdbarch);
+  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
+    {
+      if (debug_nbsd_thread)
+	fprintf_unfiltered (gdb_stdlog,
+			    "NTCB: Push back breakpoint for ptid %d,%ld\n",
+			    PIDGET (lp->ptid), TIDGET (lp->ptid));
+
+      /* Back up the PC if necessary.  */
+      if (gdbarch_decr_pc_after_break (gdbarch))
+	regcache_write_pc (regcache, pc);
+
+      /* We no longer have a pending signal for this thread.  */
+      lp->signalled = 0;
+    }
+  return 0;
+}
+
+/* The "to_wait" method of the nbsd_thread_ops.  */
+
+static ptid_t
+nbsd_thread_wait (struct target_ops *ops,
+		 ptid_t ptid, struct target_waitstatus *status, int options)
+{
+  ptid_t active_ptid;
+  struct lwp_info *sel_thread;
+  int sig_threads, i;
+  struct target_waitstatus tstatus;
+
+  /* If there were pending signals and a resume all threads was done,
+     the process wasn't actually resumed so don't wait on it.  Just
+     go on to pick a thread to report on.  */
+  if (!(pending_sigs > 0 && resume_all))
+    {
+      ptid = nbsd_thread_wait2 (ops, ptid, &tstatus, 0);
+
+      /* Default status returned is the one we just got.  */
+      *status = tstatus;
+      
+      /* The ptid returned by the target beneath us is the ptid of the process.
+	 We need to find which thread is currently active and return 
+	 its ptid.  */
+      update_lwpbuf (&tstatus);
+  
+      /* Loop checking for additional threads that are waiting, and gather
+	 up their status.  */
+      while (1)
+	{
+	  ptid = nbsd_thread_wait2 (ops, ptid, &tstatus, WNOHANG);
+	  if (PIDGET (ptid) == -1)
+	    break;
+	  update_lwpbuf (&tstatus);
+	}
+    }
+  
+  /* Find a suitable signalled thread.  Pick the stepped one, if there
+     is one; otherwise pick a random one.  */
+  sel_thread = NULL;
+  sig_threads = 0;
+  
+  for (i = 0; i < lwp_count; i++)
+    {
+      if (lwp_buffer[i].signalled)
+	{
+	  sig_threads++;
+	  if (TIDGET (lwp_buffer[i].ptid) == step_lwpid)
+	    {
+	      /* If there is a stepped thread, pick that one.  */
+	      sel_thread = &lwp_buffer[i];
+	      if (debug_nbsd_thread)
+		fprintf_unfiltered (gdb_stdlog,
+				    "NTW: Picking ptid %d,%ld because it is stepped\n",
+				    PIDGET (sel_thread->ptid),
+				    TIDGET (sel_thread->ptid));
+	      break;
+	    }
+	  /* Randomly pick this one or keep the previous choice,
+	     such that all of the signalled threads have an equal
+	     probability of being picked.  */
+	  if (sel_thread == NULL || 
+	      (((double) rand ()) / (RAND_MAX + 1.0)) < (1.0 / sig_threads))
+	    {
+	      sel_thread = &lwp_buffer[i];
+	      if (debug_nbsd_thread)
+		fprintf_unfiltered (gdb_stdlog,
+				    "NTW: Picking ptid %d,%ld out of %d\n",
+				    PIDGET (sel_thread->ptid),
+				    TIDGET (sel_thread->ptid), sig_threads);
+	    }
+	}
+    }
+  
+  /* Scan the LWP table again.  For each signalled LWP other than the
+     chosen one, back it up to the breakpoint if it was stopped by a
+     breakpoint and mark it as not signalled (it will re-break next
+     time we run the whole process).  Other LWPs (those with signals
+     other than breakpoint stop) are counted but not backed up; if we
+     find any of those then those will be delivered next.  */
+  pending_sigs = 0;
+  if (sig_threads > 1)
+    {
+      for (i = 0; i < lwp_count; i++)
+	{
+	  /* Skip the selected LWP.  */
+	  if (&lwp_buffer[i] == sel_thread)
+	    continue;
+	  
+	  if (lwp_buffer[i].signalled)
+	    {
+	      if (WIFSTOPPED (lwp_buffer[i].waitstatus))
+		{
+		  if (!nbsd_thread_cancel_breakpoint (&lwp_buffer[i]))
+		    pending_sigs++;
+		}
+	      else
+		pending_sigs++;
+	    }
+	}
+    }
+  
+  ptid = inferior_ptid;
+  if (sel_thread != NULL)
+    {
+      ptid =  MERGEPID (PIDGET (ptid), TIDGET (sel_thread->ptid));
+      *status = tstatus;
+      
+      /* The signal for this thread is now being reported, so clear
+	 the flag that says it hasn't been reported yet.  */
+      sel_thread->signalled = 0;
+    }
+  else if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTW: no signalled thread\n");
+
+  if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTW: returning ptid %d,%ld\n",
+			PIDGET (ptid), TIDGET (ptid));
+
+  return ptid;
+}
+
+/* The "to_mourn_inferior" method of the nbsd_thread_ops.  */
+
+static void
+nbsd_thread_mourn_inferior (struct target_ops *ops)
+{
+  int status;
+
+  /* Wait just one more time to collect the inferior's exit status.
+     Do not check whether this succeeds though, since we may be
+     dealing with a process that we attached to.  Such a process will
+     only report its exit status to its original parent.  */
+  waitpid (ptid_get_pid (inferior_ptid), &status, WNOHANG);
+
+  generic_mourn_inferior ();
+
+  if (!have_inferiors ())
+    unpush_target (ops);
+}
+
+
+/* The "to_thread_alive" method of the nbsd_thread_ops.  */
+static int
+nbsd_thread_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+  /* The thread list maintained by GDB is up to date, since we update
+     it everytime we stop.   So check this list.  */
+  return in_thread_list (ptid);
+}
+
+/* The "to_pid_to_str" method of the nbsd_thread_ops.  */
+
+static char *
+nbsd_thread_pid_to_str (struct target_ops *ops, ptid_t ptid)
+{
+  if (TIDGET (ptid) == 0)
+    {
+      struct target_ops *beneath = find_target_beneath (ops);
+
+      return beneath->to_pid_to_str (beneath, ptid);
+    }
+  return xstrprintf (_("Thread %ld"), TIDGET (ptid));
+}
+
+/* A "new-objfile" observer.  Used to activate/deactivate netbsd-thread
+   support.  */
+
+static void
+nbsd_thread_new_objfile_observer (struct objfile *objfile)
+{
+  if (objfile != NULL)
+     enable_nbsd_thread ();
+  else
+     disable_nbsd_thread ();
+}
+
+static void
+init_nbsd_thread_ops (void)
+{
+  nbsd_thread_ops.to_shortname          = "netbsd-threads";
+  nbsd_thread_ops.to_longname           = _("NetBSD threads support");
+  nbsd_thread_ops.to_doc                = _("NetBSD threads support");
+  nbsd_thread_ops.to_detach             = nbsd_thread_detach;
+  nbsd_thread_ops.to_resume             = nbsd_thread_resume;
+  nbsd_thread_ops.to_wait               = nbsd_thread_wait;
+  nbsd_thread_ops.to_mourn_inferior     = nbsd_thread_mourn_inferior;
+  nbsd_thread_ops.to_thread_alive       = nbsd_thread_thread_alive;
+  nbsd_thread_ops.to_pid_to_str         = nbsd_thread_pid_to_str;
+  nbsd_thread_ops.to_stratum            = thread_stratum;
+  nbsd_thread_ops.to_magic              = OPS_MAGIC;
+}
+
+void
+_initialize_nbsd_thread (void)
+{
+  init_nbsd_thread_ops ();
+  add_target (&nbsd_thread_ops);
+
+  add_setshow_zinteger_cmd ("nbsd-thread", class_maintenance,
+			    &debug_nbsd_thread, _("\
+Set debugging of NetBSD thread module."), _("\
+Show debugging of NetBSD thread module."), _("\
+Enables printf debugging output."),
+			    NULL,
+			    show_debug_nbsd_thread,
+			    &setdebuglist, &showdebuglist);
+
+  observer_attach_new_objfile (nbsd_thread_new_objfile_observer);
+}
Index: gdb/config/i386/nbsdelf.mh
===================================================================
RCS file: /cvs/src/src/gdb/config/i386/nbsdelf.mh,v
retrieving revision 1.24
diff -u -p -r1.24 nbsdelf.mh
--- gdb/config/i386/nbsdelf.mh	17 Dec 2006 13:30:44 -0000	1.24
+++ gdb/config/i386/nbsdelf.mh	22 Apr 2010 15:21:55 -0000
@@ -1,5 +1,5 @@
 # Host: NetBSD/i386 ELF
 NATDEPFILES= fork-child.o inf-ptrace.o \
-	nbsd-nat.o i386bsd-nat.o i386nbsd-nat.o bsd-kvm.o
+	nbsd-nat.o nbsd-thread.o i386bsd-nat.o i386nbsd-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
Index: gdb/config/mips/nbsd.mh
===================================================================
RCS file: /cvs/src/src/gdb/config/mips/nbsd.mh,v
retrieving revision 1.3
diff -u -p -r1.3 nbsd.mh
--- gdb/config/mips/nbsd.mh	31 Oct 2004 20:47:55 -0000	1.3
+++ gdb/config/mips/nbsd.mh	22 Apr 2010 15:21:55 -0000
@@ -1,2 +1,2 @@
 # Host: NetBSD/mips
-NATDEPFILES= fork-child.o inf-ptrace.o mipsnbsd-nat.o
+NATDEPFILES= fork-child.o inf-ptrace.o nbsd-thread.o mipsnbsd-nat.o

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PING] [RFC] Thread debug support for NetBSD 5
  2010-04-22 15:36 ` Paul Koning
@ 2010-04-29 14:49   ` Paul Koning
  2010-04-29 15:39     ` Pedro Alves
                       ` (3 more replies)
  0 siblings, 4 replies; 16+ messages in thread
From: Paul Koning @ 2010-04-29 14:49 UTC (permalink / raw)
  To: gdb-patches

Ping... any comments?  

Who can submit this if it's ok?  I don't have write privs.

    paul

----------------------------------------------------------------
This patch adds thread debug support for NetBSD 5.0.  Unlike older
NetBSDs, in that version threads are seen by the kernel and exposed
via ptrace() machinery.  The patch makes some small changes in
existing files, mainly to move some code into NetBSD specific files to
isolate the changes, and it adds a new file nbsd-thread.c.  The
machinery in that file is loosely based on existing examples in
dec-thread.c and linux-nat.c.

Tested on NetBSD on i386 and mipsel platforms.  I did not make changes
in target specific code for other platforms because I don't have them;
the changes should be pretty obvious given what is changed in
mipsnbsd-nat.c. 

I don't have write privs in GDB as far as I know, but all the
paperwork is in place.

	  paul
2010-04-22  Paul Koning  <paul_koning@dell.com>

	* i386bsd-nat.c (i386bsd_supply_gregset, i386bsd_collect_gregset):
	Make global.
	* i386bsd-nat.h: Ditto.
	* i386nbsd-nat.c: Include inferior.h, i387-tdep.h, sys/ptrace.h,
	machine/reg.h.
	(i386nbsd_fetch_inferior_registers,
	i386nbsd_store_inferior_registers): New.
	* mipsnbsd-nat.c (mipsnbsd_fetch_inferior_registers,
	mipsnbsd_store_inferior_registers): Pass thread ID to ptrace().
	* nbsd-thread.c: New file.
	* config/i386/nbsdelf.mh: Add nbsd-thread.o.
	* config/mips/nbsd.mh: Add nbsd-thread.o.

? gdb/changelog-entry
Index: gdb/i386bsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/i386bsd-nat.c,v
retrieving revision 1.43
diff -u -p -r1.43 i386bsd-nat.c
--- gdb/i386bsd-nat.c	1 Jan 2010 07:31:36 -0000	1.43
+++ gdb/i386bsd-nat.c	22 Apr 2010 15:21:55 -0000
@@ -88,7 +88,7 @@ static int have_ptrace_xmmregs = -1;
 
 /* Supply the general-purpose registers in GREGS, to REGCACHE.  */
 
-static void
+void
 i386bsd_supply_gregset (struct regcache *regcache, const void *gregs)
 {
   const char *regs = gregs;
@@ -107,7 +107,7 @@ i386bsd_supply_gregset (struct regcache 
    GREGS.  If REGNUM is -1, collect and store all appropriate
    registers.  */
 
-static void
+void
 i386bsd_collect_gregset (const struct regcache *regcache,
 			 void *gregs, int regnum)
 {
Index: gdb/i386bsd-nat.h
===================================================================
RCS file: /cvs/src/src/gdb/i386bsd-nat.h,v
retrieving revision 1.8
diff -u -p -r1.8 i386bsd-nat.h
--- gdb/i386bsd-nat.h	1 Jan 2010 07:31:36 -0000	1.8
+++ gdb/i386bsd-nat.h	22 Apr 2010 15:21:55 -0000
@@ -35,4 +35,11 @@ extern void i386bsd_dr_reset_addr (int r
 
 extern unsigned long i386bsd_dr_get_status (void);
 
+extern void i386bsd_supply_gregset (struct regcache *regcache, 
+                                    const void *gregs);
+
+extern void i386bsd_collect_gregset (const struct regcache *regcache,
+                                     void *gregs, int regnum);
+
+
 #endif /* i386bsd-nat.h */
Index: gdb/i386nbsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/i386nbsd-nat.c,v
retrieving revision 1.22
diff -u -p -r1.22 i386nbsd-nat.c
--- gdb/i386nbsd-nat.c	1 Jan 2010 07:31:36 -0000	1.22
+++ gdb/i386nbsd-nat.c	22 Apr 2010 15:21:55 -0000
@@ -19,16 +19,20 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "inferior.h"
 #include "gdbcore.h"
 #include "regcache.h"
 #include "target.h"
 
 #include "i386-tdep.h"
+#include "i387-tdep.h"
 #include "i386bsd-nat.h"
 
 /* Support for debugging kernel virtual memory images.  */
 
 #include <sys/types.h>
+#include <sys/ptrace.h>
+#include <machine/reg.h>
 #include <machine/frame.h>
 #include <machine/pcb.h>
 
@@ -71,6 +75,131 @@ i386nbsd_supply_pcb (struct regcache *re
 
   return 1;
 }
+
+/* Macro to determine if a register is fetched with PT_GETREGS.  */
+#define GETREGS_SUPPLIES(regnum) \
+  ((0 <= (regnum) && (regnum) <= 15))
+
+#ifdef HAVE_PT_GETXMMREGS
+/* Set to 1 if the kernel supports PT_GETXMMREGS.  Initialized to -1
+   so that we try PT_GETXMMREGS the first time around.  */
+static int have_ptrace_xmmregs = -1;
+#endif
+\f
+
+/* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
+   for all registers (including the floating point registers).  */
+
+static void
+i386nbsd_fetch_inferior_registers (struct target_ops *ops,
+				   struct regcache *regcache, int regnum)
+{
+  if (regnum == -1 || GETREGS_SUPPLIES (regnum))
+    {
+      struct reg regs;
+
+      if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
+	perror_with_name (_("Couldn't get registers"));
+
+      i386bsd_supply_gregset (regcache, &regs);
+      if (regnum != -1)
+	return;
+    }
+
+  if (regnum == -1 || regnum >= I386_ST0_REGNUM)
+    {
+      struct fpreg fpregs;
+#ifdef HAVE_PT_GETXMMREGS
+      char xmmregs[512];
+
+      if (have_ptrace_xmmregs != 0
+	  && ptrace(PT_GETXMMREGS, PIDGET (inferior_ptid),
+		    (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == 0)
+	{
+	  have_ptrace_xmmregs = 1;
+	  i387_supply_fxsave (regcache, -1, xmmregs);
+	}
+      else
+	{
+          if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
+	    perror_with_name (_("Couldn't get floating point status"));
+
+	  i387_supply_fsave (regcache, -1, &fpregs);
+	}
+#else
+      if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
+	perror_with_name (_("Couldn't get floating point status"));
+
+      i387_supply_fsave (regcache, -1, &fpregs);
+#endif
+    }
+}
+
+/* Store register REGNUM back into the inferior.  If REGNUM is -1, do
+   this for all registers (including the floating point registers).  */
+
+static void
+i386nbsd_store_inferior_registers (struct target_ops *ops,
+				   struct regcache *regcache, int regnum)
+{
+  if (regnum == -1 || GETREGS_SUPPLIES (regnum))
+    {
+      struct reg regs;
+
+      if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
+                  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
+        perror_with_name (_("Couldn't get registers"));
+
+      i386bsd_collect_gregset (regcache, &regs, regnum);
+
+      if (ptrace (PT_SETREGS, PIDGET (inferior_ptid),
+	          (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
+        perror_with_name (_("Couldn't write registers"));
+
+      if (regnum != -1)
+	return;
+    }
+
+  if (regnum == -1 || regnum >= I386_ST0_REGNUM)
+    {
+      struct fpreg fpregs;
+#ifdef HAVE_PT_GETXMMREGS
+      char xmmregs[512];
+
+      if (have_ptrace_xmmregs != 0
+	  && ptrace(PT_GETXMMREGS, PIDGET (inferior_ptid),
+		    (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == 0)
+	{
+	  have_ptrace_xmmregs = 1;
+
+	  i387_collect_fxsave (regcache, regnum, xmmregs);
+
+	  if (ptrace (PT_SETXMMREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == -1)
+            perror_with_name (_("Couldn't write XMM registers"));
+	}
+      else
+	{
+	  have_ptrace_xmmregs = 0;
+#endif
+          if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
+	    perror_with_name (_("Couldn't get floating point status"));
+
+          i387_collect_fsave (regcache, regnum, &fpregs);
+
+          if (ptrace (PT_SETFPREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
+	    perror_with_name (_("Couldn't write floating point status"));
+#ifdef HAVE_PT_GETXMMREGS
+        }
+#endif
+    }
+}
+
 \f
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
@@ -84,6 +213,8 @@ _initialize_i386nbsd_nat (void)
   /* Add some extra features to the common *BSD/i386 target.  */
   t = i386bsd_target ();
   t->to_pid_to_exec_file = nbsd_pid_to_exec_file;
+  t->to_fetch_registers = i386nbsd_fetch_inferior_registers;
+  t->to_store_registers = i386nbsd_store_inferior_registers;
   add_target (t);
  
   /* Support debugging kernel virtual memory images.  */
Index: gdb/mipsnbsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/mipsnbsd-nat.c,v
retrieving revision 1.17
diff -u -p -r1.17 mipsnbsd-nat.c
--- gdb/mipsnbsd-nat.c	1 Jan 2010 07:31:37 -0000	1.17
+++ gdb/mipsnbsd-nat.c	22 Apr 2010 15:21:55 -0000
@@ -49,7 +49,7 @@ mipsnbsd_fetch_inferior_registers (struc
       struct reg regs;
 
       if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get registers"));
       
       mipsnbsd_supply_reg (regcache, (char *) &regs, regno);
@@ -62,7 +62,7 @@ mipsnbsd_fetch_inferior_registers (struc
       struct fpreg fpregs;
 
       if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get floating point status"));
 
       mipsnbsd_supply_fpreg (regcache, (char *) &fpregs, regno);
@@ -79,13 +79,13 @@ mipsnbsd_store_inferior_registers (struc
       struct reg regs;
 
       if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get registers"));
 
       mipsnbsd_fill_reg (regcache, (char *) &regs, regno);
 
       if (ptrace (PT_SETREGS, PIDGET (inferior_ptid), 
-		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't write registers"));
 
       if (regno != -1)
@@ -97,13 +97,13 @@ mipsnbsd_store_inferior_registers (struc
       struct fpreg fpregs; 
 
       if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get floating point status"));
 
       mipsnbsd_fill_fpreg (regcache, (char *) &fpregs, regno);
 
       if (ptrace (PT_SETFPREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't write floating point status"));
     }
 }
Index: gdb/nbsd-thread.c
===================================================================
RCS file: gdb/nbsd-thread.c
diff -N gdb/nbsd-thread.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdb/nbsd-thread.c	22 Apr 2010 15:21:55 -0000
@@ -0,0 +1,698 @@
+/* Threads support for NetBSD 5.0.
+
+   Copyright (C) 2010 Free Software Foundation, Inc.
+
+   Contributed by Paul Koning, Dell, inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "observer.h"
+#include "target.h"
+#include "inferior.h"
+#include "gdbthread.h"
+#include "regcache.h"
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+
+/* Data structure used to track NetBSD thread state.  There is a 
+   vector of these, in ascending order of LWP ID.  */
+
+struct lwp_info
+{
+  /* The process ID of the LWP.  This is a combination of the process
+     ID and the LWP ID.  */
+  ptid_t ptid;
+  
+  /* Non-zero if we this LWP was reported as having been signalled
+     by the PT_LWPINFO ptrace() call.  */
+  int signalled;
+  
+  /* The waitstatus for this LWP's last event.  */
+  struct target_waitstatus waitstatus;
+};
+
+/* The lwp_info buffer and its control variables.  */
+static struct lwp_info *lwp_buffer;
+static int lwp_count;
+static int lwp_bufsize;
+
+/* Count of signals still pending delivery to GDB.  These are threads
+   that were found to be stopped and not breakpoints.  For threads that
+   hit a breakpoint, we simply push back the thread so it will hit the
+   break again (if it isn't removed before then) but for other signals,
+   for example faults, the signal remains pending, the "to_resume" that
+   resumes the whole process is skipped, and then the "to_wait" returns
+   the information about one of the pending signals instead.  */
+static int pending_sigs;
+
+/* The LWP ID of the thread being stepped, or 0 if none.  */
+static int step_lwpid;
+
+/* Flag to indicate whether last resume was a resume all threads or
+   a resume single thread.  */
+static int resume_all;
+
+/* Non-zero if the netbsd-thread layer is active.  */
+static int nbsd_thread_active = 0;
+
+/* The netbsd-thread target_ops structure.  */
+static struct target_ops nbsd_thread_ops;
+
+int debug_nbsd_thread;
+static void
+show_debug_nbsd_thread (struct ui_file *file, int from_tty,
+		      struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Debugging of NetBSD thread module is %s.\n"),
+		    value);
+}
+
+
+/* Activate thread support if appropriate.  Do nothing if thread
+   support is already active.  */
+
+static void
+enable_nbsd_thread (void)
+{
+  struct minimal_symbol *msym;
+  void* caller_context;
+  int status;
+
+  /* If already active, nothing more to do.  */
+  if (nbsd_thread_active)
+    return;
+
+  if (lwp_buffer != NULL)
+    {
+      xfree (lwp_buffer);
+      lwp_buffer = NULL;
+      lwp_count = lwp_bufsize = 0;
+    }
+  
+  push_target (&nbsd_thread_ops);
+  nbsd_thread_active = 1;
+}
+
+/* Deactivate thread support.  Do nothing is thread support is
+   already inactive.  */
+
+static void
+disable_nbsd_thread (void)
+{
+  if (!nbsd_thread_active)
+    return;
+
+  unpush_target (&nbsd_thread_ops);
+  nbsd_thread_active = 0;
+}
+
+/* Update our lwp_info buffer, and tell GDB about adds or deletes.
+
+   By doing the thread add and thread delete operations here as we
+   learn about threads, we allow users to run thread-specific commands
+   without needing to run "info threads" first. 
+
+   The argument is a pointer to the waitstatus struct, which
+   is copied into the waitstatus for the thread we find as the signalled
+   thread.  */
+
+static void
+update_lwpbuf (struct target_waitstatus *status)
+{
+  int pi;
+  lwpid_t lwp_id, sig_lwpid;
+  struct ptrace_lwpinfo pt_info;
+  ptid_t ptid;
+  
+  /* Accumulate an array of NetBSD threads, in descending order of LWP id.
+
+     The reason for using descending order is that this is the order
+     in which LWPs are returned by the ptrace() PT_LWPINFO function,
+     because it returns them in the order in which they exist in the
+     p_lwps list for the process, and new entries are assigned ascending
+     LWP IDs and are added to the head of that list.  */
+
+  lwp_id = sig_lwpid = 0;
+  pi = 0;
+  
+  while (1)
+    {
+      pt_info.pl_lwpid = lwp_id;
+      errno = 0;
+      if (ptrace (PT_LWPINFO, PIDGET (inferior_ptid),
+		  (PTRACE_TYPE_ARG3) &pt_info, sizeof (pt_info)) == -1)
+	{
+	  if (errno == ESRCH)
+	    break;
+	  else if (errno != 0)
+	    perror_with_name (_("Couldn't get thread information"));
+	}
+
+      if (debug_nbsd_thread)
+	fprintf_unfiltered (gdb_stdlog,
+			    "NTUP: lwpinfo on %d returns lwp %d, pl_event %d\n",
+			    lwp_id, pt_info.pl_lwpid, pt_info.pl_event);
+	  
+      /* Retrieve the LWP ID that was found.  This also sets the ID to
+	 start from the next time around the loop.  */
+      lwp_id = pt_info.pl_lwpid;
+
+      /* LWP id 0 is end of list.  */
+      if (lwp_id == 0)
+	break;
+      
+      /* If the LWP we found has an ID less than the ID of the current
+	 buffer entry, then the current buffer entry is a deleted thread.
+	 Tell GDB about its demise, then remove it from the buffer. 
+	  
+	 We have to do this in a loop until we run out of threads
+	 to be removed.  */
+      while (pi < lwp_count && lwp_id < TIDGET (lwp_buffer[pi].ptid))
+	{
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTUP: thread ptid %d,%ld has disappeared\n",
+				PIDGET (lwp_buffer[pi].ptid),
+				TIDGET (lwp_buffer[pi].ptid));
+
+	  /* Tell GDB.  */
+	  delete_thread (lwp_buffer[pi].ptid);
+
+	  /* Remove the deleted entry.  */
+	  if (pi < lwp_count)
+	    memmove (lwp_buffer + pi + 1, lwp_buffer + pi, 
+		     (lwp_count - pi) * sizeof (struct lwp_info));
+	  lwp_count--;
+	}
+
+      /* If we're now at the end of the current buffer, or the LWP found
+	 has an LWP ID greater than the current entry in the buffer, this
+	 is a new thread.  Allocate more buffer space if need be,
+	 make room for this entry, and store it.  Then tell GDB about
+	 the new thread.  */
+      if (pi >= lwp_count || lwp_id > TIDGET (lwp_buffer[pi].ptid))
+	{
+	  /* Allocate more space, if we need it.  */
+	  if (lwp_count == lwp_bufsize)
+	    {
+	      if (lwp_bufsize)
+		lwp_bufsize *= 2;
+	      else
+		lwp_bufsize = 1;
+	      lwp_buffer = (struct lwp_info *) xrealloc (lwp_buffer, 
+							 lwp_bufsize * sizeof (struct lwp_info));
+	    }
+
+	  /* Push current and later entries, if any, over.  */
+	  if (pi < lwp_count)
+	    memmove (lwp_buffer + pi + 1, lwp_buffer + pi, 
+		     (lwp_count - pi) * sizeof (struct lwp_info));
+
+	  /* Update the count of LWPs.  */
+	  lwp_count++;
+
+	  /* Initialize the new entry.  */
+	  lwp_buffer[pi].ptid = MERGEPID (PIDGET (inferior_ptid), lwp_id);
+	  if (pt_info.pl_event == PL_EVENT_SIGNAL)
+	    {
+	      lwp_buffer[pi].signalled = 1;
+	      lwp_buffer[pi].waitstatus = *status;
+	      sig_lwpid = lwp_id;
+	    }
+	  else
+	    lwp_buffer[pi].signalled = 0;
+
+	  /* Advance the LWP buffer pointer.  */
+	  pi++;
+	  
+	  /* Tell GDB about the new thread.  */
+	  if (lwp_count == 1)
+	    {
+	      /* See if GDB still has TID zero, if so set the TID.  */
+	      if (TIDGET (inferior_ptid) == 0)
+		{
+		  ptid = MERGEPID (PIDGET (inferior_ptid), lwp_id);
+		  thread_change_ptid (inferior_ptid, ptid);
+		  if (debug_nbsd_thread)
+		    fprintf_unfiltered (gdb_stdlog,
+					"NTUP: setting main thread ptid to %d,%ld\n",
+					PIDGET (ptid), TIDGET (ptid));
+		}
+	    }
+	  else
+	    {
+	      /* New thread but not the first, add it to GDB.  */
+	      if (debug_nbsd_thread)
+		fprintf_unfiltered (gdb_stdlog,
+				    "NTUP: adding new thread ptid %d,%d\n",
+				    PIDGET (inferior_ptid), lwp_id);
+	      add_thread (MERGEPID (PIDGET (inferior_ptid), lwp_id));
+	    }
+	}
+      else
+	{
+	  /* Found an existing thread.  Update its status in the buffer.
+	     Note that we clear the signalled flag if this is the first
+	     call and this thread wasn't the signalled thread, but we 
+	     leave it alone on subsequent calls.  That way the subsequent
+	     calls will accumulate the set of signalled threads.  */
+	  if (pt_info.pl_event == PL_EVENT_SIGNAL)
+	    {
+	      lwp_buffer[pi].signalled = 1;
+	      lwp_buffer[pi].waitstatus = *status;
+	      sig_lwpid = lwp_id;
+	    }
+	  
+	  /* Advance the LWP buffer pointer.  */
+	  pi++;
+	}
+    }
+  if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTUP: signalled thread lwpid is %d\n", sig_lwpid);
+
+}
+
+/* The "to_detach" method of the nbsd_thread_ops.  */
+
+static void
+nbsd_thread_detach (struct target_ops *ops, char *args, int from_tty)
+{   
+  struct target_ops *beneath = find_target_beneath (ops);
+
+  disable_nbsd_thread ();
+  beneath->to_detach (beneath, args, from_tty);
+}
+
+/* Resume execution of thread PTID, or all threads if PTID is -1.  If
+   STEP is nonzero, single-step it.  If SIGNAL is nonzero, give it
+   that signal.  */
+
+static void
+nbsd_thread_resume (struct target_ops *ops,
+		    ptid_t ptid, int step, enum target_signal signal)
+{
+  pid_t pid;
+  int request;
+  
+  /* A specific PTID means `step only this process id'.  */
+  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
+    {
+      resume_all = 1;
+      ptid = inferior_ptid;
+    }
+  else
+    resume_all = 0;
+  
+  pid = ptid_get_pid (ptid);
+
+  if (catch_syscall_enabled () > 0)
+    request = PT_SYSCALL;
+  else
+    request = PT_CONTINUE;
+
+  if (step)
+    {
+      /* If this system does not support PT_STEP, a higher level
+         function will have called single_step() to transmute the step
+         request into a continue request (by setting breakpoints on
+         all possible successor instructions), so we don't have to
+         worry about that here.  */
+      request = PT_STEP;
+    }
+
+  /* An address of (PTRACE_TYPE_ARG3)1 tells ptrace to continue from
+     where it was.  If GDB wanted it to start some other way, we have
+     already written a new program counter value to the child.  */
+  errno = 0;
+  if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTR: %s ptid %d,%ld, %s, signal %d\n",
+			(step ? "stepping" : "resuming"),
+			PIDGET (ptid), TIDGET (ptid),
+			(resume_all ? "all threads" : "single thread"),
+			signal);
+
+  /* Assume not stepping some LWP ID.  */
+  step_lwpid = 0;
+  if (step)
+    {
+      step_lwpid = TIDGET (ptid);
+      if (step_lwpid < 0)
+	step_lwpid = -step_lwpid;
+    }	
+  
+  if (resume_all)
+    {
+      if (pending_sigs > 0)
+	{
+	  /* We have pending signals from the previous wait still 
+	     needing to be delivered.  So don't resume the process,
+	     instead take no action and we'll deliver one of those
+	     pending signals at the next wait.  */
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTR: ptid %d,%ld has %d pending signals, skipping resume\n",
+				PIDGET (ptid), TIDGET (ptid), pending_sigs);
+	  return;
+	}
+      if (step)
+	ptrace (request, pid, (PTRACE_TYPE_ARG3)1, TIDGET (ptid));
+      else
+	ptrace (request, pid, (PTRACE_TYPE_ARG3)1,
+		target_signal_to_host (signal));
+    }
+  else
+    ptrace (request, pid, (PTRACE_TYPE_ARG3)1, -TIDGET (ptid));
+  
+  if (errno != 0)
+    perror_with_name (("ptrace"));
+}
+
+/* Wait for the child specified by PTID to do something.  Return the
+   process ID of the child, or MINUS_ONE_PTID in case of error; store
+   the status in *OURSTATUS.  */
+
+static ptid_t
+nbsd_thread_wait2 (struct target_ops *ops, ptid_t ptid,
+		   struct target_waitstatus *ourstatus, int options)
+{
+  pid_t pid;
+  int status, save_errno;
+
+  do
+    {
+      set_sigint_trap ();
+
+      do
+	{
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTW2: waiting for ptid %d,%ld, opt %d\n",
+				PIDGET (ptid), TIDGET (ptid), options);
+
+	  pid = waitpid (ptid_get_pid (ptid), &status, options);
+	  save_errno = errno;
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTW2: waitpid errno is %d, pid %d, status %x\n",
+				save_errno, pid, status);
+	}
+      while (pid == -1 && save_errno == EINTR);
+
+      clear_sigint_trap ();
+
+      /* If nothing found in the no wait case, report that.  */
+      if (options == WNOHANG && pid == 0)
+	return pid_to_ptid (-1);
+      
+      if (pid == -1)
+	{
+	  fprintf_unfiltered (gdb_stderr,
+			      _("Child process unexpectedly missing: %s.\n"),
+			      safe_strerror (save_errno));
+
+	  /* If first wait, claim it exited with unknown signal; 
+	     else claim there is nothing left to wait for.  */
+	  if (options == WNOHANG)
+	    return pid_to_ptid (-1);
+	  ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+	  ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
+	  return inferior_ptid;
+	}
+
+      /* Ignore terminated detached child processes.  */
+      if (!WIFSTOPPED (status) && pid != ptid_get_pid (inferior_ptid))
+	pid = -1;
+    }
+  while (pid == -1);
+
+  store_waitstatus (ourstatus, status);
+  return pid_to_ptid (pid);
+}
+
+static int
+nbsd_thread_cancel_breakpoint (struct lwp_info *lp)
+{
+  /* Arrange for a breakpoint to be hit again later.  We don't keep
+     the SIGTRAP status and don't forward the SIGTRAP signal to the
+     LWP.  We will handle the current event, eventually we will resume
+     this LWP, and this breakpoint will trap again.
+
+     If we do not do this, then we run the risk that the user will
+     delete or disable the breakpoint, but the LWP will have already
+     tripped on it.  */
+
+  struct regcache *regcache = get_thread_regcache (lp->ptid);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  CORE_ADDR pc;
+
+  pc = regcache_read_pc (regcache) - gdbarch_decr_pc_after_break (gdbarch);
+  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
+    {
+      if (debug_nbsd_thread)
+	fprintf_unfiltered (gdb_stdlog,
+			    "NTCB: Push back breakpoint for ptid %d,%ld\n",
+			    PIDGET (lp->ptid), TIDGET (lp->ptid));
+
+      /* Back up the PC if necessary.  */
+      if (gdbarch_decr_pc_after_break (gdbarch))
+	regcache_write_pc (regcache, pc);
+
+      /* We no longer have a pending signal for this thread.  */
+      lp->signalled = 0;
+    }
+  return 0;
+}
+
+/* The "to_wait" method of the nbsd_thread_ops.  */
+
+static ptid_t
+nbsd_thread_wait (struct target_ops *ops,
+		 ptid_t ptid, struct target_waitstatus *status, int options)
+{
+  ptid_t active_ptid;
+  struct lwp_info *sel_thread;
+  int sig_threads, i;
+  struct target_waitstatus tstatus;
+
+  /* If there were pending signals and a resume all threads was done,
+     the process wasn't actually resumed so don't wait on it.  Just
+     go on to pick a thread to report on.  */
+  if (!(pending_sigs > 0 && resume_all))
+    {
+      ptid = nbsd_thread_wait2 (ops, ptid, &tstatus, 0);
+
+      /* Default status returned is the one we just got.  */
+      *status = tstatus;
+      
+      /* The ptid returned by the target beneath us is the ptid of the process.
+	 We need to find which thread is currently active and return 
+	 its ptid.  */
+      update_lwpbuf (&tstatus);
+  
+      /* Loop checking for additional threads that are waiting, and gather
+	 up their status.  */
+      while (1)
+	{
+	  ptid = nbsd_thread_wait2 (ops, ptid, &tstatus, WNOHANG);
+	  if (PIDGET (ptid) == -1)
+	    break;
+	  update_lwpbuf (&tstatus);
+	}
+    }
+  
+  /* Find a suitable signalled thread.  Pick the stepped one, if there
+     is one; otherwise pick a random one.  */
+  sel_thread = NULL;
+  sig_threads = 0;
+  
+  for (i = 0; i < lwp_count; i++)
+    {
+      if (lwp_buffer[i].signalled)
+	{
+	  sig_threads++;
+	  if (TIDGET (lwp_buffer[i].ptid) == step_lwpid)
+	    {
+	      /* If there is a stepped thread, pick that one.  */
+	      sel_thread = &lwp_buffer[i];
+	      if (debug_nbsd_thread)
+		fprintf_unfiltered (gdb_stdlog,
+				    "NTW: Picking ptid %d,%ld because it is stepped\n",
+				    PIDGET (sel_thread->ptid),
+				    TIDGET (sel_thread->ptid));
+	      break;
+	    }
+	  /* Randomly pick this one or keep the previous choice,
+	     such that all of the signalled threads have an equal
+	     probability of being picked.  */
+	  if (sel_thread == NULL || 
+	      (((double) rand ()) / (RAND_MAX + 1.0)) < (1.0 / sig_threads))
+	    {
+	      sel_thread = &lwp_buffer[i];
+	      if (debug_nbsd_thread)
+		fprintf_unfiltered (gdb_stdlog,
+				    "NTW: Picking ptid %d,%ld out of %d\n",
+				    PIDGET (sel_thread->ptid),
+				    TIDGET (sel_thread->ptid), sig_threads);
+	    }
+	}
+    }
+  
+  /* Scan the LWP table again.  For each signalled LWP other than the
+     chosen one, back it up to the breakpoint if it was stopped by a
+     breakpoint and mark it as not signalled (it will re-break next
+     time we run the whole process).  Other LWPs (those with signals
+     other than breakpoint stop) are counted but not backed up; if we
+     find any of those then those will be delivered next.  */
+  pending_sigs = 0;
+  if (sig_threads > 1)
+    {
+      for (i = 0; i < lwp_count; i++)
+	{
+	  /* Skip the selected LWP.  */
+	  if (&lwp_buffer[i] == sel_thread)
+	    continue;
+	  
+	  if (lwp_buffer[i].signalled)
+	    {
+	      if (WIFSTOPPED (lwp_buffer[i].waitstatus))
+		{
+		  if (!nbsd_thread_cancel_breakpoint (&lwp_buffer[i]))
+		    pending_sigs++;
+		}
+	      else
+		pending_sigs++;
+	    }
+	}
+    }
+  
+  ptid = inferior_ptid;
+  if (sel_thread != NULL)
+    {
+      ptid =  MERGEPID (PIDGET (ptid), TIDGET (sel_thread->ptid));
+      *status = tstatus;
+      
+      /* The signal for this thread is now being reported, so clear
+	 the flag that says it hasn't been reported yet.  */
+      sel_thread->signalled = 0;
+    }
+  else if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTW: no signalled thread\n");
+
+  if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTW: returning ptid %d,%ld\n",
+			PIDGET (ptid), TIDGET (ptid));
+
+  return ptid;
+}
+
+/* The "to_mourn_inferior" method of the nbsd_thread_ops.  */
+
+static void
+nbsd_thread_mourn_inferior (struct target_ops *ops)
+{
+  int status;
+
+  /* Wait just one more time to collect the inferior's exit status.
+     Do not check whether this succeeds though, since we may be
+     dealing with a process that we attached to.  Such a process will
+     only report its exit status to its original parent.  */
+  waitpid (ptid_get_pid (inferior_ptid), &status, WNOHANG);
+
+  generic_mourn_inferior ();
+
+  if (!have_inferiors ())
+    unpush_target (ops);
+}
+
+
+/* The "to_thread_alive" method of the nbsd_thread_ops.  */
+static int
+nbsd_thread_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+  /* The thread list maintained by GDB is up to date, since we update
+     it everytime we stop.   So check this list.  */
+  return in_thread_list (ptid);
+}
+
+/* The "to_pid_to_str" method of the nbsd_thread_ops.  */
+
+static char *
+nbsd_thread_pid_to_str (struct target_ops *ops, ptid_t ptid)
+{
+  if (TIDGET (ptid) == 0)
+    {
+      struct target_ops *beneath = find_target_beneath (ops);
+
+      return beneath->to_pid_to_str (beneath, ptid);
+    }
+  return xstrprintf (_("Thread %ld"), TIDGET (ptid));
+}
+
+/* A "new-objfile" observer.  Used to activate/deactivate netbsd-thread
+   support.  */
+
+static void
+nbsd_thread_new_objfile_observer (struct objfile *objfile)
+{
+  if (objfile != NULL)
+     enable_nbsd_thread ();
+  else
+     disable_nbsd_thread ();
+}
+
+static void
+init_nbsd_thread_ops (void)
+{
+  nbsd_thread_ops.to_shortname          = "netbsd-threads";
+  nbsd_thread_ops.to_longname           = _("NetBSD threads support");
+  nbsd_thread_ops.to_doc                = _("NetBSD threads support");
+  nbsd_thread_ops.to_detach             = nbsd_thread_detach;
+  nbsd_thread_ops.to_resume             = nbsd_thread_resume;
+  nbsd_thread_ops.to_wait               = nbsd_thread_wait;
+  nbsd_thread_ops.to_mourn_inferior     = nbsd_thread_mourn_inferior;
+  nbsd_thread_ops.to_thread_alive       = nbsd_thread_thread_alive;
+  nbsd_thread_ops.to_pid_to_str         = nbsd_thread_pid_to_str;
+  nbsd_thread_ops.to_stratum            = thread_stratum;
+  nbsd_thread_ops.to_magic              = OPS_MAGIC;
+}
+
+void
+_initialize_nbsd_thread (void)
+{
+  init_nbsd_thread_ops ();
+  add_target (&nbsd_thread_ops);
+
+  add_setshow_zinteger_cmd ("nbsd-thread", class_maintenance,
+			    &debug_nbsd_thread, _("\
+Set debugging of NetBSD thread module."), _("\
+Show debugging of NetBSD thread module."), _("\
+Enables printf debugging output."),
+			    NULL,
+			    show_debug_nbsd_thread,
+			    &setdebuglist, &showdebuglist);
+
+  observer_attach_new_objfile (nbsd_thread_new_objfile_observer);
+}
Index: gdb/config/i386/nbsdelf.mh
===================================================================
RCS file: /cvs/src/src/gdb/config/i386/nbsdelf.mh,v
retrieving revision 1.24
diff -u -p -r1.24 nbsdelf.mh
--- gdb/config/i386/nbsdelf.mh	17 Dec 2006 13:30:44 -0000	1.24
+++ gdb/config/i386/nbsdelf.mh	22 Apr 2010 15:21:55 -0000
@@ -1,5 +1,5 @@
 # Host: NetBSD/i386 ELF
 NATDEPFILES= fork-child.o inf-ptrace.o \
-	nbsd-nat.o i386bsd-nat.o i386nbsd-nat.o bsd-kvm.o
+	nbsd-nat.o nbsd-thread.o i386bsd-nat.o i386nbsd-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
Index: gdb/config/mips/nbsd.mh
===================================================================
RCS file: /cvs/src/src/gdb/config/mips/nbsd.mh,v
retrieving revision 1.3
diff -u -p -r1.3 nbsd.mh
--- gdb/config/mips/nbsd.mh	31 Oct 2004 20:47:55 -0000	1.3
+++ gdb/config/mips/nbsd.mh	22 Apr 2010 15:21:55 -0000
@@ -1,2 +1,2 @@
 # Host: NetBSD/mips
-NATDEPFILES= fork-child.o inf-ptrace.o mipsnbsd-nat.o
+NATDEPFILES= fork-child.o inf-ptrace.o nbsd-thread.o mipsnbsd-nat.o

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PING] [RFC] Thread debug support for NetBSD 5
  2010-04-29 14:49   ` [PING] " Paul Koning
@ 2010-04-29 15:39     ` Pedro Alves
  2010-05-03 13:51       ` Paul Koning
  2010-04-30 12:44     ` Nick Hudson
                       ` (2 subsequent siblings)
  3 siblings, 1 reply; 16+ messages in thread
From: Pedro Alves @ 2010-04-29 15:39 UTC (permalink / raw)
  To: gdb-patches; +Cc: Paul Koning

On Thursday 29 April 2010 15:49:16, Paul Koning wrote:
> Ping... any comments?  

(I know nothing about NetBSD threads support)

> 2010-04-22  Paul Koning  <paul_koning@dell.com>
> 
> 	* i386bsd-nat.c (i386bsd_supply_gregset, i386bsd_collect_gregset):
> 	Make global.
> 	* i386bsd-nat.h: Ditto.
> 	* i386nbsd-nat.c: Include inferior.h, i387-tdep.h, sys/ptrace.h,
> 	machine/reg.h.
> 	(i386nbsd_fetch_inferior_registers,
> 	i386nbsd_store_inferior_registers): New.
> 	* mipsnbsd-nat.c (mipsnbsd_fetch_inferior_registers,
> 	mipsnbsd_store_inferior_registers): Pass thread ID to ptrace().


> 	* nbsd-thread.c: New file.

I took a look at this file, only.  Mark would probably
be the most qualified to review the rest of the patch.


> 	* config/i386/nbsdelf.mh: Add nbsd-thread.o.
> 	* config/mips/nbsd.mh: Add nbsd-thread.o.

> Index: gdb/nbsd-thread.c
> ===================================================================
> RCS file: gdb/nbsd-thread.c
> diff -N gdb/nbsd-thread.c
> --- /dev/null	1 Jan 1970 00:00:00 -0000
> +++ gdb/nbsd-thread.c	22 Apr 2010 15:21:55 -0000
> @@ -0,0 +1,698 @@

> +/* Count of signals still pending delivery to GDB.  These are threads
> +   that were found to be stopped and not breakpoints.  For threads that
> +   hit a breakpoint, we simply push back the thread so it will hit the
> +   break again (if it isn't removed before then) but for other signals,
> +   for example faults, the signal remains pending, the "to_resume" that
> +   resumes the whole process is skipped, and then the "to_wait" returns
> +   the information about one of the pending signals instead.  */
> +static int pending_sigs;

I'm not sure whether this global can get stale between
debug sessions or not, but it looked like it.  Say, if you
kill a process while you have pending sigs, the next
debug session will trip on it being != 0?  It also points
out that you should probably do something to the pending
signals when you go about detaching from a process, so
they don't get lost.


> +/* Deactivate thread support.  Do nothing is thread support is
> +   already inactive.  */

Typo: s/is thread/if thread/

> +  if (catch_syscall_enabled () > 0)
> +    request = PT_SYSCALL;
> +  else
> +    request = PT_CONTINUE;

I think this will be dead code, since you don't
support inserting catch syscalls.

> +  /* An address of (PTRACE_TYPE_ARG3)1 tells ptrace to continue from
> +     where it was.  If GDB wanted it to start some other way, we have
> +     already written a new program counter value to the child.  */
> +  errno = 0;

If this clearing of errno is needed, then it should move to just
before the `ptrace' calls.  You have several function calls between
this and the `ptrace' calls (at least when debugging is enable),
and any of those could clobber `errno'.  (That's the reason
for `save_errno' in your patch somewhere else, BTW.)


> +      /* If nothing found in the no wait case, report that.  */
> +      if (options == WNOHANG && pid == 0)
> +	return pid_to_ptid (-1);

Use minus_one_ptid, here and everywhere else.


> +static char *
> +nbsd_thread_pid_to_str (struct target_ops *ops, ptid_t ptid)
> +{
> +  if (TIDGET (ptid) == 0)
> +    {
> +      struct target_ops *beneath = find_target_beneath (ops);
> +
> +      return beneath->to_pid_to_str (beneath, ptid);
> +    }
> +  return xstrprintf (_("Thread %ld"), TIDGET (ptid));

This leaks.  Nothing ever releases the return of
target_pid_to_str calls; that's why all implementations
return a pointer to a static buffer.

> +static void
> +init_nbsd_thread_ops (void)
> +{
> +  nbsd_thread_ops.to_shortname          = "netbsd-threads";
> +  nbsd_thread_ops.to_longname           = _("NetBSD threads support");
> +  nbsd_thread_ops.to_doc                = _("NetBSD threads support");
> +  nbsd_thread_ops.to_detach             = nbsd_thread_detach;
> +  nbsd_thread_ops.to_resume             = nbsd_thread_resume;
> +  nbsd_thread_ops.to_wait               = nbsd_thread_wait;
> +  nbsd_thread_ops.to_mourn_inferior     = nbsd_thread_mourn_inferior;
> +  nbsd_thread_ops.to_thread_alive       = nbsd_thread_thread_alive;
> +  nbsd_thread_ops.to_pid_to_str         = nbsd_thread_pid_to_str;
> +  nbsd_thread_ops.to_stratum            = thread_stratum;
> +  nbsd_thread_ops.to_magic              = OPS_MAGIC;
> +}

I'd really prefer to get rid of the vertical alignment, and
put just one space before and after the `='.  I was bitten
before for greeping for e.g., "to_detach = " when adjusting
all targets for an interface change, and not finding
some uses like this one, and thus ending up breaking
the build for some targets.

> +
> +void
> +_initialize_nbsd_thread (void)

Please provide a prototype for this.  E.g.,:

 /* Provide a prototype to silence -Wmissing-prototypes.  */
 extern initialize_file_ftype _initialize_linux_nat;

Otherwise, looks fine to me.

-- 
Pedro Alves

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PING] [RFC] Thread debug support for NetBSD 5
  2010-04-29 14:49   ` [PING] " Paul Koning
  2010-04-29 15:39     ` Pedro Alves
@ 2010-04-30 12:44     ` Nick Hudson
  2010-05-02 13:29     ` Mark Kettenis
  2010-05-03 20:33     ` Paul Koning
  3 siblings, 0 replies; 16+ messages in thread
From: Nick Hudson @ 2010-04-30 12:44 UTC (permalink / raw)
  To: gdb-patches; +Cc: Paul Koning

On Thursday 29 April 2010 15:49:16 Paul Koning wrote:
> Ping... any comments?

Can the addition of nbsd-thread.o be made conditional on the kernel fixes that 
have been committed or maybe just for NetBSD 5 or later where 1:1 threading 
exists? Maybe configure can check version?

> Who can submit this if it's ok?  I don't have write privs.

I have write privs, but I'm not sure what approvals are needed before I do, 
or, indeed, if I need to check that you have the relevant forms in place.

Cheers,
Nick

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PING] [RFC] Thread debug support for NetBSD 5
  2010-04-29 14:49   ` [PING] " Paul Koning
  2010-04-29 15:39     ` Pedro Alves
  2010-04-30 12:44     ` Nick Hudson
@ 2010-05-02 13:29     ` Mark Kettenis
  2010-05-03  0:58       ` Paul Koning
  2010-05-03 20:33     ` Paul Koning
  3 siblings, 1 reply; 16+ messages in thread
From: Mark Kettenis @ 2010-05-02 13:29 UTC (permalink / raw)
  To: Paul_Koning; +Cc: gdb-patches

> Date: Thu, 29 Apr 2010 10:49:16 -0400
> From: Paul Koning <Paul_Koning@dell.com>
> 
> This patch adds thread debug support for NetBSD 5.0.  Unlike older
> NetBSDs, in that version threads are seen by the kernel and exposed
> via ptrace() machinery.  The patch makes some small changes in
> existing files, mainly to move some code into NetBSD specific files to
> isolate the changes, and it adds a new file nbsd-thread.c.  The
> machinery in that file is loosely based on existing examples in
> dec-thread.c and linux-nat.c.
> 
> Tested on NetBSD on i386 and mipsel platforms.  I did not make changes
> in target specific code for other platforms because I don't have them;
> the changes should be pretty obvious given what is changed in
> mipsnbsd-nat.c. 

I have two questions:

1. Does the code still work on older versions of NetBSD?

2. You could consider getting rid of the HAVE_PT_GETXMMREGS
   conditionaization.  That would simplify the code a bit.  It would
   mean mean dropping support for ancient NetBSD versions though.

I didn't look too closely at the nbsd-thread.c code, but nothing
stands out as obviously wrong to me.  Pedro spotted a few things there
though.



> 2010-04-22  Paul Koning  <paul_koning@dell.com>
> 
> 	* i386bsd-nat.c (i386bsd_supply_gregset, i386bsd_collect_gregset):
> 	Make global.
> 	* i386bsd-nat.h: Ditto.
> 	* i386nbsd-nat.c: Include inferior.h, i387-tdep.h, sys/ptrace.h,
> 	machine/reg.h.
> 	(i386nbsd_fetch_inferior_registers,
> 	i386nbsd_store_inferior_registers): New.
> 	* mipsnbsd-nat.c (mipsnbsd_fetch_inferior_registers,
> 	mipsnbsd_store_inferior_registers): Pass thread ID to ptrace().
> 	* nbsd-thread.c: New file.
> 	* config/i386/nbsdelf.mh: Add nbsd-thread.o.
> 	* config/mips/nbsd.mh: Add nbsd-thread.o.
> 
> Index: gdb/i386bsd-nat.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/i386bsd-nat.c,v
> retrieving revision 1.43
> diff -u -p -r1.43 i386bsd-nat.c
> --- gdb/i386bsd-nat.c	1 Jan 2010 07:31:36 -0000	1.43
> +++ gdb/i386bsd-nat.c	22 Apr 2010 15:21:55 -0000
> @@ -88,7 +88,7 @@ static int have_ptrace_xmmregs = -1;
>  
>  /* Supply the general-purpose registers in GREGS, to REGCACHE.  */
>  
> -static void
> +void
>  i386bsd_supply_gregset (struct regcache *regcache, const void *gregs)
>  {
>    const char *regs = gregs;
> @@ -107,7 +107,7 @@ i386bsd_supply_gregset (struct regcache 
>     GREGS.  If REGNUM is -1, collect and store all appropriate
>     registers.  */
>  
> -static void
> +void
>  i386bsd_collect_gregset (const struct regcache *regcache,
>  			 void *gregs, int regnum)
>  {
> Index: gdb/i386bsd-nat.h
> ===================================================================
> RCS file: /cvs/src/src/gdb/i386bsd-nat.h,v
> retrieving revision 1.8
> diff -u -p -r1.8 i386bsd-nat.h
> --- gdb/i386bsd-nat.h	1 Jan 2010 07:31:36 -0000	1.8
> +++ gdb/i386bsd-nat.h	22 Apr 2010 15:21:55 -0000
> @@ -35,4 +35,11 @@ extern void i386bsd_dr_reset_addr (int r
>  
>  extern unsigned long i386bsd_dr_get_status (void);
>  
> +extern void i386bsd_supply_gregset (struct regcache *regcache, 
> +                                    const void *gregs);
> +
> +extern void i386bsd_collect_gregset (const struct regcache *regcache,
> +                                     void *gregs, int regnum);
> +
> +
>  #endif /* i386bsd-nat.h */
> Index: gdb/i386nbsd-nat.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/i386nbsd-nat.c,v
> retrieving revision 1.22
> diff -u -p -r1.22 i386nbsd-nat.c
> --- gdb/i386nbsd-nat.c	1 Jan 2010 07:31:36 -0000	1.22
> +++ gdb/i386nbsd-nat.c	22 Apr 2010 15:21:55 -0000
> @@ -19,16 +19,20 @@
>     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
>  
>  #include "defs.h"
> +#include "inferior.h"
>  #include "gdbcore.h"
>  #include "regcache.h"
>  #include "target.h"
>  
>  #include "i386-tdep.h"
> +#include "i387-tdep.h"
>  #include "i386bsd-nat.h"
>  
>  /* Support for debugging kernel virtual memory images.  */
>  
>  #include <sys/types.h>
> +#include <sys/ptrace.h>
> +#include <machine/reg.h>
>  #include <machine/frame.h>
>  #include <machine/pcb.h>
>  
> @@ -71,6 +75,131 @@ i386nbsd_supply_pcb (struct regcache *re
>  
>    return 1;
>  }
> +
> +/* Macro to determine if a register is fetched with PT_GETREGS.  */
> +#define GETREGS_SUPPLIES(regnum) \
> +  ((0 <= (regnum) && (regnum) <= 15))
> +
> +#ifdef HAVE_PT_GETXMMREGS
> +/* Set to 1 if the kernel supports PT_GETXMMREGS.  Initialized to -1
> +   so that we try PT_GETXMMREGS the first time around.  */
> +static int have_ptrace_xmmregs = -1;
> +#endif
> +\f
> +
> +/* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
> +   for all registers (including the floating point registers).  */
> +
> +static void
> +i386nbsd_fetch_inferior_registers (struct target_ops *ops,
> +				   struct regcache *regcache, int regnum)
> +{
> +  if (regnum == -1 || GETREGS_SUPPLIES (regnum))
> +    {
> +      struct reg regs;
> +
> +      if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
> +		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
> +	perror_with_name (_("Couldn't get registers"));
> +
> +      i386bsd_supply_gregset (regcache, &regs);
> +      if (regnum != -1)
> +	return;
> +    }
> +
> +  if (regnum == -1 || regnum >= I386_ST0_REGNUM)
> +    {
> +      struct fpreg fpregs;
> +#ifdef HAVE_PT_GETXMMREGS
> +      char xmmregs[512];
> +
> +      if (have_ptrace_xmmregs != 0
> +	  && ptrace(PT_GETXMMREGS, PIDGET (inferior_ptid),
> +		    (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == 0)
> +	{
> +	  have_ptrace_xmmregs = 1;
> +	  i387_supply_fxsave (regcache, -1, xmmregs);
> +	}
> +      else
> +	{
> +          if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
> +		      (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
> +	    perror_with_name (_("Couldn't get floating point status"));
> +
> +	  i387_supply_fsave (regcache, -1, &fpregs);
> +	}
> +#else
> +      if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
> +		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
> +	perror_with_name (_("Couldn't get floating point status"));
> +
> +      i387_supply_fsave (regcache, -1, &fpregs);
> +#endif
> +    }
> +}
> +
> +/* Store register REGNUM back into the inferior.  If REGNUM is -1, do
> +   this for all registers (including the floating point registers).  */
> +
> +static void
> +i386nbsd_store_inferior_registers (struct target_ops *ops,
> +				   struct regcache *regcache, int regnum)
> +{
> +  if (regnum == -1 || GETREGS_SUPPLIES (regnum))
> +    {
> +      struct reg regs;
> +
> +      if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
> +                  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
> +        perror_with_name (_("Couldn't get registers"));
> +
> +      i386bsd_collect_gregset (regcache, &regs, regnum);
> +
> +      if (ptrace (PT_SETREGS, PIDGET (inferior_ptid),
> +	          (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
> +        perror_with_name (_("Couldn't write registers"));
> +
> +      if (regnum != -1)
> +	return;
> +    }
> +
> +  if (regnum == -1 || regnum >= I386_ST0_REGNUM)
> +    {
> +      struct fpreg fpregs;
> +#ifdef HAVE_PT_GETXMMREGS
> +      char xmmregs[512];
> +
> +      if (have_ptrace_xmmregs != 0
> +	  && ptrace(PT_GETXMMREGS, PIDGET (inferior_ptid),
> +		    (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == 0)
> +	{
> +	  have_ptrace_xmmregs = 1;
> +
> +	  i387_collect_fxsave (regcache, regnum, xmmregs);
> +
> +	  if (ptrace (PT_SETXMMREGS, PIDGET (inferior_ptid),
> +		      (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == -1)
> +            perror_with_name (_("Couldn't write XMM registers"));
> +	}
> +      else
> +	{
> +	  have_ptrace_xmmregs = 0;
> +#endif
> +          if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
> +		      (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
> +	    perror_with_name (_("Couldn't get floating point status"));
> +
> +          i387_collect_fsave (regcache, regnum, &fpregs);
> +
> +          if (ptrace (PT_SETFPREGS, PIDGET (inferior_ptid),
> +		      (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
> +	    perror_with_name (_("Couldn't write floating point status"));
> +#ifdef HAVE_PT_GETXMMREGS
> +        }
> +#endif
> +    }
> +}
> +
>  \f
>  
>  /* Provide a prototype to silence -Wmissing-prototypes.  */
> @@ -84,6 +213,8 @@ _initialize_i386nbsd_nat (void)
>    /* Add some extra features to the common *BSD/i386 target.  */
>    t = i386bsd_target ();
>    t->to_pid_to_exec_file = nbsd_pid_to_exec_file;
> +  t->to_fetch_registers = i386nbsd_fetch_inferior_registers;
> +  t->to_store_registers = i386nbsd_store_inferior_registers;
>    add_target (t);
>   
>    /* Support debugging kernel virtual memory images.  */
> Index: gdb/mipsnbsd-nat.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/mipsnbsd-nat.c,v
> retrieving revision 1.17
> diff -u -p -r1.17 mipsnbsd-nat.c
> --- gdb/mipsnbsd-nat.c	1 Jan 2010 07:31:37 -0000	1.17
> +++ gdb/mipsnbsd-nat.c	22 Apr 2010 15:21:55 -0000
> @@ -49,7 +49,7 @@ mipsnbsd_fetch_inferior_registers (struc
>        struct reg regs;
>  
>        if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
> -		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
> +		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
>  	perror_with_name (_("Couldn't get registers"));
>        
>        mipsnbsd_supply_reg (regcache, (char *) &regs, regno);
> @@ -62,7 +62,7 @@ mipsnbsd_fetch_inferior_registers (struc
>        struct fpreg fpregs;
>  
>        if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
> -		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
> +		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
>  	perror_with_name (_("Couldn't get floating point status"));
>  
>        mipsnbsd_supply_fpreg (regcache, (char *) &fpregs, regno);
> @@ -79,13 +79,13 @@ mipsnbsd_store_inferior_registers (struc
>        struct reg regs;
>  
>        if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
> -		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
> +		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
>  	perror_with_name (_("Couldn't get registers"));
>  
>        mipsnbsd_fill_reg (regcache, (char *) &regs, regno);
>  
>        if (ptrace (PT_SETREGS, PIDGET (inferior_ptid), 
> -		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
> +		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
>  	perror_with_name (_("Couldn't write registers"));
>  
>        if (regno != -1)
> @@ -97,13 +97,13 @@ mipsnbsd_store_inferior_registers (struc
>        struct fpreg fpregs; 
>  
>        if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
> -		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
> +		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
>  	perror_with_name (_("Couldn't get floating point status"));
>  
>        mipsnbsd_fill_fpreg (regcache, (char *) &fpregs, regno);
>  
>        if (ptrace (PT_SETFPREGS, PIDGET (inferior_ptid),
> -		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
> +		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
>  	perror_with_name (_("Couldn't write floating point status"));
>      }
>  }
> Index: gdb/nbsd-thread.c
> ===================================================================
> RCS file: gdb/nbsd-thread.c
> diff -N gdb/nbsd-thread.c
> --- /dev/null	1 Jan 1970 00:00:00 -0000
> +++ gdb/nbsd-thread.c	22 Apr 2010 15:21:55 -0000
> @@ -0,0 +1,698 @@
> +/* Threads support for NetBSD 5.0.
> +
> +   Copyright (C) 2010 Free Software Foundation, Inc.
> +
> +   Contributed by Paul Koning, Dell, inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#include "defs.h"
> +#include "command.h"
> +#include "gdbcmd.h"
> +#include "observer.h"
> +#include "target.h"
> +#include "inferior.h"
> +#include "gdbthread.h"
> +#include "regcache.h"
> +
> +#include <sys/types.h>
> +#include <sys/ptrace.h>
> +#include <sys/wait.h>
> +#include <stdlib.h>
> +
> +/* Data structure used to track NetBSD thread state.  There is a 
> +   vector of these, in ascending order of LWP ID.  */
> +
> +struct lwp_info
> +{
> +  /* The process ID of the LWP.  This is a combination of the process
> +     ID and the LWP ID.  */
> +  ptid_t ptid;
> +  
> +  /* Non-zero if we this LWP was reported as having been signalled
> +     by the PT_LWPINFO ptrace() call.  */
> +  int signalled;
> +  
> +  /* The waitstatus for this LWP's last event.  */
> +  struct target_waitstatus waitstatus;
> +};
> +
> +/* The lwp_info buffer and its control variables.  */
> +static struct lwp_info *lwp_buffer;
> +static int lwp_count;
> +static int lwp_bufsize;
> +
> +/* Count of signals still pending delivery to GDB.  These are threads
> +   that were found to be stopped and not breakpoints.  For threads that
> +   hit a breakpoint, we simply push back the thread so it will hit the
> +   break again (if it isn't removed before then) but for other signals,
> +   for example faults, the signal remains pending, the "to_resume" that
> +   resumes the whole process is skipped, and then the "to_wait" returns
> +   the information about one of the pending signals instead.  */
> +static int pending_sigs;
> +
> +/* The LWP ID of the thread being stepped, or 0 if none.  */
> +static int step_lwpid;
> +
> +/* Flag to indicate whether last resume was a resume all threads or
> +   a resume single thread.  */
> +static int resume_all;
> +
> +/* Non-zero if the netbsd-thread layer is active.  */
> +static int nbsd_thread_active = 0;
> +
> +/* The netbsd-thread target_ops structure.  */
> +static struct target_ops nbsd_thread_ops;
> +
> +int debug_nbsd_thread;
> +static void
> +show_debug_nbsd_thread (struct ui_file *file, int from_tty,
> +		      struct cmd_list_element *c, const char *value)
> +{
> +  fprintf_filtered (file, _("Debugging of NetBSD thread module is %s.\n"),
> +		    value);
> +}
> +
> +
> +/* Activate thread support if appropriate.  Do nothing if thread
> +   support is already active.  */
> +
> +static void
> +enable_nbsd_thread (void)
> +{
> +  struct minimal_symbol *msym;
> +  void* caller_context;
> +  int status;
> +
> +  /* If already active, nothing more to do.  */
> +  if (nbsd_thread_active)
> +    return;
> +
> +  if (lwp_buffer != NULL)
> +    {
> +      xfree (lwp_buffer);
> +      lwp_buffer = NULL;
> +      lwp_count = lwp_bufsize = 0;
> +    }
> +  
> +  push_target (&nbsd_thread_ops);
> +  nbsd_thread_active = 1;
> +}
> +
> +/* Deactivate thread support.  Do nothing is thread support is
> +   already inactive.  */
> +
> +static void
> +disable_nbsd_thread (void)
> +{
> +  if (!nbsd_thread_active)
> +    return;
> +
> +  unpush_target (&nbsd_thread_ops);
> +  nbsd_thread_active = 0;
> +}
> +
> +/* Update our lwp_info buffer, and tell GDB about adds or deletes.
> +
> +   By doing the thread add and thread delete operations here as we
> +   learn about threads, we allow users to run thread-specific commands
> +   without needing to run "info threads" first. 
> +
> +   The argument is a pointer to the waitstatus struct, which
> +   is copied into the waitstatus for the thread we find as the signalled
> +   thread.  */
> +
> +static void
> +update_lwpbuf (struct target_waitstatus *status)
> +{
> +  int pi;
> +  lwpid_t lwp_id, sig_lwpid;
> +  struct ptrace_lwpinfo pt_info;
> +  ptid_t ptid;
> +  
> +  /* Accumulate an array of NetBSD threads, in descending order of LWP id.
> +
> +     The reason for using descending order is that this is the order
> +     in which LWPs are returned by the ptrace() PT_LWPINFO function,
> +     because it returns them in the order in which they exist in the
> +     p_lwps list for the process, and new entries are assigned ascending
> +     LWP IDs and are added to the head of that list.  */
> +
> +  lwp_id = sig_lwpid = 0;
> +  pi = 0;
> +  
> +  while (1)
> +    {
> +      pt_info.pl_lwpid = lwp_id;
> +      errno = 0;
> +      if (ptrace (PT_LWPINFO, PIDGET (inferior_ptid),
> +		  (PTRACE_TYPE_ARG3) &pt_info, sizeof (pt_info)) == -1)
> +	{
> +	  if (errno == ESRCH)
> +	    break;
> +	  else if (errno != 0)
> +	    perror_with_name (_("Couldn't get thread information"));
> +	}
> +
> +      if (debug_nbsd_thread)
> +	fprintf_unfiltered (gdb_stdlog,
> +			    "NTUP: lwpinfo on %d returns lwp %d, pl_event %d\n",
> +			    lwp_id, pt_info.pl_lwpid, pt_info.pl_event);
> +	  
> +      /* Retrieve the LWP ID that was found.  This also sets the ID to
> +	 start from the next time around the loop.  */
> +      lwp_id = pt_info.pl_lwpid;
> +
> +      /* LWP id 0 is end of list.  */
> +      if (lwp_id == 0)
> +	break;
> +      
> +      /* If the LWP we found has an ID less than the ID of the current
> +	 buffer entry, then the current buffer entry is a deleted thread.
> +	 Tell GDB about its demise, then remove it from the buffer. 
> +	  
> +	 We have to do this in a loop until we run out of threads
> +	 to be removed.  */
> +      while (pi < lwp_count && lwp_id < TIDGET (lwp_buffer[pi].ptid))
> +	{
> +	  if (debug_nbsd_thread)
> +	    fprintf_unfiltered (gdb_stdlog,
> +				"NTUP: thread ptid %d,%ld has disappeared\n",
> +				PIDGET (lwp_buffer[pi].ptid),
> +				TIDGET (lwp_buffer[pi].ptid));
> +
> +	  /* Tell GDB.  */
> +	  delete_thread (lwp_buffer[pi].ptid);
> +
> +	  /* Remove the deleted entry.  */
> +	  if (pi < lwp_count)
> +	    memmove (lwp_buffer + pi + 1, lwp_buffer + pi, 
> +		     (lwp_count - pi) * sizeof (struct lwp_info));
> +	  lwp_count--;
> +	}
> +
> +      /* If we're now at the end of the current buffer, or the LWP found
> +	 has an LWP ID greater than the current entry in the buffer, this
> +	 is a new thread.  Allocate more buffer space if need be,
> +	 make room for this entry, and store it.  Then tell GDB about
> +	 the new thread.  */
> +      if (pi >= lwp_count || lwp_id > TIDGET (lwp_buffer[pi].ptid))
> +	{
> +	  /* Allocate more space, if we need it.  */
> +	  if (lwp_count == lwp_bufsize)
> +	    {
> +	      if (lwp_bufsize)
> +		lwp_bufsize *= 2;
> +	      else
> +		lwp_bufsize = 1;
> +	      lwp_buffer = (struct lwp_info *) xrealloc (lwp_buffer, 
> +							 lwp_bufsize * sizeof (struct lwp_info));
> +	    }
> +
> +	  /* Push current and later entries, if any, over.  */
> +	  if (pi < lwp_count)
> +	    memmove (lwp_buffer + pi + 1, lwp_buffer + pi, 
> +		     (lwp_count - pi) * sizeof (struct lwp_info));
> +
> +	  /* Update the count of LWPs.  */
> +	  lwp_count++;
> +
> +	  /* Initialize the new entry.  */
> +	  lwp_buffer[pi].ptid = MERGEPID (PIDGET (inferior_ptid), lwp_id);
> +	  if (pt_info.pl_event == PL_EVENT_SIGNAL)
> +	    {
> +	      lwp_buffer[pi].signalled = 1;
> +	      lwp_buffer[pi].waitstatus = *status;
> +	      sig_lwpid = lwp_id;
> +	    }
> +	  else
> +	    lwp_buffer[pi].signalled = 0;
> +
> +	  /* Advance the LWP buffer pointer.  */
> +	  pi++;
> +	  
> +	  /* Tell GDB about the new thread.  */
> +	  if (lwp_count == 1)
> +	    {
> +	      /* See if GDB still has TID zero, if so set the TID.  */
> +	      if (TIDGET (inferior_ptid) == 0)
> +		{
> +		  ptid = MERGEPID (PIDGET (inferior_ptid), lwp_id);
> +		  thread_change_ptid (inferior_ptid, ptid);
> +		  if (debug_nbsd_thread)
> +		    fprintf_unfiltered (gdb_stdlog,
> +					"NTUP: setting main thread ptid to %d,%ld\n",
> +					PIDGET (ptid), TIDGET (ptid));
> +		}
> +	    }
> +	  else
> +	    {
> +	      /* New thread but not the first, add it to GDB.  */
> +	      if (debug_nbsd_thread)
> +		fprintf_unfiltered (gdb_stdlog,
> +				    "NTUP: adding new thread ptid %d,%d\n",
> +				    PIDGET (inferior_ptid), lwp_id);
> +	      add_thread (MERGEPID (PIDGET (inferior_ptid), lwp_id));
> +	    }
> +	}
> +      else
> +	{
> +	  /* Found an existing thread.  Update its status in the buffer.
> +	     Note that we clear the signalled flag if this is the first
> +	     call and this thread wasn't the signalled thread, but we 
> +	     leave it alone on subsequent calls.  That way the subsequent
> +	     calls will accumulate the set of signalled threads.  */
> +	  if (pt_info.pl_event == PL_EVENT_SIGNAL)
> +	    {
> +	      lwp_buffer[pi].signalled = 1;
> +	      lwp_buffer[pi].waitstatus = *status;
> +	      sig_lwpid = lwp_id;
> +	    }
> +	  
> +	  /* Advance the LWP buffer pointer.  */
> +	  pi++;
> +	}
> +    }
> +  if (debug_nbsd_thread)
> +    fprintf_unfiltered (gdb_stdlog,
> +			"NTUP: signalled thread lwpid is %d\n", sig_lwpid);
> +
> +}
> +
> +/* The "to_detach" method of the nbsd_thread_ops.  */
> +
> +static void
> +nbsd_thread_detach (struct target_ops *ops, char *args, int from_tty)
> +{   
> +  struct target_ops *beneath = find_target_beneath (ops);
> +
> +  disable_nbsd_thread ();
> +  beneath->to_detach (beneath, args, from_tty);
> +}
> +
> +/* Resume execution of thread PTID, or all threads if PTID is -1.  If
> +   STEP is nonzero, single-step it.  If SIGNAL is nonzero, give it
> +   that signal.  */
> +
> +static void
> +nbsd_thread_resume (struct target_ops *ops,
> +		    ptid_t ptid, int step, enum target_signal signal)
> +{
> +  pid_t pid;
> +  int request;
> +  
> +  /* A specific PTID means `step only this process id'.  */
> +  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
> +    {
> +      resume_all = 1;
> +      ptid = inferior_ptid;
> +    }
> +  else
> +    resume_all = 0;
> +  
> +  pid = ptid_get_pid (ptid);
> +
> +  if (catch_syscall_enabled () > 0)
> +    request = PT_SYSCALL;
> +  else
> +    request = PT_CONTINUE;
> +
> +  if (step)
> +    {
> +      /* If this system does not support PT_STEP, a higher level
> +         function will have called single_step() to transmute the step
> +         request into a continue request (by setting breakpoints on
> +         all possible successor instructions), so we don't have to
> +         worry about that here.  */
> +      request = PT_STEP;
> +    }
> +
> +  /* An address of (PTRACE_TYPE_ARG3)1 tells ptrace to continue from
> +     where it was.  If GDB wanted it to start some other way, we have
> +     already written a new program counter value to the child.  */
> +  errno = 0;
> +  if (debug_nbsd_thread)
> +    fprintf_unfiltered (gdb_stdlog,
> +			"NTR: %s ptid %d,%ld, %s, signal %d\n",
> +			(step ? "stepping" : "resuming"),
> +			PIDGET (ptid), TIDGET (ptid),
> +			(resume_all ? "all threads" : "single thread"),
> +			signal);
> +
> +  /* Assume not stepping some LWP ID.  */
> +  step_lwpid = 0;
> +  if (step)
> +    {
> +      step_lwpid = TIDGET (ptid);
> +      if (step_lwpid < 0)
> +	step_lwpid = -step_lwpid;
> +    }	
> +  
> +  if (resume_all)
> +    {
> +      if (pending_sigs > 0)
> +	{
> +	  /* We have pending signals from the previous wait still 
> +	     needing to be delivered.  So don't resume the process,
> +	     instead take no action and we'll deliver one of those
> +	     pending signals at the next wait.  */
> +	  if (debug_nbsd_thread)
> +	    fprintf_unfiltered (gdb_stdlog,
> +				"NTR: ptid %d,%ld has %d pending signals, skipping resume\n",
> +				PIDGET (ptid), TIDGET (ptid), pending_sigs);
> +	  return;
> +	}
> +      if (step)
> +	ptrace (request, pid, (PTRACE_TYPE_ARG3)1, TIDGET (ptid));
> +      else
> +	ptrace (request, pid, (PTRACE_TYPE_ARG3)1,
> +		target_signal_to_host (signal));
> +    }
> +  else
> +    ptrace (request, pid, (PTRACE_TYPE_ARG3)1, -TIDGET (ptid));
> +  
> +  if (errno != 0)
> +    perror_with_name (("ptrace"));
> +}
> +
> +/* Wait for the child specified by PTID to do something.  Return the
> +   process ID of the child, or MINUS_ONE_PTID in case of error; store
> +   the status in *OURSTATUS.  */
> +
> +static ptid_t
> +nbsd_thread_wait2 (struct target_ops *ops, ptid_t ptid,
> +		   struct target_waitstatus *ourstatus, int options)
> +{
> +  pid_t pid;
> +  int status, save_errno;
> +
> +  do
> +    {
> +      set_sigint_trap ();
> +
> +      do
> +	{
> +	  if (debug_nbsd_thread)
> +	    fprintf_unfiltered (gdb_stdlog,
> +				"NTW2: waiting for ptid %d,%ld, opt %d\n",
> +				PIDGET (ptid), TIDGET (ptid), options);
> +
> +	  pid = waitpid (ptid_get_pid (ptid), &status, options);
> +	  save_errno = errno;
> +	  if (debug_nbsd_thread)
> +	    fprintf_unfiltered (gdb_stdlog,
> +				"NTW2: waitpid errno is %d, pid %d, status %x\n",
> +				save_errno, pid, status);
> +	}
> +      while (pid == -1 && save_errno == EINTR);
> +
> +      clear_sigint_trap ();
> +
> +      /* If nothing found in the no wait case, report that.  */
> +      if (options == WNOHANG && pid == 0)
> +	return pid_to_ptid (-1);
> +      
> +      if (pid == -1)
> +	{
> +	  fprintf_unfiltered (gdb_stderr,
> +			      _("Child process unexpectedly missing: %s.\n"),
> +			      safe_strerror (save_errno));
> +
> +	  /* If first wait, claim it exited with unknown signal; 
> +	     else claim there is nothing left to wait for.  */
> +	  if (options == WNOHANG)
> +	    return pid_to_ptid (-1);
> +	  ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
> +	  ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
> +	  return inferior_ptid;
> +	}
> +
> +      /* Ignore terminated detached child processes.  */
> +      if (!WIFSTOPPED (status) && pid != ptid_get_pid (inferior_ptid))
> +	pid = -1;
> +    }
> +  while (pid == -1);
> +
> +  store_waitstatus (ourstatus, status);
> +  return pid_to_ptid (pid);
> +}
> +
> +static int
> +nbsd_thread_cancel_breakpoint (struct lwp_info *lp)
> +{
> +  /* Arrange for a breakpoint to be hit again later.  We don't keep
> +     the SIGTRAP status and don't forward the SIGTRAP signal to the
> +     LWP.  We will handle the current event, eventually we will resume
> +     this LWP, and this breakpoint will trap again.
> +
> +     If we do not do this, then we run the risk that the user will
> +     delete or disable the breakpoint, but the LWP will have already
> +     tripped on it.  */
> +
> +  struct regcache *regcache = get_thread_regcache (lp->ptid);
> +  struct gdbarch *gdbarch = get_regcache_arch (regcache);
> +  CORE_ADDR pc;
> +
> +  pc = regcache_read_pc (regcache) - gdbarch_decr_pc_after_break (gdbarch);
> +  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
> +    {
> +      if (debug_nbsd_thread)
> +	fprintf_unfiltered (gdb_stdlog,
> +			    "NTCB: Push back breakpoint for ptid %d,%ld\n",
> +			    PIDGET (lp->ptid), TIDGET (lp->ptid));
> +
> +      /* Back up the PC if necessary.  */
> +      if (gdbarch_decr_pc_after_break (gdbarch))
> +	regcache_write_pc (regcache, pc);
> +
> +      /* We no longer have a pending signal for this thread.  */
> +      lp->signalled = 0;
> +    }
> +  return 0;
> +}
> +
> +/* The "to_wait" method of the nbsd_thread_ops.  */
> +
> +static ptid_t
> +nbsd_thread_wait (struct target_ops *ops,
> +		 ptid_t ptid, struct target_waitstatus *status, int options)
> +{
> +  ptid_t active_ptid;
> +  struct lwp_info *sel_thread;
> +  int sig_threads, i;
> +  struct target_waitstatus tstatus;
> +
> +  /* If there were pending signals and a resume all threads was done,
> +     the process wasn't actually resumed so don't wait on it.  Just
> +     go on to pick a thread to report on.  */
> +  if (!(pending_sigs > 0 && resume_all))
> +    {
> +      ptid = nbsd_thread_wait2 (ops, ptid, &tstatus, 0);
> +
> +      /* Default status returned is the one we just got.  */
> +      *status = tstatus;
> +      
> +      /* The ptid returned by the target beneath us is the ptid of the process.
> +	 We need to find which thread is currently active and return 
> +	 its ptid.  */
> +      update_lwpbuf (&tstatus);
> +  
> +      /* Loop checking for additional threads that are waiting, and gather
> +	 up their status.  */
> +      while (1)
> +	{
> +	  ptid = nbsd_thread_wait2 (ops, ptid, &tstatus, WNOHANG);
> +	  if (PIDGET (ptid) == -1)
> +	    break;
> +	  update_lwpbuf (&tstatus);
> +	}
> +    }
> +  
> +  /* Find a suitable signalled thread.  Pick the stepped one, if there
> +     is one; otherwise pick a random one.  */
> +  sel_thread = NULL;
> +  sig_threads = 0;
> +  
> +  for (i = 0; i < lwp_count; i++)
> +    {
> +      if (lwp_buffer[i].signalled)
> +	{
> +	  sig_threads++;
> +	  if (TIDGET (lwp_buffer[i].ptid) == step_lwpid)
> +	    {
> +	      /* If there is a stepped thread, pick that one.  */
> +	      sel_thread = &lwp_buffer[i];
> +	      if (debug_nbsd_thread)
> +		fprintf_unfiltered (gdb_stdlog,
> +				    "NTW: Picking ptid %d,%ld because it is stepped\n",
> +				    PIDGET (sel_thread->ptid),
> +				    TIDGET (sel_thread->ptid));
> +	      break;
> +	    }
> +	  /* Randomly pick this one or keep the previous choice,
> +	     such that all of the signalled threads have an equal
> +	     probability of being picked.  */
> +	  if (sel_thread == NULL || 
> +	      (((double) rand ()) / (RAND_MAX + 1.0)) < (1.0 / sig_threads))
> +	    {
> +	      sel_thread = &lwp_buffer[i];
> +	      if (debug_nbsd_thread)
> +		fprintf_unfiltered (gdb_stdlog,
> +				    "NTW: Picking ptid %d,%ld out of %d\n",
> +				    PIDGET (sel_thread->ptid),
> +				    TIDGET (sel_thread->ptid), sig_threads);
> +	    }
> +	}
> +    }
> +  
> +  /* Scan the LWP table again.  For each signalled LWP other than the
> +     chosen one, back it up to the breakpoint if it was stopped by a
> +     breakpoint and mark it as not signalled (it will re-break next
> +     time we run the whole process).  Other LWPs (those with signals
> +     other than breakpoint stop) are counted but not backed up; if we
> +     find any of those then those will be delivered next.  */
> +  pending_sigs = 0;
> +  if (sig_threads > 1)
> +    {
> +      for (i = 0; i < lwp_count; i++)
> +	{
> +	  /* Skip the selected LWP.  */
> +	  if (&lwp_buffer[i] == sel_thread)
> +	    continue;
> +	  
> +	  if (lwp_buffer[i].signalled)
> +	    {
> +	      if (WIFSTOPPED (lwp_buffer[i].waitstatus))
> +		{
> +		  if (!nbsd_thread_cancel_breakpoint (&lwp_buffer[i]))
> +		    pending_sigs++;
> +		}
> +	      else
> +		pending_sigs++;
> +	    }
> +	}
> +    }
> +  
> +  ptid = inferior_ptid;
> +  if (sel_thread != NULL)
> +    {
> +      ptid =  MERGEPID (PIDGET (ptid), TIDGET (sel_thread->ptid));
> +      *status = tstatus;
> +      
> +      /* The signal for this thread is now being reported, so clear
> +	 the flag that says it hasn't been reported yet.  */
> +      sel_thread->signalled = 0;
> +    }
> +  else if (debug_nbsd_thread)
> +    fprintf_unfiltered (gdb_stdlog,
> +			"NTW: no signalled thread\n");
> +
> +  if (debug_nbsd_thread)
> +    fprintf_unfiltered (gdb_stdlog,
> +			"NTW: returning ptid %d,%ld\n",
> +			PIDGET (ptid), TIDGET (ptid));
> +
> +  return ptid;
> +}
> +
> +/* The "to_mourn_inferior" method of the nbsd_thread_ops.  */
> +
> +static void
> +nbsd_thread_mourn_inferior (struct target_ops *ops)
> +{
> +  int status;
> +
> +  /* Wait just one more time to collect the inferior's exit status.
> +     Do not check whether this succeeds though, since we may be
> +     dealing with a process that we attached to.  Such a process will
> +     only report its exit status to its original parent.  */
> +  waitpid (ptid_get_pid (inferior_ptid), &status, WNOHANG);
> +
> +  generic_mourn_inferior ();
> +
> +  if (!have_inferiors ())
> +    unpush_target (ops);
> +}
> +
> +
> +/* The "to_thread_alive" method of the nbsd_thread_ops.  */
> +static int
> +nbsd_thread_thread_alive (struct target_ops *ops, ptid_t ptid)
> +{
> +  /* The thread list maintained by GDB is up to date, since we update
> +     it everytime we stop.   So check this list.  */
> +  return in_thread_list (ptid);
> +}
> +
> +/* The "to_pid_to_str" method of the nbsd_thread_ops.  */
> +
> +static char *
> +nbsd_thread_pid_to_str (struct target_ops *ops, ptid_t ptid)
> +{
> +  if (TIDGET (ptid) == 0)
> +    {
> +      struct target_ops *beneath = find_target_beneath (ops);
> +
> +      return beneath->to_pid_to_str (beneath, ptid);
> +    }
> +  return xstrprintf (_("Thread %ld"), TIDGET (ptid));
> +}
> +
> +/* A "new-objfile" observer.  Used to activate/deactivate netbsd-thread
> +   support.  */
> +
> +static void
> +nbsd_thread_new_objfile_observer (struct objfile *objfile)
> +{
> +  if (objfile != NULL)
> +     enable_nbsd_thread ();
> +  else
> +     disable_nbsd_thread ();
> +}
> +
> +static void
> +init_nbsd_thread_ops (void)
> +{
> +  nbsd_thread_ops.to_shortname          = "netbsd-threads";
> +  nbsd_thread_ops.to_longname           = _("NetBSD threads support");
> +  nbsd_thread_ops.to_doc                = _("NetBSD threads support");
> +  nbsd_thread_ops.to_detach             = nbsd_thread_detach;
> +  nbsd_thread_ops.to_resume             = nbsd_thread_resume;
> +  nbsd_thread_ops.to_wait               = nbsd_thread_wait;
> +  nbsd_thread_ops.to_mourn_inferior     = nbsd_thread_mourn_inferior;
> +  nbsd_thread_ops.to_thread_alive       = nbsd_thread_thread_alive;
> +  nbsd_thread_ops.to_pid_to_str         = nbsd_thread_pid_to_str;
> +  nbsd_thread_ops.to_stratum            = thread_stratum;
> +  nbsd_thread_ops.to_magic              = OPS_MAGIC;
> +}
> +
> +void
> +_initialize_nbsd_thread (void)
> +{
> +  init_nbsd_thread_ops ();
> +  add_target (&nbsd_thread_ops);
> +
> +  add_setshow_zinteger_cmd ("nbsd-thread", class_maintenance,
> +			    &debug_nbsd_thread, _("\
> +Set debugging of NetBSD thread module."), _("\
> +Show debugging of NetBSD thread module."), _("\
> +Enables printf debugging output."),
> +			    NULL,
> +			    show_debug_nbsd_thread,
> +			    &setdebuglist, &showdebuglist);
> +
> +  observer_attach_new_objfile (nbsd_thread_new_objfile_observer);
> +}
> Index: gdb/config/i386/nbsdelf.mh
> ===================================================================
> RCS file: /cvs/src/src/gdb/config/i386/nbsdelf.mh,v
> retrieving revision 1.24
> diff -u -p -r1.24 nbsdelf.mh
> --- gdb/config/i386/nbsdelf.mh	17 Dec 2006 13:30:44 -0000	1.24
> +++ gdb/config/i386/nbsdelf.mh	22 Apr 2010 15:21:55 -0000
> @@ -1,5 +1,5 @@
>  # Host: NetBSD/i386 ELF
>  NATDEPFILES= fork-child.o inf-ptrace.o \
> -	nbsd-nat.o i386bsd-nat.o i386nbsd-nat.o bsd-kvm.o
> +	nbsd-nat.o nbsd-thread.o i386bsd-nat.o i386nbsd-nat.o bsd-kvm.o
>  
>  LOADLIBES= -lkvm
> Index: gdb/config/mips/nbsd.mh
> ===================================================================
> RCS file: /cvs/src/src/gdb/config/mips/nbsd.mh,v
> retrieving revision 1.3
> diff -u -p -r1.3 nbsd.mh
> --- gdb/config/mips/nbsd.mh	31 Oct 2004 20:47:55 -0000	1.3
> +++ gdb/config/mips/nbsd.mh	22 Apr 2010 15:21:55 -0000
> @@ -1,2 +1,2 @@
>  # Host: NetBSD/mips
> -NATDEPFILES= fork-child.o inf-ptrace.o mipsnbsd-nat.o
> +NATDEPFILES= fork-child.o inf-ptrace.o nbsd-thread.o mipsnbsd-nat.o
> 
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [PING] [RFC] Thread debug support for NetBSD 5
  2010-05-02 13:29     ` Mark Kettenis
@ 2010-05-03  0:58       ` Paul Koning
  2010-05-03 10:50         ` Paul Koning
  2010-05-03 11:58         ` Mark Kettenis
  0 siblings, 2 replies; 16+ messages in thread
From: Paul Koning @ 2010-05-03  0:58 UTC (permalink / raw)
  To: Mark Kettenis; +Cc: gdb-patches

> I have two questions:
> 
> 1. Does the code still work on older versions of NetBSD?

There isn't any released threads support for older NetBSDs that I know
of.  Also, NetBSD needed some work (to make the ptrace function
sufficiently capable) which is in NetBSD-current, which is 5.0.
 
> 2. You could consider getting rid of the HAVE_PT_GETXMMREGS
>    conditionaization.  That would simplify the code a bit.  It would
>    mean mean dropping support for ancient NetBSD versions though.

Ok, thanks.
 
> I didn't look too closely at the nbsd-thread.c code, but nothing
> stands out as obviously wrong to me.  Pedro spotted a few things there
> though.

Thanks.  I haven't had any other feedback yet.

	paul

^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [PING] [RFC] Thread debug support for NetBSD 5
  2010-05-03  0:58       ` Paul Koning
@ 2010-05-03 10:50         ` Paul Koning
  2010-05-03 11:58         ` Mark Kettenis
  1 sibling, 0 replies; 16+ messages in thread
From: Paul Koning @ 2010-05-03 10:50 UTC (permalink / raw)
  To: Paul Koning, Mark Kettenis; +Cc: gdb-patches

> > I didn't look too closely at the nbsd-thread.c code, but nothing
> > stands out as obviously wrong to me.  Pedro spotted a few things
> there
> > though.
> 
> Thanks.  I haven't had any other feedback yet.

My oversight, it was there and I missed it.  Thanks Pedro.

	paul

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PING] [RFC] Thread debug support for NetBSD 5
  2010-05-03  0:58       ` Paul Koning
  2010-05-03 10:50         ` Paul Koning
@ 2010-05-03 11:58         ` Mark Kettenis
  2010-05-03 13:39           ` Paul Koning
  1 sibling, 1 reply; 16+ messages in thread
From: Mark Kettenis @ 2010-05-03 11:58 UTC (permalink / raw)
  To: Paul_Koning; +Cc: gdb-patches

> Date: Sun, 2 May 2010 20:57:49 -0400
> From: "Paul Koning" <Paul_Koning@Dell.com>
> 
> > I have two questions:
> > 
> > 1. Does the code still work on older versions of NetBSD?
> 
> There isn't any released threads support for older NetBSDs that I know
> of.  Also, NetBSD needed some work (to make the ptrace function
> sufficiently capable) which is in NetBSD-current, which is 5.0.

What I meant to ask is wether a GDB with your patch integrated will
still build and run on NetBSD 4.x (obviously for debugging
single-threaded code only).

^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [PING] [RFC] Thread debug support for NetBSD 5
  2010-05-03 11:58         ` Mark Kettenis
@ 2010-05-03 13:39           ` Paul Koning
  0 siblings, 0 replies; 16+ messages in thread
From: Paul Koning @ 2010-05-03 13:39 UTC (permalink / raw)
  To: Mark Kettenis; +Cc: gdb-patches

> > > 1. Does the code still work on older versions of NetBSD?
> >
> > There isn't any released threads support for older NetBSDs that I
> know
> > of.  Also, NetBSD needed some work (to make the ptrace function
> > sufficiently capable) which is in NetBSD-current, which is 5.0.
> 
> What I meant to ask is wether a GDB with your patch integrated will
> still build and run on NetBSD 4.x (obviously for debugging
> single-threaded code only).

No, it won't.  I have to fix that.  Nick suggested a version check.  I
have to learn more about configure to do that.

	paul

^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [PING] [RFC] Thread debug support for NetBSD 5
  2010-04-29 15:39     ` Pedro Alves
@ 2010-05-03 13:51       ` Paul Koning
  0 siblings, 0 replies; 16+ messages in thread
From: Paul Koning @ 2010-05-03 13:51 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

> > +static int pending_sigs;
> 
> I'm not sure whether this global can get stale between
> debug sessions or not, but it looked like it.  Say, if you
> kill a process while you have pending sigs, the next
> debug session will trip on it being != 0? 

Probably true, I'll fix that.

> It also points
> out that you should probably do something to the pending
> signals when you go about detaching from a process, so
> they don't get lost.

No, this variable is for reporting signals to gdb, so on a detach it
can/should be cleared.
 
> > +  if (catch_syscall_enabled () > 0)
> > +    request = PT_SYSCALL;
> > +  else
> > +    request = PT_CONTINUE;
> 
> I think this will be dead code, since you don't
> support inserting catch syscalls.

I copied that code out of inf-ptrace.c which is the common target code
for targets that use ptrace.  NetBSD was using that before. 
 
> > +  /* An address of (PTRACE_TYPE_ARG3)1 tells ptrace to continue
from
> > +     where it was.  If GDB wanted it to start some other way, we
> have
> > +     already written a new program counter value to the child.  */
> > +  errno = 0;
> 
> If this clearing of errno is needed, then it should move to just
> before the `ptrace' calls.  You have several function calls between
> this and the `ptrace' calls (at least when debugging is enable),
> and any of those could clobber `errno'.  (That's the reason
> for `save_errno' in your patch somewhere else, BTW.)

Right, I missed that when I added the debug messages.  Thanks.
 
> > +      /* If nothing found in the no wait case, report that.  */
> > +      if (options == WNOHANG && pid == 0)
> > +	return pid_to_ptid (-1);
> 
> Use minus_one_ptid, here and everywhere else.
> 
> 
> > +static char *
> > +nbsd_thread_pid_to_str (struct target_ops *ops, ptid_t ptid)
> > +{
> > +  if (TIDGET (ptid) == 0)
> > +    {
> > +      struct target_ops *beneath = find_target_beneath (ops);
> > +
> > +      return beneath->to_pid_to_str (beneath, ptid);
> > +    }
> > +  return xstrprintf (_("Thread %ld"), TIDGET (ptid));
> 
> This leaks.  Nothing ever releases the return of
> target_pid_to_str calls; that's why all implementations
> return a pointer to a static buffer.

Oops.  I copied that from dec-thread.c.
 
	paul

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PING] [RFC] Thread debug support for NetBSD 5
  2010-04-29 14:49   ` [PING] " Paul Koning
                       ` (2 preceding siblings ...)
  2010-05-02 13:29     ` Mark Kettenis
@ 2010-05-03 20:33     ` Paul Koning
  2010-06-10 17:30       ` Tom Tromey
  3 siblings, 1 reply; 16+ messages in thread
From: Paul Koning @ 2010-05-03 20:33 UTC (permalink / raw)
  To: gdb-patches

Excerpt of message (sent 29 April 2010) by Paul Koning:
Thanks for the review comments, attached is an updated patch.

Is the correct procedure for changes to configure.ac to regenerate
configure and include the resulting configure in the patch diffs also?

	  paul

2010-05-03  Paul Koning  <paul_koning@dell.com>

	* i386bsd-nat.c (i386bsd_supply_gregset, i386bsd_collect_gregset):
	Make global.
	* i386bsd-nat.h: Ditto.
	* i386nbsd-nat.c: Include inferior.h, i387-tdep.h, sys/ptrace.h,
	machine/reg.h.
	(i386nbsd_fetch_inferior_registers,
	i386nbsd_store_inferior_registers): New.
	* mipsnbsd-nat.c (mipsnbsd_fetch_inferior_registers,
	mipsnbsd_store_inferior_registers): Pass thread ID to ptrace().
	* nbsd-thread.c: New file.
	* configure.ac: Add nbsd-thread.o to CONFIG_OBS if NetBSD 5 or later
	* configure: Regenerate

Index: gdb/configure
===================================================================
RCS file: /cvs/src/src/gdb/configure,v
retrieving revision 1.302
diff -u -p -r1.302 configure
--- gdb/configure	23 Apr 2010 18:07:27 -0000	1.302
+++ gdb/configure	3 May 2010 20:24:37 -0000
@@ -13113,6 +13113,22 @@ $as_echo "$gdb_cv_have_aix_thread_debug"
          CONFIG_LDFLAGS="${CONFIG_LDFLAGS} -lpthdebug"
       fi
       ;;
+   netbsd*)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for NetBSD thread debugging support" >&5
+$as_echo_n "checking for NetBSD thread debugging support... " >&6; }
+      case ${host_os} in
+         netbsd[56789]* | netbsd[1-9][0-9]* | \
+         netbsdelf[56789]* | netbsdelf[1-9][0-9]*)
+            { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+            CONFIG_SRCS="${CONFIG_SRCS} nbsd-thread.c"
+            CONFIG_OBS="${CONFIG_OBS} nbsd-thread.o"
+            ;;
+         *)
+            { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+            ;;
+      esac
    esac
 
 fi
Index: gdb/configure.ac
===================================================================
RCS file: /cvs/src/src/gdb/configure.ac,v
retrieving revision 1.117
diff -u -p -r1.117 configure.ac
--- gdb/configure.ac	23 Apr 2010 18:07:26 -0000	1.117
+++ gdb/configure.ac	3 May 2010 20:24:37 -0000
@@ -1392,6 +1392,19 @@ if test ${build} = ${host} -a ${host} = 
          CONFIG_LDFLAGS="${CONFIG_LDFLAGS} -lpthdebug"
       fi
       ;;
+   netbsd*)
+      AC_MSG_CHECKING(for NetBSD thread debugging support)
+      case ${host_os} in
+         netbsd[[56789]]* | netbsd[[1-9]][[0-9]]* | \
+         netbsdelf[[56789]]* | netbsdelf[[1-9]][[0-9]]*)
+            AC_MSG_RESULT(yes)
+            CONFIG_SRCS="${CONFIG_SRCS} nbsd-thread.c"
+            CONFIG_OBS="${CONFIG_OBS} nbsd-thread.o"
+            ;;
+         *)
+            AC_MSG_RESULT(no)
+            ;;
+      esac
    esac
    AC_SUBST(CONFIG_LDFLAGS)
 fi
Index: gdb/i386bsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/i386bsd-nat.c,v
retrieving revision 1.43
diff -u -p -r1.43 i386bsd-nat.c
--- gdb/i386bsd-nat.c	1 Jan 2010 07:31:36 -0000	1.43
+++ gdb/i386bsd-nat.c	3 May 2010 20:24:37 -0000
@@ -88,7 +88,7 @@ static int have_ptrace_xmmregs = -1;
 
 /* Supply the general-purpose registers in GREGS, to REGCACHE.  */
 
-static void
+void
 i386bsd_supply_gregset (struct regcache *regcache, const void *gregs)
 {
   const char *regs = gregs;
@@ -107,7 +107,7 @@ i386bsd_supply_gregset (struct regcache 
    GREGS.  If REGNUM is -1, collect and store all appropriate
    registers.  */
 
-static void
+void
 i386bsd_collect_gregset (const struct regcache *regcache,
 			 void *gregs, int regnum)
 {
Index: gdb/i386bsd-nat.h
===================================================================
RCS file: /cvs/src/src/gdb/i386bsd-nat.h,v
retrieving revision 1.8
diff -u -p -r1.8 i386bsd-nat.h
--- gdb/i386bsd-nat.h	1 Jan 2010 07:31:36 -0000	1.8
+++ gdb/i386bsd-nat.h	3 May 2010 20:24:37 -0000
@@ -35,4 +35,11 @@ extern void i386bsd_dr_reset_addr (int r
 
 extern unsigned long i386bsd_dr_get_status (void);
 
+extern void i386bsd_supply_gregset (struct regcache *regcache, 
+                                    const void *gregs);
+
+extern void i386bsd_collect_gregset (const struct regcache *regcache,
+                                     void *gregs, int regnum);
+
+
 #endif /* i386bsd-nat.h */
Index: gdb/i386nbsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/i386nbsd-nat.c,v
retrieving revision 1.22
diff -u -p -r1.22 i386nbsd-nat.c
--- gdb/i386nbsd-nat.c	1 Jan 2010 07:31:36 -0000	1.22
+++ gdb/i386nbsd-nat.c	3 May 2010 20:24:37 -0000
@@ -19,16 +19,20 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "inferior.h"
 #include "gdbcore.h"
 #include "regcache.h"
 #include "target.h"
 
 #include "i386-tdep.h"
+#include "i387-tdep.h"
 #include "i386bsd-nat.h"
 
 /* Support for debugging kernel virtual memory images.  */
 
 #include <sys/types.h>
+#include <sys/ptrace.h>
+#include <machine/reg.h>
 #include <machine/frame.h>
 #include <machine/pcb.h>
 
@@ -71,6 +75,87 @@ i386nbsd_supply_pcb (struct regcache *re
 
   return 1;
 }
+
+/* Macro to determine if a register is fetched with PT_GETREGS.  */
+#define GETREGS_SUPPLIES(regnum) \
+  ((0 <= (regnum) && (regnum) <= 15))
+\f
+
+/* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
+   for all registers (including the floating point registers).  */
+
+static void
+i386nbsd_fetch_inferior_registers (struct target_ops *ops,
+				   struct regcache *regcache, int regnum)
+{
+  if (regnum == -1 || GETREGS_SUPPLIES (regnum))
+    {
+      struct reg regs;
+
+      if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
+	perror_with_name (_("Couldn't get registers"));
+
+      i386bsd_supply_gregset (regcache, &regs);
+      if (regnum != -1)
+	return;
+    }
+
+  if (regnum == -1 || regnum >= I386_ST0_REGNUM)
+    {
+      struct fpreg fpregs;
+      char xmmregs[512];
+
+      if (ptrace(PT_GETXMMREGS, PIDGET (inferior_ptid),
+		 (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == -1)
+	perror_with_name (_("Couldn't read XMM registers"));
+
+      i387_supply_fxsave (regcache, -1, xmmregs);
+    }
+}
+
+/* Store register REGNUM back into the inferior.  If REGNUM is -1, do
+   this for all registers (including the floating point registers).  */
+
+static void
+i386nbsd_store_inferior_registers (struct target_ops *ops,
+				   struct regcache *regcache, int regnum)
+{
+  if (regnum == -1 || GETREGS_SUPPLIES (regnum))
+    {
+      struct reg regs;
+
+      if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
+                  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
+        perror_with_name (_("Couldn't get registers"));
+
+      i386bsd_collect_gregset (regcache, &regs, regnum);
+
+      if (ptrace (PT_SETREGS, PIDGET (inferior_ptid),
+	          (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
+        perror_with_name (_("Couldn't write registers"));
+
+      if (regnum != -1)
+	return;
+    }
+
+  if (regnum == -1 || regnum >= I386_ST0_REGNUM)
+    {
+      struct fpreg fpregs;
+      char xmmregs[512];
+
+      if (ptrace(PT_GETXMMREGS, PIDGET (inferior_ptid),
+		 (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == -1)
+	perror_with_name (_("Couldn't read XMM registers"));
+
+      i387_collect_fxsave (regcache, regnum, xmmregs);
+
+      if (ptrace (PT_SETXMMREGS, PIDGET (inferior_ptid),
+		      (PTRACE_TYPE_ARG3) xmmregs, TIDGET (inferior_ptid)) == -1)
+	perror_with_name (_("Couldn't write XMM registers"));
+    }
+}
+
 \f
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
@@ -84,6 +169,8 @@ _initialize_i386nbsd_nat (void)
   /* Add some extra features to the common *BSD/i386 target.  */
   t = i386bsd_target ();
   t->to_pid_to_exec_file = nbsd_pid_to_exec_file;
+  t->to_fetch_registers = i386nbsd_fetch_inferior_registers;
+  t->to_store_registers = i386nbsd_store_inferior_registers;
   add_target (t);
  
   /* Support debugging kernel virtual memory images.  */
Index: gdb/mipsnbsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/mipsnbsd-nat.c,v
retrieving revision 1.17
diff -u -p -r1.17 mipsnbsd-nat.c
--- gdb/mipsnbsd-nat.c	1 Jan 2010 07:31:37 -0000	1.17
+++ gdb/mipsnbsd-nat.c	3 May 2010 20:24:37 -0000
@@ -49,7 +49,7 @@ mipsnbsd_fetch_inferior_registers (struc
       struct reg regs;
 
       if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get registers"));
       
       mipsnbsd_supply_reg (regcache, (char *) &regs, regno);
@@ -62,7 +62,7 @@ mipsnbsd_fetch_inferior_registers (struc
       struct fpreg fpregs;
 
       if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get floating point status"));
 
       mipsnbsd_supply_fpreg (regcache, (char *) &fpregs, regno);
@@ -79,13 +79,13 @@ mipsnbsd_store_inferior_registers (struc
       struct reg regs;
 
       if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get registers"));
 
       mipsnbsd_fill_reg (regcache, (char *) &regs, regno);
 
       if (ptrace (PT_SETREGS, PIDGET (inferior_ptid), 
-		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &regs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't write registers"));
 
       if (regno != -1)
@@ -97,13 +97,13 @@ mipsnbsd_store_inferior_registers (struc
       struct fpreg fpregs; 
 
       if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't get floating point status"));
 
       mipsnbsd_fill_fpreg (regcache, (char *) &fpregs, regno);
 
       if (ptrace (PT_SETFPREGS, PIDGET (inferior_ptid),
-		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+		  (PTRACE_TYPE_ARG3) &fpregs, TIDGET (inferior_ptid)) == -1)
 	perror_with_name (_("Couldn't write floating point status"));
     }
 }
Index: gdb/nbsd-thread.c
===================================================================
RCS file: gdb/nbsd-thread.c
diff -N gdb/nbsd-thread.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdb/nbsd-thread.c	3 May 2010 20:24:37 -0000
@@ -0,0 +1,708 @@
+/* Threads support for NetBSD 5.0.
+
+   Copyright (C) 2010 Free Software Foundation, Inc.
+
+   Contributed by Paul Koning, Dell, inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "observer.h"
+#include "target.h"
+#include "inferior.h"
+#include "gdbthread.h"
+#include "regcache.h"
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+
+/* Data structure used to track NetBSD thread state.  There is a 
+   vector of these, in ascending order of LWP ID.  */
+
+struct lwp_info
+{
+  /* The process ID of the LWP.  This is a combination of the process
+     ID and the LWP ID.  */
+  ptid_t ptid;
+  
+  /* Non-zero if we this LWP was reported as having been signalled
+     by the PT_LWPINFO ptrace() call.  */
+  int signalled;
+  
+  /* The waitstatus for this LWP's last event.  */
+  struct target_waitstatus waitstatus;
+};
+
+/* The lwp_info buffer and its control variables.  */
+static struct lwp_info *lwp_buffer;
+static int lwp_count;
+static int lwp_bufsize;
+
+/* Count of signals still pending delivery to GDB.  These are threads
+   that were found to be stopped and not breakpoints.  For threads that
+   hit a breakpoint, we simply push back the thread so it will hit the
+   break again (if it isn't removed before then) but for other signals,
+   for example faults, the signal remains pending, the "to_resume" that
+   resumes the whole process is skipped, and then the "to_wait" returns
+   the information about one of the pending signals instead.  */
+static int pending_sigs;
+
+/* The LWP ID of the thread being stepped, or 0 if none.  */
+static int step_lwpid;
+
+/* Flag to indicate whether last resume was a resume all threads or
+   a resume single thread.  */
+static int resume_all;
+
+/* Non-zero if the netbsd-thread layer is active.  */
+static int nbsd_thread_active = 0;
+
+/* The netbsd-thread target_ops structure.  */
+static struct target_ops nbsd_thread_ops;
+
+int debug_nbsd_thread;
+static void
+show_debug_nbsd_thread (struct ui_file *file, int from_tty,
+		      struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Debugging of NetBSD thread module is %s.\n"),
+		    value);
+}
+
+
+/* Activate thread support if appropriate.  Do nothing if thread
+   support is already active.  */
+
+static void
+enable_nbsd_thread (void)
+{
+  struct minimal_symbol *msym;
+  void* caller_context;
+  int status;
+
+  /* If already active, nothing more to do.  */
+  if (nbsd_thread_active)
+    return;
+
+  if (lwp_buffer != NULL)
+    {
+      xfree (lwp_buffer);
+      lwp_buffer = NULL;
+      lwp_count = lwp_bufsize = 0;
+    }
+  
+  pending_sigs = 0;
+  
+  push_target (&nbsd_thread_ops);
+  nbsd_thread_active = 1;
+}
+
+/* Deactivate thread support.  Do nothing if thread support is
+   already inactive.  */
+
+static void
+disable_nbsd_thread (void)
+{
+  if (!nbsd_thread_active)
+    return;
+
+  unpush_target (&nbsd_thread_ops);
+  nbsd_thread_active = 0;
+}
+
+/* Update our lwp_info buffer, and tell GDB about adds or deletes.
+
+   By doing the thread add and thread delete operations here as we
+   learn about threads, we allow users to run thread-specific commands
+   without needing to run "info threads" first. 
+
+   The argument is a pointer to the waitstatus struct, which
+   is copied into the waitstatus for the thread we find as the signalled
+   thread.  */
+
+static void
+update_lwpbuf (struct target_waitstatus *status)
+{
+  int pi;
+  lwpid_t lwp_id, sig_lwpid;
+  struct ptrace_lwpinfo pt_info;
+  ptid_t ptid;
+  
+  /* Accumulate an array of NetBSD threads, in descending order of LWP id.
+
+     The reason for using descending order is that this is the order
+     in which LWPs are returned by the ptrace() PT_LWPINFO function,
+     because it returns them in the order in which they exist in the
+     p_lwps list for the process, and new entries are assigned ascending
+     LWP IDs and are added to the head of that list.  */
+
+  lwp_id = sig_lwpid = 0;
+  pi = 0;
+  
+  while (1)
+    {
+      pt_info.pl_lwpid = lwp_id;
+      errno = 0;
+      if (ptrace (PT_LWPINFO, PIDGET (inferior_ptid),
+		  (PTRACE_TYPE_ARG3) &pt_info, sizeof (pt_info)) == -1)
+	{
+	  if (errno == ESRCH)
+	    break;
+	  else if (errno != 0)
+	    perror_with_name (_("Couldn't get thread information"));
+	}
+
+      if (debug_nbsd_thread)
+	fprintf_unfiltered (gdb_stdlog,
+			    "NTUP: lwpinfo on %d returns lwp %d, pl_event %d\n",
+			    lwp_id, pt_info.pl_lwpid, pt_info.pl_event);
+	  
+      /* Retrieve the LWP ID that was found.  This also sets the ID to
+	 start from the next time around the loop.  */
+      lwp_id = pt_info.pl_lwpid;
+
+      /* LWP id 0 is end of list.  */
+      if (lwp_id == 0)
+	break;
+      
+      /* If the LWP we found has an ID less than the ID of the current
+	 buffer entry, then the current buffer entry is a deleted thread.
+	 Tell GDB about its demise, then remove it from the buffer. 
+	  
+	 We have to do this in a loop until we run out of threads
+	 to be removed.  */
+      while (pi < lwp_count && lwp_id < TIDGET (lwp_buffer[pi].ptid))
+	{
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTUP: thread ptid %d,%ld has disappeared\n",
+				PIDGET (lwp_buffer[pi].ptid),
+				TIDGET (lwp_buffer[pi].ptid));
+
+	  /* Tell GDB.  */
+	  delete_thread (lwp_buffer[pi].ptid);
+
+	  /* Remove the deleted entry.  */
+	  if (pi + 1 < lwp_count)
+	    memmove (lwp_buffer + pi, lwp_buffer + pi + 1, 
+		     (lwp_count - pi - 1) * sizeof (struct lwp_info));
+	  lwp_count--;
+	}
+
+      /* If we're now at the end of the current buffer, or the LWP found
+	 has an LWP ID greater than the current entry in the buffer, this
+	 is a new thread.  Allocate more buffer space if need be,
+	 make room for this entry, and store it.  Then tell GDB about
+	 the new thread.  */
+      if (pi >= lwp_count || lwp_id > TIDGET (lwp_buffer[pi].ptid))
+	{
+	  /* Allocate more space, if we need it.  */
+	  if (lwp_count == lwp_bufsize)
+	    {
+	      if (lwp_bufsize)
+		lwp_bufsize *= 2;
+	      else
+		lwp_bufsize = 1;
+	      lwp_buffer = (struct lwp_info *) xrealloc (lwp_buffer, 
+							 lwp_bufsize * sizeof (struct lwp_info));
+	    }
+
+	  /* Push current and later entries, if any, over.  */
+	  if (pi < lwp_count)
+	    memmove (lwp_buffer + pi + 1, lwp_buffer + pi, 
+		     (lwp_count - pi) * sizeof (struct lwp_info));
+
+	  /* Update the count of LWPs.  */
+	  lwp_count++;
+
+	  /* Initialize the new entry.  */
+	  lwp_buffer[pi].ptid = MERGEPID (PIDGET (inferior_ptid), lwp_id);
+	  if (pt_info.pl_event == PL_EVENT_SIGNAL)
+	    {
+	      lwp_buffer[pi].signalled = 1;
+	      lwp_buffer[pi].waitstatus = *status;
+	      sig_lwpid = lwp_id;
+	    }
+	  else
+	    lwp_buffer[pi].signalled = 0;
+
+	  /* Advance the LWP buffer pointer.  */
+	  pi++;
+	  
+	  /* Tell GDB about the new thread.  */
+	  if (lwp_count == 1)
+	    {
+	      /* See if GDB still has TID zero, if so set the TID.  */
+	      if (TIDGET (inferior_ptid) == 0)
+		{
+		  ptid = MERGEPID (PIDGET (inferior_ptid), lwp_id);
+		  thread_change_ptid (inferior_ptid, ptid);
+		  if (debug_nbsd_thread)
+		    fprintf_unfiltered (gdb_stdlog,
+					"NTUP: setting main thread ptid to %d,%ld\n",
+					PIDGET (ptid), TIDGET (ptid));
+		}
+	    }
+	  else
+	    {
+	      /* New thread but not the first, add it to GDB.  */
+	      if (debug_nbsd_thread)
+		fprintf_unfiltered (gdb_stdlog,
+				    "NTUP: adding new thread ptid %d,%d\n",
+				    PIDGET (inferior_ptid), lwp_id);
+	      add_thread (MERGEPID (PIDGET (inferior_ptid), lwp_id));
+	    }
+	}
+      else
+	{
+	  /* Found an existing thread.  Update its status in the buffer.
+	     Note that we clear the signalled flag if this is the first
+	     call and this thread wasn't the signalled thread, but we 
+	     leave it alone on subsequent calls.  That way the subsequent
+	     calls will accumulate the set of signalled threads.  */
+	  if (pt_info.pl_event == PL_EVENT_SIGNAL)
+	    {
+	      lwp_buffer[pi].signalled = 1;
+	      lwp_buffer[pi].waitstatus = *status;
+	      sig_lwpid = lwp_id;
+	    }
+	  
+	  /* Advance the LWP buffer pointer.  */
+	  pi++;
+	}
+    }
+  if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTUP: signalled thread lwpid is %d\n", sig_lwpid);
+
+}
+
+/* The "to_detach" method of the nbsd_thread_ops.  */
+
+static void
+nbsd_thread_detach (struct target_ops *ops, char *args, int from_tty)
+{   
+  struct target_ops *beneath = find_target_beneath (ops);
+
+  disable_nbsd_thread ();
+  beneath->to_detach (beneath, args, from_tty);
+}
+
+/* Resume execution of thread PTID, or all threads if PTID is -1.  If
+   STEP is nonzero, single-step it.  If SIGNAL is nonzero, give it
+   that signal.  */
+
+static void
+nbsd_thread_resume (struct target_ops *ops,
+		    ptid_t ptid, int step, enum target_signal signal)
+{
+  pid_t pid;
+  int request;
+  
+  /* A specific PTID means `step only this process id'.  */
+  if (ptid_equal (minus_one_ptid, ptid) || ptid_is_pid (ptid))
+    {
+      resume_all = 1;
+      ptid = inferior_ptid;
+    }
+  else
+    resume_all = 0;
+  
+  pid = ptid_get_pid (ptid);
+
+  if (catch_syscall_enabled () > 0)
+    request = PT_SYSCALL;
+  else
+    request = PT_CONTINUE;
+
+  if (step)
+    {
+      /* If this system does not support PT_STEP, a higher level
+         function will have called single_step() to transmute the step
+         request into a continue request (by setting breakpoints on
+         all possible successor instructions), so we don't have to
+         worry about that here.  */
+      request = PT_STEP;
+    }
+
+  /* An address of (PTRACE_TYPE_ARG3)1 tells ptrace to continue from
+     where it was.  If GDB wanted it to start some other way, we have
+     already written a new program counter value to the child.  */
+  if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTR: %s ptid %d,%ld, %s, signal %d\n",
+			(step ? "stepping" : "resuming"),
+			PIDGET (ptid), TIDGET (ptid),
+			(resume_all ? "all threads" : "single thread"),
+			signal);
+
+  /* Assume not stepping some LWP ID.  */
+  step_lwpid = 0;
+  if (step)
+    {
+      step_lwpid = TIDGET (ptid);
+      if (step_lwpid < 0)
+	step_lwpid = -step_lwpid;
+    }	
+  
+  if (resume_all)
+    {
+      if (pending_sigs > 0)
+	{
+	  /* We have pending signals from the previous wait still 
+	     needing to be delivered.  So don't resume the process,
+	     instead take no action and we'll deliver one of those
+	     pending signals at the next wait.  */
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTR: ptid %d,%ld has %d pending signals, skipping resume\n",
+				PIDGET (ptid), TIDGET (ptid), pending_sigs);
+	  return;
+	}
+      errno = 0;
+      if (step)
+	ptrace (request, pid, (PTRACE_TYPE_ARG3)1, TIDGET (ptid));
+      else
+	ptrace (request, pid, (PTRACE_TYPE_ARG3)1,
+		target_signal_to_host (signal));
+    }
+  else
+    {
+      errno = 0;
+      ptrace (request, pid, (PTRACE_TYPE_ARG3)1, -TIDGET (ptid));
+    }
+  
+  if (errno != 0)
+    perror_with_name (("ptrace"));
+}
+
+/* Wait for the child specified by PTID to do something.  Return the
+   process ID of the child, or MINUS_ONE_PTID in case of error; store
+   the status in *OURSTATUS.  */
+
+static ptid_t
+nbsd_thread_wait2 (struct target_ops *ops, ptid_t ptid,
+		   struct target_waitstatus *ourstatus, int options)
+{
+  pid_t pid;
+  int status, save_errno;
+
+  do
+    {
+      set_sigint_trap ();
+
+      do
+	{
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTW2: waiting for ptid %d,%ld, opt %d\n",
+				PIDGET (ptid), TIDGET (ptid), options);
+
+	  pid = waitpid (ptid_get_pid (ptid), &status, options);
+	  save_errno = errno;
+	  if (debug_nbsd_thread)
+	    fprintf_unfiltered (gdb_stdlog,
+				"NTW2: waitpid errno is %d, pid %d, status %x\n",
+				save_errno, pid, status);
+	}
+      while (pid == -1 && save_errno == EINTR);
+
+      clear_sigint_trap ();
+
+      /* If nothing found in the no wait case, report that.  */
+      if (options == WNOHANG && pid == 0)
+	return minus_one_ptid;
+      
+      if (pid == -1)
+	{
+	  /* If first wait, claim it exited with unknown signal; 
+	     else claim there is nothing left to wait for.  */
+	  if (options == WNOHANG)
+	    return minus_one_ptid;
+
+	  fprintf_unfiltered (gdb_stderr,
+			      _("Child process unexpectedly missing: %s.\n"),
+			      safe_strerror (save_errno));
+	  ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+	  ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
+	  return inferior_ptid;
+	}
+
+      /* Ignore terminated detached child processes.  */
+      if (!WIFSTOPPED (status) && pid != ptid_get_pid (inferior_ptid))
+	pid = -1;
+    }
+  while (pid == -1);
+
+  store_waitstatus (ourstatus, status);
+  return pid_to_ptid (pid);
+}
+
+static int
+nbsd_thread_cancel_breakpoint (struct lwp_info *lp)
+{
+  /* Arrange for a breakpoint to be hit again later.  We don't keep
+     the SIGTRAP status and don't forward the SIGTRAP signal to the
+     LWP.  We will handle the current event, eventually we will resume
+     this LWP, and this breakpoint will trap again.
+
+     If we do not do this, then we run the risk that the user will
+     delete or disable the breakpoint, but the LWP will have already
+     tripped on it.  */
+
+  struct regcache *regcache = get_thread_regcache (lp->ptid);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  CORE_ADDR pc;
+
+  pc = regcache_read_pc (regcache) - gdbarch_decr_pc_after_break (gdbarch);
+  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
+    {
+      if (debug_nbsd_thread)
+	fprintf_unfiltered (gdb_stdlog,
+			    "NTCB: Push back breakpoint for ptid %d,%ld\n",
+			    PIDGET (lp->ptid), TIDGET (lp->ptid));
+
+      /* Back up the PC if necessary.  */
+      if (gdbarch_decr_pc_after_break (gdbarch))
+	regcache_write_pc (regcache, pc);
+
+      /* We no longer have a pending signal for this thread.  */
+      lp->signalled = 0;
+    }
+  return 0;
+}
+
+/* The "to_wait" method of the nbsd_thread_ops.  */
+
+static ptid_t
+nbsd_thread_wait (struct target_ops *ops,
+		 ptid_t ptid, struct target_waitstatus *status, int options)
+{
+  ptid_t active_ptid;
+  struct lwp_info *sel_thread;
+  int sig_threads, i;
+  struct target_waitstatus tstatus;
+
+  /* If there were pending signals and a resume all threads was done,
+     the process wasn't actually resumed so don't wait on it.  Just
+     go on to pick a thread to report on.  */
+  if (!(pending_sigs > 0 && resume_all))
+    {
+      ptid = nbsd_thread_wait2 (ops, ptid, &tstatus, 0);
+
+      /* Default status returned is the one we just got.  */
+      *status = tstatus;
+      
+      /* The ptid returned by the target beneath us is the ptid of the process.
+	 We need to find which thread is currently active and return 
+	 its ptid.  */
+      update_lwpbuf (&tstatus);
+  
+      /* Loop checking for additional threads that are waiting, and gather
+	 up their status.  */
+      while (1)
+	{
+	  ptid = nbsd_thread_wait2 (ops, ptid, &tstatus, WNOHANG);
+	  if (PIDGET (ptid) == -1)
+	    break;
+	  update_lwpbuf (&tstatus);
+	}
+    }
+  
+  /* Find a suitable signalled thread.  Pick the stepped one, if there
+     is one; otherwise pick a random one.  */
+  sel_thread = NULL;
+  sig_threads = 0;
+  
+  for (i = 0; i < lwp_count; i++)
+    {
+      if (lwp_buffer[i].signalled)
+	{
+	  sig_threads++;
+	  if (TIDGET (lwp_buffer[i].ptid) == step_lwpid)
+	    {
+	      /* If there is a stepped thread, pick that one.  */
+	      sel_thread = &lwp_buffer[i];
+	      if (debug_nbsd_thread)
+		fprintf_unfiltered (gdb_stdlog,
+				    "NTW: Picking ptid %d,%ld because it is stepped\n",
+				    PIDGET (sel_thread->ptid),
+				    TIDGET (sel_thread->ptid));
+	      break;
+	    }
+	  /* Randomly pick this one or keep the previous choice,
+	     such that all of the signalled threads have an equal
+	     probability of being picked.  */
+	  if (sel_thread == NULL || 
+	      (((double) rand ()) / (RAND_MAX + 1.0)) < (1.0 / sig_threads))
+	    {
+	      sel_thread = &lwp_buffer[i];
+	      if (debug_nbsd_thread)
+		fprintf_unfiltered (gdb_stdlog,
+				    "NTW: Picking ptid %d,%ld out of %d\n",
+				    PIDGET (sel_thread->ptid),
+				    TIDGET (sel_thread->ptid), sig_threads);
+	    }
+	}
+    }
+  
+  /* Scan the LWP table again.  For each signalled LWP other than the
+     chosen one, back it up to the breakpoint if it was stopped by a
+     breakpoint and mark it as not signalled (it will re-break next
+     time we run the whole process).  Other LWPs (those with signals
+     other than breakpoint stop) are counted but not backed up; if we
+     find any of those then those will be delivered next.  */
+  pending_sigs = 0;
+  if (sig_threads > 1)
+    {
+      for (i = 0; i < lwp_count; i++)
+	{
+	  /* Skip the selected LWP.  */
+	  if (&lwp_buffer[i] == sel_thread)
+	    continue;
+	  
+	  if (lwp_buffer[i].signalled)
+	    {
+	      if (WIFSTOPPED (lwp_buffer[i].waitstatus))
+		{
+		  if (!nbsd_thread_cancel_breakpoint (&lwp_buffer[i]))
+		    pending_sigs++;
+		}
+	      else
+		pending_sigs++;
+	    }
+	}
+    }
+  
+  ptid = inferior_ptid;
+  if (sel_thread != NULL)
+    {
+      ptid =  MERGEPID (PIDGET (ptid), TIDGET (sel_thread->ptid));
+      *status = tstatus;
+      
+      /* The signal for this thread is now being reported, so clear
+	 the flag that says it hasn't been reported yet.  */
+      sel_thread->signalled = 0;
+    }
+  else if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTW: no signalled thread\n");
+
+  if (debug_nbsd_thread)
+    fprintf_unfiltered (gdb_stdlog,
+			"NTW: returning ptid %d,%ld\n",
+			PIDGET (ptid), TIDGET (ptid));
+
+  return ptid;
+}
+
+/* The "to_mourn_inferior" method of the nbsd_thread_ops.  */
+
+static void
+nbsd_thread_mourn_inferior (struct target_ops *ops)
+{
+  int status;
+
+  /* Wait just one more time to collect the inferior's exit status.
+     Do not check whether this succeeds though, since we may be
+     dealing with a process that we attached to.  Such a process will
+     only report its exit status to its original parent.  */
+  waitpid (ptid_get_pid (inferior_ptid), &status, WNOHANG);
+
+  generic_mourn_inferior ();
+
+  if (!have_inferiors ())
+    unpush_target (ops);
+}
+
+
+/* The "to_thread_alive" method of the nbsd_thread_ops.  */
+static int
+nbsd_thread_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+  /* The thread list maintained by GDB is up to date, since we update
+     it everytime we stop.   So check this list.  */
+  return in_thread_list (ptid);
+}
+
+/* The "to_pid_to_str" method of the nbsd_thread_ops.  */
+
+static char *
+nbsd_thread_pid_to_str (struct target_ops *ops, ptid_t ptid)
+{
+  static char buf[64];
+  if (TIDGET (ptid) == 0)
+    {
+      struct target_ops *beneath = find_target_beneath (ops);
+
+      return beneath->to_pid_to_str (beneath, ptid);
+    }
+  snprintf (buf, sizeof (buf), _("Thread %ld"), TIDGET (ptid));
+  return buf;
+}
+
+/* A "new-objfile" observer.  Used to activate/deactivate netbsd-thread
+   support.  */
+
+static void
+nbsd_thread_new_objfile_observer (struct objfile *objfile)
+{
+  if (objfile != NULL)
+     enable_nbsd_thread ();
+  else
+     disable_nbsd_thread ();
+}
+
+static void
+init_nbsd_thread_ops (void)
+{
+  nbsd_thread_ops.to_shortname = "netbsd-threads";
+  nbsd_thread_ops.to_longname = _("NetBSD threads support");
+  nbsd_thread_ops.to_doc = _("NetBSD threads support");
+  nbsd_thread_ops.to_detach = nbsd_thread_detach;
+  nbsd_thread_ops.to_resume = nbsd_thread_resume;
+  nbsd_thread_ops.to_wait = nbsd_thread_wait;
+  nbsd_thread_ops.to_mourn_inferior = nbsd_thread_mourn_inferior;
+  nbsd_thread_ops.to_thread_alive = nbsd_thread_thread_alive;
+  nbsd_thread_ops.to_pid_to_str = nbsd_thread_pid_to_str;
+  nbsd_thread_ops.to_stratum = thread_stratum;
+  nbsd_thread_ops.to_magic = OPS_MAGIC;
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+extern initialize_file_ftype _initialize_nbsd_thread;
+
+void
+_initialize_nbsd_thread (void)
+{
+  init_nbsd_thread_ops ();
+  add_target (&nbsd_thread_ops);
+
+  add_setshow_zinteger_cmd ("nbsd-thread", class_maintenance,
+			    &debug_nbsd_thread, _("\
+Set debugging of NetBSD thread module."), _("\
+Show debugging of NetBSD thread module."), _("\
+Enables printf debugging output."),
+			    NULL,
+			    show_debug_nbsd_thread,
+			    &setdebuglist, &showdebuglist);
+
+  observer_attach_new_objfile (nbsd_thread_new_objfile_observer);
+}

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PING] [RFC] Thread debug support for NetBSD 5
  2010-05-03 20:33     ` Paul Koning
@ 2010-06-10 17:30       ` Tom Tromey
  2010-06-11 14:02         ` Pedro Alves
  0 siblings, 1 reply; 16+ messages in thread
From: Tom Tromey @ 2010-06-10 17:30 UTC (permalink / raw)
  To: Paul Koning; +Cc: gdb-patches

>>>>> "Paul" == Paul Koning <Paul_Koning@dell.com> writes:

Paul> Excerpt of message (sent 29 April 2010) by Paul Koning:
Paul> Thanks for the review comments, attached is an updated patch.

I didn't see a response to this yet.
I am not really the best person to review it :-(.
However, I don't like patches to languish this long -- if you don't hear
a response in the next couple of weeks, please ping me and I will take a
stab at it.

Paul> Is the correct procedure for changes to configure.ac to regenerate
Paul> configure and include the resulting configure in the patch diffs also?

For gcc, the usual custom is to exclude generated files from the patch.
I am not sure whether this holds for gdb or not.  I does not matter to
me personally, I just skip over the noisy bits.

Tom

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PING] [RFC] Thread debug support for NetBSD 5
  2010-06-10 17:30       ` Tom Tromey
@ 2010-06-11 14:02         ` Pedro Alves
  2010-06-11 14:40           ` Paul Koning
  2010-06-22 17:37           ` Mark Kettenis
  0 siblings, 2 replies; 16+ messages in thread
From: Pedro Alves @ 2010-06-11 14:02 UTC (permalink / raw)
  To: gdb-patches, tromey; +Cc: Paul Koning

This

>         * nbsd-thread.c: New file.

looks good to me now, thanks for the update.

I lost track and I'm not sure whether MarkK's remarks have been addressed
and/or reviewed already, and/or what else needs reviewing.

-- 
Pedro Alves

^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [PING] [RFC] Thread debug support for NetBSD 5
  2010-06-11 14:02         ` Pedro Alves
@ 2010-06-11 14:40           ` Paul Koning
  2010-06-22 17:37           ` Mark Kettenis
  1 sibling, 0 replies; 16+ messages in thread
From: Paul Koning @ 2010-06-11 14:40 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches, tromey

Thanks gents, I'll review the previous mail and report.

	paul

> -----Original Message-----
> From: Pedro Alves [mailto:pedro@codesourcery.com]
> Sent: Friday, June 11, 2010 10:02 AM
> To: gdb-patches@sourceware.org; tromey@redhat.com
> Cc: Paul Koning
> Subject: Re: [PING] [RFC] Thread debug support for NetBSD 5
> 
> This
> 
> >         * nbsd-thread.c: New file.
> 
> looks good to me now, thanks for the update.
> 
> I lost track and I'm not sure whether MarkK's remarks have been
> addressed
> and/or reviewed already, and/or what else needs reviewing.
> 
> --
> Pedro Alves

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PING] [RFC] Thread debug support for NetBSD 5
  2010-06-11 14:02         ` Pedro Alves
  2010-06-11 14:40           ` Paul Koning
@ 2010-06-22 17:37           ` Mark Kettenis
  1 sibling, 0 replies; 16+ messages in thread
From: Mark Kettenis @ 2010-06-22 17:37 UTC (permalink / raw)
  To: pedro; +Cc: gdb-patches, tromey, Paul_Koning

> From: Pedro Alves <pedro@codesourcery.com>
> Date: Fri, 11 Jun 2010 15:02:22 +0100
> 
> This
> 
> >         * nbsd-thread.c: New file.
> 
> looks good to me now, thanks for the update.
> 
> I lost track and I'm not sure whether MarkK's remarks have been addressed
> and/or reviewed already, and/or what else needs reviewing.

They've been addressed.  I think someone should set up Paul with an
account on sourceware.org such that he can add himself to the Write
After Approval list and commit these diffs.

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2010-06-22 17:37 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-04-20 15:49 [RFC] Thread debug support for NetBSD 5 Paul Koning
2010-04-22 15:36 ` Paul Koning
2010-04-29 14:49   ` [PING] " Paul Koning
2010-04-29 15:39     ` Pedro Alves
2010-05-03 13:51       ` Paul Koning
2010-04-30 12:44     ` Nick Hudson
2010-05-02 13:29     ` Mark Kettenis
2010-05-03  0:58       ` Paul Koning
2010-05-03 10:50         ` Paul Koning
2010-05-03 11:58         ` Mark Kettenis
2010-05-03 13:39           ` Paul Koning
2010-05-03 20:33     ` Paul Koning
2010-06-10 17:30       ` Tom Tromey
2010-06-11 14:02         ` Pedro Alves
2010-06-11 14:40           ` Paul Koning
2010-06-22 17:37           ` Mark Kettenis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).