aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2009-10-02 16:51:04 +0000
committerPedro Alves <palves@redhat.com>2009-10-02 16:51:04 +0000
commitca2163eb33ba5572fd2988bda8bb80608e6c36be (patch)
tree86e5c3a39a572b01ce08fd12c777054009630ea1
parenta71e0887e3568d4e38601402d00f40493e696601 (diff)
downloadgdb-ca2163eb33ba5572fd2988bda8bb80608e6c36be.zip
gdb-ca2163eb33ba5572fd2988bda8bb80608e6c36be.tar.gz
gdb-ca2163eb33ba5572fd2988bda8bb80608e6c36be.tar.bz2
* linux-nat.c (TRAP_IS_SYSCALL, TRAP_REMOVE_SYSCALL_FLAG): Delete.
(SYSCALL_SIGTRAP): New. (status_to_str): Adjust. (get_pending_status): Pending events in lp->waitstatus don't map to any signal. Simplify. (linux_handle_syscall_trap): New. (linux_handle_extended_wait): When handling PTRACE_EVENT_CLONE events, use linux_ops->to_resume instead of direct ptrace with PTRACE_CONT. Remove all TRAP_IS_SYSCALL handling. (wait_lwp): Handle syscall traps with linux_handle_syscall_trap, and clear the sysgood bit. (status_callback): Make it clearer and add comments. (cancel_breakpoints_callback): Ignore if LP has waitstatus set. (linux_nat_filter_event): Handle syscall traps with linux_handle_syscall_trap, and clear the sysgood bit. Move the check for storing siginfo to after handling extended statuses and syscall traps. Store status in the lwp object. (linux_wait_1): Don't swap the pending status out of the lwp object until after deciding we found an lwp with an interesting event. Requeue a new pending signal if we find one while getting rid or a pending SIGSTOP we sent ourselves. Don't clear the sysgood bit here. * infrun.c (deal_with_syscall_event): Rename to ... (handle_syscall_event): ... this. Always context switch and set stop_pc, even if not catching the syscall. If not catching the syscall, always resume with keep_going. (handle_inferior_event): Adjust.
-rw-r--r--gdb/ChangeLog31
-rw-r--r--gdb/infrun.c60
-rw-r--r--gdb/linux-nat.c432
3 files changed, 344 insertions, 179 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 2c616a3..f8c57dd 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,34 @@
+2009-10-02 Pedro Alves <pedro@codesourcery.com>
+
+ * linux-nat.c (TRAP_IS_SYSCALL, TRAP_REMOVE_SYSCALL_FLAG): Delete.
+ (SYSCALL_SIGTRAP): New.
+ (status_to_str): Adjust.
+ (get_pending_status): Pending events in lp->waitstatus don't map
+ to any signal. Simplify.
+ (linux_handle_syscall_trap): New.
+ (linux_handle_extended_wait): When handling PTRACE_EVENT_CLONE
+ events, use linux_ops->to_resume instead of direct ptrace with
+ PTRACE_CONT. Remove all TRAP_IS_SYSCALL handling.
+ (wait_lwp): Handle syscall traps with linux_handle_syscall_trap,
+ and clear the sysgood bit.
+ (status_callback): Make it clearer and add comments.
+ (cancel_breakpoints_callback): Ignore if LP has waitstatus set.
+ (linux_nat_filter_event): Handle syscall traps with
+ linux_handle_syscall_trap, and clear the sysgood bit. Move the
+ check for storing siginfo to after handling extended statuses and
+ syscall traps. Store status in the lwp object.
+ (linux_wait_1): Don't swap the pending status out of the lwp
+ object until after deciding we found an lwp with an interesting
+ event. Requeue a new pending signal if we find one while getting
+ rid or a pending SIGSTOP we sent ourselves. Don't clear the
+ sysgood bit here.
+
+ * infrun.c (deal_with_syscall_event): Rename to ...
+ (handle_syscall_event): ... this. Always context switch and set
+ stop_pc, even if not catching the syscall. If not catching the
+ syscall, always resume with keep_going.
+ (handle_inferior_event): Adjust.
+
2009-10-02 Jan Kratochvil <jan.kratochvil@redhat.com>
Fix compatibility of --with-system-readline and readline-6.0+.
diff --git a/gdb/infrun.c b/gdb/infrun.c
index ff7c6b9..4ce07d9 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -2386,13 +2386,22 @@ stepped_in_from (struct frame_info *frame, struct frame_id step_frame_id)
It returns 1 if the inferior should keep going (and GDB
should ignore the event), or 0 if the event deserves to be
processed. */
+
static int
-deal_with_syscall_event (struct execution_control_state *ecs)
+handle_syscall_event (struct execution_control_state *ecs)
{
- struct regcache *regcache = get_thread_regcache (ecs->ptid);
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
- int syscall_number = gdbarch_get_syscall_number (gdbarch,
- ecs->ptid);
+ struct regcache *regcache;
+ struct gdbarch *gdbarch;
+ int syscall_number;
+
+ if (!ptid_equal (ecs->ptid, inferior_ptid))
+ context_switch (ecs->ptid);
+
+ regcache = get_thread_regcache (ecs->ptid);
+ gdbarch = get_regcache_arch (regcache);
+ syscall_number = gdbarch_get_syscall_number (gdbarch, ecs->ptid);
+ stop_pc = regcache_read_pc (regcache);
+
target_last_waitstatus.value.syscall_number = syscall_number;
if (catch_syscall_enabled () > 0
@@ -2401,35 +2410,22 @@ deal_with_syscall_event (struct execution_control_state *ecs)
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: syscall number = '%d'\n",
syscall_number);
- ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP;
-
- if (!ptid_equal (ecs->ptid, inferior_ptid))
- {
- context_switch (ecs->ptid);
- reinit_frame_cache ();
- }
-
- stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
-
ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
- /* If no catchpoint triggered for this, then keep going. */
- if (ecs->random_signal)
- {
- ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
- keep_going (ecs);
- return 1;
- }
- return 0;
- }
- else
- {
- resume (0, TARGET_SIGNAL_0);
- prepare_to_wait (ecs);
- return 1;
+ if (!ecs->random_signal)
+ {
+ /* Catchpoint hit. */
+ ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP;
+ return 0;
+ }
}
+
+ /* If no catchpoint triggered for this, then keep going. */
+ ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
+ keep_going (ecs);
+ return 1;
}
/* Given an execution control state that has been freshly filled in
@@ -2753,10 +2749,9 @@ handle_inferior_event (struct execution_control_state *ecs)
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL_ENTRY\n");
/* Getting the current syscall number */
- if (deal_with_syscall_event (ecs) != 0)
+ if (handle_syscall_event (ecs) != 0)
return;
goto process_event_stop_test;
- break;
/* Before examining the threads further, step this thread to
get it entirely out of the syscall. (We get notice of the
@@ -2766,10 +2761,9 @@ handle_inferior_event (struct execution_control_state *ecs)
case TARGET_WAITKIND_SYSCALL_RETURN:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL_RETURN\n");
- if (deal_with_syscall_event (ecs) != 0)
+ if (handle_syscall_event (ecs) != 0)
return;
goto process_event_stop_test;
- break;
case TARGET_WAITKIND_STOPPED:
if (debug_infrun)
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 9b8cf48..f4f843b 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -67,11 +67,6 @@
# endif
#endif /* HAVE_PERSONALITY */
-/* To be used when one needs to know wether a
- WSTOPSIG (status) is a syscall */
-#define TRAP_IS_SYSCALL (SIGTRAP | 0x80)
-#define TRAP_REMOVE_SYSCALL_FLAG(status) ((status) & ~(0x80 << 8))
-
/* This comment documents high-level logic of this file.
Waiting for events in sync mode
@@ -189,6 +184,11 @@ blocked. */
#endif /* PTRACE_EVENT_FORK */
+/* Unlike other extended result codes, WSTOPSIG (status) on
+ PTRACE_O_TRACESYSGOOD syscall events doesn't return SIGTRAP, but
+ instead SIGTRAP with bit 7 set. */
+#define SYSCALL_SIGTRAP (SIGTRAP | 0x80)
+
/* We can't always assume that this flag is available, but all systems
with the ptrace event handlers also have __WALL, so it's safe to use
here. */
@@ -982,7 +982,7 @@ status_to_str (int status)
if (WIFSTOPPED (status))
{
- if (WSTOPSIG (status) == TRAP_IS_SYSCALL)
+ if (WSTOPSIG (status) == SYSCALL_SIGTRAP)
snprintf (buf, sizeof (buf), "%s (stopped at syscall)",
strsignal (SIGTRAP));
else
@@ -1514,74 +1514,78 @@ linux_nat_attach (struct target_ops *ops, char *args, int from_tty)
static int
get_pending_status (struct lwp_info *lp, int *status)
{
- struct target_waitstatus last;
- ptid_t last_ptid;
-
- get_last_target_status (&last_ptid, &last);
-
- /* If this lwp is the ptid that GDB is processing an event from, the
- signal will be in stop_signal. Otherwise, we may cache pending
- events in lp->status while trying to stop all threads (see
- stop_wait_callback). */
+ enum target_signal signo = TARGET_SIGNAL_0;
+
+ /* If we paused threads momentarily, we may have stored pending
+ events in lp->status or lp->waitstatus (see stop_wait_callback),
+ and GDB core hasn't seen any signal for those threads.
+ Otherwise, the last signal reported to the core is found in the
+ thread object's stop_signal.
+
+ There's a corner case that isn't handled here at present. Only
+ if the thread stopped with a TARGET_WAITKIND_STOPPED does
+ stop_signal make sense as a real signal to pass to the inferior.
+ Some catchpoint related events, like
+ TARGET_WAITKIND_(V)FORK|EXEC|SYSCALL, have their stop_signal set
+ to TARGET_SIGNAL_SIGTRAP when the catchpoint triggers. But,
+ those traps are debug API (ptrace in our case) related and
+ induced; the inferior wouldn't see them if it wasn't being
+ traced. Hence, we should never pass them to the inferior, even
+ when set to pass state. Since this corner case isn't handled by
+ infrun.c when proceeding with a signal, for consistency, neither
+ do we handle it here (or elsewhere in the file we check for
+ signal pass state). Normally SIGTRAP isn't set to pass state, so
+ this is really a corner case. */
- *status = 0;
-
- if (non_stop)
+ if (lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
+ signo = TARGET_SIGNAL_0; /* a pending ptrace event, not a real signal. */
+ else if (lp->status)
+ signo = target_signal_from_host (WSTOPSIG (lp->status));
+ else if (non_stop && !is_executing (lp->ptid))
+ {
+ struct thread_info *tp = find_thread_ptid (lp->ptid);
+ signo = tp->stop_signal;
+ }
+ else if (!non_stop)
{
- enum target_signal signo = TARGET_SIGNAL_0;
+ struct target_waitstatus last;
+ ptid_t last_ptid;
- if (is_executing (lp->ptid))
- {
- /* If the core thought this lwp was executing --- e.g., the
- executing property hasn't been updated yet, but the
- thread has been stopped with a stop_callback /
- stop_wait_callback sequence (see linux_nat_detach for
- example) --- we can only have pending events in the local
- queue. */
- signo = target_signal_from_host (WSTOPSIG (lp->status));
- }
- else
- {
- /* If the core knows the thread is not executing, then we
- have the last signal recorded in
- thread_info->stop_signal. */
+ get_last_target_status (&last_ptid, &last);
+ if (GET_LWP (lp->ptid) == GET_LWP (last_ptid))
+ {
struct thread_info *tp = find_thread_ptid (lp->ptid);
signo = tp->stop_signal;
}
+ }
- if (signo != TARGET_SIGNAL_0
- && !signal_pass_state (signo))
- {
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog, "\
-GPT: lwp %s had signal %s, but it is in no pass state\n",
- target_pid_to_str (lp->ptid),
- target_signal_to_string (signo));
- }
- else
- {
- if (signo != TARGET_SIGNAL_0)
- *status = W_STOPCODE (target_signal_to_host (signo));
+ *status = 0;
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "GPT: lwp %s as pending signal %s\n",
- target_pid_to_str (lp->ptid),
- target_signal_to_string (signo));
- }
+ if (signo == TARGET_SIGNAL_0)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "GPT: lwp %s has no pending signal\n",
+ target_pid_to_str (lp->ptid));
+ }
+ else if (!signal_pass_state (signo))
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog, "\
+GPT: lwp %s had signal %s, but it is in no pass state\n",
+ target_pid_to_str (lp->ptid),
+ target_signal_to_string (signo));
}
else
{
- if (GET_LWP (lp->ptid) == GET_LWP (last_ptid))
- {
- struct thread_info *tp = find_thread_ptid (lp->ptid);
- if (tp->stop_signal != TARGET_SIGNAL_0
- && signal_pass_state (tp->stop_signal))
- *status = W_STOPCODE (target_signal_to_host (tp->stop_signal));
- }
- else
- *status = lp->status;
+ *status = W_STOPCODE (target_signal_to_host (signo));
+
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "GPT: lwp %s has pending signal %s\n",
+ target_pid_to_str (lp->ptid),
+ target_signal_to_string (signo));
}
return 0;
@@ -1893,6 +1897,133 @@ kill_lwp (int lwpid, int signo)
return kill (lwpid, signo);
}
+/* Handle a GNU/Linux syscall trap wait response. If we see a syscall
+ event, check if the core is interested in it: if not, ignore the
+ event, and keep waiting; otherwise, we need to toggle the LWP's
+ syscall entry/exit status, since the ptrace event itself doesn't
+ indicate it, and report the trap to higher layers. */
+
+static int
+linux_handle_syscall_trap (struct lwp_info *lp, int stopping)
+{
+ struct target_waitstatus *ourstatus = &lp->waitstatus;
+ struct gdbarch *gdbarch = target_thread_architecture (lp->ptid);
+ int syscall_number = (int) gdbarch_get_syscall_number (gdbarch, lp->ptid);
+
+ if (stopping)
+ {
+ /* If we're stopping threads, there's a SIGSTOP pending, which
+ makes it so that the LWP reports an immediate syscall return,
+ followed by the SIGSTOP. Skip seeing that "return" using
+ PTRACE_CONT directly, and let stop_wait_callback collect the
+ SIGSTOP. Later when the thread is resumed, a new syscall
+ entry event. If we didn't do this (and returned 0), we'd
+ leave a syscall entry pending, and our caller, by using
+ PTRACE_CONT to collect the SIGSTOP, skips the syscall return
+ itself. Later, when the user re-resumes this LWP, we'd see
+ another syscall entry event and we'd mistake it for a return.
+
+ If stop_wait_callback didn't force the SIGSTOP out of the LWP
+ (leaving immediately with LWP->signalled set, without issuing
+ a PTRACE_CONT), it would still be problematic to leave this
+ syscall enter pending, as later when the thread is resumed,
+ it would then see the same syscall exit mentioned above,
+ followed by the delayed SIGSTOP, while the syscall didn't
+ actually get to execute. It seems it would be even more
+ confusing to the user. */
+
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LHST: ignoring syscall %d "
+ "for LWP %ld (stopping threads), "
+ "resuming with PTRACE_CONT for SIGSTOP\n",
+ syscall_number,
+ GET_LWP (lp->ptid));
+
+ lp->syscall_state = TARGET_WAITKIND_IGNORE;
+ ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
+ return 1;
+ }
+
+ if (catch_syscall_enabled ())
+ {
+ /* Always update the entry/return state, even if this particular
+ syscall isn't interesting to the core now. In async mode,
+ the user could install a new catchpoint for this syscall
+ between syscall enter/return, and we'll need to know to
+ report a syscall return if that happens. */
+ lp->syscall_state = (lp->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY
+ ? TARGET_WAITKIND_SYSCALL_RETURN
+ : TARGET_WAITKIND_SYSCALL_ENTRY);
+
+ if (catching_syscall_number (syscall_number))
+ {
+ /* Alright, an event to report. */
+ ourstatus->kind = lp->syscall_state;
+ ourstatus->value.syscall_number = syscall_number;
+
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LHST: stopping for %s of syscall %d"
+ " for LWP %ld\n",
+ lp->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY
+ ? "entry" : "return",
+ syscall_number,
+ GET_LWP (lp->ptid));
+ return 0;
+ }
+
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LHST: ignoring %s of syscall %d "
+ "for LWP %ld\n",
+ lp->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY
+ ? "entry" : "return",
+ syscall_number,
+ GET_LWP (lp->ptid));
+ }
+ else
+ {
+ /* If we had been syscall tracing, and hence used PT_SYSCALL
+ before on this LWP, it could happen that the user removes all
+ syscall catchpoints before we get to process this event.
+ There are two noteworthy issues here:
+
+ - When stopped at a syscall entry event, resuming with
+ PT_STEP still resumes executing the syscall and reports a
+ syscall return.
+
+ - Only PT_SYSCALL catches syscall enters. If we last
+ single-stepped this thread, then this event can't be a
+ syscall enter. If we last single-stepped this thread, this
+ has to be a syscall exit.
+
+ The points above mean that the next resume, be it PT_STEP or
+ PT_CONTINUE, can not trigger a syscall trace event. */
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LHST: caught syscall event with no syscall catchpoints."
+ " %d for LWP %ld, ignoring\n",
+ syscall_number,
+ GET_LWP (lp->ptid));
+ lp->syscall_state = TARGET_WAITKIND_IGNORE;
+ }
+
+ /* The core isn't interested in this event. For efficiency, avoid
+ stopping all threads only to have the core resume them all again.
+ Since we're not stopping threads, if we're still syscall tracing
+ and not stepping, we can't use PTRACE_CONT here, as we'd miss any
+ subsequent syscall. Simply resume using the inf-ptrace layer,
+ which knows when to use PT_SYSCALL or PT_CONTINUE. */
+
+ /* Note that gdbarch_get_syscall_number may access registers, hence
+ fill a regcache. */
+ registers_changed ();
+ linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
+ lp->step, TARGET_SIGNAL_0);
+ return 1;
+}
+
/* Handle a GNU/Linux extended wait response. If we see a clone
event, we need to add the new LWP to our list (and not report the
trap to higher layers). This function returns non-zero if the
@@ -2018,19 +2149,30 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
}
}
+ /* Note the need to use the low target ops to resume, to
+ handle resuming with PT_SYSCALL if we have syscall
+ catchpoints. */
if (!stopping)
{
+ int signo;
+
new_lp->stopped = 0;
new_lp->resumed = 1;
- ptrace (PTRACE_CONT, new_pid, 0,
- status ? WSTOPSIG (status) : 0);
+
+ signo = (status
+ ? target_signal_from_host (WSTOPSIG (status))
+ : TARGET_SIGNAL_0);
+
+ linux_ops->to_resume (linux_ops, pid_to_ptid (new_pid),
+ 0, signo);
}
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LHEW: Got clone event from LWP %ld, resuming\n",
GET_LWP (lp->ptid));
- ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
+ linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
+ 0, TARGET_SIGNAL_0);
return 1;
}
@@ -2070,47 +2212,6 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
return 0;
}
- /* Used for 'catch syscall' feature. */
- if (WSTOPSIG (status) == TRAP_IS_SYSCALL)
- {
- if (catch_syscall_enabled () == 0)
- ourstatus->kind = TARGET_WAITKIND_IGNORE;
- else
- {
- struct regcache *regcache = get_thread_regcache (lp->ptid);
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
-
- ourstatus->value.syscall_number =
- (int) gdbarch_get_syscall_number (gdbarch, lp->ptid);
-
- /* If we are catching this specific syscall number, then we
- should update the target_status to reflect which event
- has occurred. But if this syscall is not to be caught,
- then we can safely mark the event as a SYSCALL_RETURN.
-
- This is particularly needed if:
-
- - We are catching any syscalls, or
- - We are catching the syscall "exit"
-
- In this case, as the syscall "exit" *doesn't* return,
- then GDB would be confused because it would mark the last
- syscall event as a SYSCALL_ENTRY. After that, if we re-ran the
- inferior GDB will think that the first syscall event is
- the opposite of a SYSCALL_ENTRY, which is the SYSCALL_RETURN.
- Therefore, GDB would report inverted syscall events. */
- if (catching_syscall_number (ourstatus->value.syscall_number))
- ourstatus->kind =
- (lp->syscall_state == TARGET_WAITKIND_SYSCALL_ENTRY) ?
- TARGET_WAITKIND_SYSCALL_RETURN : TARGET_WAITKIND_SYSCALL_ENTRY;
- else
- ourstatus->kind = TARGET_WAITKIND_SYSCALL_RETURN;
-
- lp->syscall_state = ourstatus->kind;
- }
- return 0;
- }
-
internal_error (__FILE__, __LINE__,
_("unknown ptrace event %d"), event);
}
@@ -2176,6 +2277,18 @@ wait_lwp (struct lwp_info *lp)
gdb_assert (WIFSTOPPED (status));
+ /* Handle GNU/Linux's syscall SIGTRAPs. */
+ if (WIFSTOPPED (status) && WSTOPSIG (status) == SYSCALL_SIGTRAP)
+ {
+ /* No longer need the sysgood bit. The ptrace event ends up
+ recorded in lp->waitstatus if we care for it. We can carry
+ on handling the event like a regular SIGTRAP from here
+ on. */
+ status = W_STOPCODE (SIGTRAP);
+ if (linux_handle_syscall_trap (lp, 1))
+ return wait_lwp (lp);
+ }
+
/* Handle GNU/Linux's extended waitstatus for trace events. */
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
{
@@ -2442,12 +2555,23 @@ status_callback (struct lwp_info *lp, void *data)
{
/* Only report a pending wait status if we pretend that this has
indeed been resumed. */
- /* We check for lp->waitstatus in addition to lp->status, because we
- can have pending process exits recorded in lp->waitstatus, and
- W_EXITCODE(0,0) == 0. */
- return ((lp->status != 0
- || lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
- && lp->resumed);
+ if (!lp->resumed)
+ return 0;
+
+ if (lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
+ {
+ /* A ptrace event, like PTRACE_FORK|VFORK|EXEC, syscall event,
+ or a a pending process exit. Note that `W_EXITCODE(0,0) ==
+ 0', so a clean process exit can not be stored pending in
+ lp->status, it is indistinguishable from
+ no-pending-status. */
+ return 1;
+ }
+
+ if (lp->status != 0)
+ return 1;
+
+ return 0;
}
/* Return non-zero if LP isn't stopped. */
@@ -2557,7 +2681,8 @@ cancel_breakpoints_callback (struct lwp_info *lp, void *data)
delete or disable the breakpoint, but the LWP will have already
tripped on it. */
- if (lp->status != 0
+ if (lp->waitstatus.kind == TARGET_WAITKIND_IGNORE
+ && lp->status != 0
&& WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP
&& cancel_breakpoint (lp))
/* Throw away the SIGTRAP. */
@@ -2708,17 +2833,20 @@ linux_nat_filter_event (int lwpid, int status, int options)
add_thread (lp->ptid);
}
- /* Save the trap's siginfo in case we need it later. */
- if (WIFSTOPPED (status)
- && (WSTOPSIG (status) == SIGTRAP || WSTOPSIG (status) == TRAP_IS_SYSCALL))
- save_siginfo (lp);
+ /* Handle GNU/Linux's syscall SIGTRAPs. */
+ if (WIFSTOPPED (status) && WSTOPSIG (status) == SYSCALL_SIGTRAP)
+ {
+ /* No longer need the sysgood bit. The ptrace event ends up
+ recorded in lp->waitstatus if we care for it. We can carry
+ on handling the event like a regular SIGTRAP from here
+ on. */
+ status = W_STOPCODE (SIGTRAP);
+ if (linux_handle_syscall_trap (lp, 0))
+ return NULL;
+ }
- /* Handle GNU/Linux's extended waitstatus for trace events.
- It is necessary to check if WSTOPSIG is signaling that
- the inferior is entering/exiting a system call. */
- if (WIFSTOPPED (status)
- && ((WSTOPSIG (status) == TRAP_IS_SYSCALL)
- || (WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)))
+ /* Handle GNU/Linux's extended waitstatus for trace events. */
+ if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
@@ -2728,6 +2856,10 @@ linux_nat_filter_event (int lwpid, int status, int options)
return NULL;
}
+ /* Save the trap's siginfo in case we need it later. */
+ if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP)
+ save_siginfo (lp);
+
/* Check if the thread has exited. */
if ((WIFEXITED (status) || WIFSIGNALED (status))
&& num_lwps (GET_PID (lp->ptid)) > 1)
@@ -2849,6 +2981,7 @@ linux_nat_filter_event (int lwpid, int status, int options)
/* An interesting event. */
gdb_assert (lp);
+ lp->status = status;
return lp;
}
@@ -2908,13 +3041,10 @@ retry:
lp = iterate_over_lwps (ptid, status_callback, NULL);
if (lp)
{
- status = lp->status;
- lp->status = 0;
-
- if (debug_linux_nat && status)
+ if (debug_linux_nat && lp->status)
fprintf_unfiltered (gdb_stdlog,
"LLW: Using pending wait status %s for %s.\n",
- status_to_str (status),
+ status_to_str (lp->status),
target_pid_to_str (lp->ptid));
}
@@ -2933,13 +3063,11 @@ retry:
/* We have a specific LWP to check. */
lp = find_lwp_pid (ptid);
gdb_assert (lp);
- status = lp->status;
- lp->status = 0;
- if (debug_linux_nat && status)
+ if (debug_linux_nat && lp->status)
fprintf_unfiltered (gdb_stdlog,
"LLW: Using pending wait status %s for %s.\n",
- status_to_str (status),
+ status_to_str (lp->status),
target_pid_to_str (lp->ptid));
/* If we have to wait, take into account whether PID is a cloned
@@ -2952,7 +3080,7 @@ retry:
because we can have pending process exits recorded in
lp->status and W_EXITCODE(0,0) == 0. We should probably have
an additional lp->status_p flag. */
- if (status == 0 && lp->waitstatus.kind == TARGET_WAITKIND_IGNORE)
+ if (lp->status == 0 && lp->waitstatus.kind == TARGET_WAITKIND_IGNORE)
lp = NULL;
}
@@ -2980,8 +3108,26 @@ retry:
lp->stopped = 0;
gdb_assert (lp->resumed);
- /* This should catch the pending SIGSTOP. */
+ /* Catch the pending SIGSTOP. */
+ status = lp->status;
+ lp->status = 0;
+
stop_wait_callback (lp, NULL);
+
+ /* If the lp->status field isn't empty, we caught another signal
+ while flushing the SIGSTOP. Return it back to the event
+ queue of the LWP, as we already have an event to handle. */
+ if (lp->status)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLW: kill %s, %s\n",
+ target_pid_to_str (lp->ptid),
+ status_to_str (lp->status));
+ kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status));
+ }
+
+ lp->status = status;
}
if (!target_can_async_p ())
@@ -3013,12 +3159,6 @@ retry:
lp = linux_nat_filter_event (lwpid, status, options);
- /* If this was a syscall trap, we no longer need or want
- the 0x80 flag, remove it. */
- if (WIFSTOPPED (status)
- && WSTOPSIG (status) == TRAP_IS_SYSCALL)
- status = TRAP_REMOVE_SYSCALL_FLAG (status);
-
if (lp
&& ptid_is_pid (ptid)
&& ptid_get_pid (lp->ptid) != ptid_get_pid (ptid))
@@ -3027,12 +3167,10 @@ retry:
fprintf (stderr, "LWP %ld got an event %06x, leaving pending.\n",
ptid_get_lwp (lp->ptid), status);
- if (WIFSTOPPED (status))
+ if (WIFSTOPPED (lp->status))
{
- if (WSTOPSIG (status) != SIGSTOP)
+ if (WSTOPSIG (lp->status) != SIGSTOP)
{
- lp->status = status;
-
stop_callback (lp, NULL);
/* Resume in order to collect the sigstop. */
@@ -3059,7 +3197,6 @@ retry:
about the exit code/signal, leave the status
pending for the next time we're able to report
it. */
- lp->status = status;
/* Prevent trying to stop this thread again. We'll
never try to resume it because it has a pending
@@ -3072,7 +3209,7 @@ retry:
/* Store the pending event in the waitstatus as
well, because W_EXITCODE(0,0) == 0. */
- store_waitstatus (&lp->waitstatus, status);
+ store_waitstatus (&lp->waitstatus, lp->status);
}
/* Keep looking. */
@@ -3128,6 +3265,9 @@ retry:
gdb_assert (lp);
+ status = lp->status;
+ lp->status = 0;
+
/* Don't report signals that GDB isn't interested in, such as
signals that are neither printed nor stopped upon. Stopping all
threads can be a bit time-consuming so if we want decent