From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 30299 invoked by alias); 22 Apr 2010 15:36:08 -0000 Received: (qmail 30152 invoked by uid 22791); 22 Apr 2010 15:35:45 -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 aussmtpmrkps320.us.dell.com (HELO aussmtpmrkps320.us.dell.com) (143.166.224.254) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 22 Apr 2010 15:35:21 +0000 X-Loopcount0: from 12.110.134.31 Received: from unknown (HELO M31.equallogic.com) ([12.110.134.31]) by aussmtpmrkps320.us.dell.com with SMTP; 22 Apr 2010 10:35:19 -0500 MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Message-ID: <19408.27827.830391.509465@pkoning-laptop.equallogic.com> Date: Thu, 22 Apr 2010 15:36:00 -0000 From: Paul Koning To: gdb-patches@sourceware.org Subject: Re: [RFC] Thread debug support for NetBSD 5 References: <19405.52446.728141.329821@pkoning-laptop.equallogic.com> X-IsSubscribed: yes 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-04/txt/msg00754.txt.bz2 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 * 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 . */ #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