This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PING] [RFC] Thread debug support for NetBSD 5
- From: Mark Kettenis <mark dot kettenis at xs4all dot nl>
- To: Paul_Koning at dell dot com
- Cc: gdb-patches at sourceware dot org
- Date: Sun, 2 May 2010 15:28:47 +0200 (CEST)
- Subject: Re: [PING] [RFC] Thread debug support for NetBSD 5
- References: <19405.52446.728141.329821@pkoning-laptop.equallogic.com> <19408.27827.830391.509465@pkoning-laptop.equallogic.com> <19417.40044.762978.858637@pkoning-laptop.equallogic.com>
> 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) ®s, TIDGET (inferior_ptid)) == -1)
> + perror_with_name (_("Couldn't get registers"));
> +
> + i386bsd_supply_gregset (regcache, ®s);
> + 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) ®s, TIDGET (inferior_ptid)) == -1)
> + perror_with_name (_("Couldn't get registers"));
> +
> + i386bsd_collect_gregset (regcache, ®s, regnum);
> +
> + if (ptrace (PT_SETREGS, PIDGET (inferior_ptid),
> + (PTRACE_TYPE_ARG3) ®s, 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) ®s, 0) == -1)
> + (PTRACE_TYPE_ARG3) ®s, TIDGET (inferior_ptid)) == -1)
> perror_with_name (_("Couldn't get registers"));
>
> mipsnbsd_supply_reg (regcache, (char *) ®s, 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) ®s, 0) == -1)
> + (PTRACE_TYPE_ARG3) ®s, TIDGET (inferior_ptid)) == -1)
> perror_with_name (_("Couldn't get registers"));
>
> mipsnbsd_fill_reg (regcache, (char *) ®s, regno);
>
> if (ptrace (PT_SETREGS, PIDGET (inferior_ptid),
> - (PTRACE_TYPE_ARG3) ®s, 0) == -1)
> + (PTRACE_TYPE_ARG3) ®s, 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
>
>