2008-05-01 Daniel Jacobowitz <dan@codesourcery.com>

Pedro Alves  <pedro@codesourcery.com>

	Based on work by Jan Kratochvil <jan.kratochvil@redhat.com> and Jeff
 	Johnston <jjohnstn@redhat.com>.

	* NEWS: Mention attach to stopped process fix.
	* infcmd.c (detach_command, disconnect_command): Discard the thread
	list.
	* infrun.c (handle_inferior_event): Do not ignore non-SIGSTOP while
	attaching.  Use signal_stop_state.
	(signal_stop_state): Check stop_soon.
	* linux-nat.c (kill_lwp): Declare earlier.
	(pid_is_stopped, linux_nat_post_attach_wait): New.
	(lin_lwp_attach_lwp): Use linux_nat_post_attach_wait.  Update
	comments.
	(linux_nat_attach): Use linux_nat_post_attach_wait.
	(detach_callback, linux_nat_detach): Improve handling for signalled
	processes.
	(linux_nat_pid_to_str): Always print out the LWP ID if it differs
	from the process ID.
	* Makefile.in (infcmd.o): Update.

2008-05-01  Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Daniel Jacobowitz  <dan@codesourcery.com>

	* gdb.threads/attach-into-signal.c, gdb.threads/attach-into-signal.exp,
	gdb.threads/attach-stopped.c, gdb.threads/attach-stopped.exp,
	gdb.threads/attachstop-mt.c, gdb.threads/attachstop-mt.exp: New.
This commit is contained in:
Daniel Jacobowitz 2008-05-01 18:50:14 +00:00
parent ca38c58efa
commit a0ef42744d
11 changed files with 985 additions and 87 deletions

View File

@ -2316,7 +2316,7 @@ infcmd.o: infcmd.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \
$(objfiles_h) $(completer_h) $(ui_out_h) $(event_top_h) \
$(parser_defs_h) $(regcache_h) $(reggroups_h) $(block_h) \
$(solib_h) $(gdb_assert_h) $(observer_h) $(target_descriptions_h) \
$(user_regs_h) $(exceptions_h) $(cli_decode_h)
$(user_regs_h) $(exceptions_h) $(cli_decode_h) $(gdbthread_h)
inf-loop.o: inf-loop.c $(defs_h) $(inferior_h) $(target_h) $(event_loop_h) \
$(event_top_h) $(inf_loop_h) $(remote_h) $(exceptions_h) \
$(language_h)

View File

@ -4,11 +4,11 @@
*** Changes since GDB 6.8
* GDB now supports multiple function calling conventions according to the
DWARF-2 DW_AT_calling_convention function attribute.
DWARF-2 DW_AT_calling_convention function attribute.
* The SH target utilizes the aforementioned change to distinguish between gcc
and Renesas calling convention. It also adds the new CLI commands
`set/show sh calling-convention'.
and Renesas calling convention. It also adds the new CLI commands
`set/show sh calling-convention'.
* GDB can now read compressed debug sections, as produced by GNU gold
with the --compress-debug-sections=zlib flag.
@ -21,6 +21,10 @@ which will be allocated using malloc later in program execution.
* The qXfer:libraries:read remote procotol packet now allows passing a
list of section offsets.
* On GNU/Linux, GDB can now attach to stopped processes. Several race
conditions handling signals delivered during attach or thread creation
have also been fixed.
* New features in the GDB remote stub, gdbserver
- The "--wrapper" command-line argument tells gdbserver to use a

View File

@ -50,6 +50,7 @@
#include "user-regs.h"
#include "exceptions.h"
#include "cli/cli-decode.h"
#include "gdbthread.h"
/* Functions exported for general use, in inferior.h: */
@ -2050,6 +2051,7 @@ detach_command (char *args, int from_tty)
dont_repeat (); /* Not for the faint of heart. */
target_detach (args, from_tty);
no_shared_libraries (NULL, from_tty);
init_thread_list ();
if (deprecated_detach_hook)
deprecated_detach_hook ();
}
@ -2068,6 +2070,7 @@ disconnect_command (char *args, int from_tty)
dont_repeat (); /* Not for the faint of heart */
target_disconnect (args, from_tty);
no_shared_libraries (NULL, from_tty);
init_thread_list ();
if (deprecated_detach_hook)
deprecated_detach_hook ();
}

View File

