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.
This commit is contained in:
parent
ad071a3055
commit
00db26facc
3 changed files with 54 additions and 35 deletions
|
@ -1,3 +1,17 @@
|
|||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue