This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc 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]

NPTL libthread_db changes for static debugging


I recently posted GDB patches to support debugging statically linked
applications using NPTL; they need a corresponding adjustment in glibc.
For reference, you can reproduce this problem with GDB's
staticthreads.exp testcase.

Here's the basic problem: the API doesn't directly offer an "OK,
libpthread.so is now initialized and available" event.  GDB checks
at the beginning of execution (for the static linking case) and again
at every shared library load event for the symbols libthread_db
needs; if they are available it initializes libthread_db.  But
if libpthread.a is statically linked into the executable, there is
a window where the symbols are available but the library is not yet
initialized.

In LinuxThreads libthread_db detected this case and reported a single
"fake" thread; enough of the standard operations worked on the fake
thread that GDB could display and debug it.  NPTL's libthread_db tries
to do the same thing.  But in NPTL, before libpthread is initialized,
there's no way to predict the thread's TID.  All sorts of operations
fail; iterating over threads, for instance, reads an uninitialized
value from the target to get the TLS base.  And we can't fill in
useful information in td_thr_get_info, because we don't have a
TID yet.

The best solution I found was to present a view in which there are no
threads at all.  Then, keyed off the global event mask, report the
"creation" of the first thread when the library has initialized.
Now GDB sets the global thread event mask right away, initializes
its thread list, and if there are none, waits for glibc to inform
it that the first thread is ready.

