This is the mail archive of the gdb-cvs@sourceware.org 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]

[binutils-gdb] Linux gdbserver confused when event randomization picks process exit event


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=00db26facc14ac830adef704bba9b24d0d366ddf

commit 00db26facc14ac830adef704bba9b24d0d366ddf
Author: Pedro Alves <palves@redhat.com>
Date:   Thu Aug 6 10:30:17 2015 +0100

    Linux gdbserver confused when event randomization picks process exit event
    
    The tail end of linux_wait_1 isn't expecting that the select_event_lwp
    machinery can pick a whole-process exit event to report to GDB.  When
    that happens, both gdb and gdbserver end up quite confused:
    
     ...
     (gdb)
     [Thread 24971.24971] #1 stopped.
     0x0000003615a011f0 in ?? ()
     c&
     Continuing.
     (gdb) [New Thread 24971.24981]
     [New Thread 24983.24983]
     [New Thread 24971.24982]
    
     [Thread 24983.24983] #3 stopped.
     0x0000003615ebc7cc in __libc_fork () at ../nptl/sysdeps/unix/sysv/linux/fork.c:130
     130       pid = ARCH_FORK ();
     [New Thread 24984.24984]
     Error in re-setting breakpoint -16: PC register is not available
     Error in re-setting breakpoint -17: PC register is not available
     Error in re-setting breakpoint -18: PC register is not available
     Error in re-setting breakpoint -19: PC register is not available
     Error in re-setting breakpoint -24: PC register is not available
     Error in re-setting breakpoint -25: PC register is not available
     Error in re-setting breakpoint -26: PC register is not available
     Error in re-setting breakpoint -27: PC register is not available
     Error in re-setting breakpoint -28: PC register is not available
     Error in re-setting breakpoint -29: PC register is not available
     Error in re-setting breakpoint -30: PC register is not available
     PC register is not available
     (gdb)
    
    gdb/gdbserver/ChangeLog:
    2015-08-06  Pedro Alves  <palves@redhat.com>
    
    	* linux-low.c (add_lwp): Set waitstatus to TARGET_WAITKIND_IGNORE.
    	(linux_thread_alive): Use lwp_is_marked_dead.
    	(extended_event_reported): Delete.
    	(linux_wait_1): Check if waitstatus is TARGET_WAITKIND_IGNORE
    	instead of extended_event_reported.
    	(mark_lwp_dead): Don't set the 'dead' flag.  Store the waitstatus
    	as well.
    	(lwp_is_marked_dead): New function.
    	(lwp_running): Use lwp_is_marked_dead.
    	* linux-low.h: Delete 'dead' field, and update 'waitstatus's
    	comment.

Diff:
---
 gdb/gdbserver/ChangeLog   | 14 +++++++++++
 gdb/gdbserver/linux-low.c | 64 ++++++++++++++++++++++++++---------------------
 gdb/gdbserver/linux-low.h | 11 +++-----
 3 files changed, 54 insertions(+), 35 deletions(-)

diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index eb1101c..77b4330 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,5 +1,19 @@
 2015-08-06  Pedro Alves  <palves@redhat.com>
 
+	* linux-low.c (add_lwp): Set waitstatus to TARGET_WAITKIND_IGNORE.
+	(linux_thread_alive): Use lwp_is_marked_dead.
+	(extended_event_reported): Delete.
+	(linux_wait_1): Check if waitstatus is TARGET_WAITKIND_IGNORE
+	instead of extended_event_reported.
+	(mark_lwp_dead): Don't set the 'dead' flag.  Store the waitstatus
+	as well.
+	(lwp_is_marked_dead): New function.
+	(lwp_running): Use lwp_is_marked_dead.
+	* linux-low.h: Delete 'dead' field, and update 'waitstatus's
+	comment.
+
+2015-08-06  Pedro Alves  <palves@redhat.com>
+
 	* linux-low.c (linux_wait_1): Move fork event output out of the
 	!report_to_gdb check.  Pass event_child->waitstatus to
 	target_waitstatus_to_string instead of ourstatus.
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 76b212d..6a7182a 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -267,6 +267,7 @@ static int linux_wait_for_event (ptid_t ptid, int *wstat, int options);
 static struct lwp_info *add_lwp (ptid_t ptid);
 static int linux_stopped_by_watchpoint (void);
 static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
+static int lwp_is_marked_dead (struct lwp_info *lwp);
 static void proceed_all_lwps (void);
 static int finish_step_over (struct lwp_info *lwp);
 static int kill_lwp (unsigned long lwpid, int signo);
@@ -746,8 +747,9 @@ add_lwp (ptid_t ptid)
 {
   struct lwp_info *lwp;
 
-  lwp = (struct lwp_info *) xmalloc (sizeof (*lwp));
-  memset (lwp, 0, sizeof (*lwp));
+  lwp = (struct lwp_info *) xcalloc (1, sizeof (*lwp));
+
+  lwp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
 
   if (the_low_target.new_thread != NULL)
     the_low_target.new_thread (lwp);
@@ -1396,7 +1398,7 @@ linux_thread_alive (ptid_t ptid)
      exited but we still haven't been able to report it to GDB, we'll
      hold on to the last lwp of the dead process.  */
   if (lwp != NULL)
-    return !lwp->dead;
+    return !lwp_is_marked_dead (lwp);
   else
     return 0;
 }