@ -1946,13 +1946,15 @@ handle_inferior_event (struct execution_control_state *ecs)
/* This originates from attach_command(). We need to overwrite
the stop_signal here, because some kernels don't ignore a
SIGSTOP in a subsequent ptrace(PTRACE_SONT,SOGSTOP) call.
See more comments in inferior.h. */
if (stop_soon == STOP_QUIETLY_NO_SIGSTOP)
SIGSTOP in a subsequent ptrace(PTRACE_CONT,SIGSTOP) call.
See more comments in inferior.h. On the other hand, if we
get a non-SIGSTOP, report it to the user - assume the backend
will handle the SIGSTOP if it should show up later. */
if (stop_soon == STOP_QUIETLY_NO_SIGSTOP
&& stop_signal == TARGET_SIGNAL_STOP)
{
stop_stepping (ecs);
if (stop_signal == TARGET_SIGNAL_STOP)
stop_signal = TARGET_SIGNAL_0;
stop_signal = TARGET_SIGNAL_0;
return;
}
@ -2023,7 +2025,7 @@ process_event_stop_test:
target_terminal_ours_for_output ();
print_stop_reason (SIGNAL_RECEIVED, stop_signal);
}
if (signal_stop[stop_signal])
if (signal_stop_state (stop_signal))
{
stop_stepping (ecs);
return;
@ -3276,7 +3278,9 @@ hook_stop_stub (void *cmd)
int
signal_stop_state (int signo)
{
return signal_stop[signo];
/* Always stop on signals if we're just gaining control of the
program. */
return signal_stop[signo] || stop_soon != NO_STOP_QUIETLY;
}
int

View File

@ -50,6 +50,30 @@
#include "event-loop.h"
#include "event-top.h"
/* Note on this file's use of signals:
We stop threads by sending a SIGSTOP. The use of SIGSTOP instead
of another signal is not entirely significant; we just need for a
signal to be delivered, so that we can intercept it. SIGSTOP's
advantage is that it can not be blocked. A disadvantage is that it
is not a real-time signal, so it can only be queued once; we do not
keep track of other sources of SIGSTOP.
Two other signals that can't be blocked are SIGCONT and SIGKILL.
But we can't use them, because they have special behavior when the
signal is generated - not when it is delivered. SIGCONT resumes
the entire thread group and SIGKILL kills the entire thread group.
A delivered SIGSTOP would stop the entire thread group, not just the
thread we tkill'd. But we never let the SIGSTOP deliver; we always
intercept and cancel it (by PTRACE_CONT without passing SIGSTOP).
We could use a real-time signal instead. This would solve those
problems; we could use PTRACE_GETSIGINFO to locate the specific
stop signals sent by GDB. But we would still have to have some
support for SIGSTOP, since PTRACE_ATTACH generates it, and there
are races with trying to find a signal that is not blocked. */
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif
@ -186,6 +210,7 @@ static void linux_nat_async (void (*callback)
(enum inferior_event_type event_type, void *context),
void *context);
static int linux_nat_async_mask (int mask);
static int kill_lwp (int lwpid, int signo);
/* Captures the result of a successful waitpid call, along with the
options used in that call. */
@ -1010,10 +1035,103 @@ exit_lwp (struct lwp_info *lp)
delete_lwp (lp->ptid);
}
/* Attach to the LWP specified by PID. If VERBOSE is non-zero, print
a message telling the user that a new LWP has been added to the
process. Return 0 if successful or -1 if the new LWP could not
be attached. */
/* Detect `T (stopped)' in `/proc/PID/status'.
Other states including `T (tracing stop)' are reported as false. */
static int
pid_is_stopped (pid_t pid)
{
FILE *status_file;
char buf[100];
int retval = 0;
snprintf (buf, sizeof (buf), "/proc/%d/status", (int) pid);
status_file = fopen (buf, "r");
if (status_file != NULL)
{
int have_state = 0;
while (fgets (buf, sizeof (buf), status_file))
{
if (strncmp (buf, "State:", 6) == 0)
{
have_state = 1;
break;
}
}
if (have_state && strstr (buf, "T (stopped)") != NULL)
retval = 1;
fclose (status_file);
}
return retval;
}
/* Wait for the LWP specified by LP, which we have just attached to.
Returns a wait status for that LWP, to cache. */
static int
linux_nat_post_attach_wait (ptid_t ptid, int first, int *cloned,
int *signalled)
{
pid_t new_pid, pid = GET_LWP (ptid);
int status;
if (pid_is_stopped (pid))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LNPAW: Attaching to a stopped process\n");
/* The process is definitely stopped. It is in a job control
stop, unless the kernel predates the TASK_STOPPED /
TASK_TRACED distinction, in which case it might be in a
ptrace stop. Make sure it is in a ptrace stop; from there we
can kill it, signal it, et cetera.
First make sure there is a pending SIGSTOP. Since we are
already attached, the process can not transition from stopped
to running without a PTRACE_CONT; so we know this signal will
go into the queue. The SIGSTOP generated by PTRACE_ATTACH is
probably already in the queue (unless this kernel is old
enough to use TASK_STOPPED for ptrace stops); but since SIGSTOP
is not an RT signal, it can only be queued once. */
kill_lwp (pid, SIGSTOP);
/* Finally, resume the stopped process. This will deliver the SIGSTOP
(or a higher priority signal, just like normal PTRACE_ATTACH). */
ptrace (PTRACE_CONT, pid, 0, 0);
}
/* Make sure the initial process is stopped. The user-level threads
layer might want to poke around in the inferior, and that won't
work if things haven't stabilized yet. */
new_pid = my_waitpid (pid, &status, 0);
if (new_pid == -1 && errno == ECHILD)
{
if (first)
warning (_("%s is a cloned process"), target_pid_to_str (ptid));
/* Try again with __WCLONE to check cloned processes. */
new_pid = my_waitpid (pid, &status, __WCLONE);
*cloned = 1;
}
gdb_assert (pid == new_pid && WIFSTOPPED (status));
if (WSTOPSIG (status) != SIGSTOP)
{
*signalled = 1;
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LNPAW: Received %s after attaching\n",
status_to_str (status));
}
return status;
}
/* Attach to the LWP specified by PID. Return 0 if successful or -1
if the new LWP could not be attached. */
int
lin_lwp_attach_lwp (ptid_t ptid)
@ -1036,9 +1154,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
to happen. */
if (GET_LWP (ptid) != GET_PID (ptid) && lp == NULL)
{
pid_t pid;
int status;
int cloned = 0;
int status, cloned = 0, signalled = 0;
if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
{
@ -1057,24 +1173,18 @@ lin_lwp_attach_lwp (ptid_t ptid)
"LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
target_pid_to_str (ptid));
pid = my_waitpid (GET_LWP (ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
status = linux_nat_post_attach_wait (ptid, 0, &cloned, &signalled);
lp = add_lwp (ptid);
lp->stopped = 1;
lp->cloned = cloned;
lp->signalled = signalled;
if (WSTOPSIG (status) != SIGSTOP)
{
/* Try again with __WCLONE to check cloned processes. */
pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
cloned = 1;
lp->resumed = 1;
lp->status = status;
}
gdb_assert (pid == GET_LWP (ptid)
&& WIFSTOPPED (status) && WSTOPSIG (status));
if (lp == NULL)
lp = add_lwp (ptid);
lp->cloned = cloned;
target_post_attach (pid);
lp->stopped = 1;
target_post_attach (GET_LWP (lp->ptid));
if (debug_linux_nat)
{
@ -1133,10 +1243,7 @@ static void
linux_nat_attach (char *args, int from_tty)
{
struct lwp_info *lp;
pid_t pid;
int status;
int cloned = 0;
int options = 0;
/* FIXME: We should probably accept a list of process id's, and
attach all of them. */
@ -1151,54 +1258,69 @@ linux_nat_attach (char *args, int from_tty)
sigdelset (&suspend_mask, SIGCHLD);
}
/* Make sure the initial process is stopped. The user-level threads
layer might want to poke around in the inferior, and that won't
work if things haven't stabilized yet. */
pid = my_waitpid (GET_PID (inferior_ptid), &status, options);
if (pid == -1 && errno == ECHILD)
{
warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
/* Try again with __WCLONE to check cloned processes. */
options = __WCLONE;
pid = my_waitpid (GET_PID (inferior_ptid), &status, options);
cloned = 1;
}
gdb_assert (pid == GET_PID (inferior_ptid)
&& WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
/* Add the initial process as the first LWP to the list. */
inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
lp = add_lwp (inferior_ptid);
lp->cloned = cloned;
status = linux_nat_post_attach_wait (lp->ptid, 1, &lp->cloned,
&lp->signalled);
lp->stopped = 1;
/* If this process is not using thread_db, then we still don't
detect any other threads, but add at least this one. */
add_thread_silent (lp->ptid);
lp->stopped = 1;
/* Save the wait status to report later. */
lp->resumed = 1;
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LNA: waitpid %ld, saving status %s\n",
(long) GET_PID (lp->ptid), status_to_str (status));
if (!target_can_async_p ())
{
/* Fake the SIGSTOP that core GDB expects. */
lp->status = W_STOPCODE (SIGSTOP);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LNA: waitpid %ld, faking SIGSTOP\n", (long) pid);
}
lp->status = status;
else
{
/* We already waited for this LWP, so put the wait result on the
pipe. The event loop will wake up and gets us to handling
this event. */
linux_nat_event_pipe_push (pid, status, options);
linux_nat_event_pipe_push (GET_PID (lp->ptid), status,
lp->cloned ? __WCLONE : 0);
/* Register in the event loop. */
target_async (inferior_event_handler, 0);
}
}
/* Get pending status of LP. */
static int
get_pending_status (struct lwp_info *lp, int *status)
{
struct target_waitstatus last;
ptid_t last_ptid;
get_last_target_status (&last_ptid, &last);
/* If this lwp is the ptid that GDB is processing an event from, the
signal will be in stop_signal. Otherwise, in all-stop + sync
mode, we may cache pending events in lp->status while trying to
stop all threads (see stop_wait_callback). In async mode, the
events are always cached in waitpid_queue. */
*status = 0;
if (GET_LWP (lp->ptid) == GET_LWP (last_ptid))
{
if (stop_signal != TARGET_SIGNAL_0
&& signal_pass_state (stop_signal))
*status = W_STOPCODE (target_signal_to_host (stop_signal));
}
else if (target_can_async_p ())
queued_waitpid (GET_LWP (lp->ptid), status, __WALL);
else
*status = lp->status;
return 0;
}
static int
detach_callback (struct lwp_info *lp, void *data)
{
@ -1209,40 +1331,30 @@ detach_callback (struct lwp_info *lp, void *data)
strsignal (WSTOPSIG (lp->status)),
target_pid_to_str (lp->ptid));
while (lp->signalled && lp->stopped)
/* If there is a pending SIGSTOP, get rid of it. */
if (lp->signalled)
{
errno = 0;
if (ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0,
WSTOPSIG (lp->status)) < 0)
error (_("Can't continue %s: %s"), target_pid_to_str (lp->ptid),
safe_strerror (errno));
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"DC: PTRACE_CONTINUE (%s, 0, %s) (OK)\n",
target_pid_to_str (lp->ptid),
status_to_str (lp->status));
"DC: Sending SIGCONT to %s\n",
target_pid_to_str (lp->ptid));
lp->stopped = 0;
kill_lwp (GET_LWP (lp->ptid), SIGCONT);
lp->signalled = 0;
lp->status = 0;
/* FIXME drow/2003-08-26: There was a call to stop_wait_callback
here. But since lp->signalled was cleared above,
stop_wait_callback didn't do anything; the process was left
running. Shouldn't we be waiting for it to stop?
I've removed the call, since stop_wait_callback now does do
something when called with lp->signalled == 0. */
gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status));
}
/* We don't actually detach from the LWP that has an id equal to the
overall process id just yet. */
if (GET_LWP (lp->ptid) != GET_PID (lp->ptid))
{
int status = 0;
/* Pass on any pending signal for this LWP. */
get_pending_status (lp, &status);
errno = 0;
if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
WSTOPSIG (lp->status)) < 0)
WSTOPSIG (status)) < 0)
error (_("Can't detach %s: %s"), target_pid_to_str (lp->ptid),
safe_strerror (errno));
@ -1252,7 +1364,6 @@ detach_callback (struct lwp_info *lp, void *data)
target_pid_to_str (lp->ptid),
strsignal (WSTOPSIG (lp->status)));
drain_queued_events (GET_LWP (lp->ptid));
delete_lwp (lp->ptid);
}
@ -1263,6 +1374,9 @@ static void
linux_nat_detach (char *args, int from_tty)
{
int pid;
int status;
enum target_signal sig;
if (target_can_async_p ())
linux_nat_async (NULL, 0);
@ -1271,6 +1385,21 @@ linux_nat_detach (char *args, int from_tty)
/* Only the initial process should be left right now. */
gdb_assert (num_lwps == 1);
/* Pass on any pending signal for the last LWP. */
if ((args == NULL || *args == '\0')
&& get_pending_status (lwp_list, &status) != -1
&& WIFSTOPPED (status))
{
/* Put the signal number in ARGS so that inf_ptrace_detach will
pass it along with PTRACE_DETACH. */
args = alloca (8);
sprintf (args, "%d", (int) WSTOPSIG (status));
fprintf_unfiltered (gdb_stdlog,
"LND: Sending signal %s to %s\n",
args,
target_pid_to_str (lwp_list->ptid));
}
trap_ptid = null_ptid;
/* Destroy LWP info; it's no longer valid. */
@ -2848,7 +2977,9 @@ linux_nat_pid_to_str (ptid_t ptid)
{
static char buf[64];
if (lwp_list && lwp_list->next && is_lwp (ptid))
if (is_lwp (ptid)
&& ((lwp_list && lwp_list->next)
|| GET_PID (ptid) != GET_LWP (ptid)))
{
snprintf (buf, sizeof (buf), "LWP %ld", GET_LWP (ptid));
return buf;
@ -4205,4 +4336,3 @@ lin_thread_get_thread_signals (sigset_t *set)
/* ... except during a sigsuspend. */
sigdelset (&suspend_mask, cancel);
}

View File

@ -0,0 +1,67 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2008 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 <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef USE_THREADS
#include <pthread.h>
#endif
void action(int sig, siginfo_t * info, void *uc)
{
raise (SIGALRM);
}
static void *func (void *arg)
{
struct sigaction act;
memset (&act, 0, sizeof(struct sigaction));
act.sa_sigaction = action;
act.sa_flags = SA_RESTART;
sigaction (SIGALRM, &act, 0);
raise (SIGALRM);
/* We must not get past this point, either in a free standing or debugged
state. */
abort ();
/* NOTREACHED */
return NULL;
}
int main ()
{
#ifndef USE_THREADS
func (NULL);
#else
pthread_t th;
pthread_create (&th, NULL, func, NULL);
pthread_join (th, NULL);
#endif
return 0;
}

View File

@ -0,0 +1,168 @@
# Copyright 2008
# 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/>.
# This test was created by modifying attach-stopped.exp.
# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
# This test only works on Linux
if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
continue
}
set testfile "attach-into-signal"
set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile}
set escapedbinfile [string_to_regexp ${objdir}/${subdir}/${testfile}]
remote_exec build "rm -f ${binfile}"
# For debugging this test
#
#log_user 1
proc corefunc { threadtype } {
global srcfile
global binfile
global escapedbinfile
global srcdir
global subdir
global gdb_prompt
if [get_compiler_info ${binfile}] {
return -1
}
# Start the program running and then wait for a bit, to be sure
# that it can be attached to.
# Statistically there is a better chance without giving process a nice.
set testpid [eval exec $binfile &]
exec sleep 2
# Run 2 passes of the test.
# The C file inferior stops pending its signals if a single one is lost,
# we test successful redelivery of the caught signal by the 2nd pass.
# linux-2.6.20.4.x86_64 had maximal attempt # 20 in 4 test runs.
set attempts 100
set attempt 1
set passes 1
while { $passes < 3 && $attempt <= $attempts } {
set stoppedtry 0
while { $stoppedtry < 10 } {
if [catch {open /proc/${testpid}/status r} fileid] {
set stoppedtry 10
break
}
gets $fileid line1;
gets $fileid line2;
close $fileid;
if {![string match "*(stopped)*" $line2]} {
# No PASS message as we may be looping in multiple attempts.
break
}
sleep 1
set stoppedtry [expr $stoppedtry + 1]
}
if { $stoppedtry >= 10 } {
verbose -log $line2
set test "$threadtype: process is still running on the attempt # $attempt of $attempts"
break
}
# Main test:
set test "$threadtype: attach (pass $passes), pending signal catch"
if {[gdb_test_multiple "attach $testpid" $test {
-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*Received Alarm clock.*$gdb_prompt $" {
# nonthreaded:
pass $test
verbose -log "$test succeeded on the attempt # $attempt of $attempts"
set passes [expr $passes + 1]
}
-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
# nonthreaded:
# We just lack the luck, we should try it again.
set attempt [expr $attempt + 1]
}
-re "Attaching to process $testpid.*Received Alarm clock.*$gdb_prompt $" {
# threaded:
pass $test
verbose -log "$test succeeded on the attempt # $attempt of $attempts"
set passes [expr $passes + 1]
}
-re "Attaching to process $testpid.*$gdb_prompt $" {
# threaded:
# We just lack the luck, we should try it again.
set attempt [expr $attempt - 1]
}
}] != 0 } {
break
}
gdb_test "detach" "Detaching from.*" ""
}
if {$passes < 3} {
if {$attempt > $attempts} {
unresolved $test
} else {
fail $test
}
}
# Exit and detach the process.
gdb_exit
# Make sure we don't leave a process around to confuse
# the next test run (and prevent the compile by keeping
# the text file busy), in case the "set should_exit" didn't
# work.
# Continue the program - some Linux kernels need it before -9 if the
# process is stopped.
remote_exec build "kill -s CONT ${testpid}"
remote_exec build "kill -9 ${testpid}"
}
# Start with clean gdb
gdb_exit
# build the test case first without threads
#
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
}
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
gdb_load ${binfile}
gdb_test "set debug lin-lwp 1" "" ""
corefunc nonthreaded
# build the test case also with threads
#
if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
}
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
gdb_load ${binfile}
gdb_test "set debug lin-lwp 1" "" ""
corefunc threaded

