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:
Pedro Alves 2012-06-28 16:34:04 +00:00
parent dc60a23811
commit e5ef252af0
12 changed files with 1002 additions and 350 deletions

View file

@ -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.

View file

@ -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 */

View file

@ -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;

View file

@ -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. */

View file

@ -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);
}

View file

@ -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);

View file

@ -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

View 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;
}

View 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.*"

View 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;
}

View 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"

View file

@ -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);