#include "defs.h"
#include <sys/types.h>
#include <sys/ptrace.h>
#include "gdb_wait.h"
#include <signal.h>
#include <sys/utsname.h>
#include "target.h"
#include "inferior.h"
#include "gdbcore.h"
#include "gdbthread.h"
#include "gdbcmd.h"
#include "breakpoint.h"
#ifndef PT_ATTACH
#define PT_ATTACH PTRACE_ATTACH
#endif
#ifndef PT_KILL
#define PT_KILL PTRACE_KILL
#endif
#ifndef PT_READ_U
#define PT_READ_U PTRACE_PEEKUSR
#endif
#ifdef NSIG
#define LINUXTHREAD_NSIG NSIG
#else
#ifdef _NSIG
#define LINUXTHREAD_NSIG _NSIG
#endif
#endif
extern int child_suppress_run;
struct target_ops linuxthreads_ops;
extern struct target_ops child_ops;
static CORE_ADDR linuxthreads_handles;
static CORE_ADDR linuxthreads_manager;
static CORE_ADDR linuxthreads_initial;
static CORE_ADDR linuxthreads_debug;
static CORE_ADDR linuxthreads_num;
static int linuxthreads_max;
static int linuxthreads_sizeof_handle;
static int linuxthreads_offset_descr;
static int linuxthreads_offset_pid;
static int linuxthreads_manager_pid;
static int linuxthreads_initial_pid;
static int *linuxthreads_wait_pid;
static int *linuxthreads_wait_status;
static int linuxthreads_wait_last;
static sigset_t linuxthreads_block_mask;
static int linuxthreads_step_pid;
static int linuxthreads_step_signo;
static int linuxthreads_exit_status;
static int linuxthreads_inferior_pid;
static int linuxthreads_breakpoint_pid;
static int linuxthreads_attach_pending;
static int linuxthreads_breakpoints_inserted;
struct linuxthreads_signal {
char *var;
int required;
CORE_ADDR addr;
int signal;
int stop, print;
};
struct linuxthreads_signal linuxthreads_sig_restart = {
"__pthread_sig_restart", 1, 0, 0, 0, 0
};
struct linuxthreads_signal linuxthreads_sig_cancel = {
"__pthread_sig_cancel", 1, 0, 0, 0, 0
};
struct linuxthreads_signal linuxthreads_sig_debug = {
"__pthread_sig_debug", 0, 0, 0, 0, 0
};
int using_thread_db = 0;
static struct linuxthreads_breakpoint {
CORE_ADDR pc;
int pid;
int step;
} *linuxthreads_breakpoint_zombie;
static int linuxthreads_breakpoint_last;
static CORE_ADDR linuxthreads_breakpoint_addr;
#define REMOVE_BREAKPOINT_ZOMBIE(_i) \
{ \
if ((_i) < linuxthreads_breakpoint_last) \
linuxthreads_breakpoint_zombie[(_i)] = \
linuxthreads_breakpoint_zombie[linuxthreads_breakpoint_last]; \
linuxthreads_breakpoint_last--; \
}
#ifndef PTRACE_XFER_TYPE
#define PTRACE_XFER_TYPE int
#endif
static int
linuxthreads_thread_alive (ptid_t ptid)
{
errno = 0;
return ptrace (PT_READ_U, PIDGET (ptid), (PTRACE_ARG3_TYPE)0, 0) >= 0
|| errno == 0;
}
static int
linuxthreads_find_trap (int pid, int stop)
{
int i;
int rpid;
int status;
int found_stop = 0;
int found_trap = 0;
int *wstatus = alloca (LINUXTHREAD_NSIG * sizeof (int));
int last = 0;
for (i = linuxthreads_wait_last; i >= 0; i--)
if (linuxthreads_wait_pid[i] == pid)
{
status = linuxthreads_wait_status[i];
if (i < linuxthreads_wait_last)
{
linuxthreads_wait_status[i] =
linuxthreads_wait_status[linuxthreads_wait_last];
linuxthreads_wait_pid[i] =
linuxthreads_wait_pid[linuxthreads_wait_last];
}
linuxthreads_wait_last--;
if (!WIFSTOPPED(status))
return 0;
if (WSTOPSIG(status) == SIGTRAP)
{
if (stop)
found_trap = 1;
else
return 1;
}
else if (WSTOPSIG(status) == SIGSTOP)
{
if (stop)
found_stop = 1;
}
else
{
wstatus[0] = status;
last = 1;
}
break;
}
if (stop)
{
if (!found_trap)
{
kill (pid, SIGTRAP);
}
if (!found_stop)
{
kill (pid, SIGSTOP);
}
}
for (;;)
{
child_resume (pid_to_ptid (pid), 1, TARGET_SIGNAL_0);
for (;;)
{
rpid = waitpid (pid, &status, __WCLONE);
if (rpid > 0)
{
break;
}
if (errno == EINTR)
{
continue;
}
rpid = waitpid (pid, &status, 0);
if (rpid > 0)
{
break;
}
if (errno != EINTR)
perror_with_name ("find_trap/waitpid");
}
if (!WIFSTOPPED(status))
return 0;
if (WSTOPSIG(status) == SIGTRAP)
if (!stop || found_stop)
break;
else
found_trap = 1;
else if (WSTOPSIG(status) != SIGSTOP)
wstatus[last++] = status;
else if (stop)
{
if (found_trap)
break;
else
found_stop = 1;
}
}
while (--last >= 0)
{
kill (pid, WSTOPSIG(wstatus[last]));
}
return 1;
}
static void
sigchld_handler (int signo)
{
}
static int
linuxthreads_pending_status (int pid)
{
int i;
for (i = linuxthreads_wait_last; i >= 0; i--)
if (linuxthreads_wait_pid[i] == pid)
return 1;
return 0;
}
static int
find_signal_var (struct linuxthreads_signal *sig, struct objfile *objfile)
{
struct minimal_symbol *ms = lookup_minimal_symbol (sig->var, NULL, objfile);
if (! ms)
{
if (sig->required)
{
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
sig->var);
return 0;
}
else
{
sig->addr = 0;
return 1;
}
}
sig->addr = SYMBOL_VALUE_ADDRESS (ms);
return 1;
}
static int
find_all_signal_vars (struct objfile *objfile)
{
return ( find_signal_var (&linuxthreads_sig_restart, objfile)
&& find_signal_var (&linuxthreads_sig_cancel, objfile)
&& find_signal_var (&linuxthreads_sig_debug, objfile));
}
static int complained_cannot_determine_thread_signal_number = 0;
static void
check_signal_number (struct linuxthreads_signal *sig)
{
int num;
if (sig->signal)
return;
if (! sig->addr)
return;
if (target_read_memory (sig->addr, (char *)&num, sizeof (num))
!= 0)
{
if (! complained_cannot_determine_thread_signal_number)
warning ("Cannot determine thread signal number; "
"GDB may report spurious signals.");
complained_cannot_determine_thread_signal_number = 1;
return;
}
if (num == 0)
return;
sig->signal = num;
sig->stop = signal_stop_update (target_signal_from_host (num), 0);
sig->print = signal_print_update (target_signal_from_host (num), 0);
}
void
check_all_signal_numbers (void)
{
if (! linuxthreads_max)
return;
check_signal_number (&linuxthreads_sig_restart);
check_signal_number (&linuxthreads_sig_cancel);
check_signal_number (&linuxthreads_sig_debug);
if (linuxthreads_sig_debug.signal
|| linuxthreads_sig_restart.signal)
{
struct sigaction sact;
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
sigaction(linuxthreads_sig_restart.signal, &sact, NULL);
}
}
static void
restore_signal (struct linuxthreads_signal *sig)
{
if (! sig->signal)
return;
signal_stop_update (target_signal_from_host (sig->signal), sig->stop);
signal_print_update (target_signal_from_host (sig->signal), sig->print);
sig->signal = 0;
sig->addr = 0;
}
static void
restore_all_signals (void)
{
restore_signal (&linuxthreads_sig_restart);
restore_signal (&linuxthreads_sig_cancel);
restore_signal (&linuxthreads_sig_debug);
complained_cannot_determine_thread_signal_number = 0;
}
static void
iterate_active_threads (void (*func) (int), int all)
{
CORE_ADDR descr;
int pid;
int i;
int num;
read_memory (linuxthreads_num, (char *)&num, sizeof (int));
for (i = 0; i < linuxthreads_max && num > 0; i++)
{
read_memory (linuxthreads_handles +
linuxthreads_sizeof_handle * i + linuxthreads_offset_descr,
(char *)&descr, sizeof (void *));
if (descr)
{
num--;
read_memory (descr + linuxthreads_offset_pid,
(char *)&pid, sizeof (pid_t));
if (pid > 0 && pid != linuxthreads_manager_pid
&& (all || (!linuxthreads_pending_status (pid))))
(*func)(pid);
}
}
}
static void
insert_breakpoint (int pid)
{
int j;
for (j = linuxthreads_breakpoint_last; j >= 0; j--)
if (linuxthreads_breakpoint_zombie[j].pid == pid)
{
if ((linuxthreads_breakpoint_zombie[j].pc - DECR_PC_AFTER_BREAK
== linuxthreads_breakpoint_addr)
&& !linuxthreads_breakpoint_zombie[j].step)
REMOVE_BREAKPOINT_ZOMBIE(j);
break;
}
}
static void
remove_breakpoint (int pid)
{
int j;
for (j = 0; j <= linuxthreads_breakpoint_last; j++)
if (linuxthreads_breakpoint_zombie[j].pid == pid)
break;
if (in_thread_list (pid_to_ptid (pid))
&& linuxthreads_thread_alive (pid_to_ptid (pid)))
{
CORE_ADDR pc = read_pc_pid (pid_to_ptid (pid));
if (linuxthreads_breakpoint_addr == pc - DECR_PC_AFTER_BREAK
&& j > linuxthreads_breakpoint_last)
{
linuxthreads_breakpoint_zombie[j].pid = pid;
linuxthreads_breakpoint_zombie[j].pc = pc;
linuxthreads_breakpoint_zombie[j].step = 0;
linuxthreads_breakpoint_last++;
}
}
}
static void
kill_thread (int pid)
{
if (in_thread_list (pid_to_ptid (pid)))
{
ptrace (PT_KILL, pid, (PTRACE_ARG3_TYPE) 0, 0);
}
else
{
kill (pid, SIGKILL);
}
}
static void
resume_thread (int pid)
{
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);
}
}
}
static void
detach_thread (int pid)
{
ptid_t ptid = pid_to_ptid (pid);
if (in_thread_list (ptid) && linuxthreads_thread_alive (ptid))
{
linuxthreads_find_trap (pid, 1);
inferior_ptid = ptid;
detach (TARGET_SIGNAL_0);
inferior_ptid = pid_to_ptid (linuxthreads_manager_pid);
}
}
void
attach_thread (int pid)
{
if (ptrace (PT_ATTACH, pid, (PTRACE_ARG3_TYPE) 0, 0) != 0)
perror_with_name ("attach_thread");
}
static void
stop_thread (int pid)
{
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)
{
kill (pid, linuxthreads_sig_restart.signal);
}
}
else
perror_with_name ("ptrace in stop_thread");
}
}
static void
wait_thread (int pid)
{
int status;
int rpid;
if (pid != PIDGET (inferior_ptid) && in_thread_list (pid_to_ptid (pid)))
{
for (;;)
{
rpid = waitpid(pid, &status, __WCLONE);
if (rpid > 0)
{
break;
}
if (errno == EINTR)
{
continue;
}
rpid = waitpid(pid, &status, 0);
if (rpid > 0)
{
break;
}
if (errno != EINTR && linuxthreads_thread_alive (pid_to_ptid (pid)))
perror_with_name ("wait_thread/waitpid");
return;
}
if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
{
linuxthreads_wait_pid[++linuxthreads_wait_last] = pid;
linuxthreads_wait_status[linuxthreads_wait_last] = status;
}
}
}
static void
update_stop_threads (int test_pid)
{
struct cleanup *old_chain = NULL;
check_all_signal_numbers ();
if (linuxthreads_manager_pid == 0)
{
if (linuxthreads_manager)
{
if (test_pid > 0 && test_pid != PIDGET (inferior_ptid))
{
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 != PIDGET (inferior_ptid))
{
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 != PIDGET (inferior_ptid)
&& linuxthreads_thread_alive (pid_to_ptid (test_pid)))
{
old_chain = save_inferior_ptid ();
inferior_ptid = pid_to_ptid (test_pid);
}
if (linuxthreads_thread_alive (inferior_ptid))
{
if (test_pid > 0)
{
if (test_pid != linuxthreads_manager_pid
&& !linuxthreads_pending_status (linuxthreads_manager_pid))
{
stop_thread (linuxthreads_manager_pid);
wait_thread (linuxthreads_manager_pid);
}
if (!in_thread_list (pid_to_ptid (test_pid)))
{
if (!linuxthreads_attach_pending)
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
&& PIDGET (inferior_ptid) == test_pid)
{
kill (test_pid, linuxthreads_sig_restart.signal);
}
}
}
iterate_active_threads (stop_thread, 0);
iterate_active_threads (wait_thread, 0);
}
}
if (old_chain != NULL)
do_cleanups (old_chain);
}
static void (*target_new_objfile_chain) (struct objfile *);
void
linuxthreads_new_objfile (struct objfile *objfile)
{
struct minimal_symbol *ms;
if (target_new_objfile_chain)
target_new_objfile_chain (objfile);
if (!objfile)
{
restore_all_signals ();
linuxthreads_max = 0;
goto quit;
}
if (linuxthreads_max)
goto quit;
if (! lookup_minimal_symbol ("__pthread_initial_thread", NULL, objfile))
goto quit;
if ((ms = lookup_minimal_symbol ("__pthread_threads_debug",
NULL, objfile)) == NULL)
{
warning ("\
This program seems to use POSIX threads, but the thread library used\n\
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.");
goto quit;
}
linuxthreads_debug = SYMBOL_VALUE_ADDRESS (ms);
if ((ms = lookup_minimal_symbol ("__pthread_sizeof_handle",
NULL, objfile)) == NULL
|| target_read_memory (SYMBOL_VALUE_ADDRESS (ms),
(char *)&linuxthreads_sizeof_handle,
sizeof (linuxthreads_sizeof_handle)) != 0)
{
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_sizeof_handle");
goto quit;
}
if ((ms = lookup_minimal_symbol ("__pthread_offsetof_descr",
NULL, objfile)) == NULL
|| target_read_memory (SYMBOL_VALUE_ADDRESS (ms),
(char *)&linuxthreads_offset_descr,
sizeof (linuxthreads_offset_descr)) != 0)
{
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_offsetof_descr");
goto quit;
}
if ((ms = lookup_minimal_symbol ("__pthread_offsetof_pid",
NULL, objfile)) == NULL
|| target_read_memory (SYMBOL_VALUE_ADDRESS (ms),
(char *)&linuxthreads_offset_pid,
sizeof (linuxthreads_offset_pid)) != 0)
{
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_offsetof_pid");
goto quit;
}
if (! find_all_signal_vars (objfile))
goto quit;
if ((ms = lookup_minimal_symbol ("__pthread_handles",
NULL, objfile)) == NULL)
{
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_handles");
goto quit;
}
linuxthreads_handles = SYMBOL_VALUE_ADDRESS (ms);
if ((ms = lookup_minimal_symbol ("__pthread_handles_num",
NULL, objfile)) == NULL)
{
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_handles_num");
goto quit;
}
linuxthreads_num = SYMBOL_VALUE_ADDRESS (ms);
if ((ms = lookup_minimal_symbol ("__pthread_manager_thread",
NULL, objfile)) == NULL)
{
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_manager_thread");
goto quit;
}
linuxthreads_manager = SYMBOL_VALUE_ADDRESS (ms) + linuxthreads_offset_pid;
if ((ms = lookup_minimal_symbol ("__pthread_initial_thread",
NULL, objfile)) == NULL)
{
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_initial_thread");
goto quit;
}
linuxthreads_initial = SYMBOL_VALUE_ADDRESS (ms) + linuxthreads_offset_pid;
if ((ms = lookup_minimal_symbol ("__pthread_threads_max",
NULL, objfile)) == NULL
|| target_read_memory (SYMBOL_VALUE_ADDRESS (ms),
(char *)&linuxthreads_max,
sizeof (linuxthreads_max)) != 0)
{
fprintf_unfiltered (gdb_stderr,
"Unable to find linuxthreads symbol \"%s\"\n",
"__pthread_threads_max");
goto quit;
}
linuxthreads_wait_pid =
(int *) xmalloc (sizeof (int) * (linuxthreads_max + 1));
linuxthreads_wait_status =
(int *) xmalloc (sizeof (int) * (linuxthreads_max + 1));
linuxthreads_breakpoint_zombie = (struct linuxthreads_breakpoint *)
xmalloc (sizeof (struct linuxthreads_breakpoint) * (linuxthreads_max + 1));
if (PIDGET (inferior_ptid) != 0 &&
!linuxthreads_attach_pending &&
!using_thread_db)
{
int on = 1;
target_write_memory (linuxthreads_debug, (char *)&on, sizeof (on));
linuxthreads_attach_pending = 1;
update_stop_threads (PIDGET (inferior_ptid));
linuxthreads_attach_pending = 0;
}
check_all_signal_numbers ();
quit:
}
int
linuxthreads_prepare_to_proceed (int step)
{
if (!linuxthreads_max
|| !linuxthreads_manager_pid
|| !linuxthreads_breakpoint_pid
|| !breakpoint_here_p (
read_pc_pid (pid_to_ptid (linuxthreads_breakpoint_pid))))
return 0;
if (step)
{
linuxthreads_step_pid = PIDGET (inferior_ptid);
}
linuxthreads_inferior_pid = linuxthreads_breakpoint_pid;
return linuxthreads_breakpoint_pid;
}
char *
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)"
: (pid == linuxthreads_initial_pid) ? " (initial thread)"
: "");
return buf;
}
static void
linuxthreads_attach (char *args, int from_tty)
{
if (!args)
error_no_arg ("process-id to attach");
push_target (&linuxthreads_ops);
linuxthreads_breakpoints_inserted = 1;
linuxthreads_breakpoint_last = -1;
linuxthreads_wait_last = -1;
WSETSTOP (linuxthreads_exit_status, 0);
child_ops.to_attach (args, from_tty);
if (linuxthreads_max)
linuxthreads_attach_pending = 1;
}
static void
linuxthreads_detach (char *args, int from_tty)
{
if (linuxthreads_max)
{
int i;
int pid;
int off = 0;
target_write_memory (linuxthreads_debug, (char *)&off, sizeof (off));
if (linuxthreads_manager_pid != 0)
{
for (i = 0; i <= linuxthreads_breakpoint_last; i++)
{
if (linuxthreads_breakpoint_zombie[i].step)
continue;
pid = linuxthreads_breakpoint_zombie[i].pid;
if (!linuxthreads_thread_alive (pid_to_ptid (pid)))
continue;
if (linuxthreads_breakpoint_zombie[i].pc
!= read_pc_pid (pid_to_ptid (pid)))
continue;
if (linuxthreads_find_trap (pid, 0)
&& 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_to_ptid (pid));
}
inferior_ptid = pid_to_ptid (linuxthreads_manager_pid);
iterate_active_threads (detach_thread, 1);
linuxthreads_find_trap (PIDGET (inferior_ptid), 1);
linuxthreads_wait_last = -1;
WSETSTOP (linuxthreads_exit_status, 0);
}
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;
init_thread_list ();
}
child_ops.to_detach (args, from_tty);
unpush_target (&linuxthreads_ops);
}
static void
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 (ptid, step, signo);
}
else
{
int rpid;
if (linuxthreads_inferior_pid)
{
linuxthreads_breakpoints_inserted = 0;
rpid = linuxthreads_inferior_pid;
linuxthreads_step_signo = signo;
}
else
{
struct cleanup *old_chain = NULL;
int i;
if (PIDGET (ptid) < 0)
{
linuxthreads_step_pid = step ? PIDGET (inferior_ptid) : 0;
linuxthreads_step_signo = signo;
rpid = PIDGET (inferior_ptid);
}
else
rpid = PIDGET (ptid);
if (PIDGET (ptid) < 0 || !step)
{
linuxthreads_breakpoints_inserted = 1;
if (PIDGET (ptid) >= 0 && !ptid_equal (inferior_ptid, ptid))
{
old_chain = save_inferior_ptid ();
inferior_ptid = ptid;
}
iterate_active_threads (resume_thread, 0);
if (linuxthreads_manager_pid != PIDGET (inferior_ptid)
&& !linuxthreads_pending_status (linuxthreads_manager_pid))
resume_thread (linuxthreads_manager_pid);
}
else
linuxthreads_breakpoints_inserted = 0;
for (i = 0; i <= linuxthreads_breakpoint_last; i++)
if (linuxthreads_breakpoint_zombie[i].pid == rpid)
{
if (linuxthreads_breakpoint_zombie[i].pc
!= read_pc_pid (pid_to_ptid (rpid)))
{
REMOVE_BREAKPOINT_ZOMBIE(i);
}
break;
}
if (old_chain != NULL)
do_cleanups (old_chain);
}
if (!linuxthreads_pending_status (rpid))
{
child_ops.to_resume (pid_to_ptid (rpid), step, signo);
}
}
}
int
linux_child_wait (int pid, int *rpid, int *status)
{
int save_errno;
set_sigint_trap ();
set_sigio_trap ();
errno = save_errno = 0;
for (;;)
{
errno = 0;
*rpid = waitpid (pid, status, __WCLONE | WNOHANG);
save_errno = errno;
if (*rpid > 0)
{
break;
}
if (errno == EINTR)
{
continue;
}
errno = 0;
*rpid = waitpid (pid, status, WNOHANG);
if (*rpid > 0)
{
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;
}
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));
check_all_signal_numbers ();
for (;;)
{
if (!linuxthreads_max)
rpid = 0;
else if (!linuxthreads_breakpoints_inserted)
{
if (linuxthreads_inferior_pid)
pid = linuxthreads_inferior_pid;
else if (pid < 0)
pid = PIDGET (inferior_ptid);
last = rpid = 0;
}
else if (pid < 0 && linuxthreads_wait_last >= 0)
{
status = linuxthreads_wait_status[linuxthreads_wait_last];
rpid = linuxthreads_wait_pid[linuxthreads_wait_last--];
}
else if (pid > 0 && linuxthreads_pending_status (pid))
{
for (i = linuxthreads_wait_last; i >= 0; i--)
if (linuxthreads_wait_pid[i] == pid)
break;
if (i < 0)
rpid = 0;
else
{
status = linuxthreads_wait_status[i];
rpid = pid;
if (i < linuxthreads_wait_last)
{
linuxthreads_wait_status[i] =
linuxthreads_wait_status[linuxthreads_wait_last];
linuxthreads_wait_pid[i] =
linuxthreads_wait_pid[linuxthreads_wait_last];
}
linuxthreads_wait_last--;
}
}
else
rpid = 0;
if (rpid == 0)
{
int save_errno;
save_errno = linux_child_wait (pid, &rpid, &status);
if (rpid == -1)
{
if (WIFEXITED(linuxthreads_exit_status))
{
store_waitstatus (ourstatus, linuxthreads_exit_status);
return inferior_ptid;
}
else
{
fprintf_unfiltered
(gdb_stderr, "Child process unexpectedly missing: %s.\n",
safe_strerror (save_errno));
ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
return pid_to_ptid (-1);
}
}
if (linuxthreads_max
&& !linuxthreads_breakpoints_inserted
&& WIFSTOPPED(status))
if (WSTOPSIG(status) == SIGTRAP)
{
while (--last >= 0)
{
kill (rpid, WSTOPSIG(wstatus[last]));
}
for (i = 0; i <= linuxthreads_breakpoint_last; i++)
if (linuxthreads_breakpoint_zombie[i].pid == rpid)
break;
if (i > linuxthreads_breakpoint_last)
{
linuxthreads_breakpoint_zombie[i].pid = rpid;
linuxthreads_breakpoint_last++;
}
linuxthreads_breakpoint_zombie[i].pc
= read_pc_pid (pid_to_ptid (rpid));
linuxthreads_breakpoint_zombie[i].step = 1;
}
else
{
if (WSTOPSIG(status) != SIGSTOP)
{
for (i = 0; i < last; i++)
if (wstatus[i] == status)
break;
if (i >= last)
{
wstatus[last++] = status;
}
}
child_resume (pid_to_ptid (rpid), 1, TARGET_SIGNAL_0);
continue;
}
if (linuxthreads_inferior_pid)
linuxthreads_inferior_pid = 0;
}
if (linuxthreads_max && !stop_soon_quietly)
{
if (linuxthreads_max
&& WIFSTOPPED(status)
&& WSTOPSIG(status) == SIGSTOP)
{
if (!linuxthreads_pending_status (rpid))
{
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;
}
if (WIFEXITED(status))
{
if (rpid == linuxthreads_initial_pid)
linuxthreads_exit_status = status;
for (i = 0; i <= linuxthreads_breakpoint_last; i++)
if (linuxthreads_breakpoint_zombie[i].pid == rpid)
{
REMOVE_BREAKPOINT_ZOMBIE(i);
break;
}
if (pid > 0)
pid = -1;
continue;
}
for (i = 0; i <= linuxthreads_breakpoint_last; i++)
if (linuxthreads_breakpoint_zombie[i].pid == rpid)
break;
if (i <= linuxthreads_breakpoint_last)
{
if (WIFEXITED(status)
|| linuxthreads_breakpoint_zombie[i].pc
!= read_pc_pid (pid_to_ptid (rpid)))
{
REMOVE_BREAKPOINT_ZOMBIE(i);
}
else if (!linuxthreads_breakpoint_zombie[i].step
&& WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP)
{
write_pc_pid (linuxthreads_breakpoint_zombie[i].pc
- DECR_PC_AFTER_BREAK, pid_to_ptid (rpid));
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;
}
}
if (linuxthreads_breakpoints_inserted)
update_stop_threads (rpid);
}
else if (rpid != PIDGET (inferior_ptid))
continue;
store_waitstatus (ourstatus, status);
if (linuxthreads_attach_pending && !stop_soon_quietly)
{
int on = 1;
if (!using_thread_db)
{
target_write_memory (linuxthreads_debug,
(char *) &on, sizeof (on));
update_stop_threads (rpid);
}
linuxthreads_attach_pending = 0;
}
if (linuxthreads_breakpoints_inserted
&& WIFSTOPPED(status)
&& WSTOPSIG(status) == SIGTRAP)
linuxthreads_breakpoint_pid = rpid;
else if (linuxthreads_breakpoint_pid)
linuxthreads_breakpoint_pid = 0;
return pid_to_ptid (rpid);
}
}
static void
linuxthreads_create_inferior (char *exec_file, char *allargs, char **env)
{
if (!exec_file && !exec_bfd)
{
error ("No executable file specified.\n\
Use the \"file\" or \"exec-file\" command.");
return;
}
push_target (&linuxthreads_ops);
linuxthreads_breakpoints_inserted = 1;
linuxthreads_breakpoint_last = -1;
linuxthreads_wait_last = -1;
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;
}
static void
linuxthreads_mourn_inferior (void)
{
if (linuxthreads_max)
{
int off = 0;
target_write_memory (linuxthreads_debug, (char *)&off, sizeof (off));
linuxthreads_discard_global_state ();
init_thread_list();
}
child_ops.to_mourn_inferior ();
unpush_target (&linuxthreads_ops);
}
static void
linuxthreads_kill (void)
{
int rpid;
int status;
if (PIDGET (inferior_ptid) == 0)
return;
if (linuxthreads_max && linuxthreads_manager_pid != 0)
{
inferior_ptid = pid_to_ptid (linuxthreads_manager_pid);
iterate_active_threads (kill_thread, 1);
}
kill_thread (PIDGET (inferior_ptid));
#if 0
if (doing_quit_force >= 0)
{
if (linuxthreads_max && linuxthreads_manager_pid != 0)
{
while ((rpid = waitpid (-1, &status, __WCLONE)) > 0)
if (!WIFEXITED(status))
kill_thread (rpid);
while ((rpid = waitpid (-1, &status, 0)) > 0)
if (!WIFEXITED(status))
kill_thread (rpid);
}
else
while ((rpid = waitpid (PIDGET (inferior_ptid), &status, 0)) > 0)
if (!WIFEXITED(status))
ptrace (PT_KILL, PIDGET (inferior_ptid), (PTRACE_ARG3_TYPE) 0, 0);
}
#endif
do
{
rpid = waitpid (-1, &status, __WCLONE | WNOHANG);
}
while (rpid > 0 || errno == EINTR);
do
{
rpid = waitpid (-1, &status, WNOHANG);
}
while (rpid > 0 || errno == EINTR);
linuxthreads_mourn_inferior ();
}
static int
linuxthreads_insert_breakpoint (CORE_ADDR addr, char *contents_cache)
{
if (linuxthreads_max && linuxthreads_manager_pid != 0)
{
linuxthreads_breakpoint_addr = addr;
iterate_active_threads (insert_breakpoint, 1);
insert_breakpoint (linuxthreads_manager_pid);
}
return child_ops.to_insert_breakpoint (addr, contents_cache);
}
static int
linuxthreads_remove_breakpoint (CORE_ADDR addr, char *contents_cache)
{
if (linuxthreads_max && linuxthreads_manager_pid != 0)
{
linuxthreads_breakpoint_addr = addr;
iterate_active_threads (remove_breakpoint, 1);
remove_breakpoint (linuxthreads_manager_pid);
}
return child_ops.to_remove_breakpoint (addr, contents_cache);
}
static int
linuxthreads_can_run (void)
{
return child_suppress_run;
}
static void
init_linuxthreads_ops (void)
{
linuxthreads_ops.to_shortname = "linuxthreads";
linuxthreads_ops.to_longname = "LINUX threads and pthread.";
linuxthreads_ops.to_doc = "LINUX threads and pthread support.";
linuxthreads_ops.to_attach = linuxthreads_attach;
linuxthreads_ops.to_detach = linuxthreads_detach;
linuxthreads_ops.to_resume = linuxthreads_resume;
linuxthreads_ops.to_wait = linuxthreads_wait;
linuxthreads_ops.to_kill = linuxthreads_kill;
linuxthreads_ops.to_can_run = linuxthreads_can_run;
linuxthreads_ops.to_stratum = thread_stratum;
linuxthreads_ops.to_insert_breakpoint = linuxthreads_insert_breakpoint;
linuxthreads_ops.to_remove_breakpoint = linuxthreads_remove_breakpoint;
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 (void)
{
struct sigaction sact;
sigset_t linuxthreads_wait_mask;
init_linuxthreads_ops ();
add_target (&linuxthreads_ops);
child_suppress_run = 1;
target_new_objfile_chain = target_new_objfile_hook;
target_new_objfile_hook = linuxthreads_new_objfile;
sact.sa_handler = sigchld_handler;
sigemptyset (&sact.sa_mask);
sact.sa_flags = 0;
sigaction (SIGCHLD, &sact, NULL);
sigemptyset (&linuxthreads_wait_mask);
sigaddset (&linuxthreads_wait_mask, SIGCHLD);
sigprocmask(SIG_BLOCK,
&linuxthreads_wait_mask,
&linuxthreads_block_mask);
sigdelset (&linuxthreads_block_mask, SIGCHLD);
}