View File

@ -0,0 +1,50 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2008 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/>. */
/* This program is intended to be started outside of gdb, then
manually stopped via a signal. */
#include <stddef.h>
#include <unistd.h>
#ifdef USE_THREADS
#include <pthread.h>
#endif
static void *func (void *arg)
{
sleep (10000); /* Ridiculous time, but we will eventually kill it. */
sleep (10000); /* Second sleep. */
return NULL;
}
int main ()
{
#ifndef USE_THREADS
func (NULL);
#else
pthread_t th;
pthread_create (&th, NULL, func, NULL);
pthread_join (th, NULL);
#endif
return 0;
}

View File

@ -0,0 +1,157 @@
# Copyright 2008
# 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/>.
# This test was created by modifying attach.exp.
# This file was created by Jeff Johnston <jjohnstn@redhat.com>.
# This file was updated by Jan Kratochvil <jan.kratochvil@redhat.com>.
# This test only works on Linux
if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
continue
}
set testfile "attach-stopped"
set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile}
set escapedbinfile [string_to_regexp ${objdir}/${subdir}/${testfile}]
#execute_anywhere "rm -f ${binfile}"
remote_exec build "rm -f ${binfile}"
# For debugging this test
#
#log_user 1
proc corefunc { threadtype } {
global srcfile
global binfile
global escapedbinfile
global srcdir
global subdir
global gdb_prompt
if [get_compiler_info ${binfile}] {
return -1
}
# Start the program running and then wait for a bit, to be sure
# that it can be attached to.
set testpid [eval exec $binfile &]
# Avoid some race:
sleep 2
# Stop the program
remote_exec build "kill -s STOP ${testpid}"
# Start with clean gdb
gdb_exit
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
gdb_load ${binfile}
# Verify that we can attach to the stopped process.
set test "$threadtype: attach2 to stopped, after setting file"
gdb_test_multiple "attach $testpid" "$test" {
-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
pass "$test"
}
}
# ".*sleep.*clone.*" would fail on s390x as bt stops at START_THREAD there.
if {[string equal $threadtype threaded]} {
gdb_test "thread apply all bt" ".*sleep.*start_thread.*" "$threadtype: attach2 to stopped bt"
} else {
gdb_test "bt" ".*sleep.*main.*" "$threadtype: attach2 to stopped bt"
}
# This breakpoint is there for old/non-x86 kernels not restarting syscalls.
gdb_breakpoint [gdb_get_line_number "Second sleep"]
set test "$threadtype: attach2 continue"
send_gdb "continue\n"
gdb_expect {
-re "Continuing"
{ pass "continue ($test)" }
timeout
{ fail "continue ($test) (timeout)" }
}
# For this to work we must be sure to consume the "Continuing."
# message first, or GDB's signal handler may not be in place.
after 1000 {send_gdb "\003"}
set test "$threadtype: attach2 stop interrupt"
gdb_expect 10 {
-re "Program received signal SIGINT.*$gdb_prompt $"
{
pass $test
}
-re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
{
pass $test
}
timeout
{
fail $test
}
}
gdb_exit
# Avoid some race:
sleep 2
# At this point, the process should be sleeping
if [catch {open /proc/${testpid}/status r} fileid2] {
set line2 "NOTFOUND"
} else {
gets $fileid2 line1;
gets $fileid2 line2;
close $fileid2;
}
set test "$threadtype: attach2, exit leaves process sleeping"
if {[string match "*(sleeping)*" $line2]} {
pass $test
} else {
fail $test
}
# Make sure we don't leave a process around to confuse
# the next test run (and prevent the compile by keeping
# the text file busy), in case the "set should_exit" didn't
# work.
remote_exec build "kill -9 ${testpid}"
}
# build the test case first without threads
#
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
gdb_suppress_entire_file "Testcase nonthraded compile failed, so all tests in this file will automatically fail."
}
corefunc nonthreaded
# build the test case first without threads
#
if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DUSE_THREADS}] != "" } {
gdb_suppress_entire_file "Testcase threaded compile failed, so all tests in this file will automatically fail."
}
corefunc threaded
return 0

