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(a)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(a)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