From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 24946 invoked by alias); 2 May 2010 13:29:11 -0000 Received: (qmail 24927 invoked by uid 22791); 2 May 2010 13:29:07 -0000 X-SWARE-Spam-Status: No, hits=0.9 required=5.0 tests=BAYES_50,TW_BJ,TW_EG,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from sibelius.xs4all.nl (HELO glazunov.sibelius.xs4all.nl) (83.163.83.176) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sun, 02 May 2010 13:29:00 +0000 Received: from glazunov.sibelius.xs4all.nl (kettenis@localhost [127.0.0.1]) by glazunov.sibelius.xs4all.nl (8.14.3/8.14.3) with ESMTP id o42DSnD8023321; Sun, 2 May 2010 15:28:49 +0200 (CEST) Received: (from kettenis@localhost) by glazunov.sibelius.xs4all.nl (8.14.3/8.14.3/Submit) id o42DSlce001475; Sun, 2 May 2010 15:28:47 +0200 (CEST) Date: Sun, 02 May 2010 13:29:00 -0000 Message-Id: <201005021328.o42DSlce001475@glazunov.sibelius.xs4all.nl> From: Mark Kettenis To: Paul_Koning@dell.com CC: gdb-patches@sourceware.org In-reply-to: <19417.40044.762978.858637@pkoning-laptop.equallogic.com> (message from Paul Koning on Thu, 29 Apr 2010 10:49:16 -0400) 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> Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2010-05/txt/msg00012.txt.bz2 > Date: Thu, 29 Apr 2010 10:49:16 -0400 > From: 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 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 > > * 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 . */ > > #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 > +#include > +#include > #include > #include > > @@ -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 . */ > + > +#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 > +#include > +#include > +#include > + > +/* 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 > >