This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB 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]

RFA [threads]: Fix print-threads.exp


As you could tell from earlier messages, this bug is somewhat gross...

Basically, at one point in the process of exiting a thread (in LinuxThreads,
this is) sets its own p_terminated flag.  After that, libthread_db reports
it as a zombie and refuses to do certain things with it.  But the LWP is
really still there, and we still need to handle it, even if the "thread" is
logically gone.

The reason print-threads.exp triggered this problem is because of a
breakpoint on kill (); kill is used in the thread's exit path.  I think
breakpointing kill is a reasonable thing to do, so I'd like this to work.

The only thing we can do is detect this case [if map_id2thr calls, we
iterate over threads looking for zombies], and handle that thread like an
LWP instead of querying libthread_db about it.  This works just fine, and
fixes print-threads.exp.  It also fixes schedlock.exp in my last couple of
tests, but I'm betting that's just random.  I'll get back to making
schedlock.exp less flaky next week.

Is this OK?  Does anyone have any better ideas?  Yes, I imagine I could fix
this more easily by fixing the system's threads library, but for obvious
reasons I'd rather GDB coped.

-- 
Daniel Jacobowitz
MontaVista Software                         Debian GNU/Linux Developer

2003-01-10  Daniel Jacobowitz  <drow@mvista.com>

	* thread-db.c (struct private_thread_info): Add zombie flag.
	(thread_get_info_callback): Check for zombies.
	(thread_db_map_id2thr): Use thread_get_info_callback as a fallback
	if td_ta_map_id2thr_p fails.
	(attach_thread): Update comment.
	(clear_lwpid_cache): Clear zombie flag.
	(thread_db_fetch_registers, thread_db_store_registers): Pass
	requests for zombie threads down to the lower target.
	(thread_db_thread_alive): Return that zombie threads are alive.

--- thread-db.c.minimalcach	2003-01-10 15:09:03.000000000 -0500
+++ thread-db.c	2003-01-10 15:51:31.000000000 -0500
@@ -42,7 +42,6 @@
    threads.  */
 
 /* FIXME: There is certainly some room for improvements:
-   - Cache LWP ids.
    - Bypass libthread_db when fetching or storing registers for
    threads bound to a LWP.  */
 
@@ -144,7 +143,13 @@ static void attach_thread (ptid_t ptid, 
 
 
 /* Use "struct private_thread_info" to cache thread state.  This is
-   a substantial optimization.  */
+   a substantial optimization, and also required for correctness on
+   GNU Libc (2.2.5, 2.3.1) LinuxThreads.  With that threads package,
+   a thread can be reported as a zombie (that's TD_THR_ZOMBIE) while
+   the underlying LWP still exists.  But at this point we can't call
+   td_ta_map_id2thr_p on it any more, so we have to find the information
+   another way: by iterating over threads and saving what thread_db tells
+   us in this cache.  */
 
 struct private_thread_info
 {
@@ -152,6 +157,11 @@ struct private_thread_info
   unsigned int th_valid : 1;
   unsigned int ti_valid : 1;
 
+  /* This flag will be set if the thread has "exited" according to thread_db
+     but the underlying LWP still exists and is still running that same
+     thread.  This limits what we can do to the thread.  */
+  unsigned int zombie : 1;
+
   td_thrhandle_t th;
   td_thrinfo_t ti;
 };
@@ -238,7 +248,9 @@ thread_db_state_str (td_thr_state_e stat
 }
 
 /* A callback function for td_ta_thr_iter, which we use to map all
-   threads to LWPs.  
+   threads to LWPs.  This function also implements a workaround for
+   the TD_THR_ZOMBIE behavior discussed above (see "struct
+   private_thread_info" for more information).
 
    THP is a handle to the current thread; if INFOP is not NULL, the
    struct thread_info associated with this thread is returned in
@@ -274,6 +286,13 @@ thread_get_info_callback (const td_thrha
   memcpy (&thread_info->private->ti, &ti, sizeof (ti));
   thread_info->private->ti_valid = 1;
 
+  /* If the thread is marked as a zombie or dead, but it has an LWP ID and
+     we're still tracing that LWP, mark the thread as a "zombie".  */
+
+  if (ti.ti_state == TD_THR_ZOMBIE || ti.ti_state == TD_THR_UNKNOWN)
+    if (target_beneath->to_thread_alive (BUILD_LWP (ti.ti_lid, ti.ti_lid)))
+      thread_info->private->zombie = 1;
+
   if (infop != NULL)
     *(struct thread_info **) infop = thread_info;
 
@@ -292,6 +311,26 @@ thread_db_map_id2thr (struct thread_info
 
   err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (thread_info->ptid),
 			    &thread_info->private->th);
+  if (err == TD_NOTHR)
+    {
+      /* Depending on the libthread_db implementation, there's a good chance
+	 the thread exists, but has been bitten by the misguided TD_THR_ZOMBIE
+	 handling discussed above.  We have to iterate over all threads trying
+	 to find it.  If we succeed, thread_get_info_callback will set the
+	 cached thread handle from what thread_db passed it, and we can use
+	 that.  */
+
+      td_ta_thr_iter_p (thread_agent, thread_get_info_callback, NULL,
+			TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+			TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+      if (thread_info->private->th_valid)
+	return;
+
+      /* Otherwise, the thread really appears to be gone.  There may still be
+	 some problems here if we're debugging detached threads; thread_db
+	 will gladly tell us they no longer exist.  */
+    }
+
   if (err != TD_OK)
     {
       if (fatal)
@@ -674,6 +713,7 @@ attach_thread (ptid_t ptid, const td_thr
   if (verbose)
     printf_unfiltered ("[New %s]\n", target_pid_to_str (ptid));
 
+  /* Note: Bad for "pseudo-zombies" that LinuxThreads leaves... */
   if (ti_p->ti_state == TD_THR_UNKNOWN || ti_p->ti_state == TD_THR_ZOMBIE)
     return;			/* A zombie thread -- do not attach.  */
 
@@ -738,6 +778,7 @@ clear_lwpid_callback (struct thread_info
 
   thread->private->th_valid = 0;
   thread->private->ti_valid = 0;
+  thread->private->zombie = 0;
 
   return 0;
 }
@@ -902,6 +943,16 @@ thread_db_fetch_registers (int regno)
 
   thread_info = find_thread_pid (inferior_ptid);
   thread_db_map_id2thr (thread_info, 1);
+  if (thread_info->private->zombie)
+    {
+      /* Pass the request to the target beneath us.  */
+      struct cleanup *old_chain = save_inferior_ptid ();
+      inferior_ptid = BUILD_LWP (thread_info->private->ti.ti_lid,
+				 GET_PID (inferior_ptid));
+      target_beneath->to_fetch_registers (regno);
+      do_cleanups (old_chain);
+      return;
+    }
 
   err = td_thr_getgregs_p (&thread_info->private->th, gregset);
   if (err != TD_OK)
@@ -937,6 +988,16 @@ thread_db_store_registers (int regno)
 
   thread_info = find_thread_pid (inferior_ptid);
   thread_db_map_id2thr (thread_info, 1);
+  if (thread_info->private->zombie)
+    {
+      /* Pass the request to the target beneath us.  */
+      struct cleanup *old_chain = save_inferior_ptid ();
+      inferior_ptid = BUILD_LWP (thread_info->private->ti.ti_lid,
+				 GET_PID (inferior_ptid));
+      target_beneath->to_store_registers (regno);
+      do_cleanups (old_chain);
+      return;
+    }
 
   if (regno != -1)
     {
@@ -1023,6 +1084,11 @@ thread_db_thread_alive (ptid_t ptid)
       if (! thread_info->private->th_valid)
 	return 0;
 
+      /* If it's a known thread library zombie (unlike normal zombies,
+	 this implies that the LWP still exists) consider it alive.  */
+      if (thread_info->private->zombie)
+	return 1;
+
       err = td_thr_validate_p (&thread_info->private->th);
       if (err != TD_OK)
 	return 0;


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