/* Low level interface for debugging GNU/Linux threads for GDB,
the GNU debugger.
- Copyright 1998, 1999 Free Software Foundation, Inc.
+ Copyright 1998, 1999, 2000 Free Software Foundation, Inc.
This file is part of GDB.
linuxthreads package heavily relies on wait() synchronization to keep
them correct. */
+#include "defs.h"
#include <sys/types.h> /* for pid_t */
#include <sys/ptrace.h> /* for PT_* flags */
-#include <sys/wait.h> /* for WUNTRACED and __WCLONE flags */
+#include "gdb_wait.h" /* for WUNTRACED and __WCLONE flags */
#include <signal.h> /* for struct sigaction and NSIG */
#include <sys/utsname.h>
-#include "defs.h"
#include "target.h"
#include "inferior.h"
#include "gdbcore.h"
#include "gdbthread.h"
-#include "wait.h"
#include "gdbcmd.h"
#include "breakpoint.h"
static int linuxthreads_wait_last; /* index of last valid elt in
linuxthreads_wait_{pid,status} */
-static sigset_t linuxthreads_wait_mask; /* sigset with SIGCHLD */
+static sigset_t linuxthreads_block_mask; /* sigset without SIGCHLD */
static int linuxthreads_step_pid; /* current stepped pid */
static int linuxthreads_step_signo; /* current stepped target signal */
};
struct linuxthreads_signal linuxthreads_sig_restart = {
- "__pthread_sig_restart", 1, 0, 0, 0
+ "__pthread_sig_restart", 1, 0, 0, 0, 0
};
struct linuxthreads_signal linuxthreads_sig_cancel = {
- "__pthread_sig_cancel", 1, 0, 0, 0
+ "__pthread_sig_cancel", 1, 0, 0, 0, 0
};
struct linuxthreads_signal linuxthreads_sig_debug = {
- "__pthread_sig_debug", 0, 0, 0, 0
+ "__pthread_sig_debug", 0, 0, 0, 0, 0
};
+/* Set by thread_db module when it takes over the thread_stratum.
+ In that case we must:
+ a) refrain from turning on the debug signal, and
+ b) refrain from calling add_thread. */
+
+int using_thread_db = 0;
+
/* A table of breakpoint locations, one per PID. */
static struct linuxthreads_breakpoint {
CORE_ADDR pc; /* PC of breakpoint */
#endif
/* Check to see if the given thread is alive. */
static int
-linuxthreads_thread_alive (pid)
- int pid;
+linuxthreads_thread_alive (ptid_t ptid)
{
errno = 0;
- return ptrace (PT_READ_U, pid, (PTRACE_ARG3_TYPE)0, 0) >= 0 || errno == 0;
+ return ptrace (PT_READ_U, PIDGET (ptid), (PTRACE_ARG3_TYPE)0, 0) >= 0
+ || errno == 0;
}
/* On detach(), find a SIGTRAP status. If stop is non-zero, find a
our efforts to debug it, accept them with wait, but don't pass them
through to PID. Do pass all other signals through. */
static int
-linuxthreads_find_trap (pid, stop)
- int pid;
- int stop;
+linuxthreads_find_trap (int pid, int stop)
{
int i;
int rpid;
{
/* Make sure that we'll find what we're looking for. */
if (!found_trap)
- kill (pid, SIGTRAP);
+ {
+ kill (pid, SIGTRAP);
+ }
if (!found_stop)
- kill (pid, SIGSTOP);
+ {
+ kill (pid, SIGSTOP);
+ }
}
/* Catch all status until SIGTRAP and optionally SIGSTOP show up. */
for (;;)
{
- child_resume (pid, 1, TARGET_SIGNAL_0);
-
+ /* resume the child every time... */
+ child_resume (pid_to_ptid (pid), 1, TARGET_SIGNAL_0);
+
+ /* loop as long as errno == EINTR:
+ waitpid syscall may be aborted due to GDB receiving a signal.
+ FIXME: EINTR handling should no longer be necessary here, since
+ we now block SIGCHLD except in an explicit sigsuspend call. */
+
for (;;)
{
rpid = waitpid (pid, &status, __WCLONE);
if (rpid > 0)
- break;
+ {
+ break;
+ }
if (errno == EINTR)
- continue;
+ {
+ continue;
+ }
/* There are a few reasons the wait call above may have
failed. If the thread manager dies, its children get
2.0.36. */
rpid = waitpid (pid, &status, 0);
if (rpid > 0)
- break;
+ {
+ break;
+ }
if (errno != EINTR)
- perror_with_name ("waitpid");
+ perror_with_name ("find_trap/waitpid");
}
if (!WIFSTOPPED(status)) /* Thread has died */
else if (WSTOPSIG(status) != SIGSTOP)
wstatus[last++] = status;
else if (stop)
- if (found_trap)
- break;
- else
- found_stop = 1;
+ {
+ if (found_trap)
+ break;
+ else
+ found_stop = 1;
+ }
}
/* Resend any other signals we noticed to the thread, to be received
when we continue it. */
while (--last >= 0)
- kill (pid, WSTOPSIG(wstatus[last]));
+ {
+ kill (pid, WSTOPSIG(wstatus[last]));
+ }
return 1;
}
-/* Cleanup stub for save_inferior_pid. */
static void
-restore_inferior_pid (arg)
- void *arg;
-{
- int pid = (int) arg;
- inferior_pid = pid;
-}
-
-/* Register a cleanup to restore the value of inferior_pid. */
-static struct cleanup *
-save_inferior_pid ()
-{
- return make_cleanup (restore_inferior_pid, (void *) inferior_pid);
-}
-
-static void
-sigchld_handler (signo)
- int signo;
+sigchld_handler (int signo)
{
/* This handler is used to get an EINTR while doing waitpid()
when an event is received */
/* Have we already collected a wait status for PID in the
linuxthreads_wait bag? */
static int
-linuxthreads_pending_status (pid)
- int pid;
+linuxthreads_pending_status (int pid)
{
int i;
for (i = linuxthreads_wait_last; i >= 0; i--)
in OBJFILE, so we complain if it's required, but not there.
Return true iff things are okay. */
static int
-find_signal_var (sig, objfile)
- struct linuxthreads_signal *sig;
- struct objfile *objfile;
+find_signal_var (struct linuxthreads_signal *sig, struct objfile *objfile)
{
struct minimal_symbol *ms = lookup_minimal_symbol (sig->var, NULL, objfile);
}
static int
-find_all_signal_vars (objfile)
- struct objfile *objfile;
+find_all_signal_vars (struct objfile *objfile)
{
return ( find_signal_var (&linuxthreads_sig_restart, objfile)
&& find_signal_var (&linuxthreads_sig_cancel, objfile)
been initialized yet. If it has, tell GDB to pass that signal
through to the inferior silently. */
static void
-check_signal_number (sig)
- struct linuxthreads_signal *sig;
+check_signal_number (struct linuxthreads_signal *sig)
{
int num;
sig->print = signal_print_update (target_signal_from_host (num), 0);
}
-
-static void
-check_all_signal_numbers ()
+void
+check_all_signal_numbers (void)
{
/* If this isn't a LinuxThreads program, quit early. */
if (! linuxthreads_max)
sact.sa_handler = sigchld_handler;
sigemptyset(&sact.sa_mask);
sact.sa_flags = 0;
+
if (linuxthreads_sig_debug.signal > 0)
sigaction(linuxthreads_sig_cancel.signal, &sact, NULL);
else
talking to an executable that uses LinuxThreads, so we clear the
signal number and variable address too. */
static void
-restore_signal (sig)
- struct linuxthreads_signal *sig;
+restore_signal (struct linuxthreads_signal *sig)
{
if (! sig->signal)
return;
talking to an executable that uses LinuxThreads, so we clear the
signal number and variable address too. */
static void
-restore_all_signals ()
+restore_all_signals (void)
{
restore_signal (&linuxthreads_sig_restart);
restore_signal (&linuxthreads_sig_cancel);
If ALL is non-zero, process all threads.
If ALL is zero, skip threads with pending status. */
static void
-iterate_active_threads (func, all)
- void (*func)(int);
- int all;
+iterate_active_threads (void (*func) (int), int all)
{
CORE_ADDR descr;
int pid;
(*func)(pid);
}
}
-
}
/* Insert a thread breakpoint at linuxthreads_breakpoint_addr.
This is the worker function for linuxthreads_insert_breakpoint,
which passes it to iterate_active_threads. */
static void
-insert_breakpoint (pid)
- int pid;
+insert_breakpoint (int pid)
{
int j;
breakpoint if the thread's PC is pointing at the breakpoint being
removed. */
static void
-remove_breakpoint (pid)
- int pid;
+remove_breakpoint (int pid)
{
int j;
if (linuxthreads_breakpoint_zombie[j].pid == pid)
break;
- if (in_thread_list (pid) && linuxthreads_thread_alive (pid))
+ if (in_thread_list (pid_to_ptid (pid))
+ && linuxthreads_thread_alive (pid_to_ptid (pid)))
{
- CORE_ADDR pc = read_pc_pid (pid);
+ CORE_ADDR pc = read_pc_pid (pid_to_ptid (pid));
if (linuxthreads_breakpoint_addr == pc - DECR_PC_AFTER_BREAK
&& j > linuxthreads_breakpoint_last)
{
/* Kill a thread */
static void
-kill_thread (pid)
- int pid;
+kill_thread (int pid)
{
- if (in_thread_list (pid))
- ptrace (PT_KILL, pid, (PTRACE_ARG3_TYPE) 0, 0);
+ if (in_thread_list (pid_to_ptid (pid)))
+ {
+ ptrace (PT_KILL, pid, (PTRACE_ARG3_TYPE) 0, 0);
+ }
else
- kill (pid, SIGKILL);
+ {
+ kill (pid, SIGKILL);
+ }
}
/* Resume a thread */
static void
-resume_thread (pid)
- int pid;
+resume_thread (int pid)
{
- if (pid != inferior_pid
- && in_thread_list (pid)
- && linuxthreads_thread_alive (pid))
- if (pid == linuxthreads_step_pid)
- child_resume (pid, 1, linuxthreads_step_signo);
- else
- child_resume (pid, 0, TARGET_SIGNAL_0);
+ if (pid != PIDGET (inferior_ptid)
+ && in_thread_list (pid_to_ptid (pid))
+ && linuxthreads_thread_alive (pid_to_ptid (pid)))
+ {
+ if (pid == linuxthreads_step_pid)
+ {
+ child_resume (pid_to_ptid (pid), 1, linuxthreads_step_signo);
+ }
+ else
+ {
+ child_resume (pid_to_ptid (pid), 0, TARGET_SIGNAL_0);
+ }
+ }
}
/* Detach a thread */
static void
-detach_thread (pid)
- int pid;
+detach_thread (int pid)
{
- if (in_thread_list (pid) && linuxthreads_thread_alive (pid))
+ ptid_t ptid = pid_to_ptid (pid);
+
+ if (in_thread_list (ptid) && linuxthreads_thread_alive (ptid))
{
/* Remove pending SIGTRAP and SIGSTOP */
linuxthreads_find_trap (pid, 1);
- inferior_pid = pid;
+ inferior_ptid = ptid;
detach (TARGET_SIGNAL_0);
- inferior_pid = linuxthreads_manager_pid;
+ inferior_ptid = pid_to_ptid (linuxthreads_manager_pid);
}
}
+/* Attach a thread */
+void
+attach_thread (int pid)
+{
+ if (ptrace (PT_ATTACH, pid, (PTRACE_ARG3_TYPE) 0, 0) != 0)
+ perror_with_name ("attach_thread");
+}
+
/* Stop a thread */
static void
-stop_thread (pid)
- int pid;
+stop_thread (int pid)
{
- if (pid != inferior_pid)
- if (in_thread_list (pid))
- kill (pid, SIGSTOP);
- else if (ptrace (PT_ATTACH, pid, (PTRACE_ARG3_TYPE) 0, 0) == 0)
- {
- if (!linuxthreads_attach_pending)
- printf_unfiltered ("[New %s]\n", target_pid_to_str (pid));
- add_thread (pid);
- if (linuxthreads_sig_debug.signal)
- /* After a new thread in glibc 2.1 signals gdb its existence,
- it suspends itself and wait for linuxthreads_sig_restart,
- now we can wake up it. */
- kill (pid, linuxthreads_sig_restart.signal);
- }
- else
- perror_with_name ("ptrace in stop_thread");
+ if (pid != PIDGET (inferior_ptid))
+ {
+ if (in_thread_list (pid_to_ptid (pid)))
+ {
+ kill (pid, SIGSTOP);
+ }
+ else if (ptrace (PT_ATTACH, pid, (PTRACE_ARG3_TYPE) 0, 0) == 0)
+ {
+ if (!linuxthreads_attach_pending)
+ printf_filtered ("[New %s]\n",
+ target_pid_to_str (pid_to_ptid (pid)));
+ add_thread (pid_to_ptid (pid));
+ if (linuxthreads_sig_debug.signal)
+ {
+ /* After a new thread in glibc 2.1 signals gdb its existence,
+ it suspends itself and wait for linuxthreads_sig_restart,
+ now we can wake it up. */
+ kill (pid, linuxthreads_sig_restart.signal);
+ }
+ }
+ else
+ perror_with_name ("ptrace in stop_thread");
+ }
}
/* Wait for a thread */
static void
-wait_thread (pid)
- int pid;
+wait_thread (int pid)
{
int status;
int rpid;
- if (pid != inferior_pid && in_thread_list (pid))
+ if (pid != PIDGET (inferior_ptid) && in_thread_list (pid_to_ptid (pid)))
{
+ /* loop as long as errno == EINTR:
+ waitpid syscall may be aborted if GDB receives a signal.
+ FIXME: EINTR handling should no longer be necessary here, since
+ we now block SIGCHLD except during an explicit sigsuspend call. */
for (;;)
{
/* Get first pid status. */
rpid = waitpid(pid, &status, __WCLONE);
if (rpid > 0)
- break;
+ {
+ break;
+ }
if (errno == EINTR)
- continue;
+ {
+ continue;
+ }
/* There are two reasons this might have failed:
didn't work. */
rpid = waitpid(pid, &status, 0);
if (rpid > 0)
- break;
- if (errno != EINTR && linuxthreads_thread_alive (pid))
- perror_with_name ("waitpid");
+ {
+ break;
+ }
+ if (errno != EINTR && linuxthreads_thread_alive (pid_to_ptid (pid)))
+ perror_with_name ("wait_thread/waitpid");
/* the thread is dead. */
return;
/* Walk through the linuxthreads handles in order to detect all
threads and stop them */
static void
-update_stop_threads (test_pid)
- int test_pid;
+update_stop_threads (int test_pid)
{
struct cleanup *old_chain = NULL;
{
if (linuxthreads_manager)
{
- if (test_pid > 0 && test_pid != inferior_pid)
+ if (test_pid > 0 && test_pid != PIDGET (inferior_ptid))
{
- old_chain = save_inferior_pid ();
- inferior_pid = test_pid;
+ old_chain = save_inferior_ptid ();
+ inferior_ptid = pid_to_ptid (test_pid);
}
read_memory (linuxthreads_manager,
(char *)&linuxthreads_manager_pid, sizeof (pid_t));
}
if (linuxthreads_initial)
{
- if (test_pid > 0 && test_pid != inferior_pid)
+ if (test_pid > 0 && test_pid != PIDGET (inferior_ptid))
{
- old_chain = save_inferior_pid ();
- inferior_pid = test_pid;
+ old_chain = save_inferior_ptid ();
+ inferior_ptid = pid_to_ptid (test_pid);
}
read_memory(linuxthreads_initial,
(char *)&linuxthreads_initial_pid, sizeof (pid_t));
if (linuxthreads_manager_pid != 0)
{
if (old_chain == NULL && test_pid > 0 &&
- test_pid != inferior_pid && linuxthreads_thread_alive (test_pid))
+ test_pid != PIDGET (inferior_ptid)
+ && linuxthreads_thread_alive (pid_to_ptid (test_pid)))
{
- old_chain = save_inferior_pid ();
- inferior_pid = test_pid;
+ old_chain = save_inferior_ptid ();
+ inferior_ptid = pid_to_ptid (test_pid);
}
- if (linuxthreads_thread_alive (inferior_pid))
+ if (linuxthreads_thread_alive (inferior_ptid))
{
if (test_pid > 0)
{
stop_thread (linuxthreads_manager_pid);
wait_thread (linuxthreads_manager_pid);
}
- if (!in_thread_list (test_pid))
+ if (!in_thread_list (pid_to_ptid (test_pid)))
{
if (!linuxthreads_attach_pending)
- printf_unfiltered ("[New %s]\n",
- target_pid_to_str (test_pid));
- add_thread (test_pid);
+ printf_filtered ("[New %s]\n",
+ target_pid_to_str (pid_to_ptid (test_pid)));
+ add_thread (pid_to_ptid (test_pid));
if (linuxthreads_sig_debug.signal
- && inferior_pid == test_pid)
- /* After a new thread in glibc 2.1 signals gdb its
- existence, it suspends itself and wait for
- linuxthreads_sig_restart, now we can wake up
- it. */
- kill (test_pid, linuxthreads_sig_restart.signal);
+ && PIDGET (inferior_ptid) == test_pid)
+ {
+ /* After a new thread in glibc 2.1 signals gdb its
+ existence, it suspends itself and wait for
+ linuxthreads_sig_restart, now we can wake it up. */
+ kill (test_pid, linuxthreads_sig_restart.signal);
+ }
}
}
iterate_active_threads (stop_thread, 0);
do_cleanups (old_chain);
}
-/* This routine is called whenever a new symbol table is read in, or when all
- symbol tables are removed. libpthread can only be initialized when it
- finds the right variables in libpthread.so. Since it's a shared library,
- those variables don't show up until the library gets mapped and the symbol
- table is read in. */
+/* This routine is called whenever a new symbol table is read in, or
+ when all symbol tables are removed. linux-thread event handling
+ can only be initialized when we find the right variables in
+ libpthread.so. Since it's a shared library, those variables don't
+ show up until the library gets mapped and the symbol table is read
+ in. */
+
+/* This new_objfile event is now managed by a chained function pointer.
+ * It is the callee's responsability to call the next client on the chain.
+ */
+
+/* Saved pointer to previous owner of the new_objfile event. */
+static void (*target_new_objfile_chain) (struct objfile *);
void
-linuxthreads_new_objfile (objfile)
- struct objfile *objfile;
+linuxthreads_new_objfile (struct objfile *objfile)
{
struct minimal_symbol *ms;
+ /* Call predecessor on chain, if any.
+ Calling the new module first allows it to dominate,
+ if it finds its compatible libraries. */
+
+ if (target_new_objfile_chain)
+ target_new_objfile_chain (objfile);
+
if (!objfile)
{
/* We're starting an entirely new executable, so we can no
/* Indicate that we don't know anything's address any more. */
linuxthreads_max = 0;
- return;
+ goto quit;
}
/* If we've already found our variables in another objfile, don't
bother looking for them again. */
if (linuxthreads_max)
- return;
+ goto quit;
if (! lookup_minimal_symbol ("__pthread_initial_thread", NULL, objfile))
/* This object file isn't the pthreads library. */
- return;
+ goto quit;
if ((ms = lookup_minimal_symbol ("__pthread_threads_debug",
NULL, objfile)) == NULL)
does not support debugging. This may make using GDB difficult. Don't\n\
set breakpoints or single-step through code that might be executed by\n\
any thread other than the main thread.");
- return;
+ goto quit;
}
linuxthreads_debug = SYMBOL_VALUE_ADDRESS (ms);
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_sizeof_handle");
- return;
+ goto quit;
}
if ((ms = lookup_minimal_symbol ("__pthread_offsetof_descr",
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_offsetof_descr");
- return;
+ goto quit;
}
if ((ms = lookup_minimal_symbol ("__pthread_offsetof_pid",
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_offsetof_pid");
- return;
+ goto quit;
}
if (! find_all_signal_vars (objfile))
- return;
+ goto quit;
/* Read adresses of internal structures to access */
if ((ms = lookup_minimal_symbol ("__pthread_handles",
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_handles");
- return;
+ goto quit;
}
linuxthreads_handles = SYMBOL_VALUE_ADDRESS (ms);
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_handles_num");
- return;
+ goto quit;
}
linuxthreads_num = SYMBOL_VALUE_ADDRESS (ms);
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_manager_thread");
- return;
+ goto quit;
}
linuxthreads_manager = SYMBOL_VALUE_ADDRESS (ms) + linuxthreads_offset_pid;
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_initial_thread");
- return;
+ goto quit;
}
linuxthreads_initial = SYMBOL_VALUE_ADDRESS (ms) + linuxthreads_offset_pid;
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_threads_max");
- return;
+ goto quit;
}
/* Allocate gdb internal structures */
linuxthreads_breakpoint_zombie = (struct linuxthreads_breakpoint *)
xmalloc (sizeof (struct linuxthreads_breakpoint) * (linuxthreads_max + 1));
- if (inferior_pid && !linuxthreads_attach_pending)
+ if (PIDGET (inferior_ptid) != 0 &&
+ !linuxthreads_attach_pending &&
+ !using_thread_db) /* suppressed by thread_db module */
{
int on = 1;
+
target_write_memory (linuxthreads_debug, (char *)&on, sizeof (on));
linuxthreads_attach_pending = 1;
- update_stop_threads (inferior_pid);
+ update_stop_threads (PIDGET (inferior_ptid));
linuxthreads_attach_pending = 0;
}
+
+ check_all_signal_numbers ();
+
+quit:
}
/* If we have switched threads from a one that stopped at breakpoint,
- return 1 otherwise 0. */
+ return 1 otherwise 0.
+
+ Note that this implementation is potentially redundant now that
+ default_prepare_to_proceed() has been added.
+
+ FIXME This may not support switching threads after Ctrl-C
+ correctly. The default implementation does support this. */
int
-linuxthreads_prepare_to_proceed (step)
- int step;
+linuxthreads_prepare_to_proceed (int step)
{
if (!linuxthreads_max
|| !linuxthreads_manager_pid
|| !linuxthreads_breakpoint_pid
- || !breakpoint_here_p (read_pc_pid (linuxthreads_breakpoint_pid)))
+ || !breakpoint_here_p (
+ read_pc_pid (pid_to_ptid (linuxthreads_breakpoint_pid))))
return 0;
if (step)
{
/* Mark the current inferior as single stepping process. */
- linuxthreads_step_pid = inferior_pid;
+ linuxthreads_step_pid = PIDGET (inferior_ptid);
}
linuxthreads_inferior_pid = linuxthreads_breakpoint_pid;
/* Convert a pid to printable form. */
char *
-linuxthreads_pid_to_str (pid)
- int pid;
+linuxthreads_pid_to_str (ptid_t ptid)
{
static char buf[100];
+ int pid = PIDGET (ptid);
sprintf (buf, "%s %d%s", linuxthreads_max ? "Thread" : "Pid", pid,
(pid == linuxthreads_manager_pid) ? " (manager thread)"
and wait for the trace-trap that results from attaching. */
static void
-linuxthreads_attach (args, from_tty)
- char *args;
- int from_tty;
+linuxthreads_attach (char *args, int from_tty)
{
if (!args)
error_no_arg ("process-id to attach");
linuxthreads_breakpoints_inserted = 1;
linuxthreads_breakpoint_last = -1;
linuxthreads_wait_last = -1;
- linuxthreads_exit_status = __W_STOPCODE(0);
+ WSETSTOP (linuxthreads_exit_status, 0);
child_ops.to_attach (args, from_tty);
started via the normal ptrace (PTRACE_TRACEME). */
static void
-linuxthreads_detach (args, from_tty)
- char *args;
- int from_tty;
+linuxthreads_detach (char *args, int from_tty)
{
if (linuxthreads_max)
{
continue;
pid = linuxthreads_breakpoint_zombie[i].pid;
- if (!linuxthreads_thread_alive (pid))
+ if (!linuxthreads_thread_alive (pid_to_ptid (pid)))
continue;
- if (linuxthreads_breakpoint_zombie[i].pc != read_pc_pid (pid))
+ if (linuxthreads_breakpoint_zombie[i].pc
+ != read_pc_pid (pid_to_ptid (pid)))
continue;
/* Continue in STEP mode until the thread pc has moved or
until SIGTRAP is found on the same PC. */
if (linuxthreads_find_trap (pid, 0)
- && linuxthreads_breakpoint_zombie[i].pc == read_pc_pid (pid))
+ && linuxthreads_breakpoint_zombie[i].pc
+ == read_pc_pid (pid_to_ptid (pid)))
write_pc_pid (linuxthreads_breakpoint_zombie[i].pc
- - DECR_PC_AFTER_BREAK, pid);
+ - DECR_PC_AFTER_BREAK, pid_to_ptid (pid));
}
/* Detach thread after thread. */
- inferior_pid = linuxthreads_manager_pid;
+ inferior_ptid = pid_to_ptid (linuxthreads_manager_pid);
iterate_active_threads (detach_thread, 1);
/* Remove pending SIGTRAP and SIGSTOP */
- linuxthreads_find_trap (inferior_pid, 1);
+ linuxthreads_find_trap (PIDGET (inferior_ptid), 1);
linuxthreads_wait_last = -1;
- linuxthreads_exit_status = __W_STOPCODE(0);
+ WSETSTOP (linuxthreads_exit_status, 0);
}
linuxthreads_inferior_pid = 0;
signal activated. */
static void
-linuxthreads_resume (pid, step, signo)
- int pid;
- int step;
- enum target_signal signo;
+linuxthreads_resume (ptid_t ptid, int step, enum target_signal signo)
{
if (!linuxthreads_max || stop_soon_quietly || linuxthreads_manager_pid == 0)
- child_ops.to_resume (pid, step, signo);
+ {
+ child_ops.to_resume (ptid, step, signo);
+ }
else
{
int rpid;
struct cleanup *old_chain = NULL;
int i;
- if (pid < 0)
+ if (PIDGET (ptid) < 0)
{
- linuxthreads_step_pid = step ? inferior_pid : 0;
+ linuxthreads_step_pid = step ? PIDGET (inferior_ptid) : 0;
linuxthreads_step_signo = signo;
- rpid = inferior_pid;
+ rpid = PIDGET (inferior_ptid);
}
else
- rpid = pid;
+ rpid = PIDGET (ptid);
- if (pid < 0 || !step)
+ if (PIDGET (ptid) < 0 || !step)
{
linuxthreads_breakpoints_inserted = 1;
/* Walk through linuxthreads array in order to resume threads */
- if (pid >= 0 && inferior_pid != pid)
+ if (PIDGET (ptid) >= 0 && !ptid_equal (inferior_ptid, ptid))
{
- old_chain = save_inferior_pid ();
- inferior_pid = pid;
+ old_chain = save_inferior_ptid ();
+ inferior_ptid = ptid;
}
iterate_active_threads (resume_thread, 0);
- if (linuxthreads_manager_pid != inferior_pid
+ if (linuxthreads_manager_pid != PIDGET (inferior_ptid)
&& !linuxthreads_pending_status (linuxthreads_manager_pid))
resume_thread (linuxthreads_manager_pid);
}
for (i = 0; i <= linuxthreads_breakpoint_last; i++)
if (linuxthreads_breakpoint_zombie[i].pid == rpid)
{
- if (linuxthreads_breakpoint_zombie[i].pc != read_pc_pid (rpid))
+ if (linuxthreads_breakpoint_zombie[i].pc
+ != read_pc_pid (pid_to_ptid (rpid)))
{
/* The current pc is out of zombie breakpoint. */
REMOVE_BREAKPOINT_ZOMBIE(i);
}
/* Resume initial thread. */
+ /* [unles it has a wait event pending] */
if (!linuxthreads_pending_status (rpid))
- child_ops.to_resume (rpid, step, signo);
+ {
+ child_ops.to_resume (pid_to_ptid (rpid), step, signo);
+ }
}
}
+/* Abstract out the child_wait functionality. */
+int
+linux_child_wait (int pid, int *rpid, int *status)
+{
+ int save_errno;
+
+ /* Note: inftarg has these inside the loop. */
+ set_sigint_trap (); /* Causes SIGINT to be passed on to the
+ attached process. */
+ set_sigio_trap ();
+
+ errno = save_errno = 0;
+ for (;;)
+ {
+ errno = 0;
+ *rpid = waitpid (pid, status, __WCLONE | WNOHANG);
+ save_errno = errno;
+
+ if (*rpid > 0)
+ {
+ /* Got an event -- break out */
+ break;
+ }
+ if (errno == EINTR) /* interrupted by signal, try again */
+ {
+ continue;
+ }
+
+ errno = 0;
+ *rpid = waitpid (pid, status, WNOHANG);
+ if (*rpid > 0)
+ {
+ /* Got an event -- break out */
+ break;
+ }
+ if (errno == EINTR)
+ {
+ continue;
+ }
+ if (errno != 0 && save_errno != 0)
+ {
+ break;
+ }
+ sigsuspend(&linuxthreads_block_mask);
+ }
+ clear_sigio_trap ();
+ clear_sigint_trap ();
+
+ return errno ? errno : save_errno;
+}
+
+
/* Wait for any threads to stop. We may have to convert PID from a thread id
to a LWP id, and vice versa on the way out. */
-static int
-linuxthreads_wait (pid, ourstatus)
- int pid;
- struct target_waitstatus *ourstatus;
+static ptid_t
+linuxthreads_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
{
int status;
int rpid;
int i;
int last;
int *wstatus;
+ int pid = PIDGET (ptid);
if (linuxthreads_max && !linuxthreads_breakpoints_inserted)
wstatus = alloca (LINUXTHREAD_NSIG * sizeof (int));
if (linuxthreads_inferior_pid)
pid = linuxthreads_inferior_pid;
else if (pid < 0)
- pid = inferior_pid;
+ pid = PIDGET (inferior_ptid);
last = rpid = 0;
}
else if (pid < 0 && linuxthreads_wait_last >= 0)
if (rpid == 0)
{
int save_errno;
- sigset_t omask;
- set_sigint_trap(); /* Causes SIGINT to be passed on to the
- attached process. */
- set_sigio_trap ();
-
- sigprocmask(SIG_BLOCK, &linuxthreads_wait_mask, &omask);
- for (;;)
- {
- rpid = waitpid (pid, &status, __WCLONE | WNOHANG);
- if (rpid > 0)
- break;
- if (rpid == 0)
- save_errno = 0;
- else if (errno != EINTR)
- save_errno = errno;
- else
- continue;
-
- rpid = waitpid (pid, &status, WNOHANG);
- if (rpid > 0)
- break;
- if (rpid < 0)
- if (errno == EINTR)
- continue;
- else if (save_errno != 0)
- break;
-
- sigsuspend(&omask);
- }
- sigprocmask(SIG_SETMASK, &omask, NULL);
-
- save_errno = errno;
- clear_sigio_trap ();
-
- clear_sigint_trap();
+ save_errno = linux_child_wait (pid, &rpid, &status);
if (rpid == -1)
{
if (WIFEXITED(linuxthreads_exit_status))
{
store_waitstatus (ourstatus, linuxthreads_exit_status);
- return inferior_pid;
+ return inferior_ptid;
}
else
{
/* Claim it exited with unknown signal. */
ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
- return -1;
+ return pid_to_ptid (-1);
}
}
- /* Signals arrive in any order. So get all signals until SIGTRAP
- and resend previous ones to be held after. */
+ /* We have now gotten a new event from waitpid above. */
+
+ /* Signals arrive in any order. So get all signals until
+ SIGTRAP and resend previous ones to be held after. */
if (linuxthreads_max
&& !linuxthreads_breakpoints_inserted
&& WIFSTOPPED(status))
if (WSTOPSIG(status) == SIGTRAP)
{
while (--last >= 0)
- kill (rpid, WSTOPSIG(wstatus[last]));
+ {
+ kill (rpid, WSTOPSIG(wstatus[last]));
+ }
/* insert negative zombie breakpoint */
for (i = 0; i <= linuxthreads_breakpoint_last; i++)
linuxthreads_breakpoint_zombie[i].pid = rpid;
linuxthreads_breakpoint_last++;
}
- linuxthreads_breakpoint_zombie[i].pc = read_pc_pid (rpid);
+ linuxthreads_breakpoint_zombie[i].pc
+ = read_pc_pid (pid_to_ptid (rpid));
linuxthreads_breakpoint_zombie[i].step = 1;
}
else
if (wstatus[i] == status)
break;
if (i >= last)
- wstatus[last++] = status;
+ {
+ wstatus[last++] = status;
+ }
}
- child_resume (rpid, 1, TARGET_SIGNAL_0);
+ child_resume (pid_to_ptid (rpid), 1, TARGET_SIGNAL_0);
continue;
}
if (linuxthreads_inferior_pid)
{
/* Skip SIGSTOP signals. */
if (!linuxthreads_pending_status (rpid))
- if (linuxthreads_step_pid == rpid)
- child_resume (rpid, 1, linuxthreads_step_signo);
- else
- child_resume (rpid, 0, TARGET_SIGNAL_0);
+ {
+ if (linuxthreads_step_pid == rpid)
+ {
+ child_resume (pid_to_ptid (rpid), 1,
+ linuxthreads_step_signo);
+ }
+ else
+ {
+ child_resume (pid_to_ptid (rpid), 0, TARGET_SIGNAL_0);
+ }
+ }
continue;
}
{
/* There is a potential zombie breakpoint */
if (WIFEXITED(status)
- || linuxthreads_breakpoint_zombie[i].pc != read_pc_pid (rpid))
+ || linuxthreads_breakpoint_zombie[i].pc
+ != read_pc_pid (pid_to_ptid (rpid)))
{
/* The current pc is out of zombie breakpoint. */
REMOVE_BREAKPOINT_ZOMBIE(i);
{
/* This is a real one ==> decrement PC and restart. */
write_pc_pid (linuxthreads_breakpoint_zombie[i].pc
- - DECR_PC_AFTER_BREAK, rpid);
+ - DECR_PC_AFTER_BREAK, pid_to_ptid (rpid));
if (linuxthreads_step_pid == rpid)
- child_resume (rpid, 1, linuxthreads_step_signo);
+ {
+ child_resume (pid_to_ptid (rpid), 1, linuxthreads_step_signo);
+ }
else
- child_resume (rpid, 0, TARGET_SIGNAL_0);
+ {
+ child_resume (pid_to_ptid (rpid), 0, TARGET_SIGNAL_0);
+ }
continue;
}
}
update_stop_threads (rpid);
}
- else if (rpid != inferior_pid)
+ else if (rpid != PIDGET (inferior_ptid))
continue;
store_waitstatus (ourstatus, status);
if (linuxthreads_attach_pending && !stop_soon_quietly)
{
int on = 1;
- target_write_memory (linuxthreads_debug, (char *)&on, sizeof (on));
- update_stop_threads (rpid);
+ if (!using_thread_db)
+ {
+ target_write_memory (linuxthreads_debug,
+ (char *) &on, sizeof (on));
+ update_stop_threads (rpid);
+ }
linuxthreads_attach_pending = 0;
}
else if (linuxthreads_breakpoint_pid)
linuxthreads_breakpoint_pid = 0;
- return rpid;
+ return pid_to_ptid (rpid);
}
}
/* Fork an inferior process, and start debugging it with ptrace. */
static void
-linuxthreads_create_inferior (exec_file, allargs, env)
- char *exec_file;
- char *allargs;
- char **env;
+linuxthreads_create_inferior (char *exec_file, char *allargs, char **env)
{
if (!exec_file && !exec_bfd)
{
linuxthreads_breakpoints_inserted = 1;
linuxthreads_breakpoint_last = -1;
linuxthreads_wait_last = -1;
- linuxthreads_exit_status = __W_STOPCODE(0);
+ WSETSTOP (linuxthreads_exit_status, 0);
if (linuxthreads_max)
linuxthreads_attach_pending = 1;
child_ops.to_create_inferior (exec_file, allargs, env);
}
+void
+linuxthreads_discard_global_state (void)
+{
+ linuxthreads_inferior_pid = 0;
+ linuxthreads_breakpoint_pid = 0;
+ linuxthreads_step_pid = 0;
+ linuxthreads_step_signo = TARGET_SIGNAL_0;
+ linuxthreads_manager_pid = 0;
+ linuxthreads_initial_pid = 0;
+ linuxthreads_attach_pending = 0;
+ linuxthreads_max = 0;
+}
+
/* Clean up after the inferior dies. */
static void
-linuxthreads_mourn_inferior ()
+linuxthreads_mourn_inferior (void)
{
if (linuxthreads_max)
{
int off = 0;
target_write_memory (linuxthreads_debug, (char *)&off, sizeof (off));
- linuxthreads_inferior_pid = 0;
- linuxthreads_breakpoint_pid = 0;
- linuxthreads_step_pid = 0;
- linuxthreads_step_signo = TARGET_SIGNAL_0;
- linuxthreads_manager_pid = 0;
- linuxthreads_initial_pid = 0;
- linuxthreads_attach_pending = 0;
+ linuxthreads_discard_global_state ();
init_thread_list(); /* Destroy thread info */
}
/* Kill the inferior process */
static void
-linuxthreads_kill ()
+linuxthreads_kill (void)
{
int rpid;
int status;
- if (inferior_pid == 0)
+ if (PIDGET (inferior_ptid) == 0)
return;
if (linuxthreads_max && linuxthreads_manager_pid != 0)
{
/* Remove all threads status. */
- inferior_pid = linuxthreads_manager_pid;
+ inferior_ptid = pid_to_ptid (linuxthreads_manager_pid);
iterate_active_threads (kill_thread, 1);
}
- kill_thread (inferior_pid);
+ kill_thread (PIDGET (inferior_ptid));
#if 0
/* doing_quit_force solves a real problem, but I think a properly
kill_thread (rpid);
}
else
- while ((rpid = waitpid (inferior_pid, &status, 0)) > 0)
+ while ((rpid = waitpid (PIDGET (inferior_ptid), &status, 0)) > 0)
if (!WIFEXITED(status))
- ptrace (PT_KILL, inferior_pid, (PTRACE_ARG3_TYPE) 0, 0);
+ ptrace (PT_KILL, PIDGET (inferior_ptid), (PTRACE_ARG3_TYPE) 0, 0);
}
#endif
/* Wait for all threads. */
do
- rpid = waitpid (-1, &status, __WCLONE | WNOHANG);
+ {
+ rpid = waitpid (-1, &status, __WCLONE | WNOHANG);
+ }
while (rpid > 0 || errno == EINTR);
+ /* FIXME: should no longer need to handle EINTR here. */
do
- rpid = waitpid (-1, &status, WNOHANG);
+ {
+ rpid = waitpid (-1, &status, WNOHANG);
+ }
while (rpid > 0 || errno == EINTR);
+ /* FIXME: should no longer need to handle EINTR here. */
linuxthreads_mourn_inferior ();
}
/* Insert a breakpoint */
static int
-linuxthreads_insert_breakpoint (addr, contents_cache)
- CORE_ADDR addr;
- char *contents_cache;
+linuxthreads_insert_breakpoint (CORE_ADDR addr, char *contents_cache)
{
if (linuxthreads_max && linuxthreads_manager_pid != 0)
{
/* Remove a breakpoint */
static int
-linuxthreads_remove_breakpoint (addr, contents_cache)
- CORE_ADDR addr;
- char *contents_cache;
+linuxthreads_remove_breakpoint (CORE_ADDR addr, char *contents_cache)
{
if (linuxthreads_max && linuxthreads_manager_pid != 0)
{
/* Mark our target-struct as eligible for stray "run" and "attach" commands. */
static int
-linuxthreads_can_run ()
+linuxthreads_can_run (void)
{
return child_suppress_run;
}
+
\f
static void
-init_linuxthreads_ops ()
+init_linuxthreads_ops (void)
{
linuxthreads_ops.to_shortname = "linuxthreads";
linuxthreads_ops.to_longname = "LINUX threads and pthread.";
linuxthreads_ops.to_create_inferior = linuxthreads_create_inferior;
linuxthreads_ops.to_mourn_inferior = linuxthreads_mourn_inferior;
linuxthreads_ops.to_thread_alive = linuxthreads_thread_alive;
+ linuxthreads_ops.to_pid_to_str = linuxthreads_pid_to_str;
linuxthreads_ops.to_magic = OPS_MAGIC;
}
void
-_initialize_linuxthreads ()
+_initialize_linuxthreads (void)
{
struct sigaction sact;
+ sigset_t linuxthreads_wait_mask; /* sigset with SIGCHLD */
init_linuxthreads_ops ();
add_target (&linuxthreads_ops);
child_suppress_run = 1;
+ /* Hook onto the "new_objfile" event.
+ * If someone else is already hooked onto the event,
+ * then make sure he will be called after we are.
+ */
+ target_new_objfile_chain = target_new_objfile_hook;
+ target_new_objfile_hook = linuxthreads_new_objfile;
+
/* Attach SIGCHLD handler */
sact.sa_handler = sigchld_handler;
sigemptyset (&sact.sa_mask);
/* initialize SIGCHLD mask */
sigemptyset (&linuxthreads_wait_mask);
sigaddset (&linuxthreads_wait_mask, SIGCHLD);
+
+ /* Use SIG_BLOCK to block receipt of SIGCHLD.
+ The block_mask will allow us to wait for this signal explicitly. */
+ sigprocmask(SIG_BLOCK,
+ &linuxthreads_wait_mask,
+ &linuxthreads_block_mask);
+ /* Make sure that linuxthreads_block_mask is not blocking SIGCHLD */
+ sigdelset (&linuxthreads_block_mask, SIGCHLD);
}