aboutsummaryrefslogtreecommitdiff
path: root/gdb/gdbserver
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2015-08-06 10:30:17 +0100
committerPedro Alves <palves@redhat.com>2015-08-06 10:30:17 +0100
commit00db26facc14ac830adef704bba9b24d0d366ddf (patch)
tree4568e351692b2abe6f9e96b944ec8747f097d3d8 /gdb/gdbserver
parentad071a3055b9b47dad340c1a1cb3a9b39529cef0 (diff)
downloadgdb-00db26facc14ac830adef704bba9b24d0d366ddf.zip
gdb-00db26facc14ac830adef704bba9b24d0d366ddf.tar.gz
gdb-00db26facc14ac830adef704bba9b24d0d366ddf.tar.bz2
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.
Diffstat (limited to 'gdb/gdbserver')
-rw-r--r--gdb/gdbserver/ChangeLog14
-rw-r--r--gdb/gdbserver/linux-low.c64
-rw-r--r--gdb/gdbserver/linux-low.h11
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