Unfortunately this won't work with existing GDBs; they will get
confused and resume all zero threads from the thread list, and then
wait for the program to stop.  You need the patches I've posted, which
I plan to check in soon.  In order to work around this problem, which
would be an unpleasant version lock, I've arranged to only do this for
static executables (which previously didn't work); the same approach
used for static executables works with a patched GDB and a patched
glibc for dynamic executables, but the old approach is correct enough
so this is safer.

Tested on x86_64-pc-linux-gnu, using new glibc with both old and new
gdb, and the glibc testsuite.  Is this patch OK?

-- 
Daniel Jacobowitz
CodeSourcery

2006-03-01  Daniel Jacobowitz  <dan@codesourcery.com>

	* init.c (__pthread_initialize_minimal_internal) [!__SHARED]: Report
	a thread creation event.
	* pthreadP.h (__nptl_threads_events, __nptl_last_event): New.
	* pthread_create.c (__nptl_threads_events, __nptl_last_event):
	Change from static to hidden.

2006-03-01  Daniel Jacobowitz  <dan@codesourcery.com>

	* structs.def (__libc_setup_tls): New entry.
	* td_ta_thr_iter.c (iterate_thread_list): Adjust fake_empty
	handling.
	* td_thr_validate.c (td_thr_validate): Adjust uninit handling.

Index: nptl/init.c
===================================================================
RCS file: /big/fsf/rsync/glibc/libc/nptl/init.c,v
retrieving revision 1.56
diff -u -p -r1.56 init.c
--- nptl/init.c	13 Feb 2006 01:22:36 -0000	1.56
+++ nptl/init.c	1 Mar 2006 17:12:42 -0000
@@ -268,6 +268,28 @@ __pthread_initialize_minimal_internal (v
   INIT_LIST_HEAD (&__stack_user);
   list_add (&pd->list, &__stack_user);
 
+#ifndef SHARED
+  /* If event reporting has been enabled for the process, report the
+     "creation" of the first thread now.  */
+  const int _idx = __td_eventword (TD_CREATE);
+  const uint32_t _mask = __td_eventmask (TD_CREATE);
+
+  if ((_mask & __nptl_threads_events.event_bits[_idx]) != 0)
+    {
+      pd->eventbuf.eventnum = TD_CREATE;
+      pd->eventbuf.eventdata = pd;
+
+      /* Enqueue the descriptor.  */
+      do
+	pd->nextevent = __nptl_last_event;
+      while (atomic_compare_and_exchange_bool_acq (&__nptl_last_event,
+						   pd, pd->nextevent)
+	     != 0);
+
+      /* Now call the function which signals the event.  */
+      __nptl_create_event ();
+    }
+#endif
 
   /* Install the cancellation signal handler.  If for some reason we
      cannot install the handler we do not abort.  Maybe we should, but
Index: nptl/pthreadP.h
===================================================================
RCS file: /big/fsf/rsync/glibc/libc/nptl/pthreadP.h,v
retrieving revision 1.58
diff -u -p -r1.58 pthreadP.h
--- nptl/pthreadP.h	15 Feb 2006 16:51:35 -0000	1.58
+++ nptl/pthreadP.h	1 Mar 2006 17:12:42 -0000
@@ -290,6 +290,8 @@ extern void __nptl_create_event (void);
 extern void __nptl_death_event (void);
 hidden_proto (__nptl_create_event)
 hidden_proto (__nptl_death_event)
+extern td_thr_events_t __nptl_threads_events attribute_hidden;
+extern struct pthread *__nptl_last_event attribute_hidden;
 
 /* Register the generation counter in the libpthread with the libc.  */
 #ifdef TLS_MULTIPLE_THREADS_IN_TCB
Index: nptl/pthread_create.c
===================================================================
RCS file: /big/fsf/rsync/glibc/libc/nptl/pthread_create.c,v
retrieving revision 1.50
diff -u -p -r1.50 pthread_create.c
--- nptl/pthread_create.c	15 Feb 2006 16:53:15 -0000	1.50
+++ nptl/pthread_create.c	1 Mar 2006 17:12:42 -0000
@@ -39,10 +39,10 @@ static int start_thread (void *arg);
 int __pthread_debug;
 
 /* Globally enabled events.  */
-static td_thr_events_t __nptl_threads_events;
+td_thr_events_t __nptl_threads_events attribute_hidden;
 
 /* Pointer to descriptor with the last event.  */
-static struct pthread *__nptl_last_event;
+struct pthread *__nptl_last_event attribute_hidden;
 
 /* Number of threads running.  */
 unsigned int __nptl_nthreads = 1;
Index: nptl_db/structs.def
===================================================================
RCS file: /big/fsf/rsync/glibc/libc/nptl_db/structs.def,v
retrieving revision 1.3
diff -u -p -r1.3 structs.def
--- nptl_db/structs.def	4 Feb 2006 00:47:58 -0000	1.3
+++ nptl_db/structs.def	1 Mar 2006 17:12:42 -0000
@@ -48,6 +48,7 @@ DB_STRUCT (td_eventbuf_t)
 DB_STRUCT_FIELD (td_eventbuf_t, eventnum)
 DB_STRUCT_FIELD (td_eventbuf_t, eventdata)
 
+DB_FUNCTION (__libc_setup_tls)
 DB_SYMBOL (stack_used)
 DB_SYMBOL (__stack_user)
 DB_SYMBOL (nptl_version)
Index: nptl_db/td_ta_thr_iter.c
===================================================================
RCS file: /big/fsf/rsync/glibc/libc/nptl_db/td_ta_thr_iter.c,v
retrieving revision 1.8
diff -u -p -r1.8 td_ta_thr_iter.c
--- nptl_db/td_ta_thr_iter.c	4 Apr 2004 00:31:10 -0000	1.8
+++ nptl_db/td_ta_thr_iter.c	1 Mar 2006 17:12:42 -0000
@@ -1,5 +1,6 @@
 /* Iterate over a process's threads.
-   Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
+   Copyright (C) 1999,2000,2001,2002,2003,2004, 2006
+   Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 1999.
 
@@ -41,13 +42,24 @@ iterate_thread_list (td_thragent_t *ta, 
 
   if (next == 0 && fake_empty)
     {
-      /* __pthread_initialize_minimal has not run.
-	 There is just the main thread to return.  */
-      td_thrhandle_t th;
-      err = td_ta_map_lwp2thr (ta, ps_getpid (ta->ph), &th);
+      /* __pthread_initialize_minimal has not run yet.  If we have
+	 initialized TLS, the main thread still has a valid ID;
+	 in static applications the main thread doesn't have
+	 an ID yet, so skip it.  */
+      psaddr_t taddr;
+
+      err = DB_GET_SYMBOL (taddr, ta, __libc_setup_tls);
       if (err == TD_OK)
-	err = callback (&th, cbdata_p) != 0 ? TD_DBERR : TD_OK;
-      return err;
+	return TD_OK;
+      else
+	{
+	  /* There is just the main thread to return.  */
+	  td_thrhandle_t th;
+	  err = td_ta_map_lwp2thr (ta, ps_getpid (ta->ph), &th);
+	  if (err == TD_OK)
+	    err = callback (&th, cbdata_p) != 0 ? TD_DBERR : TD_OK;
+	  return err;
+	}
     }
 
   /* Cache the offset from struct pthread to its list_t member.  */
Index: nptl_db/td_thr_validate.c
===================================================================
RCS file: /big/fsf/rsync/glibc/libc/nptl_db/td_thr_validate.c,v
retrieving revision 1.4
diff -u -p -r1.4 td_thr_validate.c
--- nptl_db/td_thr_validate.c	1 Jun 2004 21:42:02 -0000	1.4
+++ nptl_db/td_thr_validate.c	1 Mar 2006 17:12:42 -0000
@@ -1,5 +1,6 @@
 /* Validate a thread handle.
-   Copyright (C) 1999, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006
+   Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 1999.
 
@@ -77,13 +78,23 @@ td_thr_validate (const td_thrhandle_t *t
 
       if (err == TD_NOTHR && uninit)
 	{
-	  /* __pthread_initialize_minimal has not run yet.
-	     But the main thread still has a valid ID.  */
-	  td_thrhandle_t main_th;
-	  err = td_ta_map_lwp2thr (th->th_ta_p,
-				   ps_getpid (th->th_ta_p->ph), &main_th);
-	  if (err == TD_OK && th->th_unique != main_th.th_unique)
+	  /* __pthread_initialize_minimal has not run yet.  If we have
+	     initialized TLS, the main thread still has a valid ID;
+	     in static applications the main thread doesn't have
+	     an ID yet, so skip it.  */
+	  psaddr_t taddr;
+
+	  err = DB_GET_SYMBOL (taddr, th->th_ta_p, __libc_setup_tls);
+	  if (err == TD_OK)
 	    err = TD_NOTHR;
+	  else
+	    {
+	      td_thrhandle_t main_th;
+	      err = td_ta_map_lwp2thr (th->th_ta_p,
+				       ps_getpid (th->th_ta_p->ph), &main_th);
+	      if (err == TD_OK && th->th_unique != main_th.th_unique)
+		err = TD_NOTHR;
+	    }
 	}
     }
 


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