Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [patch] Fix Linux attach to signalled/stopped processes
@ 2007-06-06 14:34 Jan Kratochvil
  2007-06-11 13:44 ` Jan Kratochvil
  2007-06-15 18:02 ` Mark Kettenis
  0 siblings, 2 replies; 52+ messages in thread
From: Jan Kratochvil @ 2007-06-06 14:34 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1126 bytes --]

Hi,

GDB hangs while attaching to an already SIGSTOPped process.
GDB also crashes if some signal delivery was pending right at the time GDB was
attaching:
	linux-nat.c:981: internal-error: linux_nat_attach: Assertion `pid == GET_PID (inferior_ptid) && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP' failed.

The attached patch was tested on kernel.org linux-2.6.20.4 on x86_64.

The patch changes the functionality of TO_ATTACH.  The former functionality was
too UNIX centric.  I was reading all the TO_ATTACH OS-flavor implementations
and I believe the non-"inf-ptrace.c" ones do not need update.  The code was
tested only on Linux kernel, though.

GDB will leave the detached processes still stopped in the default case.
If you ever run the `continue' command the process will be left running.
This behavior was coded this way before by Jeff Johnston.

FYI waitpid(2) may return a non-SIGSTOP signal before getting SIGSTOP if the
non-SIGSTOP signal was already in the queue.  It is a very rare race.


Regards,
Jan

Already posted a broken patch before:
	http://sources.redhat.com/ml/gdb-patches/2006-09/msg00092.html

[-- Attachment #2: gdb-attach-signalled.patch --]
[-- Type: text/plain, Size: 26289 bytes --]

2007-06-06  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb/target.h (target_attach): Comment it also calls WAITPID now.
	* gdb/inf-ptrace.c (stopped_pid): New variable.
	(inf_ptrace_follow_fork): Optionally stop detached parents of the
	followed children.
	(my_waitpid): New function copied from `linux-nat.c'.
	(inf_ptrace_attach): New variables GOT_PID, STATUS, SIG, STATUS_FILE,
	BUF, SIGSTOP.  Initialize STOPPED_PID.  Handle processes stopped before.
	Call also WAITPID before returning.  Handle signals returned by WAITPID.
	Handle exits during the attachment.
	(inf_ptrace_detach): Removed handling of the parameter ARGS as not useful
	and already disabled since the `detach checkpoint' command existence.
	Optionally stop the detached process.
	(inf_ptrace_resume): Clear STOPPED_PID if appropriate.
	* gdb/linux-nat.c (lin_lwp_attach_lwp): Handle processes stopped before.
	Handle signals returned by WAITPID.  Handle exits during the attachment.
	(linux_nat_attach): Removed the WAITPID functionality there.

2007-06-06  Jan Kratochvil  <jan.kratochvil@redhat.com>

	gdb.threads/attach-into-signal.c, gdb.threads/attach-into-signal.exp,
	gdb.threads/attach-stopped.c, gdb.threads/attach-stopped.exp: New files.

--- ./gdb/target.h	11 May 2007 19:55:20 -0000	1.101
+++ ./gdb/target.h	6 Jun 2007 13:46:22 -0000
@@ -537,9 +537,9 @@ void target_close (struct target_ops *ta
    to the `attach' command by the user.  This routine can be called
    when the target is not on the target-stack, if the target_can_run
    routine returns 1; in that case, it must push itself onto the stack.
-   Upon exit, the target should be ready for normal operations, and
-   should be ready to deliver the status of the process immediately
-   (without waiting) to an upcoming target_wait call.  */
+   Upon exit, the target should be ready for normal operations.
+   The status of the inferior is already processed and possibly pending
+   signals redelivered.  */
 
 #define	target_attach(args, from_tty)	\
      (*current_target.to_attach) (args, from_tty)
--- ./gdb/inf-ptrace.c	31 May 2007 17:32:21 -0000	1.38
+++ ./gdb/inf-ptrace.c	6 Jun 2007 13:46:22 -0000
@@ -39,6 +39,9 @@
 
 /* HACK: Save the ptrace ops returned by inf_ptrace_target.  */
 static struct target_ops *ptrace_ops_hack;
+
+/* Stored PID of the process being stopped during attach.  */
+static pid_t stopped_pid;
 \f
 
 #ifdef PT_GET_PROCESS_STATE
@@ -70,14 +73,20 @@ inf_ptrace_follow_fork (struct target_op
 
   if (follow_child)
     {
+      unsigned long sig = 0;
+
       inferior_ptid = pid_to_ptid (fpid);
       detach_breakpoints (pid);
 
       /* Reset breakpoints in the child as appropriate.  */
       follow_inferior_reset_breakpoints ();
 
-      if (ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, 0) == -1)
-	perror_with_name (("ptrace"));
+      /* Stop the process again if it was stopped during the attachment.  */
+      if (pid == stopped_pid)
+	sig = target_signal_to_host (TARGET_SIGNAL_STOP));
+
+      if (ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, (void *) sig) == -1)
+	perror_with_name (("ptrace PT_DETACH"));
     }
   else
     {
@@ -174,6 +183,21 @@ inf_ptrace_mourn_inferior (void)
   generic_mourn_inferior ();
 }
 
+/* Wrapper function for waitpid which handles EINTR.  */
+
+static int
+my_waitpid (int pid, int *status, int flags)
+{
+  int ret;
+  do
+    {
+      ret = waitpid (pid, status, flags);
+    }
+  while (ret == -1 && errno == EINTR);
+
+  return ret;
+}
+
 /* Attach to the process specified by ARGS.  If FROM_TTY is non-zero,
    be chatty about it.  */
 
@@ -181,8 +205,13 @@ static void
 inf_ptrace_attach (char *args, int from_tty)
 {
   char *exec_file;
-  pid_t pid;
+  pid_t pid, got_pid;
   char *dummy;
+  int status;
+  unsigned long sig;
+  FILE *status_file;
+  char buf[100]; 
+  int sigstop = target_signal_to_host (TARGET_SIGNAL_STOP);
 
   if (!args)
     error_no_arg (_("process-id to attach"));
@@ -211,11 +241,64 @@ inf_ptrace_attach (char *args, int from_
     }
 
 #ifdef PT_ATTACH
+  stopped_pid = 0;
+  /* There is a small moment after PTRACE_ATTACH where PTRACE_CONT will
+     succeed only for originally stopped processes.  Unfortunately in a moment
+     PTRACE_ATTACH will deliver its SIGSTOP and PTRACE_CONT shows no difference
+     since that moment.
+     "/proc/%d/status" is also a race but it is safe for unstopped cases.  */
+  snprintf (buf, sizeof (buf), "/proc/%d/status", (int) pid);
+  status_file = fopen (buf, "r");
+  if (status_file != NULL)
+    {
+      int have_state = 0;
+
+      while (fgets (buf, sizeof (buf), status_file))
+	{
+	  if (strncmp (buf, "State:", 6) == 0)
+	    {
+	      have_state = 1;
+	      break;
+	    }
+	} 
+      if (have_state != 0 && strstr (buf, "T (stopped)") != NULL)
+	stopped_pid = pid;
+      fclose (status_file);
+    }
+
   errno = 0;
   ptrace (PT_ATTACH, pid, (PTRACE_TYPE_ARG3)0, 0);
   if (errno != 0)
     perror_with_name (("ptrace"));
   attach_flag = 1;
+
+  /* Deliver one SIGSTOP just for sure.
+     If the process was already stopped AND some other process (like shell)
+     has already waited for it we would get stuck in waitpid ().  */
+  sig = sigstop;
+  do
+    {
+      if (sig != sigstop)
+	printf_unfiltered (_("Redelivering pending %s.\n"),
+		       target_signal_to_string (target_signal_from_host (sig)));
+      errno = 0;
+      ptrace (PT_CONTINUE, pid, (PTRACE_TYPE_ARG3)1, (void *) sig);
+      /* For unstopped processes the preventive signal may ESRCH.  */
+      if (errno != 0 && sig != sigstop)
+	perror_with_name ("ptrace PT_CONTINUE");
+
+      got_pid = my_waitpid (pid, &status, 0);
+      gdb_assert (got_pid == pid);
+
+      /* Check if the thread has exited.  */
+      if (WIFEXITED (status) || WIFSIGNALED (status))
+	error (_("Program %s exited.\n"),
+	       target_pid_to_str (pid_to_ptid (pid)));
+      gdb_assert (WIFSTOPPED (status));
+      sig = WSTOPSIG (status);
+      gdb_assert (sig != 0);
+    }
+  while (sig != sigstop);
 #else
   error (_("This system does not support attaching to a process"));
 #endif
@@ -241,14 +324,16 @@ inf_ptrace_post_attach (int pid)
 
 #endif
 
-/* Detach from the inferior, optionally passing it the signal
-   specified by ARGS.  If FROM_TTY is non-zero, be chatty about it.  */
+/* Detach from the inferior.  If FROM_TTY is non-zero, be chatty about it.  */
 
 static void
 inf_ptrace_detach (char *args, int from_tty)
 {
   pid_t pid = ptid_get_pid (inferior_ptid);
-  int sig = 0;
+  unsigned long sig = 0;
+
+  if (args)
+    error (_("Too many arguments"));
 
   if (from_tty)
     {
@@ -259,18 +344,19 @@ inf_ptrace_detach (char *args, int from_
 			 target_pid_to_str (pid_to_ptid (pid)));
       gdb_flush (gdb_stdout);
     }
-  if (args)
-    sig = atoi (args);
 
 #ifdef PT_DETACH
   /* We'd better not have left any breakpoints in the program or it'll
      die when it hits one.  Also note that this may only work if we
      previously attached to the inferior.  It *might* work if we
      started the process ourselves.  */
+  /* Stop the process again if it was stopped during the attachment.  */
+  if (pid == stopped_pid)
+    sig = target_signal_to_host (TARGET_SIGNAL_STOP);
   errno = 0;
-  ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, sig);
+  ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, (void *) sig);
   if (errno != 0)
-    perror_with_name (("ptrace"));
+    perror_with_name (("ptrace PT_DETACH"));
   attach_flag = 0;
 #else
   error (_("This system does not support detaching from a process"));
@@ -325,6 +411,12 @@ inf_ptrace_resume (ptid_t ptid, int step
        single-threaded processes, so simply resume the inferior.  */
     pid = ptid_get_pid (inferior_ptid);
 
+  /* At this point, we are going to resume the inferior and if we
+     have attached to a stopped process, we no longer should leave
+     it as stopped if the user detaches.  */
+  if (!step && pid == stopped_pid)
+    stopped_pid = 0;
+
   if (step)
     {
       /* If this system does not support PT_STEP, a higher level
--- ./gdb/linux-nat.c	10 May 2007 21:36:00 -0000	1.61
+++ ./gdb/linux-nat.c	6 Jun 2007 13:46:22 -0000
@@ -886,6 +886,7 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
     {
       pid_t pid;
       int status;
+      unsigned long sig;
 
       if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
 	{
@@ -907,28 +908,50 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
 			    "LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
 			    target_pid_to_str (ptid));
 
-      pid = my_waitpid (GET_LWP (ptid), &status, 0);
-      if (pid == -1 && errno == ECHILD)
+      sig = SIGSTOP;
+      do
 	{
-	  /* Try again with __WCLONE to check cloned processes.  */
-	  pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
-	  lp->cloned = 1;
-	}
+	  if (sig != SIGSTOP)
+	    printf_unfiltered (_("Redelivering pending %s.\n"),
+		       target_signal_to_string (target_signal_from_host (sig)));
+	  /* For unstopped processes the preventive signal may ESRCH.  */
+	  if (ptrace (PTRACE_CONT, GET_LWP (ptid), (PTRACE_TYPE_ARG3)1,
+		      (void *) sig) != 0 && sig != SIGSTOP)
+	    perror_with_name ("ptrace");
+
+	  pid = my_waitpid (GET_LWP (ptid), &status, 0);
+	  if (pid == -1 && errno == ECHILD)
+	    {
+	      /* Try again with __WCLONE to check cloned processes.  */
+	      pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
+	      lp->cloned = 1;
+	    }
+	  gdb_assert (pid == GET_LWP (ptid));
 
-      gdb_assert (pid == GET_LWP (ptid)
-		  && WIFSTOPPED (status) && WSTOPSIG (status));
+	  if (debug_linux_nat)
+	    {
+	      fprintf_unfiltered (gdb_stdlog,
+				  "LLAL: waitpid %s received %s\n",
+				  target_pid_to_str (ptid),
+				  status_to_str (status));
+	    }
+
+	  /* Check if the thread has exited.  */
+	  if (WIFEXITED (status) || WIFSIGNALED (status))
+	    {
+	      warning (_("Thread %s exited: %s"), target_pid_to_str (ptid),
+		       status_to_str (status));
+	      return -1;
+	    }
+	  gdb_assert (WIFSTOPPED (status));
+	  sig = WSTOPSIG (status);
+	  gdb_assert (sig != 0);
+	}
+      while (sig != SIGSTOP);
 
       target_post_attach (pid);
 
       lp->stopped = 1;
-
-      if (debug_linux_nat)
-	{
-	  fprintf_unfiltered (gdb_stdlog,
-			      "LLAL: waitpid %s received %s\n",
-			      target_pid_to_str (ptid),
-			      status_to_str (status));
-	}
     }
   else
     {
@@ -953,8 +976,6 @@ static void
 linux_nat_attach (char *args, int from_tty)
 {
   struct lwp_info *lp;
-  pid_t pid;
-  int status;
 
   /* FIXME: We should probably accept a list of process id's, and
      attach all of them.  */
@@ -964,22 +985,6 @@ linux_nat_attach (char *args, int from_t
   inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
   lp = add_lwp (inferior_ptid);
 
-  /* Make sure the initial process is stopped.  The user-level threads
-     layer might want to poke around in the inferior, and that won't
-     work if things haven't stabilized yet.  */
-  pid = my_waitpid (GET_PID (inferior_ptid), &status, 0);
-  if (pid == -1 && errno == ECHILD)
-    {
-      warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
-
-      /* Try again with __WCLONE to check cloned processes.  */
-      pid = my_waitpid (GET_PID (inferior_ptid), &status, __WCLONE);
-      lp->cloned = 1;
-    }
-
-  gdb_assert (pid == GET_PID (inferior_ptid)
-	      && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
-
   lp->stopped = 1;
 
   /* Fake the SIGSTOP that core GDB expects.  */
@@ -987,8 +992,8 @@ linux_nat_attach (char *args, int from_t
   lp->resumed = 1;
   if (debug_linux_nat)
     {
-      fprintf_unfiltered (gdb_stdlog,
-			  "LLA: waitpid %ld, faking SIGSTOP\n", (long) pid);
+      fprintf_unfiltered (gdb_stdlog, "LLA: waitpid %d, faking SIGSTOP\n",
+			  GET_PID (inferior_ptid));
     }
 }
 
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-into-signal.c	6 Jun 2007 13:46:22 -0000
@@ -0,0 +1,65 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2007 Free Software Foundation, Inc.
+
+   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. */
+
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+void action(int sig, siginfo_t * info, void *uc)
+{
+  raise (SIGALRM);
+}
+
+static void *func (void *arg)
+{
+  struct sigaction act;
+
+  memset (&act, 0, sizeof(struct sigaction));
+  act.sa_sigaction = action;
+  act.sa_flags = SA_RESTART;
+  sigaction (SIGALRM, &act, 0);
+
+  raise (SIGALRM);
+
+  abort ();
+  /* NOTREACHED */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-into-signal.exp	6 Jun 2007 13:46:22 -0000
@@ -0,0 +1,149 @@
+# Copyright 2007
+
+# 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 test was created by modifying attach-stopped.exp.
+# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+set testfile "attach-into-signal"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+    # Statistically there is a better chance without giving process a nice.
+
+    set testpid [eval exec $binfile &]
+    exec sleep 2
+
+    # Run 2 passes of the test.
+    # The C file inferior stops pending its signals if a single one is lost,
+    # we test successful redelivery of the caught signal by the 2nd pass.
+
+    # linux-2.6.20.4.x86_64 had maximal attempt # 20 in 4 test runs.
+    set attempts 100
+    set attempt 0
+    set passes 1
+    while { $passes < 3 && $attempt < $attempts } {
+
+	# Start with clean gdb
+	gdb_exit
+	gdb_start
+	gdb_reinitialize_dir $srcdir/$subdir
+	gdb_load ${binfile}
+
+	# No PASS message as we may be looping in multiple attempts.
+	gdb_test "set debug lin-lwp 1" "" ""
+
+	set test "$threadtype: set file (pass $passes), before attach1 to stopped process"
+	gdb_test_multiple "file $binfile" $test {
+	   -re "Load new symbol table from.*y or n. $" {
+		# No PASS message as we may be looping in multiple attempts.
+		gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." ""
+	    }
+	    -re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+		# No PASS message as we may be looping in multiple attempts.
+	    }
+	}
+
+	# Main test:
+	set test "$threadtype: attach (pass $passes), pending signal catch"
+	gdb_test_multiple "attach $testpid" $test {
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*Redelivering pending Alarm clock..*$gdb_prompt $" {
+		# nonthreaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+		# nonthreaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt + 1]
+	    }
+	    -re "Attaching to process $testpid.*Redelivering pending Alarm clock..*$gdb_prompt $" {
+		# threaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to process $testpid.*$gdb_prompt $" {
+		# threaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt - 1]
+	    }
+	}
+    }
+    if {$passes < 3} {
+	fail $test
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+
+    # Continue the program - some Linux kernels need it before -9 if the
+    # process is stopped.
+    remote_exec build "kill -s CONT ${testpid}"
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case first without threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
+
+return 0
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-stopped.c	6 Jun 2007 13:46:22 -0000
@@ -0,0 +1,51 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2005-2007 Free Software Foundation, Inc.
+
+   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 program is intended to be started outside of gdb, then
+   manually stopped via a signal.  */
+
+#include <stddef.h>
+#include <unistd.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+static void *func (void *arg)
+{
+  sleep (10000);  /* Ridiculous time, but we will eventually kill it.  */
+  sleep (10000);  /* Second sleep.  */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-stopped.exp	6 Jun 2007 13:46:22 -0000
@@ -0,0 +1,208 @@
+# Copyright 2005-2007
+
+# 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 test was created by modifying attach.exp.
+# This file was created by Jeff Johnston <jjohnstn@redhat.com>.
+# This file was updated by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+# This test only works on Linux
+if { ![istarget "*-*-linux-gnu*"] } {
+    return 0
+}
+
+set testfile "attach-stopped"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+#execute_anywhere "rm -f ${binfile}"
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+
+    set testpid [eval exec $binfile &]
+    exec sleep 2
+       
+    # Stop the program 
+    remote_exec build "kill -s STOP ${testpid}"
+
+    # Start with clean gdb
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the process by first giving its
+    # executable name via the file command, and using attach with the
+    # process ID.
+
+    set test "$threadtype: set file, before attach1 to stopped process"
+    gdb_test_multiple "file $binfile" "$test" {
+       -re "Load new symbol table from.*y or n. $" {
+	    gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." \
+		    "$test (re-read)"
+	}
+	-re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    set test "$threadtype: attach1 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*clone.*" "$threadtype: attach1 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach1 to stopped bt"
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    set fileid [open /proc/${testpid}/status r];
+    gets $fileid line1;
+    gets $fileid line2;
+    close $fileid;
+
+    set test "$threadtype: attach1, exit leaves process stopped"
+    if {[string match "*(stopped)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # At this point, the process should still be stopped
+
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the process just by giving the
+    # process ID.
+       
+    set test "$threadtype: attach2 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*clone.*" "$threadtype: attach2 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach2 to stopped bt"
+    }
+    gdb_breakpoint [gdb_get_line_number "$threadtype: Second sleep"]
+    set test "$threadtype: attach2 continue"
+    send_gdb "continue\n"
+    gdb_expect {
+      -re "Continuing"
+	{ pass "continue ($test)" }
+      timeout
+	{ fail "continue ($test) (timeout)" }
+    }
+
+    # For this to work we must be sure to consume the "Continuing."
+    # message first, or GDB's signal handler may not be in place.
+    after 1000 {send_gdb "\003"}
+    set test "$threadtype: attach2 stop interrupt"
+    gdb_expect 10 {
+      -re "Program received signal SIGINT.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      -re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      timeout
+	{
+	  fail $test
+	}
+    }
+
+    gdb_exit
+
+    # Avoid some race:
+    exec sleep 2
+
+    # At this point, the process should be sleeping
+
+    set fileid2 [open /proc/${testpid}/status r];
+    gets $fileid2 line1;
+    gets $fileid2 line2;
+    close $fileid2;
+
+    set test "$threadtype: attach2, exit leaves process sleeping"
+    if {[string match "*(sleeping)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case first without threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
+
+return 0

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2007-06-06 14:34 [patch] Fix Linux attach to signalled/stopped processes Jan Kratochvil
@ 2007-06-11 13:44 ` Jan Kratochvil
  2007-06-15 18:02 ` Mark Kettenis
  1 sibling, 0 replies; 52+ messages in thread
From: Jan Kratochvil @ 2007-06-11 13:44 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 119 bytes --]

Hi,

just a minor update for the testcase.
It could infinitely loop in the case of some uncaught FAILs.


Regards,
Jan

[-- Attachment #2: gdb-attach-signalled.patch --]
[-- Type: text/plain, Size: 26351 bytes --]

2007-06-06  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb/target.h (target_attach): Comment it also calls WAITPID now.
	* gdb/inf-ptrace.c (stopped_pid): New variable.
	(inf_ptrace_follow_fork): Optionally stop detached parents of the
	followed children.
	(my_waitpid): New function copied from `linux-nat.c'.
	(inf_ptrace_attach): New variables GOT_PID, STATUS, SIG, STATUS_FILE,
	BUF, SIGSTOP.  Initialize STOPPED_PID.  Handle processes stopped before.
	Call also WAITPID before returning.  Handle signals returned by WAITPID.
	Handle exits during the attachment.
	(inf_ptrace_detach): Removed handling of the parameter ARGS as not useful
	and already disabled since the `detach checkpoint' command existence.
	Optionally stop the detached process.
	(inf_ptrace_resume): Clear STOPPED_PID if appropriate.
	* gdb/linux-nat.c (lin_lwp_attach_lwp): Handle processes stopped before.
	Handle signals returned by WAITPID.  Handle exits during the attachment.
	(linux_nat_attach): Removed the WAITPID functionality there.

2007-06-11  Jan Kratochvil  <jan.kratochvil@redhat.com>

	gdb.threads/attach-into-signal.c, gdb.threads/attach-into-signal.exp,
	gdb.threads/attach-stopped.c, gdb.threads/attach-stopped.exp: New files.

--- ./gdb/target.h	11 May 2007 19:55:20 -0000	1.101
+++ ./gdb/target.h	6 Jun 2007 13:46:22 -0000
@@ -537,9 +537,9 @@ void target_close (struct target_ops *ta
    to the `attach' command by the user.  This routine can be called
    when the target is not on the target-stack, if the target_can_run
    routine returns 1; in that case, it must push itself onto the stack.
-   Upon exit, the target should be ready for normal operations, and
-   should be ready to deliver the status of the process immediately
-   (without waiting) to an upcoming target_wait call.  */
+   Upon exit, the target should be ready for normal operations.
+   The status of the inferior is already processed and possibly pending
+   signals redelivered.  */
 
 #define	target_attach(args, from_tty)	\
      (*current_target.to_attach) (args, from_tty)
--- ./gdb/inf-ptrace.c	31 May 2007 17:32:21 -0000	1.38
+++ ./gdb/inf-ptrace.c	6 Jun 2007 13:46:22 -0000
@@ -39,6 +39,9 @@
 
 /* HACK: Save the ptrace ops returned by inf_ptrace_target.  */
 static struct target_ops *ptrace_ops_hack;
+
+/* Stored PID of the process being stopped during attach.  */
+static pid_t stopped_pid;
 \f
 
 #ifdef PT_GET_PROCESS_STATE
@@ -70,14 +73,20 @@ inf_ptrace_follow_fork (struct target_op
 
   if (follow_child)
     {
+      unsigned long sig = 0;
+
       inferior_ptid = pid_to_ptid (fpid);
       detach_breakpoints (pid);
 
       /* Reset breakpoints in the child as appropriate.  */
       follow_inferior_reset_breakpoints ();
 
-      if (ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, 0) == -1)
-	perror_with_name (("ptrace"));
+      /* Stop the process again if it was stopped during the attachment.  */
+      if (pid == stopped_pid)
+	sig = target_signal_to_host (TARGET_SIGNAL_STOP));
+
+      if (ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, (void *) sig) == -1)
+	perror_with_name (("ptrace PT_DETACH"));
     }
   else
     {
@@ -174,6 +183,21 @@ inf_ptrace_mourn_inferior (void)
   generic_mourn_inferior ();
 }
 
+/* Wrapper function for waitpid which handles EINTR.  */
+
+static int
+my_waitpid (int pid, int *status, int flags)
+{
+  int ret;
+  do
+    {
+      ret = waitpid (pid, status, flags);
+    }
+  while (ret == -1 && errno == EINTR);
+
+  return ret;
+}
+
 /* Attach to the process specified by ARGS.  If FROM_TTY is non-zero,
    be chatty about it.  */
 
@@ -181,8 +205,13 @@ static void
 inf_ptrace_attach (char *args, int from_tty)
 {
   char *exec_file;
-  pid_t pid;
+  pid_t pid, got_pid;
   char *dummy;
+  int status;
+  unsigned long sig;
+  FILE *status_file;
+  char buf[100]; 
+  int sigstop = target_signal_to_host (TARGET_SIGNAL_STOP);
 
   if (!args)
     error_no_arg (_("process-id to attach"));
@@ -211,11 +241,64 @@ inf_ptrace_attach (char *args, int from_
     }
 
 #ifdef PT_ATTACH
+  stopped_pid = 0;
+  /* There is a small moment after PTRACE_ATTACH where PTRACE_CONT will
+     succeed only for originally stopped processes.  Unfortunately in a moment
+     PTRACE_ATTACH will deliver its SIGSTOP and PTRACE_CONT shows no difference
+     since that moment.
+     "/proc/%d/status" is also a race but it is safe for unstopped cases.  */
+  snprintf (buf, sizeof (buf), "/proc/%d/status", (int) pid);
+  status_file = fopen (buf, "r");
+  if (status_file != NULL)
+    {
+      int have_state = 0;
+
+      while (fgets (buf, sizeof (buf), status_file))
+	{
+	  if (strncmp (buf, "State:", 6) == 0)
+	    {
+	      have_state = 1;
+	      break;
+	    }
+	} 
+      if (have_state != 0 && strstr (buf, "T (stopped)") != NULL)
+	stopped_pid = pid;
+      fclose (status_file);
+    }
+
   errno = 0;
   ptrace (PT_ATTACH, pid, (PTRACE_TYPE_ARG3)0, 0);
   if (errno != 0)
     perror_with_name (("ptrace"));
   attach_flag = 1;
+
+  /* Deliver one SIGSTOP just for sure.
+     If the process was already stopped AND some other process (like shell)
+     has already waited for it we would get stuck in waitpid ().  */
+  sig = sigstop;
+  do
+    {
+      if (sig != sigstop)
+	printf_unfiltered (_("Redelivering pending %s.\n"),
+		       target_signal_to_string (target_signal_from_host (sig)));
+      errno = 0;
+      ptrace (PT_CONTINUE, pid, (PTRACE_TYPE_ARG3)1, (void *) sig);
+      /* For unstopped processes the preventive signal may ESRCH.  */
+      if (errno != 0 && sig != sigstop)
+	perror_with_name ("ptrace PT_CONTINUE");
+
+      got_pid = my_waitpid (pid, &status, 0);
+      gdb_assert (got_pid == pid);
+
+      /* Check if the thread has exited.  */
+      if (WIFEXITED (status) || WIFSIGNALED (status))
+	error (_("Program %s exited.\n"),
+	       target_pid_to_str (pid_to_ptid (pid)));
+      gdb_assert (WIFSTOPPED (status));
+      sig = WSTOPSIG (status);
+      gdb_assert (sig != 0);
+    }
+  while (sig != sigstop);
 #else
   error (_("This system does not support attaching to a process"));
 #endif
@@ -241,14 +324,16 @@ inf_ptrace_post_attach (int pid)
 
 #endif
 
-/* Detach from the inferior, optionally passing it the signal
-   specified by ARGS.  If FROM_TTY is non-zero, be chatty about it.  */
+/* Detach from the inferior.  If FROM_TTY is non-zero, be chatty about it.  */
 
 static void
 inf_ptrace_detach (char *args, int from_tty)
 {
   pid_t pid = ptid_get_pid (inferior_ptid);
-  int sig = 0;
+  unsigned long sig = 0;
+
+  if (args)
+    error (_("Too many arguments"));
 
   if (from_tty)
     {
@@ -259,18 +344,19 @@ inf_ptrace_detach (char *args, int from_
 			 target_pid_to_str (pid_to_ptid (pid)));
       gdb_flush (gdb_stdout);
     }
-  if (args)
-    sig = atoi (args);
 
 #ifdef PT_DETACH
   /* We'd better not have left any breakpoints in the program or it'll
      die when it hits one.  Also note that this may only work if we
      previously attached to the inferior.  It *might* work if we
      started the process ourselves.  */
+  /* Stop the process again if it was stopped during the attachment.  */
+  if (pid == stopped_pid)
+    sig = target_signal_to_host (TARGET_SIGNAL_STOP);
   errno = 0;
-  ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, sig);
+  ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, (void *) sig);
   if (errno != 0)
-    perror_with_name (("ptrace"));
+    perror_with_name (("ptrace PT_DETACH"));
   attach_flag = 0;
 #else
   error (_("This system does not support detaching from a process"));
@@ -325,6 +411,12 @@ inf_ptrace_resume (ptid_t ptid, int step
        single-threaded processes, so simply resume the inferior.  */
     pid = ptid_get_pid (inferior_ptid);
 
+  /* At this point, we are going to resume the inferior and if we
+     have attached to a stopped process, we no longer should leave
+     it as stopped if the user detaches.  */
+  if (!step && pid == stopped_pid)
+    stopped_pid = 0;
+
   if (step)
     {
       /* If this system does not support PT_STEP, a higher level
--- ./gdb/linux-nat.c	10 May 2007 21:36:00 -0000	1.61
+++ ./gdb/linux-nat.c	6 Jun 2007 13:46:22 -0000
@@ -886,6 +886,7 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
     {
       pid_t pid;
       int status;
+      unsigned long sig;
 
       if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
 	{
@@ -907,28 +908,50 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
 			    "LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
 			    target_pid_to_str (ptid));
 
-      pid = my_waitpid (GET_LWP (ptid), &status, 0);
-      if (pid == -1 && errno == ECHILD)
+      sig = SIGSTOP;
+      do
 	{
-	  /* Try again with __WCLONE to check cloned processes.  */
-	  pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
-	  lp->cloned = 1;
-	}
+	  if (sig != SIGSTOP)
+	    printf_unfiltered (_("Redelivering pending %s.\n"),
+		       target_signal_to_string (target_signal_from_host (sig)));
+	  /* For unstopped processes the preventive signal may ESRCH.  */
+	  if (ptrace (PTRACE_CONT, GET_LWP (ptid), (PTRACE_TYPE_ARG3)1,
+		      (void *) sig) != 0 && sig != SIGSTOP)
+	    perror_with_name ("ptrace");
+
+	  pid = my_waitpid (GET_LWP (ptid), &status, 0);
+	  if (pid == -1 && errno == ECHILD)
+	    {
+	      /* Try again with __WCLONE to check cloned processes.  */
+	      pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
+	      lp->cloned = 1;
+	    }
+	  gdb_assert (pid == GET_LWP (ptid));
 
-      gdb_assert (pid == GET_LWP (ptid)
-		  && WIFSTOPPED (status) && WSTOPSIG (status));
+	  if (debug_linux_nat)
+	    {
+	      fprintf_unfiltered (gdb_stdlog,
+				  "LLAL: waitpid %s received %s\n",
+				  target_pid_to_str (ptid),
+				  status_to_str (status));
+	    }
+
+	  /* Check if the thread has exited.  */
+	  if (WIFEXITED (status) || WIFSIGNALED (status))
+	    {
+	      warning (_("Thread %s exited: %s"), target_pid_to_str (ptid),
+		       status_to_str (status));
+	      return -1;
+	    }
+	  gdb_assert (WIFSTOPPED (status));
+	  sig = WSTOPSIG (status);
+	  gdb_assert (sig != 0);
+	}
+      while (sig != SIGSTOP);
 
       target_post_attach (pid);
 
       lp->stopped = 1;
