This is the mail archive of the gdb-patches@sourceware.cygnus.com mailing list for the GDB project. See the GDB home page for more information.
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
Hi, I have sent an annoucement for the availability of similar patches to the Internet community, but they come with the 2 ones that I have already sent to this list. This mail contains the same patches as the ones available in our WWW page (http://www.gr.opengroup.org/java/jdk/linux/debug.htm), but they come without these 2 previous ones, and with a ChangeLog file as you require for official patch submission. This is a double work for me, but you have probably already inserted these 2 previous patches in your development tree, so this will be less work for you (but more for me :-). So, be sure that the 2 previous ones are applied before this one (or grap the complete distribution at the given URL). Best regards, -Eric P.S. These patches work only if you apply patches to the glibc that I have already sent to Ulrich Drepper for insertion into the glibc development tree. +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ Eric PAIRE Email : e.paire@gr.opengroup.org | THE Open GROUP - Research Institute Phone : +33 (0) 476 63 48 71 | 2, avenue de Vignate Fax : +33 (0) 476 51 05 32 | F-38610 Gieres FRANCE ------ ChangeLog here ------ ChangeLog here ------ ChangeLog here ------ Fri Jun 19 10:04:08 1998 Eric Paire <e.paire@gr.opengroup.org> Debugging a LinuxThreads-based application: * config/i386/linux.mh: Add new linuxthreads.o to NATDEPFILES macro. * config/i386/nm-linux.h: define new target_new_objfile() and target_pid_to_str() to take into account LinuxThreads; Added new PREPARE_TO_PROCEED macro. * config/i386/tm-i386.h: Changed FRAME_CHAIN macro definition. * config/nm-m3.h: Changed parameter of PREPARE_TO_PROCEED macro. * i386-tdep.c: Added new i386_frame_chain() funtion which supports errors when reading memory for extracting frame chain; updated child_resume() in order to handle step command in multithreaded environment. * inferior.h: Added new signal_stop_update(), signal_print_update() and signal_pass_update() prototypes. * infrun.c: Updated to new behaviour of PREPARE_TO_PROCEED macro, and added new signal_stop_update(), signal_print_update() and signal_pass_update() functions; (resume) target_resume() must be called with inferior_pid and not -1, when single stepping and no breakpoint inserted; (wait_for_inferior): target_wait[hook] must be called with inferior_pid when breakpoints are not inserted; on breakpoints hit by wrong thread which is the current thread currently stepping, don't resume all threads, but continue with the current thread management. * m3-nat.c: (mach3_prepare_to_proceed) Updated to new behaviour of PREPARE_TO_PROCEED macro. * target.h: Added new thread_stratum in order to automatically call the process_stratum target function when it does not exist at thread level. * linuxthreads.c: new file ----- diffs here ----- diffs here ----- diffs here ----- diffs here ----- diff -ru gdb-4.17.2+JMP/gdb/breakpoint.c gdb-4.17/gdb/breakpoint.c --- gdb-4.17.2+JMP/gdb/breakpoint.c Tue May 5 18:41:28 1998 +++ gdb-4.17/gdb/breakpoint.c Tue May 5 18:36:51 1998 @@ -1,6 +1,6 @@ /* Everything about breakpoints, for GDB. - Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 - Free Software Foundation, Inc. + Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, + 1998 Free Software Foundation, Inc. This file is part of GDB. diff -ru gdb-4.17.2+JMP/gdb/config/i386/linux.mh gdb-4.17/gdb/config/i386/linux.mh --- gdb-4.17.2+JMP/gdb/config/i386/linux.mh Wed Apr 22 03:23:13 1998 +++ gdb-4.17/gdb/config/i386/linux.mh Thu Apr 30 18:11:25 1998 @@ -4,4 +4,4 @@ XDEPFILES= ser-tcp.o NAT_FILE= nm-linux.h -NATDEPFILES= infptrace.o solib.o inftarg.o fork-child.o corelow.o core-aout.o core-regset.o i386v-nat.o i386v4-nat.o +NATDEPFILES= infptrace.o solib.o inftarg.o fork-child.o corelow.o core-aout.o core-regset.o i386v-nat.o i386v4-nat.o linuxthreads.o diff -ru gdb-4.17.2+JMP/gdb/config/i386/nm-linux.h gdb-4.17/gdb/config/i386/nm-linux.h --- gdb-4.17.2+JMP/gdb/config/i386/nm-linux.h Wed Apr 22 03:23:16 1998 +++ gdb-4.17/gdb/config/i386/nm-linux.h Thu Apr 30 18:20:06 1998 @@ -74,4 +74,22 @@ extern int i386_remove_watchpoint PARAMS ((int pid, CORE_ADDR addr, int len)); +/* Support for the glibc linuxthreads package. */ + +#ifdef __STDC__ +struct objfile; +#endif + +extern void +linuxthreads_new_objfile PARAMS ((struct objfile *objfile)); +#define target_new_objfile(OBJFILE) linuxthreads_new_objfile (OBJFILE) + +extern char * +linuxthreads_pid_to_str PARAMS ((int pid)); +#define target_pid_to_str(PID) linuxthreads_pid_to_str (PID) + +extern int +linuxthreads_prepare_to_proceed PARAMS ((int step)); +#define PREPARE_TO_PROCEED(STEP) linuxthreads_prepare_to_proceed (STEP) + #endif /* #ifndef NM_LINUX_H */ diff -ru gdb-4.17.2+JMP/gdb/config/i386/tm-i386.h gdb-4.17/gdb/config/i386/tm-i386.h --- gdb-4.17.2+JMP/gdb/config/i386/tm-i386.h Thu Jan 4 08:23:24 1996 +++ gdb-4.17/gdb/config/i386/tm-i386.h Tue Jun 9 17:27:39 1998 @@ -198,12 +198,9 @@ In the case of the i386, the frame's nominal address is the address of a 4-byte word containing the calling frame's address. */ -#define FRAME_CHAIN(thisframe) \ - ((thisframe)->signal_handler_caller \ - ? (thisframe)->frame \ - : (!inside_entry_file ((thisframe)->pc) \ - ? read_memory_integer ((thisframe)->frame, 4) \ - : 0)) +extern CORE_ADDR i386_frame_chain PARAMS ((struct frame_info *)); + +#define FRAME_CHAIN(FRAME) (i386_frame_chain (FRAME)) /* A macro that tells us whether the function invocation represented by FI does not have a frame on the stack associated with it. If it diff -ru gdb-4.17.2+JMP/gdb/config/nm-m3.h gdb-4.17/gdb/config/nm-m3.h --- gdb-4.17.2+JMP/gdb/config/nm-m3.h Fri Apr 12 08:15:16 1996 +++ gdb-4.17/gdb/config/nm-m3.h Thu Apr 30 18:21:07 1998 @@ -1,6 +1,6 @@ /* Mach 3.0 common definitions and global vars. - Copyright (C) 1992 Free Software Foundation, Inc. + Copyright (C) 1992, 1998 Free Software Foundation, Inc. This file is part of GDB. @@ -39,7 +39,7 @@ */ extern int must_suspend_thread; -#define PREPARE_TO_PROCEED(select_it) mach3_prepare_to_proceed(select_it) +#define PREPARE_TO_PROCEED(step) mach3_prepare_to_proceed(step) /* Try to get the privileged host port for authentication to machid * diff -ru gdb-4.17.2+JMP/gdb/i386-tdep.c gdb-4.17/gdb/i386-tdep.c --- gdb-4.17.2+JMP/gdb/i386-tdep.c Tue May 5 18:41:28 1998 +++ gdb-4.17/gdb/i386-tdep.c Tue Jun 9 17:31:04 1998 @@ -713,6 +713,26 @@ return 0; /* not a trampoline */ } +/* i386_frame_chain() takes a frame's nominal address and produces the frame's + chain-pointer. In the case of the i386, the frame's nominal address is + the address of a 4-byte word containing the calling frame's address. */ + +CORE_ADDR +i386_frame_chain (frame) + struct frame_info *frame; +{ + char buf[4]; + + if (frame->signal_handler_caller) + return frame->frame; + + if (!inside_entry_file (frame->pc) && + target_read_memory (frame->frame, buf, 4) == 0) + return extract_address (buf, 4); + + return 0; +} + /* Under Linux, signal handler invocations can be identified by the designated code sequence that is used to return from a signal handler. In particular, the return address of a signal handler @@ -781,6 +801,9 @@ #ifdef CHILD_RESUME #include <sys/ptrace.h> +#ifndef PT_SYSCALL +#define PT_SYSCALL PTRACE_SYSCALL +#endif void child_resume(pid, step, signal) @@ -805,7 +828,6 @@ request = PT_CONTINUE; else { - request = PT_STEP; pc = read_pc_pid (pid); for (i = 0; i < SYSCALL_TRAP_SIZE; i++) if (read_memory_nobpt(pc + i, (char *) &code, 1) != 0 @@ -813,7 +835,15 @@ & 0xFF)) break; - if (i >= SYSCALL_TRAP_SIZE && IN_SIGTRAMP (pc, (char *)NULL)) + if (i < SYSCALL_TRAP_SIZE) + request = PT_STEP; + else if (!IN_SIGTRAMP (pc, (char *)NULL)) + { + /* Single-step over the syscall in order to avoid being blocked + inside the kernel waiting for the thread to be unblocked. */ + request = PT_SYSCALL; + } + else { /* Put TF in the eflags from the frame set up by the signal handler */ unsigned long eflags; @@ -823,6 +853,7 @@ eflags |= 0x100; /* Trap Flag */ write_memory (addr, (char *) &eflags, 4); } + request = PT_STEP; } } call_ptrace (request, pid, (PTRACE_ARG3_TYPE) 0, @@ -832,7 +863,6 @@ perror_with_name ("ptrace"); } #endif - void _initialize_i386_tdep () diff -ru gdb-4.17.2+JMP/gdb/inferior.h gdb-4.17/gdb/inferior.h --- gdb-4.17.2+JMP/gdb/inferior.h Sat Apr 11 07:39:38 1998 +++ gdb-4.17/gdb/inferior.h Thu Apr 30 18:28:52 1998 @@ -213,6 +213,12 @@ extern int signal_pass_state PARAMS ((int)); +extern int signal_stop_update PARAMS ((int, int)); + +extern int signal_print_update PARAMS ((int, int)); + +extern int signal_pass_update PARAMS ((int, int)); + /* From infcmd.c */ extern void tty_command PARAMS ((char *, int)); diff -ru gdb-4.17.2+JMP/gdb/infrun.c gdb-4.17/gdb/infrun.c --- gdb-4.17.2+JMP/gdb/infrun.c Thu Apr 30 17:56:00 1998 +++ gdb-4.17/gdb/infrun.c Thu Apr 30 18:43:37 1998 @@ -218,6 +218,14 @@ static int stop_print_frame; +#ifdef PREPARE_TO_PROCEED +/* When a pid must be single-stepped for going over a breakpoint at + proceed (), it should be implicitely stepped by target_resume() in + resume (), implicitely waited for in target_wait() and switched to + in wait_for_inferior(). */ + +static int proceeded_pid; +#endif /* PREPARE_TO_PROCEED */ /* Things to clean up if we QUIT out of resume (). */ /* ARGSUSED */ @@ -267,7 +275,7 @@ /* Install inferior's terminal modes. */ target_terminal_inferior (); - target_resume (-1, step, sig); + target_resume (step && !breakpoints_inserted ? inferior_pid : -1, step, sig); discard_cleanups (old_cleanups); } @@ -344,15 +352,9 @@ In this case the thread that stopped at a breakpoint will immediately cause another stop, if it is not stepped over first. On the other hand, if (ADDR != -1) we only want to single step over the breakpoint if we did - switch to another thread. - - If we are single stepping, don't do any of the above. - (Note that in the current implementation single stepping another - thread after a breakpoint and then continuing will cause the original - breakpoint to be hit again, but you can always continue, so it's not - a big deal.) */ + switch to another thread. */ - if (! step && PREPARE_TO_PROCEED (1) && breakpoint_here_p (read_pc ())) + if (!oneproc && (proceeded_pid = PREPARE_TO_PROCEED (step))) oneproc = 1; #endif /* PREPARE_TO_PROCEED */ @@ -448,6 +450,10 @@ /* Don't confuse first call to proceed(). */ stop_signal = TARGET_SIGNAL_0; + +#ifdef PREPARE_TO_PROCEED + proceeded_pid = 0; +#endif } static void @@ -519,9 +525,47 @@ registers_changed (); if (target_wait_hook) - pid = target_wait_hook (-1, &w); + pid = target_wait_hook (!breakpoints_inserted ? inferior_pid : -1, &w); else - pid = target_wait (-1, &w); + pid = target_wait (!breakpoints_inserted ? inferior_pid : -1, &w); + +#ifdef PREPARE_TO_PROCEED + /* Switch to the thread selected by the last PREPARE_TO_PROCEED (). + As a side effect, the trap_expected value should be switched. */ + + if (proceeded_pid) + { + if (proceeded_pid != inferior_pid) + { + trap_expected = 0; + + /* Save infrun state for the old thread. */ + save_infrun_state (inferior_pid, prev_pc, + prev_func_start, prev_func_name, + trap_expected, step_resume_breakpoint, + through_sigtramp_breakpoint, + step_range_start, step_range_end, + step_frame_address, handling_longjmp, + another_trap); + + inferior_pid = proceeded_pid; + + /* Load infrun state for the new thread. */ + load_infrun_state (inferior_pid, &prev_pc, + &prev_func_start, &prev_func_name, + &trap_expected, &step_resume_breakpoint, + &through_sigtramp_breakpoint, + &step_range_start, &step_range_end, + &step_frame_address, &handling_longjmp, + &another_trap); + printf_filtered ("[Switching to %s]\n", + target_pid_to_str (inferior_pid)); + + trap_expected = 1; + } + proceeded_pid = 0; + } +#endif /* PREPARE_TO_PROCEED */ /* Gross. @@ -677,8 +721,8 @@ remove_breakpoints (); target_resume (pid, 1, TARGET_SIGNAL_0); /* Single step */ - /* FIXME: What if a signal arrives instead of the single-step - happening? */ + /* FIXME: What if a signal arrives instead of the + single-step happening? */ if (target_wait_hook) target_wait_hook (pid, &w); @@ -686,6 +730,9 @@ target_wait (pid, &w); insert_breakpoints (); + if (inferior_pid == pid && CURRENTLY_STEPPING()) + goto have_waited; + /* We need to restart all the threads now. */ target_resume (-1, 0, TARGET_SIGNAL_0); continue; @@ -785,7 +832,6 @@ else target_wait (pid, &tmpstatus); - goto have_waited; } @@ -1862,6 +1908,33 @@ int signo; { return signal_program[signo]; +} + +int signal_stop_update (signo, state) + int signo; + int state; +{ + int ret = signal_stop[signo]; + signal_stop[signo] = state; + return ret; +} + +int signal_print_update (signo, state) + int signo; + int state; +{ + int ret = signal_print[signo]; + signal_print[signo] = state; + return ret; +} + +int signal_pass_update (signo, state) + int signo; + int state; +{ + int ret = signal_program[signo]; + signal_program[signo] = state; + return ret; } static void diff -ru gdb-4.17.2+JMP/gdb/m3-nat.c gdb-4.17/gdb/m3-nat.c --- gdb-4.17.2+JMP/gdb/m3-nat.c Mon May 6 23:27:04 1996 +++ gdb-4.17/gdb/m3-nat.c Thu Apr 30 18:23:57 1998 @@ -1,7 +1,7 @@ /* Interface GDB to Mach 3.0 operating systems. (Most) Mach 3.0 related routines live in this file. - Copyright (C) 1992, 1996 Free Software Foundation, Inc. + Copyright (C) 1992, 1996, 1998 Free Software Foundation, Inc. This file is part of GDB. @@ -1576,26 +1576,23 @@ * * If we have switched threads and stopped at breakpoint return 1 otherwise 0. * - * if SELECT_IT is nonzero, reselect the thread that was active when - * we stopped at a breakpoint. - * + * If we are single stepping, don't do anything since in the current + * implementation single stepping another thread after a breakpoint and + * then continuing will cause the original breakpoint to be hit again, + * but you can always continue, so it's not a big deal. */ -mach3_prepare_to_proceed (select_it) - int select_it; +mach3_prepare_to_proceed (step) + int step; { - if (stop_thread && + if (!step && + stop_thread && stop_thread != current_thread && stop_exception == EXC_BREAKPOINT) { - int mid; - - if (! select_it) - return 1; - - mid = switch_to_thread (stop_thread); - - return 1; + switch_to_thread (stop_thread); + if (breakpoint_here_p (read_pc ())) + return inferior_pid; } return 0; diff -ru gdb-4.17.2+JMP/gdb/target.h gdb-4.17/gdb/target.h --- gdb-4.17.2+JMP/gdb/target.h Thu May 8 03:00:40 1997 +++ gdb-4.17/gdb/target.h Thu Apr 30 18:31:41 1998 @@ -1,5 +1,5 @@ /* Interface between GDB and target environments, including files and processes - Copyright 1990, 1991, 1992, 1993, 1994 Free Software Foundation, Inc. + Copyright 1990, 1991, 1992, 1993, 1994, 1998 Free Software Foundation, Inc. Contributed by Cygnus Support. Written by John Gilmore. This file is part of GDB. @@ -47,7 +47,8 @@ file_stratum, /* Executable files, etc */ core_stratum, /* Core dump files */ download_stratum, /* Downloading of remote targets */ - process_stratum /* Executing processes */ + process_stratum, /* Executing processes */ + thread_stratum /* Executing threads */ }; /* Stuff for target_wait. */ diff -ru /dev/null gdb-4.17/gdb/linuxthreads.c --- /dev/null Tue Jan 1 05:00:00 1980 +++ gdb-4.17/gdb/linuxthreads.c Thu Apr 30 18:31:03 1998 @@ -0,0 +1,1359 @@ +/* Low level interface for debugging GNU/Linux threads for GDB, + the GNU debugger. + Copyright 1998 Free Software Foundation, 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 2 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, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* This module implements the debugging interface of the linuxthreads package + of the glibc. This package implements a simple clone()-based implementation + of Posix threads for Linux. To use this module, be sure that you have at + least the version of the linuxthreads package that holds the support of + GDB (currently 0.8 included in the glibc-2.0.7). + + Right now, the linuxthreads package does not care of priority scheduling, + so, neither this module does; In particular, the threads are resumed + in any order, which could lead to different scheduling than the one + happening when GDB does not control the execution. + + The latest point is that ptrace(PT_ATTACH, ...) is intrusive in Linux: + When a process is attached, then the attaching process becomes the current + parent of the attached process, and the old parent has lost this child. + If the old parent does a wait[...](), then this child is no longer + considered by the kernel as a child of the old parent, thus leading to + results of the call different when the child is attached and when it's not. + + A fix has been submitted to the Linux community to solve this problem, + which consequences are not visible to the application itself, but on the + process which may wait() for the completion of the application (mostly, + it may consider that the application no longer exists (errno == ECHILD), + although it does, and thus being unable to get the exit status and resource + usage of the child. If by chance, it is able to wait() for the application + after it has died (by receiving first a SIGCHILD, and then doing a wait(), + then the exit status and resource usage may be wrong, because the + linuxthreads package heavily relies on wait() synchronization to keep + them correct. */ + +#include <sys/types.h> /* for pid_t */ +#include <sys/ptrace.h> /* for PT_* flags */ +#include <sys/wait.h> /* for WUNTRACED and __WCLONE flags */ +#include <signal.h> /* for struct sigaction and NSIG */ + +#include "defs.h" +#include "target.h" +#include "inferior.h" +#include "gdbcore.h" +#include "gdbthread.h" +#include "wait.h" + +#include "breakpoint.h" + +extern int child_suppress_run; /* make inftarg.c non-runnable */ +struct target_ops linuxthreads_ops; /* Forward declaration */ +extern struct target_ops child_ops; /* target vector for inftarg.c */ + +static CORE_ADDR linuxthreads_handles; /* array of linuxthreads handles */ +static CORE_ADDR linuxthreads_manager; /* pid of linuxthreads manager thread */ +static CORE_ADDR linuxthreads_initial; /* pid of linuxthreads initial thread */ +static CORE_ADDR linuxthreads_debug; /* linuxthreads internal debug flag */ +static CORE_ADDR linuxthreads_num; /* number of valid handle entries */ + +static int linuxthreads_max; /* maximum number of linuxthreads */ + +static int linuxthreads_sizeof_handle; /* size of a linuxthreads handle */ +static int linuxthreads_offset_descr; /* h_descr offset of the linuxthreads + handle */ +static int linuxthreads_offset_pid; /* p_pid offset of the linuxthreads + descr */ + +static int linuxthreads_manager_pid; /* manager pid */ +static int linuxthreads_initial_pid; /* initial pid */ + +static int *linuxthreads_wait_pid; /* wait array of pid */ +static int *linuxthreads_wait_status; /* wait array of status */ +static int linuxthreads_wait_last; /* last status to be reported */ +static sigset_t linuxthreads_wait_mask; /* sigset with SIGCHLD */ + +static int linuxthreads_step_pid; /* current stepped pid */ +static int linuxthreads_step_signo; /* current stepped target signal */ +static int linuxthreads_exit_status; /* exit status of initial thread */ + +static int linuxthreads_inferior_pid; /* temporary internal inferior pid */ +static int linuxthreads_breakpoint_pid; /* last pid that hit a breakpoint */ +static int linuxthreads_attach_pending; /* attach command without wait */ + +static int linuxthreads_breakpoints_inserted; /* any breakpoints inserted */ + +static int linuxthreads_sig_restart; /* SIG_RESTART target value */ +static int linuxthreads_sig_restart_stop; /* SIG_RESTART stop */ +static int linuxthreads_sig_restart_print; /* SIG_RESTART print */ + +static int linuxthreads_sig_cancel; /* SIG_CANCEL target value */ +static int linuxthreads_sig_cancel_stop; /* SIG_CANCEL stop */ +static int linuxthreads_sig_cancel_print; /* SIG_CANCEL print */ + +static struct linuxthreads_breakpoint { + CORE_ADDR pc; /* PC of breakpoint */ + int pid; /* pid of breakpoint */ + int step; /* whether the pc has been reached after sstep */ +} *linuxthreads_breakpoint_zombie; /* Zombie breakpoints array */ +static int linuxthreads_breakpoint_last; /* Last zombie breakpoint */ +static CORE_ADDR linuxthreads_breakpoint_addr; /* Zombie breapoint address */ + +#define REMOVE_BREAKPOINT_ZOMBIE(_i) \ +{ \ + if ((_i) < linuxthreads_breakpoint_last) \ + linuxthreads_breakpoint_zombie[(_i)] = \ + linuxthreads_breakpoint_zombie[linuxthreads_breakpoint_last]; \ + linuxthreads_breakpoint_last--; \ +} + +/* This should be part of the linuxthreads package */ +#define LINUXTHREAD_SIG_CANCEL 12 /* SIGUSR2 */ +#define LINUXTHREAD_SIG_EXIT 10 /* SIGUSR1 */ +#define LINUXTHREAD_NSIG _NSIG + + +#ifndef PTRACE_XFER_TYPE +#define PTRACE_XFER_TYPE int +#endif +/* Check to see if the given thread is alive. */ +static int +linuxthreads_thread_alive (pid) + int pid; +{ + errno = 0; + return ptrace (PT_READ_U, pid, (PTRACE_ARG3_TYPE)0, 0) >= 0 || errno == 0; +} + +/* On detach(), find a SIGTRAP status and optionally a SIGSTOP one. */ +static int +linuxthreads_find_trap (pid, stop) + int pid; + int stop; +{ + int i; + int rpid; + int status; + int found_stop = 0; + int found_trap = 0; + int last = 0; + int *wstatus = alloca (LINUXTHREAD_NSIG * sizeof (int)); + + /* Look at the pending status */ + for (i = linuxthreads_wait_last; i >= 0; i--) + if (linuxthreads_wait_pid[i] == pid) + { + status = linuxthreads_wait_status[i]; + if (i < linuxthreads_wait_last) + { + linuxthreads_wait_status[i] = + linuxthreads_wait_status[linuxthreads_wait_last]; + linuxthreads_wait_pid[i] = + linuxthreads_wait_pid[linuxthreads_wait_last]; + } + linuxthreads_wait_last--; + + if (!WIFSTOPPED(status)) /* Thread has died */ + return 0; + + if (WSTOPSIG(status) == SIGTRAP) + if (stop) + found_trap = 1; + else + return 1; + else if (WSTOPSIG(status) != SIGSTOP) + { + wstatus[0] = status; + last = 1; + } + else if (stop) + found_stop = 1; + + break; + } + + if (stop) + { + if (!found_trap) + kill (pid, SIGTRAP); + if (!found_stop) + kill (pid, SIGSTOP); + } + + /* Catch all status until SIGTRAP and optionally SIGSTOP show up. */ + for (;;) + { + child_resume (pid, 1, TARGET_SIGNAL_0); + + for (;;) + { + rpid = waitpid (pid, &status, __WCLONE); + if (rpid > 0) + break; + if (errno == EINTR) + continue; + + /* manager has died or pid is initial thread. */ + rpid = waitpid (pid, &status, 0); + if (rpid > 0) + break; + if (errno != EINTR) + perror_with_name ("waitpid"); + } + + if (!WIFSTOPPED(status)) /* Thread has died */ + return 0; + + if (WSTOPSIG(status) == SIGTRAP) + if (!stop || found_stop) + break; + else + found_trap = 1; + else if (WSTOPSIG(status) != SIGSTOP) + wstatus[last++] = status; + else if (stop) + if (found_trap) + break; + else + found_stop = 1; + } + + /* Resend all signals to the thread */ + while (--last >= 0) + kill (pid, WSTOPSIG(wstatus[last])); + + return 1; +} + +static void +restore_inferior_pid (pid) + int pid; +{ + inferior_pid = pid; +} + +static struct cleanup * +save_inferior_pid () +{ + return make_cleanup (restore_inferior_pid, inferior_pid); +} + +/* SIGCHLD handler */ +static void +sigchld_handler(signo) + int signo; +{ + /* This handler is used to get an EINTR while doing waitpid() + when an event is received */ +} + +/* Does the process currently have a pending status ? */ +static int +linuxthreads_pending_status (pid) + int pid; +{ + int i; + for (i = linuxthreads_wait_last; i >= 0; i--) + if (linuxthreads_wait_pid[i] == pid) + return 1; + return 0; +} + +/* Walk through the linuxthreads handles in order to execute a function */ +static void +iterate_active_threads (func, all) + void (*func)(int); + int all; +{ + CORE_ADDR descr; + int pid; + int i; + int num; + + read_memory (linuxthreads_num, (char *)&num, sizeof (int)); + + for (i = 0; i < linuxthreads_max && num > 0; i++) + { + read_memory (linuxthreads_handles + + linuxthreads_sizeof_handle * i + linuxthreads_offset_descr, + (char *)&descr, sizeof (void *)); + if (descr) + { + num--; + read_memory (descr + linuxthreads_offset_pid, + (char *)&pid, sizeof (pid_t)); + if (pid > 0 && (all || (!linuxthreads_pending_status (pid)))) + (*func)(pid); + } + } + +} + +/* Insert a thread breakpoint */ +static void +insert_breakpoint (pid) + int pid; +{ + int j; + + /* Remove (if any) the positive zombie breakpoint. */ + for (j = linuxthreads_breakpoint_last; j >= 0; j--) + if (linuxthreads_breakpoint_zombie[j].pid == pid) + { + if ((linuxthreads_breakpoint_zombie[j].pc - DECR_PC_AFTER_BREAK + == linuxthreads_breakpoint_addr) + && !linuxthreads_breakpoint_zombie[j].step) + REMOVE_BREAKPOINT_ZOMBIE(j); + break; + } +} + +/* Remove a thread breakpoint */ +static void +remove_breakpoint (pid) + int pid; +{ + int j; + + /* Insert a positive zombie breakpoint (if needed). */ + for (j = 0; j <= linuxthreads_breakpoint_last; j++) + if (linuxthreads_breakpoint_zombie[j].pid == pid) + break; + + if (in_thread_list (pid) && linuxthreads_thread_alive (pid)) + { + CORE_ADDR pc = read_pc_pid (pid); + if (linuxthreads_breakpoint_addr == pc - DECR_PC_AFTER_BREAK + && j > linuxthreads_breakpoint_last) + { + linuxthreads_breakpoint_zombie[j].pid = pid; + linuxthreads_breakpoint_zombie[j].pc = pc; + linuxthreads_breakpoint_zombie[j].step = 0; + linuxthreads_breakpoint_last++; + } + } +} + +/* Kill a thread */ +static void +kill_thread (pid) + int pid; +{ + if (in_thread_list (pid)) + ptrace (PT_KILL, pid, (PTRACE_ARG3_TYPE) 0, 0); + else + kill (pid, SIGKILL); +} + +/* Resume a thread */ +static void +resume_thread (pid) + int pid; +{ + if (pid != inferior_pid + && in_thread_list (pid) + && linuxthreads_thread_alive (pid)) + if (pid == linuxthreads_step_pid) + child_resume (pid, 1, linuxthreads_step_signo); + else + child_resume (pid, 0, TARGET_SIGNAL_0); +} + +/* Detach a thread */ +static void +detach_thread (pid) + int pid; +{ + if (in_thread_list (pid) && linuxthreads_thread_alive (pid)) + { + /* Remove pending SIGTRAP and SIGSTOP */ + linuxthreads_find_trap (pid, 1); + + inferior_pid = pid; + detach (TARGET_SIGNAL_0); + inferior_pid = linuxthreads_manager_pid; + } +} + +/* Stop a thread */ +static void +stop_thread (pid) + int pid; +{ + if (pid != inferior_pid) + if (in_thread_list (pid)) + kill (pid, SIGSTOP); + else if (ptrace (PT_ATTACH, pid, (PTRACE_ARG3_TYPE) 0, 0) == 0) + { + if (!linuxthreads_attach_pending) + printf_unfiltered ("[New %s]\n", target_pid_to_str (pid)); + add_thread (pid); + } +} + +/* Wait for a thread */ +static void +wait_thread (pid) + int pid; +{ + int status; + int rpid; + + if (pid != inferior_pid && in_thread_list (pid)) + { + for (;;) + { + /* Get first pid status. */ + rpid = waitpid(pid, &status, __WCLONE); + if (rpid > 0) + break; + if (errno == EINTR) + continue; + + /* manager has died or pid is initial thread. */ + rpid = waitpid(pid, &status, 0); + if (rpid > 0) + break; + if (errno != EINTR && linuxthreads_thread_alive (pid)) + perror_with_name ("waitpid"); + + /* the thread is dead. */ + return; + } + if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP) + { + linuxthreads_wait_pid[++linuxthreads_wait_last] = pid; + linuxthreads_wait_status[linuxthreads_wait_last] = status; + } + } +} + +/* Walk through the linuxthreads handles in order to detect all + threads and stop them */ +static void +update_stop_threads (test_pid) + int test_pid; +{ + struct cleanup *old_chain = NULL; + + if (linuxthreads_manager_pid == 0) + { + if (linuxthreads_manager) + { + if (test_pid > 0 && test_pid != inferior_pid) + { + old_chain = save_inferior_pid (); + inferior_pid = test_pid; + } + read_memory (linuxthreads_manager, + (char *)&linuxthreads_manager_pid, sizeof (pid_t)); + } + if (linuxthreads_initial) + { + if (test_pid > 0 && test_pid != inferior_pid) + { + old_chain = save_inferior_pid (); + inferior_pid = test_pid; + } + read_memory(linuxthreads_initial, + (char *)&linuxthreads_initial_pid, sizeof (pid_t)); + } + } + + if (linuxthreads_manager_pid != 0) + { + if (old_chain == NULL && test_pid > 0 && + test_pid != inferior_pid && linuxthreads_thread_alive (test_pid)) + { + old_chain = save_inferior_pid (); + inferior_pid = test_pid; + } + + if (linuxthreads_thread_alive (inferior_pid)) + { + if (test_pid > 0) + { + if (test_pid != linuxthreads_manager_pid + && !linuxthreads_pending_status (linuxthreads_manager_pid)) + { + stop_thread (linuxthreads_manager_pid); + wait_thread (linuxthreads_manager_pid); + } + if (!in_thread_list (test_pid)) + { + if (!linuxthreads_attach_pending) + printf_unfiltered ("[New %s]\n", + target_pid_to_str (test_pid)); + add_thread (test_pid); + } + } + iterate_active_threads (stop_thread, 0); + iterate_active_threads (wait_thread, 0); + } + } + + if (old_chain != NULL) + do_cleanups (old_chain); +} + +/* Internal linuxthreads signal management */ + +static void +linuxthreads_signal_update (on) + int on; +{ + int sig_restart = target_signal_from_host(linuxthreads_sig_restart); + int sig_cancel = target_signal_from_host(linuxthreads_sig_cancel); + + if (on) + { + linuxthreads_sig_restart_stop = signal_stop_update(sig_restart, 0); + linuxthreads_sig_restart_print = signal_print_update(sig_restart, 0); + if (linuxthreads_sig_restart_stop != 1 || + linuxthreads_sig_restart_print != 1) + fprintf_unfiltered (gdb_stderr, + "Linux thread target has modified %s handling\n", + target_signal_to_string(sig_restart)); + + linuxthreads_sig_cancel_stop = signal_stop_update(sig_cancel, 0); + linuxthreads_sig_cancel_print = signal_print_update(sig_cancel, 0); + if (linuxthreads_sig_cancel_stop != 1 || + linuxthreads_sig_cancel_print != 1) + fprintf_unfiltered (gdb_stderr, + "Linux thread target has modified %s handling\n", + target_signal_to_string(sig_cancel)); + } + else + { + signal_stop_update(sig_restart, linuxthreads_sig_restart_stop); + signal_print_update(sig_restart, linuxthreads_sig_restart_print); + if (linuxthreads_sig_restart_stop != 1 || + linuxthreads_sig_restart_print != 1) + fprintf_unfiltered (gdb_stderr, + "Linux thread target has restored %s handling\n", + target_signal_to_string(sig_restart)); + + signal_stop_update(sig_cancel, linuxthreads_sig_cancel_stop); + signal_print_update(sig_cancel, linuxthreads_sig_cancel_print); + if (linuxthreads_sig_cancel_stop != 1 || + linuxthreads_sig_cancel_print != 1) + fprintf_unfiltered (gdb_stderr, + "Linux thread target has restored %s handling\n", + target_signal_to_string(sig_cancel)); + } +} + +/* This routine is called whenever a new symbol table is read in, or when all + symbol tables are removed. libpthread can only be initialized when it + finds the right variables in libpthread.so. Since it's a shared library, + those variables don't show up until the library gets mapped and the symbol + table is read in. */ + +void +linuxthreads_new_objfile (objfile) + struct objfile *objfile; +{ + struct minimal_symbol *ms; + struct sigaction sact; + + if (!objfile || linuxthreads_max) + return; + + if ((ms = lookup_minimal_symbol ("__pthread_threads_debug", + NULL, objfile)) == NULL) + { + /* The debugging-aware libpthreads is not present in this objfile */ + return; + } + linuxthreads_debug = SYMBOL_VALUE_ADDRESS (ms); + + /* Read internal structures configuration */ + if ((ms = lookup_minimal_symbol ("__pthread_sizeof_handle", + NULL, objfile)) == NULL + || target_read_memory (SYMBOL_VALUE_ADDRESS (ms), + (char *)&linuxthreads_sizeof_handle, + sizeof (linuxthreads_sizeof_handle)) != 0) + { + fprintf_unfiltered (gdb_stderr, + "Unable to find linuxthreads symbol \"%s\"\n", + "__pthread_sizeof_handle"); + return; + } + + if ((ms = lookup_minimal_symbol ("__pthread_offsetof_descr", + NULL, objfile)) == NULL + || target_read_memory (SYMBOL_VALUE_ADDRESS (ms), + (char *)&linuxthreads_offset_descr, + sizeof (linuxthreads_offset_descr)) != 0) + { + fprintf_unfiltered (gdb_stderr, + "Unable to find linuxthreads symbol \"%s\"\n", + "__pthread_offsetof_descr"); + return; + } + + if ((ms = lookup_minimal_symbol ("__pthread_offsetof_pid", + NULL, objfile)) == NULL + || target_read_memory (SYMBOL_VALUE_ADDRESS (ms), + (char *)&linuxthreads_offset_pid, + sizeof (linuxthreads_offset_pid)) != 0) + { + fprintf_unfiltered (gdb_stderr, + "Unable to find linuxthreads symbol \"%s\"\n", + "__pthread_offsetof_pid"); + return; + } + + if ((ms = lookup_minimal_symbol ("__pthread_sig_restart", + NULL, objfile)) == NULL + || target_read_memory (SYMBOL_VALUE_ADDRESS (ms), + (char *)&linuxthreads_sig_restart, + sizeof (linuxthreads_sig_restart)) != 0) + { + fprintf_unfiltered (gdb_stderr, + "Unable to find linuxthreads symbol \"%s\"\n", + "__pthread_sig_restart"); + return; + } + + if ((ms = lookup_minimal_symbol ("__pthread_sig_cancel", + NULL, objfile)) == NULL + || target_read_memory (SYMBOL_VALUE_ADDRESS (ms), + (char *)&linuxthreads_sig_cancel, + sizeof (linuxthreads_sig_cancel)) != 0) + { + fprintf_unfiltered (gdb_stderr, + "Unable to find linuxthreads symbol \"%s\"\n", + "__pthread_sig_cancel"); + return; + } + + if ((ms = lookup_minimal_symbol ("__pthread_threads_max", + NULL, objfile)) == NULL + || target_read_memory (SYMBOL_VALUE_ADDRESS (ms), + (char *)&linuxthreads_max, + sizeof (linuxthreads_max)) != 0) + { + fprintf_unfiltered (gdb_stderr, + "Unable to find linuxthreads symbol \"%s\"\n", + "__pthread_threads_max"); + return; + } + + /* Read adresses of internal structures to access */ + if ((ms = lookup_minimal_symbol ("__pthread_handles", + NULL, objfile)) == NULL) + { + fprintf_unfiltered (gdb_stderr, + "Unable to find linuxthreads symbol \"%s\"\n", + "__pthread_handles"); + return; + } + linuxthreads_handles = SYMBOL_VALUE_ADDRESS (ms); + + if ((ms = lookup_minimal_symbol ("__pthread_handles_num", + NULL, objfile)) == NULL) + { + fprintf_unfiltered (gdb_stderr, + "Unable to find linuxthreads symbol \"%s\"\n", + "__pthread_handles_num"); + return; + } + linuxthreads_num = SYMBOL_VALUE_ADDRESS (ms); + + if ((ms = lookup_minimal_symbol ("__pthread_manager_thread", + NULL, objfile)) == NULL) + { + fprintf_unfiltered (gdb_stderr, + "Unable to find linuxthreads symbol \"%s\"\n", + "__pthread_manager_thread"); + return; + } + linuxthreads_manager = SYMBOL_VALUE_ADDRESS (ms) + linuxthreads_offset_pid; + + if ((ms = lookup_minimal_symbol ("__pthread_initial_thread", + NULL, objfile)) == NULL) + { + fprintf_unfiltered (gdb_stderr, + "Unable to find linuxthreads symbol \"%s\"\n", + "__pthread_initial_thread"); + return; + } + linuxthreads_initial = SYMBOL_VALUE_ADDRESS (ms) + linuxthreads_offset_pid; + + /* Allocate gdb internal structures */ + linuxthreads_wait_pid = + (int *)xmalloc (sizeof (int) * (linuxthreads_max + 1)); + linuxthreads_wait_status = + (int *)xmalloc (sizeof (int) * (linuxthreads_max + 1)); + linuxthreads_breakpoint_zombie = (struct linuxthreads_breakpoint *) + xmalloc (sizeof (struct linuxthreads_breakpoint) * (linuxthreads_max + 1)); + + /* handle linuxthread exit */ + sact.sa_handler = sigchld_handler; + sigemptyset(&sact.sa_mask); + sact.sa_flags = 0; + sigaction(linuxthreads_sig_restart, &sact, NULL); + + if (inferior_pid && !linuxthreads_attach_pending) + { + int on = 1; + target_write_memory (linuxthreads_debug, (char *)&on, sizeof (on)); + linuxthreads_attach_pending = 1; + linuxthreads_signal_update (1); + update_stop_threads (inferior_pid); + linuxthreads_attach_pending = 0; + } +} + +/* If we have switched threads from a one that stopped at breakpoint, + return 1 otherwise 0. */ + +int +linuxthreads_prepare_to_proceed (step) + int step; +{ + if (!linuxthreads_max + || !linuxthreads_manager_pid + || !linuxthreads_breakpoint_pid + || !breakpoint_here_p (read_pc_pid (linuxthreads_breakpoint_pid))) + return 0; + + if (step) + { + /* Mark the current inferior as single stepping process. */ + linuxthreads_step_pid = inferior_pid; + } + + linuxthreads_inferior_pid = linuxthreads_breakpoint_pid; + return linuxthreads_breakpoint_pid; +} + +/* Convert a pid to printable form. */ + +char * +linuxthreads_pid_to_str (pid) + int pid; +{ + static char buf[100]; + + sprintf (buf, "%s %d", linuxthreads_max ? "Thread" : "Pid", pid); + + return buf; +} + +/* Attach to process PID, then initialize for debugging it + and wait for the trace-trap that results from attaching. */ + +static void +linuxthreads_attach (args, from_tty) + char *args; + int from_tty; +{ + push_target (&linuxthreads_ops); + linuxthreads_breakpoints_inserted = 1; + linuxthreads_breakpoint_last = -1; + linuxthreads_wait_last = -1; + linuxthreads_exit_status = __W_STOPCODE(0); + + child_ops.to_attach (args, from_tty); + + if (linuxthreads_max) + linuxthreads_attach_pending = 1; +} + +/* Take a program previously attached to and detaches it. + The program resumes execution and will no longer stop + on signals, etc. We'd better not have left any breakpoints + in the program or it'll die when it hits one. For this + to work, it may be necessary for the process to have been + previously attached. It *might* work if the program was + started via the normal ptrace (PTRACE_TRACEME). */ + +static void +linuxthreads_detach (args, from_tty) + char *args; + int from_tty; +{ + if (linuxthreads_max) + { + int i; + int pid; + int off = 0; + target_write_memory (linuxthreads_debug, (char *)&off, sizeof (off)); + + /* Walk through linuxthreads array in order to detach known threads. */ + if (linuxthreads_manager_pid != 0) + { + /* Get rid of all positive zombie breakpoints. */ + for (i = 0; i <= linuxthreads_breakpoint_last; i++) + { + if (linuxthreads_breakpoint_zombie[i].step) + continue; + + pid = linuxthreads_breakpoint_zombie[i].pid; + if (!linuxthreads_thread_alive (pid)) + continue; + + if (linuxthreads_breakpoint_zombie[i].pc != read_pc_pid (pid)) + continue; + + /* Continue in STEP mode until the thread pc has moved or + until SIGTRAP is found on the same PC. */ + if (linuxthreads_find_trap (pid, 0) + && linuxthreads_breakpoint_zombie[i].pc == read_pc_pid (pid)) + write_pc_pid (linuxthreads_breakpoint_zombie[i].pc + - DECR_PC_AFTER_BREAK, pid); + } + + /* Detach thread after thread. */ + inferior_pid = linuxthreads_manager_pid; + iterate_active_threads (detach_thread, 1); + + /* Remove pending SIGTRAP and SIGSTOP */ + linuxthreads_find_trap (inferior_pid, 1); + + linuxthreads_wait_last = -1; + linuxthreads_exit_status = __W_STOPCODE(0); + } + + linuxthreads_inferior_pid = 0; + linuxthreads_breakpoint_pid = 0; + linuxthreads_step_pid = 0; + linuxthreads_step_signo = TARGET_SIGNAL_0; + linuxthreads_manager_pid = 0; + linuxthreads_initial_pid = 0; + linuxthreads_attach_pending = 0; + linuxthreads_signal_update (0); + init_thread_list (); /* Destroy thread info */ + } + + child_ops.to_detach (args, from_tty); + + unpush_target (&linuxthreads_ops); +} + +/* Resume execution of process PID. If STEP is nozero, then + just single step it. If SIGNAL is nonzero, restart it with that + signal activated. */ + +static void +linuxthreads_resume (pid, step, signo) + int pid; + int step; + enum target_signal signo; +{ + if (!linuxthreads_max || stop_soon_quietly || linuxthreads_manager_pid == 0) + child_ops.to_resume (pid, step, signo); + else + { + int rpid; + if (linuxthreads_inferior_pid) + { + /* Prepare resume of the last thread that hit a breakpoint */ + linuxthreads_breakpoints_inserted = 0; + rpid = linuxthreads_inferior_pid; + linuxthreads_step_signo = signo; + } + else + { + struct cleanup *old_chain = NULL; + int i; + + if (pid < 0) + { + linuxthreads_step_pid = step ? inferior_pid : 0; + linuxthreads_step_signo = signo; + rpid = inferior_pid; + } + else + rpid = pid; + + if (pid < 0 || !step) + { + linuxthreads_breakpoints_inserted = 1; + + /* Walk through linuxthreads array in order to resume threads */ + if (pid >= 0 && inferior_pid != pid) + { + old_chain = save_inferior_pid (); + inferior_pid = pid; + } + + iterate_active_threads (resume_thread, 0); + if (linuxthreads_manager_pid != inferior_pid + && !linuxthreads_pending_status (linuxthreads_manager_pid)) + resume_thread (linuxthreads_manager_pid); + } + else + linuxthreads_breakpoints_inserted = 0; + + /* Deal with zombie breakpoint */ + for (i = 0; i <= linuxthreads_breakpoint_last; i++) + if (linuxthreads_breakpoint_zombie[i].pid == rpid) + { + if (linuxthreads_breakpoint_zombie[i].pc != read_pc_pid (rpid)) + { + /* The current pc is out of zombie breakpoint. */ + REMOVE_BREAKPOINT_ZOMBIE(i); + } + break; + } + + if (old_chain != NULL) + do_cleanups (old_chain); + } + + /* Resume initial thread. */ + if (!linuxthreads_pending_status (rpid)) + child_ops.to_resume (rpid, step, signo); + } +} + +/* Wait for any threads to stop. We may have to convert PID from a thread id + to a LWP id, and vice versa on the way out. */ + +static int +linuxthreads_wait (pid, ourstatus) + int pid; + struct target_waitstatus *ourstatus; +{ + int status; + int rpid; + int i; + int last; + int *wstatus; + + for (;;) + { + if (!linuxthreads_max) + rpid = 0; + else if (!linuxthreads_breakpoints_inserted) + { + if (linuxthreads_inferior_pid) + pid = linuxthreads_inferior_pid; + else if (pid < 0) + pid = inferior_pid; + last = rpid = 0; + wstatus = alloca (LINUXTHREAD_NSIG * sizeof (int)); + } + else if (pid < 0 && linuxthreads_wait_last >= 0) + { + status = linuxthreads_wait_status[linuxthreads_wait_last]; + rpid = linuxthreads_wait_pid[linuxthreads_wait_last--]; + } + else if (pid > 0 && linuxthreads_pending_status (pid)) + { + for (i = linuxthreads_wait_last; i >= 0; i--) + if (linuxthreads_wait_pid[i] == pid) + break; + if (i < 0) + rpid = 0; + else + { + status = linuxthreads_wait_status[i]; + rpid = pid; + if (i < linuxthreads_wait_last) + { + linuxthreads_wait_status[i] = + linuxthreads_wait_status[linuxthreads_wait_last]; + linuxthreads_wait_pid[i] = + linuxthreads_wait_pid[linuxthreads_wait_last]; + } + linuxthreads_wait_last--; + } + } + else + rpid = 0; + + if (rpid == 0) + { + int save_errno; + sigset_t omask; + + set_sigint_trap(); /* Causes SIGINT to be passed on to the + attached process. */ + set_sigio_trap (); + + sigprocmask(SIG_BLOCK, &linuxthreads_wait_mask, &omask); + for (;;) + { + rpid = waitpid (pid, &status, __WCLONE | WNOHANG); + if (rpid > 0) + break; + if (rpid == 0) + save_errno = 0; + else if (errno != EINTR) + save_errno = errno; + else + continue; + + rpid = waitpid (pid, &status, WNOHANG); + if (rpid > 0) + break; + if (rpid < 0) + if (errno == EINTR) + continue; + else if (save_errno != 0) + break; + + sigsuspend(&omask); + } + sigprocmask(SIG_SETMASK, &omask, NULL); + + save_errno = errno; + clear_sigio_trap (); + + clear_sigint_trap(); + + if (rpid == -1) + { + if (WIFEXITED(linuxthreads_exit_status)) + { + store_waitstatus (ourstatus, linuxthreads_exit_status); + return inferior_pid; + } + else + { + fprintf_unfiltered + (gdb_stderr, "Child process unexpectedly missing: %s.\n", + safe_strerror (save_errno)); + /* Claim it exited with unknown signal. */ + ourstatus->kind = TARGET_WAITKIND_SIGNALLED; + ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; + return -1; + } + } + + /* Signals arrive in any order. So get all signals until SIGTRAP + and resend previous ones to be held after. */ + if (linuxthreads_max + && !linuxthreads_breakpoints_inserted + && WIFSTOPPED(status)) + if (WSTOPSIG(status) == SIGTRAP) + { + while (--last >= 0) + kill (rpid, WSTOPSIG(wstatus[last])); + + /* insert negative zombie breakpoint */ + for (i = 0; i <= linuxthreads_breakpoint_last; i++) + if (linuxthreads_breakpoint_zombie[i].pid == rpid) + break; + if (i > linuxthreads_breakpoint_last) + { + linuxthreads_breakpoint_zombie[i].pid = rpid; + linuxthreads_breakpoint_last++; + } + linuxthreads_breakpoint_zombie[i].pc = read_pc_pid (rpid); + linuxthreads_breakpoint_zombie[i].step = 1; + } + else + { + if (WSTOPSIG(status) != SIGSTOP) + { + for (i = 0; i < last; i++) + if (wstatus[i] == status) + break; + if (i >= last) + wstatus[last++] = status; + } + child_resume (rpid, 1, TARGET_SIGNAL_0); + continue; + } + if (linuxthreads_inferior_pid) + linuxthreads_inferior_pid = 0; + } + + if (linuxthreads_max && !stop_soon_quietly) + { + if (linuxthreads_max + && WIFSTOPPED(status) + && WSTOPSIG(status) == SIGSTOP) + { + /* Skip SIGSTOP signals. */ + if (!linuxthreads_pending_status (rpid)) + if (linuxthreads_step_pid == rpid) + child_resume (rpid, 1, linuxthreads_step_signo); + else + child_resume (rpid, 0, TARGET_SIGNAL_0); + continue; + } + + /* Do no report exit status of cloned threads. */ + if (WIFEXITED(status)) + { + if (rpid == linuxthreads_initial_pid) + linuxthreads_exit_status = status; + + /* Remove any zombie breakpoint. */ + for (i = 0; i <= linuxthreads_breakpoint_last; i++) + if (linuxthreads_breakpoint_zombie[i].pid == rpid) + { + REMOVE_BREAKPOINT_ZOMBIE(i); + break; + } + if (pid > 0) + pid = -1; + continue; + } + + /* Deal with zombie breakpoint */ + for (i = 0; i <= linuxthreads_breakpoint_last; i++) + if (linuxthreads_breakpoint_zombie[i].pid == rpid) + break; + + if (i <= linuxthreads_breakpoint_last) + { + /* There is a potential zombie breakpoint */ + if (WIFEXITED(status) + || linuxthreads_breakpoint_zombie[i].pc != read_pc_pid (rpid)) + { + /* The current pc is out of zombie breakpoint. */ + REMOVE_BREAKPOINT_ZOMBIE(i); + } + else if (!linuxthreads_breakpoint_zombie[i].step + && WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) + { + /* This is a real one ==> decrement PC and restart. */ + write_pc_pid (linuxthreads_breakpoint_zombie[i].pc + - DECR_PC_AFTER_BREAK, rpid); + if (linuxthreads_step_pid == rpid) + child_resume (rpid, 1, linuxthreads_step_signo); + else + child_resume (rpid, 0, TARGET_SIGNAL_0); + continue; + } + } + + /* Walk through linuxthreads array in order to stop them */ + if (linuxthreads_breakpoints_inserted) + update_stop_threads (rpid); + + } + else if (rpid != inferior_pid) + continue; + + store_waitstatus (ourstatus, status); + + if (linuxthreads_attach_pending && !stop_soon_quietly) + { + int on = 1; + target_write_memory (linuxthreads_debug, (char *)&on, sizeof (on)); + update_stop_threads (rpid); + linuxthreads_signal_update (1); + linuxthreads_attach_pending = 0; + } + + if (linuxthreads_breakpoints_inserted + && WIFSTOPPED(status) + && WSTOPSIG(status) == SIGTRAP) + linuxthreads_breakpoint_pid = rpid; + else if (linuxthreads_breakpoint_pid) + linuxthreads_breakpoint_pid = 0; + + return rpid; + } +} + +/* Fork an inferior process, and start debugging it with ptrace. */ + +static void +linuxthreads_create_inferior (exec_file, allargs, env) + char *exec_file; + char *allargs; + char **env; +{ + push_target (&linuxthreads_ops); + linuxthreads_breakpoints_inserted = 1; + linuxthreads_breakpoint_last = -1; + linuxthreads_wait_last = -1; + linuxthreads_exit_status = __W_STOPCODE(0); + + if (linuxthreads_max) + linuxthreads_attach_pending = 1; + + child_ops.to_create_inferior (exec_file, allargs, env); +} + +/* Clean up after the inferior dies. */ + +static void +linuxthreads_mourn_inferior () +{ + if (linuxthreads_max) + { + int off = 0; + target_write_memory (linuxthreads_debug, (char *)&off, sizeof (off)); + + linuxthreads_inferior_pid = 0; + linuxthreads_breakpoint_pid = 0; + linuxthreads_step_pid = 0; + linuxthreads_step_signo = TARGET_SIGNAL_0; + linuxthreads_manager_pid = 0; + linuxthreads_initial_pid = 0; + linuxthreads_attach_pending = 0; + init_thread_list(); /* Destroy thread info */ + linuxthreads_signal_update (0); + } + + child_ops.to_mourn_inferior (); + + unpush_target (&linuxthreads_ops); +} + +/* Kill the inferior process */ + +static void +linuxthreads_kill () +{ + int rpid; + int status; + + if (inferior_pid == 0) + return; + + if (linuxthreads_max && linuxthreads_manager_pid != 0) + { + /* Remove all threads status. */ + inferior_pid = linuxthreads_manager_pid; + iterate_active_threads (kill_thread, 1); + } + + kill_thread (inferior_pid); + + if (linuxthreads_max && linuxthreads_manager_pid != 0) + { + /* Wait for thread to complete */ + while ((rpid = waitpid (-1, &status, __WCLONE)) > 0) + if (!WIFEXITED(status)) + kill_thread (rpid); + + while ((rpid = waitpid (-1, &status, 0)) > 0) + if (!WIFEXITED(status)) + kill_thread (rpid); + } + else + while ((rpid = waitpid (inferior_pid, &status, 0)) > 0) + if (!WIFEXITED(status)) + ptrace (PT_KILL, inferior_pid, (PTRACE_ARG3_TYPE) 0, 0); + + linuxthreads_mourn_inferior (); +} + +/* Insert a breakpoint */ + +static int +linuxthreads_insert_breakpoint (addr, contents_cache) + CORE_ADDR addr; + char *contents_cache; +{ + if (linuxthreads_max && linuxthreads_manager_pid != 0) + { + linuxthreads_breakpoint_addr = addr; + iterate_active_threads (insert_breakpoint, 1); + insert_breakpoint (linuxthreads_manager_pid); + } + + return child_ops.to_insert_breakpoint (addr, contents_cache); +} + +/* Remove a breakpoint */ + +static int +linuxthreads_remove_breakpoint (addr, contents_cache) + CORE_ADDR addr; + char *contents_cache; +{ + if (linuxthreads_max && linuxthreads_manager_pid != 0) + { + linuxthreads_breakpoint_addr = addr; + iterate_active_threads (remove_breakpoint, 1); + remove_breakpoint (linuxthreads_manager_pid); + } + + return child_ops.to_remove_breakpoint (addr, contents_cache); +} + +/* Mark our target-struct as eligible for stray "run" and "attach" commands. */ + +static int +linuxthreads_can_run () +{ + return child_suppress_run; +} + +struct target_ops linuxthreads_ops = { + "linuxthreads", /* to_shortname */ + "LINUX threads and pthread.", /* to_longname */ + "LINUX threads and pthread support.", /* to_doc */ + 0, /* to_open */ + 0, /* to_close */ + linuxthreads_attach, /* to_attach */ + linuxthreads_detach, /* to_detach */ + linuxthreads_resume, /* to_resume */ + linuxthreads_wait, /* to_wait */ + 0, /* to_fetch_registers */ + 0, /* to_store_registers */ + 0, /* to_prepare_to_store */ + 0, /* to_xfer_memory */ + 0, /* to_files_info */ + linuxthreads_insert_breakpoint, /* to_insert_breakpoint */ + linuxthreads_remove_breakpoint, /* to_remove_breakpoint */ + 0, /* to_terminal_init */ + 0, /* to_terminal_inferior */ + 0, /* to_terminal_ours_for_output */ + 0, /* to_terminal_ours */ + 0, /* to_terminal_info */ + linuxthreads_kill, /* to_kill */ + 0, /* to_load */ + 0, /* to_lookup_symbol */ + linuxthreads_create_inferior, /* to_create_inferior */ + linuxthreads_mourn_inferior, /* to_mourn_inferior */ + linuxthreads_can_run, /* to_can_run */ + 0, /* to_notice_signals */ + linuxthreads_thread_alive, /* to_thread_alive */ + 0, /* to_stop */ + thread_stratum, /* to_stratum */ + 0, /* to_next */ + 0, /* to_has_all_memory */ + 0, /* to_has_memory */ + 1, /* to_has_stack */ + 1, /* to_has_registers */ + 1, /* to_has_execution */ + 0, /* sections */ + 0, /* sections_end */ + OPS_MAGIC /* to_magic */ +}; + +void +_initialize_linuxthreads () +{ + struct sigaction sact; + + add_target (&linuxthreads_ops); + child_suppress_run = 1; + + /* Attach SIGCHLD handler */ + sact.sa_handler = sigchld_handler; + sigemptyset(&sact.sa_mask); + sact.sa_flags = 0; + sigaction(SIGCHLD, &sact, NULL); + + /* initialize SIGCHLD mask */ + sigemptyset(&linuxthreads_wait_mask); + sigaddset(&linuxthreads_wait_mask, SIGCHLD); +}