This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [RFC] Thread debug support for NetBSD 5
Any comments?
Attached is a slightly updated version of the patch I sent earlier.
The change affects a few lines in the new file (nbsd-thread.c) to cure
an infinite loop if the child process exits. Credit to Antti Kantee
for help with this.
paul
Excerpt of message (sent 20 April 2010) by Paul Koning:
> This patch adds thread debug support for NetBSD 5.0. Unlike older
> NetBSDs, in that version threads are seen by the kernel and exposed
> via ptrace() machinery. The patch makes some small changes in
> existing files, mainly to move some code into NetBSD specific files to
> isolate the changes, and it adds a new file nbsd-thread.c. The
> machinery in that file is loosely based on existing examples in
> dec-thread.c and linux-nat.c.
>
> Tested on NetBSD on i386 and mipsel platforms. I did not make changes
> in target specific code for other platforms because I don't have them;
> the changes should be pretty obvious given what is changed in
> mipsnbsd-nat.c.
>
> I don't have write privs in GDB as far as I know, but all the
> paperwork is in place.
>
> paul
2010-04-22 Paul Koning <paul_koning@dell.com>
* i386bsd-nat.c (i386bsd_supply_gregset, i386bsd_collect_gregset):
Make global.
* i386bsd-nat.h: Ditto.
* i386nbsd-nat.c: Include inferior.h, i387-tdep.h, sys/ptrace.h,
machine/reg.h.
(i386nbsd_fetch_inferior_registers,
i386nbsd_store_inferior_registers): New.
* mipsnbsd-nat.c (mipsnbsd_fetch_inferior_registers,
mipsnbsd_store_inferior_registers): Pass thread ID to ptrace().
* nbsd-thread.c: New file.
* config/i386/nbsdelf.mh: Add nbsd-thread.o.
* config/mips/nbsd.mh: Add nbsd-thread.o.
? gdb/changelog-entry
Index: gdb/i386bsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/i386bsd-nat.c,v
retrieving revision 1.43
diff -u -p -r1.43 i386bsd-nat.c
--- gdb/i386bsd-nat.c 1 Jan 2010 07:31:36 -0000 1.43
+++ gdb/i386bsd-nat.c 22 Apr 2010 15:21:55 -0000
@@ -88,7 +88,7 @@ static int have_ptrace_xmmregs = -1;
/* Supply the general-purpose registers in GREGS, to REGCACHE. */
-static void
+void
i386bsd_supply_gregset (struct regcache *regcache, const void *gregs)
{
const char *regs = gregs;
@@ -107,7 +107,7 @@ i386bsd_supply_gregset (struct regcache
GREGS. If REGNUM is -1, collect and store all appropriate
registers. */
-static void
+void
i386bsd_collect_gregset (const struct regcache *regcache,
void *gregs, int regnum)
{
Index: gdb/i386bsd-nat.h
===================================================================
RCS file: /cvs/src/src/gdb/i386bsd-nat.h,v
retrieving revision 1.8
diff -u -p -r1.8 i386bsd-nat.h
--- gdb/i386bsd-nat.h 1 Jan 2010 07:31:36 -0000 1.8
+++ gdb/i386bsd-nat.h 22 Apr 2010 15:21:55 -0000
@@ -35,4 +35,11 @@ extern void i386bsd_dr_reset_addr (int r
extern unsigned long i386bsd_dr_get_status (void);
+extern void i386bsd_supply_gregset (struct regcache *regcache,
+ const void *gregs);
+
+extern void i386bsd_collect_gregset (const struct regcache *regcache,
+ void *gregs, int regnum);
+
+
#endif /* i386bsd-nat.h */
Index: gdb/i386nbsd-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/i386nbsd-nat.c,v
retrieving revision 1.22
diff -u -p -r1.22 i386nbsd-nat.c
--- gdb/i386nbsd-nat.c 1 Jan 2010 07:31:36 -0000 1.22
+++ gdb/i386nbsd-nat.c 22 Apr 2010 15:21:55 -0000
@@ -19,16 +19,20 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
+#include "inferior.h"
#include "gdbcore.h"
#include "regcache.h"
#include "target.h"
#include "i386-tdep.h"
+#include "i387-tdep.h"
#include "i386bsd-nat.h"
/* Support for debugging kernel virtual memory images. */
#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <machine/reg.h>
#include <machine/frame.h>
#include <machine/pcb.h>
@@ -71,6 +75,131 @@ i386nbsd_supply_pcb (struct regcache *re
return 1;
}
+
+/* Macro to determine if a register is fetched with PT_GETREGS. */
+#define GETREGS_SUPPLIES(regnum) \
+ ((0 <= (regnum) && (regnum) <= 15))
+
+#ifdef HAVE_PT_GETXMMREGS
+/* Set to 1 if the kernel supports PT_GETXMMREGS. Initialized to -1
+ so that we try PT_GETXMMREGS the first time around. */
+static int have_ptrace_xmmregs = -1;
+#endif
+
+
+/* Fetch register REGNUM from the inferior. If REGNUM is -1, do this
+ for all registers (including the floating point registers). */
+
+static void
+i386nbsd_fetch_inferior_registers (struct target_ops *ops,
+ struct regcache *regcache, int regnum)
+{
+ if (regnum == -1 || GETREGS_SUPPLIES (regnum))
+ {
+ struct reg regs;
+
+ if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
+ (PTRACE_TYPE_ARG3) ®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