-
-      if (debug_linux_nat)
-	{
-	  fprintf_unfiltered (gdb_stdlog,
-			      "LLAL: waitpid %s received %s\n",
-			      target_pid_to_str (ptid),
-			      status_to_str (status));
-	}
     }
   else
     {
@@ -953,8 +976,6 @@ static void
 linux_nat_attach (char *args, int from_tty)
 {
   struct lwp_info *lp;
-  pid_t pid;
-  int status;
 
   /* FIXME: We should probably accept a list of process id's, and
      attach all of them.  */
@@ -964,22 +985,6 @@ linux_nat_attach (char *args, int from_t
   inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
   lp = add_lwp (inferior_ptid);
 
-  /* Make sure the initial process is stopped.  The user-level threads
-     layer might want to poke around in the inferior, and that won't
-     work if things haven't stabilized yet.  */
-  pid = my_waitpid (GET_PID (inferior_ptid), &status, 0);
-  if (pid == -1 && errno == ECHILD)
-    {
-      warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
-
-      /* Try again with __WCLONE to check cloned processes.  */
-      pid = my_waitpid (GET_PID (inferior_ptid), &status, __WCLONE);
-      lp->cloned = 1;
-    }
-
-  gdb_assert (pid == GET_PID (inferior_ptid)
-	      && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
-
   lp->stopped = 1;
 
   /* Fake the SIGSTOP that core GDB expects.  */
@@ -987,8 +992,8 @@ linux_nat_attach (char *args, int from_t
   lp->resumed = 1;
   if (debug_linux_nat)
     {
-      fprintf_unfiltered (gdb_stdlog,
-			  "LLA: waitpid %ld, faking SIGSTOP\n", (long) pid);
+      fprintf_unfiltered (gdb_stdlog, "LLA: waitpid %d, faking SIGSTOP\n",
+			  GET_PID (inferior_ptid));
     }
 }
 
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-into-signal.c	6 Jun 2007 13:46:22 -0000
@@ -0,0 +1,65 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2007 Free Software Foundation, Inc.
+
+   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. */
+
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+void action(int sig, siginfo_t * info, void *uc)
+{
+  raise (SIGALRM);
+}
+
+static void *func (void *arg)
+{
+  struct sigaction act;
+
+  memset (&act, 0, sizeof(struct sigaction));
+  act.sa_sigaction = action;
+  act.sa_flags = SA_RESTART;
+  sigaction (SIGALRM, &act, 0);
+
+  raise (SIGALRM);
+
+  abort ();
+  /* NOTREACHED */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-into-signal.exp	6 Jun 2007 13:46:22 -0000
@@ -0,0 +1,149 @@
+# Copyright 2007
+
+# 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 test was created by modifying attach-stopped.exp.
+# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+set testfile "attach-into-signal"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+    # Statistically there is a better chance without giving process a nice.
+
+    set testpid [eval exec $binfile &]
+    exec sleep 2
+
+    # Run 2 passes of the test.
+    # The C file inferior stops pending its signals if a single one is lost,
+    # we test successful redelivery of the caught signal by the 2nd pass.
+
+    # linux-2.6.20.4.x86_64 had maximal attempt # 20 in 4 test runs.
+    set attempts 100
+    set attempt 0
+    set passes 1
+    while { $passes < 3 && $attempt < $attempts } {
+
+	# Start with clean gdb
+	gdb_exit
+	gdb_start
+	gdb_reinitialize_dir $srcdir/$subdir
+	gdb_load ${binfile}
+
+	# No PASS message as we may be looping in multiple attempts.
+	gdb_test "set debug lin-lwp 1" "" ""
+
+	set test "$threadtype: set file (pass $passes), before attach1 to stopped process"
+	if {[gdb_test_multiple "file $binfile" $test {
+	   -re "Load new symbol table from.*y or n. $" {
+		# No PASS message as we may be looping in multiple attempts.
+		gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." ""
+	    }
+	    -re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+		# No PASS message as we may be looping in multiple attempts.
+	    }
+	}] != 0 } {
+	    break
+	}
+
+	# Main test:
+	set test "$threadtype: attach (pass $passes), pending signal catch"
+	if {[gdb_test_multiple "attach $testpid" $test {
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*Redelivering pending Alarm clock..*$gdb_prompt $" {
+		# nonthreaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+		# nonthreaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt + 1]
+	    }
+	    -re "Attaching to process $testpid.*Redelivering pending Alarm clock..*$gdb_prompt $" {
+		# threaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to process $testpid.*$gdb_prompt $" {
+		# threaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt - 1]
+	    }
+	}] != 0 } {
+	    break
+	}
+    }
+    if {$passes < 3} {
+	fail $test
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+
+    # Continue the program - some Linux kernels need it before -9 if the
+    # process is stopped.
+    remote_exec build "kill -s CONT ${testpid}"
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case first without threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
+
+return 0
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-stopped.c	6 Jun 2007 13:46:22 -0000
@@ -0,0 +1,51 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2005-2007 Free Software Foundation, Inc.
+
+   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 program is intended to be started outside of gdb, then
+   manually stopped via a signal.  */
+
+#include <stddef.h>
+#include <unistd.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+static void *func (void *arg)
+{
+  sleep (10000);  /* Ridiculous time, but we will eventually kill it.  */
+  sleep (10000);  /* Second sleep.  */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-stopped.exp	6 Jun 2007 13:46:22 -0000
@@ -0,0 +1,208 @@
+# Copyright 2005-2007
+
+# 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 test was created by modifying attach.exp.
+# This file was created by Jeff Johnston <jjohnstn@redhat.com>.
+# This file was updated by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+# This test only works on Linux
+if { ![istarget "*-*-linux-gnu*"] } {
+    return 0
+}
+
+set testfile "attach-stopped"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+#execute_anywhere "rm -f ${binfile}"
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+
+    set testpid [eval exec $binfile &]
+    exec sleep 2
+       
+    # Stop the program 
+    remote_exec build "kill -s STOP ${testpid}"
+
+    # Start with clean gdb
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the process by first giving its
+    # executable name via the file command, and using attach with the
+    # process ID.
+
+    set test "$threadtype: set file, before attach1 to stopped process"
+    gdb_test_multiple "file $binfile" "$test" {
+       -re "Load new symbol table from.*y or n. $" {
+	    gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." \
+		    "$test (re-read)"
+	}
+	-re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    set test "$threadtype: attach1 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*clone.*" "$threadtype: attach1 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach1 to stopped bt"
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    set fileid [open /proc/${testpid}/status r];
+    gets $fileid line1;
+    gets $fileid line2;
+    close $fileid;
+
+    set test "$threadtype: attach1, exit leaves process stopped"
+    if {[string match "*(stopped)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # At this point, the process should still be stopped
+
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the process just by giving the
+    # process ID.
+       
+    set test "$threadtype: attach2 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*clone.*" "$threadtype: attach2 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach2 to stopped bt"
+    }
+    gdb_breakpoint [gdb_get_line_number "$threadtype: Second sleep"]
+    set test "$threadtype: attach2 continue"
+    send_gdb "continue\n"
+    gdb_expect {
+      -re "Continuing"
+	{ pass "continue ($test)" }
+      timeout
+	{ fail "continue ($test) (timeout)" }
+    }
+
+    # For this to work we must be sure to consume the "Continuing."
+    # message first, or GDB's signal handler may not be in place.
+    after 1000 {send_gdb "\003"}
+    set test "$threadtype: attach2 stop interrupt"
+    gdb_expect 10 {
+      -re "Program received signal SIGINT.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      -re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      timeout
+	{
+	  fail $test
+	}
+    }
+
+    gdb_exit
+
+    # Avoid some race:
+    exec sleep 2
+
+    # At this point, the process should be sleeping
+
+    set fileid2 [open /proc/${testpid}/status r];
+    gets $fileid2 line1;
+    gets $fileid2 line2;
+    close $fileid2;
+
+    set test "$threadtype: attach2, exit leaves process sleeping"
+    if {[string match "*(sleeping)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case first without threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
+
+return 0

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2007-06-06 14:34 [patch] Fix Linux attach to signalled/stopped processes Jan Kratochvil
  2007-06-11 13:44 ` Jan Kratochvil
@ 2007-06-15 18:02 ` Mark Kettenis
  2007-06-26 22:40   ` Jan Kratochvil
  1 sibling, 1 reply; 52+ messages in thread
From: Mark Kettenis @ 2007-06-15 18:02 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

> Date: Wed, 6 Jun 2007 16:34:32 +0200
> From: Jan Kratochvil <jan.kratochvil@redhat.com>
> 
> The patch changes the functionality of TO_ATTACH.  The former
> functionality was too UNIX centric.  I was reading all the TO_ATTACH
> OS-flavor implementations and I believe the non-"inf-ptrace.c" ones
> do not need update.  The code was tested only on Linux kernel,
> though.

I disagree here.  You're adding all sorts of goo to work around
Linux-specific problems.  Please put this stuff in linux-nat.c


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2007-06-15 18:02 ` Mark Kettenis
@ 2007-06-26 22:40   ` Jan Kratochvil
  2007-06-27  0:13     ` Mark Kettenis
  0 siblings, 1 reply; 52+ messages in thread
From: Jan Kratochvil @ 2007-06-26 22:40 UTC (permalink / raw)
  To: Mark Kettenis; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1251 bytes --]

On Fri, 15 Jun 2007 20:01:59 +0200, Mark Kettenis wrote:
> > Date: Wed, 6 Jun 2007 16:34:32 +0200
> > From: Jan Kratochvil <jan.kratochvil@redhat.com>
> > 
> > The patch changes the functionality of TO_ATTACH.  The former
> > functionality was too UNIX centric.  I was reading all the TO_ATTACH
> > OS-flavor implementations and I believe the non-"inf-ptrace.c" ones
> > do not need update.  The code was tested only on Linux kernel,
> > though.
> 
> I disagree here.  You're adding all sorts of goo to work around
> Linux-specific problems.  Please put this stuff in linux-nat.c

While that "/proc/PID/status" reading part looks definitely Linux specific
I would guess that the first waitpid(2) after PTRACE_ATTACH may not return
SIGSTOP even on other ptrace(2)-using OSes (BSD?).

Attaching a testcase for a possible non-Linux kernel test which:
Should print approx. 1x '.' per second (on each caught pending SIGALRM signal).
At least Linux 2.6.22-rc5.x86_64 prints '!' which looks as a kernel bug to me.

How does currently GDB behave there while attaching to a SIGSTOPped process?
This waitpid(2)-after-ptrace(2) signals redelivery looks as ptrace(2) specific
and not Linux specific to me.  And so it would belong to inf-ptrace.c.



Regards,
Jan

[-- Attachment #2: ptrace-test.c --]
[-- Type: text/plain, Size: 2926 bytes --]

/* This testcase is part of GDB, the GNU debugger.

   Copyright 2007 Free Software Foundation, Inc.

   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. */

#define _GNU_SOURCE
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <assert.h>
#include <fcntl.h>

static void action(int sig, siginfo_t * info, void *uc)
{
  raise (SIGALRM);
}

static void loop (void)
{
  struct sigaction act;

  memset (&act, 0, sizeof(struct sigaction));
  act.sa_sigaction = action;
  act.sa_flags = SA_RESTART;
  sigaction (SIGALRM, &act, 0);

  raise (SIGALRM);

  putchar ('!');

  for (;;)
    pause ();
  /* NOTREACHED */
  abort ();
}

static pid_t child;

static void
cleanup (void)
{
  kill (child, SIGKILL);
}

static void
handler (int signo)
{
  cleanup ();
}

int main (void)
{
  void (*handler_orig) (int signo);

  setbuf (stdout, NULL);

  child = fork ();
  switch (child)
    {
      case -1:
	abort ();
      case 0:
	loop ();
	/* NOTREACHED */
	abort ();
      default:
        break;
    }

  atexit (cleanup);
  handler_orig = signal (SIGABRT, handler);
  assert (handler_orig == SIG_DFL);

  for (;;)
    {
      errno = 0;
      ptrace (PTRACE_ATTACH, child, NULL, NULL);
      assert_perror (errno);
      unsigned long sig;

      /* Deliver one SIGSTOP just for sure.
	 If the process was already stopped AND some other process (like shell)
	 has already waited for it we would get stuck in waitpid ().  */
      sig = SIGSTOP;
      do
	{
	  pid_t got_pid;
	  int status;

	  errno = 0;
	  ptrace (PT_CONTINUE, child, (void *) 1UL, (void *) sig);
	  /* For unstopped processes the preventive signal may ESRCH.  */
	  if (sig != SIGSTOP)
	    {
	      assert_perror (errno);
	      putchar ('.');
	    }

	  got_pid = waitpid (child, &status, 0);
	  assert (got_pid == child);

	  /* Check if the thread has exited.  */
	  assert (!WIFEXITED (status));
	  assert (!WIFSIGNALED (status));

	  assert (WIFSTOPPED (status));
	  sig = WSTOPSIG (status);
	  assert (sig != 0);
	}
      while (sig != SIGSTOP);

      errno = 0;
      ptrace (PTRACE_DETACH, child, (void *) 1UL, (void *) 0UL);
      assert_perror (errno);
    }

  return 0;
}

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2007-06-26 22:40   ` Jan Kratochvil
@ 2007-06-27  0:13     ` Mark Kettenis
  2007-06-27 11:59       ` Jan Kratochvil
  0 siblings, 1 reply; 52+ messages in thread
From: Mark Kettenis @ 2007-06-27  0:13 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

> Date: Tue, 26 Jun 2007 23:03:41 +0200
> From: Jan Kratochvil <jan.kratochvil@redhat.com>
> 
> --mP3DRpeJDSE+ciuQ
> Content-Type: text/plain; charset=us-ascii
> Content-Disposition: inline
> 
> On Fri, 15 Jun 2007 20:01:59 +0200, Mark Kettenis wrote:
> > > Date: Wed, 6 Jun 2007 16:34:32 +0200
> > > From: Jan Kratochvil <jan.kratochvil@redhat.com>
> > > 
> > > The patch changes the functionality of TO_ATTACH.  The former
> > > functionality was too UNIX centric.  I was reading all the TO_ATTACH
> > > OS-flavor implementations and I believe the non-"inf-ptrace.c" ones
> > > do not need update.  The code was tested only on Linux kernel,
> > > though.
> > 
> > I disagree here.  You're adding all sorts of goo to work around
> > Linux-specific problems.  Please put this stuff in linux-nat.c
> 
> While that "/proc/PID/status" reading part looks definitely Linux specific
> I would guess that the first waitpid(2) after PTRACE_ATTACH may not return
> SIGSTOP even on other ptrace(2)-using OSes (BSD?).

It's not unimaginable that there are races, but I think it is wrong
for GDB to make things worse by sending signals itself.  We have to do
that on Linux because it has totally inadequate support for denugging
threaded processes, but we really should restrict this madness to
Linux alone.

> Attaching a testcase for a possible non-Linux kernel test which:
> Should print approx. 1x '.' per second (on each caught pending
> SIGALRM signal).  At least Linux 2.6.22-rc5.x86_64 prints '!' which
> looks as a kernel bug to me.

This is what I get on OpenBSD:

$ ./ptrace-test                                                          
.assertion "!WIFSIGNALED (status)" failed: file "ptrace-test.c", line 122, function "main"
Abort trap (core dumped) 

This is sort of odd, but then your test program has a serious problem.
It doesn't wait(2) after ptrace(PT_ATTACH, ...).  That's asking for trouble.

> How does currently GDB behave there while attaching to a SIGSTOPped process?

I don't think it matters whether the process is stopped or not.


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2007-06-27  0:13     ` Mark Kettenis
@ 2007-06-27 11:59       ` Jan Kratochvil
  2007-06-27 18:30         ` Mark Kettenis
  2007-06-30 11:45         ` Jan Kratochvil
  0 siblings, 2 replies; 52+ messages in thread
From: Jan Kratochvil @ 2007-06-27 11:59 UTC (permalink / raw)
  To: Mark Kettenis; +Cc: gdb-patches

On Wed, 27 Jun 2007 00:54:54 +0200, Mark Kettenis wrote:
...
> This is what I get on OpenBSD:
> 
> $ ./ptrace-test                                                          
> .assertion "!WIFSIGNALED (status)" failed: file "ptrace-test.c", line 122, function "main"
> Abort trap (core dumped) 
> 
> This is sort of odd,

Thanks for the test - it is now confirmed the code is not crossplatform and
different hacks may be needed on different ptrace(2)-using OSes.
OK, going to move it to linux-nat.c.

> but then your test program has a serious problem.
> It doesn't wait(2) after ptrace(PT_ATTACH, ...).  That's asking for trouble.

It does wait(2) right after a preventive `PT_CONT (SIGSTOP)' as otherwise
wait(2) would hang if the original process was already stopped ...

> > How does currently GDB behave there while attaching to a SIGSTOPped process?
> 
> I don't think it matters whether the process is stopped or not.

... the same way the current GDB hangs.  SIGSTOP is already delivered before
and the new SIGSTOP from PTRACE_ATTACH does not stack as SIGSTOP is already
active.  If the original SIGSTOP was already wait(2)ed by the pre-ptrace(2)
parent the new ptrace(2)-parent (the debugger) will not get the new SIGSTOP
caught and thus wait(2) hangs for it.
[ Info originally from Roland McGrath. ]


Regards,
Jan


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2007-06-27 11:59       ` Jan Kratochvil
@ 2007-06-27 18:30         ` Mark Kettenis
  2007-06-30 11:45         ` Jan Kratochvil
  1 sibling, 0 replies; 52+ messages in thread
From: Mark Kettenis @ 2007-06-27 18:30 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches

> Date: Wed, 27 Jun 2007 08:52:03 +0200
> From: Jan Kratochvil <jan.kratochvil@redhat.com>
> 
> On Wed, 27 Jun 2007 00:54:54 +0200, Mark Kettenis wrote:
> ...
> > This is what I get on OpenBSD:
> > 
> > $ ./ptrace-test                                                          
> > .assertion "!WIFSIGNALED (status)" failed: file "ptrace-test.c", line 122, function "main"
> > Abort trap (core dumped) 
> > 
> > This is sort of odd,
> 
> Thanks for the test - it is now confirmed the code is not crossplatform and
> different hacks may be needed on different ptrace(2)-using OSes.
> OK, going to move it to linux-nat.c.
> 
> > but then your test program has a serious problem.
> > It doesn't wait(2) after ptrace(PT_ATTACH, ...).  That's asking for trouble.
> 
> It does wait(2) right after a preventive `PT_CONT (SIGSTOP)' as otherwise
> wait(2) would hang if the original process was already stopped ...

That's a kernel bug in my book; wait(2) should always report a SIGSTOP
after a PT_ATTACH as soon as the traced process has been stopped.  If
the process was already stopped, wait(2) should simply report that
SIGSTOP immediately.

> > > How does currently GDB behave there while attaching to a SIGSTOPped process?
> > 
> > I don't think it matters whether the process is stopped or not.
> 
> ... the same way the current GDB hangs.  SIGSTOP is already delivered before
> and the new SIGSTOP from PTRACE_ATTACH does not stack as SIGSTOP is already
> active.  If the original SIGSTOP was already wait(2)ed by the pre-ptrace(2)
> parent the new ptrace(2)-parent (the debugger) will not get the new SIGSTOP
> caught and thus wait(2) hangs for it.
> [ Info originally from Roland McGrath. ]

So it seems that Linux completely ignores SIGSTOP if a process is
already stopped.  That's the right thing to do for "normal" processes,
but not ok for traced processes.  Looks like fixing that would solve a
lot of problems.  Wouldn't surprise me if that would also make lots of
the workarounds we put in for debugging multi-threaded programs
unnecessary.

Mark


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2007-06-27 11:59       ` Jan Kratochvil
  2007-06-27 18:30         ` Mark Kettenis
@ 2007-06-30 11:45         ` Jan Kratochvil
  2007-06-30 11:57           ` Eli Zaretskii
  1 sibling, 1 reply; 52+ messages in thread
From: Jan Kratochvil @ 2007-06-30 11:45 UTC (permalink / raw)
  To: gdb-patches; +Cc: Mark Kettenis

[-- Attachment #1: Type: text/plain, Size: 532 bytes --]

On Wed, 27 Jun 2007 08:52:03 +0200, Jan Kratochvil wrote:
...
> OK, going to move it to linux-nat.c.

Done; various parts changed.


There is one ugly workaround part (described in the comments in the patch).
Testcase for this workaround is also attached, it appears as (another) Linux
kernel ptrace(2) bug to me.  Is it worth workarounding it?  IMO not worth
fixing as it is a corner case and UTRACE is going to provide alternative API.

Tested on x86_64, on upstream 2.6.22-rc4-git7 and Red Hat kernel-2.6.9-55.EL.


Regards,
Jan

[-- Attachment #2: gdb-attach-signalled-ver5.patch --]
[-- Type: text/plain, Size: 27234 bytes --]

2007-06-30  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb/linux-nat.h (struct lwp_info): New field WAS_STOPPED.
	* gdb/linux-nat.c (STRINGIFY, STRINGIFY_ARG): New macros.
	(kill_lwp): New declaration.
	(linux_ptrace_post_attach, pid_is_stopped): New function.
	(linux_child_follow_fork): New comment about WAS_STOPPED.
	(lin_lwp_attach_lwp): Variable PID removed.  Part replaced by a call to
	LINUX_PTRACE_POST_ATTACH.
	(linux_nat_attach): Likewise.
	(linux_nat_detach): Optionally stop the detached process.
	(linux_nat_resume): Clear WAS_STOPPED if appropriate.

2007-06-30  Jan Kratochvil  <jan.kratochvil@redhat.com>

	gdb.threads/attach-into-signal.c, gdb.threads/attach-into-signal.exp,
	gdb.threads/attach-stopped.c, gdb.threads/attach-stopped.exp: New files.

--- ./gdb/linux-nat.c	16 Jun 2007 17:16:25 -0000	1.64
+++ ./gdb/linux-nat.c	29 Jun 2007 22:06:05 -0000
@@ -86,6 +86,12 @@
 #define __WALL          0x40000000 /* Wait for any child.  */
 #endif
 
+#define STRINGIFY_ARG(x) #x
+#define STRINGIFY(x) STRINGIFY_ARG (x)
+
+static int linux_ptrace_post_attach (struct lwp_info *lp);
+static int kill_lwp (int lwpid, int signo);
+
 /* The single-threaded native GNU/Linux target_ops.  We save a pointer for
    the use of the multi-threaded target.  */
 static struct target_ops *linux_ops;
@@ -502,6 +508,11 @@ linux_child_follow_fork (struct target_o
 	}
       else
 	{
+	  /* We should check LP->WAS_STOPPED and detach it stopped accordingly.
+	     In this point of code it cannot be 1 as we would not get FORK
+	     executed without CONTINUE first which resets LP->WAS_STOPPED.
+	     We would have to first TARGET_STOP and WAITPID it as with running
+	     inferior PTRACE_DETACH, SIGSTOP will ignore the signal.  */
 	  target_detach (NULL, 0);
 	}
 
@@ -884,7 +895,6 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
      to happen.  */
   if (GET_LWP (ptid) != GET_PID (ptid) && lp == NULL)
     {
-      pid_t pid;
       int status;
 
       if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
@@ -902,33 +912,17 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
       if (lp == NULL)
 	lp = add_lwp (ptid);
 
-      if (debug_linux_nat)
-	fprintf_unfiltered (gdb_stdlog,
-			    "LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
-			    target_pid_to_str (ptid));
-
-      pid = my_waitpid (GET_LWP (ptid), &status, 0);
-      if (pid == -1 && errno == ECHILD)
-	{
-	  /* Try again with __WCLONE to check cloned processes.  */
-	  pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
-	  lp->cloned = 1;
+      status = linux_ptrace_post_attach (lp);
+      if (status != 0)
+        {
+	  warning (_("Thread %s exited: %s"), target_pid_to_str (ptid),
+		   status_to_str (status));
+	  return -1;
 	}
 
-      gdb_assert (pid == GET_LWP (ptid)
-		  && WIFSTOPPED (status) && WSTOPSIG (status));
-
-      target_post_attach (pid);
+      target_post_attach (GET_LWP (ptid));
 
       lp->stopped = 1;
-
-      if (debug_linux_nat)
-	{
-	  fprintf_unfiltered (gdb_stdlog,
-			      "LLAL: waitpid %s received %s\n",
-			      target_pid_to_str (ptid),
-			      status_to_str (status));
-	}
     }
   else
     {
@@ -949,11 +943,172 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
   return 0;
 }
 
+/* Detect `T (stopped)' in `/proc/PID/status'.
+   Other states including `T (tracing stop)' are reported as false.  */
+
+static int
+pid_is_stopped (pid_t pid)
+{
+  FILE *status_file;
+  char buf[100]; 
+  int retval = 0;
+
+  snprintf (buf, sizeof (buf), "/proc/%d/status", (int) pid);
+  status_file = fopen (buf, "r");
+  if (status_file != NULL)
+    {
+      int have_state = 0;
+
+      while (fgets (buf, sizeof (buf), status_file))
+	{
+	  if (strncmp (buf, "State:", 6) == 0)
+	    {
+	      have_state = 1;
+	      break;
+	    }
+	} 
+      if (have_state && strstr (buf, "T (stopped)") != NULL)
+	retval = 1;
+      fclose (status_file);
+    }
+  return retval;
+}
+
+/* Handle the processing after PTRACE_ATTACH, the first WAITPID -> SIGSTOP.
+   Returns STATUS if the thread has exited, 0 otherwise.
+   Sets LP->WAS_STOPPED if the process was originally stopped.
+   Sets LP->CLONED if the given LWP is not the thread leader.
+
+   Scenario for a standard unstopped inferior:
+    * `S (sleeping)' or `R (running)' or similiar states.
+    * PTRACE_ATTACH is called.
+    * `S (sleeping)' (or similiar) for some while.
+    * `T (tracing stop)'.
+    * WAITPID succeeds here returning SIGSTOP (signalled by PTRACE_ATTACH).
+
+   Scenario for a formerly stopped inferior:
+    * `T (stopped)'.
+    * PTRACE_ATTACH is called.
+    * `T (stopped)' would stay indefinitely
+      Note since this moment the `TracerPid' field gets filled
+      (by PTRACE_ATTACH), it is no longer just the common `T (stopped)' state.
+       * If no one did WAITPID since sending SIGSTOP our WAITPID would return
+         SIGSTOP.  The state still would not turn to `T (tracing stop)'.
+       * Usually its original parent (before PTRACE_ATTACH was applied) already
+         did WAITPID.  The original parent already received our SIGSTOP
+         sinalled by our PTRACE_ATTACH.
+         In this case our own WAITPID would hang.  Therefore...
+    * ... we do artificial: tkill (SIGCONT);
+      `PTRACE_CONT, SIGSTOP' does not work in 100% cases as sometimes SIGSTOP
+      gets remembered by kernel during the first PTRACE_CONT later and we get
+      spurious SIGSTOP event.  Expecting the signal may get delivered to
+      a different task of the thread group.
+      `kill_lwp (SIGSTOP)' has no effect in this moment (it is already stopped).
+    * WAITPID returns the artifical SIGCONT.
+      (The possibly pending SIGSTOP gets vanished by specifically SIGCONT.)
+    * State turns `T (tracing stop)'.
+   In this moment everything is almost fine but we need a workaround as final
+   `PTRACE_DETACH, SIGSTOP' would leave the process unstopped otherwise:
+    * tkill (SIGSTOP);
+    * `PTRACE_CONT, 0'
+    * WAITPID returns the artifical SIGSTOP.
+
+   With the pending (unwaited for) SIGSTOP the artifical signal effects are:
+   kill (SIGSTOP)
+   PTRACE_ATTACH
+    /-tkill (SIGCONT), WAITPID: SIGCONT, WAITPID: hang !
+   //-tkill (SIGCONT), WAITPID: SIGCONT, PTRACE_CONT (SIG_0), WAITPID: wait (OK)
+   \\-tkill (SIGALRM), WAITPID: SIGSTOP, WAITPID: hang !
+    \-tkill (SIGALRM), WAITPID: SIGSTOP, PTRACE_CONT (SIG_0), WAITPID: SIGALRM !
+   Therefore we signal artifical SIGCONT and stop waiting after its reception.
+
+   For the detection whether the process was formerly stopped we need to
+   read `/proc/PID/status'.  `PTRACE_CONT, SIGSTOP' returns ESRCH
+   for `S (sleeping)' and succeeds for `T (stopped)' but it unfortunately
+   succeeds even for `T (tracing stop)'.  Depending on PTRACE_CONT, SIGSTOP
+   success value for formerly stopped processes would mean a race condition
+   as we would get false stopped processes detection if we get too slow.
+
+   `waitid (..., WSTOPPED)' hangs the same way as WAITPID.
+
+   Signals get queued for WAITPID.  PTRACE_ATTACH (or TKILL) enqueues SIGSTOP
+   there but WAITPID may return an already pending signal.
+   Redeliver it by PTRACE_CONT, SIGxxx as otherwise it would get lost.
+   Similiar processing is being done in this file by WAIT_LWP.  */
+
+static int
+linux_ptrace_post_attach (struct lwp_info *lp)
+{
+  ptid_t ptid = lp->ptid;
+  unsigned long sig;
+
+  if (debug_linux_nat)
+    fprintf_unfiltered (gdb_stdlog,
+			"LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
+			target_pid_to_str (ptid));
+
+  lp->was_stopped = pid_is_stopped (GET_LWP (ptid));
+  if (lp->was_stopped)
+    {
+      if (kill_lwp (GET_LWP (ptid), SIGCONT) != 0)
+	perror_with_name (("kill_lwp (SIGCONT)"));
+    }
+
+  for (;;)
+    {
+      pid_t pid;
+      int status;
+
+      pid = my_waitpid (GET_LWP (ptid), &status, 0);
+      if (pid == -1 && errno == ECHILD)
+	{
+	  /* Try again with __WCLONE to check cloned processes.  */
+	  pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
+	  lp->cloned = 1;
+	}
+      gdb_assert (pid == GET_LWP (ptid));
+
+      if (debug_linux_nat)
+	{
+	  fprintf_unfiltered (gdb_stdlog,
+			      "LLAL: waitpid %s received %s\n",
+			      target_pid_to_str (ptid),
+			      status_to_str (status));
+	}
+
+      /* Check if the thread has exited.  */
+      if (WIFEXITED (status) || WIFSIGNALED (status))
+        return status;
+      gdb_assert (WIFSTOPPED (status));
+      sig = WSTOPSIG (status);
+      gdb_assert (sig != 0);
+      if (sig == SIGSTOP)
+        break;
+
+      /* As the second signal for stopped processes we send SIGSTOP.  */
+      if (lp->was_stopped && sig == SIGCONT)
+	sig = SIGSTOP;
+
+      printf_unfiltered (_("Redelivering pending %s.\n"),
+		 target_signal_to_string (target_signal_from_host (sig)));
+      if (sig == SIGSTOP)
+	{
+	  if (kill_lwp (GET_LWP (ptid), sig) != 0)
+	    perror_with_name (("kill_lwp"));
+	  /* We now must resume the inferior to get SIGSTOP delivered.  */
+	  sig = 0;
+	}
+      if (ptrace (PTRACE_CONT, GET_LWP (ptid), NULL, (void *) sig) != 0)
+	perror_with_name (("ptrace"));
+    }
+
+  return 0;
+}
+
 static void
 linux_nat_attach (char *args, int from_tty)
 {
   struct lwp_info *lp;
-  pid_t pid;
   int status;
 
   /* FIXME: We should probably accept a list of process id's, and
@@ -964,21 +1122,12 @@ linux_nat_attach (char *args, int from_t
   inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
   lp = add_lwp (inferior_ptid);
 
-  /* Make sure the initial process is stopped.  The user-level threads
-     layer might want to poke around in the inferior, and that won't
-     work if things haven't stabilized yet.  */
-  pid = my_waitpid (GET_PID (inferior_ptid), &status, 0);
-  if (pid == -1 && errno == ECHILD)
-    {
-      warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
-
-      /* Try again with __WCLONE to check cloned processes.  */
-      pid = my_waitpid (GET_PID (inferior_ptid), &status, __WCLONE);
-      lp->cloned = 1;
-    }
-
-  gdb_assert (pid == GET_PID (inferior_ptid)
-	      && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
+  status = linux_ptrace_post_attach (lp);
+  if (status != 0)
+    error (_("Program %s exited: %s\n"), target_pid_to_str (inferior_ptid),
+	   status_to_str (status));
+  if (lp->cloned)
+    warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
 
   lp->stopped = 1;
 
@@ -987,8 +1136,8 @@ linux_nat_attach (char *args, int from_t
   lp->resumed = 1;
   if (debug_linux_nat)
     {
-      fprintf_unfiltered (gdb_stdlog,
-			  "LLA: waitpid %ld, faking SIGSTOP\n", (long) pid);
+      fprintf_unfiltered (gdb_stdlog, "LLA: waitpid %d, faking SIGSTOP\n",
+			  GET_PID (inferior_ptid));
     }
 }
 
@@ -1061,6 +1210,9 @@ linux_nat_detach (char *args, int from_t
 
   trap_ptid = null_ptid;
 
+  if (lwp_list->was_stopped)
+    args = STRINGIFY (SIGSTOP);
+
   /* Destroy LWP info; it's no longer valid.  */
   init_lwp_list ();
 
@@ -1196,6 +1348,12 @@ linux_nat_resume (ptid_t ptid, int step,
       lp->stopped = 0;
     }
 
+  /* At this point, we are going to resume the inferior and if we
+     have attached to a stopped process, we no longer should leave
+     it as stopped if the user detaches.  */
+  if (!step && lp != NULL)
+    lp->was_stopped = 0;
+
   if (resume_all)
     iterate_over_lwps (resume_callback, NULL);
 
--- ./gdb/linux-nat.h	10 May 2007 21:36:00 -0000	1.18
+++ ./gdb/linux-nat.h	29 Jun 2007 22:06:05 -0000
@@ -42,6 +42,9 @@ struct lwp_info
   /* Non-zero if this LWP is stopped.  */
   int stopped;
 
+  /* Non-zero if this LWP was stopped by SIGSTOP before attaching.  */
+  int was_stopped;
+
   /* Non-zero if this LWP will be/has been resumed.  Note that an LWP
      can be marked both as stopped and resumed at the same time.  This
      happens if we try to resume an LWP that has a wait status
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-into-signal.c	29 Jun 2007 22:06:05 -0000
@@ -0,0 +1,70 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2007 Free Software Foundation, Inc.
+
+   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. */
+
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+void action(int sig, siginfo_t * info, void *uc)
+{
+  raise (SIGALRM);
+}
+
+static void *func (void *arg)
+{
+  struct sigaction act;
+
+  memset (&act, 0, sizeof(struct sigaction));
+  act.sa_sigaction = action;
+  act.sa_flags = SA_RESTART;
+  sigaction (SIGALRM, &act, 0);
+
+  raise (SIGALRM);
+
+  /* It is an upstream kernel bug (2.6.22-rc4-git7.x86_64, PREEMPT, SMP).
+     We never get here without ptrace(2) but we get while under ptrace(2).  */
+  for (;;)
+    pause ();
+
+  abort ();
+  /* NOTREACHED */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-into-signal.exp	29 Jun 2007 22:06:06 -0000
@@ -0,0 +1,176 @@
+# Copyright 2007
+
+# 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 test was created by modifying attach-stopped.exp.
+# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+set testfile "attach-into-signal"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+    # Statistically there is a better chance without giving process a nice.
+
+    set testpid [eval exec $binfile &]
+    exec sleep 2
+
+    # Run 2 passes of the test.
+    # The C file inferior stops pending its signals if a single one is lost,
+    # we test successful redelivery of the caught signal by the 2nd pass.
+
+    # linux-2.6.20.4.x86_64 had maximal attempt # 20 in 4 test runs.
+    set attempts 100
+    set attempt 1
+    set passes 1
+    while { $passes < 3 && $attempt <= $attempts } {
+
+	# Start with clean gdb
+	gdb_exit
+
+	set stoppedtry 0
+	while { $stoppedtry < 10 } {
+	    set fileid [open /proc/${testpid}/status r];
+	    gets $fileid line1;
+	    gets $fileid line2;
+	    close $fileid;
+
+	    if {![string match "*(stopped)*" $line2]} {
+		# No PASS message as we may be looping in multiple attempts.
+		break
+	    }
+	    sleep 1
+	    set stoppedtry [expr $stoppedtry + 1]
+	}
+	if { $stoppedtry >= 10 } {
+	    verbose -log $line2
+	    set test "$threadtype: process is still running on the attempt # $attempt of $attempts"
+	    break
+	}
+
+	gdb_start
+	gdb_reinitialize_dir $srcdir/$subdir
+	gdb_load ${binfile}
+
+	# No PASS message as we may be looping in multiple attempts.
+	gdb_test "set debug lin-lwp 1" "" ""
+
+	set test "$threadtype: set file (pass $passes), before attach1 to stopped process"
+	if {[gdb_test_multiple "file $binfile" $test {
+	   -re "Load new symbol table from.*y or n. $" {
+		# No PASS message as we may be looping in multiple attempts.
+		gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." ""
+	    }
+	    -re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+		# No PASS message as we may be looping in multiple attempts.
+	    }
+	}] != 0 } {
+	    break
+	}
+
+	# Main test:
+	set test "$threadtype: attach (pass $passes), pending signal catch"
+	if {[gdb_test_multiple "attach $testpid" $test {
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*Redelivering pending Alarm clock..*$gdb_prompt $" {
+		# nonthreaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+		# nonthreaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt + 1]
+	    }
+	    -re "Attaching to process $testpid.*Redelivering pending Alarm clock..*$gdb_prompt $" {
+		# threaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to process $testpid.*$gdb_prompt $" {
+		# threaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt - 1]
+	    }
+	}] != 0 } {
+	    break
+	}
+    }
+    if {$passes < 3} {
+	if {$attempt > $attempts} {
+	    unresolved $test
+	} else {
+	    fail $test
+	}
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+
+    # Continue the program - some Linux kernels need it before -9 if the
+    # process is stopped.
+    remote_exec build "kill -s CONT ${testpid}"
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case also with threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-stopped.c	29 Jun 2007 22:06:06 -0000
@@ -0,0 +1,51 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2005-2007 Free Software Foundation, Inc.
+
+   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 program is intended to be started outside of gdb, then
+   manually stopped via a signal.  */
+
+#include <stddef.h>
+#include <unistd.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+static void *func (void *arg)
+{
+  sleep (10000);  /* Ridiculous time, but we will eventually kill it.  */
+  sleep (10000);  /* Second sleep.  */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-stopped.exp	29 Jun 2007 22:06:06 -0000
@@ -0,0 +1,213 @@
+# Copyright 2005-2007
+
+# 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 test was created by modifying attach.exp.
+# This file was created by Jeff Johnston <jjohnstn@redhat.com>.
+# This file was updated by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+# This test only works on Linux
+if { ![istarget "*-*-linux-gnu*"] } {
+    return 0
+}
+
+set testfile "attach-stopped"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+#execute_anywhere "rm -f ${binfile}"
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+
+    set testpid [eval exec $binfile &]
+
+    # Avoid some race:
+    sleep 2
+
+    # Stop the program 
+    remote_exec build "kill -s STOP ${testpid}"
+
+    # Start with clean gdb
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the process by first giving its
+    # executable name via the file command, and using attach with the
+    # process ID.
+
+    set test "$threadtype: set file, before attach1 to stopped process"
+    gdb_test_multiple "file $binfile" "$test" {
+       -re "Load new symbol table from.*y or n. $" {
+	    gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." \
+		    "$test (re-read)"
+	}
+	-re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    set test "$threadtype: attach1 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*clone.*" "$threadtype: attach1 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach1 to stopped bt"
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    # Avoid some race:
+    sleep 2
+
+    set fileid [open /proc/${testpid}/status r];
+    gets $fileid line1;
+    gets $fileid line2;
+    close $fileid;
+
+    set test "$threadtype: attach1, exit leaves process stopped"
+    if {[string match "*(stopped)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # At this point, the process should still be stopped
+
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the process just by giving the
+    # process ID.
+       
+    set test "$threadtype: attach2 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*clone.*" "$threadtype: attach2 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach2 to stopped bt"
+    }
+    gdb_breakpoint [gdb_get_line_number "$threadtype: Second sleep"]
+    set test "$threadtype: attach2 continue"
+    send_gdb "continue\n"
+    gdb_expect {
+      -re "Continuing"
+	{ pass "continue ($test)" }
+      timeout
+	{ fail "continue ($test) (timeout)" }
+    }
+
+    # For this to work we must be sure to consume the "Continuing."
+    # message first, or GDB's signal handler may not be in place.
+    after 1000 {send_gdb "\003"}
+    set test "$threadtype: attach2 stop interrupt"
+    gdb_expect 10 {
+      -re "Program received signal SIGINT.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      -re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      timeout
+	{
+	  fail $test
+	}
+    }
+
+    gdb_exit
+
+    # Avoid some race:
+    sleep 2
+
+    # At this point, the process should be sleeping
+
+    set fileid2 [open /proc/${testpid}/status r];
+    gets $fileid2 line1;
+    gets $fileid2 line2;
+    close $fileid2;
+
+    set test "$threadtype: attach2, exit leaves process sleeping"
+    if {[string match "*(sleeping)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case first without threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
+
+return 0

[-- Attachment #3: ignored-detach-stop-ver0.c --]
[-- Type: text/plain, Size: 6714 bytes --]

/* Copyright 2007 Free Software Foundation, Inc.

   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. */

/* Expected runs (on 2.6.22-rc4-git7.x86_64):

   130 - after fork(): T (stopped); 0
   150 - after non-debugger waitid (..., child, ..., WSTOPPED | ...): T (stopped); 0
   158 - after PTRACE_ATTACH: T (stopped); 10368
   170 - after tkill (SIGCONT): T (tracing stop); 10368
   177 - after waitpid (child): T (tracing stop); 10368
   Workaround disabled.
   209 - after PTRACE_DETACH (SIGSTOP): S (sleeping); 0
   FAIL

   140 - after fork(): T (stopped); 0
   160 - after non-debugger waitid (..., child, ..., WSTOPPED | ...): T (stopped); 0
   168 - after PTRACE_ATTACH: T (stopped); 10406
   180 - after tkill (SIGCONT): T (tracing stop); 10406
   187 - after waitpid (child): T (tracing stop); 10406
   Workaround active!
   199 - after tkill (SIGSTOP): T (tracing stop); 10406
   204 - after PTRACE_CONT (SIG_0): T (tracing stop); 10406
   210 - after waitpid (child): T (tracing stop); 10406
   219 - after PTRACE_DETACH (SIGSTOP): T (stopped); 0
   PASS
 */

#define _GNU_SOURCE
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <ctype.h>
#include <linux/ptrace.h>

#include <asm/unistd.h>
#include <unistd.h>
#define tkill(tid, sig) syscall (__NR_tkill, (tid), (sig))

#define STATE(pid, label) state_impl (__LINE__, (pid), (label))

static const char *state_impl (int line, pid_t pid, const char *label)
{
  char buf[LINE_MAX];
  int i;
  FILE *f;
  char *state = NULL, *tracerpid = NULL;

  free (state);
  state = NULL;
  free (tracerpid);
  tracerpid = NULL;
  sleep (1);

  snprintf (buf, sizeof buf, "/proc/%d/status", (int) pid);
  f = fopen (buf, "r");
  assert (f != NULL);
  while (fgets (buf, sizeof buf, f) != NULL)
    {
      char *s;

      if (strncmp (buf, "TracerPid:", strlen ("TracerPid:")) == 0)
	{
	  for (s = buf + strlen ("TracerPid:"); isspace (*s); s++);
	  tracerpid = strdup (s);
	  s = strchr (tracerpid, '\n');
	  if (s != NULL)
	    *s = 0;
	}
      if (strncmp (buf, "State:", strlen ("State:")) == 0)
	{
	  for (s = buf + strlen ("State:"); isspace (*s); s++);
	  state = strdup (s);
	  s = strchr (state, '\n');
	  if (s != NULL)
	    *s = 0;
	}

      if (state == NULL || tracerpid == NULL)
	continue;

      printf ("%d - %s: %s; %s\n", line, label, state, tracerpid);
      i = fclose (f);
      assert (i == 0);
      return state;
    }
  assert (0);
}

static pid_t child;

static void
cleanup (void)
{
  if (child != 0)
    kill (child, SIGKILL);
}

static void
handler_fail (int signo)
{
  cleanup ();

  abort ();
}

int main (void)
{
  pid_t got_pid;
  int status;
  const char *final;

  setbuf (stdout, NULL);

  child = fork ();
  switch (child)
    {
      case -1:
	abort ();
      case 0:
	raise (SIGSTOP);
	for (;;)
	  pause ();
	/* NOTREACHED */
      default:
        break;
    }
  atexit (cleanup);
  signal (SIGABRT, handler_fail);
  STATE (child, "after fork()");
  /* `T (stopped)' here.  */

#if 1
  /* This step is not needed for the reproducibility below.
     Present here as it may happen before we - as a debugger - get started.
     Without this WAITPID -> SIGSTOP the `tkill (child, SIGCONT)' call below is
     not needed and the whole problem is no longer present.
     Only removing the `tkill (child, SIGCONT)' call is not the solution.  */
  {
    int i;
    siginfo_t siginfo;

    i = waitid (P_PID, child, &siginfo, WEXITED | WSTOPPED | WCONTINUED);
    assert (i == 0);
    assert (siginfo.si_pid == child);
    assert (siginfo.si_signo == SIGCHLD);
    assert (siginfo.si_code == CLD_STOPPED);
    assert (siginfo.si_status == SIGSTOP);
  }
  STATE (child, "after non-debugger waitid (..., child, ..., WSTOPPED | ...)");
  /* `T (stopped)' here.  */
#endif

  /* Here is a point where we - as a debugger - start to attach.  */
  errno = 0;
  ptrace (PTRACE_ATTACH, child, NULL, NULL);
  assert_perror (errno);
  STATE (child, "after PTRACE_ATTACH");
  /* Still `T (stopped)' here.  */

  /* `PTRACE_CONT, SIGSTOP' does not work in 100% cases as sometimes SIGSTOP
     gets remembered by kernel during the first PTRACE_CONT later and we get
     spurious SIGSTOP event.  Expecting the signal may get delivered to
     a different task of the thread group.
     `tkill (SIGSTOP)' has no effect in this moment (it is already stopped).  */
  errno = 0;
  tkill (child, SIGCONT);
  assert_perror (errno);
  errno = 0;
  STATE (child, "after tkill (SIGCONT)");
  /* Turned `T (tracing stop)' here.  */

  got_pid = waitpid (child, &status, 0);
  assert (got_pid == child);
  assert (WIFSTOPPED (status));
  assert (WSTOPSIG (status) == SIGCONT);
  STATE (child, "after waitpid (child)");
  /* `T (tracing stop)' here.  */

  /* Workaround.
     In this moment everything is almost fine but we need a workaround as final
     `PTRACE_DETACH, SIGSTOP' would leave the process unstopped otherwise:  */
#if 0
  puts ("Workaround active!");

  errno = 0;
  tkill (child, SIGSTOP);
  assert_perror (errno);
  STATE (child, "after tkill (SIGSTOP)");

  errno = 0;
  ptrace (PTRACE_CONT, child, NULL, NULL);
  assert_perror (errno);
  STATE (child, "after PTRACE_CONT (SIG_0)");

  got_pid = waitpid (child, &status, 0);
  assert (got_pid == child);
  assert (WIFSTOPPED (status));
  assert (WSTOPSIG (status) == SIGSTOP);
  STATE (child, "after waitpid (child)");
#else
  puts ("Workaround disabled.");
#endif

  /* We would like to leave the process stopped (`T (stopped)').  */
  errno = 0;
  ptrace (PTRACE_DETACH, child, NULL, (void *) SIGSTOP);
  assert_perror (errno);
  final = STATE (child, "after PTRACE_DETACH (SIGSTOP)");
  if (strcmp (final, "T (stopped)") == 0)
    {
      puts ("PASS");
      return 0;
    }
  if (strcmp (final, "S (sleeping)") == 0)
    {
      /* 2.6.22-rc4-git7.x86_64  */
      puts ("FAIL");
      return 1;
    }
  puts ("FAIL (unexpected)");
  return 2;
}

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2007-06-30 11:45         ` Jan Kratochvil
@ 2007-06-30 11:57           ` Eli Zaretskii
  2007-06-30 17:15             ` Jan Kratochvil
  0 siblings, 1 reply; 52+ messages in thread
From: Eli Zaretskii @ 2007-06-30 11:57 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, mark.kettenis

> Date: Sat, 30 Jun 2007 12:18:54 +0200
> From: Jan Kratochvil <jan.kratochvil@redhat.com>
> Cc: Mark Kettenis <mark.kettenis@xs4all.nl>
> 
> On Wed, 27 Jun 2007 08:52:03 +0200, Jan Kratochvil wrote:
> ...
> > OK, going to move it to linux-nat.c.
> 
> Done; various parts changed.
> 
> 
> There is one ugly workaround part (described in the comments in the patch).
> Testcase for this workaround is also attached, it appears as (another) Linux
> kernel ptrace(2) bug to me.  Is it worth workarounding it?  IMO not worth
> fixing as it is a corner case and UTRACE is going to provide alternative API.
> 
> Tested on x86_64, on upstream 2.6.22-rc4-git7 and Red Hat kernel-2.6.9-55.EL.

Will this cause any user-visible changes in behavior?  If so, I think
we should reflect it in NEWS at the very least.

Thanks.


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2007-06-30 11:57           ` Eli Zaretskii
@ 2007-06-30 17:15             ` Jan Kratochvil
  2007-06-30 18:52               ` Eli Zaretskii
       [not found]               ` <200706301852.l5UIq8ek010536@brahms.sibelius.xs4all.nl>
  0 siblings, 2 replies; 52+ messages in thread
From: Jan Kratochvil @ 2007-06-30 17:15 UTC (permalink / raw)
  To: gdb-patches; +Cc: Eli Zaretskii

[-- Attachment #1: Type: text/plain, Size: 345 bytes --]

On Sat, 30 Jun 2007 13:45:05 +0200, Eli Zaretskii wrote:
...
> Will this cause any user-visible changes in behavior?  If so, I think
> we should reflect it in NEWS at the very least.

While it is considered only as a bugfix the behavior whether the detached
process is left stopped or unstopped is definitely unclear.  Documented.


Thanks,
Jan

[-- Attachment #2: gdb-attach-signalled-ver5.1.patch --]
[-- Type: text/plain, Size: 29856 bytes --]

2007-06-30  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* linux-nat.h (struct lwp_info): New field WAS_STOPPED.
	* linux-nat.c (STRINGIFY, STRINGIFY_ARG): New macros.
	(kill_lwp): New declaration.
	(linux_ptrace_post_attach, pid_is_stopped): New function.
	(linux_child_follow_fork): New comment about WAS_STOPPED.
	(lin_lwp_attach_lwp): Variable PID removed.  Part replaced by a call to
	LINUX_PTRACE_POST_ATTACH.
	(linux_nat_attach): Likewise.
	(linux_nat_detach): Optionally stop the detached process.
	(linux_nat_resume): Clear WAS_STOPPED if appropriate.
	* NEWS: Document the new behaviour.

2007-06-30  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.threads/attach-into-signal.c, gdb.threads/attach-into-signal.exp,
	gdb.threads/attach-stopped.c, gdb.threads/attach-stopped.exp: New files.

2007-06-30  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.texinfo (Attach): Document the ATTACH and DETACH commands for
	stopped processes.  Document the messages on the seen pending signals.

--- ./gdb/NEWS	21 Jun 2007 15:18:50 -0000	1.232
+++ ./gdb/NEWS	30 Jun 2007 16:27:37 -0000
@@ -44,6 +44,9 @@ segment base addresses (rather than offs
 * The /i format now outputs any trailing branch delay slot instructions 
 immediately following the last instruction within the count specified.
 
+* Stopped processes may get attached now.  Signals being delivered at the time
+of the attach command no longer get lost.
+
 * New commands
 
 set remoteflow
--- ./gdb/linux-nat.c	16 Jun 2007 17:16:25 -0000	1.64
+++ ./gdb/linux-nat.c	29 Jun 2007 22:06:05 -0000
@@ -86,6 +86,12 @@
 #define __WALL          0x40000000 /* Wait for any child.  */
 #endif
 
+#define STRINGIFY_ARG(x) #x
+#define STRINGIFY(x) STRINGIFY_ARG (x)
+
+static int linux_ptrace_post_attach (struct lwp_info *lp);
+static int kill_lwp (int lwpid, int signo);
+
 /* The single-threaded native GNU/Linux target_ops.  We save a pointer for
    the use of the multi-threaded target.  */
 static struct target_ops *linux_ops;
@@ -502,6 +508,11 @@ linux_child_follow_fork (struct target_o
 	}
       else
 	{
+	  /* We should check LP->WAS_STOPPED and detach it stopped accordingly.
+	     In this point of code it cannot be 1 as we would not get FORK
+	     executed without CONTINUE first which resets LP->WAS_STOPPED.
+	     We would have to first TARGET_STOP and WAITPID it as with running
+	     inferior PTRACE_DETACH, SIGSTOP will ignore the signal.  */
 	  target_detach (NULL, 0);
 	}
 
@@ -884,7 +895,6 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
      to happen.  */
   if (GET_LWP (ptid) != GET_PID (ptid) && lp == NULL)
     {
-      pid_t pid;
       int status;
 
       if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
@@ -902,33 +912,17 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
       if (lp == NULL)
 	lp = add_lwp (ptid);
 
-      if (debug_linux_nat)
-	fprintf_unfiltered (gdb_stdlog,
-			    "LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
-			    target_pid_to_str (ptid));
-
-      pid = my_waitpid (GET_LWP (ptid), &status, 0);
-      if (pid == -1 && errno == ECHILD)
-	{
-	  /* Try again with __WCLONE to check cloned processes.  */
-	  pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
-	  lp->cloned = 1;
+      status = linux_ptrace_post_attach (lp);
+      if (status != 0)
+        {
+	  warning (_("Thread %s exited: %s"), target_pid_to_str (ptid),
+		   status_to_str (status));
+	  return -1;
 	}
 
-      gdb_assert (pid == GET_LWP (ptid)
-		  && WIFSTOPPED (status) && WSTOPSIG (status));
-
-      target_post_attach (pid);
+      target_post_attach (GET_LWP (ptid));
 
       lp->stopped = 1;
-
-      if (debug_linux_nat)
-	{
-	  fprintf_unfiltered (gdb_stdlog,
-			      "LLAL: waitpid %s received %s\n",
-			      target_pid_to_str (ptid),
-			      status_to_str (status));
-	}
     }
   else
     {
@@ -949,11 +943,172 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
   return 0;
 }
 
+/* Detect `T (stopped)' in `/proc/PID/status'.
+   Other states including `T (tracing stop)' are reported as false.  */
+
+static int
+pid_is_stopped (pid_t pid)
+{
+  FILE *status_file;
+  char buf[100]; 
+  int retval = 0;
+
+  snprintf (buf, sizeof (buf), "/proc/%d/status", (int) pid);
+  status_file = fopen (buf, "r");
+  if (status_file != NULL)
+    {
+      int have_state = 0;
+
+      while (fgets (buf, sizeof (buf), status_file))
+	{
+	  if (strncmp (buf, "State:", 6) == 0)
+	    {
+	      have_state = 1;
+	      break;
+	    }
+	} 
+      if (have_state && strstr (buf, "T (stopped)") != NULL)
+	retval = 1;
+      fclose (status_file);
+    }
+  return retval;
+}
+
+/* Handle the processing after PTRACE_ATTACH, the first WAITPID -> SIGSTOP.
+   Returns STATUS if the thread has exited, 0 otherwise.
+   Sets LP->WAS_STOPPED if the process was originally stopped.
+   Sets LP->CLONED if the given LWP is not the thread leader.
+
+   Scenario for a standard unstopped inferior:
+    * `S (sleeping)' or `R (running)' or similiar states.
+    * PTRACE_ATTACH is called.
+    * `S (sleeping)' (or similiar) for some while.
+    * `T (tracing stop)'.
+    * WAITPID succeeds here returning SIGSTOP (signalled by PTRACE_ATTACH).
+
+   Scenario for a formerly stopped inferior:
+    * `T (stopped)'.
+    * PTRACE_ATTACH is called.
+    * `T (stopped)' would stay indefinitely
+      Note since this moment the `TracerPid' field gets filled
+      (by PTRACE_ATTACH), it is no longer just the common `T (stopped)' state.
+       * If no one did WAITPID since sending SIGSTOP our WAITPID would return
+         SIGSTOP.  The state still would not turn to `T (tracing stop)'.
+       * Usually its original parent (before PTRACE_ATTACH was applied) already
+         did WAITPID.  The original parent already received our SIGSTOP
+         sinalled by our PTRACE_ATTACH.
+         In this case our own WAITPID would hang.  Therefore...
+    * ... we do artificial: tkill (SIGCONT);
+      `PTRACE_CONT, SIGSTOP' does not work in 100% cases as sometimes SIGSTOP
+      gets remembered by kernel during the first PTRACE_CONT later and we get
+      spurious SIGSTOP event.  Expecting the signal may get delivered to
+      a different task of the thread group.
+      `kill_lwp (SIGSTOP)' has no effect in this moment (it is already stopped).
+    * WAITPID returns the artifical SIGCONT.
+      (The possibly pending SIGSTOP gets vanished by specifically SIGCONT.)
+    * State turns `T (tracing stop)'.
+   In this moment everything is almost fine but we need a workaround as final
+   `PTRACE_DETACH, SIGSTOP' would leave the process unstopped otherwise:
+    * tkill (SIGSTOP);
+    * `PTRACE_CONT, 0'
+    * WAITPID returns the artifical SIGSTOP.
+
+   With the pending (unwaited for) SIGSTOP the artifical signal effects are:
+   kill (SIGSTOP)
+   PTRACE_ATTACH
+    /-tkill (SIGCONT), WAITPID: SIGCONT, WAITPID: hang !
+   //-tkill (SIGCONT), WAITPID: SIGCONT, PTRACE_CONT (SIG_0), WAITPID: wait (OK)
+   \\-tkill (SIGALRM), WAITPID: SIGSTOP, WAITPID: hang !
+    \-tkill (SIGALRM), WAITPID: SIGSTOP, PTRACE_CONT (SIG_0), WAITPID: SIGALRM !
+   Therefore we signal artifical SIGCONT and stop waiting after its reception.
+
+   For the detection whether the process was formerly stopped we need to
+   read `/proc/PID/status'.  `PTRACE_CONT, SIGSTOP' returns ESRCH
+   for `S (sleeping)' and succeeds for `T (stopped)' but it unfortunately
+   succeeds even for `T (tracing stop)'.  Depending on PTRACE_CONT, SIGSTOP
+   success value for formerly stopped processes would mean a race condition
+   as we would get false stopped processes detection if we get too slow.
+
+   `waitid (..., WSTOPPED)' hangs the same way as WAITPID.
+
+   Signals get queued for WAITPID.  PTRACE_ATTACH (or TKILL) enqueues SIGSTOP
+   there but WAITPID may return an already pending signal.
+   Redeliver it by PTRACE_CONT, SIGxxx as otherwise it would get lost.
+   Similiar processing is being done in this file by WAIT_LWP.  */
+
+static int
+linux_ptrace_post_attach (struct lwp_info *lp)
+{
+  ptid_t ptid = lp->ptid;
+  unsigned long sig;
+
+  if (debug_linux_nat)
+    fprintf_unfiltered (gdb_stdlog,
+			"LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
+			target_pid_to_str (ptid));
+
+  lp->was_stopped = pid_is_stopped (GET_LWP (ptid));
+  if (lp->was_stopped)
+    {
+      if (kill_lwp (GET_LWP (ptid), SIGCONT) != 0)
+	perror_with_name (("kill_lwp (SIGCONT)"));
+    }
+
+  for (;;)
+    {
+      pid_t pid;
+      int status;
+
+      pid = my_waitpid (GET_LWP (ptid), &status, 0);
+      if (pid == -1 && errno == ECHILD)
+	{
+	  /* Try again with __WCLONE to check cloned processes.  */
+	  pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
+	  lp->cloned = 1;
+	}
+      gdb_assert (pid == GET_LWP (ptid));
+
+      if (debug_linux_nat)
+	{
+	  fprintf_unfiltered (gdb_stdlog,
+			      "LLAL: waitpid %s received %s\n",
+			      target_pid_to_str (ptid),
+			      status_to_str (status));
+	}
+
+      /* Check if the thread has exited.  */
+      if (WIFEXITED (status) || WIFSIGNALED (status))
+        return status;
+      gdb_assert (WIFSTOPPED (status));
+      sig = WSTOPSIG (status);
+      gdb_assert (sig != 0);
+      if (sig == SIGSTOP)
+        break;
+
+      /* As the second signal for stopped processes we send SIGSTOP.  */
+      if (lp->was_stopped && sig == SIGCONT)
+	sig = SIGSTOP;
+
+      printf_unfiltered (_("Redelivering pending %s.\n"),
+		 target_signal_to_string (target_signal_from_host (sig)));
+      if (sig == SIGSTOP)
+	{
+	  if (kill_lwp (GET_LWP (ptid), sig) != 0)
+	    perror_with_name (("kill_lwp"));
+	  /* We now must resume the inferior to get SIGSTOP delivered.  */
+	  sig = 0;
+	}
+      if (ptrace (PTRACE_CONT, GET_LWP (ptid), NULL, (void *) sig) != 0)
+	perror_with_name (("ptrace"));
+    }
+
+  return 0;
+}
+
 static void
 linux_nat_attach (char *args, int from_tty)
 {
   struct lwp_info *lp;
-  pid_t pid;
   int status;
 
   /* FIXME: We should probably accept a list of process id's, and
@@ -964,21 +1122,12 @@ linux_nat_attach (char *args, int from_t
   inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
   lp = add_lwp (inferior_ptid);
 
-  /* Make sure the initial process is stopped.  The user-level threads
-     layer might want to poke around in the inferior, and that won't
-     work if things haven't stabilized yet.  */
-  pid = my_waitpid (GET_PID (inferior_ptid), &status, 0);
-  if (pid == -1 && errno == ECHILD)
-    {
-      warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
-
-      /* Try again with __WCLONE to check cloned processes.  */
-      pid = my_waitpid (GET_PID (inferior_ptid), &status, __WCLONE);
-      lp->cloned = 1;
-    }
-
-  gdb_assert (pid == GET_PID (inferior_ptid)
-	      && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
+  status = linux_ptrace_post_attach (lp);
+  if (status != 0)
+    error (_("Program %s exited: %s\n"), target_pid_to_str (inferior_ptid),
+	   status_to_str (status));
+  if (lp->cloned)
+    warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
 
   lp->stopped = 1;
 
@@ -987,8 +1136,8 @@ linux_nat_attach (char *args, int from_t
   lp->resumed = 1;
   if (debug_linux_nat)
     {
-      fprintf_unfiltered (gdb_stdlog,
-			  "LLA: waitpid %ld, faking SIGSTOP\n", (long) pid);
+      fprintf_unfiltered (gdb_stdlog, "LLA: waitpid %d, faking SIGSTOP\n",
+			  GET_PID (inferior_ptid));
     }
 }
 
@@ -1061,6 +1210,9 @@ linux_nat_detach (char *args, int from_t
 
   trap_ptid = null_ptid;
 
+  if (lwp_list->was_stopped)
+    args = STRINGIFY (SIGSTOP);
+
   /* Destroy LWP info; it's no longer valid.  */
   init_lwp_list ();
 
@@ -1196,6 +1348,12 @@ linux_nat_resume (ptid_t ptid, int step,
       lp->stopped = 0;
     }
 
+  /* At this point, we are going to resume the inferior and if we
+     have attached to a stopped process, we no longer should leave
+     it as stopped if the user detaches.  */
+  if (!step && lp != NULL)
+    lp->was_stopped = 0;
+
   if (resume_all)
     iterate_over_lwps (resume_callback, NULL);
 
--- ./gdb/linux-nat.h	10 May 2007 21:36:00 -0000	1.18
+++ ./gdb/linux-nat.h	29 Jun 2007 22:06:05 -0000
@@ -42,6 +42,9 @@ struct lwp_info
   /* Non-zero if this LWP is stopped.  */
   int stopped;
 
+  /* Non-zero if this LWP was stopped by SIGSTOP before attaching.  */
+  int was_stopped;
+
   /* Non-zero if this LWP will be/has been resumed.  Note that an LWP
      can be marked both as stopped and resumed at the same time.  This
      happens if we try to resume an LWP that has a wait status
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-into-signal.c	29 Jun 2007 22:06:05 -0000
@@ -0,0 +1,70 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2007 Free Software Foundation, Inc.
+
+   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. */
+
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+void action(int sig, siginfo_t * info, void *uc)
+{
+  raise (SIGALRM);
+}
+
+static void *func (void *arg)
+{
+  struct sigaction act;
+
+  memset (&act, 0, sizeof(struct sigaction));
+  act.sa_sigaction = action;
+  act.sa_flags = SA_RESTART;
+  sigaction (SIGALRM, &act, 0);
+
+  raise (SIGALRM);
+
+  /* It is an upstream kernel bug (2.6.22-rc4-git7.x86_64, PREEMPT, SMP).
+     We never get here without ptrace(2) but we get while under ptrace(2).  */
+  for (;;)
+    pause ();
+
+  abort ();
+  /* NOTREACHED */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-into-signal.exp	29 Jun 2007 22:06:06 -0000
@@ -0,0 +1,176 @@
+# Copyright 2007
+
+# 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 test was created by modifying attach-stopped.exp.
+# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+set testfile "attach-into-signal"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+    # Statistically there is a better chance without giving process a nice.
+
+    set testpid [eval exec $binfile &]
+    exec sleep 2
+
+    # Run 2 passes of the test.
+    # The C file inferior stops pending its signals if a single one is lost,
+    # we test successful redelivery of the caught signal by the 2nd pass.
+
+    # linux-2.6.20.4.x86_64 had maximal attempt # 20 in 4 test runs.
+    set attempts 100
+    set attempt 1
+    set passes 1
+    while { $passes < 3 && $attempt <= $attempts } {
+
+	# Start with clean gdb
+	gdb_exit
+
+	set stoppedtry 0
+	while { $stoppedtry < 10 } {
+	    set fileid [open /proc/${testpid}/status r];
+	    gets $fileid line1;
+	    gets $fileid line2;
+	    close $fileid;
+
+	    if {![string match "*(stopped)*" $line2]} {
+		# No PASS message as we may be looping in multiple attempts.
+		break
+	    }
+	    sleep 1
+	    set stoppedtry [expr $stoppedtry + 1]
+	}
+	if { $stoppedtry >= 10 } {
+	    verbose -log $line2
+	    set test "$threadtype: process is still running on the attempt # $attempt of $attempts"
+	    break
+	}
+
+	gdb_start
+	gdb_reinitialize_dir $srcdir/$subdir
+	gdb_load ${binfile}
+
+	# No PASS message as we may be looping in multiple attempts.
+	gdb_test "set debug lin-lwp 1" "" ""
+
+	set test "$threadtype: set file (pass $passes), before attach1 to stopped process"
+	if {[gdb_test_multiple "file $binfile" $test {
+	   -re "Load new symbol table from.*y or n. $" {
+		# No PASS message as we may be looping in multiple attempts.
+		gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." ""
+	    }
+	    -re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+		# No PASS message as we may be looping in multiple attempts.
+	    }
+	}] != 0 } {
+	    break
+	}
+
+	# Main test:
+	set test "$threadtype: attach (pass $passes), pending signal catch"
+	if {[gdb_test_multiple "attach $testpid" $test {
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*Redelivering pending Alarm clock..*$gdb_prompt $" {
+		# nonthreaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+		# nonthreaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt + 1]
+	    }
+	    -re "Attaching to process $testpid.*Redelivering pending Alarm clock..*$gdb_prompt $" {
+		# threaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to process $testpid.*$gdb_prompt $" {
+		# threaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt - 1]
+	    }
+	}] != 0 } {
+	    break
+	}
+    }
+    if {$passes < 3} {
+	if {$attempt > $attempts} {
+	    unresolved $test
+	} else {
+	    fail $test
+	}
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+
+    # Continue the program - some Linux kernels need it before -9 if the
+    # process is stopped.
+    remote_exec build "kill -s CONT ${testpid}"
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case also with threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-stopped.c	29 Jun 2007 22:06:06 -0000
@@ -0,0 +1,51 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2005-2007 Free Software Foundation, Inc.
+
+   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 program is intended to be started outside of gdb, then
+   manually stopped via a signal.  */
+
+#include <stddef.h>
+#include <unistd.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+static void *func (void *arg)
+{
+  sleep (10000);  /* Ridiculous time, but we will eventually kill it.  */
+  sleep (10000);  /* Second sleep.  */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-stopped.exp	29 Jun 2007 22:06:06 -0000
@@ -0,0 +1,213 @@
+# Copyright 2005-2007
+
+# 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 test was created by modifying attach.exp.
+# This file was created by Jeff Johnston <jjohnstn@redhat.com>.
+# This file was updated by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+# This test only works on Linux
+if { ![istarget "*-*-linux-gnu*"] } {
+    return 0
+}
+
+set testfile "attach-stopped"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+#execute_anywhere "rm -f ${binfile}"
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+
+    set testpid [eval exec $binfile &]
+
+    # Avoid some race:
+    sleep 2
+
+    # Stop the program 
+    remote_exec build "kill -s STOP ${testpid}"
+
+    # Start with clean gdb
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the process by first giving its
+    # executable name via the file command, and using attach with the
+    # process ID.
+
+    set test "$threadtype: set file, before attach1 to stopped process"
+    gdb_test_multiple "file $binfile" "$test" {
+       -re "Load new symbol table from.*y or n. $" {
+	    gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." \
+		    "$test (re-read)"
+	}
+	-re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    set test "$threadtype: attach1 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*clone.*" "$threadtype: attach1 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach1 to stopped bt"
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    # Avoid some race:
+    sleep 2
+
+    set fileid [open /proc/${testpid}/status r];
+    gets $fileid line1;
+    gets $fileid line2;
+    close $fileid;
+
+    set test "$threadtype: attach1, exit leaves process stopped"
+    if {[string match "*(stopped)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # At this point, the process should still be stopped
+
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the process just by giving the
+    # process ID.
+       
+    set test "$threadtype: attach2 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*clone.*" "$threadtype: attach2 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach2 to stopped bt"
+    }
+    gdb_breakpoint [gdb_get_line_number "$threadtype: Second sleep"]
+    set test "$threadtype: attach2 continue"
+    send_gdb "continue\n"
+    gdb_expect {
+      -re "Continuing"
+	{ pass "continue ($test)" }
+      timeout
+	{ fail "continue ($test) (timeout)" }
+    }
+
+    # For this to work we must be sure to consume the "Continuing."
+    # message first, or GDB's signal handler may not be in place.
+    after 1000 {send_gdb "\003"}
+    set test "$threadtype: attach2 stop interrupt"
+    gdb_expect 10 {
+      -re "Program received signal SIGINT.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      -re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      timeout
+	{
+	  fail $test
+	}
+    }
+
+    gdb_exit
+
+    # Avoid some race:
+    sleep 2
+
+    # At this point, the process should be sleeping
+
+    set fileid2 [open /proc/${testpid}/status r];
+    gets $fileid2 line1;
+    gets $fileid2 line2;
+    close $fileid2;
+
+    set test "$threadtype: attach2, exit leaves process sleeping"
+    if {[string match "*(sleeping)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case first without threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
+
+return 0
--- ./gdb/doc/gdb.texinfo	21 Jun 2007 15:18:50 -0000	1.417
+++ ./gdb/doc/gdb.texinfo	30 Jun 2007 16:20:38 -0000
@@ -2167,16 +2167,29 @@ can step and continue; you can modify st
 process continue running, you may use the @code{continue} command after
 attaching @value{GDBN} to the process.
 
+For a process already being stopped before the @code{attach} command executed
+you get the informational message below.  Other signals may be occasionally
+shown if they were being delivered right the time the @code{attach} command
+executed.  Such process is left still stopped after the @code{detach} command
+as long as you have not used the @code{continue} command (or similiar one)
+during your debugging session.
+
+@smallexample
+Attaching to program: /bin/sleep, process 16289
+Redelivering pending Stopped (signal).
+@end smallexample
+
 @table @code
 @kindex detach
 @item detach
 When you have finished debugging the attached process, you can use the
-@code{detach} command to release it from @value{GDBN} control.  Detaching
-the process continues its execution.  After the @code{detach} command,
-that process and @value{GDBN} become completely independent once more, and you
-are ready to @code{attach} another process or start one with @code{run}.
-@code{detach} does not repeat if you press @key{RET} again after
-executing the command.
+@code{detach} command to release it from @value{GDBN} control.  Detaching the
+process continues its execution unless it was already stopped before the
+attachment and a @code{continue} type command has not been executed.  After the
+@code{detach} command, that process and @value{GDBN} become completely
+independent once more, and you are ready to @code{attach} another process or
+start one with @code{run}.  @code{detach} does not repeat if you press
+@key{RET} again after executing the command.
 @end table
 
 If you exit @value{GDBN} or use the @code{run} command while you have an

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2007-06-30 17:15             ` Jan Kratochvil
@ 2007-06-30 18:52               ` Eli Zaretskii
       [not found]               ` <200706301852.l5UIq8ek010536@brahms.sibelius.xs4all.nl>
  1 sibling, 0 replies; 52+ messages in thread
From: Eli Zaretskii @ 2007-06-30 18:52 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

> Date: Sat, 30 Jun 2007 18:39:30 +0200
> From: Jan Kratochvil <jan.kratochvil@redhat.com>
> Cc: Eli Zaretskii <eliz@gnu.org>
> 
> While it is considered only as a bugfix the behavior whether the detached
> process is left stopped or unstopped is definitely unclear.  Documented.

Thanks.

> --- ./gdb/NEWS	21 Jun 2007 15:18:50 -0000	1.232
> +++ ./gdb/NEWS	30 Jun 2007 16:27:37 -0000
> @@ -44,6 +44,9 @@ segment base addresses (rather than offs
>  * The /i format now outputs any trailing branch delay slot instructions 
>  immediately following the last instruction within the count specified.
>  
> +* Stopped processes may get attached now.  Signals being delivered at the time
                       ^^^^^^^^^^^^^^^^^^^^
Should this be "may get attached to now"?

Other than that, I'm okay with both the NEWS and gdb.texinfo patches.


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
       [not found]               ` <200706301852.l5UIq8ek010536@brahms.sibelius.xs4all.nl>
@ 2007-07-01  3:17                 ` Eli Zaretskii
  2007-07-01  9:34                   ` Mark Kettenis
  0 siblings, 1 reply; 52+ messages in thread
From: Eli Zaretskii @ 2007-07-01  3:17 UTC (permalink / raw)
  To: Mark Kettenis; +Cc: jan.kratochvil, gdb-patches

> Date: Sat, 30 Jun 2007 20:52:08 +0200 (CEST)
> From: Mark Kettenis <mark.kettenis@xs4all.nl>
> CC: gdb-patches@sourceware.org, eliz@gnu.org
> 
> > +* Stopped processes may get attached now.  Signals being delivered at the time
> > +of the attach command no longer get lost.
> > +
> >  * New commands
> 
> I don't think this is newsworthy.

I do.

> At the very least this is misleading.  This always was supposed to
> work.  And it does on most operating systems.  I'm pretty sure it
> worked on Linux too in the past.

Then all we need is a trivial change like this:

 On GNU/Linux, stopped processes may get attached now.  Signals being
 delivered at the time of the attach command no longer get lost.


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2007-07-01  3:17                 ` Eli Zaretskii
@ 2007-07-01  9:34                   ` Mark Kettenis
  2007-07-01 10:03                     ` Jan Kratochvil
  0 siblings, 1 reply; 52+ messages in thread
From: Mark Kettenis @ 2007-07-01  9:34 UTC (permalink / raw)
  To: eliz; +Cc: mark.kettenis, jan.kratochvil, gdb-patches

> Date: Sun, 01 Jul 2007 06:17:31 +0300
> From: Eli Zaretskii <eliz@gnu.org>
> 
> > Date: Sat, 30 Jun 2007 20:52:08 +0200 (CEST)
> > From: Mark Kettenis <mark.kettenis@xs4all.nl>
> > CC: gdb-patches@sourceware.org, eliz@gnu.org
> > 
> > > +* Stopped processes may get attached now.  Signals being delivered at the time
> > > +of the attach command no longer get lost.
> > > +
> > >  * New commands
> > 
> > I don't think this is newsworthy.
> 
> I do.
> 
> > At the very least this is misleading.  This always was supposed to
> > work.  And it does on most operating systems.  I'm pretty sure it
> > worked on Linux too in the past.
> 
> Then all we need is a trivial change like this:
> 
>  On GNU/Linux, stopped processes may get attached now.  Signals being
>  delivered at the time of the attach command no longer get lost.

Fine with me.


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2007-07-01  9:34                   ` Mark Kettenis
@ 2007-07-01 10:03                     ` Jan Kratochvil
  0 siblings, 0 replies; 52+ messages in thread
From: Jan Kratochvil @ 2007-07-01 10:03 UTC (permalink / raw)
  To: Mark Kettenis; +Cc: eliz, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 372 bytes --]

On Sun, 01 Jul 2007 11:34:05 +0200, Mark Kettenis wrote:
...
> > Then all we need is a trivial change like this:
> > 
> >  On GNU/Linux, stopped processes may get attached now.  Signals being
> >  delivered at the time of the attach command no longer get lost.
> 
> Fine with me.

Fixed, reattached; although some ptrace(2) review/approval is still needed.


Regards,
Jan

[-- Attachment #2: gdb-attach-signalled-ver5.2.patch --]
[-- Type: text/plain, Size: 29870 bytes --]

2007-07-01  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* linux-nat.h (struct lwp_info): New field WAS_STOPPED.
	* linux-nat.c (STRINGIFY, STRINGIFY_ARG): New macros.
	(kill_lwp): New declaration.
	(linux_ptrace_post_attach, pid_is_stopped): New function.
	(linux_child_follow_fork): New comment about WAS_STOPPED.
	(lin_lwp_attach_lwp): Variable PID removed.  Part replaced by a call to
	LINUX_PTRACE_POST_ATTACH.
	(linux_nat_attach): Likewise.
	(linux_nat_detach): Optionally stop the detached process.
	(linux_nat_resume): Clear WAS_STOPPED if appropriate.
	* NEWS: Document the new behaviour.

2007-06-30  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.threads/attach-into-signal.c, gdb.threads/attach-into-signal.exp,
	gdb.threads/attach-stopped.c, gdb.threads/attach-stopped.exp: New files.

2007-06-30  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.texinfo (Attach): Document the ATTACH and DETACH commands for
	stopped processes.  Document the messages on the seen pending signals.

--- ./gdb/NEWS	21 Jun 2007 15:18:50 -0000	1.232
+++ ./gdb/NEWS	30 Jun 2007 16:27:37 -0000
@@ -44,6 +44,9 @@ segment base addresses (rather than offs
 * The /i format now outputs any trailing branch delay slot instructions 
 immediately following the last instruction within the count specified.
 
+* On GNU/Linux, stopped processes may get attached to now.  Signals being
+delivered at the time of the attach command no longer get lost.
+
 * New commands
 
 set remoteflow
--- ./gdb/linux-nat.c	16 Jun 2007 17:16:25 -0000	1.64
+++ ./gdb/linux-nat.c	29 Jun 2007 22:06:05 -0000
@@ -86,6 +86,12 @@
 #define __WALL          0x40000000 /* Wait for any child.  */
 #endif
 
+#define STRINGIFY_ARG(x) #x
+#define STRINGIFY(x) STRINGIFY_ARG (x)
+
+static int linux_ptrace_post_attach (struct lwp_info *lp);
+static int kill_lwp (int lwpid, int signo);
+
 /* The single-threaded native GNU/Linux target_ops.  We save a pointer for
    the use of the multi-threaded target.  */
 static struct target_ops *linux_ops;
@@ -502,6 +508,11 @@ linux_child_follow_fork (struct target_o
 	}
       else
 	{
+	  /* We should check LP->WAS_STOPPED and detach it stopped accordingly.
+	     In this point of code it cannot be 1 as we would not get FORK
+	     executed without CONTINUE first which resets LP->WAS_STOPPED.
+	     We would have to first TARGET_STOP and WAITPID it as with running
+	     inferior PTRACE_DETACH, SIGSTOP will ignore the signal.  */
 	  target_detach (NULL, 0);
 	}
 
@@ -884,7 +895,6 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
      to happen.  */
   if (GET_LWP (ptid) != GET_PID (ptid) && lp == NULL)
     {
-      pid_t pid;
       int status;
 
       if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
@@ -902,33 +912,17 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
       if (lp == NULL)
 	lp = add_lwp (ptid);
 
-      if (debug_linux_nat)
-	fprintf_unfiltered (gdb_stdlog,
-			    "LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
-			    target_pid_to_str (ptid));
-
-      pid = my_waitpid (GET_LWP (ptid), &status, 0);
-      if (pid == -1 && errno == ECHILD)
-	{
-	  /* Try again with __WCLONE to check cloned processes.  */
-	  pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
-	  lp->cloned = 1;
+      status = linux_ptrace_post_attach (lp);
+      if (status != 0)
+        {
+	  warning (_("Thread %s exited: %s"), target_pid_to_str (ptid),
+		   status_to_str (status));
+	  return -1;
 	}
 
-      gdb_assert (pid == GET_LWP (ptid)
-		  && WIFSTOPPED (status) && WSTOPSIG (status));
-
-      target_post_attach (pid);
+      target_post_attach (GET_LWP (ptid));
 
       lp->stopped = 1;
-
-      if (debug_linux_nat)
-	{
-	  fprintf_unfiltered (gdb_stdlog,
-			      "LLAL: waitpid %s received %s\n",
-			      target_pid_to_str (ptid),
-			      status_to_str (status));
-	}
     }
   else
     {
@@ -949,11 +943,172 @@ lin_lwp_attach_lwp (ptid_t ptid, int ver
   return 0;
 }
 
+/* Detect `T (stopped)' in `/proc/PID/status'.
+   Other states including `T (tracing stop)' are reported as false.  */
+
+static int
+pid_is_stopped (pid_t pid)
+{
+  FILE *status_file;
+  char buf[100]; 
+  int retval = 0;
+
+  snprintf (buf, sizeof (buf), "/proc/%d/status", (int) pid);
+  status_file = fopen (buf, "r");
+  if (status_file != NULL)
+    {
+      int have_state = 0;
+
+      while (fgets (buf, sizeof (buf), status_file))
+	{
+	  if (strncmp (buf, "State:", 6) == 0)
+	    {
+	      have_state = 1;
+	      break;
+	    }
+	} 
+      if (have_state && strstr (buf, "T (stopped)") != NULL)
+	retval = 1;
+      fclose (status_file);
+    }
+  return retval;
+}
+
+/* Handle the processing after PTRACE_ATTACH, the first WAITPID -> SIGSTOP.
+   Returns STATUS if the thread has exited, 0 otherwise.
+   Sets LP->WAS_STOPPED if the process was originally stopped.
+   Sets LP->CLONED if the given LWP is not the thread leader.
+
+   Scenario for a standard unstopped inferior:
+    * `S (sleeping)' or `R (running)' or similiar states.
+    * PTRACE_ATTACH is called.
+    * `S (sleeping)' (or similiar) for some while.
+    * `T (tracing stop)'.
+    * WAITPID succeeds here returning SIGSTOP (signalled by PTRACE_ATTACH).
+
+   Scenario for a formerly stopped inferior:
+    * `T (stopped)'.
+    * PTRACE_ATTACH is called.
+    * `T (stopped)' would stay indefinitely
+      Note since this moment the `TracerPid' field gets filled
+      (by PTRACE_ATTACH), it is no longer just the common `T (stopped)' state.
+       * If no one did WAITPID since sending SIGSTOP our WAITPID would return
+         SIGSTOP.  The state still would not turn to `T (tracing stop)'.
+       * Usually its original parent (before PTRACE_ATTACH was applied) already
+         did WAITPID.  The original parent already received our SIGSTOP
+         sinalled by our PTRACE_ATTACH.
+         In this case our own WAITPID would hang.  Therefore...
+    * ... we do artificial: tkill (SIGCONT);
+      `PTRACE_CONT, SIGSTOP' does not work in 100% cases as sometimes SIGSTOP
+      gets remembered by kernel during the first PTRACE_CONT later and we get
+      spurious SIGSTOP event.  Expecting the signal may get delivered to
+      a different task of the thread group.
+      `kill_lwp (SIGSTOP)' has no effect in this moment (it is already stopped).
+    * WAITPID returns the artifical SIGCONT.
+      (The possibly pending SIGSTOP gets vanished by specifically SIGCONT.)
+    * State turns `T (tracing stop)'.
+   In this moment everything is almost fine but we need a workaround as final
+   `PTRACE_DETACH, SIGSTOP' would leave the process unstopped otherwise:
+    * tkill (SIGSTOP);
+    * `PTRACE_CONT, 0'
+    * WAITPID returns the artifical SIGSTOP.
+
+   With the pending (unwaited for) SIGSTOP the artifical signal effects are:
+   kill (SIGSTOP)
+   PTRACE_ATTACH
+    /-tkill (SIGCONT), WAITPID: SIGCONT, WAITPID: hang !
+   //-tkill (SIGCONT), WAITPID: SIGCONT, PTRACE_CONT (SIG_0), WAITPID: wait (OK)
+   \\-tkill (SIGALRM), WAITPID: SIGSTOP, WAITPID: hang !
+    \-tkill (SIGALRM), WAITPID: SIGSTOP, PTRACE_CONT (SIG_0), WAITPID: SIGALRM !
+   Therefore we signal artifical SIGCONT and stop waiting after its reception.
+
+   For the detection whether the process was formerly stopped we need to
+   read `/proc/PID/status'.  `PTRACE_CONT, SIGSTOP' returns ESRCH
+   for `S (sleeping)' and succeeds for `T (stopped)' but it unfortunately
+   succeeds even for `T (tracing stop)'.  Depending on PTRACE_CONT, SIGSTOP
+   success value for formerly stopped processes would mean a race condition
+   as we would get false stopped processes detection if we get too slow.
+
+   `waitid (..., WSTOPPED)' hangs the same way as WAITPID.
+
+   Signals get queued for WAITPID.  PTRACE_ATTACH (or TKILL) enqueues SIGSTOP
+   there but WAITPID may return an already pending signal.
+   Redeliver it by PTRACE_CONT, SIGxxx as otherwise it would get lost.
+   Similiar processing is being done in this file by WAIT_LWP.  */
+
+static int
+linux_ptrace_post_attach (struct lwp_info *lp)
+{
+  ptid_t ptid = lp->ptid;
+  unsigned long sig;
+
+  if (debug_linux_nat)
+    fprintf_unfiltered (gdb_stdlog,
+			"LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
+			target_pid_to_str (ptid));
+
+  lp->was_stopped = pid_is_stopped (GET_LWP (ptid));
+  if (lp->was_stopped)
+    {
+      if (kill_lwp (GET_LWP (ptid), SIGCONT) != 0)
+	perror_with_name (("kill_lwp (SIGCONT)"));
+    }
+
+  for (;;)
+    {
+      pid_t pid;
+      int status;
+
+      pid = my_waitpid (GET_LWP (ptid), &status, 0);
+      if (pid == -1 && errno == ECHILD)
+	{
+	  /* Try again with __WCLONE to check cloned processes.  */
+	  pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
+	  lp->cloned = 1;
+	}
+      gdb_assert (pid == GET_LWP (ptid));
+
+      if (debug_linux_nat)
+	{
+	  fprintf_unfiltered (gdb_stdlog,
+			      "LLAL: waitpid %s received %s\n",
+			      target_pid_to_str (ptid),
+			      status_to_str (status));
+	}
+
+      /* Check if the thread has exited.  */
+      if (WIFEXITED (status) || WIFSIGNALED (status))
+        return status;
+      gdb_assert (WIFSTOPPED (status));
+      sig = WSTOPSIG (status);
+      gdb_assert (sig != 0);
+      if (sig == SIGSTOP)
+        break;
+
+      /* As the second signal for stopped processes we send SIGSTOP.  */
+      if (lp->was_stopped && sig == SIGCONT)
+	sig = SIGSTOP;
+
+      printf_unfiltered (_("Redelivering pending %s.\n"),
+		 target_signal_to_string (target_signal_from_host (sig)));
+      if (sig == SIGSTOP)
+	{
+	  if (kill_lwp (GET_LWP (ptid), sig) != 0)
+	    perror_with_name (("kill_lwp"));
+	  /* We now must resume the inferior to get SIGSTOP delivered.  */
+	  sig = 0;
+	}
+      if (ptrace (PTRACE_CONT, GET_LWP (ptid), NULL, (void *) sig) != 0)
+	perror_with_name (("ptrace"));
+    }
+
+  return 0;
+}
+
 static void
 linux_nat_attach (char *args, int from_tty)
 {
   struct lwp_info *lp;
-  pid_t pid;
   int status;
 
   /* FIXME: We should probably accept a list of process id's, and
@@ -964,21 +1122,12 @@ linux_nat_attach (char *args, int from_t
   inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
   lp = add_lwp (inferior_ptid);
 
-  /* Make sure the initial process is stopped.  The user-level threads
-     layer might want to poke around in the inferior, and that won't
-     work if things haven't stabilized yet.  */
-  pid = my_waitpid (GET_PID (inferior_ptid), &status, 0);
-  if (pid == -1 && errno == ECHILD)
-    {
-      warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
-
-      /* Try again with __WCLONE to check cloned processes.  */
-      pid = my_waitpid (GET_PID (inferior_ptid), &status, __WCLONE);
-      lp->cloned = 1;
-    }
-
-  gdb_assert (pid == GET_PID (inferior_ptid)
-	      && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
+  status = linux_ptrace_post_attach (lp);
+  if (status != 0)
+    error (_("Program %s exited: %s\n"), target_pid_to_str (inferior_ptid),
+	   status_to_str (status));
+  if (lp->cloned)
+    warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
 
   lp->stopped = 1;
 
@@ -987,8 +1136,8 @@ linux_nat_attach (char *args, int from_t
   lp->resumed = 1;
   if (debug_linux_nat)
     {
-      fprintf_unfiltered (gdb_stdlog,
-			  "LLA: waitpid %ld, faking SIGSTOP\n", (long) pid);
+      fprintf_unfiltered (gdb_stdlog, "LLA: waitpid %d, faking SIGSTOP\n",
+			  GET_PID (inferior_ptid));
     }
 }
 
@@ -1061,6 +1210,9 @@ linux_nat_detach (char *args, int from_t
 
   trap_ptid = null_ptid;
 
+  if (lwp_list->was_stopped)
+    args = STRINGIFY (SIGSTOP);
+
   /* Destroy LWP info; it's no longer valid.  */
   init_lwp_list ();
 
@@ -1196,6 +1348,12 @@ linux_nat_resume (ptid_t ptid, int step,
       lp->stopped = 0;
     }
 
+  /* At this point, we are going to resume the inferior and if we
+     have attached to a stopped process, we no longer should leave
+     it as stopped if the user detaches.  */
+  if (!step && lp != NULL)
+    lp->was_stopped = 0;
+
   if (resume_all)
     iterate_over_lwps (resume_callback, NULL);
 
--- ./gdb/linux-nat.h	10 May 2007 21:36:00 -0000	1.18
+++ ./gdb/linux-nat.h	29 Jun 2007 22:06:05 -0000
@@ -42,6 +42,9 @@ struct lwp_info
   /* Non-zero if this LWP is stopped.  */
   int stopped;
 
+  /* Non-zero if this LWP was stopped by SIGSTOP before attaching.  */
+  int was_stopped;
+
   /* Non-zero if this LWP will be/has been resumed.  Note that an LWP
      can be marked both as stopped and resumed at the same time.  This
      happens if we try to resume an LWP that has a wait status
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-into-signal.c	29 Jun 2007 22:06:05 -0000
@@ -0,0 +1,70 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2007 Free Software Foundation, Inc.
+
+   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. */
+
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+void action(int sig, siginfo_t * info, void *uc)
+{
+  raise (SIGALRM);
+}
+
+static void *func (void *arg)
+{
+  struct sigaction act;
+
+  memset (&act, 0, sizeof(struct sigaction));
+  act.sa_sigaction = action;
+  act.sa_flags = SA_RESTART;
+  sigaction (SIGALRM, &act, 0);
+
+  raise (SIGALRM);
+
+  /* It is an upstream kernel bug (2.6.22-rc4-git7.x86_64, PREEMPT, SMP).
+     We never get here without ptrace(2) but we get while under ptrace(2).  */
+  for (;;)
+    pause ();
+
+  abort ();
+  /* NOTREACHED */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-into-signal.exp	29 Jun 2007 22:06:06 -0000
@@ -0,0 +1,176 @@
+# Copyright 2007
+
+# 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 test was created by modifying attach-stopped.exp.
+# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+set testfile "attach-into-signal"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+    # Statistically there is a better chance without giving process a nice.
+
+    set testpid [eval exec $binfile &]
+    exec sleep 2
+
+    # Run 2 passes of the test.
+    # The C file inferior stops pending its signals if a single one is lost,
+    # we test successful redelivery of the caught signal by the 2nd pass.
+
+    # linux-2.6.20.4.x86_64 had maximal attempt # 20 in 4 test runs.
+    set attempts 100
+    set attempt 1
+    set passes 1
+    while { $passes < 3 && $attempt <= $attempts } {
+
+	# Start with clean gdb
+	gdb_exit
+
+	set stoppedtry 0
+	while { $stoppedtry < 10 } {
+	    set fileid [open /proc/${testpid}/status r];
+	    gets $fileid line1;
+	    gets $fileid line2;
+	    close $fileid;
+
+	    if {![string match "*(stopped)*" $line2]} {
+		# No PASS message as we may be looping in multiple attempts.
+		break
+	    }
+	    sleep 1
+	    set stoppedtry [expr $stoppedtry + 1]
+	}
+	if { $stoppedtry >= 10 } {
+	    verbose -log $line2
+	    set test "$threadtype: process is still running on the attempt # $attempt of $attempts"
+	    break
+	}
+
+	gdb_start
+	gdb_reinitialize_dir $srcdir/$subdir
+	gdb_load ${binfile}
+
+	# No PASS message as we may be looping in multiple attempts.
+	gdb_test "set debug lin-lwp 1" "" ""
+
+	set test "$threadtype: set file (pass $passes), before attach1 to stopped process"
+	if {[gdb_test_multiple "file $binfile" $test {
+	   -re "Load new symbol table from.*y or n. $" {
+		# No PASS message as we may be looping in multiple attempts.
+		gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." ""
+	    }
+	    -re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+		# No PASS message as we may be looping in multiple attempts.
+	    }
+	}] != 0 } {
+	    break
+	}
+
+	# Main test:
+	set test "$threadtype: attach (pass $passes), pending signal catch"
+	if {[gdb_test_multiple "attach $testpid" $test {
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*Redelivering pending Alarm clock..*$gdb_prompt $" {
+		# nonthreaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+		# nonthreaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt + 1]
+	    }
+	    -re "Attaching to process $testpid.*Redelivering pending Alarm clock..*$gdb_prompt $" {
+		# threaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to process $testpid.*$gdb_prompt $" {
+		# threaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt - 1]
+	    }
+	}] != 0 } {
+	    break
+	}
+    }
+    if {$passes < 3} {
+	if {$attempt > $attempts} {
+	    unresolved $test
+	} else {
+	    fail $test
+	}
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+
+    # Continue the program - some Linux kernels need it before -9 if the
+    # process is stopped.
+    remote_exec build "kill -s CONT ${testpid}"
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case also with threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-stopped.c	29 Jun 2007 22:06:06 -0000
@@ -0,0 +1,51 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2005-2007 Free Software Foundation, Inc.
+
+   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 program is intended to be started outside of gdb, then
+   manually stopped via a signal.  */
+
+#include <stddef.h>
+#include <unistd.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+static void *func (void *arg)
+{
+  sleep (10000);  /* Ridiculous time, but we will eventually kill it.  */
+  sleep (10000);  /* Second sleep.  */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.threads/attach-stopped.exp	29 Jun 2007 22:06:06 -0000
@@ -0,0 +1,213 @@
+# Copyright 2005-2007
+
+# 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 test was created by modifying attach.exp.
+# This file was created by Jeff Johnston <jjohnstn@redhat.com>.
+# This file was updated by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+# This test only works on Linux
+if { ![istarget "*-*-linux-gnu*"] } {
+    return 0
+}
+
+set testfile "attach-stopped"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+#execute_anywhere "rm -f ${binfile}"
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+
+    set testpid [eval exec $binfile &]
+
+    # Avoid some race:
+    sleep 2
+
+    # Stop the program 
+    remote_exec build "kill -s STOP ${testpid}"
+
+    # Start with clean gdb
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the process by first giving its
+    # executable name via the file command, and using attach with the
+    # process ID.
+
+    set test "$threadtype: set file, before attach1 to stopped process"
+    gdb_test_multiple "file $binfile" "$test" {
+       -re "Load new symbol table from.*y or n. $" {
+	    gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." \
+		    "$test (re-read)"
+	}
+	-re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    set test "$threadtype: attach1 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*clone.*" "$threadtype: attach1 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach1 to stopped bt"
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    # Avoid some race:
+    sleep 2
+
+    set fileid [open /proc/${testpid}/status r];
+    gets $fileid line1;
+    gets $fileid line2;
+    close $fileid;
+
+    set test "$threadtype: attach1, exit leaves process stopped"
+    if {[string match "*(stopped)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # At this point, the process should still be stopped
+
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the process just by giving the
+    # process ID.
+       
+    set test "$threadtype: attach2 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*clone.*" "$threadtype: attach2 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach2 to stopped bt"
+    }
+    gdb_breakpoint [gdb_get_line_number "$threadtype: Second sleep"]
+    set test "$threadtype: attach2 continue"
+    send_gdb "continue\n"
+    gdb_expect {
+      -re "Continuing"
+	{ pass "continue ($test)" }
+      timeout
+	{ fail "continue ($test) (timeout)" }
+    }
+
+    # For this to work we must be sure to consume the "Continuing."
+    # message first, or GDB's signal handler may not be in place.
+    after 1000 {send_gdb "\003"}
+    set test "$threadtype: attach2 stop interrupt"
+    gdb_expect 10 {
+      -re "Program received signal SIGINT.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      -re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      timeout
+	{
+	  fail $test
+	}
+    }
+
+    gdb_exit
+
+    # Avoid some race:
+    sleep 2
+
+    # At this point, the process should be sleeping
+
+    set fileid2 [open /proc/${testpid}/status r];
+    gets $fileid2 line1;
+    gets $fileid2 line2;
+    close $fileid2;
+
+    set test "$threadtype: attach2, exit leaves process sleeping"
+    if {[string match "*(sleeping)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case first without threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
+
+return 0
--- ./gdb/doc/gdb.texinfo	1 Jul 2007 09:13:05 -0000	1.418
+++ ./gdb/doc/gdb.texinfo	1 Jul 2007 09:55:14 -0000
@@ -2167,16 +2167,29 @@ can step and continue; you can modify st
 process continue running, you may use the @code{continue} command after
 attaching @value{GDBN} to the process.
 
+For a process already being stopped before the @code{attach} command executed
+you get the informational message below.  Other signals may be occasionally
+shown if they were being delivered right the time the @code{attach} command
+executed.  Such process is left still stopped after the @code{detach} command
+as long as you have not used the @code{continue} command (or similiar one)
+during your debugging session.
+
+@smallexample
+Attaching to program: /bin/sleep, process 16289
+Redelivering pending Stopped (signal).
+@end smallexample
+
 @table @code
 @kindex detach
 @item detach
 When you have finished debugging the attached process, you can use the
-@code{detach} command to release it from @value{GDBN} control.  Detaching
-the process continues its execution.  After the @code{detach} command,
-that process and @value{GDBN} become completely independent once more, and you
-are ready to @code{attach} another process or start one with @code{run}.
-@code{detach} does not repeat if you press @key{RET} again after
-executing the command.
+@code{detach} command to release it from @value{GDBN} control.  Detaching the
+process continues its execution unless it was already stopped before the
+attachment and a @code{continue} type command has not been executed.  After the
+@code{detach} command, that process and @value{GDBN} become completely
+independent once more, and you are ready to @code{attach} another process or
+start one with @code{run}.  @code{detach} does not repeat if you press
+@key{RET} again after executing the command.
 @end table
 
 If you exit @value{GDBN} while you have an attached process, you detach

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-09-28 11:43                       ` Jan Kratochvil
@ 2008-09-28 14:58                         ` Daniel Jacobowitz
  0 siblings, 0 replies; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-09-28 14:58 UTC (permalink / raw)
  To: Jan Kratochvil
  Cc: Andreas Schwab, Roland McGrath, Doug Evans, GDB Patches, mark.kettenis

On Sun, Sep 28, 2008 at 01:42:49PM +0200, Jan Kratochvil wrote:
> I did not expect LinuxThreads would be still in use with kernel-2.6+.
> kernel-2.4 is not supported by the testcase due to missing /proc/PID/task/ .
> Assuming there would be just one more task (thread) listed.

Sadly, it is - for instance, we're working on an m68k NPTL port, but
it isn't quite done yet.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-09-26 13:00                     ` Daniel Jacobowitz
@ 2008-09-28 11:43                       ` Jan Kratochvil
  2008-09-28 14:58                         ` Daniel Jacobowitz
  0 siblings, 1 reply; 52+ messages in thread
From: Jan Kratochvil @ 2008-09-28 11:43 UTC (permalink / raw)
  To: Daniel Jacobowitz
  Cc: Andreas Schwab, Roland McGrath, Doug Evans, GDB Patches, mark.kettenis

[committed]

On Fri, 26 Sep 2008 15:00:09 +0200, Daniel Jacobowitz wrote:
> > +set tids [lsort -integer [split $tids]]
> > +if {[llength $tids] != 3 || [lindex $tids 0] != $testpid} {
> > +    verbose -log "Invalid TIDs <$tids> for PID $testpid"
> > +    fail "Invalid TIDs found"
> > +    remote_exec build "kill -9 ${testpid}"
> > +    return -1
> > +}
> > +set testpid2 [lindex $tids 2]
> 
> I think this will FAIL on LinuxThreads but if I ever run that
> configuration again, I'll fix it.

I did not expect LinuxThreads would be still in use with kernel-2.6+.
kernel-2.4 is not supported by the testcase due to missing /proc/PID/task/ .
Assuming there would be just one more task (thread) listed.


Regards,
Jan


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-09-26  3:52                   ` Jan Kratochvil
@ 2008-09-26 13:00                     ` Daniel Jacobowitz
  2008-09-28 11:43                       ` Jan Kratochvil
  0 siblings, 1 reply; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-09-26 13:00 UTC (permalink / raw)
  To: Jan Kratochvil
  Cc: Andreas Schwab, Roland McGrath, Doug Evans, GDB Patches, mark.kettenis

On Fri, Sep 26, 2008 at 05:51:11AM +0200, Jan Kratochvil wrote:
> 2008-09-26  Jan Kratochvil  <jan.kratochvil@redhat.com>
> 
> 	* gdb.threads/attachstop-mt.exp: Note a real testcase name this
> 	testcase is derived from.  Fix racy dependency on an expected PID
> 	number.  No longer support the testcase on Linux kernel 2.4.x.

OK.

> +set tids [lsort -integer [split $tids]]
> +if {[llength $tids] != 3 || [lindex $tids 0] != $testpid} {
> +    verbose -log "Invalid TIDs <$tids> for PID $testpid"
> +    fail "Invalid TIDs found"
> +    remote_exec build "kill -9 ${testpid}"
> +    return -1
> +}
> +set testpid2 [lindex $tids 2]

I think this will FAIL on LinuxThreads but if I ever run that
configuration again, I'll fix it.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-09-24 12:46                 ` Andreas Schwab
@ 2008-09-26  3:52                   ` Jan Kratochvil
  2008-09-26 13:00                     ` Daniel Jacobowitz
  0 siblings, 1 reply; 52+ messages in thread
From: Jan Kratochvil @ 2008-09-26  3:52 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: Roland McGrath, Doug Evans, GDB Patches, mark.kettenis

[-- Attachment #1: Type: text/plain, Size: 210 bytes --]

On Wed, 24 Sep 2008 14:45:46 +0200, Andreas Schwab wrote:
> > +set testpid2 [expr $testpid + 2]
> 
> There is no reason that this is required to work.

Yes, thanks for the notice.  OK to commit?


Regards,
Jan

[-- Attachment #2: gdb-attachstop-mt.patch --]
[-- Type: text/plain, Size: 2185 bytes --]

2008-09-26  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.threads/attachstop-mt.exp: Note a real testcase name this
	testcase is derived from.  Fix racy dependency on an expected PID
	number.  No longer support the testcase on Linux kernel 2.4.x.

--- gdb/testsuite/gdb.threads/attachstop-mt.exp	1 May 2008 18:50:14 -0000	1.1
+++ gdb/testsuite/gdb.threads/attachstop-mt.exp	26 Sep 2008 03:45:31 -0000
@@ -14,9 +14,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-# This test was created by modifying gdb.threads/attachstop.
-# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
-# Regression for: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=197584
+# This test is based on gdb.base/attach.exp with modifications by Jeff Johnston
+# <jjohnstn@redhat.com> and Jan Kratochvil <jan.kratochvil@redhat.com>.
 
 # This test only works on Linux
 if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
@@ -52,17 +51,25 @@ set testpid [eval exec $binfile &]
 # No race
 sleep 2
 
-# Do not: set testpid2 [expr $testpid + 1]
-# as it will not exist on Red Hat 2.6.9-34.0.2.ELsmp
-set testpid2 [expr $testpid + 2]
-
-set status2 /proc/${testpid}/task/${testpid2}/status
-if {[expr ! [file exists $status2]]} {
-  # kernel-2.4
-  set status2 /proc/${testpid2}/status
+# The testcase has three threads, find some other thread TID for $testpid2.
+set tids [exec sh -c "echo /proc/$testpid/task/*"]
+regsub -all /proc/$testpid/task/ $tids {} tids
+if {$tids == "*"} {
+    unresolved "/proc/PID/task is not supported (kernel-2.4?)"
+    remote_exec build "kill -9 ${testpid}"
+    return -1
 }
+set tids [lsort -integer [split $tids]]
+if {[llength $tids] != 3 || [lindex $tids 0] != $testpid} {
+    verbose -log "Invalid TIDs <$tids> for PID $testpid"
+    fail "Invalid TIDs found"
+    remote_exec build "kill -9 ${testpid}"
+    return -1
+}
+set testpid2 [lindex $tids 2]
 
 # Initial sanity test it is normally sleeping
+set status2 /proc/${testpid}/task/${testpid2}/status
 set fileid0 [open $status2 r];
 gets $fileid0 line1;
 gets $fileid0 line2;

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-14 15:31               ` Daniel Jacobowitz
  2008-04-15 22:14                 ` Jan Kratochvil
@ 2008-09-24 12:46                 ` Andreas Schwab
  2008-09-26  3:52                   ` Jan Kratochvil
  1 sibling, 1 reply; 52+ messages in thread
From: Andreas Schwab @ 2008-09-24 12:46 UTC (permalink / raw)
  To: Roland McGrath; +Cc: Jan Kratochvil, Doug Evans, GDB Patches, mark.kettenis

Daniel Jacobowitz <drow@false.org> writes:

> +# Start the program running and then wait for a bit, to be sure
> +# that it can be attached to.
> +
> +set testpid [eval exec $binfile &]
> +
> +# No race
> +sleep 2
> +
> +# Do not: set testpid2 [expr $testpid + 1]
> +# as it will not exist on Red Hat 2.6.9-34.0.2.ELsmp
> +set testpid2 [expr $testpid + 2]
> +
> +set status2 /proc/${testpid}/task/${testpid2}/status
> +if {[expr ! [file exists $status2]]} {
> +  # kernel-2.4
> +  set status2 /proc/${testpid2}/status
> +}

There is no reason that this is required to work.  Even if pids are
allocated in increasing order there can be arbitrary other processes
created between the start of the process and the creation of its
threads.

Andreas.

-- 
Andreas Schwab, SuSE Labs, schwab@suse.de
SuSE Linux Products GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
PGP key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-07-05  8:48                     ` Jan Kratochvil
@ 2008-07-05 13:48                       ` Daniel Jacobowitz
  0 siblings, 0 replies; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-07-05 13:48 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

On Sat, Jul 05, 2008 at 10:47:18AM +0200, Jan Kratochvil wrote:
> On Thu, 01 May 2008 20:50:18 +0200, Daniel Jacobowitz wrote:
> > Thanks, I've merged it.  Here's what I checked in.
> 
> Please note the ChangeLog has no entry for this commit:
> 	http://sourceware.org/ml/gdb-cvs/2008-05/msg00010.html

Thanks, I fixed it.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-05-01 18:50                   ` Daniel Jacobowitz
@ 2008-07-05  8:48                     ` Jan Kratochvil
  2008-07-05 13:48                       ` Daniel Jacobowitz
  0 siblings, 1 reply; 52+ messages in thread
From: Jan Kratochvil @ 2008-07-05  8:48 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: gdb-patches

On Thu, 01 May 2008 20:50:18 +0200, Daniel Jacobowitz wrote:
> Thanks, I've merged it.  Here's what I checked in.

Please note the ChangeLog has no entry for this commit:
	http://sourceware.org/ml/gdb-cvs/2008-05/msg00010.html

Regards,
Jan


> 2008-05-01  Daniel Jacobowitz  <dan@codesourcery.com>
> 	    Pedro Alves  <pedro@codesourcery.com>
> 
> 	Based on work by Jan Kratochvil <jan.kratochvil@redhat.com> and Jeff
>  	Johnston <jjohnstn@redhat.com>.
> 
> 	* NEWS: Mention attach to stopped process fix.
> 	* infcmd.c (detach_command, disconnect_command): Discard the thread
> 	list.
> 	* infrun.c (handle_inferior_event): Do not ignore non-SIGSTOP while
> 	attaching.  Use signal_stop_state.
> 	(signal_stop_state): Check stop_soon.
> 	* linux-nat.c (kill_lwp): Declare earlier.
> 	(pid_is_stopped, linux_nat_post_attach_wait): New.
> 	(lin_lwp_attach_lwp): Use linux_nat_post_attach_wait.  Update
> 	comments.
> 	(linux_nat_attach): Use linux_nat_post_attach_wait.
> 	(detach_callback, linux_nat_detach): Improve handling for signalled
> 	processes.
> 	(linux_nat_pid_to_str): Always print out the LWP ID if it differs
> 	from the process ID.
> 	* Makefile.in (infcmd.o): Update.
> 
> 2008-05-01  Jan Kratochvil  <jan.kratochvil@redhat.com>
> 	    Daniel Jacobowitz  <dan@codesourcery.com>
> 
> 	* gdb.threads/attach-into-signal.c, gdb.threads/attach-into-signal.exp,
> 	gdb.threads/attach-stopped.c, gdb.threads/attach-stopped.exp,
> 	gdb.threads/attachstop-mt.c, gdb.threads/attachstop-mt.exp: New.
> 
> Index: Makefile.in
> Index: NEWS
> Index: infcmd.c
> Index: infrun.c
> Index: linux-nat.c
> Index: testsuite/gdb.threads/attach-into-signal.c
> Index: testsuite/gdb.threads/attach-into-signal.exp
> Index: testsuite/gdb.threads/attach-stopped.c
> Index: testsuite/gdb.threads/attach-stopped.exp
> Index: testsuite/gdb.threads/attachstop-mt.c
> Index: testsuite/gdb.threads/attachstop-mt.exp


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-15 22:14                 ` Jan Kratochvil
@ 2008-05-01 18:50                   ` Daniel Jacobowitz
  2008-07-05  8:48                     ` Jan Kratochvil
  0 siblings, 1 reply; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-05-01 18:50 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Roland McGrath, Doug Evans, GDB Patches, mark.kettenis

On Tue, Apr 15, 2008 at 10:31:53PM +0200, Jan Kratochvil wrote:
> Hi,
> 
> please apply the attached patch to the testcase as this workaround is no longer
> required with your GDB code patch variant.

Thanks, I've merged it.  Here's what I checked in.

-- 
Daniel Jacobowitz
CodeSourcery

2008-05-01  Daniel Jacobowitz  <dan@codesourcery.com>
	    Pedro Alves  <pedro@codesourcery.com>

	Based on work by Jan Kratochvil <jan.kratochvil@redhat.com> and Jeff
 	Johnston <jjohnstn@redhat.com>.

	* NEWS: Mention attach to stopped process fix.
	* infcmd.c (detach_command, disconnect_command): Discard the thread
	list.
	* infrun.c (handle_inferior_event): Do not ignore non-SIGSTOP while
	attaching.  Use signal_stop_state.
	(signal_stop_state): Check stop_soon.
	* linux-nat.c (kill_lwp): Declare earlier.
	(pid_is_stopped, linux_nat_post_attach_wait): New.
	(lin_lwp_attach_lwp): Use linux_nat_post_attach_wait.  Update
	comments.
	(linux_nat_attach): Use linux_nat_post_attach_wait.
	(detach_callback, linux_nat_detach): Improve handling for signalled
	processes.
	(linux_nat_pid_to_str): Always print out the LWP ID if it differs
	from the process ID.
	* Makefile.in (infcmd.o): Update.

2008-05-01  Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Daniel Jacobowitz  <dan@codesourcery.com>

	* gdb.threads/attach-into-signal.c, gdb.threads/attach-into-signal.exp,
	gdb.threads/attach-stopped.c, gdb.threads/attach-stopped.exp,
	gdb.threads/attachstop-mt.c, gdb.threads/attachstop-mt.exp: New.

Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1006
diff -u -p -r1.1006 Makefile.in
--- Makefile.in	30 Apr 2008 21:19:10 -0000	1.1006
+++ Makefile.in	1 May 2008 18:47:54 -0000
@@ -2316,7 +2316,7 @@ infcmd.o: infcmd.c $(defs_h) $(gdb_strin
 	$(objfiles_h) $(completer_h) $(ui_out_h) $(event_top_h) \
 	$(parser_defs_h) $(regcache_h) $(reggroups_h) $(block_h) \
 	$(solib_h) $(gdb_assert_h) $(observer_h) $(target_descriptions_h) \
-	$(user_regs_h) $(exceptions_h) $(cli_decode_h)
+	$(user_regs_h) $(exceptions_h) $(cli_decode_h) $(gdbthread_h)
 inf-loop.o: inf-loop.c $(defs_h) $(inferior_h) $(target_h) $(event_loop_h) \
 	$(event_top_h) $(inf_loop_h) $(remote_h) $(exceptions_h) \
 	$(language_h)
Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.268
diff -u -p -r1.268 NEWS
--- NEWS	24 Apr 2008 10:21:44 -0000	1.268
+++ NEWS	1 May 2008 18:47:54 -0000
@@ -4,11 +4,11 @@
 *** Changes since GDB 6.8
 
 * GDB now supports multiple function calling conventions according to the
-  DWARF-2 DW_AT_calling_convention function attribute.
+DWARF-2 DW_AT_calling_convention function attribute.
   
 * The SH target utilizes the aforementioned change to distinguish between gcc
-  and Renesas calling convention.  It also adds the new CLI commands
-  `set/show sh calling-convention'.
+and Renesas calling convention.  It also adds the new CLI commands
+`set/show sh calling-convention'.
 
 * GDB can now read compressed debug sections, as produced by GNU gold
 with the --compress-debug-sections=zlib flag.
@@ -21,6 +21,10 @@ which will be allocated using malloc lat
 * The qXfer:libraries:read remote procotol packet now allows passing a
 list of section offsets.
 
+* On GNU/Linux, GDB can now attach to stopped processes.  Several race
+conditions handling signals delivered during attach or thread creation
+have also been fixed.
+
 * New features in the GDB remote stub, gdbserver
 
   - The "--wrapper" command-line argument tells gdbserver to use a
Index: infcmd.c
===================================================================
RCS file: /cvs/src/src/gdb/infcmd.c,v
retrieving revision 1.178
diff -u -p -r1.178 infcmd.c
--- infcmd.c	24 Apr 2008 11:43:14 -0000	1.178
+++ infcmd.c	1 May 2008 18:47:55 -0000
@@ -50,6 +50,7 @@
 #include "user-regs.h"
 #include "exceptions.h"
 #include "cli/cli-decode.h"
+#include "gdbthread.h"
 
 /* Functions exported for general use, in inferior.h: */
 
@@ -2050,6 +2051,7 @@ detach_command (char *args, int from_tty
   dont_repeat ();		/* Not for the faint of heart.  */
   target_detach (args, from_tty);
   no_shared_libraries (NULL, from_tty);
+  init_thread_list ();
   if (deprecated_detach_hook)
     deprecated_detach_hook ();
 }
@@ -2068,6 +2070,7 @@ disconnect_command (char *args, int from
   dont_repeat ();		/* Not for the faint of heart */
   target_disconnect (args, from_tty);
   no_shared_libraries (NULL, from_tty);
+  init_thread_list ();
   if (deprecated_detach_hook)
     deprecated_detach_hook ();
 }
Index: infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.271
diff -u -p -r1.271 infrun.c
--- infrun.c	25 Apr 2008 14:50:10 -0000	1.271
+++ infrun.c	1 May 2008 18:47:55 -0000
@@ -1946,13 +1946,15 @@ handle_inferior_event (struct execution_
 
       /* This originates from attach_command().  We need to overwrite
          the stop_signal here, because some kernels don't ignore a
-         SIGSTOP in a subsequent ptrace(PTRACE_SONT,SOGSTOP) call.
-         See more comments in inferior.h.  */
-      if (stop_soon == STOP_QUIETLY_NO_SIGSTOP)
+         SIGSTOP in a subsequent ptrace(PTRACE_CONT,SIGSTOP) call.
+         See more comments in inferior.h.  On the other hand, if we
+	 get a non-SIGSTOP, report it to the user - assume the backend
+	 will handle the SIGSTOP if it should show up later.  */
+      if (stop_soon == STOP_QUIETLY_NO_SIGSTOP
+	  && stop_signal == TARGET_SIGNAL_STOP)
 	{
 	  stop_stepping (ecs);
-	  if (stop_signal == TARGET_SIGNAL_STOP)
-	    stop_signal = TARGET_SIGNAL_0;
+	  stop_signal = TARGET_SIGNAL_0;
 	  return;
 	}
 
@@ -2023,7 +2025,7 @@ process_event_stop_test:
 	  target_terminal_ours_for_output ();
 	  print_stop_reason (SIGNAL_RECEIVED, stop_signal);
 	}
-      if (signal_stop[stop_signal])
+      if (signal_stop_state (stop_signal))
 	{
 	  stop_stepping (ecs);
 	  return;
@@ -3276,7 +3278,9 @@ hook_stop_stub (void *cmd)
 int
 signal_stop_state (int signo)
 {
-  return signal_stop[signo];
+  /* Always stop on signals if we're just gaining control of the
+     program.  */
+  return signal_stop[signo] || stop_soon != NO_STOP_QUIETLY;
 }
 
 int
Index: linux-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/linux-nat.c,v
retrieving revision 1.80
diff -u -p -r1.80 linux-nat.c
--- linux-nat.c	25 Mar 2008 12:26:21 -0000	1.80
+++ linux-nat.c	1 May 2008 18:47:55 -0000
@@ -50,6 +50,30 @@
 #include "event-loop.h"
 #include "event-top.h"
 
+/* Note on this file's use of signals:
+
+   We stop threads by sending a SIGSTOP.  The use of SIGSTOP instead
+   of another signal is not entirely significant; we just need for a
+   signal to be delivered, so that we can intercept it.  SIGSTOP's
+   advantage is that it can not be blocked.  A disadvantage is that it
+   is not a real-time signal, so it can only be queued once; we do not
+   keep track of other sources of SIGSTOP.
+
+   Two other signals that can't be blocked are SIGCONT and SIGKILL.
+   But we can't use them, because they have special behavior when the
+   signal is generated - not when it is delivered.  SIGCONT resumes
+   the entire thread group and SIGKILL kills the entire thread group.
+
+   A delivered SIGSTOP would stop the entire thread group, not just the
+   thread we tkill'd.  But we never let the SIGSTOP deliver; we always
+   intercept and cancel it (by PTRACE_CONT without passing SIGSTOP).
+
+   We could use a real-time signal instead.  This would solve those
+   problems; we could use PTRACE_GETSIGINFO to locate the specific
+   stop signals sent by GDB.  But we would still have to have some
+   support for SIGSTOP, since PTRACE_ATTACH generates it, and there
+   are races with trying to find a signal that is not blocked.  */
+
 #ifndef O_LARGEFILE
 #define O_LARGEFILE 0
 #endif
@@ -186,6 +210,7 @@ static void linux_nat_async (void (*call
 			     (enum inferior_event_type event_type, void *context),
 			     void *context);
 static int linux_nat_async_mask (int mask);
+static int kill_lwp (int lwpid, int signo);
 
 /* Captures the result of a successful waitpid call, along with the
    options used in that call.  */
@@ -1010,10 +1035,103 @@ exit_lwp (struct lwp_info *lp)
   delete_lwp (lp->ptid);
 }
 
-/* Attach to the LWP specified by PID.  If VERBOSE is non-zero, print
-   a message telling the user that a new LWP has been added to the
-   process.  Return 0 if successful or -1 if the new LWP could not
-   be attached.  */
+/* Detect `T (stopped)' in `/proc/PID/status'.
+   Other states including `T (tracing stop)' are reported as false.  */
+
+static int
+pid_is_stopped (pid_t pid)
+{
+  FILE *status_file;
+  char buf[100];
+  int retval = 0;
+
+  snprintf (buf, sizeof (buf), "/proc/%d/status", (int) pid);
+  status_file = fopen (buf, "r");
+  if (status_file != NULL)
+    {
+      int have_state = 0;
+
+      while (fgets (buf, sizeof (buf), status_file))
+	{
+	  if (strncmp (buf, "State:", 6) == 0)
+	    {
+	      have_state = 1;
+	      break;
+	    }
+	}
+      if (have_state && strstr (buf, "T (stopped)") != NULL)
+	retval = 1;
+      fclose (status_file);
+    }
+  return retval;
+}
+
+/* Wait for the LWP specified by LP, which we have just attached to.
+   Returns a wait status for that LWP, to cache.  */
+
+static int
+linux_nat_post_attach_wait (ptid_t ptid, int first, int *cloned,
+			    int *signalled)
+{
+  pid_t new_pid, pid = GET_LWP (ptid);
+  int status;
+
+  if (pid_is_stopped (pid))
+    {
+      if (debug_linux_nat)
+	fprintf_unfiltered (gdb_stdlog,
+			    "LNPAW: Attaching to a stopped process\n");
+
+      /* The process is definitely stopped.  It is in a job control
+	 stop, unless the kernel predates the TASK_STOPPED /
+	 TASK_TRACED distinction, in which case it might be in a
+	 ptrace stop.  Make sure it is in a ptrace stop; from there we
+	 can kill it, signal it, et cetera.
+
+         First make sure there is a pending SIGSTOP.  Since we are
+	 already attached, the process can not transition from stopped
+	 to running without a PTRACE_CONT; so we know this signal will
+	 go into the queue.  The SIGSTOP generated by PTRACE_ATTACH is
+	 probably already in the queue (unless this kernel is old
+	 enough to use TASK_STOPPED for ptrace stops); but since SIGSTOP
+	 is not an RT signal, it can only be queued once.  */
+      kill_lwp (pid, SIGSTOP);
+
+      /* Finally, resume the stopped process.  This will deliver the SIGSTOP
+	 (or a higher priority signal, just like normal PTRACE_ATTACH).  */
+      ptrace (PTRACE_CONT, pid, 0, 0);
+    }
+
+  /* Make sure the initial process is stopped.  The user-level threads
+     layer might want to poke around in the inferior, and that won't
+     work if things haven't stabilized yet.  */
+  new_pid = my_waitpid (pid, &status, 0);
+  if (new_pid == -1 && errno == ECHILD)
+    {
+      if (first)
+	warning (_("%s is a cloned process"), target_pid_to_str (ptid));
+
+      /* Try again with __WCLONE to check cloned processes.  */
+      new_pid = my_waitpid (pid, &status, __WCLONE);
+      *cloned = 1;
+    }
+
+  gdb_assert (pid == new_pid && WIFSTOPPED (status));
+
+  if (WSTOPSIG (status) != SIGSTOP)
+    {
+      *signalled = 1;
+      if (debug_linux_nat)
+	fprintf_unfiltered (gdb_stdlog,
+			    "LNPAW: Received %s after attaching\n",
+			    status_to_str (status));
+    }
+
+  return status;
+}
+
+/* Attach to the LWP specified by PID.  Return 0 if successful or -1
+   if the new LWP could not be attached.  */
 
 int
 lin_lwp_attach_lwp (ptid_t ptid)
@@ -1036,9 +1154,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
      to happen.  */
   if (GET_LWP (ptid) != GET_PID (ptid) && lp == NULL)
     {
-      pid_t pid;
-      int status;
-      int cloned = 0;
+      int status, cloned = 0, signalled = 0;
 
       if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
 	{
@@ -1057,24 +1173,18 @@ lin_lwp_attach_lwp (ptid_t ptid)
 			    "LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
 			    target_pid_to_str (ptid));
 
-      pid = my_waitpid (GET_LWP (ptid), &status, 0);
-      if (pid == -1 && errno == ECHILD)
+      status = linux_nat_post_attach_wait (ptid, 0, &cloned, &signalled);
+      lp = add_lwp (ptid);
+      lp->stopped = 1;
+      lp->cloned = cloned;
+      lp->signalled = signalled;
+      if (WSTOPSIG (status) != SIGSTOP)
 	{
-	  /* Try again with __WCLONE to check cloned processes.  */
-	  pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
-	  cloned = 1;
+	  lp->resumed = 1;
+	  lp->status = status;
 	}
 
-      gdb_assert (pid == GET_LWP (ptid)
-		  && WIFSTOPPED (status) && WSTOPSIG (status));
-
-      if (lp == NULL)
-	lp = add_lwp (ptid);
-      lp->cloned = cloned;
-
-      target_post_attach (pid);
-
-      lp->stopped = 1;
+      target_post_attach (GET_LWP (lp->ptid));
 
       if (debug_linux_nat)
 	{
@@ -1133,10 +1243,7 @@ static void
 linux_nat_attach (char *args, int from_tty)
 {
   struct lwp_info *lp;
-  pid_t pid;
   int status;
-  int cloned = 0;
-  int options = 0;
 
   /* FIXME: We should probably accept a list of process id's, and
      attach all of them.  */
@@ -1151,54 +1258,69 @@ linux_nat_attach (char *args, int from_t
       sigdelset (&suspend_mask, SIGCHLD);
     }
 
-  /* Make sure the initial process is stopped.  The user-level threads
-     layer might want to poke around in the inferior, and that won't
-     work if things haven't stabilized yet.  */
-  pid = my_waitpid (GET_PID (inferior_ptid), &status, options);
-  if (pid == -1 && errno == ECHILD)
-    {
-      warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
-
-      /* Try again with __WCLONE to check cloned processes.  */
-      options = __WCLONE;
-      pid = my_waitpid (GET_PID (inferior_ptid), &status, options);
-      cloned = 1;
-    }
-
-  gdb_assert (pid == GET_PID (inferior_ptid)
-	      && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
-
   /* Add the initial process as the first LWP to the list.  */
   inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
   lp = add_lwp (inferior_ptid);
-  lp->cloned = cloned;
+
+  status = linux_nat_post_attach_wait (lp->ptid, 1, &lp->cloned,
+				       &lp->signalled);
+  lp->stopped = 1;
 
   /* If this process is not using thread_db, then we still don't
      detect any other threads, but add at least this one.  */
   add_thread_silent (lp->ptid);
 
-  lp->stopped = 1;
+  /* Save the wait status to report later.  */
   lp->resumed = 1;
+  if (debug_linux_nat)
+    fprintf_unfiltered (gdb_stdlog,
+			"LNA: waitpid %ld, saving status %s\n",
+			(long) GET_PID (lp->ptid), status_to_str (status));
 
   if (!target_can_async_p ())
-    {
-      /* Fake the SIGSTOP that core GDB expects.  */
-      lp->status = W_STOPCODE (SIGSTOP);
-      if (debug_linux_nat)
-	fprintf_unfiltered (gdb_stdlog,
-			    "LNA: waitpid %ld, faking SIGSTOP\n", (long) pid);
-    }
+    lp->status = status;
   else
     {
       /* We already waited for this LWP, so put the wait result on the
 	 pipe.  The event loop will wake up and gets us to handling
 	 this event.  */
-      linux_nat_event_pipe_push (pid, status, options);
+      linux_nat_event_pipe_push (GET_PID (lp->ptid), status,
+				 lp->cloned ? __WCLONE : 0);
       /* Register in the event loop.  */
       target_async (inferior_event_handler, 0);
     }
 }
 
+/* Get pending status of LP.  */
+static int
+get_pending_status (struct lwp_info *lp, int *status)
+{
+  struct target_waitstatus last;
+  ptid_t last_ptid;
+
+  get_last_target_status (&last_ptid, &last);
+
+  /* If this lwp is the ptid that GDB is processing an event from, the
+     signal will be in stop_signal.  Otherwise, in all-stop + sync
+     mode, we may cache pending events in lp->status while trying to
+     stop all threads (see stop_wait_callback).  In async mode, the
+     events are always cached in waitpid_queue.  */
+
+  *status = 0;
+  if (GET_LWP (lp->ptid) == GET_LWP (last_ptid))
+    {
+      if (stop_signal != TARGET_SIGNAL_0
+	  && signal_pass_state (stop_signal))
+	*status = W_STOPCODE (target_signal_to_host (stop_signal));
+    }
+  else if (target_can_async_p ())
+    queued_waitpid (GET_LWP (lp->ptid), status, __WALL);
+  else
+    *status = lp->status;
+
+  return 0;
+}
+
 static int
 detach_callback (struct lwp_info *lp, void *data)
 {
@@ -1209,40 +1331,30 @@ detach_callback (struct lwp_info *lp, vo
 			strsignal (WSTOPSIG (lp->status)),
 			target_pid_to_str (lp->ptid));
 
-  while (lp->signalled && lp->stopped)
+  /* If there is a pending SIGSTOP, get rid of it.  */
+  if (lp->signalled)
     {
-      errno = 0;
-      if (ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0,
-		  WSTOPSIG (lp->status)) < 0)
-	error (_("Can't continue %s: %s"), target_pid_to_str (lp->ptid),
-	       safe_strerror (errno));
-
       if (debug_linux_nat)
 	fprintf_unfiltered (gdb_stdlog,
-			    "DC:  PTRACE_CONTINUE (%s, 0, %s) (OK)\n",
-			    target_pid_to_str (lp->ptid),
-			    status_to_str (lp->status));
+			    "DC: Sending SIGCONT to %s\n",
+			    target_pid_to_str (lp->ptid));
 
-      lp->stopped = 0;
+      kill_lwp (GET_LWP (lp->ptid), SIGCONT);
       lp->signalled = 0;
-      lp->status = 0;
-      /* FIXME drow/2003-08-26: There was a call to stop_wait_callback
-	 here.  But since lp->signalled was cleared above,
-	 stop_wait_callback didn't do anything; the process was left
-	 running.  Shouldn't we be waiting for it to stop?
-	 I've removed the call, since stop_wait_callback now does do
-	 something when called with lp->signalled == 0.  */
-
-      gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status));
     }
 
   /* We don't actually detach from the LWP that has an id equal to the
      overall process id just yet.  */
   if (GET_LWP (lp->ptid) != GET_PID (lp->ptid))
     {
+      int status = 0;
+
+      /* Pass on any pending signal for this LWP.  */
+      get_pending_status (lp, &status);
+
       errno = 0;
       if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
-		  WSTOPSIG (lp->status)) < 0)
+		  WSTOPSIG (status)) < 0)
 	error (_("Can't detach %s: %s"), target_pid_to_str (lp->ptid),
 	       safe_strerror (errno));
 
@@ -1252,7 +1364,6 @@ detach_callback (struct lwp_info *lp, vo
 			    target_pid_to_str (lp->ptid),
 			    strsignal (WSTOPSIG (lp->status)));
 
-      drain_queued_events (GET_LWP (lp->ptid));
       delete_lwp (lp->ptid);
     }
 
@@ -1263,6 +1374,9 @@ static void
 linux_nat_detach (char *args, int from_tty)
 {
   int pid;
+  int status;
+  enum target_signal sig;
+
   if (target_can_async_p ())
     linux_nat_async (NULL, 0);
 
@@ -1271,6 +1385,21 @@ linux_nat_detach (char *args, int from_t
   /* Only the initial process should be left right now.  */
   gdb_assert (num_lwps == 1);
 
+  /* Pass on any pending signal for the last LWP.  */
+  if ((args == NULL || *args == '\0')
+      && get_pending_status (lwp_list, &status) != -1
+      && WIFSTOPPED (status))
+    {
+      /* Put the signal number in ARGS so that inf_ptrace_detach will
+	 pass it along with PTRACE_DETACH.  */
+      args = alloca (8);
+      sprintf (args, "%d", (int) WSTOPSIG (status));
+      fprintf_unfiltered (gdb_stdlog,
+			  "LND: Sending signal %s to %s\n",
+			  args,
+			  target_pid_to_str (lwp_list->ptid));
+    }
+
   trap_ptid = null_ptid;
 
   /* Destroy LWP info; it's no longer valid.  */
@@ -2848,7 +2977,9 @@ linux_nat_pid_to_str (ptid_t ptid)
 {
   static char buf[64];
 
-  if (lwp_list && lwp_list->next && is_lwp (ptid))
+  if (is_lwp (ptid)
+      && ((lwp_list && lwp_list->next)
+	  || GET_PID (ptid) != GET_LWP (ptid)))
     {
       snprintf (buf, sizeof (buf), "LWP %ld", GET_LWP (ptid));
       return buf;
@@ -4205,4 +4336,3 @@ lin_thread_get_thread_signals (sigset_t 
   /* ... except during a sigsuspend.  */
   sigdelset (&suspend_mask, cancel);
 }
-
Index: testsuite/gdb.threads/attach-into-signal.c
===================================================================
RCS file: testsuite/gdb.threads/attach-into-signal.c
diff -N testsuite/gdb.threads/attach-into-signal.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attach-into-signal.c	1 May 2008 18:47:55 -0000
@@ -0,0 +1,67 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2008 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+void action(int sig, siginfo_t * info, void *uc)
+{
+  raise (SIGALRM);
+}
+
+static void *func (void *arg)
+{
+  struct sigaction act;
+
+  memset (&act, 0, sizeof(struct sigaction));
+  act.sa_sigaction = action;
+  act.sa_flags = SA_RESTART;
+  sigaction (SIGALRM, &act, 0);
+
+  raise (SIGALRM);
+
+  /* We must not get past this point, either in a free standing or debugged
+     state.  */
+
+  abort ();
+  /* NOTREACHED */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
Index: testsuite/gdb.threads/attach-into-signal.exp
===================================================================
RCS file: testsuite/gdb.threads/attach-into-signal.exp
diff -N testsuite/gdb.threads/attach-into-signal.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attach-into-signal.exp	1 May 2008 18:47:55 -0000
@@ -0,0 +1,168 @@
+# Copyright 2008
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This test was created by modifying attach-stopped.exp.
+# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+# This test only works on Linux
+if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
+    continue
+}
+
+set testfile "attach-into-signal"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+    # Statistically there is a better chance without giving process a nice.
+
+    set testpid [eval exec $binfile &]
+    exec sleep 2
+
+    # Run 2 passes of the test.
+    # The C file inferior stops pending its signals if a single one is lost,
+    # we test successful redelivery of the caught signal by the 2nd pass.
+
+    # linux-2.6.20.4.x86_64 had maximal attempt # 20 in 4 test runs.
+    set attempts 100
+    set attempt 1
+    set passes 1
+    while { $passes < 3 && $attempt <= $attempts } {
+	set stoppedtry 0
+	while { $stoppedtry < 10 } {
+	    if [catch {open /proc/${testpid}/status r} fileid] {
+		set stoppedtry 10
+		break
+	    }
+	    gets $fileid line1;
+	    gets $fileid line2;
+	    close $fileid;
+
+	    if {![string match "*(stopped)*" $line2]} {
+		# No PASS message as we may be looping in multiple attempts.
+		break
+	    }
+	    sleep 1
+	    set stoppedtry [expr $stoppedtry + 1]
+	}
+	if { $stoppedtry >= 10 } {
+	    verbose -log $line2
+	    set test "$threadtype: process is still running on the attempt # $attempt of $attempts"
+	    break
+	}
+
+	# Main test:
+	set test "$threadtype: attach (pass $passes), pending signal catch"
+	if {[gdb_test_multiple "attach $testpid" $test {
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*Received Alarm clock.*$gdb_prompt $" {
+		# nonthreaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+		# nonthreaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt + 1]
+	    }
+	    -re "Attaching to process $testpid.*Received Alarm clock.*$gdb_prompt $" {
+		# threaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to process $testpid.*$gdb_prompt $" {
+		# threaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt - 1]
+	    }
+	}] != 0 } {
+	    break
+	}
+
+	gdb_test "detach" "Detaching from.*" ""
+    }
+    if {$passes < 3} {
+	if {$attempt > $attempts} {
+	    unresolved $test
+	} else {
+	    fail $test
+	}
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+
+    # Continue the program - some Linux kernels need it before -9 if the
+    # process is stopped.
+    remote_exec build "kill -s CONT ${testpid}"
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# Start with clean gdb
+gdb_exit
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+gdb_test "set debug lin-lwp 1" "" ""
+
+corefunc nonthreaded
+
+# build the test case also with threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+gdb_test "set debug lin-lwp 1" "" ""
+
+corefunc threaded
Index: testsuite/gdb.threads/attach-stopped.c
===================================================================
RCS file: testsuite/gdb.threads/attach-stopped.c
diff -N testsuite/gdb.threads/attach-stopped.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attach-stopped.c	1 May 2008 18:47:55 -0000
@@ -0,0 +1,50 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2008 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* This program is intended to be started outside of gdb, then
+   manually stopped via a signal.  */
+
+#include <stddef.h>
+#include <unistd.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+static void *func (void *arg)
+{
+  sleep (10000);  /* Ridiculous time, but we will eventually kill it.  */
+  sleep (10000);  /* Second sleep.  */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
Index: testsuite/gdb.threads/attach-stopped.exp
===================================================================
RCS file: testsuite/gdb.threads/attach-stopped.exp
diff -N testsuite/gdb.threads/attach-stopped.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attach-stopped.exp	1 May 2008 18:47:55 -0000
@@ -0,0 +1,157 @@
+# Copyright 2008
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This test was created by modifying attach.exp.
+# This file was created by Jeff Johnston <jjohnstn@redhat.com>.
+# This file was updated by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+# This test only works on Linux
+if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
+    continue
+}
+
+set testfile "attach-stopped"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+#execute_anywhere "rm -f ${binfile}"
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+
+    set testpid [eval exec $binfile &]
+
+    # Avoid some race:
+    sleep 2
+
+    # Stop the program 
+    remote_exec build "kill -s STOP ${testpid}"
+
+    # Start with clean gdb
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the stopped process.
+       
+    set test "$threadtype: attach2 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    # ".*sleep.*clone.*" would fail on s390x as bt stops at START_THREAD there.
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*start_thread.*" "$threadtype: attach2 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach2 to stopped bt"
+    }
+    # This breakpoint is there for old/non-x86 kernels not restarting syscalls.
+    gdb_breakpoint [gdb_get_line_number "Second sleep"]
+    set test "$threadtype: attach2 continue"
+    send_gdb "continue\n"
+    gdb_expect {
+      -re "Continuing"
+	{ pass "continue ($test)" }
+      timeout
+	{ fail "continue ($test) (timeout)" }
+    }
+
+    # For this to work we must be sure to consume the "Continuing."
+    # message first, or GDB's signal handler may not be in place.
+    after 1000 {send_gdb "\003"}
+    set test "$threadtype: attach2 stop interrupt"
+    gdb_expect 10 {
+      -re "Program received signal SIGINT.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      -re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      timeout
+	{
+	  fail $test
+	}
+    }
+
+    gdb_exit
+
+    # Avoid some race:
+    sleep 2
+
+    # At this point, the process should be sleeping
+
+    if [catch {open /proc/${testpid}/status r} fileid2] {
+	set line2 "NOTFOUND"
+    } else {
+	gets $fileid2 line1;
+	gets $fileid2 line2;
+	close $fileid2;
+    }
+
+    set test "$threadtype: attach2, exit leaves process sleeping"
+    if {[string match "*(sleeping)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case first without threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
+
+return 0
Index: testsuite/gdb.threads/attachstop-mt.c
===================================================================
RCS file: testsuite/gdb.threads/attachstop-mt.c
diff -N testsuite/gdb.threads/attachstop-mt.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attachstop-mt.c	1 May 2008 18:47:55 -0000
@@ -0,0 +1,55 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2008 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* This program is intended to be started outside of gdb, then
+   manually stopped via a signal.  */
+
+#include <unistd.h>
+#include <pthread.h>
+#include <stdio.h>
+
+/* Red Hat BZ PR 197584.c */
+
+void *func (void *arg)
+{
+  sleep (10000);  /* Ridiculous time, but we will eventually kill it.  */
+  sleep (10000);	/* RHEL3U8 kernel-2.4.21-47.EL will cut the sleep time.  */
+
+  return NULL;	/* thread-sleep */
+}
+
+int main ()
+{
+  pthread_t t1,t2;
+  int ret;
+
+  ret = pthread_create (&t1, NULL, func, NULL);
+  if (ret)
+    fprintf(stderr, "pthread_create(): %s", strerror (ret));
+  ret = pthread_create (&t2, NULL, func, NULL);
+  if (ret)
+    fprintf(stderr, "pthread_create(): %s", strerror (ret));
+
+  ret = pthread_join (t1, NULL);
+  if (ret)	/* first-join */
+    fprintf(stderr, "pthread_join(): %s", strerror (ret));
+  ret = pthread_join (t2, NULL);
+  if (ret)
+    fprintf(stderr, "pthread_join(): %s", strerror (ret));
+
+  return 0;
+}
Index: testsuite/gdb.threads/attachstop-mt.exp
===================================================================
RCS file: testsuite/gdb.threads/attachstop-mt.exp
diff -N testsuite/gdb.threads/attachstop-mt.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attachstop-mt.exp	1 May 2008 18:47:55 -0000
@@ -0,0 +1,260 @@
+# Copyright 2008
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This test was created by modifying gdb.threads/attachstop.
+# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
+# Regression for: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=197584
+
+# This test only works on Linux
+if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
+    continue
+}
+
+set testfile "attachstop-mt"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+#execute_anywhere "rm -f ${binfile}"
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+# build the test case
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail."
+}
+
+if [get_compiler_info ${binfile}] {
+    return -1
+}
+
+# Start the program running and then wait for a bit, to be sure
+# that it can be attached to.
+
+set testpid [eval exec $binfile &]
+
+# No race
+sleep 2
+
+# Do not: set testpid2 [expr $testpid + 1]
+# as it will not exist on Red Hat 2.6.9-34.0.2.ELsmp
+set testpid2 [expr $testpid + 2]
+
+set status2 /proc/${testpid}/task/${testpid2}/status
+if {[expr ! [file exists $status2]]} {
+  # kernel-2.4
+  set status2 /proc/${testpid2}/status
+}
+
+# Initial sanity test it is normally sleeping
+set fileid0 [open $status2 r];
+gets $fileid0 line1;
+gets $fileid0 line2;
+close $fileid0;
+
+set test "attach0, initial sanity check of the sleeping state"
+if {[string match "*(sleeping)*" $line2]} {
+  pass $test
+} else {
+  fail $test
+}
+
+# Sttach and detach to test it will not become stopped
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+set test "attach0 to sleeping"
+gdb_test_multiple "attach $testpid" "$test" {
+    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+        pass "$test"
+    }
+}
+
+gdb_test "gcore /dev/null" ".*aved corefile.*" "attach0 to sleeping gcore invocation"
+
+gdb_test "thread 2" ".*witching to thread 2 .*" "attach0 to sleeping switch thread"
+
+gdb_test "bt" ".*sleep.*func.*" "attach0 to sleeping bt"
+
+# Exit and detach the process.
+
+gdb_exit
+
+# No race
+sleep 2
+
+# Check it did not get stopped by our gdb
+set fileid1 [open $status2 r];
+gets $fileid1 line1;
+gets $fileid1 line2;
+close $fileid1;
+
+set test "attach1, post-gdb sanity check of the sleeping state - Red Hat BZ 197584"
+if {[string match "*(sleeping)*" $line2]} {
+  pass $test
+} else {
+  fail $test
+}
+
+# Stop the program 
+remote_exec build "kill -s STOP ${testpid}"
+
+# No race
+sleep 2
+
+# Check it really got stopped by kill(1)
+set fileid2 [open $status2 r];
+gets $fileid2 line1;
+gets $fileid2 line2;
+close $fileid2;
+
+set test "attach2, initial sanity check of the stopped state"
+if {[string match "*(stopped)*" $line2]} {
+  pass $test
+} else {
+  fail $test
+}
+
+# Start with clean gdb
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+# Verify that we can attach to the process by first giving its
+# executable name via the file command, and using attach with the
+# process ID.
+
+set test "set file, before attach3 to stopped process"
+gdb_test_multiple "file $binfile" "$test" {
+   -re "Load new symbol table from.*y or n. $" {
+        gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." \
+		"$test (re-read)"
+    }
+    -re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+        pass "$test"
+    }
+}
+
+set test "attach3 to stopped, after setting file"
+gdb_test_multiple "attach $testpid" "$test" {
+    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+        pass "$test"
+    }
+}
+
+# We may be already after the threads phase.
+# `thread 2' command is important for the test to switch the current thread to
+# a non-primary one for the detach process.
+
+gdb_test "thread 2" ".*(witching to thread 2 |hread ID 2 not known).*" "attach3 to stopped switch thread"
+gdb_test "bt" ".*sleep.*(func|main).*" "attach3 to stopped bt"
+
+# Exit and detach the process.
+gdb_exit
+
+# Stop the program 
+remote_exec build "kill -s STOP ${testpid}"
+
+# No race
+sleep 2
+
+# Continue the test as we would hit another expected bug regarding
+# 	Program received signal SIGSTOP, Stopped (signal).
+# across NPTL threads.
+
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+# Verify that we can attach to the process just by giving the
+# process ID.
+   
+set test "attach4 to stopped, after setting file"
+gdb_test_multiple "attach $testpid" "$test" {
+    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+        pass "$test"
+    }
+}
+
+# We may be already after the threads phase.
+# `thread 2' command is important for the test to switch the current thread to
+# a non-primary one for the detach process.
+
+gdb_test "thread 2" ".*(witching to thread 2 |hread ID 2 not known).*" "attach4 to stopped switch thread"
+gdb_test "bt" ".*sleep.*(func|main).*" "attach4 to stopped bt"
+
+# RHEL3U8 kernel-2.4.21-47.EL will not return SIGINT but only shorten the sleep.
+gdb_breakpoint [gdb_get_line_number "Ridiculous time"]
+gdb_breakpoint [gdb_get_line_number "cut the sleep time"]
+set test "attach4 continue"
+send_gdb "continue\n"
+gdb_expect {
+  -re "Continuing"
+    { pass "continue ($test)" }
+  timeout
+    { fail "continue ($test) (timeout)" }
+}
+
+# For this to work we must be sure to consume the "Continuing."
+# message first, or GDB's signal handler may not be in place.
+after 1000 {send_gdb "\003"}
+set test "attach4 stop by interrupt"
+gdb_expect {
+  -re "Program received signal SIGINT.*$gdb_prompt $"
+    {
+      pass $test
+    }
+  -re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
+    {
+      pass $test
+    }
+  timeout
+    {
+      fail "$test (timeout)"
+    }
+}
+
+gdb_exit
+
+# No race
+sleep 2
+
+# At this point, the process should be sleeping
+
+set fileid4 [open $status2 r];
+gets $fileid4 line1;
+gets $fileid4 line2;
+close $fileid4;
+
+set test "attach4, exit leaves process sleeping"
+if {[string match "*(sleeping)*" $line2]} {
+  pass $test
+} else {
+  fail $test
+}
+
+# Make sure we don't leave a process around to confuse
+# the next test run (and prevent the compile by keeping
+# the text file busy), in case the "set should_exit" didn't
+# work.
+   
+remote_exec build "kill -9 ${testpid}"
+
+return 0


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-15 13:02                 ` Daniel Jacobowitz
@ 2008-04-16  7:01                   ` Roland McGrath
  0 siblings, 0 replies; 52+ messages in thread
From: Roland McGrath @ 2008-04-16  7:01 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: Jan Kratochvil, Doug Evans, GDB Patches, mark.kettenis

> > In practice you will 99.44% of the time be able to find some RT signal
> > that is not blocked.  But to worry about the 0.66% you still have to
> > fall back to SIGSTOP, and in that case you can't rely on queuing.
> 
> Wouldn't this be racy, again?  If the process is running, it might be
> about to block the signal we chose to stop it.

Yes.


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-14 15:31               ` Daniel Jacobowitz
@ 2008-04-15 22:14                 ` Jan Kratochvil
  2008-05-01 18:50                   ` Daniel Jacobowitz
  2008-09-24 12:46                 ` Andreas Schwab
  1 sibling, 1 reply; 52+ messages in thread
From: Jan Kratochvil @ 2008-04-15 22:14 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: Roland McGrath, Doug Evans, GDB Patches, mark.kettenis

[-- Attachment #1: Type: text/plain, Size: 1154 bytes --]

Hi,

please apply the attached patch to the testcase as this workaround is no longer
required with your GDB code patch variant.


On Mon, 14 Apr 2008 17:09:19 +0200, Daniel Jacobowitz wrote:
...
> Big thanks to Jeff Johnston and Jan Kratochvil for the original work,
> Roland for advice, Pedro for async mode fixups, and Doug for prodding
> me to look at this issue again.

Please fix the authorship of the patch as there is no significant line of code
of mine left there.

2008-04-14  Daniel Jacobowitz  <dan@codesourcery.com>
	    Pedro Alves  <pedro@codesourcery.com>

	Based on work by Jan Kratochvil <jan.kratochvil@redhat.com> and Jeff
	Johnston <jjohnstn@redhat.com>.
...

2008-04-14  Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Daniel Jacobowitz  <dan@codesourcery.com>

	* gdb.threads/attach-into-signal.c, gdb.threads/attach-into-signal.exp,
	gdb.threads/attach-stopped.c, gdb.threads/attach-stopped.exp,
	gdb.threads/attachstop-mt.c, gdb.threads/attachstop-mt.exp: New.


The attachment part works for me OK; I hope also the detach-as-stopped part
gets imported later to make gdb_gcore.sh of a stopped process transparent.


Regards,
Jan

[-- Attachment #2: attach-into-signal.c-fix.patch --]
[-- Type: text/plain, Size: 567 bytes --]

--- ./gdb/testsuite/gdb.threads-upstream/attach-into-signal.c	2008-04-15 21:57:13.000000000 +0200
+++ ./gdb/testsuite/gdb.threads/attach-into-signal.c	2008-04-15 22:15:24.000000000 +0200
@@ -40,10 +40,8 @@ static void *func (void *arg)
 
   raise (SIGALRM);
 
-  /* This should be NOTREACHED but sometimes it is reached - Bug 427860.
-     We never get here without ptrace(2).  It may also be a kernel bug.  */
-  for (;;)
-    pause ();
+  /* We must not get past this point, either in a free standing or debugged
+     state.  */
 
   abort ();
   /* NOTREACHED */

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-15  8:14               ` Roland McGrath
@ 2008-04-15 13:02                 ` Daniel Jacobowitz
  2008-04-16  7:01                   ` Roland McGrath
  0 siblings, 1 reply; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-04-15 13:02 UTC (permalink / raw)
  To: Roland McGrath; +Cc: Jan Kratochvil, Doug Evans, GDB Patches, mark.kettenis

On Mon, Apr 14, 2008 at 06:26:33PM -0700, Roland McGrath wrote:
> The significance of SIGSTOP is that it cannot be blocked (and that it's
> what PTRACE_ATTACH generates anyway).  For the thread "to be signalled",
> you have to generate some signal that is not blocked by that thread.
> Every other signal can be blocked, except for SIGKILL (which is too
> significant in its own right).

Thanks, I understand better now.  I'll update the comments in the
patch, so that I don't confuse the next person to look at this.

> In practice you will 99.44% of the time be able to find some RT signal
> that is not blocked.  But to worry about the 0.66% you still have to
> fall back to SIGSTOP, and in that case you can't rely on queuing.

Wouldn't this be racy, again?  If the process is running, it might be
about to block the signal we chose to stop it.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-14 15:09             ` Daniel Jacobowitz
  2008-04-14 15:31               ` Daniel Jacobowitz
@ 2008-04-15  8:14               ` Roland McGrath
  2008-04-15 13:02                 ` Daniel Jacobowitz
  1 sibling, 1 reply; 52+ messages in thread
From: Roland McGrath @ 2008-04-15  8:14 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: Jan Kratochvil, Doug Evans, GDB Patches, mark.kettenis

> Isn't this still racy?  [...]

Yes, you're right.

> This has the same problem of using SIGSTOP in that it may stop other
> threads of the process.  

No, it won't.  Generating SIGSTOP doesn't stop anything (it only clears all
pending SIGCONTs--unlike generating SIGCONT, which does the resuming).
Things only stop when SIGSTOP is delivered.  When you use tkill or
PTRACE_ATTACH to send the SIGSTOP to the particular thread, it is already
ptrace'd by the time it tries to deliver the signal, so you intercept it.
The only way other threads can be stopped is if you do PTRACE_CONT,SIGSTOP
or similar.

> Thanks again for the assistance.  This conversation gave me a useful
> insight into one of those things you already probably know: GDB's use
> of SIGSTOP instead of another signal is not significant.  We want the
> process to be signalled, that's all.  

The significance of SIGSTOP is that it cannot be blocked (and that it's
what PTRACE_ATTACH generates anyway).  For the thread "to be signalled",
you have to generate some signal that is not blocked by that thread.
Every other signal can be blocked, except for SIGKILL (which is too
significant in its own right).

> Since each SIGSTOP we send stops the whole group, [...]

This is not so in the contexts I think you mean, as I described above.

> I'm not going to make that change right now, but I will add some
> comments about it.  We could probably use an RT signal at each point
> we want a stop.

In practice you will 99.44% of the time be able to find some RT signal
that is not blocked.  But to worry about the 0.66% you still have to
fall back to SIGSTOP, and in that case you can't rely on queuing.


Thanks,
Roland


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-14 15:09             ` Daniel Jacobowitz
@ 2008-04-14 15:31               ` Daniel Jacobowitz
  2008-04-15 22:14                 ` Jan Kratochvil
  2008-09-24 12:46                 ` Andreas Schwab
  2008-04-15  8:14               ` Roland McGrath
  1 sibling, 2 replies; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-04-14 15:31 UTC (permalink / raw)
  To: Roland McGrath, Jan Kratochvil, Doug Evans, GDB Patches, mark.kettenis

On Mon, Apr 14, 2008 at 10:34:48AM -0400, Daniel Jacobowitz wrote:
> Fixing the race is not hard though: grub around in /proc for status.
> First PTRACE_ATTACH, which sends a SIGSTOP.  Then check /proc.  If it
> is running, it must have a pending SIGSTOP; we can wait for it so skip
> the tkill and the PTRACE_CONT.  If it is not running, it may or may
> not have a pending SIGSTOP.  So do as you described above with tkill
> and PTRACE_CONT.

Which gives me this patch.

I discussed this with Jan on IRC; it does not leave processes stopped
on detach if they were stopped on attach.  The Red Hat version of the
patch does so.  If we like that change, we can add it separately; and
we can add an explicit command to detach-and-leave-stopped.  Pedro,
I'm not sure about "detach $pid", but we can surely come up with some
spelling for it...

This version is tested on x86_64-linux and looks good.  I'll wait a
few days for anyone else who wants to try or look over it.

Big thanks to Jeff Johnston and Jan Kratochvil for the original work,
Roland for advice, Pedro for async mode fixups, and Doug for prodding
me to look at this issue again.

-- 
Daniel Jacobowitz
CodeSourcery

2008-04-14  Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Daniel Jacobowitz  <dan@codesourcery.com>
	    Pedro Alves  <pedro@codesourcery.com>

	* NEWS: Mention attach to stopped process fix.
	* infcmd.c (detach_command, disconnect_command): Discard the thread
	list.
	* infrun.c (handle_inferior_event): Do not ignore non-SIGSTOP while
	attaching.  Use signal_stop_state.
	(signal_stop_state): Check stop_soon.
	* linux-nat.c (kill_lwp): Declare earlier.
	(pid_is_stopped, linux_nat_post_attach_wait): New.
	(lin_lwp_attach_lwp): Use linux_nat_post_attach_wait.  Update
	comments.
	(linux_nat_attach): Use linux_nat_post_attach_wait.
	(detach_callback, linux_nat_detach): Improve handling for signalled
	processes.
	(linux_nat_pid_to_str): Always print out the LWP ID if it differs
	from the process ID.
	* Makefile.in (infcmd.o): Update.

2008-04-14  Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Daniel Jacobowitz  <dan@codesourcery.com>

	* gdb.threads/attach-into-signal.c, gdb.threads/attach-into-signal.exp,
	gdb.threads/attach-stopped.c, gdb.threads/attach-stopped.exp,
	gdb.threads/attachstop-mt.c, gdb.threads/attachstop-mt.exp: New.

Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.998
diff -u -p -r1.998 Makefile.in
--- Makefile.in	9 Apr 2008 13:29:51 -0000	1.998
+++ Makefile.in	14 Apr 2008 14:56:16 -0000
@@ -2291,7 +2291,7 @@ infcmd.o: infcmd.c $(defs_h) $(gdb_strin
 	$(objfiles_h) $(completer_h) $(ui_out_h) $(event_top_h) \
 	$(parser_defs_h) $(regcache_h) $(reggroups_h) $(block_h) \
 	$(solib_h) $(gdb_assert_h) $(observer_h) $(target_descriptions_h) \
-	$(user_regs_h) $(exceptions_h)
+	$(user_regs_h) $(exceptions_h) $(gdbthread_h)
 inf-loop.o: inf-loop.c $(defs_h) $(inferior_h) $(target_h) $(event_loop_h) \
 	$(event_top_h) $(inf_loop_h) $(remote_h) $(exceptions_h) \
 	$(language_h)
Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.264
diff -u -p -r1.264 NEWS
--- NEWS	4 Apr 2008 15:51:15 -0000	1.264
+++ NEWS	14 Apr 2008 14:56:17 -0000
@@ -7,7 +7,11 @@
 which will be allocated using malloc later in program execution.
 
 * The qXfer:libraries:read remote procotol packet now allows passing a
-  list of section offsets.
+list of section offsets.
+
+* On GNU/Linux, GDB can now attach to stopped processes.  Several race
+conditions handling signals delivered during attach or thread creation
+have also been fixed.
 
 * New features in the GDB remote stub, gdbserver
 
Index: infcmd.c
===================================================================
RCS file: /cvs/src/src/gdb/infcmd.c,v
retrieving revision 1.174
diff -u -p -r1.174 infcmd.c
--- infcmd.c	17 Mar 2008 17:30:29 -0000	1.174
+++ infcmd.c	14 Apr 2008 14:56:17 -0000
@@ -49,6 +49,7 @@
 #include "target-descriptions.h"
 #include "user-regs.h"
 #include "exceptions.h"
+#include "gdbthread.h"
 
 /* Functions exported for general use, in inferior.h: */
 
@@ -2067,6 +2068,7 @@ detach_command (char *args, int from_tty
   dont_repeat ();		/* Not for the faint of heart.  */
   target_detach (args, from_tty);
   no_shared_libraries (NULL, from_tty);
+  init_thread_list ();
   if (deprecated_detach_hook)
     deprecated_detach_hook ();
 }
@@ -2085,6 +2087,7 @@ disconnect_command (char *args, int from
   dont_repeat ();		/* Not for the faint of heart */
   target_disconnect (args, from_tty);
   no_shared_libraries (NULL, from_tty);
+  init_thread_list ();
   if (deprecated_detach_hook)
     deprecated_detach_hook ();
 }
Index: infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.267
diff -u -p -r1.267 infrun.c
--- infrun.c	15 Mar 2008 14:55:21 -0000	1.267
+++ infrun.c	14 Apr 2008 14:56:17 -0000
@@ -1947,13 +1947,15 @@ handle_inferior_event (struct execution_
 
       /* This originates from attach_command().  We need to overwrite
          the stop_signal here, because some kernels don't ignore a
-         SIGSTOP in a subsequent ptrace(PTRACE_SONT,SOGSTOP) call.
-         See more comments in inferior.h.  */
-      if (stop_soon == STOP_QUIETLY_NO_SIGSTOP)
+         SIGSTOP in a subsequent ptrace(PTRACE_CONT,SIGSTOP) call.
+         See more comments in inferior.h.  On the other hand, if we
+	 get a non-SIGSTOP, report it to the user - assume the backend
+	 will handle the SIGSTOP if it should show up later.  */
+      if (stop_soon == STOP_QUIETLY_NO_SIGSTOP
+	  && stop_signal == TARGET_SIGNAL_STOP)
 	{
 	  stop_stepping (ecs);
-	  if (stop_signal == TARGET_SIGNAL_STOP)
-	    stop_signal = TARGET_SIGNAL_0;
+	  stop_signal = TARGET_SIGNAL_0;
 	  return;
 	}
 
@@ -2024,7 +2026,7 @@ process_event_stop_test:
 	  target_terminal_ours_for_output ();
 	  print_stop_reason (SIGNAL_RECEIVED, stop_signal);
 	}
-      if (signal_stop[stop_signal])
+      if (signal_stop_state (stop_signal))
 	{
 	  stop_stepping (ecs);
 	  return;
@@ -3287,7 +3289,9 @@ hook_stop_stub (void *cmd)
 int
 signal_stop_state (int signo)
 {
-  return signal_stop[signo];
+  /* Always stop on signals if we're just gaining control of the
+     program.  */
+  return signal_stop[signo] || stop_soon != NO_STOP_QUIETLY;
 }
 
 int
Index: linux-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/linux-nat.c,v
retrieving revision 1.80
diff -u -p -r1.80 linux-nat.c
--- linux-nat.c	25 Mar 2008 12:26:21 -0000	1.80
+++ linux-nat.c	14 Apr 2008 14:56:18 -0000
@@ -50,6 +50,21 @@
 #include "event-loop.h"
 #include "event-top.h"
 
+/* Note on this file's use of signals:
+
+   We stop threads by sending a SIGSTOP.  The use of SIGSTOP instead
+   of another signal is not significant; we just need for a signal to
+   be delivered, so that we can catch it.  SIGSTOP has two problems
+   for this use.  It is not a real-time signal, so it can only be
+   queued once; we do not keep track of other sources of SIGSTOP.
+   And it stops the entire thread group, not just the tkill'd thread.
+   In practice, these do not cause any problem.
+
+   We could use a real-time signal instead.  This would solve those
+   problems; we could use PTRACE_GETSIGINFO to locate the specific
+   stop signals sent by GDB.  But we would still have to have some
+   support for SIGSTOP, since PTRACE_ATTACH generates it.  */
+
 #ifndef O_LARGEFILE
 #define O_LARGEFILE 0
 #endif
@@ -186,6 +201,7 @@ static void linux_nat_async (void (*call
 			     (enum inferior_event_type event_type, void *context),
 			     void *context);
 static int linux_nat_async_mask (int mask);
+static int kill_lwp (int lwpid, int signo);
 
 /* Captures the result of a successful waitpid call, along with the
    options used in that call.  */
@@ -1010,10 +1026,103 @@ exit_lwp (struct lwp_info *lp)
   delete_lwp (lp->ptid);
 }
 
-/* Attach to the LWP specified by PID.  If VERBOSE is non-zero, print
-   a message telling the user that a new LWP has been added to the
-   process.  Return 0 if successful or -1 if the new LWP could not
-   be attached.  */
+/* Detect `T (stopped)' in `/proc/PID/status'.
+   Other states including `T (tracing stop)' are reported as false.  */
+
+static int
+pid_is_stopped (pid_t pid)
+{
+  FILE *status_file;
+  char buf[100];
+  int retval = 0;
+
+  snprintf (buf, sizeof (buf), "/proc/%d/status", (int) pid);
+  status_file = fopen (buf, "r");
+  if (status_file != NULL)
+    {
+      int have_state = 0;
+
+      while (fgets (buf, sizeof (buf), status_file))
+	{
+	  if (strncmp (buf, "State:", 6) == 0)
+	    {
+	      have_state = 1;
+	      break;
+	    }
+	}
+      if (have_state && strstr (buf, "T (stopped)") != NULL)
+	retval = 1;
+      fclose (status_file);
+    }
+  return retval;
+}
+
+/* Wait for the LWP specified by LP, which we have just attached to.
+   Returns a wait status for that LWP, to cache.  */
+
+static int
+linux_nat_post_attach_wait (ptid_t ptid, int first, int *cloned,
+			    int *signalled)
+{
+  pid_t new_pid, pid = GET_LWP (ptid);
+  int status;
+
+  if (pid_is_stopped (pid))
+    {
+      if (debug_linux_nat)
+	fprintf_unfiltered (gdb_stdlog,
+			    "LNPAW: Attaching to a stopped process\n");
+
+      /* The process is definitely stopped.  It is in a job control
+	 stop, unless the kernel predates the TASK_STOPPED /
+	 TASK_TRACED distinction, in which case it might be in a
+	 ptrace stop.  Make sure it is in a ptrace stop; from there we
+	 can kill it, signal it, et cetera.
+
+         First make sure there is a pending SIGSTOP.  Since we are
+	 already attached, the process can not transition from stopped
+	 to running without a PTRACE_CONT; so we know this signal will
+	 go into the queue.  The SIGSTOP generated by PTRACE_ATTACH is
+	 probably already in the queue (unless this kernel is old
+	 enough to use TASK_STOPPED for ptrace stops); but since SIGSTOP
+	 is not an RT signal, it can only be queued once.  */
+      kill_lwp (pid, SIGSTOP);
+
+      /* Finally, resume the stopped process.  This will deliver the SIGSTOP
+	 (or a higher priority signal, just like normal PTRACE_ATTACH).  */
+      ptrace (PTRACE_CONT, pid, 0, 0);
+    }
+
+  /* Make sure the initial process is stopped.  The user-level threads
+     layer might want to poke around in the inferior, and that won't
+     work if things haven't stabilized yet.  */
+  new_pid = my_waitpid (pid, &status, 0);
+  if (new_pid == -1 && errno == ECHILD)
+    {
+      if (first)
+	warning (_("%s is a cloned process"), target_pid_to_str (ptid));
+
+      /* Try again with __WCLONE to check cloned processes.  */
+      new_pid = my_waitpid (pid, &status, __WCLONE);
+      *cloned = 1;
+    }
+
+  gdb_assert (pid == new_pid && WIFSTOPPED (status));
+
+  if (WSTOPSIG (status) != SIGSTOP)
+    {
+      *signalled = 1;
+      if (debug_linux_nat)
+	fprintf_unfiltered (gdb_stdlog,
+			    "LNPAW: Received %s after attaching\n",
+			    status_to_str (status));
+    }
+
+  return status;
+}
+
+/* Attach to the LWP specified by PID.  Return 0 if successful or -1
+   if the new LWP could not be attached.  */
 
 int
 lin_lwp_attach_lwp (ptid_t ptid)
@@ -1036,9 +1145,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
      to happen.  */
   if (GET_LWP (ptid) != GET_PID (ptid) && lp == NULL)
     {
-      pid_t pid;
-      int status;
-      int cloned = 0;
+      int status, cloned = 0, signalled = 0;
 
       if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
 	{
@@ -1057,24 +1164,18 @@ lin_lwp_attach_lwp (ptid_t ptid)
 			    "LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
 			    target_pid_to_str (ptid));
 
-      pid = my_waitpid (GET_LWP (ptid), &status, 0);
-      if (pid == -1 && errno == ECHILD)
+      status = linux_nat_post_attach_wait (ptid, 0, &cloned, &signalled);
+      lp = add_lwp (ptid);
+      lp->stopped = 1;
+      lp->cloned = cloned;
+      lp->signalled = signalled;
+      if (WSTOPSIG (status) != SIGSTOP)
 	{
-	  /* Try again with __WCLONE to check cloned processes.  */
-	  pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
-	  cloned = 1;
+	  lp->resumed = 1;
+	  lp->status = status;
 	}
 
-      gdb_assert (pid == GET_LWP (ptid)
-		  && WIFSTOPPED (status) && WSTOPSIG (status));
-
-      if (lp == NULL)
-	lp = add_lwp (ptid);
-      lp->cloned = cloned;
-
-      target_post_attach (pid);
-
-      lp->stopped = 1;
+      target_post_attach (GET_LWP (lp->ptid));
 
       if (debug_linux_nat)
 	{
@@ -1133,10 +1234,7 @@ static void
 linux_nat_attach (char *args, int from_tty)
 {
   struct lwp_info *lp;
-  pid_t pid;
   int status;
-  int cloned = 0;
-  int options = 0;
 
   /* FIXME: We should probably accept a list of process id's, and
      attach all of them.  */
@@ -1151,54 +1249,69 @@ linux_nat_attach (char *args, int from_t
       sigdelset (&suspend_mask, SIGCHLD);
     }
 
-  /* Make sure the initial process is stopped.  The user-level threads
-     layer might want to poke around in the inferior, and that won't
-     work if things haven't stabilized yet.  */
-  pid = my_waitpid (GET_PID (inferior_ptid), &status, options);
-  if (pid == -1 && errno == ECHILD)
-    {
-      warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
-
-      /* Try again with __WCLONE to check cloned processes.  */
-      options = __WCLONE;
-      pid = my_waitpid (GET_PID (inferior_ptid), &status, options);
-      cloned = 1;
-    }
-
-  gdb_assert (pid == GET_PID (inferior_ptid)
-	      && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
-
   /* Add the initial process as the first LWP to the list.  */
   inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
   lp = add_lwp (inferior_ptid);
-  lp->cloned = cloned;
+
+  status = linux_nat_post_attach_wait (lp->ptid, 1, &lp->cloned,
+				       &lp->signalled);
+  lp->stopped = 1;
 
   /* If this process is not using thread_db, then we still don't
      detect any other threads, but add at least this one.  */
   add_thread_silent (lp->ptid);
 
-  lp->stopped = 1;
+  /* Save the wait status to report later.  */
   lp->resumed = 1;
+  if (debug_linux_nat)
+    fprintf_unfiltered (gdb_stdlog,
+			"LNA: waitpid %ld, saving status %s\n",
+			(long) GET_PID (lp->ptid), status_to_str (status));
 
   if (!target_can_async_p ())
-    {
-      /* Fake the SIGSTOP that core GDB expects.  */
-      lp->status = W_STOPCODE (SIGSTOP);
-      if (debug_linux_nat)
-	fprintf_unfiltered (gdb_stdlog,
-			    "LNA: waitpid %ld, faking SIGSTOP\n", (long) pid);
-    }
+    lp->status = status;
   else
     {
       /* We already waited for this LWP, so put the wait result on the
 	 pipe.  The event loop will wake up and gets us to handling
 	 this event.  */
-      linux_nat_event_pipe_push (pid, status, options);
+      linux_nat_event_pipe_push (GET_PID (lp->ptid), status,
+				 lp->cloned ? __WCLONE : 0);
       /* Register in the event loop.  */
       target_async (inferior_event_handler, 0);
     }
 }
 
+/* Get pending status of LP.  */
+static int
+get_pending_status (struct lwp_info *lp, int *status)
+{
+  struct target_waitstatus last;
+  ptid_t last_ptid;
+
+  get_last_target_status (&last_ptid, &last);
+
+  /* If this lwp is the ptid that GDB is processing an event from, the
+     signal will be in stop_signal.  Otherwise, in all-stop + sync
+     mode, we may cache pending events in lp->status while trying to
+     stop all threads (see stop_wait_callback).  In async mode, the
+     events are always cached in waitpid_queue.  */
+
+  *status = 0;
+  if (GET_LWP (lp->ptid) == GET_LWP (last_ptid))
+    {
+      if (stop_signal != TARGET_SIGNAL_0
+	  && signal_pass_state (stop_signal))
+	*status = W_STOPCODE (target_signal_to_host (stop_signal));
+    }
+  else if (target_can_async_p ())
+    queued_waitpid (GET_LWP (lp->ptid), status, __WALL);
+  else
+    *status = lp->status;
+
+  return 0;
+}
+
 static int
 detach_callback (struct lwp_info *lp, void *data)
 {
@@ -1209,40 +1322,30 @@ detach_callback (struct lwp_info *lp, vo
 			strsignal (WSTOPSIG (lp->status)),
 			target_pid_to_str (lp->ptid));
 
-  while (lp->signalled && lp->stopped)
+  /* If there is a pending SIGSTOP, get rid of it.  */
+  if (lp->signalled)
     {
-      errno = 0;
-      if (ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0,
-		  WSTOPSIG (lp->status)) < 0)
-	error (_("Can't continue %s: %s"), target_pid_to_str (lp->ptid),
-	       safe_strerror (errno));
-
       if (debug_linux_nat)
 	fprintf_unfiltered (gdb_stdlog,
-			    "DC:  PTRACE_CONTINUE (%s, 0, %s) (OK)\n",
-			    target_pid_to_str (lp->ptid),
-			    status_to_str (lp->status));
+			    "DC: Sending SIGCONT to %s\n",
+			    target_pid_to_str (lp->ptid));
 
-      lp->stopped = 0;
+      kill_lwp (GET_LWP (lp->ptid), SIGCONT);
       lp->signalled = 0;
-      lp->status = 0;
-      /* FIXME drow/2003-08-26: There was a call to stop_wait_callback
-	 here.  But since lp->signalled was cleared above,
-	 stop_wait_callback didn't do anything; the process was left
-	 running.  Shouldn't we be waiting for it to stop?
-	 I've removed the call, since stop_wait_callback now does do
-	 something when called with lp->signalled == 0.  */
-
-      gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status));
     }
 
   /* We don't actually detach from the LWP that has an id equal to the
      overall process id just yet.  */
   if (GET_LWP (lp->ptid) != GET_PID (lp->ptid))
     {
+      int status = 0;
+
+      /* Pass on any pending signal for this LWP.  */
+      get_pending_status (lp, &status);
+
       errno = 0;
       if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
-		  WSTOPSIG (lp->status)) < 0)
+		  WSTOPSIG (status)) < 0)
 	error (_("Can't detach %s: %s"), target_pid_to_str (lp->ptid),
 	       safe_strerror (errno));
 
@@ -1252,7 +1355,6 @@ detach_callback (struct lwp_info *lp, vo
 			    target_pid_to_str (lp->ptid),
 			    strsignal (WSTOPSIG (lp->status)));
 
-      drain_queued_events (GET_LWP (lp->ptid));
       delete_lwp (lp->ptid);
     }
 
@@ -1263,6 +1365,9 @@ static void
 linux_nat_detach (char *args, int from_tty)
 {
   int pid;
+  int status;
+  enum target_signal sig;
+
   if (target_can_async_p ())
     linux_nat_async (NULL, 0);
 
@@ -1271,6 +1376,21 @@ linux_nat_detach (char *args, int from_t
   /* Only the initial process should be left right now.  */
   gdb_assert (num_lwps == 1);
 
+  /* Pass on any pending signal for the last LWP.  */
+  if ((args == NULL || *args == '\0')
+      && get_pending_status (lwp_list, &status) != -1
+      && WIFSTOPPED (status))
+    {
+      /* Put the signal number in ARGS so that inf_ptrace_detach will
+	 pass it along with PTRACE_DETACH.  */
+      args = alloca (8);
+      sprintf (args, "%d", (int) WSTOPSIG (status));
+      fprintf_unfiltered (gdb_stdlog,
+			  "LND: Sending signal %s to %s\n",
+			  args,
+			  target_pid_to_str (lwp_list->ptid));
+    }
+
   trap_ptid = null_ptid;
 
   /* Destroy LWP info; it's no longer valid.  */
@@ -2848,7 +2968,9 @@ linux_nat_pid_to_str (ptid_t ptid)
 {
   static char buf[64];
 
-  if (lwp_list && lwp_list->next && is_lwp (ptid))
+  if (is_lwp (ptid)
+      && ((lwp_list && lwp_list->next)
+	  || GET_PID (ptid) != GET_LWP (ptid)))
     {
       snprintf (buf, sizeof (buf), "LWP %ld", GET_LWP (ptid));
       return buf;
@@ -4205,4 +4327,3 @@ lin_thread_get_thread_signals (sigset_t 
   /* ... except during a sigsuspend.  */
   sigdelset (&suspend_mask, cancel);
 }
-
Index: testsuite/gdb.threads/attach-into-signal.c
===================================================================
RCS file: testsuite/gdb.threads/attach-into-signal.c
diff -N testsuite/gdb.threads/attach-into-signal.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attach-into-signal.c	14 Apr 2008 14:56:18 -0000
@@ -0,0 +1,69 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2008 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+void action(int sig, siginfo_t * info, void *uc)
+{
+  raise (SIGALRM);
+}
+
+static void *func (void *arg)
+{
+  struct sigaction act;
+
+  memset (&act, 0, sizeof(struct sigaction));
+  act.sa_sigaction = action;
+  act.sa_flags = SA_RESTART;
+  sigaction (SIGALRM, &act, 0);
+
+  raise (SIGALRM);
+
+  /* This should be NOTREACHED but sometimes it is reached - Bug 427860.
+     We never get here without ptrace(2).  It may also be a kernel bug.  */
+  for (;;)
+    pause ();
+
+  abort ();
+  /* NOTREACHED */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
Index: testsuite/gdb.threads/attach-into-signal.exp
===================================================================
RCS file: testsuite/gdb.threads/attach-into-signal.exp
diff -N testsuite/gdb.threads/attach-into-signal.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attach-into-signal.exp	14 Apr 2008 14:56:18 -0000
@@ -0,0 +1,168 @@
+# Copyright 2008
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This test was created by modifying attach-stopped.exp.
+# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+# This test only works on Linux
+if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
+    continue
+}
+
+set testfile "attach-into-signal"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+    # Statistically there is a better chance without giving process a nice.
+
+    set testpid [eval exec $binfile &]
+    exec sleep 2
+
+    # Run 2 passes of the test.
+    # The C file inferior stops pending its signals if a single one is lost,
+    # we test successful redelivery of the caught signal by the 2nd pass.
+
+    # linux-2.6.20.4.x86_64 had maximal attempt # 20 in 4 test runs.
+    set attempts 100
+    set attempt 1
+    set passes 1
+    while { $passes < 3 && $attempt <= $attempts } {
+	set stoppedtry 0
+	while { $stoppedtry < 10 } {
+	    if [catch {open /proc/${testpid}/status r} fileid] {
+		set stoppedtry 10
+		break
+	    }
+	    gets $fileid line1;
+	    gets $fileid line2;
+	    close $fileid;
+
+	    if {![string match "*(stopped)*" $line2]} {
+		# No PASS message as we may be looping in multiple attempts.
+		break
+	    }
+	    sleep 1
+	    set stoppedtry [expr $stoppedtry + 1]
+	}
+	if { $stoppedtry >= 10 } {
+	    verbose -log $line2
+	    set test "$threadtype: process is still running on the attempt # $attempt of $attempts"
+	    break
+	}
+
+	# Main test:
+	set test "$threadtype: attach (pass $passes), pending signal catch"
+	if {[gdb_test_multiple "attach $testpid" $test {
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*Received Alarm clock.*$gdb_prompt $" {
+		# nonthreaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+		# nonthreaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt + 1]
+	    }
+	    -re "Attaching to process $testpid.*Received Alarm clock.*$gdb_prompt $" {
+		# threaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to process $testpid.*$gdb_prompt $" {
+		# threaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt - 1]
+	    }
+	}] != 0 } {
+	    break
+	}
+
+	gdb_test "detach" "Detaching from.*" ""
+    }
+    if {$passes < 3} {
+	if {$attempt > $attempts} {
+	    unresolved $test
+	} else {
+	    fail $test
+	}
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+
+    # Continue the program - some Linux kernels need it before -9 if the
+    # process is stopped.
+    remote_exec build "kill -s CONT ${testpid}"
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# Start with clean gdb
+gdb_exit
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+gdb_test "set debug lin-lwp 1" "" ""
+
+corefunc nonthreaded
+
+# build the test case also with threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+gdb_test "set debug lin-lwp 1" "" ""
+
+corefunc threaded
Index: testsuite/gdb.threads/attach-stopped.c
===================================================================
RCS file: testsuite/gdb.threads/attach-stopped.c
diff -N testsuite/gdb.threads/attach-stopped.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attach-stopped.c	14 Apr 2008 14:56:18 -0000
@@ -0,0 +1,50 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2008 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* This program is intended to be started outside of gdb, then
+   manually stopped via a signal.  */
+
+#include <stddef.h>
+#include <unistd.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+static void *func (void *arg)
+{
+  sleep (10000);  /* Ridiculous time, but we will eventually kill it.  */
+  sleep (10000);  /* Second sleep.  */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
Index: testsuite/gdb.threads/attach-stopped.exp
===================================================================
RCS file: testsuite/gdb.threads/attach-stopped.exp
diff -N testsuite/gdb.threads/attach-stopped.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attach-stopped.exp	14 Apr 2008 14:56:18 -0000
@@ -0,0 +1,157 @@
+# Copyright 2008
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This test was created by modifying attach.exp.
+# This file was created by Jeff Johnston <jjohnstn@redhat.com>.
+# This file was updated by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+# This test only works on Linux
+if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
+    continue
+}
+
+set testfile "attach-stopped"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+#execute_anywhere "rm -f ${binfile}"
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+
+    set testpid [eval exec $binfile &]
+
+    # Avoid some race:
+    sleep 2
+
+    # Stop the program 
+    remote_exec build "kill -s STOP ${testpid}"
+
+    # Start with clean gdb
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the stopped process.
+       
+    set test "$threadtype: attach2 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    # ".*sleep.*clone.*" would fail on s390x as bt stops at START_THREAD there.
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*start_thread.*" "$threadtype: attach2 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach2 to stopped bt"
+    }
+    # This breakpoint is there for old/non-x86 kernels not restarting syscalls.
+    gdb_breakpoint [gdb_get_line_number "Second sleep"]
+    set test "$threadtype: attach2 continue"
+    send_gdb "continue\n"
+    gdb_expect {
+      -re "Continuing"
+	{ pass "continue ($test)" }
+      timeout
+	{ fail "continue ($test) (timeout)" }
+    }
+
+    # For this to work we must be sure to consume the "Continuing."
+    # message first, or GDB's signal handler may not be in place.
+    after 1000 {send_gdb "\003"}
+    set test "$threadtype: attach2 stop interrupt"
+    gdb_expect 10 {
+      -re "Program received signal SIGINT.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      -re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      timeout
+	{
+	  fail $test
+	}
+    }
+
+    gdb_exit
+
+    # Avoid some race:
+    sleep 2
+
+    # At this point, the process should be sleeping
+
+    if [catch {open /proc/${testpid}/status r} fileid2] {
+	set line2 "NOTFOUND"
+    } else {
+	gets $fileid2 line1;
+	gets $fileid2 line2;
+	close $fileid2;
+    }
+
+    set test "$threadtype: attach2, exit leaves process sleeping"
+    if {[string match "*(sleeping)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case first without threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
+
+return 0
Index: testsuite/gdb.threads/attachstop-mt.c
===================================================================
RCS file: testsuite/gdb.threads/attachstop-mt.c
diff -N testsuite/gdb.threads/attachstop-mt.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attachstop-mt.c	14 Apr 2008 14:56:18 -0000
@@ -0,0 +1,55 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2008 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* This program is intended to be started outside of gdb, then
+   manually stopped via a signal.  */
+
+#include <unistd.h>
+#include <pthread.h>
+#include <stdio.h>
+
+/* Red Hat BZ PR 197584.c */
+
+void *func (void *arg)
+{
+  sleep (10000);  /* Ridiculous time, but we will eventually kill it.  */
+  sleep (10000);	/* RHEL3U8 kernel-2.4.21-47.EL will cut the sleep time.  */
+
+  return NULL;	/* thread-sleep */
+}
+
+int main ()
+{
+  pthread_t t1,t2;
+  int ret;
+
+  ret = pthread_create (&t1, NULL, func, NULL);
+  if (ret)
+    fprintf(stderr, "pthread_create(): %s", strerror (ret));
+  ret = pthread_create (&t2, NULL, func, NULL);
+  if (ret)
+    fprintf(stderr, "pthread_create(): %s", strerror (ret));
+
+  ret = pthread_join (t1, NULL);
+  if (ret)	/* first-join */
+    fprintf(stderr, "pthread_join(): %s", strerror (ret));
+  ret = pthread_join (t2, NULL);
+  if (ret)
+    fprintf(stderr, "pthread_join(): %s", strerror (ret));
+
+  return 0;
+}
Index: testsuite/gdb.threads/attachstop-mt.exp
===================================================================
RCS file: testsuite/gdb.threads/attachstop-mt.exp
diff -N testsuite/gdb.threads/attachstop-mt.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attachstop-mt.exp	14 Apr 2008 14:56:18 -0000
@@ -0,0 +1,260 @@
+# Copyright 2008
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This test was created by modifying gdb.threads/attachstop.
+# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
+# Regression for: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=197584
+
+# This test only works on Linux
+if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
+    continue
+}
+
+set testfile "attachstop-mt"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+#execute_anywhere "rm -f ${binfile}"
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+# build the test case
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail."
+}
+
+if [get_compiler_info ${binfile}] {
+    return -1
+}
+
+# Start the program running and then wait for a bit, to be sure
+# that it can be attached to.
+
+set testpid [eval exec $binfile &]
+
+# No race
+sleep 2
+
+# Do not: set testpid2 [expr $testpid + 1]
+# as it will not exist on Red Hat 2.6.9-34.0.2.ELsmp
+set testpid2 [expr $testpid + 2]
+
+set status2 /proc/${testpid}/task/${testpid2}/status
+if {[expr ! [file exists $status2]]} {
+  # kernel-2.4
+  set status2 /proc/${testpid2}/status
+}
+
+# Initial sanity test it is normally sleeping
+set fileid0 [open $status2 r];
+gets $fileid0 line1;
+gets $fileid0 line2;
+close $fileid0;
+
+set test "attach0, initial sanity check of the sleeping state"
+if {[string match "*(sleeping)*" $line2]} {
+  pass $test
+} else {
+  fail $test
+}
+
+# Sttach and detach to test it will not become stopped
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+set test "attach0 to sleeping"
+gdb_test_multiple "attach $testpid" "$test" {
+    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+        pass "$test"
+    }
+}
+
+gdb_test "gcore /dev/null" ".*aved corefile.*" "attach0 to sleeping gcore invocation"
+
+gdb_test "thread 2" ".*witching to thread 2 .*" "attach0 to sleeping switch thread"
+
+gdb_test "bt" ".*sleep.*func.*" "attach0 to sleeping bt"
+
+# Exit and detach the process.
+
+gdb_exit
+
+# No race
+sleep 2
+
+# Check it did not get stopped by our gdb
+set fileid1 [open $status2 r];
+gets $fileid1 line1;
+gets $fileid1 line2;
+close $fileid1;
+
+set test "attach1, post-gdb sanity check of the sleeping state - Red Hat BZ 197584"
+if {[string match "*(sleeping)*" $line2]} {
+  pass $test
+} else {
+  fail $test
+}
+
+# Stop the program 
+remote_exec build "kill -s STOP ${testpid}"
+
+# No race
+sleep 2
+
+# Check it really got stopped by kill(1)
+set fileid2 [open $status2 r];
+gets $fileid2 line1;
+gets $fileid2 line2;
+close $fileid2;
+
+set test "attach2, initial sanity check of the stopped state"
+if {[string match "*(stopped)*" $line2]} {
+  pass $test
+} else {
+  fail $test
+}
+
+# Start with clean gdb
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+# Verify that we can attach to the process by first giving its
+# executable name via the file command, and using attach with the
+# process ID.
+
+set test "set file, before attach3 to stopped process"
+gdb_test_multiple "file $binfile" "$test" {
+   -re "Load new symbol table from.*y or n. $" {
+        gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." \
+		"$test (re-read)"
+    }
+    -re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+        pass "$test"
+    }
+}
+
+set test "attach3 to stopped, after setting file"
+gdb_test_multiple "attach $testpid" "$test" {
+    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+        pass "$test"
+    }
+}
+
+# We may be already after the threads phase.
+# `thread 2' command is important for the test to switch the current thread to
+# a non-primary one for the detach process.
+
+gdb_test "thread 2" ".*(witching to thread 2 |hread ID 2 not known).*" "attach3 to stopped switch thread"
+gdb_test "bt" ".*sleep.*(func|main).*" "attach3 to stopped bt"
+
+# Exit and detach the process.
+gdb_exit
+
+# Stop the program 
+remote_exec build "kill -s STOP ${testpid}"
+
+# No race
+sleep 2
+
+# Continue the test as we would hit another expected bug regarding
+# 	Program received signal SIGSTOP, Stopped (signal).
+# across NPTL threads.
+
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+# Verify that we can attach to the process just by giving the
+# process ID.
+   
+set test "attach4 to stopped, after setting file"
+gdb_test_multiple "attach $testpid" "$test" {
+    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+        pass "$test"
+    }
+}
+
+# We may be already after the threads phase.
+# `thread 2' command is important for the test to switch the current thread to
+# a non-primary one for the detach process.
+
+gdb_test "thread 2" ".*(witching to thread 2 |hread ID 2 not known).*" "attach4 to stopped switch thread"
+gdb_test "bt" ".*sleep.*(func|main).*" "attach4 to stopped bt"
+
+# RHEL3U8 kernel-2.4.21-47.EL will not return SIGINT but only shorten the sleep.
+gdb_breakpoint [gdb_get_line_number "Ridiculous time"]
+gdb_breakpoint [gdb_get_line_number "cut the sleep time"]
+set test "attach4 continue"
+send_gdb "continue\n"
+gdb_expect {
+  -re "Continuing"
+    { pass "continue ($test)" }
+  timeout
+    { fail "continue ($test) (timeout)" }
+}
+
+# For this to work we must be sure to consume the "Continuing."
+# message first, or GDB's signal handler may not be in place.
+after 1000 {send_gdb "\003"}
+set test "attach4 stop by interrupt"
+gdb_expect {
+  -re "Program received signal SIGINT.*$gdb_prompt $"
+    {
+      pass $test
+    }
+  -re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
+    {
+      pass $test
+    }
+  timeout
+    {
+      fail "$test (timeout)"
+    }
+}
+
+gdb_exit
+
+# No race
+sleep 2
+
+# At this point, the process should be sleeping
+
+set fileid4 [open $status2 r];
+gets $fileid4 line1;
+gets $fileid4 line2;
+close $fileid4;
+
+set test "attach4, exit leaves process sleeping"
+if {[string match "*(sleeping)*" $line2]} {
+  pass $test
+} else {
+  fail $test
+}
+
+# Make sure we don't leave a process around to confuse
+# the next test run (and prevent the compile by keeping
+# the text file busy), in case the "set should_exit" didn't
+# work.
+   
+remote_exec build "kill -9 ${testpid}"
+
+return 0


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-12  7:58           ` Roland McGrath
@ 2008-04-14 15:09             ` Daniel Jacobowitz
  2008-04-14 15:31               ` Daniel Jacobowitz
  2008-04-15  8:14               ` Roland McGrath
  0 siblings, 2 replies; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-04-14 15:09 UTC (permalink / raw)
  To: Roland McGrath; +Cc: Jan Kratochvil, Doug Evans, GDB Patches, mark.kettenis

On Fri, Apr 11, 2008 at 05:01:55PM -0700, Roland McGrath wrote:
> Ah, right.  You can also do:
> 
> 	PTRACE_ATTACH		-> pending SIGSTOP
> 	... if running		-> stopped, no pending SIGSTOP
> 	... if stopped		-> still stopped, pending SIGSTOP
> 	tkill (SIGSTOP, tid)	-> pending SIGSTOP
> 
> 	PTRACE_CONT, tid, 0	-> if not stopped the first time yet, ESRCH
> 				-> if stopped, wakes up, dequeues SIGSTOP
> 	wait			-> always see it stop
> 
> You just don't know whether you'll see one more spurious SIGSTOP or not.
> (Once you've waited, you can synchronously check /proc, but that doesn't
> tell you whether it was your spurious one or just an outside one sent
> right now.)

Isn't this still racy?  SIGCONT wakeups happen synchronously, but
SIGSTOP stops do not.  After the PTRACE_ATTACH the thread might be
running with a pending SIGSTOP.  Then the tkill has no effect, and
if the thread stops between the tkill and the PTRACE_CONT it will have
no reason to stop again.

Fixing the race is not hard though: grub around in /proc for status.
First PTRACE_ATTACH, which sends a SIGSTOP.  Then check /proc.  If it
is running, it must have a pending SIGSTOP; we can wait for it so skip
the tkill and the PTRACE_CONT.  If it is not running, it may or may
not have a pending SIGSTOP.  So do as you described above with tkill
and PTRACE_CONT.

This has the same problem of using SIGSTOP in that it may stop other
threads of the process.  But it avoids using SIGCONT, so it will not
wake up other threads of the process.  For GDB's purposes, that is
currently good enough - strictly better than before, while the
SIGCONT approach could cause other threads to run before we attached
to them.

Thanks again for the assistance.  This conversation gave me a useful
insight into one of those things you already probably know: GDB's use
of SIGSTOP instead of another signal is not significant.  We want the
process to be signalled, that's all.  Since each SIGSTOP we send stops
the whole group, I suspect if I checked using PTRACE_GETSIGINFO I
would find most of the threads in finish_stop and not fully ptrace
controllable.  To preserve our last vestiges of LinuxThreads support
we have to continue sending SIGSTOP once per thread, or else use a
different signal.

I'm not going to make that change right now, but I will add some
comments about it.  We could probably use an RT signal at each point
we want a stop.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-13  9:35                   ` Pedro Alves
@ 2008-04-13 13:40                     ` Pedro Alves
  0 siblings, 0 replies; 52+ messages in thread
From: Pedro Alves @ 2008-04-13 13:40 UTC (permalink / raw)
  To: gdb-patches
  Cc: Daniel Jacobowitz, Roland McGrath, Jan Kratochvil, Doug Evans,
	mark.kettenis

Darn, I'll never learn...  

A Saturday 12 April 2008 17:37:48, Pedro Alves wrote:
   if (GET_LWP (lp->ptid) != GET_PID (lp->ptid))
     {
+      int status = 0;
+
+      /* Pass on any pending signal for this LWP.  */
+      get_pending_status (lwp_list, &status);
                               ^
Obvious pasto here, should be lp...

Sorry for the flooding.

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-12  0:19                 ` Pedro Alves
@ 2008-04-13  9:35                   ` Pedro Alves
  2008-04-13 13:40                     ` Pedro Alves
  0 siblings, 1 reply; 52+ messages in thread
From: Pedro Alves @ 2008-04-13  9:35 UTC (permalink / raw)
  To: gdb-patches
  Cc: Daniel Jacobowitz, Roland McGrath, Jan Kratochvil, Doug Evans,
	mark.kettenis

[-- Attachment #1: Type: text/plain, Size: 806 bytes --]

A Saturday 12 April 2008 00:21:33, Pedro Alves wrote:
> A Friday 11 April 2008 23:54:48, Pedro Alves wrote:
> > A Friday 11 April 2008 23:19:58, Daniel Jacobowitz wrote:
> > > Any idea how to do this?
> >

> Actually, get_last_target_status should give you the ptid
> you want, I think.

Like so ?

The only way I could find to pass args != NULL, was to
attach, and then "quit $signo".

"detach $SIGNO" doesn't work:

(gdb) detach 21
Undefined detach command: "21".  Try "help detach".

-- and was glad it didn't.  I'm was hoping we can use
"detach $pid" in the future, to mirror "attach $pid",
but now I'm not sure we can...

Anyway, "q 19" did send a SIGSTOP, and the process
was stopped.

And I was able to attach to stopped processes, and
the new tests passed, which is nice.  Hurray!

-- 
Pedro Alves

[-- Attachment #2: attach_stopped_pend_sig.diff --]
[-- Type: text/x-diff, Size: 3404 bytes --]

2008-04-12  Pedro Alves  <pedro@codesourcery.com>

	* linux-nat.c (get_pending_status): New.
	(detach_callback): Pass pending signal.
	(linux_nat_detach): Pass pending signal.

---
 gdb/linux-nat.c |   50 ++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 40 insertions(+), 10 deletions(-)

Index: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c	2008-04-12 16:20:27.000000000 +0100
+++ src/gdb/linux-nat.c	2008-04-12 17:36:54.000000000 +0100
@@ -1289,6 +1289,36 @@ linux_nat_attach (char *args, int from_t
     }
 }
 
+/* Get pending status of LP.  */
+static int
+get_pending_status (struct lwp_info *lp, int *status)
+{
+  struct target_waitstatus last;
+  ptid_t last_ptid;
+
+  get_last_target_status (&last_ptid, &last);
+
+  /* If this lwp is the ptid that GDB is processing an event from, the
+     signal will be in stop_signal.  Otherwise, in all-stop + sync
+     mode, we may cache pending events in lp->status while trying to
+     stop all threads (see stop_wait_callback).  In async mode, the
+     events are always cached in waitpid_queue.  */
+
+  *status = 0;
+  if (GET_LWP (lp->ptid) == GET_LWP (last_ptid))
+    {
+      if (stop_signal != TARGET_SIGNAL_0
+	  && signal_pass_state (stop_signal))
+	*status = W_STOPCODE (target_signal_to_host (stop_signal));
+    }
+  else if (target_can_async_p ())
+    queued_waitpid (GET_LWP (lp->ptid), status, __WALL);
+  else
+    *status = lp->status;
+
+  return 0;
+}
+
 static int
 detach_callback (struct lwp_info *lp, void *data)
 {
@@ -1311,18 +1341,18 @@ detach_callback (struct lwp_info *lp, vo
       lp->signalled = 0;
     }
 
-  /* Pass on the last signal, if appropriate.  */
-  if (lp->status == 0 && GET_LWP (lp->ptid) == GET_LWP (inferior_ptid)
-      && stop_signal != TARGET_SIGNAL_0 && signal_pass_state (stop_signal))
-    lp->status = W_STOPCODE (target_signal_to_host (stop_signal));
-
   /* We don't actually detach from the LWP that has an id equal to the
      overall process id just yet.  */
   if (GET_LWP (lp->ptid) != GET_PID (lp->ptid))
     {
+      int status = 0;
+
+      /* Pass on any pending signal for this LWP.  */
+      get_pending_status (lwp_list, &status);
+
       errno = 0;
       if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
-		  WSTOPSIG (lp->status)) < 0)
+		  WSTOPSIG (status)) < 0)
 	error (_("Can't detach %s: %s"), target_pid_to_str (lp->ptid),
 	       safe_strerror (errno));
 
@@ -1332,7 +1362,6 @@ detach_callback (struct lwp_info *lp, vo
 			    target_pid_to_str (lp->ptid),
 			    strsignal (WSTOPSIG (lp->status)));
 
-      drain_queued_events (GET_LWP (lp->ptid));
       delete_lwp (lp->ptid);
     }
 
@@ -1349,14 +1378,15 @@ linux_nat_detach (char *args, int from_t
   if (target_can_async_p ())
     linux_nat_async (NULL, 0);
 
-  iterate_over_lwps (detach_callback, &status);
+  iterate_over_lwps (detach_callback, NULL);
 
   /* Only the initial process should be left right now.  */
   gdb_assert (num_lwps == 1);
 
   /* Pass on any pending signal for the last LWP.  */
-  status = lwp_list->status;
-  if (WIFSTOPPED (status) && (args == NULL || *args == '\0'))
+  if ((args == NULL || *args == '\0')
+      && get_pending_status (lwp_list, &status) != -1
+      && WIFSTOPPED (status))
     {
       args = alloca (8);
       sprintf (args, "%d", (int) WSTOPSIG (status));

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-11 22:20         ` Daniel Jacobowitz
  2008-04-11 22:21           ` Pedro Alves
  2008-04-12 16:38           ` Roland McGrath
@ 2008-04-12 16:43           ` Eli Zaretskii
  2 siblings, 0 replies; 52+ messages in thread
From: Eli Zaretskii @ 2008-04-12 16:43 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: gdb-patches

> Date: Fri, 11 Apr 2008 17:02:21 -0400
> From: Daniel Jacobowitz <drow@false.org>
> Cc: Jan Kratochvil <jan.kratochvil@redhat.com>, Doug Evans <dje@google.com>, 	GDB Patches <gdb-patches@sourceware.org>, mark.kettenis@xs4all.nl
> 
> And all that leaves me with the attached patch.  Anyone else want to
> test it before I check it in, or see any problems with it?  Tested
> x86_64-linux.
> 
> -- 
> Daniel Jacobowitz
> CodeSourcery
> 
> 2008-04-11  Jan Kratochvil  <jan.kratochvil@redhat.com>
> 	    Daniel Jacobowitz  <dan@codesourcery.com>
> 
> 	* NEWS: Mention attach to stopped process fix.

This part is approved.  Thanks.


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-11 22:20         ` Daniel Jacobowitz
  2008-04-11 22:21           ` Pedro Alves
@ 2008-04-12 16:38           ` Roland McGrath
  2008-04-12 16:43           ` Eli Zaretskii
  2 siblings, 0 replies; 52+ messages in thread
From: Roland McGrath @ 2008-04-12 16:38 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: Jan Kratochvil, Doug Evans, GDB Patches, mark.kettenis

> PTRACE_KILL; that only sets the exit_code and wakes the process but at

PTRACE_KILL is completely useless AFAIK.  Sending SIGKILL with kill always
works (except in a few buggy kernels from a short window a few years ago).

> I feel like there's a kernel bug here somewhere, but it's buried in
> the depths of poorly defined ptrace semantics, so maybe not.  I think
> it prudent not to deliberately put things into the job-control stop
> state because of this limitation.

There isn't really any entirely satisfactory definition of "right" I can
find, because it's just so wrong that the real parent will never see
CLD_CONTINUED or CLD_STOPPED indicators no matter what.  I gave up on
ptrace meaning anything other than whatever it has stably meant for kernels
in use for a good while.

> What we need is to get from the job control stopped state, back into
> the signal delivery stopped state.  Jan's artificially delivered
> SIGCONT is the only way I can think of to do this.

Or you can use any unblocked signal with tkill to get one pending and then
PTRACE_CONT,tid,0,0 to force the wakeup.  Generating SIGCONT (blocked or
not) will wake up all the threads in the process, which may not be what you
want.  You could use a signal >= SIGRTMIN to get your synthetic one queued
separately from any that might already be there, and then recognize yours
by si_pid when you see it stop.


Thanks,
Roland


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-11 17:46         ` Jan Kratochvil
  2008-04-11 19:01           ` Daniel Jacobowitz
@ 2008-04-12  7:58           ` Roland McGrath
  2008-04-14 15:09             ` Daniel Jacobowitz
  1 sibling, 1 reply; 52+ messages in thread
From: Roland McGrath @ 2008-04-12  7:58 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Daniel Jacobowitz, Doug Evans, GDB Patches, mark.kettenis

> There is a race if:
> * inferior was not stopped (a regular running process)

Ah, right.  You can also do:

	PTRACE_ATTACH		-> pending SIGSTOP
	... if running		-> stopped, no pending SIGSTOP
	... if stopped		-> still stopped, pending SIGSTOP
	tkill (SIGSTOP, tid)	-> pending SIGSTOP

	PTRACE_CONT, tid, 0	-> if not stopped the first time yet, ESRCH
				-> if stopped, wakes up, dequeues SIGSTOP
	wait			-> always see it stop

You just don't know whether you'll see one more spurious SIGSTOP or not.
(Once you've waited, you can synchronously check /proc, but that doesn't
tell you whether it was your spurious one or just an outside one sent
right now.)


Thanks,
Roland


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-12  0:02               ` Pedro Alves
@ 2008-04-12  0:19                 ` Pedro Alves
  2008-04-13  9:35                   ` Pedro Alves
  0 siblings, 1 reply; 52+ messages in thread
From: Pedro Alves @ 2008-04-12  0:19 UTC (permalink / raw)
  To: gdb-patches
  Cc: Daniel Jacobowitz, Roland McGrath, Jan Kratochvil, Doug Evans,
	mark.kettenis

A Friday 11 April 2008 23:54:48, Pedro Alves wrote:
> A Friday 11 April 2008 23:19:58, Daniel Jacobowitz wrote:

> > Any idea how to do this?
>
> Not really.  I'm don't think we can context-switch
> stop_signal in all-stop mode.  At least not without more work
> in infrun.c.  I'd have to think and try a bit more.  My current
> patch leaves it global in all-stop mode.  Maybe you just
> need a event_ptid, similar to trap_ptid, or just resend the signal
> to the main thread.

Actually, get_last_target_status should give you the ptid
you want, I think.

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-11 22:25             ` Daniel Jacobowitz
@ 2008-04-12  0:02               ` Pedro Alves
  2008-04-12  0:19                 ` Pedro Alves
  0 siblings, 1 reply; 52+ messages in thread
From: Pedro Alves @ 2008-04-12  0:02 UTC (permalink / raw)
  To: gdb-patches
  Cc: Daniel Jacobowitz, Roland McGrath, Jan Kratochvil, Doug Evans,
	mark.kettenis

A Friday 11 April 2008 23:19:58, Daniel Jacobowitz wrote:
> On Fri, Apr 11, 2008 at 11:00:37PM +0100, Pedro Alves wrote:
> >  > +  /* Pass on the last signal, if appropriate.  */
> >
> > > +  if (lp->status == 0 && GET_LWP (lp->ptid) == GET_LWP (inferior_ptid)
> > > +      && stop_signal != TARGET_SIGNAL_0 && signal_pass_state
> > > (stop_signal)) +    lp->status = W_STOPCODE (target_signal_to_host
> > > (stop_signal));
> >
> > You're writing to lp->status of inferior_ptid, which isn't garantied
> > to be the main thread or the thread that got the last signal (user
> > may have used the "thread" command to switch threads, or we're stopped
> > at a breakpoint in some other thread not the main one, for example, or
> > the kernel decided to send the signal to some other thread because the
> > main one was already stopped?).
>
> I want the ptid associated with the global variable stop_signal.  It's
> deliberately not the ptid of the main thread but I hadn't thought
> about the thread command.

Ok, but the last lwp in detach_command is always the main lwp, not
inferior_ptid, even if the user doesn't use the "thread" command.

That is, this is the main thread:

>+  /* Pass on any pending signal for the last LWP.  */
>+  status = lwp_list->status;

but back in detach_callback, if the inferior_ptid's lwp is not the main
thread, you're writing the lp->status to an lwp that is going to
be deleted right afterwards:

  /* We don't actually detach from the LWP that has an id equal to the
     overall process id just yet.  */
  if (GET_LWP (lp->ptid) != GET_PID (lp->ptid))
    {
      (...)
      delete_lwp (lp->ptid);
    }

> Is that variable context-switched (or should it be)?

Not currently.  One of my non-stop patches makes it so.
Currently, in all-stop mode, if the user switches threads
after getting a signal, when resuming, GDB will pass
the signal to the current thread, not the one that got
it in the first place...

> Any idea how to do this?  

Not really.  I'm don't think we can context-switch
stop_signal in all-stop mode.  At least not without more work
in infrun.c.  I'd have to think and try a bit more.  My current
patch leaves it global in all-stop mode.  Maybe you just
need a event_ptid, similar to trap_ptid, or just resend the signal
to the main thread.

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-11 22:21           ` Pedro Alves
@ 2008-04-11 22:25             ` Daniel Jacobowitz
  2008-04-12  0:02               ` Pedro Alves
  0 siblings, 1 reply; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-04-11 22:25 UTC (permalink / raw)
  To: Pedro Alves
  Cc: gdb-patches, Roland McGrath, Jan Kratochvil, Doug Evans, mark.kettenis

On Fri, Apr 11, 2008 at 11:00:37PM +0100, Pedro Alves wrote:
>  > +  /* Pass on the last signal, if appropriate.  */
> > +  if (lp->status == 0 && GET_LWP (lp->ptid) == GET_LWP (inferior_ptid)
> > +      && stop_signal != TARGET_SIGNAL_0 && signal_pass_state (stop_signal))
> > +    lp->status = W_STOPCODE (target_signal_to_host (stop_signal));
> 
> You're writing to lp->status of inferior_ptid, which isn't garantied
> to be the main thread or the thread that got the last signal (user
> may have used the "thread" command to switch threads, or we're stopped
> at a breakpoint in some other thread not the main one, for example, or
> the kernel decided to send the signal to some other thread because the
> main one was already stopped?).

I want the ptid associated with the global variable stop_signal.  It's
deliberately not the ptid of the main thread but I hadn't thought
about the thread command.  Any idea how to do this?  Is that variable
context-switched (or should it be)?

> >-  iterate_over_lwps (detach_callback, NULL);
> >+  iterate_over_lwps (detach_callback, &status);
> >
> 
> Passing &status seems bogus, since you're passing the status
> in lp->status ?

Stray from the previous version.  Removed, thanks.

> I don't see where you're actually sending the signal.  Even if you
> did, it isn't guarantied you are passing this signal.  It will miss it
> if inferior_ptid isn't the main thread.  See comments above.
> 
> Also, why the alloca dance instead of using
> "Sending signal %d to %s\n" directly, if you're not doing anything
> else with args?  AKA, what's the plan for args?

These two are related - it goes to inf_ptrace_detach just outside the
range of the context diff.

> What's supposed to happen if stop_signal was a SIGTRAP ?

Then it shouldn't have signal_pass_state set.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-11 22:20         ` Daniel Jacobowitz
@ 2008-04-11 22:21           ` Pedro Alves
  2008-04-11 22:25             ` Daniel Jacobowitz
  2008-04-12 16:38           ` Roland McGrath
  2008-04-12 16:43           ` Eli Zaretskii
  2 siblings, 1 reply; 52+ messages in thread
From: Pedro Alves @ 2008-04-11 22:21 UTC (permalink / raw)
  To: gdb-patches
  Cc: Daniel Jacobowitz, Roland McGrath, Jan Kratochvil, Doug Evans,
	mark.kettenis

Some quick comments,

A Friday 11 April 2008 22:02:21, Daniel Jacobowitz wrote:

 > +  /* Pass on the last signal, if appropriate.  */
> +  if (lp->status == 0 && GET_LWP (lp->ptid) == GET_LWP (inferior_ptid)
> +      && stop_signal != TARGET_SIGNAL_0 && signal_pass_state (stop_signal))
> +    lp->status = W_STOPCODE (target_signal_to_host (stop_signal));

You're writing to lp->status of inferior_ptid, which isn't garantied
to be the main thread or the thread that got the last signal (user
may have used the "thread" command to switch threads, or we're stopped
at a breakpoint in some other thread not the main one, for example, or
the kernel decided to send the signal to some other thread because the
main one was already stopped?).

>   /* We don't actually detach from the LWP that has an id equal to the
>      overall process id just yet.  */
>   if (GET_LWP (lp->ptid) != GET_PID (lp->ptid))
>@@ -1263,14 +1343,29 @@ static void
> linux_nat_detach (char *args, int from_tty)
> {
>   int pid;
>+  int status;
>+  enum target_signal sig;
>+
>   if (target_can_async_p ())
>     linux_nat_async (NULL, 0);
> 
>-  iterate_over_lwps (detach_callback, NULL);
>+  iterate_over_lwps (detach_callback, &status);
>

Passing &status seems bogus, since you're passing the status
in lp->status ?
 
>   /* Only the initial process should be left right now.  */
>   gdb_assert (num_lwps == 1);
> 
>+  /* Pass on any pending signal for the last LWP.  */
>+  status = lwp_list->status;
>+  if (WIFSTOPPED (status) && (args == NULL || *args == '\0'))
>+    {
>+      args = alloca (8);
>+      sprintf (args, "%d", (int) WSTOPSIG (status));
>+      fprintf_unfiltered (gdb_stdlog,
>+                         "LND: Sending signal %s to %s\n",
>+                         args,
>+                         target_pid_to_str (lwp_list->ptid));
>+    }
>+
>

I don't see where you're actually sending the signal.  Even if you
did, it isn't guarantied you are passing this signal.  It will miss it
if inferior_ptid isn't the main thread.  See comments above.

Also, why the alloca dance instead of using
"Sending signal %d to %s\n" directly, if you're not doing anything
else with args?  AKA, what's the plan for args?

What's supposed to happen if stop_signal was a SIGTRAP ?

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-11  8:46       ` Roland McGrath
  2008-04-11 17:46         ` Jan Kratochvil
@ 2008-04-11 22:20         ` Daniel Jacobowitz
  2008-04-11 22:21           ` Pedro Alves
                             ` (2 more replies)
  1 sibling, 3 replies; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-04-11 22:20 UTC (permalink / raw)
  To: Roland McGrath; +Cc: Jan Kratochvil, Doug Evans, GDB Patches, mark.kettenis

On Thu, Apr 10, 2008 at 04:12:05PM -0700, Roland McGrath wrote:
> > As the shell already did WAITPID on the process and ate the SIGSTOP
> > notification so there is no other notification left there for GDB.
> > A process already stopped does not generate another SIGSTOP during
> > PTRACE_ATTACH as signals never count.
> 
> To be pedantic about it: a SIGSTOP is generated, but causes no wakeup or
> delivery for ptrace to see (because it's already stopped).  You can see
> the SIGSTOP pending in /proc/pid/status.  Generating a SIGCONT clears
> any pending stop signals, so normally it's as if it never existed.  But,
> I think if you were to do PTRACE_CONT,pid,0,0 you would make it wake up,
> dequeue the new SIGSTOP and then report to you for wait/SIGCHLD.  And
> since PTRACE_CONT would give ESRCH if it weren't stopped yet, you might
> be able to do PTRACE_ATTACH;PTRACE_CONT;wait reliably.  But don't hold
> me to it.

There's a race: if the SIGSTOP has already stopped the attachee,
whether or not it's been waited for yet, PTRACE_CONT will clear it.
So PTRACE_CONT,pid,0,0 is quite likely to leave it running.

I also experimented with PTRACE_ATTACH; PTRACE_CONT,pid,0,SIGSTOP;
waitpid.  We go from ptrace_stop in get_signal_to_deliver, off to the
normal signal handling via finish_stop.  This is not so good, because
normal ptrace behavior doesn't apply there.  For instance, we can't
PTRACE_KILL; that only sets the exit_code and wakes the process but at
this point nothing reads the exit code so the process starts running
again.  For the same reason, we can't send a signal during wakeup
(PTRACE_CONT is basically the same as PTRACE_KILL in this regard).

I feel like there's a kernel bug here somewhere, but it's buried in
the depths of poorly defined ptrace semantics, so maybe not.  I think
it prudent not to deliberately put things into the job-control stop
state because of this limitation.

We can determine where we are either by parsing /proc, or by trying
PTRACE_GETSIGINFO; at the moment, at least, it returns EINVAL if we
are in some other stopped state that did not come through ptrace_stop
(the comments above ptrace_stop describe this too; I think I wrote
that...).  As our first ptrace call will transform TASK_STOPPED into
TASK_TRACED /proc is somewhat tricky but fortunately I only need
it at the very beginning of the attach process.

What we need is to get from the job control stopped state, back into
the signal delivery stopped state.  Jan's artificially delivered
SIGCONT is the only way I can think of to do this.

Which gives me these steps:

  PTRACE_ATTACH
    A SIGSTOP is generated.  The process may still run until it is
    delivered.  If it was running, we will be able to wait for it
    and it will end up in ptrace_stop.  If it was stopped but not
    waited for by its parent, we will be able to wait for it and it
    will end up in job control stop.  If it was stopped and waited
    for, it will already be in job control stop and we will not be
    able to wait for it.

  Check /proc
    The process may be running.  If so, waiting for it will be fine.
    It may be in a tracing stop.  If so, it's our tracing stop, and
    waiting for it will be fine.  It may be blocked - not much we can
    do about that, we have to wait.  It may be stopped.  On old
    kernels I believe this will happen even if we're in a tracing
    stop, so whatever we do should work OK from tracing stops too.

  If the process was T (stopped):
    In this case, waiting might or might not work (depending on
    whether the real parent has waited yet), but if it's going
    to work it will immediately.  So we can use WNOHANG.  After that,
    whether the wait succeeded or not, we can get the process into a
    sane state by tkill(SIGCONT) and waiting for it.  If a signal
    other than the SIGCONT arrives, we have to be prepared for the
    SIGCONT to show up again later.  The least intrusive way to handle
    that in GDB is, oddly, tkill (SIGSTOP) to cancel the pending SIGCONT.

  Otherwise:
    Just wait for the process and expect the SIGSTOP.

We can do it a little more simply than in Jan's patch, by making the
GDB core display non-SIGSTOP signals that come in during attach.  As
far as I know this should be safe for other targets.

Then I worked through the RH package's tests and fixed the rest of
them, again a little differently.  Sorry about that.  I deleted the
tests that detach left processes stopped, since I don't think it's
in line with GDB's past behavior.  Otherwise I left the tests pretty
much alone (though restricted to Linux native); we'll see how portable
they end up being.

And all that leaves me with the attached patch.  Anyone else want to
test it before I check it in, or see any problems with it?  Tested
x86_64-linux.

-- 
Daniel Jacobowitz
CodeSourcery

2008-04-11  Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Daniel Jacobowitz  <dan@codesourcery.com>

	* NEWS: Mention attach to stopped process fix.
	* infcmd.c (detach_command, disconnect_command): Discard the thread
	list.
	* infrun.c (handle_inferior_event): Do not ignore non-SIGSTOP while
	attaching.  Use signal_stop_state.
	(signal_stop_state): Check stop_soon.
	* linux-nat.c (kill_lwp): Declare earlier.
	(pid_is_stopped, linux_nat_post_attach_wait): New.
	(lin_lwp_attach_lwp): Use linux_nat_post_attach_wait.  Update
	comments.
	(linux_nat_attach): Use linux_nat_post_attach_wait.
	(detach_callback, linux_nat_detach): Improve handling for signalled
	processes.
	(linux_nat_pid_to_str): Always print out the LWP ID if it differs
	from the process ID.
	* Makefile.in (infcmd.o): Update.

2008-04-11  Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Daniel Jacobowitz  <dan@codesourcery.com>

	* gdb.threads/attach-into-signal.c, gdb.threads/attach-into-signal.exp,
	gdb.threads/attach-stopped.c, gdb.threads/attach-stopped.exp,
	gdb.threads/attachstop-mt.c, gdb.threads/attachstop-mt.exp: New.

Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.263
diff -u -p -r1.263 NEWS
--- NEWS	14 Mar 2008 22:30:07 -0000	1.263
+++ NEWS	11 Apr 2008 20:40:19 -0000
@@ -7,7 +7,11 @@
 which will be allocated using malloc later in program execution.
 
 * The qXfer:libraries:read remote procotol packet now allows passing a
-  list of section offsets.
+list of section offsets.
+
+* On GNU/Linux, GDB can now attach to stopped processes.  Several race
+conditions handling signals delivered during attach or thread creation
+have also been fixed.
 
 * New features in the GDB remote stub, gdbserver
 
Index: infcmd.c
===================================================================
RCS file: /cvs/src/src/gdb/infcmd.c,v
retrieving revision 1.174
diff -u -p -r1.174 infcmd.c
--- infcmd.c	17 Mar 2008 17:30:29 -0000	1.174
+++ infcmd.c	11 Apr 2008 20:40:20 -0000
@@ -49,6 +49,7 @@
 #include "target-descriptions.h"
 #include "user-regs.h"
 #include "exceptions.h"
+#include "gdbthread.h"
 
 /* Functions exported for general use, in inferior.h: */
 
@@ -2067,6 +2068,7 @@ detach_command (char *args, int from_tty
   dont_repeat ();		/* Not for the faint of heart.  */
   target_detach (args, from_tty);
   no_shared_libraries (NULL, from_tty);
+  init_thread_list ();
   if (deprecated_detach_hook)
     deprecated_detach_hook ();
 }
@@ -2085,6 +2087,7 @@ disconnect_command (char *args, int from
   dont_repeat ();		/* Not for the faint of heart */
   target_disconnect (args, from_tty);
   no_shared_libraries (NULL, from_tty);
+  init_thread_list ();
   if (deprecated_detach_hook)
     deprecated_detach_hook ();
 }
Index: infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.267
diff -u -p -r1.267 infrun.c
--- infrun.c	15 Mar 2008 14:55:21 -0000	1.267
+++ infrun.c	11 Apr 2008 20:40:20 -0000
@@ -1947,13 +1947,15 @@ handle_inferior_event (struct execution_
 
       /* This originates from attach_command().  We need to overwrite
          the stop_signal here, because some kernels don't ignore a
-         SIGSTOP in a subsequent ptrace(PTRACE_SONT,SOGSTOP) call.
-         See more comments in inferior.h.  */
-      if (stop_soon == STOP_QUIETLY_NO_SIGSTOP)
+         SIGSTOP in a subsequent ptrace(PTRACE_CONT,SIGSTOP) call.
+         See more comments in inferior.h.  On the other hand, if we
+	 get a non-SIGSTOP, report it to the user - assume the backend
+	 will handle the SIGSTOP if it should show up later.  */
+      if (stop_soon == STOP_QUIETLY_NO_SIGSTOP
+	  && stop_signal == TARGET_SIGNAL_STOP)
 	{
 	  stop_stepping (ecs);
-	  if (stop_signal == TARGET_SIGNAL_STOP)
-	    stop_signal = TARGET_SIGNAL_0;
+	  stop_signal = TARGET_SIGNAL_0;
 	  return;
 	}
 
@@ -2024,7 +2026,7 @@ process_event_stop_test:
 	  target_terminal_ours_for_output ();
 	  print_stop_reason (SIGNAL_RECEIVED, stop_signal);
 	}
-      if (signal_stop[stop_signal])
+      if (signal_stop_state (stop_signal))
 	{
 	  stop_stepping (ecs);
 	  return;
@@ -3287,7 +3289,9 @@ hook_stop_stub (void *cmd)
 int
 signal_stop_state (int signo)
 {
-  return signal_stop[signo];
+  /* Always stop on signals if we're just gaining control of the
+     program.  */
+  return signal_stop[signo] || stop_soon != NO_STOP_QUIETLY;
 }
 
 int
Index: linux-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/linux-nat.c,v
retrieving revision 1.80
diff -u -p -r1.80 linux-nat.c
--- linux-nat.c	25 Mar 2008 12:26:21 -0000	1.80
+++ linux-nat.c	11 Apr 2008 20:40:20 -0000
@@ -186,6 +186,7 @@ static void linux_nat_async (void (*call
 			     (enum inferior_event_type event_type, void *context),
 			     void *context);
 static int linux_nat_async_mask (int mask);
+static int kill_lwp (int lwpid, int signo);
 
 /* Captures the result of a successful waitpid call, along with the
    options used in that call.  */
@@ -1010,10 +1011,125 @@ exit_lwp (struct lwp_info *lp)
   delete_lwp (lp->ptid);
 }
 
-/* Attach to the LWP specified by PID.  If VERBOSE is non-zero, print
-   a message telling the user that a new LWP has been added to the
-   process.  Return 0 if successful or -1 if the new LWP could not
-   be attached.  */
+/* Detect `T (stopped)' in `/proc/PID/status'.
+   Other states including `T (tracing stop)' are reported as false.  */
+
+static int
+pid_is_stopped (pid_t pid)
+{
+  FILE *status_file;
+  char buf[100];
+  int retval = 0;
+
+  snprintf (buf, sizeof (buf), "/proc/%d/status", (int) pid);
+  status_file = fopen (buf, "r");
+  if (status_file != NULL)
+    {
+      int have_state = 0;
+
+      while (fgets (buf, sizeof (buf), status_file))
+	{
+	  if (strncmp (buf, "State:", 6) == 0)
+	    {
+	      have_state = 1;
+	      break;
+	    }
+	}
+      if (have_state && strstr (buf, "T (stopped)") != NULL)
+	retval = 1;
+      fclose (status_file);
+    }
+  return retval;
+}
+
+/* Wait for the LWP specified by LP, which we have just attached to.
+   Returns a wait status for that LWP, to cache.  */
+
+static int
+linux_nat_post_attach_wait (ptid_t ptid, int first, int *cloned,
+			    int *signalled)
+{
+  pid_t new_pid, pid = GET_LWP (ptid);
+  int sent_sigcont = 0;
+  int status;
+
+  if (pid_is_stopped (pid))
+    {
+      if (debug_linux_nat)
+	fprintf_unfiltered (gdb_stdlog,
+			    "LNPAW: Attaching to a stopped process\n");
+
+      /* Consume any pending SIGSTOP, if there is one.  */
+      new_pid = my_waitpid (pid, &status, WNOHANG);
+      if (new_pid == -1 && errno == ECHILD)
+	new_pid = my_waitpid (pid, &status, __WCLONE | WNOHANG);
+      if (debug_linux_nat)
+	fprintf_unfiltered (gdb_stdlog,
+			    "LNPAW: Wait for stopped process returned %ld, "
+			    "status %s\n",
+			    (long) new_pid, status_to_str (status));
+
+      /* The process is definitely stopped, whether the waits above succeeded
+	 or not.  It is in a job control stop, unless the kernel predates
+	 the TASK_STOPPED / TASK_TRACED distinction, in which case it might
+	 be in a ptrace stop.  Make sure it is in a ptrace stop; from
+	 there we can kill it, signal it, et cetera.  */
+	kill_lwp (pid, SIGCONT);
+	sent_sigcont = 1;
+      }
+
+  /* Make sure the initial process is stopped.  The user-level threads
+     layer might want to poke around in the inferior, and that won't
+     work if things haven't stabilized yet.  */
+  new_pid = my_waitpid (pid, &status, 0);
+  if (new_pid == -1 && errno == ECHILD)
+    {
+      if (first)
+	warning (_("%s is a cloned process"), target_pid_to_str (ptid));
+
+      /* Try again with __WCLONE to check cloned processes.  */
+      new_pid = my_waitpid (pid, &status, __WCLONE);
+      *cloned = 1;
+    }
+
+  gdb_assert (pid == new_pid && WIFSTOPPED (status));
+
+  if (sent_sigcont)
+    {
+      gdb_assert (WSTOPSIG (status) != SIGSTOP);
+
+      if (WSTOPSIG (status) != SIGCONT)
+	{
+	  /* There's a pending SIGCONT, but a higher-priority
+	     (i.e. lower-numbered) signal was delivered.  Clear the
+	     pending SIGCONT; we already know how to handle a pending
+	     SIGSTOP, and this will clear the pending SIGCONT.  */
+	  kill_lwp (GET_PID (inferior_ptid), SIGSTOP);
+	  *signalled = 1;
+
+	  if (debug_linux_nat)
+	    fprintf_unfiltered (gdb_stdlog,
+				"LNPAW: Received a signal after SIGCONT; "
+				"sending SIGSTOP\n");
+	}
+      else
+	/* Pretend the whole SIGCONT dance never happened.  */
+	status = W_STOPCODE (SIGSTOP);
+    }
+  else if (WSTOPSIG (status) != SIGSTOP)
+    {
+      *signalled = 1;
+      if (debug_linux_nat)
+	fprintf_unfiltered (gdb_stdlog,
+			    "LNPAW: Received %s after attaching\n",
+			    status_to_str (status));
+    }
+
+  return status;
+}
+
+/* Attach to the LWP specified by PID.  Return 0 if successful or -1
+   if the new LWP could not be attached.  */
 
 int
 lin_lwp_attach_lwp (ptid_t ptid)
@@ -1036,9 +1152,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
      to happen.  */
   if (GET_LWP (ptid) != GET_PID (ptid) && lp == NULL)
     {
-      pid_t pid;
-      int status;
-      int cloned = 0;
+      int status, cloned = 0, signalled = 0;
 
       if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
 	{
@@ -1057,24 +1171,18 @@ lin_lwp_attach_lwp (ptid_t ptid)
 			    "LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
 			    target_pid_to_str (ptid));
 
-      pid = my_waitpid (GET_LWP (ptid), &status, 0);
-      if (pid == -1 && errno == ECHILD)
+      status = linux_nat_post_attach_wait (ptid, 0, &cloned, &signalled);
+      lp = add_lwp (ptid);
+      lp->stopped = 1;
+      lp->cloned = cloned;
+      lp->signalled = signalled;
+      if (WSTOPSIG (status) != SIGSTOP)
 	{
-	  /* Try again with __WCLONE to check cloned processes.  */
-	  pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
-	  cloned = 1;
+	  lp->resumed = 1;
+	  lp->status = status;
 	}
 
-      gdb_assert (pid == GET_LWP (ptid)
-		  && WIFSTOPPED (status) && WSTOPSIG (status));
-
-      if (lp == NULL)
-	lp = add_lwp (ptid);
-      lp->cloned = cloned;
-
-      target_post_attach (pid);
-
-      lp->stopped = 1;
+      target_post_attach (GET_LWP (lp->ptid));
 
       if (debug_linux_nat)
 	{
@@ -1133,10 +1241,7 @@ static void
 linux_nat_attach (char *args, int from_tty)
 {
   struct lwp_info *lp;
-  pid_t pid;
   int status;
-  int cloned = 0;
-  int options = 0;
 
   /* FIXME: We should probably accept a list of process id's, and
      attach all of them.  */
@@ -1151,49 +1256,34 @@ linux_nat_attach (char *args, int from_t
       sigdelset (&suspend_mask, SIGCHLD);
     }
 
-  /* Make sure the initial process is stopped.  The user-level threads
-     layer might want to poke around in the inferior, and that won't
-     work if things haven't stabilized yet.  */
-  pid = my_waitpid (GET_PID (inferior_ptid), &status, options);
-  if (pid == -1 && errno == ECHILD)
-    {
-      warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
-
-      /* Try again with __WCLONE to check cloned processes.  */
-      options = __WCLONE;
-      pid = my_waitpid (GET_PID (inferior_ptid), &status, options);
-      cloned = 1;
-    }
-
-  gdb_assert (pid == GET_PID (inferior_ptid)
-	      && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
-
   /* Add the initial process as the first LWP to the list.  */
   inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
   lp = add_lwp (inferior_ptid);
-  lp->cloned = cloned;
+
+  status = linux_nat_post_attach_wait (lp->ptid, 1, &lp->cloned,
+				       &lp->signalled);
+  lp->stopped = 1;
 
   /* If this process is not using thread_db, then we still don't
      detect any other threads, but add at least this one.  */
   add_thread_silent (lp->ptid);
 
-  lp->stopped = 1;
+  /* Save the wait status to report later.  */
   lp->resumed = 1;
+  if (debug_linux_nat)
+    fprintf_unfiltered (gdb_stdlog,
+			"LNA: waitpid %ld, saving status %s\n",
+			(long) GET_PID (lp->ptid), status_to_str (status));
 
   if (!target_can_async_p ())
-    {
-      /* Fake the SIGSTOP that core GDB expects.  */
-      lp->status = W_STOPCODE (SIGSTOP);
-      if (debug_linux_nat)
-	fprintf_unfiltered (gdb_stdlog,
-			    "LNA: waitpid %ld, faking SIGSTOP\n", (long) pid);
-    }
+    lp->status = status;
   else
     {
       /* We already waited for this LWP, so put the wait result on the
 	 pipe.  The event loop will wake up and gets us to handling
 	 this event.  */
-      linux_nat_event_pipe_push (pid, status, options);
+      linux_nat_event_pipe_push (GET_PID (lp->ptid), status,
+				 lp->cloned ? __WCLONE : 0);
       /* Register in the event loop.  */
       target_async (inferior_event_handler, 0);
     }
@@ -1209,33 +1299,23 @@ detach_callback (struct lwp_info *lp, vo
 			strsignal (WSTOPSIG (lp->status)),
 			target_pid_to_str (lp->ptid));
 
-  while (lp->signalled && lp->stopped)
+  /* If there is a pending SIGSTOP, get rid of it.  */
+  if (lp->signalled)
     {
-      errno = 0;
-      if (ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0,
-		  WSTOPSIG (lp->status)) < 0)
-	error (_("Can't continue %s: %s"), target_pid_to_str (lp->ptid),
-	       safe_strerror (errno));
-
       if (debug_linux_nat)
 	fprintf_unfiltered (gdb_stdlog,
-			    "DC:  PTRACE_CONTINUE (%s, 0, %s) (OK)\n",
-			    target_pid_to_str (lp->ptid),
-			    status_to_str (lp->status));
+			    "DC: Sending SIGCONT to %s\n",
+			    target_pid_to_str (lp->ptid));
 
-      lp->stopped = 0;
+      kill_lwp (GET_LWP (lp->ptid), SIGCONT);
       lp->signalled = 0;
-      lp->status = 0;
-      /* FIXME drow/2003-08-26: There was a call to stop_wait_callback
-	 here.  But since lp->signalled was cleared above,
-	 stop_wait_callback didn't do anything; the process was left
-	 running.  Shouldn't we be waiting for it to stop?
-	 I've removed the call, since stop_wait_callback now does do
-	 something when called with lp->signalled == 0.  */
-
-      gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status));
     }
 
+  /* Pass on the last signal, if appropriate.  */
+  if (lp->status == 0 && GET_LWP (lp->ptid) == GET_LWP (inferior_ptid)
+      && stop_signal != TARGET_SIGNAL_0 && signal_pass_state (stop_signal))
+    lp->status = W_STOPCODE (target_signal_to_host (stop_signal));
+
   /* We don't actually detach from the LWP that has an id equal to the
      overall process id just yet.  */
   if (GET_LWP (lp->ptid) != GET_PID (lp->ptid))
@@ -1263,14 +1343,29 @@ static void
 linux_nat_detach (char *args, int from_tty)
 {
   int pid;
+  int status;
+  enum target_signal sig;
+
   if (target_can_async_p ())
     linux_nat_async (NULL, 0);
 
-  iterate_over_lwps (detach_callback, NULL);
+  iterate_over_lwps (detach_callback, &status);
 
   /* Only the initial process should be left right now.  */
   gdb_assert (num_lwps == 1);
 
+  /* Pass on any pending signal for the last LWP.  */
+  status = lwp_list->status;
+  if (WIFSTOPPED (status) && (args == NULL || *args == '\0'))
+    {
+      args = alloca (8);
+      sprintf (args, "%d", (int) WSTOPSIG (status));
+      fprintf_unfiltered (gdb_stdlog,
+			  "LND: Sending signal %s to %s\n",
+			  args,
+			  target_pid_to_str (lwp_list->ptid));
+    }
+
   trap_ptid = null_ptid;
 
   /* Destroy LWP info; it's no longer valid.  */
@@ -2848,7 +2943,9 @@ linux_nat_pid_to_str (ptid_t ptid)
 {
   static char buf[64];
 
-  if (lwp_list && lwp_list->next && is_lwp (ptid))
+  if (is_lwp (ptid)
+      && ((lwp_list && lwp_list->next)
+	  || GET_PID (ptid) != GET_LWP (ptid)))
     {
       snprintf (buf, sizeof (buf), "LWP %ld", GET_LWP (ptid));
       return buf;
Index: testsuite/gdb.threads/attach-into-signal.c
===================================================================
RCS file: testsuite/gdb.threads/attach-into-signal.c
diff -N testsuite/gdb.threads/attach-into-signal.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attach-into-signal.c	11 Apr 2008 20:40:21 -0000
@@ -0,0 +1,69 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2008 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+void action(int sig, siginfo_t * info, void *uc)
+{
+  raise (SIGALRM);
+}
+
+static void *func (void *arg)
+{
+  struct sigaction act;
+
+  memset (&act, 0, sizeof(struct sigaction));
+  act.sa_sigaction = action;
+  act.sa_flags = SA_RESTART;
+  sigaction (SIGALRM, &act, 0);
+
+  raise (SIGALRM);
+
+  /* This should be NOTREACHED but sometimes it is reached - Bug 427860.
+     We never get here without ptrace(2).  It may also be a kernel bug.  */
+  for (;;)
+    pause ();
+
+  abort ();
+  /* NOTREACHED */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
Index: testsuite/gdb.threads/attach-into-signal.exp
===================================================================
RCS file: testsuite/gdb.threads/attach-into-signal.exp
diff -N testsuite/gdb.threads/attach-into-signal.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attach-into-signal.exp	11 Apr 2008 20:40:21 -0000
@@ -0,0 +1,168 @@
+# Copyright 2008
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This test was created by modifying attach-stopped.exp.
+# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+# This test only works on Linux
+if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
+    continue
+}
+
+set testfile "attach-into-signal"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+    # Statistically there is a better chance without giving process a nice.
+
+    set testpid [eval exec $binfile &]
+    exec sleep 2
+
+    # Run 2 passes of the test.
+    # The C file inferior stops pending its signals if a single one is lost,
+    # we test successful redelivery of the caught signal by the 2nd pass.
+
+    # linux-2.6.20.4.x86_64 had maximal attempt # 20 in 4 test runs.
+    set attempts 100
+    set attempt 1
+    set passes 1
+    while { $passes < 3 && $attempt <= $attempts } {
+	set stoppedtry 0
+	while { $stoppedtry < 10 } {
+	    if [catch {open /proc/${testpid}/status r} fileid] {
+		set stoppedtry 10
+		break
+	    }
+	    gets $fileid line1;
+	    gets $fileid line2;
+	    close $fileid;
+
+	    if {![string match "*(stopped)*" $line2]} {
+		# No PASS message as we may be looping in multiple attempts.
+		break
+	    }
+	    sleep 1
+	    set stoppedtry [expr $stoppedtry + 1]
+	}
+	if { $stoppedtry >= 10 } {
+	    verbose -log $line2
+	    set test "$threadtype: process is still running on the attempt # $attempt of $attempts"
+	    break
+	}
+
+	# Main test:
+	set test "$threadtype: attach (pass $passes), pending signal catch"
+	if {[gdb_test_multiple "attach $testpid" $test {
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*Received Alarm clock.*$gdb_prompt $" {
+		# nonthreaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+		# nonthreaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt + 1]
+	    }
+	    -re "Attaching to process $testpid.*Received Alarm clock.*$gdb_prompt $" {
+		# threaded:
+		pass $test
+		verbose -log "$test succeeded on the attempt # $attempt of $attempts"
+		set passes [expr $passes + 1]
+	    }
+	    -re "Attaching to process $testpid.*$gdb_prompt $" {
+		# threaded:
+		# We just lack the luck, we should try it again.
+		set attempt [expr $attempt - 1]
+	    }
+	}] != 0 } {
+	    break
+	}
+
+	gdb_test "detach" "Detaching from.*" ""
+    }
+    if {$passes < 3} {
+	if {$attempt > $attempts} {
+	    unresolved $test
+	} else {
+	    fail $test
+	}
+    }
+
+    # Exit and detach the process.
+       
+    gdb_exit
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+
+    # Continue the program - some Linux kernels need it before -9 if the
+    # process is stopped.
+    remote_exec build "kill -s CONT ${testpid}"
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# Start with clean gdb
+gdb_exit
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+gdb_test "set debug lin-lwp 1" "" ""
+
+corefunc nonthreaded
+
+# build the test case also with threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+gdb_test "set debug lin-lwp 1" "" ""
+
+corefunc threaded
Index: testsuite/gdb.threads/attach-stopped.c
===================================================================
RCS file: testsuite/gdb.threads/attach-stopped.c
diff -N testsuite/gdb.threads/attach-stopped.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attach-stopped.c	11 Apr 2008 20:40:21 -0000
@@ -0,0 +1,50 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2008 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* This program is intended to be started outside of gdb, then
+   manually stopped via a signal.  */
+
+#include <stddef.h>
+#include <unistd.h>
+#ifdef USE_THREADS
+#include <pthread.h>
+#endif
+
+static void *func (void *arg)
+{
+  sleep (10000);  /* Ridiculous time, but we will eventually kill it.  */
+  sleep (10000);  /* Second sleep.  */
+  return NULL;
+}
+
+int main ()
+{
+
+#ifndef USE_THREADS
+
+  func (NULL);
+
+#else
+
+  pthread_t th;
+  pthread_create (&th, NULL, func, NULL);
+  pthread_join (th, NULL);
+
+#endif
+
+  return 0;
+}
Index: testsuite/gdb.threads/attach-stopped.exp
===================================================================
RCS file: testsuite/gdb.threads/attach-stopped.exp
diff -N testsuite/gdb.threads/attach-stopped.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attach-stopped.exp	11 Apr 2008 20:40:21 -0000
@@ -0,0 +1,157 @@
+# Copyright 2008
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This test was created by modifying attach.exp.
+# This file was created by Jeff Johnston <jjohnstn@redhat.com>.
+# This file was updated by Jan Kratochvil <jan.kratochvil@redhat.com>.
+
+# This test only works on Linux
+if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
+    continue
+}
+
+set testfile "attach-stopped"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+#execute_anywhere "rm -f ${binfile}"
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+proc corefunc { threadtype } {
+    global srcfile
+    global binfile
+    global escapedbinfile
+    global srcdir
+    global subdir
+    global gdb_prompt
+
+    if [get_compiler_info ${binfile}] {
+	return -1
+    }
+
+    # Start the program running and then wait for a bit, to be sure
+    # that it can be attached to.
+
+    set testpid [eval exec $binfile &]
+
+    # Avoid some race:
+    sleep 2
+
+    # Stop the program 
+    remote_exec build "kill -s STOP ${testpid}"
+
+    # Start with clean gdb
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Verify that we can attach to the stopped process.
+       
+    set test "$threadtype: attach2 to stopped, after setting file"
+    gdb_test_multiple "attach $testpid" "$test" {
+	-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+	    pass "$test"
+	}
+    }
+
+    # ".*sleep.*clone.*" would fail on s390x as bt stops at START_THREAD there.
+    if {[string equal $threadtype threaded]} {
+	gdb_test "thread apply all bt" ".*sleep.*start_thread.*" "$threadtype: attach2 to stopped bt"
+    } else {
+	gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach2 to stopped bt"
+    }
+    # This breakpoint is there for old/non-x86 kernels not restarting syscalls.
+    gdb_breakpoint [gdb_get_line_number "Second sleep"]
+    set test "$threadtype: attach2 continue"
+    send_gdb "continue\n"
+    gdb_expect {
+      -re "Continuing"
+	{ pass "continue ($test)" }
+      timeout
+	{ fail "continue ($test) (timeout)" }
+    }
+
+    # For this to work we must be sure to consume the "Continuing."
+    # message first, or GDB's signal handler may not be in place.
+    after 1000 {send_gdb "\003"}
+    set test "$threadtype: attach2 stop interrupt"
+    gdb_expect 10 {
+      -re "Program received signal SIGINT.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      -re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
+	{
+	  pass $test
+	}
+      timeout
+	{
+	  fail $test
+	}
+    }
+
+    gdb_exit
+
+    # Avoid some race:
+    sleep 2
+
+    # At this point, the process should be sleeping
+
+    if [catch {open /proc/${testpid}/status r} fileid2] {
+	set line2 "NOTFOUND"
+    } else {
+	gets $fileid2 line1;
+	gets $fileid2 line2;
+	close $fileid2;
+    }
+
+    set test "$threadtype: attach2, exit leaves process sleeping"
+    if {[string match "*(sleeping)*" $line2]} {
+      pass $test
+    } else {
+      fail $test
+    }
+
+    # Make sure we don't leave a process around to confuse
+    # the next test run (and prevent the compile by keeping
+    # the text file busy), in case the "set should_exit" didn't
+    # work.
+       
+    remote_exec build "kill -9 ${testpid}"
+}
+
+# build the test case first without threads
+#
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc nonthreaded
+
+# build the test case first without threads
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
+    gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
+}
+
+corefunc threaded
+
+return 0
Index: testsuite/gdb.threads/attachstop-mt.c
===================================================================
RCS file: testsuite/gdb.threads/attachstop-mt.c
diff -N testsuite/gdb.threads/attachstop-mt.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attachstop-mt.c	11 Apr 2008 20:40:21 -0000
@@ -0,0 +1,55 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2008 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* This program is intended to be started outside of gdb, then
+   manually stopped via a signal.  */
+
+#include <unistd.h>
+#include <pthread.h>
+#include <stdio.h>
+
+/* Red Hat BZ PR 197584.c */
+
+void *func (void *arg)
+{
+  sleep (10000);  /* Ridiculous time, but we will eventually kill it.  */
+  sleep (10000);	/* RHEL3U8 kernel-2.4.21-47.EL will cut the sleep time.  */
+
+  return NULL;	/* thread-sleep */
+}
+
+int main ()
+{
+  pthread_t t1,t2;
+  int ret;
+
+  ret = pthread_create (&t1, NULL, func, NULL);
+  if (ret)
+    fprintf(stderr, "pthread_create(): %s", strerror (ret));
+  ret = pthread_create (&t2, NULL, func, NULL);
+  if (ret)
+    fprintf(stderr, "pthread_create(): %s", strerror (ret));
+
+  ret = pthread_join (t1, NULL);
+  if (ret)	/* first-join */
+    fprintf(stderr, "pthread_join(): %s", strerror (ret));
+  ret = pthread_join (t2, NULL);
+  if (ret)
+    fprintf(stderr, "pthread_join(): %s", strerror (ret));
+
+  return 0;
+}
Index: testsuite/gdb.threads/attachstop-mt.exp
===================================================================
RCS file: testsuite/gdb.threads/attachstop-mt.exp
diff -N testsuite/gdb.threads/attachstop-mt.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/attachstop-mt.exp	11 Apr 2008 20:40:21 -0000
@@ -0,0 +1,260 @@
+# Copyright 2008
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This test was created by modifying gdb.threads/attachstop.
+# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
+# Regression for: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=197584
+
+# This test only works on Linux
+if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
+    continue
+}
+
+set testfile "attachstop-mt"
+set srcfile  ${testfile}.c
+set binfile  ${objdir}/${subdir}/${testfile}
+set escapedbinfile  [string_to_regexp ${objdir}/${subdir}/${testfile}]
+
+#execute_anywhere "rm -f ${binfile}"
+remote_exec build "rm -f ${binfile}"
+# For debugging this test
+#
+#log_user 1
+
+# build the test case
+#
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail."
+}
+
+if [get_compiler_info ${binfile}] {
+    return -1
+}
+
+# Start the program running and then wait for a bit, to be sure
+# that it can be attached to.
+
+set testpid [eval exec $binfile &]
+
+# No race
+sleep 2
+
+# Do not: set testpid2 [expr $testpid + 1]
+# as it will not exist on Red Hat 2.6.9-34.0.2.ELsmp
+set testpid2 [expr $testpid + 2]
+
+set status2 /proc/${testpid}/task/${testpid2}/status
+if {[expr ! [file exists $status2]]} {
+  # kernel-2.4
+  set status2 /proc/${testpid2}/status
+}
+
+# Initial sanity test it is normally sleeping
+set fileid0 [open $status2 r];
+gets $fileid0 line1;
+gets $fileid0 line2;
+close $fileid0;
+
+set test "attach0, initial sanity check of the sleeping state"
+if {[string match "*(sleeping)*" $line2]} {
+  pass $test
+} else {
+  fail $test
+}
+
+# Sttach and detach to test it will not become stopped
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+set test "attach0 to sleeping"
+gdb_test_multiple "attach $testpid" "$test" {
+    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+        pass "$test"
+    }
+}
+
+gdb_test "gcore /dev/null" ".*aved corefile.*" "attach0 to sleeping gcore invocation"
+
+gdb_test "thread 2" ".*witching to thread 2 .*" "attach0 to sleeping switch thread"
+
+gdb_test "bt" ".*sleep.*func.*" "attach0 to sleeping bt"
+
+# Exit and detach the process.
+
+gdb_exit
+
+# No race
+sleep 2
+
+# Check it did not get stopped by our gdb
+set fileid1 [open $status2 r];
+gets $fileid1 line1;
+gets $fileid1 line2;
+close $fileid1;
+
+set test "attach1, post-gdb sanity check of the sleeping state - Red Hat BZ 197584"
+if {[string match "*(sleeping)*" $line2]} {
+  pass $test
+} else {
+  fail $test
+}
+
+# Stop the program 
+remote_exec build "kill -s STOP ${testpid}"
+
+# No race
+sleep 2
+
+# Check it really got stopped by kill(1)
+set fileid2 [open $status2 r];
+gets $fileid2 line1;
+gets $fileid2 line2;
+close $fileid2;
+
+set test "attach2, initial sanity check of the stopped state"
+if {[string match "*(stopped)*" $line2]} {
+  pass $test
+} else {
+  fail $test
+}
+
+# Start with clean gdb
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+# Verify that we can attach to the process by first giving its
+# executable name via the file command, and using attach with the
+# process ID.
+
+set test "set file, before attach3 to stopped process"
+gdb_test_multiple "file $binfile" "$test" {
+   -re "Load new symbol table from.*y or n. $" {
+        gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." \
+		"$test (re-read)"
+    }
+    -re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
+        pass "$test"
+    }
+}
+
+set test "attach3 to stopped, after setting file"
+gdb_test_multiple "attach $testpid" "$test" {
+    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+        pass "$test"
+    }
+}
+
+# We may be already after the threads phase.
+# `thread 2' command is important for the test to switch the current thread to
+# a non-primary one for the detach process.
+
+gdb_test "thread 2" ".*(witching to thread 2 |hread ID 2 not known).*" "attach3 to stopped switch thread"
+gdb_test "bt" ".*sleep.*(func|main).*" "attach3 to stopped bt"
+
+# Exit and detach the process.
+gdb_exit
+
+# Stop the program 
+remote_exec build "kill -s STOP ${testpid}"
+
+# No race
+sleep 2
+
+# Continue the test as we would hit another expected bug regarding
+# 	Program received signal SIGSTOP, Stopped (signal).
+# across NPTL threads.
+
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+# Verify that we can attach to the process just by giving the
+# process ID.
+   
+set test "attach4 to stopped, after setting file"
+gdb_test_multiple "attach $testpid" "$test" {
+    -re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
+        pass "$test"
+    }
+}
+
+# We may be already after the threads phase.
+# `thread 2' command is important for the test to switch the current thread to
+# a non-primary one for the detach process.
+
+gdb_test "thread 2" ".*(witching to thread 2 |hread ID 2 not known).*" "attach4 to stopped switch thread"
+gdb_test "bt" ".*sleep.*(func|main).*" "attach4 to stopped bt"
+
+# RHEL3U8 kernel-2.4.21-47.EL will not return SIGINT but only shorten the sleep.
+gdb_breakpoint [gdb_get_line_number "Ridiculous time"]
+gdb_breakpoint [gdb_get_line_number "cut the sleep time"]
+set test "attach4 continue"
+send_gdb "continue\n"
+gdb_expect {
+  -re "Continuing"
+    { pass "continue ($test)" }
+  timeout
+    { fail "continue ($test) (timeout)" }
+}
+
+# For this to work we must be sure to consume the "Continuing."
+# message first, or GDB's signal handler may not be in place.
+after 1000 {send_gdb "\003"}
+set test "attach4 stop by interrupt"
+gdb_expect {
+  -re "Program received signal SIGINT.*$gdb_prompt $"
+    {
+      pass $test
+    }
+  -re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
+    {
+      pass $test
+    }
+  timeout
+    {
+      fail "$test (timeout)"
+    }
+}
+
+gdb_exit
+
+# No race
+sleep 2
+
+# At this point, the process should be sleeping
+
+set fileid4 [open $status2 r];
+gets $fileid4 line1;
+gets $fileid4 line2;
+close $fileid4;
+
+set test "attach4, exit leaves process sleeping"
+if {[string match "*(sleeping)*" $line2]} {
+  pass $test
+} else {
+  fail $test
+}
+
+# Make sure we don't leave a process around to confuse
+# the next test run (and prevent the compile by keeping
+# the text file busy), in case the "set should_exit" didn't
+# work.
+   
+remote_exec build "kill -9 ${testpid}"
+
+return 0
Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.996
diff -u -p -r1.996 Makefile.in
--- Makefile.in	26 Mar 2008 14:53:28 -0000	1.996
+++ Makefile.in	11 Apr 2008 20:41:44 -0000
@@ -2291,7 +2291,7 @@ infcmd.o: infcmd.c $(defs_h) $(gdb_strin
 	$(objfiles_h) $(completer_h) $(ui_out_h) $(event_top_h) \
 	$(parser_defs_h) $(regcache_h) $(reggroups_h) $(block_h) \
 	$(solib_h) $(gdb_assert_h) $(observer_h) $(target_descriptions_h) \
-	$(user_regs_h) $(exceptions_h)
+	$(user_regs_h) $(exceptions_h) $(gdbthread_h)
 inf-loop.o: inf-loop.c $(defs_h) $(inferior_h) $(target_h) $(event_loop_h) \
 	$(event_top_h) $(inf_loop_h) $(remote_h) $(exceptions_h) \
 	$(language_h)


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-11 17:46         ` Jan Kratochvil
@ 2008-04-11 19:01           ` Daniel Jacobowitz
  2008-04-12  7:58           ` Roland McGrath
  1 sibling, 0 replies; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-04-11 19:01 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Roland McGrath, Doug Evans, GDB Patches, mark.kettenis

On Fri, Apr 11, 2008 at 06:18:24PM +0200, Jan Kratochvil wrote:
> There is a race if:

Right.  Patch coming soon, that uses basically your approach with some
simplifications.  I'm working through your testcases first.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-11  8:46       ` Roland McGrath
@ 2008-04-11 17:46         ` Jan Kratochvil
  2008-04-11 19:01           ` Daniel Jacobowitz
  2008-04-12  7:58           ` Roland McGrath
  2008-04-11 22:20         ` Daniel Jacobowitz
  1 sibling, 2 replies; 52+ messages in thread
From: Jan Kratochvil @ 2008-04-11 17:46 UTC (permalink / raw)
  To: Roland McGrath; +Cc: Daniel Jacobowitz, Doug Evans, GDB Patches, mark.kettenis

On Fri, 11 Apr 2008 01:12:05 +0200, Roland McGrath wrote:
> > As the shell already did WAITPID on the process and ate the SIGSTOP
> > notification so there is no other notification left there for GDB.
> > A process already stopped does not generate another SIGSTOP during
> > PTRACE_ATTACH as signals never count.
> 
> To be pedantic about it: a SIGSTOP is generated, but causes no wakeup or
> delivery for ptrace to see (because it's already stopped).  You can see
> the SIGSTOP pending in /proc/pid/status.  Generating a SIGCONT clears
> any pending stop signals, so normally it's as if it never existed.  But,
> I think if you were to do PTRACE_CONT,pid,0,0 you would make it wake up,
> dequeue the new SIGSTOP and then report to you for wait/SIGCHLD.  And
> since PTRACE_CONT would give ESRCH if it weren't stopped yet, you might
> be able to do PTRACE_ATTACH;PTRACE_CONT;wait reliably.  But don't hold
> me to it.

There is a race if:
* inferior was not stopped (a regular running process)
* PTRACE_ATTACH
* sleep a bit
* PTRACE_CONT
  - Here we do not get ESRCH but it behaves as a regular continuation of the
    inferior - as we would do PTRACE_ATTACH;waitpid;PTRACE_CONT.
    nonESRCH does not mean the inferior was stopped before PTRACE_ATTACH.

So IMO we must first check if the inferior is stopped in `/proc/PID/status'
which is already racy in respect to later PTRACE_ATTACH and the other ops.


Regards,
Jan


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-10 16:00     ` Jan Kratochvil
  2008-04-10 19:48       ` Daniel Jacobowitz
@ 2008-04-11  8:46       ` Roland McGrath
  2008-04-11 17:46         ` Jan Kratochvil
  2008-04-11 22:20         ` Daniel Jacobowitz
  1 sibling, 2 replies; 52+ messages in thread
From: Roland McGrath @ 2008-04-11  8:46 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Daniel Jacobowitz, Doug Evans, GDB Patches, mark.kettenis

> As the shell already did WAITPID on the process and ate the SIGSTOP
> notification so there is no other notification left there for GDB.
> A process already stopped does not generate another SIGSTOP during
> PTRACE_ATTACH as signals never count.

To be pedantic about it: a SIGSTOP is generated, but causes no wakeup or
delivery for ptrace to see (because it's already stopped).  You can see
the SIGSTOP pending in /proc/pid/status.  Generating a SIGCONT clears
any pending stop signals, so normally it's as if it never existed.  But,
I think if you were to do PTRACE_CONT,pid,0,0 you would make it wake up,
dequeue the new SIGSTOP and then report to you for wait/SIGCHLD.  And
since PTRACE_CONT would give ESRCH if it weren't stopped yet, you might
be able to do PTRACE_ATTACH;PTRACE_CONT;wait reliably.  But don't hold
me to it.


Thanks,
Roland


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-10 16:00           ` Jan Kratochvil
@ 2008-04-10 19:59             ` Daniel Jacobowitz
  0 siblings, 0 replies; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-04-10 19:59 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Roland McGrath, Doug Evans, GDB Patches, mark.kettenis

On Thu, Apr 10, 2008 at 05:52:44PM +0200, Jan Kratochvil wrote:
> Oh, OK.  But the same way works for me even for PTRACE_CONT:
> 
> after
> 	ptrace (PTRACE_CONT, child_pid, NULL, (void *) (long) SIGSTOP):
> State	T (stopped)
> Pid	5771
> TracerPid	5770
> 
> $ kill -CONT 5771
> and then:
> State	T (tracing stop)
> Pid	5771
> TracerPid	5770

Thanks, I see.  I will take another stab at it this afternoon.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-10 16:00     ` Jan Kratochvil
@ 2008-04-10 19:48       ` Daniel Jacobowitz
  2008-04-11  8:46       ` Roland McGrath
  1 sibling, 0 replies; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-04-10 19:48 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Doug Evans, GDB Patches, mark.kettenis, Roland McGrath

On Thu, Apr 10, 2008 at 05:48:39PM +0200, Jan Kratochvil wrote:
> > If I run attach-stopped from your testcase in a shell, then send it a
> > stop signal using kill from another window, stock GDB fails to attach
> > to it - just as I'd expect, that's the bug we're discussing.  But if I
> > run the attach-stopped.exp test script this part works fine.  It turns
> > out that if we spawn the program in expect (even at the expect1.1>
> > prompt, by hand) instead of using a shell with job control, GDB can
> > attach to it just fine.
> 
> As the shell already did WAITPID on the process and ate the SIGSTOP
> notification so there is no other notification left there for GDB.
> A process already stopped does not generate another SIGSTOP during
> PTRACE_ATTACH as signals never count.
> 
> [ Sure from info from Roland before. ]

Ah-hah.  Thanks, that makes sense.  exit_code doesn't show up in
/proc anywhere.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-10 15:39   ` Daniel Jacobowitz
@ 2008-04-10 16:00     ` Jan Kratochvil
  2008-04-10 19:48       ` Daniel Jacobowitz
  2008-04-11  8:46       ` Roland McGrath
  0 siblings, 2 replies; 52+ messages in thread
From: Jan Kratochvil @ 2008-04-10 16:00 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: Doug Evans, GDB Patches, mark.kettenis, Roland McGrath

On Thu, 10 Apr 2008 17:37:35 +0200, Daniel Jacobowitz wrote:
> I have another idea to solve the attach problem that does not involve
> redelivering signals - use WNOHANG in the initial wait if /proc
> already shows the process as stopped.  There shouldn't be a race if
> this is done after we PTRACE_ATTACH.

You can try it yourself but all the signals take some time to get delivered and
WNOHANG usually misses some signals which should get caught.


> If I run attach-stopped from your testcase in a shell, then send it a
> stop signal using kill from another window, stock GDB fails to attach
> to it - just as I'd expect, that's the bug we're discussing.  But if I
> run the attach-stopped.exp test script this part works fine.  It turns
> out that if we spawn the program in expect (even at the expect1.1>
> prompt, by hand) instead of using a shell with job control, GDB can
> attach to it just fine.

As the shell already did WAITPID on the process and ate the SIGSTOP
notification so there is no other notification left there for GDB.
A process already stopped does not generate another SIGSTOP during
PTRACE_ATTACH as signals never count.

[ Sure from info from Roland before. ]


Regards,
Jan


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-10 15:49         ` Daniel Jacobowitz
@ 2008-04-10 16:00           ` Jan Kratochvil
  2008-04-10 19:59             ` Daniel Jacobowitz
  0 siblings, 1 reply; 52+ messages in thread
From: Jan Kratochvil @ 2008-04-10 16:00 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: Roland McGrath, Doug Evans, GDB Patches, mark.kettenis

On Thu, 10 Apr 2008 17:38:54 +0200, Daniel Jacobowitz wrote:
> On Thu, Apr 10, 2008 at 05:30:17PM +0200, Jan Kratochvil wrote:
> > On Thu, 10 Apr 2008 17:15:45 +0200, Daniel Jacobowitz wrote:
> > > On Tue, Apr 01, 2008 at 03:49:36PM -0700, Roland McGrath wrote:
> > ...
> > > > As a gdb user, I have long been annoyed by the behavior on attaching to a
> > > > stopped process.  What I want is for attach to complete immediately and let
> > > > me examine the process.  If I then "cont", the process should go back to
> > > > what it was doing--sitting in job control stop.  Then fg or otherwise
> > > > sending SIGCONT will wake it up normally, and gdb should stop and tell me
> > > > about the SIGCONT like normal.
> > > 
> > > How could GDB put an attached process back into job control stop?
> > 
> > ptrace (PTRACE_DETACH, child_pid, NULL, (void *) (long) SIGSTOP);
> 
> Yes, but Roland said "continue" should put the process back into job
> control sleep - that's what I was asking about.  I know how to do it
> if we're detaching, but not without detaching.

Oh, OK.  But the same way works for me even for PTRACE_CONT:

after
	ptrace (PTRACE_CONT, child_pid, NULL, (void *) (long) SIGSTOP):
State	T (stopped)
Pid	5771
TracerPid	5770

$ kill -CONT 5771
and then:
State	T (tracing stop)
Pid	5771
TracerPid	5770


Regards,
Jan


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-10 15:37       ` Jan Kratochvil
@ 2008-04-10 15:49         ` Daniel Jacobowitz
  2008-04-10 16:00           ` Jan Kratochvil
  0 siblings, 1 reply; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-04-10 15:49 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Roland McGrath, Doug Evans, GDB Patches, mark.kettenis

On Thu, Apr 10, 2008 at 05:30:17PM +0200, Jan Kratochvil wrote:
> On Thu, 10 Apr 2008 17:15:45 +0200, Daniel Jacobowitz wrote:
> > On Tue, Apr 01, 2008 at 03:49:36PM -0700, Roland McGrath wrote:
> ...
> > > As a gdb user, I have long been annoyed by the behavior on attaching to a
> > > stopped process.  What I want is for attach to complete immediately and let
> > > me examine the process.  If I then "cont", the process should go back to
> > > what it was doing--sitting in job control stop.  Then fg or otherwise
> > > sending SIGCONT will wake it up normally, and gdb should stop and tell me
> > > about the SIGCONT like normal.
> > 
> > How could GDB put an attached process back into job control stop?
> 
> ptrace (PTRACE_DETACH, child_pid, NULL, (void *) (long) SIGSTOP);

Yes, but Roland said "continue" should put the process back into job
control sleep - that's what I was asking about.  I know how to do it
if we're detaching, but not without detaching.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-02  0:01 ` Jan Kratochvil
  2008-04-02  0:07   ` Roland McGrath
@ 2008-04-10 15:39   ` Daniel Jacobowitz
  2008-04-10 16:00     ` Jan Kratochvil
  1 sibling, 1 reply; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-04-10 15:39 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Doug Evans, GDB Patches, mark.kettenis, Roland McGrath

[Roland, job control question for you in here.  I'm not sure who else
to ask...]

On Wed, Apr 02, 2008 at 12:30:12AM +0200, Jan Kratochvil wrote:
> The condition whether the detached process (previously attached as stopped)
> should be left stopped or running is not intuitive there now, it is Red Hat
> patches backward compatible but IMO it should rather ask the user (with
> a default of leaving it stopped).

Yes, I don't like the changed behavior of detach.  I think it would be
more intuitive if detach always resumed, and the disconnect command
worked for native debugging.  Although that leaves gdbserver out in
the cold (disconnect already has meaning there).  So maybe it should
be detach --stopped?  For the FSF version we can address this
separately from the attach bug.

I have another idea to solve the attach problem that does not involve
redelivering signals - use WNOHANG in the initial wait if /proc
already shows the process as stopped.  There shouldn't be a race if
this is done after we PTRACE_ATTACH.

But I got hung up trying to figure out what was going on with
testing...

If I run attach-stopped from your testcase in a shell, then send it a
stop signal using kill from another window, stock GDB fails to attach
to it - just as I'd expect, that's the bug we're discussing.  But if I
run the attach-stopped.exp test script this part works fine.  It turns
out that if we spawn the program in expect (even at the expect1.1>
prompt, by hand) instead of using a shell with job control, GDB can
attach to it just fine.

In both cases the kernel reports the process as stopped.  The
difference in ps is the "s+":

drow     28162  0.0  0.0   6152   376 pts/50   Ts+  11:19   0:00 ./gdb.threads/attach-stopped
drow     28179  0.0  0.0   6152   376 pts/51   T    11:20   0:00 ./gdb.threads/attach-stopped

There's no difference in /proc/pid/status at all, besides the PIDs.
The "s" means session leader, the "+" means foreground process group.
28162 is the one run from expect.

Any idea why this makes a difference?  Kernel version is 2.6.24, if
that matters.  If we could get the foreground process group behavior
all the time, then we don't even need to change GDB.  But I don't know
if that's right since I don't understand how it occurs.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-10 15:30     ` Daniel Jacobowitz
@ 2008-04-10 15:37       ` Jan Kratochvil
  2008-04-10 15:49         ` Daniel Jacobowitz
  0 siblings, 1 reply; 52+ messages in thread
From: Jan Kratochvil @ 2008-04-10 15:37 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: Roland McGrath, Doug Evans, GDB Patches, mark.kettenis

On Thu, 10 Apr 2008 17:15:45 +0200, Daniel Jacobowitz wrote:
> On Tue, Apr 01, 2008 at 03:49:36PM -0700, Roland McGrath wrote:
...
> > As a gdb user, I have long been annoyed by the behavior on attaching to a
> > stopped process.  What I want is for attach to complete immediately and let
> > me examine the process.  If I then "cont", the process should go back to
> > what it was doing--sitting in job control stop.  Then fg or otherwise
> > sending SIGCONT will wake it up normally, and gdb should stop and tell me
> > about the SIGCONT like normal.
> 
> How could GDB put an attached process back into job control stop?

ptrace (PTRACE_DETACH, child_pid, NULL, (void *) (long) SIGSTOP);

It is being done by the patch (in the appropriate cases)
	http://cvs.fedora.redhat.com/viewcvs/*checkout*/rpms/gdb/devel/gdb-6.7-bz233852-attach-signalled-fix.patch
	(this patch does not follow the Roland's suggested behavior)

The current GDB code already interprets the DETACH_COMMAND ARGS parameter as
the signal number for PTRACE_DETACH but one cannot enter any arguments for the
`detach' command by the cli interface.


Regards,
Jan


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-02  0:07   ` Roland McGrath
@ 2008-04-10 15:30     ` Daniel Jacobowitz
  2008-04-10 15:37       ` Jan Kratochvil
  0 siblings, 1 reply; 52+ messages in thread
From: Daniel Jacobowitz @ 2008-04-10 15:30 UTC (permalink / raw)
  To: Roland McGrath; +Cc: Jan Kratochvil, Doug Evans, GDB Patches, mark.kettenis

On Tue, Apr 01, 2008 at 03:49:36PM -0700, Roland McGrath wrote:
> I'm not sure if there was any question for me about kernel behavior in there.
> 
> As a gdb user, I have long been annoyed by the behavior on attaching to a
> stopped process.  What I want is for attach to complete immediately and let
> me examine the process.  If I then "cont", the process should go back to
> what it was doing--sitting in job control stop.  Then fg or otherwise
> sending SIGCONT will wake it up normally, and gdb should stop and tell me
> about the SIGCONT like normal.

How could GDB put an attached process back into job control stop?

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-04-02  0:01 ` Jan Kratochvil
@ 2008-04-02  0:07   ` Roland McGrath
  2008-04-10 15:30     ` Daniel Jacobowitz
  2008-04-10 15:39   ` Daniel Jacobowitz
  1 sibling, 1 reply; 52+ messages in thread
From: Roland McGrath @ 2008-04-02  0:07 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Doug Evans, GDB Patches, mark.kettenis

I'm not sure if there was any question for me about kernel behavior in there.

As a gdb user, I have long been annoyed by the behavior on attaching to a
stopped process.  What I want is for attach to complete immediately and let
me examine the process.  If I then "cont", the process should go back to
what it was doing--sitting in job control stop.  Then fg or otherwise
sending SIGCONT will wake it up normally, and gdb should stop and tell me
about the SIGCONT like normal.


Thanks,
Roland


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
  2008-03-31 22:07 Doug Evans
@ 2008-04-02  0:01 ` Jan Kratochvil
  2008-04-02  0:07   ` Roland McGrath
  2008-04-10 15:39   ` Daniel Jacobowitz
  0 siblings, 2 replies; 52+ messages in thread
From: Jan Kratochvil @ 2008-04-02  0:01 UTC (permalink / raw)
  To: Doug Evans; +Cc: GDB Patches, mark.kettenis, Roland McGrath

On Mon, 31 Mar 2008 21:18:51 +0200, Doug Evans wrote:
> Was there any final resolution on this patch?
> 
> http://sourceware.org/ml/gdb-patches/2007-06/msg00546.html
> http://sourceware.org/ml/gdb-patches/2007-07/msg00003.html
> 
> It doesn't seem to have gotten checked in but I couldn't find an
> explanation.  I don't see the problem on my fc8 box but I do see the
> problem on an older one, so I'm guessing it was resolved to leave GDB
> alone and leave the fix to the kernel.  Is that correct?

No, Fedora kernel ptrace(2)/waitpid(2) etc. syscalls (which use ptrace
emulation running on top of the utrace subsystem) behave exactly the same as
their kernel.org counterparts - except various bugs are being fixed by Roland
McGrath in both of the implementations.  The ptrace validity tests are at:
	http://sourceware.org/systemtap/wiki/utrace/tests

The signaling problem needs to be handled carefully in GDB itself as the
ptrace(2) API standard is unfriendly in these conditions:
	http://cvs.fedora.redhat.com/viewcvs/*checkout*/rpms/gdb/devel/gdb-6.7-bz233852-attach-signalled-fix.patch
	http://cvs.fedora.redhat.com/viewcvs/*checkout*/rpms/gdb/devel/gdb-6.7-bz233852-attach-signalled-test.patch
(it applies OK, runtime not tested without the other Fedora patches there)

Current/latest update of Fedora 8 already contains all the gdb changes (and
kernel utrace compatibility fixes with kernel.org ptrace) to be in a good
shape.  Older Fedora kernel+gdb did have various bugs in this functionality.

The GDB patch is fully compatible with the kernel.org (non-utrace) ptrace.
The patch addresses these two issues:
 * Attachment to a process which is stopped (kill -SIGSTOP).
 * Attachment to a process with a rare race condition of having a signal
   currently being delivered - WAITPID returns there the signal which needs
   to be redelivered (otherwise it gets lost), upstream GDB assertion-fails
   as it will not get the expected SIGSTOP signal (which comes on next WAITPID
   from the kernel queue).

Current patch still has one known problem that if we have a process already
stopped and we attach to it by GDB with this patch it may start with the
inferior state several instructions later than it was originally stopped.
	https://bugzilla.redhat.com/show_bug.cgi?id=244162#c17
It would be solvable if we would delay each attachment to a stopped process by
some constant delay - such as 1 second - to give it a time for landing of all
the possibly pending signals.  OTOH probably better way to solve this flaw is
to move from the ptrace(2) interface to the utrace interface (AFAIK with its
userland part not defined so far) as the patch in its current form is insane.

Also the more generic question may be what should happen if we try to attach
a debugger to a SIGSTOPped process.  Naive debugger will hang - this is right
as the inferior is stopped - it should not run any operation, incl. its
debugging.

The condition whether the detached process (previously attached as stopped)
should be left stopped or running is not intuitive there now, it is Red Hat
patches backward compatible but IMO it should rather ask the user (with
a default of leaving it stopped).

This patch was developed with a lot of ptrace(2) info from Roland McGrath.



Regards,
Jan


^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [patch] Fix Linux attach to signalled/stopped processes
@ 2008-03-31 22:07 Doug Evans
  2008-04-02  0:01 ` Jan Kratochvil
  0 siblings, 1 reply; 52+ messages in thread
From: Doug Evans @ 2008-03-31 22:07 UTC (permalink / raw)
  To: GDB Patches, jan.kratochvil, mark.kettenis

Was there any final resolution on this patch?

http://sourceware.org/ml/gdb-patches/2007-06/msg00546.html
http://sourceware.org/ml/gdb-patches/2007-07/msg00003.html

It doesn't seem to have gotten checked in but I couldn't find an
explanation.  I don't see the problem on my fc8 box but I do see the
problem on an older one, so I'm guessing it was resolved to leave GDB
alone and leave the fix to the kernel.  Is that correct?


^ permalink raw reply	[flat|nested] 52+ messages in thread

end of thread, other threads:[~2008-09-28 14:58 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-06-06 14:34 [patch] Fix Linux attach to signalled/stopped processes Jan Kratochvil
2007-06-11 13:44 ` Jan Kratochvil
2007-06-15 18:02 ` Mark Kettenis
2007-06-26 22:40   ` Jan Kratochvil
2007-06-27  0:13     ` Mark Kettenis
2007-06-27 11:59       ` Jan Kratochvil
2007-06-27 18:30         ` Mark Kettenis
2007-06-30 11:45         ` Jan Kratochvil
2007-06-30 11:57           ` Eli Zaretskii
2007-06-30 17:15             ` Jan Kratochvil
2007-06-30 18:52               ` Eli Zaretskii
     [not found]               ` <200706301852.l5UIq8ek010536@brahms.sibelius.xs4all.nl>
2007-07-01  3:17                 ` Eli Zaretskii
2007-07-01  9:34                   ` Mark Kettenis
2007-07-01 10:03                     ` Jan Kratochvil
2008-03-31 22:07 Doug Evans
2008-04-02  0:01 ` Jan Kratochvil
2008-04-02  0:07   ` Roland McGrath
2008-04-10 15:30     ` Daniel Jacobowitz
2008-04-10 15:37       ` Jan Kratochvil
2008-04-10 15:49         ` Daniel Jacobowitz
2008-04-10 16:00           ` Jan Kratochvil
2008-04-10 19:59             ` Daniel Jacobowitz
2008-04-10 15:39   ` Daniel Jacobowitz
2008-04-10 16:00     ` Jan Kratochvil
2008-04-10 19:48       ` Daniel Jacobowitz
2008-04-11  8:46       ` Roland McGrath
2008-04-11 17:46         ` Jan Kratochvil
2008-04-11 19:01           ` Daniel Jacobowitz
2008-04-12  7:58           ` Roland McGrath
2008-04-14 15:09             ` Daniel Jacobowitz
2008-04-14 15:31               ` Daniel Jacobowitz
2008-04-15 22:14                 ` Jan Kratochvil
2008-05-01 18:50                   ` Daniel Jacobowitz
2008-07-05  8:48                     ` Jan Kratochvil
2008-07-05 13:48                       ` Daniel Jacobowitz
2008-09-24 12:46                 ` Andreas Schwab
2008-09-26  3:52                   ` Jan Kratochvil
2008-09-26 13:00                     ` Daniel Jacobowitz
2008-09-28 11:43                       ` Jan Kratochvil
2008-09-28 14:58                         ` Daniel Jacobowitz
2008-04-15  8:14               ` Roland McGrath
2008-04-15 13:02                 ` Daniel Jacobowitz
2008-04-16  7:01                   ` Roland McGrath
2008-04-11 22:20         ` Daniel Jacobowitz
2008-04-11 22:21           ` Pedro Alves
2008-04-11 22:25             ` Daniel Jacobowitz
2008-04-12  0:02               ` Pedro Alves
2008-04-12  0:19                 ` Pedro Alves
2008-04-13  9:35                   ` Pedro Alves
2008-04-13 13:40                     ` Pedro Alves
2008-04-12 16:38           ` Roland McGrath
2008-04-12 16:43           ` Eli Zaretskii

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox