This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[unwinder-portable patch 1/3] Handle T-stopped detach for old kernels


Hi Mark,

this patch is for the branch 'portable' to be checked in wotj
'jankratochvil/unwindx86', that is for RHEL-5 systems.
It has been verified it is not needed on RHEL-6 (despite RHEL-6 is not yet so
forgiving wrt SIGSTOP like the most recent Linux kernels are).

RHEL-5 kernels were not enough with
	ptrace (PTRACE_DETACH, tid, NULL, SIGSTOP);
and they need to explicitly
	syscall (__NR_tkill, tid, SIGSTOP);
beforehand.

Moreover for a short time after such PTRACE_DETACH the TID's /proc/PID/status
State may be R (running), so if one tries to PTRACE_ATTACH that time again the
attacher will not notice the process in fact should be T (stopped).  Therefore
it detaches the process without __NR_tkill SIGSTOP and the process is no
longer stopped, as it should have been.

For GDB it is handled by:
	http://pkgs.fedoraproject.org/cgit/gdb.git/tree/gdb-rhel5-compat.patch


Thanks,
Jan

libdwfl/
2013-11-01  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Handle T-stopped detach for old kernels.
	* linux-pid-attach.c (struct pid_arg): New field stopped.
	(ptrace_attach): New parameter stoppedp.  Set it appropriately.
	(pid_set_initial_registers): Pass the new field.
	(pid_thread_detach): Handle the case of STOPPED for old kernels.
	(__libdwfl_attach_state_for_pid): Initialize STOPPED.

tests/
2013-11-01  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Handle T-stopped detach for old kernels.
	* backtrace.c: Include sys/syscall.h.
	(linux_proc_pid_is_stopped): New function.
	(ptrace_detach_stopped): Handle old kernels.

--- a/libdwfl/linux-pid-attach.c
+++ b/libdwfl/linux-pid-attach.c
@@ -42,6 +42,8 @@ struct pid_arg
   DIR *dir;
   /* It is 0 if not used.  */
   pid_t tid_attached;
+  /* TRUE if the process (first of its threads) was State: T (stopped).  */
+  bool stopped;
 };
 
 static bool
@@ -69,7 +71,7 @@ linux_proc_pid_is_stopped (pid_t pid)
 }
 
 static bool
-ptrace_attach (pid_t tid)
+ptrace_attach (pid_t tid, bool *stoppedp)
 {
   if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0)
     {
@@ -84,6 +86,7 @@ ptrace_attach (pid_t tid)
 	 above.  Which would make the waitpid below wait forever.  So emulate
 	 it.  Since there can only be one SIGSTOP notification pending this is
 	 safe.  See also gdb/linux-nat.c linux_nat_post_attach_wait.  */
+      *stoppedp = true;
       syscall (__NR_tkill, tid, SIGSTOP);
       ptrace (PTRACE_CONT, tid, NULL, NULL);
     }
@@ -213,7 +216,7 @@ pid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg)
   struct pid_arg *pid_arg = thread_arg;
   assert (pid_arg->tid_attached == 0);
   pid_t tid = INTUSE(dwfl_thread_tid) (thread);
-  if (! ptrace_attach (tid))
+  if (! ptrace_attach (tid, &pid_arg->stopped))
     return false;
   pid_arg->tid_attached = tid;
   Dwfl_Process *process = thread->process;
@@ -237,7 +240,18 @@ pid_thread_detach (Dwfl_Thread *thread, void *thread_arg)
   pid_t tid = INTUSE(dwfl_thread_tid) (thread);
   assert (pid_arg->tid_attached == tid);
   pid_arg->tid_attached = 0;
-  ptrace (PTRACE_DETACH, tid, NULL, NULL);
+  if (! pid_arg->stopped)
+    {
+      ptrace (PTRACE_DETACH, tid, NULL, NULL);
+      return;
+    }
+  syscall (__NR_tkill, tid, SIGSTOP);
+  ptrace (PTRACE_DETACH, tid, NULL, (void *) (intptr_t) SIGSTOP);
+  // Wait till the SIGSTOP settles down.
+  int i;
+  for (i = 0; i < 100000; i++)
+    if (linux_proc_pid_is_stopped (tid))
+      break;
 }
 
 static const Dwfl_Thread_Callbacks pid_thread_callbacks =
@@ -271,6 +285,7 @@ __libdwfl_attach_state_for_pid (Dwfl *dwfl, pid_t pid)
     }
   pid_arg->dir = dir;
   pid_arg->tid_attached = 0;
+  pid_arg->stopped = false;
   if (! INTUSE(dwfl_attach_state) (dwfl, EM_NONE, pid, &pid_thread_callbacks,
 				   pid_arg))
     {
--- a/tests/backtrace.c
+++ b/tests/backtrace.c
@@ -35,6 +35,7 @@
 #include <sys/user.h>
 #include <fcntl.h>
 #include <string.h>
+#include <sys/syscall.h>
 #include ELFUTILS_HEADER(dwfl)
 
 static void
@@ -370,13 +371,43 @@ prepare_thread (pid_t pid2, Dwarf_Addr plt_start, Dwarf_Addr plt_end,
 #include <unistd.h>
 #define tgkill(pid, tid, sig) syscall (__NR_tgkill, (pid), (tid), (sig))
 
+static bool
+linux_proc_pid_is_stopped (pid_t pid)
+{
+  char buffer[64];
+  FILE *procfile;
+  bool retval, have_state;
+
+  snprintf (buffer, sizeof (buffer), "/proc/%ld/status", (long) pid);
+  procfile = fopen (buffer, "r");
+  if (procfile == NULL)
+    return false;
+
+  have_state = false;
+  while (fgets (buffer, sizeof (buffer), procfile) != NULL)
+    if (strncmp (buffer, "State:", 6) == 0)
+      {
+	have_state = true;
+	break;
+      }
+  retval = (have_state && strstr (buffer, "T (stopped)") != NULL);
+  fclose (procfile);
+  return retval;
+}
+
 static void
 ptrace_detach_stopped (pid_t pid)
 {
+  syscall (__NR_tkill, pid, SIGSTOP);
   errno = 0;
   long l = ptrace (PTRACE_DETACH, pid, NULL, (void *) (intptr_t) SIGSTOP);
   assert_perror (errno);
   assert (l == 0);
+  // Wait till the SIGSTOP settles down.
+  int i;
+  for (i = 0; i < 100000; i++)
+    if (linux_proc_pid_is_stopped (pid))
+      break;
 }
 
 static void

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]