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: [PING] [RFC] Thread debug support for NetBSD 5


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


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