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]

patches for debugging a LinuxThreads-based application



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);
+}