gdb/
2012-06-28 Jan Kratochvil <jan.kratochvil@redhat.com> Pedro Alves <palves@redhat.com> * gdbthread.h (ALL_THREADS): New macro. (thread_list): Declare. * infrun.c (handle_inferior_event) <spurious signal>: Don't keep going, but instead fall through to the stepping handling. * linux-nat.c (resume_lwp): New parameter 'signo'. Resume with the passed in signal. Adjust debug output. (resume_callback): Rename to ... (linux_nat_resume_callback): ... this. Pass the thread's last stop signal, if in "pass" state. (linux_nat_resume): Adjust to rename. (stop_wait_callback): New assertion. Don't respawn signals; instead let the LWP remain with SIGNALLED set. (linux_nat_wait_1): Remove flushing of pending SIGSTOPs. * remote.c (append_pending_thread_resumptions): New. (remote_vcont_resume): Call it. * target.h (target_resume): Extend comment. gdb/testsuite/ 2012-06-28 Jan Kratochvil <jan.kratochvil@redhat.com> Pedro Alves <palves@redhat.com> * gdb.threads/siginfo-threads.exp: New file. * gdb.threads/siginfo-threads.c: New file. * gdb.threads/sigstep-threads.exp: New file. * gdb.threads/sigstep-threads.c: New file.
This commit is contained in:
parent
dc60a23811
commit
e5ef252af0
12 changed files with 1002 additions and 350 deletions
|
@ -1,3 +1,23 @@
|
|||
2012-06-28 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||
Pedro Alves <palves@redhat.com>
|
||||
|
||||
* gdbthread.h (ALL_THREADS): New macro.
|
||||
(thread_list): Declare.
|
||||
* infrun.c (handle_inferior_event) <spurious signal>: Don't keep
|
||||
going, but instead fall through to the stepping handling.
|
||||
* linux-nat.c (resume_lwp): New parameter 'signo'. Resume with
|
||||
the passed in signal. Adjust debug output.
|
||||
(resume_callback): Rename to ...
|
||||
(linux_nat_resume_callback): ... this. Pass the thread's last
|
||||
stop signal, if in "pass" state.
|
||||
(linux_nat_resume): Adjust to rename.
|
||||
(stop_wait_callback): New assertion. Don't respawn signals;
|
||||
instead let the LWP remain with SIGNALLED set.
|
||||
(linux_nat_wait_1): Remove flushing of pending SIGSTOPs.
|
||||
* remote.c (append_pending_thread_resumptions): New.
|
||||
(remote_vcont_resume): Call it.
|
||||
* target.h (target_resume): Extend comment.
|
||||
|
||||
2012-06-28 Iain Sandoe <iain@codesourcery.com>
|
||||
|
||||
* auxv.c (fprint_target_auxv): Handle extended cache data tags.
|
||||
|
|
|
@ -301,6 +301,11 @@ void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid);
|
|||
typedef int (*thread_callback_func) (struct thread_info *, void *);
|
||||
extern struct thread_info *iterate_over_threads (thread_callback_func, void *);
|
||||
|
||||
/* Traverse all threads. */
|
||||
|
||||
#define ALL_THREADS(T) \
|
||||
for (T = thread_list; T; T = T->next)
|
||||
|
||||
extern int thread_count (void);
|
||||
|
||||
/* Switch from one thread to another. */
|
||||
|
@ -391,4 +396,6 @@ extern struct thread_info* inferior_thread (void);
|
|||
|
||||
extern void update_thread_list (void);
|
||||
|
||||
extern struct thread_info *thread_list;
|
||||
|
||||
#endif /* GDBTHREAD_H */
|
||||
|
|
44
gdb/infrun.c
44
gdb/infrun.c
|
@ -4363,12 +4363,11 @@ process_event_stop_test:
|
|||
(leaving the inferior at the step-resume-breakpoint without
|
||||
actually executing it). Either way continue until the
|
||||
breakpoint is really hit. */
|
||||
keep_going (ecs);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Handle cases caused by hitting a breakpoint. */
|
||||
else
|
||||
{
|
||||
/* Handle cases caused by hitting a breakpoint. */
|
||||
|
||||
CORE_ADDR jmp_buf_pc;
|
||||
struct bpstat_what what;
|
||||
|
||||
|
@ -4380,8 +4379,8 @@ process_event_stop_test:
|
|||
}
|
||||
|
||||
/* If we hit an internal event that triggers symbol changes, the
|
||||
current frame will be invalidated within bpstat_what (e.g., if
|
||||
we hit an internal solib event). Re-fetch it. */
|
||||
current frame will be invalidated within bpstat_what (e.g.,
|
||||
if we hit an internal solib event). Re-fetch it. */
|
||||
frame = get_current_frame ();
|
||||
gdbarch = get_frame_arch (frame);
|
||||
|
||||
|
@ -4402,9 +4401,10 @@ process_event_stop_test:
|
|||
{
|
||||
struct value *arg_value;
|
||||
|
||||
/* If we set the longjmp breakpoint via a SystemTap probe,
|
||||
then use it to extract the arguments. The destination
|
||||
PC is the third argument to the probe. */
|
||||
/* If we set the longjmp breakpoint via a SystemTap
|
||||
probe, then use it to extract the arguments. The
|
||||
destination PC is the third argument to the
|
||||
probe. */
|
||||
arg_value = probe_safe_evaluate_at_pc (frame, 2);
|
||||
if (arg_value)
|
||||
jmp_buf_pc = value_as_address (arg_value);
|
||||
|
@ -4439,17 +4439,17 @@ process_event_stop_test:
|
|||
too far.
|
||||
|
||||
2. The initiating frame exists, and is the same as the
|
||||
current frame. We stop, because the exception or longjmp
|
||||
has been caught.
|
||||
current frame. We stop, because the exception or
|
||||
longjmp has been caught.
|
||||
|
||||
3. The initiating frame exists and is different from the
|
||||
current frame. This means the exception or longjmp has
|
||||
been caught beneath the initiating frame, so keep
|
||||
3. The initiating frame exists and is different from
|
||||
the current frame. This means the exception or longjmp
|
||||
has been caught beneath the initiating frame, so keep
|
||||
going.
|
||||
|
||||
4. longjmp breakpoint has been placed just to protect
|
||||
against stale dummy frames and user is not interested in
|
||||
stopping around longjmps. */
|
||||
against stale dummy frames and user is not interested
|
||||
in stopping around longjmps. */
|
||||
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
|
@ -4504,8 +4504,8 @@ process_event_stop_test:
|
|||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_SINGLE\n");
|
||||
ecs->event_thread->stepping_over_breakpoint = 1;
|
||||
/* Still need to check other stuff, at least the case
|
||||
where we are stepping and step out of the right range. */
|
||||
/* Still need to check other stuff, at least the case where
|
||||
we are stepping and step out of the right range. */
|
||||
break;
|
||||
|
||||
case BPSTAT_WHAT_STEP_RESUME:
|
||||
|
@ -4519,10 +4519,10 @@ process_event_stop_test:
|
|||
struct thread_info *tp = ecs->event_thread;
|
||||
|
||||
/* We are finishing a function in reverse, and just hit
|
||||
the step-resume breakpoint at the start address of the
|
||||
function, and we're almost there -- just need to back
|
||||
up by one more single-step, which should take us back
|
||||
to the function call. */
|
||||
the step-resume breakpoint at the start address of
|
||||
the function, and we're almost there -- just need to
|
||||
back up by one more single-step, which should take us
|
||||
back to the function call. */
|
||||
tp->control.step_range_start = tp->control.step_range_end = 1;
|
||||
keep_going (ecs);
|
||||
return;
|
||||
|
|
186
gdb/linux-nat.c
186
gdb/linux-nat.c
|
@ -1901,7 +1901,7 @@ linux_nat_detach (struct target_ops *ops, char *args, int from_tty)
|
|||
/* Resume LP. */
|
||||
|
||||
static void
|
||||
resume_lwp (struct lwp_info *lp, int step)
|
||||
resume_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
|
||||
{
|
||||
if (lp->stopped)
|
||||
{
|
||||
|
@ -1919,14 +1919,18 @@ resume_lwp (struct lwp_info *lp, int step)
|
|||
{
|
||||
if (debug_linux_nat)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"RC: PTRACE_CONT %s, 0, 0 (resuming sibling)\n",
|
||||
target_pid_to_str (lp->ptid));
|
||||
"RC: Resuming sibling %s, %s, %s\n",
|
||||
target_pid_to_str (lp->ptid),
|
||||
(signo != GDB_SIGNAL_0
|
||||
? strsignal (gdb_signal_to_host (signo))
|
||||
: "0"),
|
||||
step ? "step" : "resume");
|
||||
|
||||
if (linux_nat_prepare_to_resume != NULL)
|
||||
linux_nat_prepare_to_resume (lp);
|
||||
linux_ops->to_resume (linux_ops,
|
||||
pid_to_ptid (GET_LWP (lp->ptid)),
|
||||
step, GDB_SIGNAL_0);
|
||||
step, signo);
|
||||
lp->stopped = 0;
|
||||
lp->step = step;
|
||||
memset (&lp->siginfo, 0, sizeof (lp->siginfo));
|
||||
|
@ -1949,10 +1953,27 @@ resume_lwp (struct lwp_info *lp, int step)
|
|||
}
|
||||
}
|
||||
|
||||
/* Resume LWP, with the last stop signal, if it is in pass state. */
|
||||
|
||||
static int
|
||||
resume_callback (struct lwp_info *lp, void *data)
|
||||
linux_nat_resume_callback (struct lwp_info *lp, void *data)
|
||||
{
|
||||
resume_lwp (lp, 0);
|
||||
enum gdb_signal signo = GDB_SIGNAL_0;
|
||||
|
||||
if (lp->stopped)
|
||||
{
|
||||
struct thread_info *thread;
|
||||
|
||||
thread = find_thread_ptid (lp->ptid);
|
||||
if (thread != NULL)
|
||||
{
|
||||
if (signal_pass_state (thread->suspend.stop_signal))
|
||||
signo = thread->suspend.stop_signal;
|
||||
thread->suspend.stop_signal = GDB_SIGNAL_0;
|
||||
}
|
||||
}
|
||||
|
||||
resume_lwp (lp, 0, signo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2059,11 +2080,11 @@ linux_nat_resume (struct target_ops *ops,
|
|||
}
|
||||
|
||||
/* Mark LWP as not stopped to prevent it from being continued by
|
||||
resume_callback. */
|
||||
linux_nat_resume_callback. */
|
||||
lp->stopped = 0;
|
||||
|
||||
if (resume_many)
|
||||
iterate_over_lwps (ptid, resume_callback, NULL);
|
||||
iterate_over_lwps (ptid, linux_nat_resume_callback, NULL);
|
||||
|
||||
/* Convert to something the lower layer understands. */
|
||||
ptid = pid_to_ptid (GET_LWP (lp->ptid));
|
||||
|
@ -2881,110 +2902,39 @@ stop_wait_callback (struct lwp_info *lp, void *data)
|
|||
|
||||
if (WSTOPSIG (status) != SIGSTOP)
|
||||
{
|
||||
if (linux_nat_status_is_event (status))
|
||||
{
|
||||
/* If a LWP other than the LWP that we're reporting an
|
||||
event for has hit a GDB breakpoint (as opposed to
|
||||
some random trap signal), then just arrange for it to
|
||||
hit it again later. We don't keep the SIGTRAP status
|
||||
and don't forward the SIGTRAP signal to the LWP. We
|
||||
will handle the current event, eventually we will
|
||||
resume all LWPs, and this one will get its breakpoint
|
||||
trap again.
|
||||
|
||||
If we do not do this, then we run the risk that the
|
||||
user will delete or disable the breakpoint, but the
|
||||
thread will have already tripped on it. */
|
||||
/* The thread was stopped with a signal other than SIGSTOP. */
|
||||
|
||||
/* Save the trap's siginfo in case we need it later. */
|
||||
save_siginfo (lp);
|
||||
|
||||
save_sigtrap (lp);
|
||||
|
||||
/* Now resume this LWP and get the SIGSTOP event. */
|
||||
errno = 0;
|
||||
ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
|
||||
if (debug_linux_nat)
|
||||
{
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"PTRACE_CONT %s, 0, 0 (%s)\n",
|
||||
target_pid_to_str (lp->ptid),
|
||||
errno ? safe_strerror (errno) : "OK");
|
||||
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"SWC: Candidate SIGTRAP event in %s\n",
|
||||
target_pid_to_str (lp->ptid));
|
||||
}
|
||||
/* Hold this event/waitstatus while we check to see if
|
||||
there are any more (we still want to get that SIGSTOP). */
|
||||
stop_wait_callback (lp, NULL);
|
||||
|
||||
/* Hold the SIGTRAP for handling by linux_nat_wait. If
|
||||
there's another event, throw it back into the
|
||||
queue. */
|
||||
if (lp->status)
|
||||
{
|
||||
if (debug_linux_nat)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"SWC: kill %s, %s\n",
|
||||
target_pid_to_str (lp->ptid),
|
||||
status_to_str ((int) status));
|
||||
kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status));
|
||||
}
|
||||
|
||||
/* Save the sigtrap event. */
|
||||
lp->status = status;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The thread was stopped with a signal other than
|
||||
SIGSTOP, and didn't accidentally trip a breakpoint. */
|
||||
|
||||
if (debug_linux_nat)
|
||||
{
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"SWC: Pending event %s in %s\n",
|
||||
status_to_str ((int) status),
|
||||
target_pid_to_str (lp->ptid));
|
||||
}
|
||||
/* Now resume this LWP and get the SIGSTOP event. */
|
||||
errno = 0;
|
||||
ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
|
||||
if (debug_linux_nat)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"SWC: PTRACE_CONT %s, 0, 0 (%s)\n",
|
||||
target_pid_to_str (lp->ptid),
|
||||
errno ? safe_strerror (errno) : "OK");
|
||||
|
||||
/* Hold this event/waitstatus while we check to see if
|
||||
there are any more (we still want to get that SIGSTOP). */
|
||||
stop_wait_callback (lp, NULL);
|
||||
|
||||
/* If the lp->status field is still empty, use it to
|
||||
hold this event. If not, then this event must be
|
||||
returned to the event queue of the LWP. */
|
||||
if (lp->status)
|
||||
{
|
||||
if (debug_linux_nat)
|
||||
{
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"SWC: kill %s, %s\n",
|
||||
target_pid_to_str (lp->ptid),
|
||||
status_to_str ((int) status));
|
||||
}
|
||||
kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (status));
|
||||
}
|
||||
else
|
||||
/* Save the sigtrap event. */
|
||||
lp->status = status;
|
||||
return 0;
|
||||
}
|
||||
gdb_assert (!lp->stopped);
|
||||
gdb_assert (lp->signalled);
|
||||
lp->stopped = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We caught the SIGSTOP that we intended to catch, so
|
||||
there's no SIGSTOP pending. */
|
||||
|
||||
if (debug_linux_nat)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"SWC: Delayed SIGSTOP caught for %s.\n",
|
||||
target_pid_to_str (lp->ptid));
|
||||
|
||||
lp->stopped = 1;
|
||||
|
||||
/* Reset SIGNALLED only after the stop_wait_callback call
|
||||
above as it does gdb_assert on SIGNALLED. */
|
||||
lp->signalled = 0;
|
||||
}
|
||||
}
|
||||
|
@ -3238,7 +3188,7 @@ stop_and_resume_callback (struct lwp_info *lp, void *data)
|
|||
fprintf_unfiltered (gdb_stdlog,
|
||||
"SARC: re-resuming LWP %ld\n",
|
||||
GET_LWP (lp->ptid));
|
||||
resume_lwp (lp, lp->step);
|
||||
resume_lwp (lp, lp->step, GDB_SIGNAL_0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3612,54 +3562,6 @@ retry:
|
|||
lp = NULL;
|
||||
}
|
||||
|
||||
if (lp && lp->signalled && lp->last_resume_kind != resume_stop)
|
||||
{
|
||||
/* A pending SIGSTOP may interfere with the normal stream of
|
||||
events. In a typical case where interference is a problem,
|
||||
we have a SIGSTOP signal pending for LWP A while
|
||||
single-stepping it, encounter an event in LWP B, and take the
|
||||
pending SIGSTOP while trying to stop LWP A. After processing
|
||||
the event in LWP B, LWP A is continued, and we'll never see
|
||||
the SIGTRAP associated with the last time we were
|
||||
single-stepping LWP A. */
|
||||
|
||||
/* Resume the thread. It should halt immediately returning the
|
||||
pending SIGSTOP. */
|
||||
registers_changed ();
|
||||
if (linux_nat_prepare_to_resume != NULL)
|
||||
linux_nat_prepare_to_resume (lp);
|
||||
linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
|
||||
lp->step, GDB_SIGNAL_0);
|
||||
if (debug_linux_nat)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"LLW: %s %s, 0, 0 (expect SIGSTOP)\n",
|
||||
lp->step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT",
|
||||
target_pid_to_str (lp->ptid));
|
||||
lp->stopped = 0;
|
||||
gdb_assert (lp->resumed);
|
||||
|
||||
/* 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 ())
|
||||
{
|
||||
/* Causes SIGINT to be passed on to the attached process. */
|
||||
|
|
26
gdb/remote.c
26
gdb/remote.c
|
@ -4644,6 +4644,28 @@ append_resumption (char *p, char *endp,
|
|||
return p;
|
||||
}
|
||||
|
||||
/* Append a vCont continue-with-signal action for threads that have a
|
||||
non-zero stop signal. */
|
||||
|
||||
static char *
|
||||
append_pending_thread_resumptions (char *p, char *endp, ptid_t ptid)
|
||||
{
|
||||
struct thread_info *thread;
|
||||
|
||||
ALL_THREADS (thread)
|
||||
if (ptid_match (thread->ptid, ptid)
|
||||
&& !ptid_equal (inferior_ptid, thread->ptid)
|
||||
&& thread->suspend.stop_signal != GDB_SIGNAL_0
|
||||
&& signal_pass_state (thread->suspend.stop_signal))
|
||||
{
|
||||
p = append_resumption (p, endp, thread->ptid,
|
||||
0, thread->suspend.stop_signal);
|
||||
thread->suspend.stop_signal = GDB_SIGNAL_0;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Resume the remote inferior by using a "vCont" packet. The thread
|
||||
to be resumed is PTID; STEP and SIGGNAL indicate whether the
|
||||
resumed thread should be single-stepped and/or signalled. If PTID
|
||||
|
@ -4696,6 +4718,10 @@ remote_vcont_resume (ptid_t ptid, int step, enum gdb_signal siggnal)
|
|||
p = append_resumption (p, endp, inferior_ptid, step, siggnal);
|
||||
}
|
||||
|
||||
/* Also pass down any pending signaled resumption for other
|
||||
threads not the current. */
|
||||
p = append_pending_thread_resumptions (p, endp, ptid);
|
||||
|
||||
/* And continue others without a signal. */
|
||||
append_resumption (p, endp, ptid, /*step=*/ 0, GDB_SIGNAL_0);
|
||||
}
|
||||
|
|
14
gdb/target.h
14
gdb/target.h
|
@ -920,10 +920,16 @@ extern void target_detach (char *, int);
|
|||
|
||||
extern void target_disconnect (char *, int);
|
||||
|
||||
/* Resume execution of the target process PTID. STEP says whether to
|
||||
single-step or to run free; SIGGNAL is the signal to be given to
|
||||
the target, or GDB_SIGNAL_0 for no signal. The caller may not
|
||||
pass GDB_SIGNAL_DEFAULT. */
|
||||
/* Resume execution of the target process PTID (or a group of
|
||||
threads). STEP says whether to single-step or to run free; SIGGNAL
|
||||
is the signal to be given to the target, or GDB_SIGNAL_0 for no
|
||||
signal. The caller may not pass GDB_SIGNAL_DEFAULT. A specific
|
||||
PTID means `step/resume only this process id'. A wildcard PTID
|
||||
(all threads, or all threads of process) means `step/resume
|
||||
INFERIOR_PTID, and let other threads (for which the wildcard PTID
|
||||
matches) resume with their 'thread->suspend.stop_signal' signal
|
||||
(usually GDB_SIGNAL_0) if it is in "pass" state, or with no signal
|
||||
if in "no pass" state. */
|
||||
|
||||
extern void target_resume (ptid_t ptid, int step, enum gdb_signal signal);
|
||||
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
2012-06-28 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||
Pedro Alves <palves@redhat.com>
|
||||
|
||||
* gdb.threads/siginfo-threads.exp: New file.
|
||||
* gdb.threads/siginfo-threads.c: New file.
|
||||
* gdb.threads/sigstep-threads.exp: New file.
|
||||
* gdb.threads/sigstep-threads.c: New file.
|
||||
|
||||
2012-06-28 Tom Tromey <tromey@redhat.com>
|
||||
|
||||
* gdb.go/package.exp: Partially revert earlier patch; use
|
||||
|
|
457
gdb/testsuite/gdb.threads/siginfo-threads.c
Normal file
457
gdb/testsuite/gdb.threads/siginfo-threads.c
Normal file
|
@ -0,0 +1,457 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2010-2012 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#define gettid() syscall (__NR_gettid)
|
||||
#define tgkill(tgid, tid, sig) syscall (__NR_tgkill, tgid, tid, sig)
|
||||
|
||||
/* Terminate always in the main task. It can lock up with SIGSTOPped
|
||||
GDB otherwise. */
|
||||
#define TIMEOUT (gettid () == getpid() ? 10 : 15)
|
||||
|
||||
static pid_t thread1_tid;
|
||||
static pthread_cond_t thread1_tid_cond
|
||||
= PTHREAD_COND_INITIALIZER;
|
||||
static pthread_mutex_t thread1_tid_mutex
|
||||
= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
|
||||
static int thread1_sigusr1_hit;
|
||||
static int thread1_sigusr2_hit;
|
||||
|
||||
static pid_t thread2_tid;
|
||||
static pthread_cond_t thread2_tid_cond
|
||||
= PTHREAD_COND_INITIALIZER;
|
||||
static pthread_mutex_t thread2_tid_mutex
|
||||
= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
|
||||
static int thread2_sigusr1_hit;
|
||||
static int thread2_sigusr2_hit;
|
||||
|
||||
static pthread_mutex_t terminate_mutex
|
||||
= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
|
||||
|
||||
/* Do not use alarm as it would create a ptrace event which would hang
|
||||
us up if we are being traced by GDB, which we stopped
|
||||
ourselves. */
|
||||
|
||||
static void
|
||||
timed_mutex_lock (pthread_mutex_t *mutex)
|
||||
{
|
||||
int i;
|
||||
struct timespec start, now;
|
||||
|
||||
i = clock_gettime (CLOCK_MONOTONIC, &start);
|
||||
assert (i == 0);
|
||||
|
||||
do
|
||||
{
|
||||
i = pthread_mutex_trylock (mutex);
|
||||
if (i == 0)
|
||||
return;
|
||||
assert (i == EBUSY);
|
||||
|
||||
i = clock_gettime (CLOCK_MONOTONIC, &now);
|
||||
assert (i == 0);
|
||||
assert (now.tv_sec >= start.tv_sec);
|
||||
}
|
||||
while (now.tv_sec - start.tv_sec < TIMEOUT);
|
||||
|
||||
fprintf (stderr, "Timed out waiting for internal lock!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void
|
||||
handler (int signo, siginfo_t *siginfo, void *exception)
|
||||
{
|
||||
int *varp;
|
||||
|
||||
assert (siginfo->si_signo == signo);
|
||||
assert (siginfo->si_code == SI_TKILL);
|
||||
assert (siginfo->si_pid == getpid ());
|
||||
|
||||
if (gettid () == thread1_tid)
|
||||
{
|
||||
if (signo == SIGUSR1)
|
||||
varp = &thread1_sigusr1_hit;
|
||||
else if (signo == SIGUSR2)
|
||||
varp = &thread1_sigusr2_hit;
|
||||
else
|
||||
assert (0);
|
||||
}
|
||||
else if (gettid () == thread2_tid)
|
||||
{
|
||||
if (signo == SIGUSR1)
|
||||
varp = &thread2_sigusr1_hit;
|
||||
else if (signo == SIGUSR2)
|
||||
varp = &thread2_sigusr2_hit;
|
||||
else
|
||||
assert (0);
|
||||
}
|
||||
else
|
||||
assert (0);
|
||||
|
||||
if (*varp)
|
||||
{
|
||||
fprintf (stderr, "Signal %d for TID %lu has been already hit!\n", signo,
|
||||
(unsigned long) gettid ());
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
*varp = 1;
|
||||
}
|
||||
|
||||
static void *
|
||||
thread1_func (void *unused)
|
||||
{
|
||||
int i;
|
||||
|
||||
timed_mutex_lock (&thread1_tid_mutex);
|
||||
|
||||
/* THREAD1_TID_MUTEX must be already locked to avoid a race. */
|
||||
thread1_tid = gettid ();
|
||||
|
||||
i = pthread_cond_signal (&thread1_tid_cond);
|
||||
assert (i == 0);
|
||||
i = pthread_mutex_unlock (&thread1_tid_mutex);
|
||||
assert (i == 0);
|
||||
|
||||
/* Be sure the "t (tracing stop)" test can proceed for both
|
||||
threads. */
|
||||
timed_mutex_lock (&terminate_mutex);
|
||||
i = pthread_mutex_unlock (&terminate_mutex);
|
||||
assert (i == 0);
|
||||
|
||||
if (!thread1_sigusr1_hit)
|
||||
{
|
||||
fprintf (stderr, "Thread 1 signal SIGUSR1 not hit!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (!thread1_sigusr2_hit)
|
||||
{
|
||||
fprintf (stderr, "Thread 1 signal SIGUSR2 not hit!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *
|
||||
thread2_func (void *unused)
|
||||
{
|
||||
int i;
|
||||
|
||||
timed_mutex_lock (&thread2_tid_mutex);
|
||||
|
||||
/* THREAD2_TID_MUTEX must be already locked to avoid a race. */
|
||||
thread2_tid = gettid ();
|
||||
|
||||
i = pthread_cond_signal (&thread2_tid_cond);
|
||||
assert (i == 0);
|
||||
i = pthread_mutex_unlock (&thread2_tid_mutex);
|
||||
assert (i == 0);
|
||||
|
||||
/* Be sure the "t (tracing stop)" test can proceed for both
|
||||
threads. */
|
||||
timed_mutex_lock (&terminate_mutex);
|
||||
i = pthread_mutex_unlock (&terminate_mutex);
|
||||
assert (i == 0);
|
||||
|
||||
if (!thread2_sigusr1_hit)
|
||||
{
|
||||
fprintf (stderr, "Thread 2 signal SIGUSR1 not hit!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (!thread2_sigusr2_hit)
|
||||
{
|
||||
fprintf (stderr, "Thread 2 signal SIGUSR2 not hit!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
proc_string (const char *filename, const char *line)
|
||||
{
|
||||
FILE *f;
|
||||
static char buf[LINE_MAX];
|
||||
size_t line_len = strlen (line);
|
||||
|
||||
f = fopen (filename, "r");
|
||||
if (f == NULL)
|
||||
{
|
||||
fprintf (stderr, "fopen (\"%s\") for \"%s\": %s\n", filename, line,
|
||||
strerror (errno));
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
while (errno = 0, fgets (buf, sizeof (buf), f))
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = strchr (buf, '\n');
|
||||
assert (s != NULL);
|
||||
*s = 0;
|
||||
|
||||
if (strncmp (buf, line, line_len) != 0)
|
||||
continue;
|
||||
|
||||
if (fclose (f))
|
||||
{
|
||||
fprintf (stderr, "fclose (\"%s\") for \"%s\": %s\n", filename, line,
|
||||
strerror (errno));
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return &buf[line_len];
|
||||
}
|
||||
if (errno != 0)
|
||||
{
|
||||
fprintf (stderr, "fgets (\"%s\": %s\n", filename, strerror (errno));
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
fprintf (stderr, "\"%s\": No line \"%s\" found.\n", filename, line);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
proc_ulong (const char *filename, const char *line)
|
||||
{
|
||||
const char *s = proc_string (filename, line);
|
||||
long retval;
|
||||
char *end;
|
||||
|
||||
errno = 0;
|
||||
retval = strtol (s, &end, 10);
|
||||
if (retval < 0 || retval >= LONG_MAX || (end && *end))
|
||||
{
|
||||
fprintf (stderr, "\"%s\":\"%s\": %ld, %s\n", filename, line, retval,
|
||||
strerror (errno));
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
state_wait (pid_t process, const char *wanted)
|
||||
{
|
||||
char *filename;
|
||||
int i;
|
||||
struct timespec start, now;
|
||||
const char *state;
|
||||
|
||||
i = asprintf (&filename, "/proc/%lu/status", (unsigned long) process);
|
||||
assert (i > 0);
|
||||
|
||||
i = clock_gettime (CLOCK_MONOTONIC, &start);
|
||||
assert (i == 0);
|
||||
|
||||
do
|
||||
{
|
||||
state = proc_string (filename, "State:\t");
|
||||
|
||||
/* torvalds/linux-2.6.git 464763cf1c6df632dccc8f2f4c7e50163154a2c0
|
||||
has changed "T (tracing stop)" to "t (tracing stop)". Make the GDB
|
||||
testcase backward compatible with older Linux kernels. */
|
||||
if (strcmp (state, "T (tracing stop)") == 0)
|
||||
state = "t (tracing stop)";
|
||||
|
||||
if (strcmp (state, wanted) == 0)
|
||||
{
|
||||
free (filename);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sched_yield ())
|
||||
{
|
||||
perror ("sched_yield()");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
i = clock_gettime (CLOCK_MONOTONIC, &now);
|
||||
assert (i == 0);
|
||||
assert (now.tv_sec >= start.tv_sec);
|
||||
}
|
||||
while (now.tv_sec - start.tv_sec < TIMEOUT);
|
||||
|
||||
fprintf (stderr, "Timed out waiting for PID %lu \"%s\" (now it is \"%s\")!\n",
|
||||
(unsigned long) process, wanted, state);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static volatile pid_t tracer = 0;
|
||||
static pthread_t thread1, thread2;
|
||||
|
||||
static void
|
||||
cleanup (void)
|
||||
{
|
||||
printf ("Resuming GDB PID %lu.\n", (unsigned long) tracer);
|
||||
|
||||
if (tracer)
|
||||
{
|
||||
int i;
|
||||
int tracer_save = tracer;
|
||||
|
||||
tracer = 0;
|
||||
|
||||
i = kill (tracer_save, SIGCONT);
|
||||
assert (i == 0);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
int standalone = 0;
|
||||
struct sigaction act;
|
||||
|
||||
if (argc == 2 && strcmp (argv[1], "-s") == 0)
|
||||
standalone = 1;
|
||||
else
|
||||
assert (argc == 1);
|
||||
|
||||
setbuf (stdout, NULL);
|
||||
|
||||
timed_mutex_lock (&thread1_tid_mutex);
|
||||
timed_mutex_lock (&thread2_tid_mutex);
|
||||
|
||||
timed_mutex_lock (&terminate_mutex);
|
||||
|
||||
errno = 0;
|
||||
memset (&act, 0, sizeof (act));
|
||||
act.sa_sigaction = handler;
|
||||
act.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
i = sigemptyset (&act.sa_mask);
|
||||
assert_perror (errno);
|
||||
assert (i == 0);
|
||||
i = sigaction (SIGUSR1, &act, NULL);
|
||||
assert_perror (errno);
|
||||
assert (i == 0);
|
||||
i = sigaction (SIGUSR2, &act, NULL);
|
||||
assert_perror (errno);
|
||||
assert (i == 0);
|
||||
|
||||
i = pthread_create (&thread1, NULL, thread1_func, NULL);
|
||||
assert (i == 0);
|
||||
|
||||
i = pthread_create (&thread2, NULL, thread2_func, NULL);
|
||||
assert (i == 0);
|
||||
|
||||
if (!standalone)
|
||||
{
|
||||
tracer = proc_ulong ("/proc/self/status", "TracerPid:\t");
|
||||
if (tracer == 0)
|
||||
{
|
||||
fprintf (stderr, "The testcase must be run by GDB!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
if (tracer != getppid ())
|
||||
{
|
||||
fprintf (stderr, "The testcase parent must be our GDB tracer!\n");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* SIGCONT our debugger in the case of our crash as we would deadlock
|
||||
otherwise. */
|
||||
|
||||
atexit (cleanup);
|
||||
|
||||
printf ("Stopping GDB PID %lu.\n", (unsigned long) tracer);
|
||||
|
||||
if (tracer)
|
||||
{
|
||||
i = kill (tracer, SIGSTOP);
|
||||
assert (i == 0);
|
||||
state_wait (tracer, "T (stopped)");
|
||||
}
|
||||
|
||||
/* Threads are now waiting at timed_mutex_lock (thread1_tid_mutex)
|
||||
and so they could not trigger the signals before GDB is unstopped
|
||||
later. Threads get resumed by the pthread_cond_wait below. Use
|
||||
`while' loops for protection against spurious pthread_cond_wait
|
||||
wakeups. */
|
||||
|
||||
printf ("Waiting till the threads initialize their TIDs.\n");
|
||||
|
||||
while (thread1_tid == 0)
|
||||
{
|
||||
i = pthread_cond_wait (&thread1_tid_cond, &thread1_tid_mutex);
|
||||
assert (i == 0);
|
||||
}
|
||||
|
||||
while (thread2_tid == 0)
|
||||
{
|
||||
i = pthread_cond_wait (&thread2_tid_cond, &thread2_tid_mutex);
|
||||
assert (i == 0);
|
||||
}
|
||||
|
||||
printf ("Thread 1 TID = %lu, thread 2 TID = %lu, PID = %lu.\n",
|
||||
(unsigned long) thread1_tid, (unsigned long) thread2_tid,
|
||||
(unsigned long) getpid ());
|
||||
|
||||
errno = 0;
|
||||
i = tgkill (getpid (), thread1_tid, SIGUSR1);
|
||||
assert_perror (errno);
|
||||
assert (i == 0);
|
||||
i = tgkill (getpid (), thread1_tid, SIGUSR2);
|
||||
assert_perror (errno);
|
||||
assert (i == 0);
|
||||
i = tgkill (getpid (), thread2_tid, SIGUSR1);
|
||||
assert_perror (errno);
|
||||
assert (i == 0);
|
||||
i = tgkill (getpid (), thread2_tid, SIGUSR2);
|
||||
assert_perror (errno);
|
||||
assert (i == 0);
|
||||
|
||||
printf ("Waiting till the threads are trapped by the signals.\n");
|
||||
|
||||
if (tracer)
|
||||
{
|
||||
/* s390x-unknown-linux-gnu will fail with "R (running)". */
|
||||
|
||||
state_wait (thread1_tid, "t (tracing stop)");
|
||||
|
||||
state_wait (thread2_tid, "t (tracing stop)");
|
||||
}
|
||||
|
||||
cleanup ();
|
||||
|
||||
printf ("Joining the threads.\n");
|
||||
|
||||
i = pthread_mutex_unlock (&terminate_mutex);
|
||||
assert (i == 0);
|
||||
|
||||
i = pthread_join (thread1, NULL);
|
||||
assert (i == 0);
|
||||
|
||||
i = pthread_join (thread2, NULL);
|
||||
assert (i == 0);
|
||||
|
||||
printf ("Exiting.\n"); /* break-at-exit */
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
99
gdb/testsuite/gdb.threads/siginfo-threads.exp
Normal file
99
gdb/testsuite/gdb.threads/siginfo-threads.exp
Normal file
|
@ -0,0 +1,99 @@
|
|||
# Copyright 2010-2012 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
standard_testfile
|
||||
set executable ${testfile}
|
||||
|
||||
if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" \
|
||||
${binfile} \
|
||||
executable \
|
||||
[list debug additional_flags=-lrt]] != "" } {
|
||||
return -1
|
||||
}
|
||||
|
||||
clean_restart $testfile
|
||||
|
||||
if ![runto_main] {
|
||||
return -1
|
||||
}
|
||||
|
||||
# `nostop noprint pass' could in some cases report false PASS due to
|
||||
# the (preempt 'handle') code path in linux-nat.c.
|
||||
|
||||
gdb_test "handle SIGUSR1 stop print pass" \
|
||||
"Signal\[ \t\]+Stop\[ \t\]+Print\[ \t\]+Pass to program\[ \t\]+Description\r\nSIGUSR1\[ \t\]+Yes\[ \t\]+Yes\[ \t\]+Yes\[ \t\].*"
|
||||
gdb_test "handle SIGUSR2 stop print pass" \
|
||||
"Signal\[ \t\]+Stop\[ \t\]+Print\[ \t\]+Pass to program\[ \t\]+Description\r\nSIGUSR2\[ \t\]+Yes\[ \t\]+Yes\[ \t\]+Yes\[ \t\].*"
|
||||
|
||||
gdb_breakpoint [gdb_get_line_number "break-at-exit"]
|
||||
|
||||
set test "get pid"
|
||||
gdb_test_multiple "p getpid ()" $test {
|
||||
-re " = (\[0-9\]+)\r\n$gdb_prompt $" {
|
||||
set pid $expect_out(1,string)
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
|
||||
for {set sigcount 0} {$sigcount < 4} {incr sigcount} {
|
||||
set test "catch signal $sigcount"
|
||||
set sigusr ""
|
||||
gdb_test_multiple "continue" $test {
|
||||
-re "Program received signal SIGUSR(\[12\]), User defined signal \[12\]\\.\r\n.*\r\n$gdb_prompt $" {
|
||||
set sigusr $expect_out(1,string)
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
if {$sigusr == ""} {
|
||||
return -1
|
||||
}
|
||||
|
||||
set test "signal $sigcount si_signo"
|
||||
if {$sigusr == 1} {
|
||||
set signo 10
|
||||
} else {
|
||||
set signo 12
|
||||
}
|
||||
gdb_test_multiple {p $_siginfo.si_signo} $test {
|
||||
-re " = $signo\r\n$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
-re "Attempt to extract a component of a value that is not a structure\\.\r\n$gdb_prompt $" {
|
||||
unsupported $test
|
||||
}
|
||||
}
|
||||
|
||||
set test "signal $sigcount si_code is SI_TKILL"
|
||||
gdb_test_multiple {p $_siginfo.si_code} $test {
|
||||
-re " = -6\r\n$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
-re "Attempt to extract a component of a value that is not a structure\\.\r\n$gdb_prompt $" {
|
||||
unsupported $test
|
||||
}
|
||||
}
|
||||
|
||||
set test "signal $sigcount si_pid"
|
||||
gdb_test_multiple {p $_siginfo._sifields._kill.si_pid} $test {
|
||||
-re " = $pid\r\n$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
-re "Attempt to extract a component of a value that is not a structure\\.\r\n$gdb_prompt $" {
|
||||
unsupported $test
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gdb_continue_to_breakpoint break-at-exit ".*break-at-exit.*"
|
54
gdb/testsuite/gdb.threads/sigstep-threads.c
Normal file
54
gdb/testsuite/gdb.threads/sigstep-threads.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2010-2012 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <asm/unistd.h>
|
||||
#include <unistd.h>
|
||||
#define tgkill(tgid, tid, sig) syscall (__NR_tgkill, (tgid), (tid), (sig))
|
||||
#define gettid() syscall (__NR_gettid)
|
||||
|
||||
static volatile int var;
|
||||
|
||||
static void
|
||||
handler (int signo) /* step-0 */
|
||||
{ /* step-0 */
|
||||
var++; /* step-1 */
|
||||
tgkill (getpid (), gettid (), SIGUSR1); /* step-2 */
|
||||
}
|
||||
|
||||
static void *
|
||||
start (void *arg)
|
||||
{
|
||||
tgkill (getpid (), gettid (), SIGUSR1);
|
||||
assert (0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
pthread_t thread;
|
||||
|
||||
signal (SIGUSR1, handler);
|
||||
|
||||
pthread_create (&thread, NULL, start, NULL);
|
||||
start (NULL); /* main-start */
|
||||
return 0;
|
||||
}
|
73
gdb/testsuite/gdb.threads/sigstep-threads.exp
Normal file
73
gdb/testsuite/gdb.threads/sigstep-threads.exp
Normal file
|
@ -0,0 +1,73 @@
|
|||
# Copyright 2010-2012 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
standard_testfile
|
||||
set executable ${testfile}
|
||||
|
||||
if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
|
||||
untested ${testfile}.exp
|
||||
return -1
|
||||
}
|
||||
|
||||
clean_restart $executable
|
||||
|
||||
if ![runto_main] {
|
||||
return -1;
|
||||
}
|
||||
|
||||
# `noprint' would not test the full logic of GDB.
|
||||
gdb_test "handle SIGUSR1 nostop print pass" \
|
||||
"\r\nSIGUSR1\[ \t\]+No\[ \t\]+Yes\[ \t\]+Yes\[ \t\].*"
|
||||
|
||||
gdb_test_no_output "set scheduler-locking off"
|
||||
|
||||
gdb_breakpoint [gdb_get_line_number "step-1"]
|
||||
gdb_test_no_output {set $step1=$bpnum}
|
||||
gdb_continue_to_breakpoint "step-1" ".* step-1 .*"
|
||||
gdb_test_no_output {disable $step1}
|
||||
|
||||
# 1 as we are now stopped at the `step-1' label.
|
||||
set step_at 1
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
set test "step $i"
|
||||
# Presume this step failed - as in the case of a timeout.
|
||||
set failed 1
|
||||
gdb_test_multiple "step" $test {
|
||||
-re "\r\nProgram received signal SIGUSR1, User defined signal 1.\r\n" {
|
||||
exp_continue -continue_timer
|
||||
}
|
||||
-re "step-(\[012\]).*\r\n$gdb_prompt $" {
|
||||
set now $expect_out(1,string)
|
||||
if {$step_at == 2 && $now == 1} {
|
||||
set failed 0
|
||||
} elseif {$step_at == 1 && $now == 2} {
|
||||
set failed 0
|
||||
# Continue over the re-signalling back to the handle entry.
|
||||
gdb_test_no_output {enable $step1} ""
|
||||
gdb_test "continue" " step-1 .*" ""
|
||||
set now 1
|
||||
gdb_test_no_output {disable $step1} ""
|
||||
} else {
|
||||
fail $test
|
||||
}
|
||||
set step_at $now
|
||||
}
|
||||
}
|
||||
if $failed {
|
||||
return
|
||||
}
|
||||
}
|
||||
# We can never reliably say the racy problematic case has been tested.
|
||||
pass "step"
|
|
@ -54,7 +54,7 @@ void _initialize_thread (void);
|
|||
|
||||
/* Prototypes for local functions. */
|
||||
|
||||
static struct thread_info *thread_list = NULL;
|
||||
struct thread_info *thread_list = NULL;
|
||||
static int highest_thread_num;
|
||||
|
||||
static void thread_command (char *tidstr, int from_tty);
|
||||
|
|
Loading…
Add table
Reference in a new issue