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


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