This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFC] Thread debug support for NetBSD 5


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
+
+
+/* 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
+    }
+}
+
 
 
 /* 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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]