| /* Async support for GDB, the GNU debugger. |
| Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2006 |
| Free Software Foundation, Inc. |
| |
| Original code from Apple Computer, Inc. |
| Modified by Nick Roberts <nickrob@snap.net.nz> |
| |
| This file is part of GDB. |
| |
| 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 2 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, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. */ |
| |
| #include "defs.h" |
| #include "gdbcmd.h" |
| #include "event-loop.h" |
| #include "inferior.h" |
| |
| #include "async-nat-sigthread.h" |
| |
| #include <string.h> |
| #include <sys/select.h> |
| |
| static FILE *sigthread_stderr_re = NULL; |
| static int sigthread_debugflag = 0; |
| static void* gdb_signal_thread (void *arg); |
| |
| /* A re-entrant version for use by the signal handling thread */ |
| |
| void |
| sigthread_debug_re (const char *fmt, ...) |
| { |
| va_list ap; |
| if (sigthread_debugflag) |
| { |
| va_start (ap, fmt); |
| fprintf (sigthread_stderr_re, "[%d sigthread]: ", getpid ()); |
| vfprintf (sigthread_stderr_re, fmt, ap); |
| va_end (ap); |
| fflush (sigthread_stderr_re); |
| } |
| } |
| |
| void |
| gdb_signal_thread_init (gdb_signal_thread_status *s) |
| { |
| s->transmit_fd = -1; |
| s->receive_fd = -1; |
| |
| s->inferior_pid = -1; |
| s->signal_thread = 0; |
| } |
| |
| void |
| gdb_signal_thread_create (gdb_signal_thread_status *s, int pid) |
| { |
| int fd[2]; |
| int ret; |
| |
| ret = pipe (fd); |
| |
| s->transmit_fd = fd[1]; |
| s->receive_fd = fd[0]; |
| |
| s->inferior_pid = pid; |
| |
| s->signal_thread = |
| gdb_thread_fork (gdb_signal_thread, s); |
| } |
| |
| void |
| gdb_signal_thread_destroy (gdb_signal_thread_status *s) |
| { |
| if (s->signal_thread != 0) |
| { |
| gdb_thread_kill (s->signal_thread); |
| } |
| |
| if (s->receive_fd > 0) |
| { |
| delete_file_handler (s->receive_fd); |
| close (s->receive_fd); |
| } |
| if (s->transmit_fd > 0) |
| close (s->transmit_fd); |
| |
| gdb_signal_thread_init (s); |
| } |
| |
| void |
| gdb_signal_thread_debug_status (FILE *f, WAITSTATUS status) |
| { |
| if (WIFEXITED (status)) |
| { |
| fprintf (f, "process exited with status %d\n", WEXITSTATUS (status)); |
| } |
| else if (WIFSIGNALED (status)) |
| { |
| fprintf (f, "process terminated with signal %d (%s)\n", |
| WTERMSIG (status), |
| target_signal_to_string (WTERMSIG (status))); |
| } |
| else if (WIFSTOPPED (status)) |
| { |
| fprintf (f, "process stopped with signal %d (%s)\n", WSTOPSIG (status), |
| target_signal_to_string (WSTOPSIG (status))); |
| } |
| else |
| { |
| fprintf (f, "unknown status value %d\n", status); |
| } |
| } |
| |
| static void* |
| gdb_signal_thread (void *arg) |
| { |
| gdb_signal_thread_status *s = (gdb_signal_thread_status *) arg; |
| |
| for (;;) |
| { |
| |
| gdb_signal_thread_message msg; |
| WAITSTATUS status = 0; |
| pid_t pid = 0; |
| |
| pthread_testcancel (); |
| |
| sigthread_debug_re |
| ("gdb_signal_thread: waiting for events for pid %d\n", |
| s->inferior_pid); |
| |
| pid = waitpid (s->inferior_pid, &status, 0); |
| |
| sigthread_debug_re |
| ("gdb_signal_thread: received event for pid %d\n", |
| s->inferior_pid); |
| |
| if ((pid < 0) && (errno == ECHILD)) |
| { |
| sigthread_debug_re |
| ("gdb_signal_thread: no children present; waiting for parent\n"); |
| for (;;) |
| { |
| pthread_testcancel (); |
| sched_yield (); |
| } |
| } |
| |
| if ((pid < 0) && (errno == EINTR)) |
| { |
| sigthread_debug_re |
| ("gdb_signal_thread: wait interrupted; continuing\n"); |
| continue; |
| } |
| |
| if (pid < 0) |
| { |
| fprintf (sigthread_stderr_re, |
| "gdb_signal_thread: unexpected error: %s\n", |
| strerror (errno)); |
| abort (); |
| } |
| |
| if (sigthread_debugflag) |
| { |
| sigthread_debug_re ("gdb_signal_thread: received event for pid %d: ", pid); |
| gdb_signal_thread_debug_status (sigthread_stderr_re, status); |
| } |
| |
| if (pid != s->inferior_pid) |
| { |
| fprintf (sigthread_stderr_re, |
| "gdb_signal_thread: event was for unexpected pid (got %d, was expecting %d)\n", |
| pid, s->inferior_pid); |
| abort (); |
| } |
| |
| msg.pid = pid; |
| msg.status = status; |
| write (s->transmit_fd, &msg, sizeof (msg)); |
| } |
| } |
| |
| void |
| gdb_pthread_kill (pthread_t pthread) |
| { |
| int ret; |
| |
| ret = pthread_cancel (pthread); |
| if (ret != 0) |
| { |
| warning ("Unable to cancel thread: %s (%d)", strerror (errno), errno); |
| } |
| |
| ret = pthread_join (pthread, NULL); |
| if (ret != 0) |
| { |
| warning ("Unable to join to canceled thread: %s (%d)", strerror (errno), |
| errno); |
| } |
| } |
| |
| pthread_t |
| gdb_pthread_fork (pthread_fn_t function, void *arg) |
| { |
| int result; |
| pthread_t pthread = 0; |
| pthread_attr_t attr; |
| |
| result = pthread_attr_init (&attr); |
| if (result != 0) |
| { |
| error ("Unable to initialize thread attributes: %s (%d)", |
| strerror (errno), errno); |
| } |
| |
| result = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE); |
| if (result != 0) |
| { |
| error ("Unable to initialize thread attributes: %s (%d)", |
| strerror (errno), errno); |
| } |
| |
| result = pthread_create (&pthread, &attr, function, arg); |
| if (result != 0) |
| { |
| error ("Unable to create thread: %s (%d)", strerror (errno), errno); |
| } |
| |
| result = pthread_attr_destroy (&attr); |
| if (result != 0) |
| { |
| warning ("Unable to deallocate thread attributes: %s (%d)", |
| strerror (errno), errno); |
| } |
| |
| return pthread; |
| } |
| |
| void |
| _initialize_gdb_nat_sigthread () |
| { |
| sigthread_stderr_re = fdopen (fileno (stderr), "w+"); |
| |
| add_setshow_boolean_cmd ("signals", no_class, |
| &sigthread_debugflag, _("\ |
| Set if printing signal thread debugging statements."), _("\ |
| Show if printing signal thread debugging statements."), NULL, |
| NULL, NULL, |
| &setdebuglist, &showdebuglist); |
| } |