* 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.
This commit is contained in:
parent
a71e0887e3
commit
ca2163eb33
|
@ -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>
|
2009-10-02 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||||
|
|
||||||
Fix compatibility of --with-system-readline and readline-6.0+.
|
Fix compatibility of --with-system-readline and readline-6.0+.
|
||||||
|
|
60
gdb/infrun.c
60
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
|
It returns 1 if the inferior should keep going (and GDB
|
||||||
should ignore the event), or 0 if the event deserves to be
|
should ignore the event), or 0 if the event deserves to be
|
||||||
processed. */
|
processed. */
|
||||||
|
|
||||||
static int
|
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 regcache *regcache;
|
||||||
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
struct gdbarch *gdbarch;
|
||||||
int syscall_number = gdbarch_get_syscall_number (gdbarch,
|
int syscall_number;
|
||||||
ecs->ptid);
|
|
||||||
|
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;
|
target_last_waitstatus.value.syscall_number = syscall_number;
|
||||||
|
|
||||||
if (catch_syscall_enabled () > 0
|
if (catch_syscall_enabled () > 0
|
||||||
|
@ -2401,35 +2410,22 @@ deal_with_syscall_event (struct execution_control_state *ecs)
|
||||||
if (debug_infrun)
|
if (debug_infrun)
|
||||||
fprintf_unfiltered (gdb_stdlog, "infrun: syscall number = '%d'\n",
|
fprintf_unfiltered (gdb_stdlog, "infrun: syscall number = '%d'\n",
|
||||||
syscall_number);
|
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->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
|
||||||
|
|
||||||
ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
|
ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
|
||||||
|
|
||||||
/* If no catchpoint triggered for this, then keep going. */
|
if (!ecs->random_signal)
|
||||||
if (ecs->random_signal)
|
{
|
||||||
{
|
/* Catchpoint hit. */
|
||||||
ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
|
ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP;
|
||||||
keep_going (ecs);
|
return 0;
|
||||||
return 1;
|
}
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
resume (0, TARGET_SIGNAL_0);
|
|
||||||
prepare_to_wait (ecs);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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
|
/* 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)
|
if (debug_infrun)
|
||||||
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL_ENTRY\n");
|
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL_ENTRY\n");
|
||||||
/* Getting the current syscall number */
|
/* Getting the current syscall number */
|
||||||
if (deal_with_syscall_event (ecs) != 0)
|
if (handle_syscall_event (ecs) != 0)
|
||||||
return;
|
return;
|
||||||
goto process_event_stop_test;
|
goto process_event_stop_test;
|
||||||
break;
|
|
||||||
|
|
||||||
/* Before examining the threads further, step this thread to
|
/* Before examining the threads further, step this thread to
|
||||||
get it entirely out of the syscall. (We get notice of the
|
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:
|
case TARGET_WAITKIND_SYSCALL_RETURN:
|
||||||
if (debug_infrun)
|
if (debug_infrun)
|
||||||
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL_RETURN\n");
|
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL_RETURN\n");
|
||||||
if (deal_with_syscall_event (ecs) != 0)
|
if (handle_syscall_event (ecs) != 0)
|
||||||
return;
|
return;
|
||||||
goto process_event_stop_test;
|
goto process_event_stop_test;
|
||||||
break;
|
|
||||||
|
|
||||||
case TARGET_WAITKIND_STOPPED:
|
case TARGET_WAITKIND_STOPPED:
|
||||||
if (debug_infrun)
|
if (debug_infrun)
|
||||||
|
|
436
gdb/linux-nat.c
436
gdb/linux-nat.c
|
@ -67,11 +67,6 @@
|
||||||
# endif
|
# endif
|
||||||
#endif /* HAVE_PERSONALITY */
|
#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.
|
/* This comment documents high-level logic of this file.
|
||||||
|
|
||||||
Waiting for events in sync mode
|
Waiting for events in sync mode
|
||||||
|
@ -189,6 +184,11 @@ blocked. */
|
||||||
|
|
||||||
#endif /* PTRACE_EVENT_FORK */
|
#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
|
/* 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
|
with the ptrace event handlers also have __WALL, so it's safe to use
|
||||||
here. */
|
here. */
|
||||||
|
@ -982,7 +982,7 @@ status_to_str (int status)
|
||||||
|
|
||||||
if (WIFSTOPPED (status))
|
if (WIFSTOPPED (status))
|
||||||
{
|
{
|
||||||
if (WSTOPSIG (status) == TRAP_IS_SYSCALL)
|
if (WSTOPSIG (status) == SYSCALL_SIGTRAP)
|
||||||
snprintf (buf, sizeof (buf), "%s (stopped at syscall)",
|
snprintf (buf, sizeof (buf), "%s (stopped at syscall)",
|
||||||
strsignal (SIGTRAP));
|
strsignal (SIGTRAP));
|
||||||
else
|
else
|
||||||
|
@ -1514,74 +1514,78 @@ linux_nat_attach (struct target_ops *ops, char *args, int from_tty)
|
||||||
static int
|
static int
|
||||||
get_pending_status (struct lwp_info *lp, int *status)
|
get_pending_status (struct lwp_info *lp, int *status)
|
||||||
{
|
{
|
||||||
struct target_waitstatus last;
|
enum target_signal signo = TARGET_SIGNAL_0;
|
||||||
ptid_t last_ptid;
|
|
||||||
|
|
||||||
get_last_target_status (&last_ptid, &last);
|
/* 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.
|
||||||
|
|
||||||
/* If this lwp is the ptid that GDB is processing an event from, the
|
There's a corner case that isn't handled here at present. Only
|
||||||
signal will be in stop_signal. Otherwise, we may cache pending
|
if the thread stopped with a TARGET_WAITKIND_STOPPED does
|
||||||
events in lp->status while trying to stop all threads (see
|
stop_signal make sense as a real signal to pass to the inferior.
|
||||||
stop_wait_callback). */
|
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 (lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
|
||||||
|
signo = TARGET_SIGNAL_0; /* a pending ptrace event, not a real signal. */
|
||||||
if (non_stop)
|
else if (lp->status)
|
||||||
|
signo = target_signal_from_host (WSTOPSIG (lp->status));
|
||||||
|
else if (non_stop && !is_executing (lp->ptid))
|
||||||
{
|
{
|
||||||
enum target_signal signo = TARGET_SIGNAL_0;
|
struct thread_info *tp = find_thread_ptid (lp->ptid);
|
||||||
|
signo = tp->stop_signal;
|
||||||
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. */
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else if (!non_stop)
|
||||||
{
|
{
|
||||||
|
struct target_waitstatus last;
|
||||||
|
ptid_t last_ptid;
|
||||||
|
|
||||||
|
get_last_target_status (&last_ptid, &last);
|
||||||
|
|
||||||
if (GET_LWP (lp->ptid) == GET_LWP (last_ptid))
|
if (GET_LWP (lp->ptid) == GET_LWP (last_ptid))
|
||||||
{
|
{
|
||||||
struct thread_info *tp = find_thread_ptid (lp->ptid);
|
struct thread_info *tp = find_thread_ptid (lp->ptid);
|
||||||
if (tp->stop_signal != TARGET_SIGNAL_0
|
signo = tp->stop_signal;
|
||||||
&& signal_pass_state (tp->stop_signal))
|
|
||||||
*status = W_STOPCODE (target_signal_to_host (tp->stop_signal));
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
*status = lp->status;
|
|
||||||
|
*status = 0;
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
*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;
|
return 0;
|
||||||
|
@ -1893,6 +1897,133 @@ kill_lwp (int lwpid, int signo)
|
||||||
return kill (lwpid, 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
|
/* 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
|
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
|
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)
|
if (!stopping)
|
||||||
{
|
{
|
||||||
|
int signo;
|
||||||
|
|
||||||
new_lp->stopped = 0;
|
new_lp->stopped = 0;
|
||||||
new_lp->resumed = 1;
|
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)
|
if (debug_linux_nat)
|
||||||
fprintf_unfiltered (gdb_stdlog,
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
"LHEW: Got clone event from LWP %ld, resuming\n",
|
"LHEW: Got clone event from LWP %ld, resuming\n",
|
||||||
GET_LWP (lp->ptid));
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -2070,47 +2212,6 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
|
||||||
return 0;
|
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__,
|
internal_error (__FILE__, __LINE__,
|
||||||
_("unknown ptrace event %d"), event);
|
_("unknown ptrace event %d"), event);
|
||||||
}
|
}
|
||||||
|
@ -2176,6 +2277,18 @@ wait_lwp (struct lwp_info *lp)
|
||||||
|
|
||||||
gdb_assert (WIFSTOPPED (status));
|
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. */
|
/* Handle GNU/Linux's extended waitstatus for trace events. */
|
||||||
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
|
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
|
/* Only report a pending wait status if we pretend that this has
|
||||||
indeed been resumed. */
|
indeed been resumed. */
|
||||||
/* We check for lp->waitstatus in addition to lp->status, because we
|
if (!lp->resumed)
|
||||||
can have pending process exits recorded in lp->waitstatus, and
|
return 0;
|
||||||
W_EXITCODE(0,0) == 0. */
|
|
||||||
return ((lp->status != 0
|
if (lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
|
||||||
|| lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
|
{
|
||||||
&& lp->resumed);
|
/* 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. */
|
/* 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
|
delete or disable the breakpoint, but the LWP will have already
|
||||||
tripped on it. */
|
tripped on it. */
|
||||||
|
|
||||||
if (lp->status != 0
|
if (lp->waitstatus.kind == TARGET_WAITKIND_IGNORE
|
||||||
|
&& lp->status != 0
|
||||||
&& WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP
|
&& WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP
|
||||||
&& cancel_breakpoint (lp))
|
&& cancel_breakpoint (lp))
|
||||||
/* Throw away the SIGTRAP. */
|
/* Throw away the SIGTRAP. */
|
||||||
|
@ -2708,17 +2833,20 @@ linux_nat_filter_event (int lwpid, int status, int options)
|
||||||
add_thread (lp->ptid);
|
add_thread (lp->ptid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save the trap's siginfo in case we need it later. */
|
/* Handle GNU/Linux's syscall SIGTRAPs. */
|
||||||
if (WIFSTOPPED (status)
|
if (WIFSTOPPED (status) && WSTOPSIG (status) == SYSCALL_SIGTRAP)
|
||||||
&& (WSTOPSIG (status) == SIGTRAP || WSTOPSIG (status) == TRAP_IS_SYSCALL))
|
{
|
||||||
save_siginfo (lp);
|
/* 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.
|
/* Handle GNU/Linux's extended waitstatus for trace events. */
|
||||||
It is necessary to check if WSTOPSIG is signaling that
|
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
|
||||||
the inferior is entering/exiting a system call. */
|
|
||||||
if (WIFSTOPPED (status)
|
|
||||||
&& ((WSTOPSIG (status) == TRAP_IS_SYSCALL)
|
|
||||||
|| (WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)))
|
|
||||||
{
|
{
|
||||||
if (debug_linux_nat)
|
if (debug_linux_nat)
|
||||||
fprintf_unfiltered (gdb_stdlog,
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
@ -2728,6 +2856,10 @@ linux_nat_filter_event (int lwpid, int status, int options)
|
||||||
return NULL;
|
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. */
|
/* Check if the thread has exited. */
|
||||||
if ((WIFEXITED (status) || WIFSIGNALED (status))
|
if ((WIFEXITED (status) || WIFSIGNALED (status))
|
||||||
&& num_lwps (GET_PID (lp->ptid)) > 1)
|
&& num_lwps (GET_PID (lp->ptid)) > 1)
|
||||||
|
@ -2849,6 +2981,7 @@ linux_nat_filter_event (int lwpid, int status, int options)
|
||||||
|
|
||||||
/* An interesting event. */
|
/* An interesting event. */
|
||||||
gdb_assert (lp);
|
gdb_assert (lp);
|
||||||
|
lp->status = status;
|
||||||
return lp;
|
return lp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2908,13 +3041,10 @@ retry:
|
||||||
lp = iterate_over_lwps (ptid, status_callback, NULL);
|
lp = iterate_over_lwps (ptid, status_callback, NULL);
|
||||||
if (lp)
|
if (lp)
|
||||||
{
|
{
|
||||||
status = lp->status;
|
if (debug_linux_nat && lp->status)
|
||||||
lp->status = 0;
|
|
||||||
|
|
||||||
if (debug_linux_nat && status)
|
|
||||||
fprintf_unfiltered (gdb_stdlog,
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
"LLW: Using pending wait status %s for %s.\n",
|
"LLW: Using pending wait status %s for %s.\n",
|
||||||
status_to_str (status),
|
status_to_str (lp->status),
|
||||||
target_pid_to_str (lp->ptid));
|
target_pid_to_str (lp->ptid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2933,13 +3063,11 @@ retry:
|
||||||
/* We have a specific LWP to check. */
|
/* We have a specific LWP to check. */
|
||||||
lp = find_lwp_pid (ptid);
|
lp = find_lwp_pid (ptid);
|
||||||
gdb_assert (lp);
|
gdb_assert (lp);
|
||||||
status = lp->status;
|
|
||||||
lp->status = 0;
|
|
||||||
|
|
||||||
if (debug_linux_nat && status)
|
if (debug_linux_nat && lp->status)
|
||||||
fprintf_unfiltered (gdb_stdlog,
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
"LLW: Using pending wait status %s for %s.\n",
|
"LLW: Using pending wait status %s for %s.\n",
|
||||||
status_to_str (status),
|
status_to_str (lp->status),
|
||||||
target_pid_to_str (lp->ptid));
|
target_pid_to_str (lp->ptid));
|
||||||
|
|
||||||
/* If we have to wait, take into account whether PID is a cloned
|
/* 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
|
because we can have pending process exits recorded in
|
||||||
lp->status and W_EXITCODE(0,0) == 0. We should probably have
|
lp->status and W_EXITCODE(0,0) == 0. We should probably have
|
||||||
an additional lp->status_p flag. */
|
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;
|
lp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2980,8 +3108,26 @@ retry:
|
||||||
lp->stopped = 0;
|
lp->stopped = 0;
|
||||||
gdb_assert (lp->resumed);
|
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);
|
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 ())
|
if (!target_can_async_p ())
|
||||||
|
@ -3013,12 +3159,6 @@ retry:
|
||||||
|
|
||||||
lp = linux_nat_filter_event (lwpid, status, options);
|
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
|
if (lp
|
||||||
&& ptid_is_pid (ptid)
|
&& ptid_is_pid (ptid)
|
||||||
&& ptid_get_pid (lp->ptid) != ptid_get_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",
|
fprintf (stderr, "LWP %ld got an event %06x, leaving pending.\n",
|
||||||
ptid_get_lwp (lp->ptid), status);
|
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);
|
stop_callback (lp, NULL);
|
||||||
|
|
||||||
/* Resume in order to collect the sigstop. */
|
/* Resume in order to collect the sigstop. */
|
||||||
|
@ -3059,7 +3197,6 @@ retry:
|
||||||
about the exit code/signal, leave the status
|
about the exit code/signal, leave the status
|
||||||
pending for the next time we're able to report
|
pending for the next time we're able to report
|
||||||
it. */
|
it. */
|
||||||
lp->status = status;
|
|
||||||
|
|
||||||
/* Prevent trying to stop this thread again. We'll
|
/* Prevent trying to stop this thread again. We'll
|
||||||
never try to resume it because it has a pending
|
never try to resume it because it has a pending
|
||||||
|
@ -3072,7 +3209,7 @@ retry:
|
||||||
|
|
||||||
/* Store the pending event in the waitstatus as
|
/* Store the pending event in the waitstatus as
|
||||||
well, because W_EXITCODE(0,0) == 0. */
|
well, because W_EXITCODE(0,0) == 0. */
|
||||||
store_waitstatus (&lp->waitstatus, status);
|
store_waitstatus (&lp->waitstatus, lp->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Keep looking. */
|
/* Keep looking. */
|
||||||
|
@ -3128,6 +3265,9 @@ retry:
|
||||||
|
|
||||||
gdb_assert (lp);
|
gdb_assert (lp);
|
||||||
|
|
||||||
|
status = lp->status;
|
||||||
|
lp->status = 0;
|
||||||
|
|
||||||
/* Don't report signals that GDB isn't interested in, such as
|
/* Don't report signals that GDB isn't interested in, such as
|
||||||
signals that are neither printed nor stopped upon. Stopping all
|
signals that are neither printed nor stopped upon. Stopping all
|
||||||
threads can be a bit time-consuming so if we want decent
|
threads can be a bit time-consuming so if we want decent
|
||||||
|
|
Loading…
Reference in New Issue