2012-02-27 Pedro Alves <palves@redhat.com>
authorPedro Alves <palves@redhat.com>
Mon, 27 Feb 2012 16:19:19 +0000 (16:19 +0000)
committerPedro Alves <palves@redhat.com>
Mon, 27 Feb 2012 16:19:19 +0000 (16:19 +0000)
PR server/9684
* linux-low.c (pid_is_stopped): New.
(linux_attach_lwp_1): Handle attaching to 'T (stopped)' processes.

gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-low.c

index 1e2f5e9..b6d9ce9 100644 (file)
@@ -1,3 +1,9 @@
+2012-02-27  Pedro Alves  <palves@redhat.com>
+
+       PR server/9684
+       * linux-low.c (pid_is_stopped): New.
+       (linux_attach_lwp_1): Handle attaching to 'T (stopped)' processes.
+
 2012-02-25  Luis Machado  <lgustavo@codesourcery.com>
 
        * mem-break.c (clear_gdb_breakpoint_conditions): Fix de-allocation
index 8a83231..f2887e6 100644 (file)
@@ -598,6 +598,37 @@ linux_create_inferior (char *program, char **allargs)
   return pid;
 }
 
+/* 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;
+}
+
 /* Attach to an inferior process.  */
 
 static void
@@ -643,6 +674,33 @@ linux_attach_lwp_1 (unsigned long lwpid, int initial)
      ptrace call on this LWP.  */
   new_lwp->must_set_ptrace_flags = 1;
 
+  if (pid_is_stopped (lwpid))
+    {
+      if (debug_threads)
+       fprintf (stderr,
+                "Attached 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 (lwpid, SIGSTOP);
+
+      /* Finally, resume the stopped process.  This will deliver the
+        SIGSTOP (or a higher priority signal, just like normal
+        PTRACE_ATTACH), which we'll catch later on.  */
+      ptrace (PTRACE_CONT, lwpid, 0, 0);
+    }
+
   /* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH
      brings it to a halt.