View File

@ -0,0 +1,55 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2008 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/>. */
/* This program is intended to be started outside of gdb, then
manually stopped via a signal. */
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
/* Red Hat BZ PR 197584.c */
void *func (void *arg)
{
sleep (10000); /* Ridiculous time, but we will eventually kill it. */
sleep (10000); /* RHEL3U8 kernel-2.4.21-47.EL will cut the sleep time. */
return NULL; /* thread-sleep */
}
int main ()
{
pthread_t t1,t2;
int ret;
ret = pthread_create (&t1, NULL, func, NULL);
if (ret)
fprintf(stderr, "pthread_create(): %s", strerror (ret));
ret = pthread_create (&t2, NULL, func, NULL);
if (ret)
fprintf(stderr, "pthread_create(): %s", strerror (ret));
ret = pthread_join (t1, NULL);
if (ret) /* first-join */
fprintf(stderr, "pthread_join(): %s", strerror (ret));
ret = pthread_join (t2, NULL);
if (ret)
fprintf(stderr, "pthread_join(): %s", strerror (ret));
return 0;
}

View File

@ -0,0 +1,260 @@
# Copyright 2008
# 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/>.
# This test was created by modifying gdb.threads/attachstop.
# This file was created by Jan Kratochvil <jan.kratochvil@redhat.com>.
# Regression for: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=197584
# This test only works on Linux
if { ![isnative] || [is_remote host] || ![istarget *-linux*] } {
continue
}
set testfile "attachstop-mt"
set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile}
set escapedbinfile [string_to_regexp ${objdir}/${subdir}/${testfile}]
#execute_anywhere "rm -f ${binfile}"
remote_exec build "rm -f ${binfile}"
# For debugging this test
#
#log_user 1
# build the test case
#
if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail."
}
if [get_compiler_info ${binfile}] {
return -1
}
# Start the program running and then wait for a bit, to be sure
# that it can be attached to.
set testpid [eval exec $binfile &]
# No race
sleep 2
# Do not: set testpid2 [expr $testpid + 1]
# as it will not exist on Red Hat 2.6.9-34.0.2.ELsmp
set testpid2 [expr $testpid + 2]
set status2 /proc/${testpid}/task/${testpid2}/status
if {[expr ! [file exists $status2]]} {
# kernel-2.4
set status2 /proc/${testpid2}/status
}
# Initial sanity test it is normally sleeping
set fileid0 [open $status2 r];
gets $fileid0 line1;
gets $fileid0 line2;
close $fileid0;
set test "attach0, initial sanity check of the sleeping state"
if {[string match "*(sleeping)*" $line2]} {
pass $test
} else {
fail $test
}
# Sttach and detach to test it will not become stopped
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
gdb_load ${binfile}
set test "attach0 to sleeping"
gdb_test_multiple "attach $testpid" "$test" {
-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
pass "$test"
}
}
gdb_test "gcore /dev/null" ".*aved corefile.*" "attach0 to sleeping gcore invocation"
gdb_test "thread 2" ".*witching to thread 2 .*" "attach0 to sleeping switch thread"
gdb_test "bt" ".*sleep.*func.*" "attach0 to sleeping bt"
# Exit and detach the process.
gdb_exit
# No race
sleep 2
# Check it did not get stopped by our gdb
set fileid1 [open $status2 r];
gets $fileid1 line1;
gets $fileid1 line2;
close $fileid1;
set test "attach1, post-gdb sanity check of the sleeping state - Red Hat BZ 197584"
if {[string match "*(sleeping)*" $line2]} {
pass $test
} else {
fail $test
}
# Stop the program
remote_exec build "kill -s STOP ${testpid}"
# No race
sleep 2
# Check it really got stopped by kill(1)
set fileid2 [open $status2 r];
gets $fileid2 line1;
gets $fileid2 line2;
close $fileid2;
set test "attach2, initial sanity check of the stopped state"
if {[string match "*(stopped)*" $line2]} {
pass $test
} else {
fail $test
}
# Start with clean gdb
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
gdb_load ${binfile}
# Verify that we can attach to the process by first giving its
# executable name via the file command, and using attach with the
# process ID.
set test "set file, before attach3 to stopped process"
gdb_test_multiple "file $binfile" "$test" {
-re "Load new symbol table from.*y or n. $" {
gdb_test "y" "Reading symbols from $escapedbinfile\.\.\.*done." \
"$test (re-read)"
}
-re "Reading symbols from $escapedbinfile\.\.\.*done.*$gdb_prompt $" {
pass "$test"
}
}
set test "attach3 to stopped, after setting file"
gdb_test_multiple "attach $testpid" "$test" {
-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
pass "$test"
}
}
# We may be already after the threads phase.
# `thread 2' command is important for the test to switch the current thread to
# a non-primary one for the detach process.
gdb_test "thread 2" ".*(witching to thread 2 |hread ID 2 not known).*" "attach3 to stopped switch thread"
gdb_test "bt" ".*sleep.*(func|main).*" "attach3 to stopped bt"
# Exit and detach the process.
gdb_exit
# Stop the program
remote_exec build "kill -s STOP ${testpid}"
# No race
sleep 2
# Continue the test as we would hit another expected bug regarding
# Program received signal SIGSTOP, Stopped (signal).
# across NPTL threads.
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
gdb_load ${binfile}
# Verify that we can attach to the process just by giving the
# process ID.
set test "attach4 to stopped, after setting file"
gdb_test_multiple "attach $testpid" "$test" {
-re "Attaching to program.*`?$escapedbinfile'?, process $testpid.*$gdb_prompt $" {
pass "$test"
}
}
# We may be already after the threads phase.
# `thread 2' command is important for the test to switch the current thread to
# a non-primary one for the detach process.
gdb_test "thread 2" ".*(witching to thread 2 |hread ID 2 not known).*" "attach4 to stopped switch thread"
gdb_test "bt" ".*sleep.*(func|main).*" "attach4 to stopped bt"
# RHEL3U8 kernel-2.4.21-47.EL will not return SIGINT but only shorten the sleep.
gdb_breakpoint [gdb_get_line_number "Ridiculous time"]
gdb_breakpoint [gdb_get_line_number "cut the sleep time"]
set test "attach4 continue"
send_gdb "continue\n"
gdb_expect {
-re "Continuing"
{ pass "continue ($test)" }
timeout
{ fail "continue ($test) (timeout)" }
}
# For this to work we must be sure to consume the "Continuing."
# message first, or GDB's signal handler may not be in place.
after 1000 {send_gdb "\003"}
set test "attach4 stop by interrupt"
gdb_expect {
-re "Program received signal SIGINT.*$gdb_prompt $"
{
pass $test
}
-re "Breakpoint \[0-9\].*$srcfile.*$gdb_prompt $"
{
pass $test
}
timeout
{
fail "$test (timeout)"
}
}
gdb_exit
# No race
sleep 2
# At this point, the process should be sleeping
set fileid4 [open $status2 r];
gets $fileid4 line1;
gets $fileid4 line2;
close $fileid4;
set test "attach4, exit leaves process sleeping"
if {[string match "*(sleeping)*" $line2]} {
pass $test
} else {
fail $test
}
# Make sure we don't leave a process around to confuse
# the next test run (and prevent the compile by keeping
# the text file busy), in case the "set should_exit" didn't
# work.
remote_exec build "kill -9 ${testpid}"
return 0