@@ -2750,20 +2752,6 @@ ignore_event (struct target_waitstatus *ourstatus)
   return null_ptid;
 }
 
-/* Return non-zero if WAITSTATUS reflects an extended linux
-   event.  Otherwise, return zero.  */
-
-static int
-extended_event_reported (const struct target_waitstatus *waitstatus)
-{
-  if (waitstatus == NULL)
-    return 0;
-
-  return (waitstatus->kind == TARGET_WAITKIND_FORKED
-	  || waitstatus->kind == TARGET_WAITKIND_VFORKED
-	  || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE);
-}
-
 /* Wait for process, returns status.  */
 
 static ptid_t
@@ -3131,7 +3119,7 @@ linux_wait_1 (ptid_t ptid,
 		   || (gdb_breakpoint_here (event_child->stop_pc)
 		       && gdb_condition_true_at_breakpoint (event_child->stop_pc)
 		       && gdb_no_commands_at_breakpoint (event_child->stop_pc))
-		   || extended_event_reported (&event_child->waitstatus));
+		   || event_child->waitstatus.kind != TARGET_WAITKIND_IGNORE);
 
   run_breakpoint_commands (event_child->stop_pc);
 
@@ -3183,7 +3171,7 @@ linux_wait_1 (ptid_t ptid,
 
   if (debug_threads)
     {
-      if (extended_event_reported (&event_child->waitstatus))
+      if (event_child->waitstatus.kind != TARGET_WAITKIND_IGNORE)
 	{
 	  char *str;
 
@@ -3271,12 +3259,11 @@ linux_wait_1 (ptid_t ptid,
 	unstop_all_lwps (1, event_child);
     }
 
-  if (extended_event_reported (&event_child->waitstatus))
+  if (event_child->waitstatus.kind != TARGET_WAITKIND_IGNORE)
     {
-      /* If the reported event is a fork, vfork or exec, let GDB know.  */
-      ourstatus->kind = event_child->waitstatus.kind;
-      ourstatus->value = event_child->waitstatus.value;
-
+      /* If the reported event is an exit, fork, vfork or exec, let
+	 GDB know.  */
+      *ourstatus = event_child->waitstatus;
       /* Clear the event lwp's waitstatus since we handled it already.  */
       event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE;
     }
@@ -3484,13 +3471,23 @@ suspend_and_send_sigstop_callback (struct inferior_list_entry *entry,
 static void
 mark_lwp_dead (struct lwp_info *lwp, int wstat)
 {
-  /* It's dead, really.  */
-  lwp->dead = 1;
-
   /* Store the exit status for later.  */
   lwp->status_pending_p = 1;
   lwp->status_pending = wstat;
 
+  /* Store in waitstatus as well, as there's nothing else to process
+     for this event.  */
+  if (WIFEXITED (wstat))
+    {
+      lwp->waitstatus.kind = TARGET_WAITKIND_EXITED;
+      lwp->waitstatus.value.integer = WEXITSTATUS (wstat);
+    }
+  else if (WIFSIGNALED (wstat))
+    {
+      lwp->waitstatus.kind = TARGET_WAITKIND_SIGNALLED;
+      lwp->waitstatus.value.sig = gdb_signal_from_host (WTERMSIG (wstat));
+    }
+
   /* Prevent trying to stop it.  */
   lwp->stopped = 1;
 
@@ -3498,6 +3495,17 @@ mark_lwp_dead (struct lwp_info *lwp, int wstat)
   lwp->stop_expected = 0;
 }
 
+/* Return true if LWP has exited already, and has a pending exit event
+   to report to GDB.  */
+
+static int
+lwp_is_marked_dead (struct lwp_info *lwp)
+{
+  return (lwp->status_pending_p
+	  && (WIFEXITED (lwp->status_pending)
+	      || WIFSIGNALED (lwp->status_pending)));
+}
+
 /* Wait for all children to stop for the SIGSTOPs we just queued.  */
 
 static void
@@ -3614,7 +3622,7 @@ lwp_running (struct inferior_list_entry *entry, void *data)
   struct thread_info *thread = (struct thread_info *) entry;
   struct lwp_info *lwp = get_thread_lwp (thread);
 
-  if (lwp->dead)
+  if (lwp_is_marked_dead (lwp))
     return 0;
   if (lwp->stopped)
     return 0;
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index 24fb015..f8f6e78 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -261,16 +261,13 @@ struct lwp_info
      event already received in a wait()).  */
   int stopped;
 
-  /* If this flag is set, the lwp is known to be dead already (exit
-     event already received in a wait(), and is cached in
-     status_pending).  */
-  int dead;
-
   /* When stopped is set, the last wait status recorded for this lwp.  */
   int last_status;
 
-  /* This is used to store extended ptrace event information until
-     it is reported to GDB.  */
+  /* If WAITSTATUS->KIND != TARGET_WAITKIND_IGNORE, the waitstatus for
+     this LWP's last event, to pass to GDB without any further
+     processing.  This is used to store extended ptrace event
+     information or exit status until it can be reported to GDB.  */
   struct target_waitstatus waitstatus;
 
   /* When stopped is set, this is where the lwp last stopped, with


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