libthread_db: Skip attaching to terminated and joined threads
authorPedro Alves <palves@redhat.com>
Tue, 16 Dec 2014 16:12:24 +0000 (16:12 +0000)
committerPedro Alves <palves@redhat.com>
Fri, 9 Jan 2015 11:41:01 +0000 (11:41 +0000)
I wrote a test that attaches to a program that constantly spawns
short-lived threads, which exposed several issues.  This is one of
them.

On GNU/Linux, attaching to a multi-threaded program sometimes prints
out warnings like:

 ...
 [New LWP 20700]
 warning: unable to open /proc file '/proc/-1/status'
 [New LWP 20850]
 [New LWP 21019]
 ...

That happens because when a thread exits, and is joined, glibc does:

nptl/pthread_join.c:
pthread_join ()
{
...
  if (__glibc_likely (result == 0))
    {
      /* We mark the thread as terminated and as joined.  */
      pd->tid = -1;
...
     /* Free the TCB.  */
      __free_tcb (pd);
    }

So if we attach or interrupt the program (which does an implicit "info
threads") at just the right (or rather, wrong) time, we can find and
return threads in the libthread_db/pthreads thread list with kernel
thread ID -1.  I've filed glibc PR nptl/17707 for this.  You'll find
more info there.

This patch handles this as a special case in GDB.

This is actually more than just a cosmetic issue.  lin_lwp_attach_lwp
will think that this -1 is an LWP we're not attached to yet, and after
failing to attach will try to check we were already attached to the
process, using a waitpid call, which in this case ends up being
"waitpid (-1, ...", which obviously results in GDB potentially
discarding an event when it shouldn't...

Tested on x86_64 Fedora 20, native and gdbserver.

gdb/gdbserver/
2015-01-09  Pedro Alves  <palves@redhat.com>

* thread-db.c (find_new_threads_callback): Ignore thread if the
kernel thread ID is -1.

gdb/
2015-01-09  Pedro Alves  <palves@redhat.com>

* linux-nat.c (lin_lwp_attach_lwp): Assert that the lwp id we're
about to wait for is > 0.
* linux-thread-db.c (find_new_threads_callback): Ignore thread if
the kernel thread ID is -1.

gdb/ChangeLog
gdb/gdbserver/ChangeLog
gdb/gdbserver/thread-db.c
gdb/linux-nat.c
gdb/linux-thread-db.c

index 4ee17b31e559e1e66122490f3b5712024b8f8260..fb99d260ff5fe925f783eca26d0efa22c7c44307 100644 (file)
@@ -1,3 +1,10 @@
+2015-01-09  Pedro Alves  <palves@redhat.com>
+
+       * linux-nat.c (lin_lwp_attach_lwp): Assert that the lwp id we're
+       about to wait for is > 0.
+       * linux-thread-db.c (find_new_threads_callback): Ignore thread if
+       the kernel thread ID is -1.
+
 2015-01-09  Pedro Alves  <palves@redhat.com>
 
        * linux-nat.c (attach_proc_task_lwp_callback): New function.
index 2ee6a00f83d784396135385446a0d5624f19c6ce..d6bafae9b8db8487aaa13a5afdd92dee3939b083 100644 (file)
@@ -1,3 +1,8 @@
+2015-01-09  Pedro Alves  <palves@redhat.com>
+
+       * thread-db.c (find_new_threads_callback): Ignore thread if the
+       kernel thread ID is -1.
+
 2015-01-09  Pedro Alves  <palves@redhat.com>
 
        * linux-low.c (linux_attach_fail_reason_string): Move to
index 4e0d32acfcec159da8fe078590ca460dac7a6482..b0d1f0d945a4b38532cda1c5e152637c7e164e14 100644 (file)
@@ -396,6 +396,17 @@ find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
   if (err != TD_OK)
     error ("Cannot get thread info: %s", thread_db_err_str (err));
 
+  if (ti.ti_lid == -1)
+    {
+      /* A thread with kernel thread ID -1 is either a thread that
+        exited and was joined, or a thread that is being created but
+        hasn't started yet, and that is reusing the tcb/stack of a
+        thread that previously exited and was joined.  (glibc marks
+        terminated and joined threads with kernel thread ID -1.  See
+        glibc PR17707.  */
+      return 0;
+    }
+
   /* Check for zombies.  */
   if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
     return 0;
index 0adf3a9c769c92fc6829e33c35bfd943e30a1374..77aa8e31377fa885898fe9c0a924001badbca00f 100644 (file)
@@ -1023,6 +1023,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
 
                  /* See if we've got a stop for this new child
                     pending.  If so, we're already attached.  */
+                 gdb_assert (lwpid > 0);
                  new_pid = my_waitpid (lwpid, &status, WNOHANG);
                  if (new_pid == -1 && errno == ECHILD)
                    new_pid = my_waitpid (lwpid, &status, __WCLONE | WNOHANG);
index b7afb039d2cfc6dfe0b70c603db7485b95d8076c..1417542a6b6bb272d21b8bfab5dffd8014f203dc 100644 (file)
@@ -1610,6 +1610,17 @@ find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
     error (_("find_new_threads_callback: cannot get thread info: %s"),
           thread_db_err_str (err));
 
+  if (ti.ti_lid == -1)
+    {
+      /* A thread with kernel thread ID -1 is either a thread that
+        exited and was joined, or a thread that is being created but
+        hasn't started yet, and that is reusing the tcb/stack of a
+        thread that previously exited and was joined.  (glibc marks
+        terminated and joined threads with kernel thread ID -1.  See
+        glibc PR17707.  */
+      return 0;
+    }
+
   if (ti.ti_tid == 0)
     {
       /* A thread ID of zero means that this is the main thread, but