X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fprocfs.c;h=8a68a8964976f485d487ca2231cb845310989872;hb=b83266a0e1813b7f4891d5d6b0ed6b7302a3fe98;hp=c2605d573857d616160e92354396a3d085e714e5;hpb=f5a8f1a6f38e8f87950258eb7a3f5417392458ac;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/procfs.c b/gdb/procfs.c index c2605d5738..8a68a89649 100644 --- a/gdb/procfs.c +++ b/gdb/procfs.c @@ -1,6 +1,7 @@ /* Machine independent support for SVR4 /proc (process file system) for GDB. - Copyright 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. - Written by Fred Fish at Cygnus Support. + Copyright 1991, 1992-98, 1999 Free Software Foundation, Inc. + Written by Fred Fish at Cygnus Support. Changes for sysv4.2mp procfs + compatibility by Geoffrey Noer at Cygnus Solutions. This file is part of GDB. @@ -16,7 +17,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* N O T E S @@ -24,8 +25,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ For information on the details of using /proc consult section proc(4) in the UNIX System V Release 4 System Administrator's Reference Manual. -The general register and floating point register sets are manipulated by -separate ioctl's. This file makes the assumption that if FP0_REGNUM is +The general register and floating point register sets are manipulated +separately. This file makes the assumption that if FP0_REGNUM is defined, then support for the floating point register set is desired, regardless of whether or not the actual target has floating point hardware. @@ -41,25 +42,119 @@ regardless of whether or not the actual target has floating point hardware. #include #include #include -#include +#include "gdb_string.h" #include #include #include -#include +#include "gdb_stat.h" #include "inferior.h" #include "target.h" #include "command.h" #include "gdbcore.h" -#include "thread.h" +#include "gdbthread.h" + +#if !defined(SYS_lwp_create) && defined(SYS_lwpcreate) +# define SYS_lwp_create SYS_lwpcreate +#endif + +#if !defined(SYS_lwp_exit) && defined(SYS_lwpexit) +# define SYS_lwp_exit SYS_lwpexit +#endif + +#if !defined(SYS_lwp_wait) && defined(SYS_lwpwait) +# define SYS_lwp_wait SYS_lwpwait +#endif + +#if !defined(SYS_lwp_self) && defined(SYS_lwpself) +# define SYS_lwp_self SYS_lwpself +#endif + +#if !defined(SYS_lwp_info) && defined(SYS_lwpinfo) +# define SYS_lwp_info SYS_lwpinfo +#endif + +#if !defined(SYS_lwp_private) && defined(SYS_lwpprivate) +# define SYS_lwp_private SYS_lwpprivate +#endif + +#if !defined(SYS_lwp_kill) && defined(SYS_lwpkill) +# define SYS_lwp_kill SYS_lwpkill +#endif + +#if !defined(SYS_lwp_suspend) && defined(SYS_lwpsuspend) +# define SYS_lwp_suspend SYS_lwpsuspend +#endif + +#if !defined(SYS_lwp_continue) && defined(SYS_lwpcontinue) +# define SYS_lwp_continue SYS_lwpcontinue +#endif + +/* the name of the proc status struct depends on the implementation */ +/* Wrap Light Weight Process member in THE_PR_LWP macro for clearer code */ +#ifndef HAVE_PSTATUS_T + typedef prstatus_t gdb_prstatus_t; +#define THE_PR_LWP(a) a +#else /* HAVE_PSTATUS_T */ + typedef pstatus_t gdb_prstatus_t; +#define THE_PR_LWP(a) a.pr_lwp +#if !defined(HAVE_PRRUN_T) && defined(HAVE_MULTIPLE_PROC_FDS) + /* Fallback definitions - for using configure information directly */ +#ifndef UNIXWARE +#define UNIXWARE 1 +#endif +#if !defined(PROCFS_USE_READ_WRITE) && !defined(HAVE_PROCFS_PIOCSET) +#define PROCFS_USE_READ_WRITE 1 +#endif +#endif /* !HAVE_PRRUN_T && HAVE_MULTIPLE_PROC_FDS */ +#endif /* HAVE_PSTATUS_T */ #define MAX_SYSCALLS 256 /* Maximum number of syscalls for table */ -#ifndef PROC_NAME_FMT -#define PROC_NAME_FMT "/proc/%05d" +/* proc name formats may vary depending on the proc implementation */ +#ifdef HAVE_MULTIPLE_PROC_FDS +# ifndef CTL_PROC_NAME_FMT +# define CTL_PROC_NAME_FMT "/proc/%d/ctl" +# define AS_PROC_NAME_FMT "/proc/%d/as" +# define MAP_PROC_NAME_FMT "/proc/%d/map" +# define STATUS_PROC_NAME_FMT "/proc/%d/status" +# endif +#else /* HAVE_MULTIPLE_PROC_FDS */ +# ifndef CTL_PROC_NAME_FMT +# define CTL_PROC_NAME_FMT "/proc/%05d" +# define AS_PROC_NAME_FMT "/proc/%05d" +# define MAP_PROC_NAME_FMT "/proc/%05d" +# define STATUS_PROC_NAME_FMT "/proc/%05d" +# endif +#endif /* HAVE_MULTIPLE_PROC_FDS */ + + +/* These #ifdefs are for sol2.x in particular. sol2.x has + both a "gregset_t" and a "prgregset_t", which have + similar uses but different layouts. sol2.x gdb tries to + use prgregset_t (and prfpregset_t) everywhere. */ + +#ifdef GDB_GREGSET_TYPE + typedef GDB_GREGSET_TYPE gdb_gregset_t; +#else + typedef gregset_t gdb_gregset_t; +#endif + +#ifdef GDB_FPREGSET_TYPE + typedef GDB_FPREGSET_TYPE gdb_fpregset_t; +#else + typedef fpregset_t gdb_fpregset_t; #endif -extern struct target_ops procfs_ops; /* Forward declaration */ + +#define MAX_PROC_NAME_SIZE sizeof("/proc/1234567890/status") + +struct target_ops procfs_ops; + +int procfs_suppress_run = 0; /* Non-zero if procfs should pretend not to + be a runnable target. Used by targets + that can sit atop procfs, such as solaris + thread support. */ #if 1 /* FIXME: Gross and ugly hack to resolve coredep.c global */ CORE_ADDR kernel_u_addr; @@ -72,6 +167,52 @@ CORE_ADDR kernel_u_addr; #define si_uid _data._proc._pdata._kill.uid #endif /* BROKEN_SIGINFO_H */ +/* Define structures for passing commands to /proc/pid/ctl file. Note that + while we create these for the PROCFS_USE_READ_WRITE world, we use them + and ignore the extra cmd int in other proc schemes. +*/ +/* generic ctl msg */ +struct proc_ctl { + int cmd; + long data; +}; + +/* set general registers */ +struct greg_ctl { + int cmd; + gdb_gregset_t gregset; +}; + +/* set fp registers */ +struct fpreg_ctl { + int cmd; + gdb_fpregset_t fpregset; +}; + +/* set signals to be traced */ +struct sig_ctl { + int cmd; + sigset_t sigset; +}; + +/* set faults to be traced */ +struct flt_ctl { + int cmd; + fltset_t fltset; +}; + +/* set system calls to be traced */ +struct sys_ctl { + int cmd; + sysset_t sysset; +}; + +/* set current signal to be traced */ +struct sigi_ctl { + int cmd; + siginfo_t siginfo; +}; + /* All access to the inferior, either one started by gdb or one that has been attached to, is controlled by an instance of a procinfo structure, defined below. Since gdb currently only handles one inferior at a time, @@ -84,35 +225,44 @@ CORE_ADDR kernel_u_addr; struct procinfo { struct procinfo *next; int pid; /* Process ID of inferior */ - int fd; /* File descriptor for /proc entry */ + int ctl_fd; /* File descriptor for /proc ctl file */ + int status_fd; /* File descriptor for /proc status file */ + int as_fd; /* File descriptor for /proc as file */ + int map_fd; /* File descriptor for /proc map file */ char *pathname; /* Pathname to /proc entry */ int had_event; /* poll/select says something happened */ int was_stopped; /* Nonzero if was stopped prior to attach */ int nopass_next_sigstop; /* Don't pass a sigstop on next resume */ +#ifdef HAVE_PRRUN_T prrun_t prrun; /* Control state when it is run */ - prstatus_t prstatus; /* Current process status info */ - gregset_t gregset; /* General register set */ - fpregset_t fpregset; /* Floating point register set */ - fltset_t fltset; /* Current traced hardware fault set */ - sigset_t trace; /* Current traced signal set */ - sysset_t exitset; /* Current traced system call exit set */ - sysset_t entryset; /* Current traced system call entry set */ - fltset_t saved_fltset; /* Saved traced hardware fault set */ - sigset_t saved_trace; /* Saved traced signal set */ - sigset_t saved_sighold; /* Saved held signal set */ - sysset_t saved_exitset; /* Saved traced system call exit set */ - sysset_t saved_entryset; /* Saved traced system call entry set */ +#endif + gdb_prstatus_t prstatus; /* Current process status info */ + struct greg_ctl gregset; /* General register set */ + struct fpreg_ctl fpregset; /* Floating point register set */ + struct flt_ctl fltset; /* Current traced hardware fault set */ + struct sig_ctl trace; /* Current traced signal set */ + struct sys_ctl exitset; /* Current traced system call exit set */ + struct sys_ctl entryset; /* Current traced system call entry set */ + struct sig_ctl saved_sighold; /* Saved held signal set */ + struct flt_ctl saved_fltset; /* Saved traced hardware fault set */ + struct sig_ctl saved_trace; /* Saved traced signal set */ + struct sys_ctl saved_exitset; /* Saved traced system call exit set */ + struct sys_ctl saved_entryset;/* Saved traced system call entry set */ + int num_syscall_handlers; /* Number of syscall trap handlers + currently installed */ + /* Pointer to list of syscall trap handlers */ + struct procfs_syscall_handler *syscall_handlers; + int saved_rtnval; /* return value and status for wait(), */ + int saved_statval; /* as supplied by a syscall handler. */ + int new_child; /* Non-zero if it's a new thread */ }; /* List of inferior process information */ static struct procinfo *procinfo_list = NULL; - static struct pollfd *poll_list; /* pollfds used for waiting on /proc */ static int num_poll_list = 0; /* Number of entries in poll_list */ -static int last_resume_pid = -1; /* Last pid used with procfs_resume */ - /* Much of the information used in the /proc interface, particularly for printing status information, is kept as tables of structures of the following form. These tables can be used to map numeric values to @@ -167,6 +317,15 @@ static struct trans pr_flag_table[] = #endif #if defined (PR_PCOMPAT) { PR_PCOMPAT, "PR_PCOMPAT", "Ptrace compatibility mode in effect" }, +#endif +#if defined (PR_MSACCT) + { PR_MSACCT, "PR_MSACCT", "Microstate accounting enabled" }, +#endif +#if defined (PR_BPTADJ) + { PR_BPTADJ, "PR_BPTADJ", "Breakpoint PC adjustment in effect" }, +#endif +#if defined (PR_ASLWP) + { PR_ASLWP, "PR_ASLWP", "Asynchronus signal LWP" }, #endif { 0, NULL, NULL } }; @@ -181,9 +340,6 @@ static struct trans pr_why_table[] = #if defined (PR_SIGNALLED) { PR_SIGNALLED, "PR_SIGNALLED", "Receipt of a traced signal" }, #endif -#if defined (PR_FAULTED) - { PR_FAULTED, "PR_FAULTED", "Incurred a traced hardware fault" }, -#endif #if defined (PR_SYSENTRY) { PR_SYSENTRY, "PR_SYSENTRY", "Entry to a traced system call" }, #endif @@ -193,8 +349,14 @@ static struct trans pr_why_table[] = #if defined (PR_JOBCONTROL) { PR_JOBCONTROL, "PR_JOBCONTROL", "Default job control stop signal action" }, #endif +#if defined (PR_FAULTED) + { PR_FAULTED, "PR_FAULTED", "Incurred a traced hardware fault" }, +#endif #if defined (PR_SUSPENDED) { PR_SUSPENDED, "PR_SUSPENDED", "Process suspended" }, +#endif +#if defined (PR_CHECKPOINT) + { PR_CHECKPOINT, "PR_CHECKPOINT", "(???)" }, #endif { 0, NULL, NULL } }; @@ -360,6 +522,50 @@ static char *syscall_table[MAX_SYSCALLS]; /* Prototypes for local functions */ +static void procfs_stop PARAMS ((void)); + +static int procfs_thread_alive PARAMS ((int)); + +static int procfs_can_run PARAMS ((void)); + +static void procfs_mourn_inferior PARAMS ((void)); + +static void procfs_fetch_registers PARAMS ((int)); + +static int procfs_wait PARAMS ((int, struct target_waitstatus *)); + +static void procfs_open PARAMS ((char *, int)); + +static void procfs_files_info PARAMS ((struct target_ops *)); + +static void procfs_prepare_to_store PARAMS ((void)); + +static void procfs_detach PARAMS ((char *, int)); + +static void procfs_attach PARAMS ((char *, int)); + +static void proc_set_exec_trap PARAMS ((void)); + +static void procfs_init_inferior PARAMS ((int)); + +static struct procinfo *create_procinfo PARAMS ((int)); + +static void procfs_store_registers PARAMS ((int)); + +static int procfs_xfer_memory PARAMS ((CORE_ADDR, char *, int, int, struct target_ops *)); + +static void procfs_kill_inferior PARAMS ((void)); + +static char *sigcodedesc PARAMS ((siginfo_t *)); + +static char *sigcodename PARAMS ((siginfo_t *)); + +static struct procinfo *wait_fd PARAMS ((void)); + +static void remove_fd PARAMS ((struct procinfo *)); + +static void add_fd PARAMS ((struct procinfo *)); + static void set_proc_siginfo PARAMS ((struct procinfo *, int)); static void init_syscall_table PARAMS ((void)); @@ -372,13 +578,17 @@ static char *errnoname PARAMS ((int)); static int proc_address_to_fd PARAMS ((struct procinfo *, CORE_ADDR, int)); -static int open_proc_file PARAMS ((int, struct procinfo *, int)); +static int open_proc_file PARAMS ((int, struct procinfo *, int, int)); static void close_proc_file PARAMS ((struct procinfo *)); +static void close_proc_file_cleanup PARAMS ((void*)); + +static struct cleanup *make_cleanup_close_proc_file PARAMS ((struct procinfo *)); + static void unconditionally_kill_inferior PARAMS ((struct procinfo *)); -static NORETURN void proc_init_failed PARAMS ((struct procinfo *, char *)) ATTR_NORETURN; +static NORETURN void proc_init_failed PARAMS ((struct procinfo *, char *, int)) ATTR_NORETURN; static void info_proc PARAMS ((char *, int)); @@ -410,18 +620,69 @@ static void procfs_create_inferior PARAMS ((char *, char *, char **)); static void procfs_notice_signals PARAMS ((int pid)); +static void notice_signals PARAMS ((struct procinfo *, struct sig_ctl *)); + static struct procinfo *find_procinfo PARAMS ((pid_t pid, int okfail)); +static int procfs_write_pcwstop PARAMS ((struct procinfo *)); +static int procfs_read_status PARAMS ((struct procinfo *)); +static void procfs_write_pckill PARAMS ((struct procinfo *)); + +typedef int syscall_func_t PARAMS ((struct procinfo *pi, int syscall_num, + int why, int *rtnval, int *statval)); + +static void procfs_set_syscall_trap PARAMS ((struct procinfo *pi, + int syscall_num, int flags, + syscall_func_t *func)); + +static void procfs_clear_syscall_trap PARAMS ((struct procinfo *pi, + int syscall_num, int errok)); + +#define PROCFS_SYSCALL_ENTRY 0x1 /* Trap on entry to sys call */ +#define PROCFS_SYSCALL_EXIT 0x2 /* Trap on exit from sys call */ + +static syscall_func_t procfs_exit_handler; + +static syscall_func_t procfs_exec_handler; + +#ifdef SYS_sproc +static syscall_func_t procfs_sproc_handler; +static syscall_func_t procfs_fork_handler; +#endif + +#ifdef SYS_lwp_create +static syscall_func_t procfs_lwp_creation_handler; +#endif + +static void modify_inherit_on_fork_flag PARAMS ((int fd, int flag)); +static void modify_run_on_last_close_flag PARAMS ((int fd, int flag)); + +/* */ + +struct procfs_syscall_handler +{ + int syscall_num; /* The number of the system call being handled */ + /* The function to be called */ + syscall_func_t *func; +}; + +static void procfs_resume PARAMS ((int pid, int step, + enum target_signal signo)); + +static void init_procfs_ops PARAMS ((void)); + /* External function prototypes that can't be easily included in any header file because the args are typedefs in system include files. */ -extern void supply_gregset PARAMS ((gregset_t *)); +extern void supply_gregset PARAMS ((gdb_gregset_t *)); -extern void fill_gregset PARAMS ((gregset_t *, int)); +extern void fill_gregset PARAMS ((gdb_gregset_t *, int)); -extern void supply_fpregset PARAMS ((fpregset_t *)); +#ifdef FP0_REGNUM +extern void supply_fpregset PARAMS ((gdb_fpregset_t *)); -extern void fill_fpregset PARAMS ((fpregset_t *, int)); +extern void fill_fpregset PARAMS ((gdb_fpregset_t *, int)); +#endif /* @@ -501,12 +762,32 @@ add_fd (pi) poll_list = (struct pollfd *) xrealloc (poll_list, (num_poll_list + 1) * sizeof (struct pollfd)); - poll_list[num_poll_list].fd = pi->fd; + poll_list[num_poll_list].fd = pi->ctl_fd; +#ifdef UNIXWARE + poll_list[num_poll_list].events = POLLWRNORM; +#else poll_list[num_poll_list].events = POLLPRI; +#endif num_poll_list++; } +/* + +LOCAL FUNCTION + + remove_fd -- Remove the fd from the poll/select list + +SYNOPSIS + + static void remove_fd (struct procinfo *); + +DESCRIPTION + + Remove the fd of the supplied procinfo from the list of fds used + for poll/select operations. + */ + static void remove_fd (pi) struct procinfo *pi; @@ -515,10 +796,10 @@ remove_fd (pi) for (i = 0; i < num_poll_list; i++) { - if (poll_list[i].fd == pi->fd) + if (poll_list[i].fd == pi->ctl_fd) { if (i != num_poll_list - 1) - memcpy (poll_list, poll_list + i + 1, + memcpy (poll_list + i, poll_list + i + 1, (num_poll_list - i - 1) * sizeof (struct pollfd)); num_poll_list--; @@ -534,25 +815,132 @@ remove_fd (pi) } } -#define LOSING_POLL unixware_sux +/* + +LOCAL FUNCTION + + procfs_read_status - get procfs fd status + +SYNOPSIS + + static int procfs_read_status (pi) struct procinfo *pi; + +DESCRIPTION + + Given a pointer to a procinfo struct, get the status of + the status_fd in the appropriate way. Returns 0 on failure, + 1 on success. + */ + +static int +procfs_read_status (pi) + struct procinfo *pi; +{ +#ifdef PROCFS_USE_READ_WRITE + if ((lseek (pi->status_fd, 0, SEEK_SET) < 0) || + (read (pi->status_fd, (char *) &pi->prstatus, + sizeof (gdb_prstatus_t)) != sizeof (gdb_prstatus_t))) +#else + if (ioctl (pi->status_fd, PIOCSTATUS, &pi->prstatus) < 0) +#endif + return 0; + else + return 1; +} + +/* + +LOCAL FUNCTION + + procfs_write_pcwstop - send a PCWSTOP to procfs fd + +SYNOPSIS + + static int procfs_write_pcwstop (pi) struct procinfo *pi; + +DESCRIPTION + + Given a pointer to a procinfo struct, send a PCWSTOP to + the ctl_fd in the appropriate way. Returns 0 on failure, + 1 on success. + */ + +static int +procfs_write_pcwstop (pi) + struct procinfo *pi; +{ +#ifdef PROCFS_USE_READ_WRITE + long cmd = PCWSTOP; + if (write (pi->ctl_fd, (char *) &cmd, sizeof (long)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCWSTOP, &pi->prstatus) < 0) +#endif + return 0; + else + return 1; +} + +/* + +LOCAL FUNCTION + + procfs_write_pckill - send a kill to procfs fd + +SYNOPSIS + + static void procfs_write_pckill (pi) struct procinfo *pi; + +DESCRIPTION + + Given a pointer to a procinfo struct, send a kill to + the ctl_fd in the appropriate way. Returns 0 on failure, + 1 on success. + */ + +static void +procfs_write_pckill (pi) + struct procinfo *pi; +{ +#ifdef PROCFS_USE_READ_WRITE + struct proc_ctl pctl; + pctl.cmd = PCKILL; + pctl.data = SIGKILL; + write (pi->ctl_fd, &pctl, sizeof (struct proc_ctl)); +#else + int signo = SIGKILL; + ioctl (pi->ctl_fd, PIOCKILL, &signo); +#endif +} static struct procinfo * wait_fd () { - struct procinfo *pi; + struct procinfo *pi, *next_pi; +#ifndef LOSING_POLL int num_fds; int i; +#endif set_sigint_trap (); /* Causes SIGINT to be passed on to the attached process. */ set_sigio_trap (); + wait_again: #ifndef LOSING_POLL - num_fds = poll (poll_list, num_poll_list, -1); -#else + while (1) + { + num_fds = poll (poll_list, num_poll_list, -1); + if (num_fds > 0) + break; + if (num_fds < 0 && errno == EINTR) + continue; + print_sys_errmsg ("poll failed", errno); + error ("Poll failed, returned %d", num_fds); + } +#else /* LOSING_POLL */ pi = current_procinfo; - while (ioctl (pi->fd, PIOCWSTOP, &pi->prstatus) < 0) + while (!procfs_write_pcwstop (pi)) { if (errno == ENOENT) { @@ -563,43 +951,53 @@ wait_fd () else if (errno != EINTR) { print_sys_errmsg (pi->pathname, errno); - error ("PIOCWSTOP failed"); + error ("procfs_write_pcwstop failed"); } } pi->had_event = 1; -#endif +#endif /* LOSING_POLL */ clear_sigint_trap (); clear_sigio_trap (); #ifndef LOSING_POLL - if (num_fds <= 0) - { - print_sys_errmsg ("poll failed\n", errno); - error ("Poll failed, returned %d", num_fds); - } - for (i = 0; i < num_poll_list && num_fds > 0; i++) { - if ((poll_list[i].revents & (POLLPRI|POLLERR|POLLHUP|POLLNVAL)) == 0) + if (0 == (poll_list[i].revents & + (POLLWRNORM | POLLPRI | POLLERR | POLLHUP | POLLNVAL))) continue; - for (pi = procinfo_list; pi; pi = pi->next) + for (pi = procinfo_list; pi; pi = next_pi) { - if (poll_list[i].fd == pi->fd) + next_pi = pi->next; + if (poll_list[i].fd == pi->ctl_fd) { - if (ioctl (pi->fd, PIOCSTATUS, &pi->prstatus) < 0) - { - print_sys_errmsg (pi->pathname, errno); - error ("PIOCSTATUS failed"); - } num_fds--; + if ((poll_list[i].revents & POLLHUP) != 0 || + !procfs_read_status(pi)) + { /* The LWP has apparently terminated. */ + if (num_poll_list <= 1) + { + pi->prstatus.pr_flags = 0; + pi->had_event = 1; + break; + } + if (info_verbose) + printf_filtered ("LWP %d exited.\n", + (pi->pid >> 16) & 0xffff); + close_proc_file (pi); + i--; /* don't skip deleted entry */ + if (num_fds != 0) + break; /* already another event to process */ + else + goto wait_again; /* wait for another event */ + } pi->had_event = 1; break; } } if (!pi) - error ("procfs_wait: Couldn't find procinfo for fd %d\n", + error ("wait_fd: Couldn't find procinfo for fd %d\n", poll_list[i].fd); } #endif /* LOSING_POLL */ @@ -776,18 +1174,15 @@ syscallname (syscallnum) int syscallnum; { static char locbuf[32]; - char *rtnval; - if (syscallnum >= 0 && syscallnum < MAX_SYSCALLS) - { - rtnval = syscall_table[syscallnum]; - } + if (syscallnum >= 0 && syscallnum < MAX_SYSCALLS + && syscall_table[syscallnum] != NULL) + return syscall_table[syscallnum]; else { sprintf (locbuf, "syscall %u", syscallnum); - rtnval = locbuf; + return locbuf; } - return (rtnval); } /* @@ -971,6 +1366,9 @@ init_syscall_table () #if defined (SYS_sys3b) syscall_table[SYS_sys3b] = "sys3b"; #endif +#if defined (SYS_sysi86) + syscall_table[SYS_sysi86] = "sysi86"; +#endif #if defined (SYS_acct) syscall_table[SYS_acct] = "acct"; #endif @@ -1187,72 +1585,316 @@ init_syscall_table () #if defined (SYS_sproc) syscall_table[SYS_sproc] = "sproc"; #endif -} - -/* - -LOCAL FUNCTION - - procfs_kill_inferior - kill any currently inferior - -SYNOPSIS - - void procfs_kill_inferior (void) - -DESCRIPTION - - Kill any current inferior. - -NOTES - - Kills even attached inferiors. Presumably the user has already - been prompted that the inferior is an attached one rather than - one started by gdb. (FIXME?) - -*/ - -static void -procfs_kill_inferior () -{ - target_mourn_inferior (); -} - -/* - -LOCAL FUNCTION - - unconditionally_kill_inferior - terminate the inferior - -SYNOPSIS - - static void unconditionally_kill_inferior (struct procinfo *) - -DESCRIPTION - - Kill the specified inferior. - -NOTE - - A possibly useful enhancement would be to first try sending - the inferior a terminate signal, politely asking it to commit - suicide, before we murder it (we could call that - politely_kill_inferior()). - -*/ - -static void -unconditionally_kill_inferior (pi) - struct procinfo *pi; -{ - int signo; - int ppid; - - ppid = pi->prstatus.pr_ppid; +#if defined (SYS_keyctl) + syscall_table[SYS_keyctl] = "keyctl"; +#endif +#if defined (SYS_secsys) + syscall_table[SYS_secsys] = "secsys"; +#endif +#if defined (SYS_filepriv) + syscall_table[SYS_filepriv] = "filepriv"; +#endif +#if defined (SYS_procpriv) + syscall_table[SYS_procpriv] = "procpriv"; +#endif +#if defined (SYS_devstat) + syscall_table[SYS_devstat] = "devstat"; +#endif +#if defined (SYS_aclipc) + syscall_table[SYS_aclipc] = "aclipc"; +#endif +#if defined (SYS_fdevstat) + syscall_table[SYS_fdevstat] = "fdevstat"; +#endif +#if defined (SYS_flvlfile) + syscall_table[SYS_flvlfile] = "flvlfile"; +#endif +#if defined (SYS_lvlfile) + syscall_table[SYS_lvlfile] = "lvlfile"; +#endif +#if defined (SYS_lvlequal) + syscall_table[SYS_lvlequal] = "lvlequal"; +#endif +#if defined (SYS_lvlproc) + syscall_table[SYS_lvlproc] = "lvlproc"; +#endif +#if defined (SYS_lvlipc) + syscall_table[SYS_lvlipc] = "lvlipc"; +#endif +#if defined (SYS_acl) + syscall_table[SYS_acl] = "acl"; +#endif +#if defined (SYS_auditevt) + syscall_table[SYS_auditevt] = "auditevt"; +#endif +#if defined (SYS_auditctl) + syscall_table[SYS_auditctl] = "auditctl"; +#endif +#if defined (SYS_auditdmp) + syscall_table[SYS_auditdmp] = "auditdmp"; +#endif +#if defined (SYS_auditlog) + syscall_table[SYS_auditlog] = "auditlog"; +#endif +#if defined (SYS_auditbuf) + syscall_table[SYS_auditbuf] = "auditbuf"; +#endif +#if defined (SYS_lvldom) + syscall_table[SYS_lvldom] = "lvldom"; +#endif +#if defined (SYS_lvlvfs) + syscall_table[SYS_lvlvfs] = "lvlvfs"; +#endif +#if defined (SYS_mkmld) + syscall_table[SYS_mkmld] = "mkmld"; +#endif +#if defined (SYS_mldmode) + syscall_table[SYS_mldmode] = "mldmode"; +#endif +#if defined (SYS_secadvise) + syscall_table[SYS_secadvise] = "secadvise"; +#endif +#if defined (SYS_online) + syscall_table[SYS_online] = "online"; +#endif +#if defined (SYS_setitimer) + syscall_table[SYS_setitimer] = "setitimer"; +#endif +#if defined (SYS_getitimer) + syscall_table[SYS_getitimer] = "getitimer"; +#endif +#if defined (SYS_gettimeofday) + syscall_table[SYS_gettimeofday] = "gettimeofday"; +#endif +#if defined (SYS_settimeofday) + syscall_table[SYS_settimeofday] = "settimeofday"; +#endif +#if defined (SYS_lwp_create) + syscall_table[SYS_lwp_create] = "_lwp_create"; +#endif +#if defined (SYS_lwp_exit) + syscall_table[SYS_lwp_exit] = "_lwp_exit"; +#endif +#if defined (SYS_lwp_wait) + syscall_table[SYS_lwp_wait] = "_lwp_wait"; +#endif +#if defined (SYS_lwp_self) + syscall_table[SYS_lwp_self] = "_lwp_self"; +#endif +#if defined (SYS_lwp_info) + syscall_table[SYS_lwp_info] = "_lwp_info"; +#endif +#if defined (SYS_lwp_private) + syscall_table[SYS_lwp_private] = "_lwp_private"; +#endif +#if defined (SYS_processor_bind) + syscall_table[SYS_processor_bind] = "processor_bind"; +#endif +#if defined (SYS_processor_exbind) + syscall_table[SYS_processor_exbind] = "processor_exbind"; +#endif +#if defined (SYS_prepblock) + syscall_table[SYS_prepblock] = "prepblock"; +#endif +#if defined (SYS_block) + syscall_table[SYS_block] = "block"; +#endif +#if defined (SYS_rdblock) + syscall_table[SYS_rdblock] = "rdblock"; +#endif +#if defined (SYS_unblock) + syscall_table[SYS_unblock] = "unblock"; +#endif +#if defined (SYS_cancelblock) + syscall_table[SYS_cancelblock] = "cancelblock"; +#endif +#if defined (SYS_pread) + syscall_table[SYS_pread] = "pread"; +#endif +#if defined (SYS_pwrite) + syscall_table[SYS_pwrite] = "pwrite"; +#endif +#if defined (SYS_truncate) + syscall_table[SYS_truncate] = "truncate"; +#endif +#if defined (SYS_ftruncate) + syscall_table[SYS_ftruncate] = "ftruncate"; +#endif +#if defined (SYS_lwp_kill) + syscall_table[SYS_lwp_kill] = "_lwp_kill"; +#endif +#if defined (SYS_sigwait) + syscall_table[SYS_sigwait] = "sigwait"; +#endif +#if defined (SYS_fork1) + syscall_table[SYS_fork1] = "fork1"; +#endif +#if defined (SYS_forkall) + syscall_table[SYS_forkall] = "forkall"; +#endif +#if defined (SYS_modload) + syscall_table[SYS_modload] = "modload"; +#endif +#if defined (SYS_moduload) + syscall_table[SYS_moduload] = "moduload"; +#endif +#if defined (SYS_modpath) + syscall_table[SYS_modpath] = "modpath"; +#endif +#if defined (SYS_modstat) + syscall_table[SYS_modstat] = "modstat"; +#endif +#if defined (SYS_modadm) + syscall_table[SYS_modadm] = "modadm"; +#endif +#if defined (SYS_getksym) + syscall_table[SYS_getksym] = "getksym"; +#endif +#if defined (SYS_lwp_suspend) + syscall_table[SYS_lwp_suspend] = "_lwp_suspend"; +#endif +#if defined (SYS_lwp_continue) + syscall_table[SYS_lwp_continue] = "_lwp_continue"; +#endif +#if defined (SYS_priocntllst) + syscall_table[SYS_priocntllst] = "priocntllst"; +#endif +#if defined (SYS_sleep) + syscall_table[SYS_sleep] = "sleep"; +#endif +#if defined (SYS_lwp_sema_wait) + syscall_table[SYS_lwp_sema_wait] = "_lwp_sema_wait"; +#endif +#if defined (SYS_lwp_sema_post) + syscall_table[SYS_lwp_sema_post] = "_lwp_sema_post"; +#endif +#if defined (SYS_lwp_sema_trywait) + syscall_table[SYS_lwp_sema_trywait] = "lwp_sema_trywait"; +#endif +#if defined(SYS_fstatvfs64) + syscall_table[SYS_fstatvfs64] = "fstatvfs64"; +#endif +#if defined(SYS_statvfs64) + syscall_table[SYS_statvfs64] = "statvfs64"; +#endif +#if defined(SYS_ftruncate64) + syscall_table[SYS_ftruncate64] = "ftruncate64"; +#endif +#if defined(SYS_truncate64) + syscall_table[SYS_truncate64] = "truncate64"; +#endif +#if defined(SYS_getrlimit64) + syscall_table[SYS_getrlimit64] = "getrlimit64"; +#endif +#if defined(SYS_setrlimit64) + syscall_table[SYS_setrlimit64] = "setrlimit64"; +#endif +#if defined(SYS_lseek64) + syscall_table[SYS_lseek64] = "lseek64"; +#endif +#if defined(SYS_mmap64) + syscall_table[SYS_mmap64] = "mmap64"; +#endif +#if defined(SYS_pread64) + syscall_table[SYS_pread64] = "pread64"; +#endif +#if defined(SYS_creat64) + syscall_table[SYS_creat64] = "creat64"; +#endif +#if defined(SYS_dshmsys) + syscall_table[SYS_dshmsys] = "dshmsys"; +#endif +#if defined(SYS_invlpg) + syscall_table[SYS_invlpg] = "invlpg"; +#endif +#if defined(SYS_cg_ids) + syscall_table[SYS_cg_ids] = "cg_ids"; +#endif +#if defined(SYS_cg_processors) + syscall_table[SYS_cg_processors] = "cg_processors"; +#endif +#if defined(SYS_cg_info) + syscall_table[SYS_cg_info] = "cg_info"; +#endif +#if defined(SYS_cg_bind) + syscall_table[SYS_cg_bind] = "cg_bind"; +#endif +#if defined(SYS_cg_current) + syscall_table[SYS_cg_current] = "cg_current"; +#endif +#if defined(SYS_cg_memloc) + syscall_table[SYS_cg_memloc] = "cg_memloc"; +#endif +} + +/* + +LOCAL FUNCTION + + procfs_kill_inferior - kill any current inferior + +SYNOPSIS + + void procfs_kill_inferior (void) + +DESCRIPTION + + Kill any current inferior. + +NOTES + + Kills even attached inferiors. Presumably the user has already + been prompted that the inferior is an attached one rather than + one started by gdb. (FIXME?) + +*/ + +static void +procfs_kill_inferior () +{ + target_mourn_inferior (); +} + +/* + +LOCAL FUNCTION + + unconditionally_kill_inferior - terminate the inferior + +SYNOPSIS + + static void unconditionally_kill_inferior (struct procinfo *) + +DESCRIPTION + + Kill the specified inferior. + +NOTE + + A possibly useful enhancement would be to first try sending + the inferior a terminate signal, politely asking it to commit + suicide, before we murder it (we could call that + politely_kill_inferior()). + +*/ - signo = SIGKILL; +static void +unconditionally_kill_inferior (pi) + struct procinfo *pi; +{ + int ppid; + struct proc_ctl pctl; + + ppid = pi->prstatus.pr_ppid; +#ifdef PROCFS_NEED_CLEAR_CURSIG_FOR_KILL + /* Alpha OSF/1-3.x procfs needs a clear of the current signal + before the PIOCKILL, otherwise it might generate a corrupted core + file for the inferior. */ + ioctl (pi->ctl_fd, PIOCSSIG, NULL); +#endif #ifdef PROCFS_NEED_PIOCSSIG_FOR_KILL - /* Alpha OSF/1 procfs needs a PIOCSSIG call with a SIGKILL signal + /* Alpha OSF/1-2.x procfs needs a PIOCSSIG call with a SIGKILL signal to kill the inferior, otherwise it might remain stopped with a pending SIGKILL. We do not check the result of the PIOCSSIG, the inferior might have @@ -1261,16 +1903,16 @@ unconditionally_kill_inferior (pi) struct siginfo newsiginfo; memset ((char *) &newsiginfo, 0, sizeof (newsiginfo)); - newsiginfo.si_signo = signo; + newsiginfo.si_signo = SIGKILL; newsiginfo.si_code = 0; newsiginfo.si_errno = 0; newsiginfo.si_pid = getpid (); newsiginfo.si_uid = getuid (); - ioctl (pi->fd, PIOCSSIG, &newsiginfo); + ioctl (pi->ctl_fd, PIOCSSIG, &newsiginfo); } -#else - ioctl (pi->fd, PIOCKILL, &signo); -#endif +#else /* PROCFS_NEED_PIOCSSIG_FOR_KILL */ + procfs_write_pckill (pi); +#endif /* PROCFS_NEED_PIOCSSIG_FOR_KILL */ close_proc_file (pi); @@ -1321,15 +1963,15 @@ procfs_xfer_memory (memaddr, myaddr, len, dowrite, target) pi = current_procinfo; - if (lseek(pi->fd, (off_t) memaddr, 0) == (off_t) memaddr) + if (lseek(pi->as_fd, (off_t) memaddr, SEEK_SET) == (off_t) memaddr) { if (dowrite) { - nbytes = write (pi->fd, myaddr, len); + nbytes = write (pi->as_fd, myaddr, len); } else { - nbytes = read (pi->fd, myaddr, len); + nbytes = read (pi->as_fd, myaddr, len); } if (nbytes < 0) { @@ -1382,15 +2024,32 @@ procfs_store_registers (regno) int regno; { struct procinfo *pi; +#ifdef PROCFS_USE_READ_WRITE + struct greg_ctl greg; + struct fpreg_ctl fpreg; +#endif pi = current_procinfo; +#ifdef PROCFS_USE_READ_WRITE + if (regno != -1) + { + procfs_read_status (pi); + memcpy ((char *) &greg.gregset, + (char *) &pi->prstatus.pr_lwp.pr_context.uc_mcontext.gregs, + sizeof (gdb_gregset_t)); + } + fill_gregset (&greg.gregset, regno); + greg.cmd = PCSREG; + write (pi->ctl_fd, &greg, sizeof (greg)); +#else /* PROCFS_USE_READ_WRITE */ if (regno != -1) { - ioctl (pi->fd, PIOCGREG, &pi->gregset); + ioctl (pi->ctl_fd, PIOCGREG, &pi->gregset.gregset); } - fill_gregset (&pi->gregset, regno); - ioctl (pi->fd, PIOCSREG, &pi->gregset); + fill_gregset (&pi->gregset.gregset, regno); + ioctl (pi->ctl_fd, PIOCSREG, &pi->gregset.gregset); +#endif /* PROCFS_USE_READ_WRITE */ #if defined (FP0_REGNUM) @@ -1398,12 +2057,25 @@ procfs_store_registers (regno) target has floating point hardware. Since we ignore the returned value, we'll never know whether it worked or not anyway. */ +#ifdef PROCFS_USE_READ_WRITE + if (regno != -1) + { + procfs_read_status (pi); + memcpy ((char *) &fpreg.fpregset, + (char *) &pi->prstatus.pr_lwp.pr_context.uc_mcontext.fpregs, + sizeof (gdb_fpregset_t)); + } + fill_fpregset (&fpreg.fpregset, regno); + fpreg.cmd = PCSFPREG; + write (pi->ctl_fd, &fpreg, sizeof (fpreg)); +#else /* PROCFS_USE_READ_WRITE */ if (regno != -1) { - ioctl (pi->fd, PIOCGFPREG, &pi->fpregset); + ioctl (pi->ctl_fd, PIOCGFPREG, &pi->fpregset.fpregset); } - fill_fpregset (&pi->fpregset, regno); - ioctl (pi->fd, PIOCSFPREG, &pi->fpregset); + fill_fpregset (&pi->fpregset.fpregset, regno); + ioctl (pi->ctl_fd, PIOCSFPREG, &pi->fpregset.fpregset); +#endif /* PROCFS_USE_READ_WRITE */ #endif /* FP0_REGNUM */ @@ -1413,17 +2085,17 @@ procfs_store_registers (regno) LOCAL FUNCTION - create_procinfo - initialize access to a /proc entry + init_procinfo - setup a procinfo struct and connect it to a process SYNOPSIS - struct procinfo * create_procinfo (int pid) + struct procinfo * init_procinfo (int pid) DESCRIPTION Allocate a procinfo structure, open the /proc file and then set up the set of signals and faults that are to be traced. Returns a pointer to - the new procinfo structure. + the new procinfo structure. NOTES @@ -1433,19 +2105,22 @@ NOTES */ static struct procinfo * -create_procinfo (pid) +init_procinfo (pid, kill) int pid; + int kill; { - struct procinfo *pi; + struct procinfo *pi = (struct procinfo *) + xmalloc (sizeof (struct procinfo)); + struct sig_ctl sctl; + struct flt_ctl fctl; - pi = find_procinfo (pid, 1); - if (pi != NULL) - return pi; /* All done! It already exists */ + memset ((char *) pi, 0, sizeof (*pi)); + if (!open_proc_file (pid, pi, O_RDWR, 1)) + proc_init_failed (pi, "can't open process file", kill); - pi = (struct procinfo *) xmalloc (sizeof (struct procinfo)); + /* open_proc_file may modify pid. */ - if (!open_proc_file (pid, pi, O_RDWR)) - proc_init_failed (pi, "can't open process file"); + pid = pi -> pid; /* Add new process to process info list */ @@ -1454,21 +2129,46 @@ create_procinfo (pid) add_fd (pi); /* Add to list for poll/select */ + /* Remember some things about the inferior that we will, or might, change + so that we can restore them when we detach. */ +#ifdef UNIXWARE + memcpy ((char *) &pi->saved_trace.sigset, + (char *) &pi->prstatus.pr_sigtrace, sizeof (sigset_t)); + memcpy ((char *) &pi->saved_fltset.fltset, + (char *) &pi->prstatus.pr_flttrace, sizeof (fltset_t)); + memcpy ((char *) &pi->saved_entryset.sysset, + (char *) &pi->prstatus.pr_sysentry, sizeof (sysset_t)); + memcpy ((char *) &pi->saved_exitset.sysset, + (char *) &pi->prstatus.pr_sysexit, sizeof (sysset_t)); + + /* Set up trace and fault sets, as gdb expects them. */ + + prfillset (&sctl.sigset); + notice_signals (pi, &sctl); + prfillset (&fctl.fltset); + prdelset (&fctl.fltset, FLTPAGE); + +#else /* ! UNIXWARE */ + ioctl (pi->ctl_fd, PIOCGTRACE, &pi->saved_trace.sigset); + ioctl (pi->ctl_fd, PIOCGHOLD, &pi->saved_sighold.sigset); + ioctl (pi->ctl_fd, PIOCGFAULT, &pi->saved_fltset.fltset); + ioctl (pi->ctl_fd, PIOCGENTRY, &pi->saved_entryset.sysset); + ioctl (pi->ctl_fd, PIOCGEXIT, &pi->saved_exitset.sysset); + + /* Set up trace and fault sets, as gdb expects them. */ + memset ((char *) &pi->prrun, 0, sizeof (pi->prrun)); prfillset (&pi->prrun.pr_trace); procfs_notice_signals (pid); prfillset (&pi->prrun.pr_fault); prdelset (&pi->prrun.pr_fault, FLTPAGE); - #ifdef PROCFS_DONT_TRACE_FAULTS premptyset (&pi->prrun.pr_fault); #endif +#endif /* UNIXWARE */ - if (ioctl (pi->fd, PIOCWSTOP, &pi->prstatus) < 0) - proc_init_failed (pi, "PIOCWSTOP failed"); - - if (ioctl (pi->fd, PIOCSFAULT, &pi->prrun.pr_fault) < 0) - proc_init_failed (pi, "PIOCSFAULT failed"); + if (!procfs_read_status (pi)) + proc_init_failed (pi, "procfs_read_status failed", kill); return pi; } @@ -1477,20 +2177,17 @@ create_procinfo (pid) LOCAL FUNCTION - procfs_init_inferior - initialize target vector and access to a - /proc entry + create_procinfo - initialize access to a /proc entry SYNOPSIS - void procfs_init_inferior (int pid) + struct procinfo * create_procinfo (int pid) DESCRIPTION - When gdb starts an inferior, this function is called in the parent - process immediately after the fork. It waits for the child to stop - on the return from the exec system call (the child itself takes care - of ensuring that this is set up), then sets up the set of signals - and faults that are to be traced. + Allocate a procinfo structure, open the /proc file and then set up the + set of signals and faults that are to be traced. Returns a pointer to + the new procinfo structure. NOTES @@ -1499,2048 +2196,3263 @@ NOTES */ -static void -procfs_init_inferior (pid) +static struct procinfo * +create_procinfo (pid) int pid; { - push_target (&procfs_ops); + struct procinfo *pi; + struct sig_ctl sctl; + struct flt_ctl fctl; + + pi = find_procinfo (pid, 1); + if (pi != NULL) + return pi; /* All done! It already exists */ - create_procinfo (pid); - add_thread (pid); /* Setup initial thread */ + pi = init_procinfo (pid, 1); -#ifdef START_INFERIOR_TRAPS_EXPECTED - startup_inferior (START_INFERIOR_TRAPS_EXPECTED); +#ifndef UNIXWARE +/* A bug in Solaris (2.5 at least) causes PIOCWSTOP to hang on LWPs that are + already stopped, even if they all have PR_ASYNC set. */ + if (!(pi->prstatus.pr_flags & PR_STOPPED)) +#endif + if (!procfs_write_pcwstop (pi)) + proc_init_failed (pi, "procfs_write_pcwstop failed", 1); + +#ifdef PROCFS_USE_READ_WRITE + fctl.cmd = PCSFAULT; + if (write (pi->ctl_fd, (char *) &fctl, sizeof (struct flt_ctl)) < 0) + proc_init_failed (pi, "PCSFAULT failed", 1); #else - /* One trap to exec the shell, one to exec the program being debugged. */ - startup_inferior (2); + if (ioctl (pi->ctl_fd, PIOCSFAULT, &pi->prrun.pr_fault) < 0) + proc_init_failed (pi, "PIOCSFAULT failed", 1); #endif + + return pi; } /* -GLOBAL FUNCTION +LOCAL FUNCTION - procfs_notice_signals + procfs_exit_handler - handle entry into the _exit syscall SYNOPSIS - static void procfs_notice_signals (int pid); + int procfs_exit_handler (pi, syscall_num, why, rtnvalp, statvalp) DESCRIPTION - When the user changes the state of gdb's signal handling via the - "handle" command, this function gets called to see if any change - in the /proc interface is required. It is also called internally - by other /proc interface functions to initialize the state of - the traced signal set. + This routine is called when an inferior process enters the _exit() + system call. It continues the process, and then collects the exit + status and pid which are returned in *statvalp and *rtnvalp. After + that it returns non-zero to indicate that procfs_wait should wake up. + +NOTES + There is probably a better way to do this. - One thing it does is that signals for which the state is "nostop", - "noprint", and "pass", have their trace bits reset in the pr_trace - field, so that they are no longer traced. This allows them to be - delivered directly to the inferior without the debugger ever being - involved. */ -static void -procfs_notice_signals (pid) - int pid; +static int +procfs_exit_handler (pi, syscall_num, why, rtnvalp, statvalp) + struct procinfo *pi; + int syscall_num; + int why; + int *rtnvalp; + int *statvalp; { - int signo; - struct procinfo *pi; + struct procinfo *temp_pi, *next_pi; + struct proc_ctl pctl; - pi = find_procinfo (pid, 0); +#ifdef UNIXWARE + pctl.cmd = PCRUN; + pctl.data = PRCFAULT; +#else + pi->prrun.pr_flags = PRCFAULT; +#endif - for (signo = 0; signo < NSIG; signo++) +#ifdef PROCFS_USE_READ_WRITE + if (write (pi->ctl_fd, (char *)&pctl, sizeof (struct proc_ctl)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) +#endif + perror_with_name (pi->pathname); + + if (attach_flag) { - if (signal_stop_state (target_signal_from_host (signo)) == 0 && - signal_print_state (target_signal_from_host (signo)) == 0 && - signal_pass_state (target_signal_from_host (signo)) == 1) - { - prdelset (&pi->prrun.pr_trace, signo); - } - else - { - praddset (&pi->prrun.pr_trace, signo); - } + /* Claim it exited (don't call wait). */ + if (info_verbose) + printf_filtered ("(attached process has exited)\n"); + *statvalp = 0; + *rtnvalp = inferior_pid; + } + else + { + *rtnvalp = wait (statvalp); + if (*rtnvalp >= 0) + *rtnvalp = pi->pid; } - if (ioctl (pi->fd, PIOCSTRACE, &pi->prrun.pr_trace)) + + /* Close ALL open proc file handles, + except the one that called SYS_exit. */ + for (temp_pi = procinfo_list; temp_pi; temp_pi = next_pi) { - print_sys_errmsg ("PIOCSTRACE failed", errno); + next_pi = temp_pi->next; + if (temp_pi == pi) + continue; /* Handled below */ + close_proc_file (temp_pi); } + return 1; } /* LOCAL FUNCTION - proc_set_exec_trap -- arrange for exec'd child to halt at startup + procfs_exec_handler - handle exit from the exec family of syscalls SYNOPSIS - void proc_set_exec_trap (void) + int procfs_exec_handler (pi, syscall_num, why, rtnvalp, statvalp) DESCRIPTION - This function is called in the child process when starting up - an inferior, prior to doing the exec of the actual inferior. - It sets the child process's exitset to make exit from the exec - system call an event of interest to stop on, and then simply - returns. The child does the exec, the system call returns, and - the child stops at the first instruction, ready for the gdb - parent process to take control of it. + This routine is called when an inferior process is about to finish any + of the exec() family of system calls. It pretends that we got a + SIGTRAP (for compatibility with ptrace behavior), and returns non-zero + to tell procfs_wait to wake up. -NOTE +NOTES + This need for compatibility with ptrace is questionable. In the + future, it shouldn't be necessary. - We need to use all local variables since the child may be sharing - it's data space with the parent, if vfork was used rather than - fork. + */ - Also note that we want to turn off the inherit-on-fork flag in - the child process so that any grand-children start with all - tracing flags cleared. - */ - -static void -proc_set_exec_trap () +static int +procfs_exec_handler (pi, syscall_num, why, rtnvalp, statvalp) + struct procinfo *pi; + int syscall_num; + int why; + int *rtnvalp; + int *statvalp; { - sysset_t exitset; - sysset_t entryset; - auto char procname[32]; - int fd; - - sprintf (procname, PROC_NAME_FMT, getpid ()); - if ((fd = open (procname, O_RDWR)) < 0) - { - perror (procname); - gdb_flush (gdb_stderr); - _exit (127); - } - premptyset (&exitset); - premptyset (&entryset); + *statvalp = (SIGTRAP << 8) | 0177; -#ifdef PIOCSSPCACT - /* Under Alpha OSF/1 we have to use a PIOCSSPCACT ioctl to trace - exits from exec system calls because of the user level loader. */ - { - int prfs_flags; + return 1; +} - if (ioctl (fd, PIOCGSPCACT, &prfs_flags) < 0) - { - perror (procname); - gdb_flush (gdb_stderr); - _exit (127); - } - prfs_flags |= PRFS_STOPEXEC; - if (ioctl (fd, PIOCSSPCACT, &prfs_flags) < 0) - { - perror (procname); - gdb_flush (gdb_stderr); - _exit (127); - } - } -#else - /* GW: Rationale... - Not all systems with /proc have all the exec* syscalls with the same - names. On the SGI, for example, there is no SYS_exec, but there - *is* a SYS_execv. So, we try to account for that. */ +#if defined(SYS_sproc) && !defined(UNIXWARE) +/* IRIX lwp creation system call */ -#ifdef SYS_exec - praddset (&exitset, SYS_exec); -#endif -#ifdef SYS_execve - praddset (&exitset, SYS_execve); -#endif -#ifdef SYS_execv - praddset (&exitset, SYS_execv); -#endif +/* - if (ioctl (fd, PIOCSEXIT, &exitset) < 0) - { - perror (procname); - gdb_flush (gdb_stderr); - _exit (127); - } -#endif +LOCAL FUNCTION + + procfs_sproc_handler - handle exit from the sproc syscall + +SYNOPSIS + + int procfs_sproc_handler (pi, syscall_num, why, rtnvalp, statvalp) + +DESCRIPTION + + This routine is called when an inferior process is about to finish an + sproc() system call. This is the system call that IRIX uses to create + a lightweight process. When the target process gets this event, we can + look at rval1 to find the new child processes ID, and create a new + procinfo struct from that. + + After that, it pretends that we got a SIGTRAP, and returns non-zero + to tell procfs_wait to wake up. Subsequently, wait_for_inferior gets + woken up, sees the new process and continues it. - praddset (&entryset, SYS_exit); +NOTES + We actually never see the child exiting from sproc because we will + shortly stop the child with PIOCSTOP, which is then registered as the + event of interest. + */ + +static int +procfs_sproc_handler (pi, syscall_num, why, rtnvalp, statvalp) + struct procinfo *pi; + int syscall_num; + int why; + int *rtnvalp; + int *statvalp; +{ +/* We've just detected the completion of an sproc system call. Now we need to + setup a procinfo struct for this thread, and notify the thread system of the + new arrival. */ + +/* If sproc failed, then nothing interesting happened. Continue the process + and go back to sleep. */ - if (ioctl (fd, PIOCSENTRY, &entryset) < 0) + if (pi->prstatus.pr_errno != 0) { - perror (procname); - gdb_flush (gdb_stderr); - _exit (126); + pi->prrun.pr_flags &= PRSTEP; + pi->prrun.pr_flags |= PRCFAULT; + + if (ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) + perror_with_name (pi->pathname); + + return 0; } - /* Turn off inherit-on-fork flag so that all grand-children of gdb - start with tracing flags cleared. */ + /* At this point, the new thread is stopped at it's first instruction, and + the parent is stopped at the exit from sproc. */ -#if defined (PIOCRESET) /* New method */ - { - long pr_flags; - pr_flags = PR_FORK; - ioctl (fd, PIOCRESET, &pr_flags); - } -#else -#if defined (PIOCRFORK) /* Original method */ - ioctl (fd, PIOCRFORK, NULL); -#endif -#endif + /* Notify the caller of the arrival of a new thread. */ + create_procinfo (pi->prstatus.pr_rval1); - /* Turn on run-on-last-close flag so that this process will not hang - if GDB goes away for some reason. */ + *rtnvalp = pi->prstatus.pr_rval1; + *statvalp = (SIGTRAP << 8) | 0177; -#if defined (PIOCSET) /* New method */ - { - long pr_flags; - pr_flags = PR_RLC; - (void) ioctl (fd, PIOCSET, &pr_flags); - } -#else -#if defined (PIOCSRLC) /* Original method */ - (void) ioctl (fd, PIOCSRLC, 0); -#endif -#endif + return 1; } /* -GLOBAL FUNCTION +LOCAL FUNCTION - proc_iterate_over_mappings -- call function for every mapped space + procfs_fork_handler - handle exit from the fork syscall SYNOPSIS - int proc_iterate_over_mappings (int (*func)()) + int procfs_fork_handler (pi, syscall_num, why, rtnvalp, statvalp) DESCRIPTION - Given a pointer to a function, call that function for every - mapped address space, passing it an open file descriptor for - the file corresponding to that mapped address space (if any) - and the base address of the mapped space. Quit when we hit - the end of the mappings or the function returns nonzero. + This routine is called when an inferior process is about to finish a + fork() system call. We will open up the new process, and then close + it, which releases it from the clutches of the debugger. + + After that, we continue the target process as though nothing had + happened. + +NOTES + This is necessary for IRIX because we have to set PR_FORK in order + to catch the creation of lwps (via sproc()). When an actual fork + occurs, it becomes necessary to reset the forks debugger flags and + continue it because we can't hack multiple processes yet. */ -int -proc_iterate_over_mappings (func) - int (*func) PARAMS ((int, CORE_ADDR)); +static int +procfs_fork_handler (pi, syscall_num, why, rtnvalp, statvalp) + struct procinfo *pi; + int syscall_num; + int why; + int *rtnvalp; + int *statvalp; { - int nmap; - int fd; - int funcstat = 0; - struct prmap *prmaps; - struct prmap *prmap; - struct procinfo *pi; + struct procinfo *pitemp; - pi = current_procinfo; +/* At this point, we've detected the completion of a fork (or vfork) call in + our child. The grandchild is also stopped because we set inherit-on-fork + earlier. (Note that nobody has the grandchilds' /proc file open at this + point.) We will release the grandchild from the debugger by opening it's + /proc file and then closing it. Since run-on-last-close is set, the + grandchild continues on its' merry way. */ - if (ioctl (pi->fd, PIOCNMAP, &nmap) == 0) - { - prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); - if (ioctl (pi->fd, PIOCMAP, prmaps) == 0) - { - for (prmap = prmaps; prmap -> pr_size && funcstat == 0; ++prmap) - { - fd = proc_address_to_fd (pi, (CORE_ADDR) prmap -> pr_vaddr, 0); - funcstat = (*func) (fd, (CORE_ADDR) prmap -> pr_vaddr); - close (fd); - } - } - } - return (funcstat); + + pitemp = create_procinfo (pi->prstatus.pr_rval1); + if (pitemp) + close_proc_file (pitemp); + + if (ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) + perror_with_name (pi->pathname); + + return 0; } +#endif /* SYS_sproc && !UNIXWARE */ -#if 0 /* Currently unused */ /* -GLOBAL FUNCTION +LOCAL FUNCTION - proc_base_address -- find base address for segment containing address + procfs_set_inferior_syscall_traps - setup the syscall traps SYNOPSIS - CORE_ADDR proc_base_address (CORE_ADDR addr) + void procfs_set_inferior_syscall_traps (struct procinfo *pip) DESCRIPTION - Given an address of a location in the inferior, find and return - the base address of the mapped segment containing that address. + Called for each "procinfo" (process, thread, or LWP) in the + inferior, to register for notification of and handlers for + syscall traps in the inferior. - This is used for example, by the shared library support code, - where we have the pc value for some location in the shared library - where we are stopped, and need to know the base address of the - segment containing that address. -*/ + */ -CORE_ADDR -proc_base_address (addr) - CORE_ADDR addr; +static void +procfs_set_inferior_syscall_traps (pip) + struct procinfo *pip; { - int nmap; - struct prmap *prmaps; - struct prmap *prmap; - CORE_ADDR baseaddr = 0; - struct procinfo *pi; + procfs_set_syscall_trap (pip, SYS_exit, PROCFS_SYSCALL_ENTRY, + procfs_exit_handler); - pi = current_procinfo; +#ifndef PRFS_STOPEXEC +#ifdef SYS_exec + procfs_set_syscall_trap (pip, SYS_exec, PROCFS_SYSCALL_EXIT, + procfs_exec_handler); +#endif +#ifdef SYS_execv + procfs_set_syscall_trap (pip, SYS_execv, PROCFS_SYSCALL_EXIT, + procfs_exec_handler); +#endif +#ifdef SYS_execve + procfs_set_syscall_trap (pip, SYS_execve, PROCFS_SYSCALL_EXIT, + procfs_exec_handler); +#endif +#endif /* PRFS_STOPEXEC */ - if (ioctl (pi->fd, PIOCNMAP, &nmap) == 0) - { - prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); - if (ioctl (pi->fd, PIOCMAP, prmaps) == 0) - { - for (prmap = prmaps; prmap -> pr_size; ++prmap) - { - if ((prmap -> pr_vaddr <= (caddr_t) addr) && - (prmap -> pr_vaddr + prmap -> pr_size > (caddr_t) addr)) - { - baseaddr = (CORE_ADDR) prmap -> pr_vaddr; - break; - } - } - } - } - return (baseaddr); -} + /* Setup traps on exit from sproc() */ -#endif /* 0 */ +#ifdef SYS_sproc + procfs_set_syscall_trap (pip, SYS_sproc, PROCFS_SYSCALL_EXIT, + procfs_sproc_handler); + procfs_set_syscall_trap (pip, SYS_fork, PROCFS_SYSCALL_EXIT, + procfs_fork_handler); +#ifdef SYS_vfork + procfs_set_syscall_trap (pip, SYS_vfork, PROCFS_SYSCALL_EXIT, + procfs_fork_handler); +#endif +/* Turn on inherit-on-fork flag so that all children of the target process + start with tracing flags set. This allows us to trap lwp creation. Note + that we also have to trap on fork and vfork in order to disable all tracing + in the targets child processes. */ + + modify_inherit_on_fork_flag (pip->ctl_fd, 1); +#endif + +#ifdef SYS_lwp_create + procfs_set_syscall_trap (pip, SYS_lwp_create, PROCFS_SYSCALL_EXIT, + procfs_lwp_creation_handler); +#endif +} /* LOCAL FUNCTION - proc_address_to_fd -- return open fd for file mapped to address + procfs_init_inferior - initialize target vector and access to a + /proc entry SYNOPSIS - int proc_address_to_fd (struct procinfo *pi, CORE_ADDR addr, complain) + void procfs_init_inferior (int pid) DESCRIPTION - Given an address in the current inferior's address space, use the - /proc interface to find an open file descriptor for the file that - this address was mapped in from. Return -1 if there is no current - inferior. Print a warning message if there is an inferior but - the address corresponds to no file (IE a bogus address). + When gdb starts an inferior, this function is called in the parent + process immediately after the fork. It waits for the child to stop + on the return from the exec system call (the child itself takes care + of ensuring that this is set up), then sets up the set of signals + and faults that are to be traced. Returns the pid, which may have had + the thread-id added to it. -*/ +NOTES -static int -proc_address_to_fd (pi, addr, complain) - struct procinfo *pi; - CORE_ADDR addr; - int complain; -{ - int fd = -1; + If proc_init_failed ever gets called, control returns to the command + processing loop via the standard error handling code. - if ((fd = ioctl (pi->fd, PIOCOPENM, (caddr_t *) &addr)) < 0) - { - if (complain) - { - print_sys_errmsg (pi->pathname, errno); - warning ("can't find mapped file for address 0x%x", addr); - } - } - return (fd); -} + */ +static void +procfs_init_inferior (pid) + int pid; +{ + struct procinfo *pip; -/* Attach to process PID, then initialize for debugging it - and wait for the trace-trap that results from attaching. */ + push_target (&procfs_ops); -static void -procfs_attach (args, from_tty) - char *args; - int from_tty; -{ - char *exec_file; - int pid; + pip = create_procinfo (pid); - if (!args) - error_no_arg ("process-id to attach"); + procfs_set_inferior_syscall_traps (pip); - pid = atoi (args); + /* create_procinfo may change the pid, so we have to update inferior_pid + here before calling other gdb routines that need the right pid. */ - if (pid == getpid()) /* Trying to masturbate? */ - error ("I refuse to debug myself!"); + pid = pip -> pid; + inferior_pid = pid; - if (from_tty) - { - exec_file = (char *) get_exec_file (0); + add_thread (pip -> pid); /* Setup initial thread */ - if (exec_file) - printf_unfiltered ("Attaching to program `%s', %s\n", exec_file, target_pid_to_str (pid)); - else - printf_unfiltered ("Attaching to %s\n", target_pid_to_str (pid)); +#ifdef START_INFERIOR_TRAPS_EXPECTED + startup_inferior (START_INFERIOR_TRAPS_EXPECTED); +#else + /* One trap to exec the shell, one to exec the program being debugged. */ + startup_inferior (2); +#endif +} - gdb_flush (gdb_stdout); - } +/* - do_attach (pid); - inferior_pid = pid; - push_target (&procfs_ops); -} +GLOBAL FUNCTION + procfs_notice_signals -/* Take a program previously attached to and detaches it. - The program resumes execution and will no longer stop - on signals, etc. We'd better not have left any breakpoints - in the program or it'll die when it hits one. For this - to work, it may be necessary for the process to have been - previously attached. It *might* work if the program was - started via the normal ptrace (PTRACE_TRACEME). */ +SYNOPSIS -static void -procfs_detach (args, from_tty) - char *args; - int from_tty; -{ - int siggnal = 0; + static void procfs_notice_signals (int pid); - if (from_tty) - { - char *exec_file = get_exec_file (0); - if (exec_file == 0) - exec_file = ""; - printf_unfiltered ("Detaching from program: %s %s\n", - exec_file, target_pid_to_str (inferior_pid)); - gdb_flush (gdb_stdout); - } - if (args) - siggnal = atoi (args); - - do_detach (siggnal); - inferior_pid = 0; - unpush_target (&procfs_ops); /* Pop out of handling an inferior */ -} +DESCRIPTION -/* Get ready to modify the registers array. On machines which store - individual registers, this doesn't need to do anything. On machines - which store all the registers in one fell swoop, this makes sure - that registers contains all the registers from the program being - debugged. */ + When the user changes the state of gdb's signal handling via the + "handle" command, this function gets called to see if any change + in the /proc interface is required. It is also called internally + by other /proc interface functions to initialize the state of + the traced signal set. + + One thing it does is that signals for which the state is "nostop", + "noprint", and "pass", have their trace bits reset in the pr_trace + field, so that they are no longer traced. This allows them to be + delivered directly to the inferior without the debugger ever being + involved. + */ static void -procfs_prepare_to_store () +procfs_notice_signals (pid) + int pid; { -#ifdef CHILD_PREPARE_TO_STORE - CHILD_PREPARE_TO_STORE (); + struct procinfo *pi; + struct sig_ctl sctl; + + pi = find_procinfo (pid, 0); + +#ifndef HAVE_PRRUN_T + premptyset (&sctl.sigset); +#else + sctl.sigset = pi->prrun.pr_trace; #endif -} -/* Print status information about what we're accessing. */ + notice_signals (pi, &sctl); -static void -procfs_files_info (ignore) - struct target_ops *ignore; -{ - printf_unfiltered ("\tUsing the running image of %s %s via /proc.\n", - attach_flag? "attached": "child", target_pid_to_str (inferior_pid)); +#ifdef HAVE_PRRUN_T + pi->prrun.pr_trace = sctl.sigset; +#endif } -/* ARGSUSED */ static void -procfs_open (arg, from_tty) - char *arg; - int from_tty; +notice_signals (pi, sctl) + struct procinfo *pi; + struct sig_ctl *sctl; { - error ("Use the \"run\" command to start a Unix child process."); + int signo; + + for (signo = 0; signo < NSIG; signo++) + { + if (signal_stop_state (target_signal_from_host (signo)) == 0 && + signal_print_state (target_signal_from_host (signo)) == 0 && + signal_pass_state (target_signal_from_host (signo)) == 1) + { + prdelset (&sctl->sigset, signo); + } + else + { + praddset (&sctl->sigset, signo); + } + } +#ifdef PROCFS_USE_READ_WRITE + sctl->cmd = PCSTRACE; + if (write (pi->ctl_fd, (char *) sctl, sizeof (struct sig_ctl)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCSTRACE, &sctl->sigset)) +#endif + { + print_sys_errmsg ("PIOCSTRACE failed", errno); + } } /* LOCAL FUNCTION - do_attach -- attach to an already existing process + proc_set_exec_trap -- arrange for exec'd child to halt at startup SYNOPSIS - int do_attach (int pid) + void proc_set_exec_trap (void) DESCRIPTION - Attach to an already existing process with the specified process - id. If the process is not already stopped, query whether to - stop it or not. + This function is called in the child process when starting up + an inferior, prior to doing the exec of the actual inferior. + It sets the child process's exitset to make exit from the exec + system call an event of interest to stop on, and then simply + returns. The child does the exec, the system call returns, and + the child stops at the first instruction, ready for the gdb + parent process to take control of it. -NOTES +NOTE - The option of stopping at attach time is specific to the /proc - versions of gdb. Versions using ptrace force the attachee - to stop. (I have changed this version to do so, too. All you - have to do is "continue" to make it go on. -- gnu@cygnus.com) + We need to use all local variables since the child may be sharing + it's data space with the parent, if vfork was used rather than + fork. -*/ + Also note that we want to turn off the inherit-on-fork flag in + the child process so that any grand-children start with all + tracing flags cleared. + */ -static int -do_attach (pid) - int pid; +static void +proc_set_exec_trap () { - int result; - struct procinfo *pi; - - pi = (struct procinfo *) xmalloc (sizeof (struct procinfo)); - - if (!open_proc_file (pid, pi, O_RDWR)) + struct sys_ctl exitset; + struct sys_ctl entryset; + char procname[MAX_PROC_NAME_SIZE]; + int fd; + + sprintf (procname, CTL_PROC_NAME_FMT, getpid ()); +#ifdef UNIXWARE + if ((fd = open (procname, O_WRONLY)) < 0) +#else + if ((fd = open (procname, O_RDWR)) < 0) +#endif { - free (pi); - perror_with_name (pi->pathname); - /* NOTREACHED */ + perror (procname); + gdb_flush (gdb_stderr); + _exit (127); } - - /* Add new process to process info list */ + premptyset (&exitset.sysset); + premptyset (&entryset.sysset); - pi->next = procinfo_list; - procinfo_list = pi; +#ifdef PRFS_STOPEXEC + /* Under Alpha OSF/1 we have to use a PIOCSSPCACT ioctl to trace + exits from exec system calls because of the user level loader. */ + { + int prfs_flags; - add_fd (pi); /* Add to list for poll/select */ + if (ioctl (fd, PIOCGSPCACT, &prfs_flags) < 0) + { + perror (procname); + gdb_flush (gdb_stderr); + _exit (127); + } + prfs_flags |= PRFS_STOPEXEC; + if (ioctl (fd, PIOCSSPCACT, &prfs_flags) < 0) + { + perror (procname); + gdb_flush (gdb_stderr); + _exit (127); + } + } +#else /* PRFS_STOPEXEC */ + /* GW: Rationale... + Not all systems with /proc have all the exec* syscalls with the same + names. On the SGI, for example, there is no SYS_exec, but there + *is* a SYS_execv. So, we try to account for that. */ - /* Get current status of process and if it is not already stopped, - then stop it. Remember whether or not it was stopped when we first - examined it. */ - - if (ioctl (pi->fd, PIOCSTATUS, &pi->prstatus) < 0) - { - print_sys_errmsg (pi->pathname, errno); - close_proc_file (pi); - error ("PIOCSTATUS failed"); - } - if (pi->prstatus.pr_flags & (PR_STOPPED | PR_ISTOP)) - { - pi->was_stopped = 1; - } - else - { - pi->was_stopped = 0; - if (1 || query ("Process is currently running, stop it? ")) - { - /* Make it run again when we close it. */ -#if defined (PIOCSET) /* New method */ - { - long pr_flags; - pr_flags = PR_RLC; - result = ioctl (pi->fd, PIOCSET, &pr_flags); - } -#else -#if defined (PIOCSRLC) /* Original method */ - result = ioctl (pi->fd, PIOCSRLC, 0); +#ifdef SYS_exec + praddset (&exitset.sysset, SYS_exec); #endif +#ifdef SYS_execve + praddset (&exitset.sysset, SYS_execve); #endif - if (result < 0) - { - print_sys_errmsg (pi->pathname, errno); - close_proc_file (pi); - error ("PIOCSRLC or PIOCSET failed"); - } - if (ioctl (pi->fd, PIOCSTOP, &pi->prstatus) < 0) - { - print_sys_errmsg (pi->pathname, errno); - close_proc_file (pi); - error ("PIOCSTOP failed"); - } - pi->nopass_next_sigstop = 1; - } - else - { - printf_unfiltered ("Ok, gdb will wait for %s to stop.\n", target_pid_to_str (pid)); - } - } - - /* Remember some things about the inferior that we will, or might, change - so that we can restore them when we detach. */ - - ioctl (pi->fd, PIOCGTRACE, &pi->saved_trace); - ioctl (pi->fd, PIOCGHOLD, &pi->saved_sighold); - ioctl (pi->fd, PIOCGFAULT, &pi->saved_fltset); - ioctl (pi->fd, PIOCGENTRY, &pi->saved_entryset); - ioctl (pi->fd, PIOCGEXIT, &pi->saved_exitset); - - /* Set up trace and fault sets, as gdb expects them. */ - - memset (&pi->prrun, 0, sizeof (pi->prrun)); - prfillset (&pi->prrun.pr_trace); - procfs_notice_signals (pid); - prfillset (&pi->prrun.pr_fault); - prdelset (&pi->prrun.pr_fault, FLTPAGE); - -#ifdef PROCFS_DONT_TRACE_FAULTS - premptyset (&pi->prrun.pr_fault); +#ifdef SYS_execv + praddset (&exitset.sysset, SYS_execv); #endif - if (ioctl (pi->fd, PIOCSFAULT, &pi->prrun.pr_fault)) +#ifdef PROCFS_USE_READ_WRITE + exitset.cmd = PCSEXIT; + if (write (fd, (char *) &exitset, sizeof (struct sys_ctl)) < 0) +#else + if (ioctl (fd, PIOCSEXIT, &exitset.sysset) < 0) +#endif { - print_sys_errmsg ("PIOCSFAULT failed", errno); + perror (procname); + gdb_flush (gdb_stderr); + _exit (127); } - if (ioctl (pi->fd, PIOCSTRACE, &pi->prrun.pr_trace)) +#endif /* PRFS_STOPEXEC */ + + praddset (&entryset.sysset, SYS_exit); + +#ifdef PROCFS_USE_READ_WRITE + entryset.cmd = PCSENTRY; + if (write (fd, (char *) &entryset, sizeof (struct sys_ctl)) < 0) +#else + if (ioctl (fd, PIOCSENTRY, &entryset.sysset) < 0) +#endif { - print_sys_errmsg ("PIOCSTRACE failed", errno); + perror (procname); + gdb_flush (gdb_stderr); + _exit (126); } - attach_flag = 1; - return (pid); + + /* Turn off inherit-on-fork flag so that all grand-children of gdb + start with tracing flags cleared. */ + + modify_inherit_on_fork_flag (fd, 0); + + /* Turn on run-on-last-close flag so that this process will not hang + if GDB goes away for some reason. */ + + modify_run_on_last_close_flag (fd, 1); + +#ifndef UNIXWARE /* since this is a solaris-ism, we don't want it */ + /* NOTE: revisit when doing thread support for UW */ +#ifdef PR_ASYNC + { + long pr_flags; + struct proc_ctl pctl; + +/* Solaris needs this to make procfs treat all threads seperately. Without + this, all threads halt whenever something happens to any thread. Since + GDB wants to control all this itself, it needs to set PR_ASYNC. */ + + pr_flags = PR_ASYNC; +#ifdef PROCFS_USE_READ_WRITE + pctl.cmd = PCSET; + pctl.data = PR_FORK|PR_ASYNC; + write (fd, (char *) &pctl, sizeof (struct proc_ctl)); +#else + ioctl (fd, PIOCSET, &pr_flags); +#endif + } +#endif /* PR_ASYNC */ +#endif /* !UNIXWARE */ } /* -LOCAL FUNCTION +GLOBAL FUNCTION - do_detach -- detach from an attached-to process + proc_iterate_over_mappings -- call function for every mapped space SYNOPSIS - void do_detach (int signal) + int proc_iterate_over_mappings (int (*func)()) DESCRIPTION - Detach from the current attachee. - - If signal is non-zero, the attachee is started running again and sent - the specified signal. - - If signal is zero and the attachee was not already stopped when we - attached to it, then we make it runnable again when we detach. - - Otherwise, we query whether or not to make the attachee runnable - again, since we may simply want to leave it in the state it was in - when we attached. - - We report any problems, but do not consider them errors, since we - MUST detach even if some things don't seem to go right. This may not - be the ideal situation. (FIXME). + Given a pointer to a function, call that function for every + mapped address space, passing it an open file descriptor for + the file corresponding to that mapped address space (if any) + and the base address of the mapped space. Quit when we hit + the end of the mappings or the function returns nonzero. */ -static void -do_detach (signal) - int signal; +#ifdef UNIXWARE +int +proc_iterate_over_mappings (func) + int (*func) PARAMS ((int, CORE_ADDR)); { - int result; + int nmap; + int fd; + int funcstat = 0; + prmap_t *prmaps; + prmap_t *prmap; struct procinfo *pi; + struct stat sbuf; pi = current_procinfo; - if (signal) - { - set_proc_siginfo (pi, signal); - } - if (ioctl (pi->fd, PIOCSEXIT, &pi->saved_exitset) < 0) - { - print_sys_errmsg (pi->pathname, errno); - printf_unfiltered ("PIOCSEXIT failed.\n"); - } - if (ioctl (pi->fd, PIOCSENTRY, &pi->saved_entryset) < 0) - { - print_sys_errmsg (pi->pathname, errno); - printf_unfiltered ("PIOCSENTRY failed.\n"); - } - if (ioctl (pi->fd, PIOCSTRACE, &pi->saved_trace) < 0) - { - print_sys_errmsg (pi->pathname, errno); - printf_unfiltered ("PIOCSTRACE failed.\n"); - } - if (ioctl (pi->fd, PIOCSHOLD, &pi->saved_sighold) < 0) - { - print_sys_errmsg (pi->pathname, errno); - printf_unfiltered ("PIOSCHOLD failed.\n"); - } - if (ioctl (pi->fd, PIOCSFAULT, &pi->saved_fltset) < 0) - { - print_sys_errmsg (pi->pathname, errno); - printf_unfiltered ("PIOCSFAULT failed.\n"); - } - if (ioctl (pi->fd, PIOCSTATUS, &pi->prstatus) < 0) + if (fstat (pi->map_fd, &sbuf) < 0) + return 0; + + nmap = sbuf.st_size / sizeof (prmap_t); + prmaps = (prmap_t *) alloca (nmap * sizeof(prmap_t)); + if ((lseek (pi->map_fd, 0, SEEK_SET) == 0) && + (read (pi->map_fd, (char *) prmaps, nmap * sizeof (prmap_t)) == + (nmap * sizeof (prmap_t)))) { - print_sys_errmsg (pi->pathname, errno); - printf_unfiltered ("PIOCSTATUS failed.\n"); + int i = 0; + for (prmap = prmaps; i < nmap && funcstat == 0; ++prmap, ++i) + { + char name[sizeof ("/proc/1234567890/object") + + sizeof (prmap->pr_mapname)]; + sprintf (name, "/proc/%d/object/%s", pi->pid, prmap->pr_mapname); + if ((fd = open (name, O_RDONLY)) == -1) + { + funcstat = 1; + break; + } + funcstat = (*func) (fd, (CORE_ADDR) prmap->pr_vaddr); + close (fd); + } } - else + return (funcstat); +} +#else /* UNIXWARE */ +int +proc_iterate_over_mappings (func) + int (*func) PARAMS ((int, CORE_ADDR)); +{ + int nmap; + int fd; + int funcstat = 0; + struct prmap *prmaps; + struct prmap *prmap; + struct procinfo *pi; + + pi = current_procinfo; + + if (ioctl (pi->map_fd, PIOCNMAP, &nmap) == 0) { - if (signal || (pi->prstatus.pr_flags & (PR_STOPPED | PR_ISTOP))) + prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); + if (ioctl (pi->map_fd, PIOCMAP, prmaps) == 0) { - if (signal || !pi->was_stopped || - query ("Was stopped when attached, make it runnable again? ")) + for (prmap = prmaps; prmap -> pr_size && funcstat == 0; ++prmap) { - /* Clear any pending signal if we want to detach without - a signal. */ - if (signal == 0) - set_proc_siginfo (pi, signal); - - /* Clear any fault that might have stopped it. */ - if (ioctl (pi->fd, PIOCCFAULT, 0)) - { - print_sys_errmsg (pi->pathname, errno); - printf_unfiltered ("PIOCCFAULT failed.\n"); - } - - /* Make it run again when we close it. */ -#if defined (PIOCSET) /* New method */ - { - long pr_flags; - pr_flags = PR_RLC; - result = ioctl (pi->fd, PIOCSET, &pr_flags); - } -#else -#if defined (PIOCSRLC) /* Original method */ - result = ioctl (pi->fd, PIOCSRLC, 0); -#endif -#endif - if (result) - { - print_sys_errmsg (pi->pathname, errno); - printf_unfiltered ("PIOCSRLC or PIOCSET failed.\n"); - } + fd = proc_address_to_fd (pi, (CORE_ADDR) prmap -> pr_vaddr, 0); + funcstat = (*func) (fd, (CORE_ADDR) prmap -> pr_vaddr); + close (fd); } } } - close_proc_file (pi); - attach_flag = 0; + return (funcstat); } +#endif /* UNIXWARE */ -/* emulate wait() as much as possible. - Wait for child to do something. Return pid of child, or -1 in case - of error; store status in *OURSTATUS. +#if 0 /* Currently unused */ +/* - Not sure why we can't - just use wait(), but it seems to have problems when applied to a - process being controlled with the /proc interface. +GLOBAL FUNCTION - We have a race problem here with no obvious solution. We need to let - the inferior run until it stops on an event of interest, which means - that we need to use the PIOCWSTOP ioctl. However, we cannot use this - ioctl if the process is already stopped on something that is not an - event of interest, or the call will hang indefinitely. Thus we first - use PIOCSTATUS to see if the process is not stopped. If not, then we - use PIOCWSTOP. But during the window between the two, if the process - stops for any reason that is not an event of interest (such as a job - control signal) then gdb will hang. One possible workaround is to set - an alarm to wake up every minute of so and check to see if the process - is still running, and if so, then reissue the PIOCWSTOP. But this is - a real kludge, so has not been implemented. FIXME: investigate - alternatives. + proc_base_address -- find base address for segment containing address - FIXME: Investigate why wait() seems to have problems with programs - being control by /proc routines. */ +SYNOPSIS -static int -procfs_wait (pid, ourstatus) - int pid; - struct target_waitstatus *ourstatus; -{ - short what; - short why; - int statval = 0; - int checkerr = 0; - int rtnval = -1; - struct procinfo *pi; + CORE_ADDR proc_base_address (CORE_ADDR addr) - if (pid != -1) /* Non-specific process? */ - pi = NULL; - else - for (pi = procinfo_list; pi; pi = pi->next) - if (pi->had_event) - break; +DESCRIPTION - if (!pi) - { - wait_again: + Given an address of a location in the inferior, find and return + the base address of the mapped segment containing that address. - pi = wait_fd (); - } + This is used for example, by the shared library support code, + where we have the pc value for some location in the shared library + where we are stopped, and need to know the base address of the + segment containing that address. +*/ - if (pid != -1) - for (pi = procinfo_list; pi; pi = pi->next) - if (pi->pid == pid && pi->had_event) - break; +CORE_ADDR +proc_base_address (addr) + CORE_ADDR addr; +{ + int nmap; + struct prmap *prmaps; + struct prmap *prmap; + CORE_ADDR baseaddr = 0; + struct procinfo *pi; - if (!pi && !checkerr) - goto wait_again; + pi = current_procinfo; - if (!checkerr && !(pi->prstatus.pr_flags & (PR_STOPPED | PR_ISTOP))) - { - if (ioctl (pi->fd, PIOCWSTOP, &pi->prstatus) < 0) - { - checkerr++; - } - } - if (checkerr) + if (ioctl (pi->map_fd, PIOCNMAP, &nmap) == 0) { - if (errno == ENOENT) + prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); + if (ioctl (pi->map_fd, PIOCMAP, prmaps) == 0) { - rtnval = wait (&statval); - if (rtnval != inferior_pid) + for (prmap = prmaps; prmap -> pr_size; ++prmap) { - print_sys_errmsg (pi->pathname, errno); - error ("PIOCWSTOP, wait failed, returned %d", rtnval); - /* NOTREACHED */ + if ((prmap -> pr_vaddr <= (caddr_t) addr) && + (prmap -> pr_vaddr + prmap -> pr_size > (caddr_t) addr)) + { + baseaddr = (CORE_ADDR) prmap -> pr_vaddr; + break; + } } } - else - { - print_sys_errmsg (pi->pathname, errno); - error ("PIOCSTATUS or PIOCWSTOP failed."); - /* NOTREACHED */ - } } - else if (pi->prstatus.pr_flags & (PR_STOPPED | PR_ISTOP)) - { - rtnval = pi->prstatus.pr_pid; - why = pi->prstatus.pr_why; - what = pi->prstatus.pr_what; + return (baseaddr); +} - switch (why) - { - case PR_SIGNALLED: - statval = (what << 8) | 0177; - break; - case PR_SYSENTRY: - if (what != SYS_exit) - error ("PR_SYSENTRY, unknown system call %d", what); +#endif /* 0 */ - pi->prrun.pr_flags = PRCFAULT; +#ifndef UNIXWARE +/* - if (ioctl (pi->fd, PIOCRUN, &pi->prrun) != 0) - perror_with_name (pi->pathname); +LOCAL FUNCTION - rtnval = wait (&statval); + proc_address_to_fd -- return open fd for file mapped to address - break; - case PR_SYSEXIT: - switch (what) - { -#ifdef SYS_exec - case SYS_exec: -#endif -#ifdef SYS_execve - case SYS_execve: -#endif -#ifdef SYS_execv - case SYS_execv: -#endif - statval = (SIGTRAP << 8) | 0177; - break; -#ifdef SYS_sproc - case SYS_sproc: -/* We've just detected the completion of an sproc system call. Now we need to - setup a procinfo struct for this thread, and notify the thread system of the - new arrival. */ +SYNOPSIS -/* If sproc failed, then nothing interesting happened. Continue the process and - go back to sleep. */ + int proc_address_to_fd (struct procinfo *pi, CORE_ADDR addr, complain) - if (pi->prstatus.pr_errno != 0) - { - pi->prrun.pr_flags &= PRSTEP; - pi->prrun.pr_flags |= PRCFAULT; +DESCRIPTION - if (ioctl (pi->fd, PIOCRUN, &pi->prrun) != 0) - perror_with_name (pi->pathname); + Given an address in the current inferior's address space, use the + /proc interface to find an open file descriptor for the file that + this address was mapped in from. Return -1 if there is no current + inferior. Print a warning message if there is an inferior but + the address corresponds to no file (IE a bogus address). - goto wait_again; - } +*/ -/* At this point, the new thread is stopped at it's first instruction, and - the parent is stopped at the exit from sproc. */ +static int +proc_address_to_fd (pi, addr, complain) + struct procinfo *pi; + CORE_ADDR addr; + int complain; +{ + int fd = -1; -/* Notify the caller of the arrival of a new thread. */ - create_procinfo (pi->prstatus.pr_rval1); + if ((fd = ioctl (pi->ctl_fd, PIOCOPENM, (caddr_t *) &addr)) < 0) + { + if (complain) + { + print_sys_errmsg (pi->pathname, errno); + warning ("can't find mapped file for address 0x%x", addr); + } + } + return (fd); +} +#endif /* !UNIXWARE */ - rtnval = pi->prstatus.pr_rval1; - statval = (SIGTRAP << 8) | 0177; +/* Attach to process PID, then initialize for debugging it + and wait for the trace-trap that results from attaching. */ - break; - case SYS_fork: -#ifdef SYS_vfork - case SYS_vfork: -#endif -/* At this point, we've detected the completion of a fork (or vfork) call in - our child. The grandchild is also stopped because we set inherit-on-fork - earlier. (Note that nobody has the grandchilds' /proc file open at this - point.) We will release the grandchild from the debugger by opening it's - /proc file and then closing it. Since run-on-last-close is set, the - grandchild continues on its' merry way. */ +static void +procfs_attach (args, from_tty) + char *args; + int from_tty; +{ + char *exec_file; + int pid; - { - struct procinfo *pitemp; + if (!args) + error_no_arg ("process-id to attach"); - pitemp = create_procinfo (pi->prstatus.pr_rval1); - if (pitemp) - close_proc_file (pitemp); + pid = atoi (args); - if (ioctl (pi->fd, PIOCRUN, &pi->prrun) != 0) - perror_with_name (pi->pathname); - } - goto wait_again; -#endif /* SYS_sproc */ + if (pid == getpid()) /* Trying to masturbate? */ + error ("I refuse to debug myself!"); - default: - error ("PIOCSTATUS (PR_SYSEXIT): Unknown system call %d", what); - } - break; - case PR_REQUESTED: - statval = (SIGSTOP << 8) | 0177; - break; - case PR_JOBCONTROL: - statval = (what << 8) | 0177; - break; - case PR_FAULTED: - switch (what) - { -#ifdef FLTWATCH - case FLTWATCH: - statval = (SIGTRAP << 8) | 0177; - break; -#endif -#ifdef FLTKWATCH - case FLTKWATCH: - statval = (SIGTRAP << 8) | 0177; - break; -#endif -#ifndef FAULTED_USE_SIGINFO - /* Irix, contrary to the documentation, fills in 0 for si_signo. - Solaris fills in si_signo. I'm not sure about others. */ - case FLTPRIV: - case FLTILL: - statval = (SIGILL << 8) | 0177; - break; - case FLTBPT: - case FLTTRACE: - statval = (SIGTRAP << 8) | 0177; - break; - case FLTSTACK: - case FLTACCESS: - case FLTBOUNDS: - statval = (SIGSEGV << 8) | 0177; - break; - case FLTIOVF: - case FLTIZDIV: - case FLTFPE: - statval = (SIGFPE << 8) | 0177; - break; - case FLTPAGE: /* Recoverable page fault */ -#endif /* not FAULTED_USE_SIGINFO */ - default: - /* Use the signal which the kernel assigns. This is better than - trying to second-guess it from the fault. In fact, I suspect - that FLTACCESS can be either SIGSEGV or SIGBUS. */ - statval = ((pi->prstatus.pr_info.si_signo) << 8) | 0177; - break; - } - break; - default: - error ("PIOCWSTOP, unknown why %d, what %d", why, what); - } -/* Stop all the other threads when any of them stops. */ + if (from_tty) + { + exec_file = (char *) get_exec_file (0); - { - struct procinfo *procinfo; + if (exec_file) + printf_unfiltered ("Attaching to program `%s', %s\n", exec_file, target_pid_to_str (pid)); + else + printf_unfiltered ("Attaching to %s\n", target_pid_to_str (pid)); - for (procinfo = procinfo_list; procinfo; procinfo = procinfo->next) - { - if (!procinfo->had_event) - if (ioctl (procinfo->fd, PIOCSTOP, &procinfo->prstatus) < 0) - { - print_sys_errmsg (procinfo->pathname, errno); - error ("PIOCSTOP failed"); - } - } - } - } - else - { - error ("PIOCWSTOP, stopped for unknown/unhandled reason, flags %#x", - pi->prstatus.pr_flags); + gdb_flush (gdb_stdout); } - store_waitstatus (ourstatus, statval); + inferior_pid = pid = do_attach (pid); + push_target (&procfs_ops); +} - if (rtnval == -1) /* No more children to wait for */ + +/* Take a program previously attached to and detaches it. + The program resumes execution and will no longer stop + on signals, etc. We'd better not have left any breakpoints + in the program or it'll die when it hits one. For this + to work, it may be necessary for the process to have been + previously attached. It *might* work if the program was + started via the normal ptrace (PTRACE_TRACEME). */ + +static void +procfs_detach (args, from_tty) + char *args; + int from_tty; +{ + int siggnal = 0; + + if (from_tty) { - fprintf_unfiltered (gdb_stderr, "Child process unexpectedly missing.\n"); - /* Claim it exited with unknown signal. */ - ourstatus->kind = TARGET_WAITKIND_SIGNALLED; - ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; - return rtnval; + char *exec_file = get_exec_file (0); + if (exec_file == 0) + exec_file = ""; + printf_unfiltered ("Detaching from program: %s %s\n", + exec_file, target_pid_to_str (inferior_pid)); + gdb_flush (gdb_stdout); } + if (args) + siggnal = atoi (args); + + do_detach (siggnal); + inferior_pid = 0; + unpush_target (&procfs_ops); /* Pop out of handling an inferior */ +} - pi->had_event = 0; /* Indicate that we've seen this one */ - return (rtnval); +/* Get ready to modify the registers array. On machines which store + individual registers, this doesn't need to do anything. On machines + which store all the registers in one fell swoop, this makes sure + that registers contains all the registers from the program being + debugged. */ + +static void +procfs_prepare_to_store () +{ +#ifdef CHILD_PREPARE_TO_STORE + CHILD_PREPARE_TO_STORE (); +#endif +} + +/* Print status information about what we're accessing. */ + +static void +procfs_files_info (ignore) + struct target_ops *ignore; +{ + printf_unfiltered ("\tUsing the running image of %s %s via /proc.\n", + attach_flag? "attached": "child", target_pid_to_str (inferior_pid)); +} + +/* ARGSUSED */ +static void +procfs_open (arg, from_tty) + char *arg; + int from_tty; +{ + error ("Use the \"run\" command to start a Unix child process."); } /* LOCAL FUNCTION - set_proc_siginfo - set a process's current signal info + do_attach -- attach to an already existing process SYNOPSIS - void set_proc_siginfo (struct procinfo *pip, int signo); + int do_attach (int pid) DESCRIPTION - Given a pointer to a process info struct in PIP and a signal number - in SIGNO, set the process's current signal and its associated signal - information. The signal will be delivered to the process immediately - after execution is resumed, even if it is being held. In addition, - this particular delivery will not cause another PR_SIGNALLED stop - even if the signal is being traced. + Attach to an already existing process with the specified process + id. If the process is not already stopped, query whether to + stop it or not. - If we are not delivering the same signal that the prstatus siginfo - struct contains information about, then synthesize a siginfo struct - to match the signal we are doing to deliver, make it of the type - "generated by a user process", and send this synthesized copy. When - used to set the inferior's signal state, this will be required if we - are not currently stopped because of a traced signal, or if we decide - to continue with a different signal. +NOTES - Note that when continuing the inferior from a stop due to receipt - of a traced signal, we either have set PRCSIG to clear the existing - signal, or we have to call this function to do a PIOCSSIG with either - the existing siginfo struct from pr_info, or one we have synthesized - appropriately for the signal we want to deliver. Otherwise if the - signal is still being traced, the inferior will immediately stop - again. + The option of stopping at attach time is specific to the /proc + versions of gdb. Versions using ptrace force the attachee + to stop. (I have changed this version to do so, too. All you + have to do is "continue" to make it go on. -- gnu@cygnus.com) - See siginfo(5) for more details. */ -static void -set_proc_siginfo (pip, signo) - struct procinfo *pip; - int signo; +static int +do_attach (pid) + int pid; { - struct siginfo newsiginfo; - struct siginfo *sip; + struct procinfo *pi; + struct sig_ctl sctl; + struct flt_ctl fctl; + int nlwp, *lwps; -#ifdef PROCFS_DONT_PIOCSSIG_CURSIG - /* With Alpha OSF/1 procfs, the kernel gets really confused if it - receives a PIOCSSIG with a signal identical to the current signal, - it messes up the current signal. Work around the kernel bug. */ - if (signo == pip -> prstatus.pr_cursig) - return; -#endif + pi = init_procinfo (pid, 0); - if (signo == pip -> prstatus.pr_info.si_signo) - { - sip = &pip -> prstatus.pr_info; - } - else +#ifdef PIOCLWPIDS + nlwp = pi->prstatus.pr_nlwp; + lwps = alloca ((2 * nlwp + 2) * sizeof (id_t)); + + if (ioctl (pi->ctl_fd, PIOCLWPIDS, lwps)) { - memset ((char *) &newsiginfo, 0, sizeof (newsiginfo)); - sip = &newsiginfo; - sip -> si_signo = signo; - sip -> si_code = 0; - sip -> si_errno = 0; - sip -> si_pid = getpid (); - sip -> si_uid = getuid (); + print_sys_errmsg (pi -> pathname, errno); + error ("PIOCLWPIDS failed"); } - if (ioctl (pip -> fd, PIOCSSIG, sip) < 0) +#else /* PIOCLWPIDS */ + nlwp = 1; + lwps = alloca ((2 * nlwp + 2) * sizeof *lwps); + lwps[0] = 0; +#endif + for (; nlwp > 0; nlwp--, lwps++) { - print_sys_errmsg (pip -> pathname, errno); - warning ("PIOCSSIG failed"); + /* First one has already been created above. */ + if ((pi = find_procinfo ((*lwps << 16) | pid, 1)) == 0) + pi = init_procinfo ((*lwps << 16) | pid, 0); + + if (THE_PR_LWP(pi->prstatus).pr_flags & (PR_STOPPED | PR_ISTOP)) + { + pi->was_stopped = 1; + } + else + { + pi->was_stopped = 0; + if (1 || query ("Process is currently running, stop it? ")) + { + long cmd; + /* Make it run again when we close it. */ + modify_run_on_last_close_flag (pi->ctl_fd, 1); +#ifdef PROCFS_USE_READ_WRITE + cmd = PCSTOP; + if (write (pi->ctl_fd, (char *) &cmd, sizeof (long)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCSTOP, &pi->prstatus) < 0) +#endif + { + print_sys_errmsg (pi->pathname, errno); + close_proc_file (pi); + error ("PIOCSTOP failed"); + } +#ifdef UNIXWARE + if (!procfs_read_status (pi)) + { + print_sys_errmsg (pi->pathname, errno); + close_proc_file (pi); + error ("procfs_read_status failed"); + } +#endif + pi->nopass_next_sigstop = 1; + } + else + { + printf_unfiltered ("Ok, gdb will wait for %s to stop.\n", + target_pid_to_str (pi->pid)); + } + } + +#ifdef PROCFS_USE_READ_WRITE + fctl.cmd = PCSFAULT; + if (write (pi->ctl_fd, (char *) &fctl, sizeof (struct flt_ctl)) < 0) + print_sys_errmsg ("PCSFAULT failed", errno); +#else /* PROCFS_USE_READ_WRITE */ + if (ioctl (pi->ctl_fd, PIOCSFAULT, &pi->prrun.pr_fault)) + { + print_sys_errmsg ("PIOCSFAULT failed", errno); + } + if (ioctl (pi->ctl_fd, PIOCSTRACE, &pi->prrun.pr_trace)) + { + print_sys_errmsg ("PIOCSTRACE failed", errno); + } + add_thread (pi->pid); + procfs_set_inferior_syscall_traps (pi); +#endif /* PROCFS_USE_READ_WRITE */ } + attach_flag = 1; + return (pi->pid); } -/* Resume execution of process PID. If STEP is nozero, then - just single step it. If SIGNAL is nonzero, restart it with that - signal activated. */ +/* -static void -procfs_resume (pid, step, signo) - int pid; - int step; - enum target_signal signo; -{ - int signal_to_pass; - struct procinfo *pi, *procinfo; +LOCAL FUNCTION - pi = find_procinfo (pid == -1 ? inferior_pid : pid, 0); + do_detach -- detach from an attached-to process - errno = 0; - pi->prrun.pr_flags = PRSTRACE | PRSFAULT | PRCFAULT; +SYNOPSIS -#if 0 - /* It should not be necessary. If the user explicitly changes the value, - value_assign calls write_register_bytes, which writes it. */ -/* It may not be absolutely necessary to specify the PC value for - restarting, but to be safe we use the value that gdb considers - to be current. One case where this might be necessary is if the - user explicitly changes the PC value that gdb considers to be - current. FIXME: Investigate if this is necessary or not. */ - -#ifdef PRSVADDR_BROKEN -/* Can't do this under Solaris running on a Sparc, as there seems to be no - place to put nPC. In fact, if you use this, nPC seems to be set to some - random garbage. We have to rely on the fact that PC and nPC have been - written previously via PIOCSREG during a register flush. */ - - pi->prrun.pr_vaddr = (caddr_t) *(int *) ®isters[REGISTER_BYTE (PC_REGNUM)]; - pi->prrun.pr_flags != PRSVADDR; -#endif -#endif - - if (signo == TARGET_SIGNAL_STOP && pi->nopass_next_sigstop) - /* When attaching to a child process, if we forced it to stop with - a PIOCSTOP, then we will have set the nopass_next_sigstop flag. - Upon resuming the first time after such a stop, we explicitly - inhibit sending it another SIGSTOP, which would be the normal - result of default signal handling. One potential drawback to - this is that we will also ignore any attempt to by the user - to explicitly continue after the attach with a SIGSTOP. Ultimately - this problem should be dealt with by making the routines that - deal with the inferior a little smarter, and possibly even allow - an inferior to continue running at the same time as gdb. (FIXME?) */ - signal_to_pass = 0; - else if (signo == TARGET_SIGNAL_TSTP - && pi->prstatus.pr_cursig == SIGTSTP - && pi->prstatus.pr_action.sa_handler == SIG_DFL) - - /* We are about to pass the inferior a SIGTSTP whose action is - SIG_DFL. The SIG_DFL action for a SIGTSTP is to stop - (notifying the parent via wait()), and then keep going from the - same place when the parent is ready for you to keep going. So - under the debugger, it should do nothing (as if the program had - been stopped and then later resumed. Under ptrace, this - happens for us, but under /proc, the system obligingly stops - the process, and wait_for_inferior would have no way of - distinguishing that type of stop (which indicates that we - should just start it again), with a stop due to the pr_trace - field of the prrun_t struct. - - Note that if the SIGTSTP is being caught, we *do* need to pass it, - because the handler needs to get executed. */ - signal_to_pass = 0; - else - signal_to_pass = target_signal_to_host (signo); - - if (signal_to_pass) - { - set_proc_siginfo (pi, signal_to_pass); - } - else - { - pi->prrun.pr_flags |= PRCSIG; - } - pi->nopass_next_sigstop = 0; - if (step) - { - pi->prrun.pr_flags |= PRSTEP; - } - if (ioctl (pi->fd, PIOCRUN, &pi->prrun) != 0) - { - perror_with_name (pi->pathname); - /* NOTREACHED */ - } - - pi->had_event = 0; - - /* Continue all the other threads that haven't had an event of - interest. */ - - if (pid == -1) - for (procinfo = procinfo_list; procinfo; procinfo = procinfo->next) - { - if (pi != procinfo && !procinfo->had_event) - { - procinfo->prrun.pr_flags &= PRSTEP; - procinfo->prrun.pr_flags |= PRCFAULT | PRCSIG; - ioctl (procinfo->fd, PIOCSTATUS, &procinfo->prstatus); - if (ioctl (procinfo->fd, PIOCRUN, &procinfo->prrun) < 0) - { - if (ioctl (procinfo->fd, PIOCSTATUS, &procinfo->prstatus) < 0) - { - fprintf_unfiltered(gdb_stderr, "PIOCSTATUS failed, errno=%d\n", errno); - } - print_sys_errmsg (procinfo->pathname, errno); - error ("PIOCRUN failed"); - } - ioctl (procinfo->fd, PIOCSTATUS, &procinfo->prstatus); - } - } -} - -/* - -LOCAL FUNCTION + void do_detach (int signal) - procfs_fetch_registers -- fetch current registers from inferior +DESCRIPTION -SYNOPSIS + Detach from the current attachee. - void procfs_fetch_registers (int regno) + If signal is non-zero, the attachee is started running again and sent + the specified signal. -DESCRIPTION + If signal is zero and the attachee was not already stopped when we + attached to it, then we make it runnable again when we detach. - Read the current values of the inferior's registers, both the - general register set and floating point registers (if supported) - and update gdb's idea of their current values. + Otherwise, we query whether or not to make the attachee runnable + again, since we may simply want to leave it in the state it was in + when we attached. -*/ + We report any problems, but do not consider them errors, since we + MUST detach even if some things don't seem to go right. This may not + be the ideal situation. (FIXME). + */ static void -procfs_fetch_registers (regno) - int regno; +do_detach (signal) + int signal; { struct procinfo *pi; - pi = current_procinfo; - - if (ioctl (pi->fd, PIOCGREG, &pi->gregset) != -1) - { - supply_gregset (&pi->gregset); - } -#if defined (FP0_REGNUM) - if (ioctl (pi->fd, PIOCGFPREG, &pi->fpregset) != -1) + for (pi = procinfo_list; pi; pi = pi->next) { - supply_fpregset (&pi->fpregset); - } + if (signal) + { + set_proc_siginfo (pi, signal); + } +#ifdef PROCFS_USE_READ_WRITE + pi->saved_exitset.cmd = PCSEXIT; + if (write (pi->ctl_fd, (char *) &pi->saved_exitset, + sizeof (struct sys_ctl)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCSEXIT, &pi->saved_exitset.sysset) < 0) #endif -} - -/* - -LOCAL FUNCTION - - proc_init_failed - called whenever /proc access initialization -fails - -SYNOPSIS - - static void proc_init_failed (struct procinfo *pi, char *why) + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("PIOCSEXIT failed.\n"); + } +#ifdef PROCFS_USE_READ_WRITE + pi->saved_entryset.cmd = PCSENTRY; + if (write (pi->ctl_fd, (char *) &pi->saved_entryset, + sizeof (struct sys_ctl)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCSENTRY, &pi->saved_entryset.sysset) < 0) +#endif + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("PIOCSENTRY failed.\n"); + } +#ifdef PROCFS_USE_READ_WRITE + pi->saved_trace.cmd = PCSTRACE; + if (write (pi->ctl_fd, (char *) &pi->saved_trace, + sizeof (struct sig_ctl)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCSTRACE, &pi->saved_trace.sigset) < 0) +#endif + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("PIOCSTRACE failed.\n"); + } +#ifndef UNIXWARE + if (ioctl (pi->ctl_fd, PIOCSHOLD, &pi->saved_sighold.sigset) < 0) + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("PIOSCHOLD failed.\n"); + } +#endif +#ifdef PROCFS_USE_READ_WRITE + pi->saved_fltset.cmd = PCSFAULT; + if (write (pi->ctl_fd, (char *) &pi->saved_fltset, + sizeof (struct flt_ctl)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCSFAULT, &pi->saved_fltset.fltset) < 0) +#endif + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("PIOCSFAULT failed.\n"); + } + if (!procfs_read_status (pi)) + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("procfs_read_status failed.\n"); + } + else + { + if (signal + || (THE_PR_LWP(pi->prstatus).pr_flags & (PR_STOPPED | PR_ISTOP))) + { + long cmd; + struct proc_ctl pctl; -DESCRIPTION + if (signal || !pi->was_stopped || + query ("Was stopped when attached, make it runnable again? ")) + { + /* Clear any pending signal if we want to detach without + a signal. */ + if (signal == 0) + set_proc_siginfo (pi, signal); + + /* Clear any fault that might have stopped it. */ +#ifdef PROCFS_USE_READ_WRITE + cmd = PCCFAULT; + if (write (pi->ctl_fd, (char *) &cmd, sizeof (long)) < 0) +#else + if (ioctl (pi->ctl_fd, PIOCCFAULT, 0)) +#endif + { + print_sys_errmsg (pi->pathname, errno); + printf_unfiltered ("PIOCCFAULT failed.\n"); + } - This function is called whenever initialization of access to a /proc - entry fails. It prints a suitable error message, does some cleanup, - and then invokes the standard error processing routine which dumps - us back into the command loop. - */ + /* Make it run again when we close it. */ -static void -proc_init_failed (pi, why) - struct procinfo *pi; - char *why; -{ - print_sys_errmsg (pi->pathname, errno); - kill (pi->pid, SIGKILL); - close_proc_file (pi); - error (why); - /* NOTREACHED */ + modify_run_on_last_close_flag (pi->ctl_fd, 1); + } + } + } + close_proc_file (pi); + } + attach_flag = 0; } -/* - -LOCAL FUNCTION - - close_proc_file - close any currently open /proc entry - -SYNOPSIS - - static void close_proc_file (struct procinfo *pip) - -DESCRIPTION +/* emulate wait() as much as possible. + Wait for child to do something. Return pid of child, or -1 in case + of error; store status in *OURSTATUS. - Close any currently open /proc entry and mark the process information - entry as invalid. In order to ensure that we don't try to reuse any - stale information, the pid, fd, and pathnames are explicitly - invalidated, which may be overkill. + Not sure why we can't + just use wait(), but it seems to have problems when applied to a + process being controlled with the /proc interface. - */ + We have a race problem here with no obvious solution. We need to let + the inferior run until it stops on an event of interest, which means + that we need to use the PIOCWSTOP ioctl. However, we cannot use this + ioctl if the process is already stopped on something that is not an + event of interest, or the call will hang indefinitely. Thus we first + use PIOCSTATUS to see if the process is not stopped. If not, then we + use PIOCWSTOP. But during the window between the two, if the process + stops for any reason that is not an event of interest (such as a job + control signal) then gdb will hang. One possible workaround is to set + an alarm to wake up every minute of so and check to see if the process + is still running, and if so, then reissue the PIOCWSTOP. But this is + a real kludge, so has not been implemented. FIXME: investigate + alternatives. -static void -close_proc_file (pip) - struct procinfo *pip; + FIXME: Investigate why wait() seems to have problems with programs + being control by /proc routines. */ +static int +procfs_wait (pid, ourstatus) + int pid; + struct target_waitstatus *ourstatus; { - struct procinfo *procinfo; - - remove_fd (pip); /* Remove fd from poll/select list */ - - close (pip -> fd); + short what; + short why; + int statval = 0; + int checkerr = 0; + int rtnval = -1; + struct procinfo *pi; + struct proc_ctl pctl; - free (pip -> pathname); +scan_again: - /* Unlink pip from the procinfo chain. Note pip might not be on the list. */ + /* handle all syscall events first, otherwise we might not + notice a thread was created until too late. */ - if (procinfo_list == pip) - procinfo_list = pip->next; - else - for (procinfo = procinfo_list; procinfo; procinfo = procinfo->next) - if (procinfo->next == pip) - procinfo->next = pip->next; + for (pi = procinfo_list; pi; pi = pi->next) + { + if (!pi->had_event) + continue; - free (pip); -} + if (! (THE_PR_LWP(pi->prstatus).pr_flags & (PR_STOPPED | PR_ISTOP)) ) + continue; -/* + why = THE_PR_LWP(pi->prstatus).pr_why; + what = THE_PR_LWP(pi->prstatus).pr_what; + if (why == PR_SYSENTRY || why == PR_SYSEXIT) + { + int i; + int found_handler = 0; -LOCAL FUNCTION + for (i = 0; i < pi->num_syscall_handlers; i++) + if (pi->syscall_handlers[i].syscall_num == what) + { + found_handler = 1; + pi->saved_rtnval = pi->pid; + pi->saved_statval = 0; + if (!pi->syscall_handlers[i].func + (pi, what, why, &pi->saved_rtnval, &pi->saved_statval)) + pi->had_event = 0; + break; + } - open_proc_file - open a /proc entry for a given process id + if (!found_handler) + { + if (why == PR_SYSENTRY) + error ("PR_SYSENTRY, unhandled system call %d", what); + else + error ("PR_SYSEXIT, unhandled system call %d", what); + } + } + } -SYNOPSIS + /* find a relevant process with an event */ - static int open_proc_file (int pid, struct procinfo *pip, int mode) + for (pi = procinfo_list; pi; pi = pi->next) + if (pi->had_event && (pid == -1 || pi->pid == pid)) + break; -DESCRIPTION + if (!pi) + { + wait_fd (); + goto scan_again; + } - Given a process id and a mode, close the existing open /proc - entry (if any) and open one for the new process id, in the - specified mode. Once it is open, then mark the local process - information structure as valid, which guarantees that the pid, - fd, and pathname fields match an open /proc entry. Returns - zero if the open fails, nonzero otherwise. + if (!checkerr + && !(THE_PR_LWP(pi->prstatus).pr_flags & (PR_STOPPED | PR_ISTOP))) + { + if (!procfs_write_pcwstop (pi)) + { + checkerr++; + } + } + if (checkerr) + { + if (errno == ENOENT) + { + /* XXX Fixme -- what to do if attached? Can't call wait... */ + rtnval = wait (&statval); + if ((rtnval) != (PIDGET (inferior_pid))) + { + print_sys_errmsg (pi->pathname, errno); + error ("procfs_wait: wait failed, returned %d", rtnval); + /* NOTREACHED */ + } + } + else + { + print_sys_errmsg (pi->pathname, errno); + error ("PIOCSTATUS or PIOCWSTOP failed."); + /* NOTREACHED */ + } + } + else if (THE_PR_LWP(pi->prstatus).pr_flags & (PR_STOPPED | PR_ISTOP)) + { +#ifdef UNIXWARE + rtnval = pi->prstatus.pr_pid; +#else + rtnval = pi->pid; +#endif + why = THE_PR_LWP(pi->prstatus).pr_why; + what = THE_PR_LWP(pi->prstatus).pr_what; - Note that the pathname is left intact, even when the open fails, - so that callers can use it to construct meaningful error messages - rather than just "file open failed". - */ + switch (why) + { + case PR_SIGNALLED: + statval = (what << 8) | 0177; + break; + case PR_SYSENTRY: + case PR_SYSEXIT: + rtnval = pi->saved_rtnval; + statval = pi->saved_statval; + break; + case PR_REQUESTED: + statval = (SIGSTOP << 8) | 0177; + break; + case PR_JOBCONTROL: + statval = (what << 8) | 0177; + break; + case PR_FAULTED: + switch (what) + { +#ifdef FLTWATCH + case FLTWATCH: + statval = (SIGTRAP << 8) | 0177; + break; +#endif +#ifdef FLTKWATCH + case FLTKWATCH: + statval = (SIGTRAP << 8) | 0177; + break; +#endif +#ifndef FAULTED_USE_SIGINFO + /* Irix, contrary to the documentation, fills in 0 for si_signo. + Solaris fills in si_signo. I'm not sure about others. */ + case FLTPRIV: + case FLTILL: + statval = (SIGILL << 8) | 0177; + break; + case FLTBPT: + case FLTTRACE: + statval = (SIGTRAP << 8) | 0177; + break; + case FLTSTACK: + case FLTACCESS: + case FLTBOUNDS: + statval = (SIGSEGV << 8) | 0177; + break; + case FLTIOVF: + case FLTIZDIV: + case FLTFPE: + statval = (SIGFPE << 8) | 0177; + break; + case FLTPAGE: /* Recoverable page fault */ +#endif /* not FAULTED_USE_SIGINFO */ + default: + /* Use the signal which the kernel assigns. This is better than + trying to second-guess it from the fault. In fact, I suspect + that FLTACCESS can be either SIGSEGV or SIGBUS. */ + statval = + ((THE_PR_LWP(pi->prstatus).pr_info.si_signo) << 8) | 0177; + break; + } + break; + default: + error ("PIOCWSTOP, unknown why %d, what %d", why, what); + } + /* Stop all the other threads when any of them stops. */ -static int -open_proc_file (pid, pip, mode) + { + struct procinfo *procinfo, *next_pi; + + for (procinfo = procinfo_list; procinfo; procinfo = next_pi) + { + next_pi = procinfo->next; + if (!procinfo->had_event) + { +#ifdef PROCFS_USE_READ_WRITE + long cmd = PCSTOP; + if (write (pi->ctl_fd, (char *) &cmd, sizeof (long)) < 0) + { + print_sys_errmsg (procinfo->pathname, errno); + error ("PCSTOP failed"); + } +#else + /* A bug in Solaris (2.5) causes us to hang when trying to + stop a stopped process. So, we have to check first in + order to avoid the hang. */ + if (!procfs_read_status (procinfo)) + { + /* The LWP has apparently terminated. */ + if (info_verbose) + printf_filtered ("LWP %d doesn't respond.\n", + (procinfo->pid >> 16) & 0xffff); + close_proc_file (procinfo); + continue; + } + + if (!(procinfo->prstatus.pr_flags & PR_STOPPED)) + if (ioctl (procinfo->ctl_fd, PIOCSTOP, &procinfo->prstatus) + < 0) + { + print_sys_errmsg (procinfo->pathname, errno); + warning ("PIOCSTOP failed"); + } +#endif + } + } + } + } + else + { + error ("PIOCWSTOP, stopped for unknown/unhandled reason, flags %#x", + THE_PR_LWP(pi->prstatus).pr_flags); + } + + store_waitstatus (ourstatus, statval); + + if (rtnval == -1) /* No more children to wait for */ + { + warning ("Child process unexpectedly missing"); + /* Claim it exited with unknown signal. */ + ourstatus->kind = TARGET_WAITKIND_SIGNALLED; + ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; + return rtnval; + } + + pi->had_event = 0; /* Indicate that we've seen this one */ + return (rtnval); +} + +/* + +LOCAL FUNCTION + + set_proc_siginfo - set a process's current signal info + +SYNOPSIS + + void set_proc_siginfo (struct procinfo *pip, int signo); + +DESCRIPTION + + Given a pointer to a process info struct in PIP and a signal number + in SIGNO, set the process's current signal and its associated signal + information. The signal will be delivered to the process immediately + after execution is resumed, even if it is being held. In addition, + this particular delivery will not cause another PR_SIGNALLED stop + even if the signal is being traced. + + If we are not delivering the same signal that the prstatus siginfo + struct contains information about, then synthesize a siginfo struct + to match the signal we are going to deliver, make it of the type + "generated by a user process", and send this synthesized copy. When + used to set the inferior's signal state, this will be required if we + are not currently stopped because of a traced signal, or if we decide + to continue with a different signal. + + Note that when continuing the inferior from a stop due to receipt + of a traced signal, we either have set PRCSIG to clear the existing + signal, or we have to call this function to do a PIOCSSIG with either + the existing siginfo struct from pr_info, or one we have synthesized + appropriately for the signal we want to deliver. Otherwise if the + signal is still being traced, the inferior will immediately stop + again. + + See siginfo(5) for more details. +*/ + +static void +set_proc_siginfo (pip, signo) + struct procinfo *pip; + int signo; +{ + struct siginfo newsiginfo; + struct siginfo *sip; + struct sigi_ctl sictl; + +#ifdef PROCFS_DONT_PIOCSSIG_CURSIG + /* With Alpha OSF/1 procfs, the kernel gets really confused if it + receives a PIOCSSIG with a signal identical to the current signal, + it messes up the current signal. Work around the kernel bug. */ + if (signo == THE_PR_LWP(pip->prstatus).pr_cursig) + return; +#endif + +#ifdef UNIXWARE + if (signo == THE_PR_LWP(pip->prstatus).pr_info.si_signo) + { + memcpy ((char *) &sictl.siginfo, (char *) &pip->prstatus.pr_lwp.pr_info, + sizeof (siginfo_t)); + } +#else + if (signo == THE_PR_LWP(pip->prstatus).pr_info.si_signo) + { + sip = &pip -> prstatus.pr_info; + } +#endif + else + { +#ifdef UNIXWARE + siginfo_t *sip = &sictl.siginfo; + memset ((char *) sip, 0, sizeof (siginfo_t)); +#else + memset ((char *) &newsiginfo, 0, sizeof (newsiginfo)); + sip = &newsiginfo; +#endif + sip -> si_signo = signo; + sip -> si_code = 0; + sip -> si_errno = 0; + sip -> si_pid = getpid (); + sip -> si_uid = getuid (); + } +#ifdef PROCFS_USE_READ_WRITE + sictl.cmd = PCSSIG; + if (write (pip->ctl_fd, (char *) &sictl, sizeof (struct sigi_ctl)) < 0) +#else + if (ioctl (pip->ctl_fd, PIOCSSIG, sip) < 0) +#endif + { + print_sys_errmsg (pip -> pathname, errno); + warning ("PIOCSSIG failed"); + } +} + +/* Resume execution of process PID. If STEP is nozero, then + just single step it. If SIGNAL is nonzero, restart it with that + signal activated. */ + +static void +procfs_resume (pid, step, signo) int pid; + int step; + enum target_signal signo; +{ + int signal_to_pass; + struct procinfo *pi, *procinfo, *next_pi; + struct proc_ctl pctl; + + pi = find_procinfo (pid == -1 ? inferior_pid : pid, 0); + + errno = 0; +#ifdef UNIXWARE + pctl.cmd = PCRUN; + pctl.data = PRCFAULT; +#else + pi->prrun.pr_flags = PRSTRACE | PRSFAULT | PRCFAULT; +#endif + +#if 0 + /* It should not be necessary. If the user explicitly changes the value, + value_assign calls write_register_bytes, which writes it. */ +/* It may not be absolutely necessary to specify the PC value for + restarting, but to be safe we use the value that gdb considers + to be current. One case where this might be necessary is if the + user explicitly changes the PC value that gdb considers to be + current. FIXME: Investigate if this is necessary or not. */ + +#ifdef PRSVADDR_BROKEN +/* Can't do this under Solaris running on a Sparc, as there seems to be no + place to put nPC. In fact, if you use this, nPC seems to be set to some + random garbage. We have to rely on the fact that PC and nPC have been + written previously via PIOCSREG during a register flush. */ + + pi->prrun.pr_vaddr = (caddr_t) *(int *) ®isters[REGISTER_BYTE (PC_REGNUM)]; + pi->prrun.pr_flags != PRSVADDR; +#endif +#endif + + if (signo == TARGET_SIGNAL_STOP && pi->nopass_next_sigstop) + /* When attaching to a child process, if we forced it to stop with + a PIOCSTOP, then we will have set the nopass_next_sigstop flag. + Upon resuming the first time after such a stop, we explicitly + inhibit sending it another SIGSTOP, which would be the normal + result of default signal handling. One potential drawback to + this is that we will also ignore any attempt to by the user + to explicitly continue after the attach with a SIGSTOP. Ultimately + this problem should be dealt with by making the routines that + deal with the inferior a little smarter, and possibly even allow + an inferior to continue running at the same time as gdb. (FIXME?) */ + signal_to_pass = 0; + else if (signo == TARGET_SIGNAL_TSTP + && THE_PR_LWP(pi->prstatus).pr_cursig == SIGTSTP + && THE_PR_LWP(pi->prstatus).pr_action.sa_handler == SIG_DFL + ) + + /* We are about to pass the inferior a SIGTSTP whose action is + SIG_DFL. The SIG_DFL action for a SIGTSTP is to stop + (notifying the parent via wait()), and then keep going from the + same place when the parent is ready for you to keep going. So + under the debugger, it should do nothing (as if the program had + been stopped and then later resumed. Under ptrace, this + happens for us, but under /proc, the system obligingly stops + the process, and wait_for_inferior would have no way of + distinguishing that type of stop (which indicates that we + should just start it again), with a stop due to the pr_trace + field of the prrun_t struct. + + Note that if the SIGTSTP is being caught, we *do* need to pass it, + because the handler needs to get executed. */ + signal_to_pass = 0; + else + signal_to_pass = target_signal_to_host (signo); + + if (signal_to_pass) + { + set_proc_siginfo (pi, signal_to_pass); + } + else + { +#ifdef UNIXWARE + pctl.data |= PRCSIG; +#else + pi->prrun.pr_flags |= PRCSIG; +#endif + } + pi->nopass_next_sigstop = 0; + if (step) + { +#ifdef UNIXWARE + pctl.data |= PRSTEP; +#else + pi->prrun.pr_flags |= PRSTEP; +#endif + } + pi->had_event = 0; + /* Don't try to start a process unless it's stopped on an + `event of interest'. Doing so will cause errors. */ + + if (!procfs_read_status (pi)) + { + /* The LWP has apparently terminated. */ + if (info_verbose) + printf_filtered ("LWP %d doesn't respond.\n", + (pi->pid >> 16) & 0xffff); + close_proc_file (pi); + } + else + { +#ifdef PROCFS_USE_READ_WRITE + if (write (pi->ctl_fd, (char *) &pctl, sizeof (struct proc_ctl)) < 0) +#else + if ((pi->prstatus.pr_flags & PR_ISTOP) + && ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) +#endif + { + /* The LWP has apparently terminated. */ + if (info_verbose) + printf_filtered ("LWP %d doesn't respond.\n", + (pi->pid >> 16) & 0xffff); + close_proc_file (pi); + } + } + + /* Continue all the other threads that haven't had an event of interest. + Also continue them if they have NOPASS_NEXT_SIGSTOP set; this is only + set by do_attach, and means this is the first resume after an attach. + All threads were CSTOP'd by do_attach, and should be resumed now. */ + + if (pid == -1) + for (procinfo = procinfo_list; procinfo; procinfo = next_pi) + { + next_pi = procinfo->next; + if (pi != procinfo) + if (!procinfo->had_event || + (procinfo->nopass_next_sigstop && signo == TARGET_SIGNAL_STOP)) + { + procinfo->had_event = procinfo->nopass_next_sigstop = 0; +#ifdef PROCFS_USE_READ_WRITE + pctl.data = PRCFAULT | PRCSIG; + if (write (procinfo->ctl_fd, (char *) &pctl, + sizeof (struct proc_ctl)) < 0) + { + if (!procfs_read_status (procinfo)) + fprintf_unfiltered(gdb_stderr, + "procfs_read_status failed, errno=%d\n", + errno); + print_sys_errmsg (procinfo->pathname, errno); + error ("PCRUN failed"); + } +#else + procinfo->prrun.pr_flags &= PRSTEP; + procinfo->prrun.pr_flags |= PRCFAULT | PRCSIG; + if (!procfs_read_status (procinfo)) + { + /* The LWP has apparently terminated. */ + if (info_verbose) + printf_filtered ("LWP %d doesn't respond.\n", + (procinfo->pid >> 16) & 0xffff); + close_proc_file (procinfo); + continue; + } + + /* Don't try to start a process unless it's stopped on an + `event of interest'. Doing so will cause errors. */ + + if ((procinfo->prstatus.pr_flags & PR_ISTOP) + && ioctl (procinfo->ctl_fd, PIOCRUN, &procinfo->prrun) < 0) + { + if (!procfs_read_status (procinfo)) + fprintf_unfiltered(gdb_stderr, + "procfs_read_status failed, errno=%d\n", + errno); + print_sys_errmsg (procinfo->pathname, errno); + warning ("PIOCRUN failed"); + } +#endif + } + procfs_read_status (procinfo); + } +} + +/* + +LOCAL FUNCTION + + procfs_fetch_registers -- fetch current registers from inferior + +SYNOPSIS + + void procfs_fetch_registers (int regno) + +DESCRIPTION + + Read the current values of the inferior's registers, both the + general register set and floating point registers (if supported) + and update gdb's idea of their current values. + +*/ + +static void +procfs_fetch_registers (regno) + int regno; +{ + struct procinfo *pi; + + pi = current_procinfo; + +#ifdef UNIXWARE + if (procfs_read_status (pi)) + { + supply_gregset (&pi->prstatus.pr_lwp.pr_context.uc_mcontext.gregs); +#if defined (FP0_REGNUM) + supply_fpregset (&pi->prstatus.pr_lwp.pr_context.uc_mcontext.fpregs); +#endif + } +#else /* UNIXWARE */ + if (ioctl (pi->ctl_fd, PIOCGREG, &pi->gregset.gregset) != -1) + { + supply_gregset (&pi->gregset.gregset); + } +#if defined (FP0_REGNUM) + if (ioctl (pi->ctl_fd, PIOCGFPREG, &pi->fpregset.fpregset) != -1) + { + supply_fpregset (&pi->fpregset.fpregset); + } +#endif +#endif /* UNIXWARE */ +} + +/* + +LOCAL FUNCTION + + proc_init_failed - called when /proc access initialization fails +fails + +SYNOPSIS + + static void proc_init_failed (struct procinfo *pi, + char *why, int kill_p) + +DESCRIPTION + + This function is called whenever initialization of access to a /proc + entry fails. It prints a suitable error message, does some cleanup, + and then invokes the standard error processing routine which dumps + us back into the command loop. If KILL_P is true, sends SIGKILL. + */ + +static void +proc_init_failed (pi, why, kill_p) + struct procinfo *pi; + char *why; + int kill_p; +{ + print_sys_errmsg (pi->pathname, errno); + if (kill_p) + kill (pi->pid, SIGKILL); + close_proc_file (pi); + error (why); + /* NOTREACHED */ +} + +/* + +LOCAL FUNCTION + + close_proc_file - close any currently open /proc entry + +SYNOPSIS + + static void close_proc_file (struct procinfo *pip) + +DESCRIPTION + + Close any currently open /proc entry and mark the process information + entry as invalid. In order to ensure that we don't try to reuse any + stale information, the pid, fd, and pathnames are explicitly + invalidated, which may be overkill. + + */ + +static void +close_proc_file (pip) + struct procinfo *pip; +{ + struct procinfo *procinfo; + + delete_thread (pip->pid); /* remove thread from GDB's thread list */ + remove_fd (pip); /* Remove fd from poll/select list */ + + close (pip->ctl_fd); +#ifdef HAVE_MULTIPLE_PROC_FDS + close (pip->as_fd); + close (pip->status_fd); + close (pip->map_fd); +#endif + + free (pip -> pathname); + + /* Unlink pip from the procinfo chain. Note pip might not be on the list. */ + + if (procinfo_list == pip) + procinfo_list = pip->next; + else + { + for (procinfo = procinfo_list; procinfo; procinfo = procinfo->next) + { + if (procinfo->next == pip) + { + procinfo->next = pip->next; + break; + } + } + free (pip); + } +} + +static void +close_proc_file_cleanup (pip) + void *pip; +{ + close_proc_file ((struct procinfo *) pip); +} + +static struct cleanup * +make_cleanup_close_proc_file (pip) + struct procinfo *pip; +{ + return make_cleanup (close_proc_file_cleanup, pip); +} + +/* + +LOCAL FUNCTION + + open_proc_file - open a /proc entry for a given process id + +SYNOPSIS + + static int open_proc_file (int pid, struct procinfo *pip, int mode) + +DESCRIPTION + + Given a process id and a mode, close the existing open /proc + entry (if any) and open one for the new process id, in the + specified mode. Once it is open, then mark the local process + information structure as valid, which guarantees that the pid, + fd, and pathname fields match an open /proc entry. Returns + zero if the open fails, nonzero otherwise. + + Note that the pathname is left intact, even when the open fails, + so that callers can use it to construct meaningful error messages + rather than just "file open failed". + + Note that for Solaris, the process-id also includes an LWP-id, so we + actually attempt to open that. If we are handed a pid with a 0 LWP-id, + then we will ask the kernel what it is and add it to the pid. Hence, + the pid can be changed by us. + */ + +static int +open_proc_file (pid, pip, mode, control) + int pid; + struct procinfo *pip; + int mode; + int control; +{ + int tmp, tmpfd; + + pip -> next = NULL; + pip -> had_event = 0; + pip -> pathname = xmalloc (MAX_PROC_NAME_SIZE); + pip -> pid = pid; + +#ifndef PIOCOPENLWP + tmp = pid; +#else + tmp = pid & 0xffff; +#endif + +#ifdef HAVE_MULTIPLE_PROC_FDS + sprintf (pip->pathname, STATUS_PROC_NAME_FMT, tmp); + if ((pip->status_fd = open (pip->pathname, O_RDONLY)) < 0) + { + return 0; + } + + sprintf (pip->pathname, AS_PROC_NAME_FMT, tmp); + if ((pip->as_fd = open (pip->pathname, O_RDWR)) < 0) + { + close (pip->status_fd); + return 0; + } + + sprintf (pip->pathname, MAP_PROC_NAME_FMT, tmp); + if ((pip->map_fd = open (pip->pathname, O_RDONLY)) < 0) + { + close (pip->status_fd); + close (pip->as_fd); + return 0; + } + + if (control) + { + sprintf (pip->pathname, CTL_PROC_NAME_FMT, tmp); + if ((pip->ctl_fd = open (pip->pathname, O_WRONLY)) < 0) + { + close (pip->status_fd); + close (pip->as_fd); + close (pip->map_fd); + return 0; + } + } + +#else /* HAVE_MULTIPLE_PROC_FDS */ + sprintf (pip -> pathname, CTL_PROC_NAME_FMT, tmp); + + if ((tmpfd = open (pip -> pathname, mode)) < 0) + return 0; + +#ifndef PIOCOPENLWP + pip -> ctl_fd = tmpfd; + pip -> as_fd = tmpfd; + pip -> map_fd = tmpfd; + pip -> status_fd = tmpfd; +#else + tmp = (pid >> 16) & 0xffff; /* Extract thread id */ + + if (tmp == 0) + { /* Don't know thread id yet */ + if (ioctl (tmpfd, PIOCSTATUS, &pip -> prstatus) < 0) + { + print_sys_errmsg (pip -> pathname, errno); + close (tmpfd); + error ("open_proc_file: PIOCSTATUS failed"); + } + + tmp = pip -> prstatus.pr_who; /* Get thread id from prstatus_t */ + pip -> pid = (tmp << 16) | pid; /* Update pip */ + } + + if ((pip -> ctl_fd = ioctl (tmpfd, PIOCOPENLWP, &tmp)) < 0) + { + close (tmpfd); + return 0; + } + +#ifdef PIOCSET /* New method */ + { + long pr_flags; + pr_flags = PR_ASYNC; + ioctl (pip -> ctl_fd, PIOCSET, &pr_flags); + } +#endif + + /* keep extra fds in sync */ + pip->as_fd = pip->ctl_fd; + pip->map_fd = pip->ctl_fd; + pip->status_fd = pip->ctl_fd; + + close (tmpfd); /* All done with main pid */ +#endif /* PIOCOPENLWP */ + +#endif /* HAVE_MULTIPLE_PROC_FDS */ + + return 1; +} + +static char * +mappingflags (flags) + long flags; +{ + static char asciiflags[8]; + + strcpy (asciiflags, "-------"); +#if defined (MA_PHYS) + if (flags & MA_PHYS) asciiflags[0] = 'd'; +#endif + if (flags & MA_STACK) asciiflags[1] = 's'; + if (flags & MA_BREAK) asciiflags[2] = 'b'; + if (flags & MA_SHARED) asciiflags[3] = 's'; + if (flags & MA_READ) asciiflags[4] = 'r'; + if (flags & MA_WRITE) asciiflags[5] = 'w'; + if (flags & MA_EXEC) asciiflags[6] = 'x'; + return (asciiflags); +} + +static void +info_proc_flags (pip, summary) + struct procinfo *pip; + int summary; +{ + struct trans *transp; +#ifdef UNIXWARE + long flags = pip->prstatus.pr_flags | pip->prstatus.pr_lwp.pr_flags; +#else + long flags = pip->prstatus.pr_flags; +#endif + + printf_filtered ("%-32s", "Process status flags:"); + if (!summary) + { + printf_filtered ("\n\n"); + } + for (transp = pr_flag_table; transp -> name != NULL; transp++) + { + if (flags & transp -> value) + { + if (summary) + { + printf_filtered ("%s ", transp -> name); + } + else + { + printf_filtered ("\t%-16s %s.\n", transp -> name, transp -> desc); + } + } + } + printf_filtered ("\n"); +} + +static void +info_proc_stop (pip, summary) + struct procinfo *pip; + int summary; +{ + struct trans *transp; + int why; + int what; + + why = THE_PR_LWP(pip->prstatus).pr_why; + what = THE_PR_LWP(pip->prstatus).pr_what; + + if (THE_PR_LWP(pip->prstatus).pr_flags & PR_STOPPED) + { + printf_filtered ("%-32s", "Reason for stopping:"); + if (!summary) + { + printf_filtered ("\n\n"); + } + for (transp = pr_why_table; transp -> name != NULL; transp++) + { + if (why == transp -> value) + { + if (summary) + { + printf_filtered ("%s ", transp -> name); + } + else + { + printf_filtered ("\t%-16s %s.\n", + transp -> name, transp -> desc); + } + break; + } + } + + /* Use the pr_why field to determine what the pr_what field means, and + print more information. */ + + switch (why) + { + case PR_REQUESTED: + /* pr_what is unused for this case */ + break; + case PR_JOBCONTROL: + case PR_SIGNALLED: + if (summary) + { + printf_filtered ("%s ", signalname (what)); + } + else + { + printf_filtered ("\t%-16s %s.\n", signalname (what), + safe_strsignal (what)); + } + break; + case PR_SYSENTRY: + if (summary) + { + printf_filtered ("%s ", syscallname (what)); + } + else + { + printf_filtered ("\t%-16s %s.\n", syscallname (what), + "Entered this system call"); + } + break; + case PR_SYSEXIT: + if (summary) + { + printf_filtered ("%s ", syscallname (what)); + } + else + { + printf_filtered ("\t%-16s %s.\n", syscallname (what), + "Returned from this system call"); + } + break; + case PR_FAULTED: + if (summary) + { + printf_filtered ("%s ", + lookupname (faults_table, what, "fault")); + } + else + { + printf_filtered ("\t%-16s %s.\n", + lookupname (faults_table, what, "fault"), + lookupdesc (faults_table, what)); + } + break; + } + printf_filtered ("\n"); + } +} + +static void +info_proc_siginfo (pip, summary) + struct procinfo *pip; + int summary; +{ + struct siginfo *sip; + + if ((THE_PR_LWP(pip->prstatus).pr_flags & PR_STOPPED) && + (THE_PR_LWP(pip->prstatus).pr_why == PR_SIGNALLED || + THE_PR_LWP(pip->prstatus).pr_why == PR_FAULTED)) + { + printf_filtered ("%-32s", "Additional signal/fault info:"); + sip = &(THE_PR_LWP(pip->prstatus).pr_info); + if (summary) + { + printf_filtered ("%s ", signalname (sip -> si_signo)); + if (sip -> si_errno > 0) + { + printf_filtered ("%s ", errnoname (sip -> si_errno)); + } + if (sip -> si_code <= 0) + { + printf_filtered ("sent by %s, uid %d ", + target_pid_to_str (sip -> si_pid), + sip -> si_uid); + } + else + { + printf_filtered ("%s ", sigcodename (sip)); + if ((sip -> si_signo == SIGILL) || + (sip -> si_signo == SIGFPE) || + (sip -> si_signo == SIGSEGV) || + (sip -> si_signo == SIGBUS)) + { + printf_filtered ("addr=%#lx ", + (unsigned long) sip -> si_addr); + } + else if ((sip -> si_signo == SIGCHLD)) + { + printf_filtered ("child %s, status %u ", + target_pid_to_str (sip -> si_pid), + sip -> si_status); + } + else if ((sip -> si_signo == SIGPOLL)) + { + printf_filtered ("band %u ", sip -> si_band); + } + } + } + else + { + printf_filtered ("\n\n"); + printf_filtered ("\t%-16s %s.\n", signalname (sip -> si_signo), + safe_strsignal (sip -> si_signo)); + if (sip -> si_errno > 0) + { + printf_filtered ("\t%-16s %s.\n", + errnoname (sip -> si_errno), + safe_strerror (sip -> si_errno)); + } + if (sip -> si_code <= 0) + { + printf_filtered ("\t%-16u %s\n", sip -> si_pid, /* XXX need target_pid_to_str() */ + "PID of process sending signal"); + printf_filtered ("\t%-16u %s\n", sip -> si_uid, + "UID of process sending signal"); + } + else + { + printf_filtered ("\t%-16s %s.\n", sigcodename (sip), + sigcodedesc (sip)); + if ((sip -> si_signo == SIGILL) || + (sip -> si_signo == SIGFPE)) + { + printf_filtered ("\t%#-16lx %s.\n", + (unsigned long) sip -> si_addr, + "Address of faulting instruction"); + } + else if ((sip -> si_signo == SIGSEGV) || + (sip -> si_signo == SIGBUS)) + { + printf_filtered ("\t%#-16lx %s.\n", + (unsigned long) sip -> si_addr, + "Address of faulting memory reference"); + } + else if ((sip -> si_signo == SIGCHLD)) + { + printf_filtered ("\t%-16u %s.\n", sip -> si_pid, /* XXX need target_pid_to_str() */ + "Child process ID"); + printf_filtered ("\t%-16u %s.\n", sip -> si_status, + "Child process exit value or signal"); + } + else if ((sip -> si_signo == SIGPOLL)) + { + printf_filtered ("\t%-16u %s.\n", sip -> si_band, + "Band event for POLL_{IN,OUT,MSG}"); + } + } + } + printf_filtered ("\n"); + } +} + +static void +info_proc_syscalls (pip, summary) struct procinfo *pip; - int mode; + int summary; { - pip -> next = NULL; - pip -> had_event = 0; - pip -> pathname = xmalloc (32); - pip -> pid = pid; + int syscallnum; - sprintf (pip -> pathname, PROC_NAME_FMT, pid); - if ((pip -> fd = open (pip -> pathname, mode)) < 0) - return 0; + if (!summary) + { - return 1; +#if 0 /* FIXME: Needs to use gdb-wide configured info about system calls. */ + if (pip -> prstatus.pr_flags & PR_ASLEEP) + { + int syscallnum = pip -> prstatus.pr_reg[R_D0]; + if (summary) + { + printf_filtered ("%-32s", "Sleeping in system call:"); + printf_filtered ("%s", syscallname (syscallnum)); + } + else + { + printf_filtered ("Sleeping in system call '%s'.\n", + syscallname (syscallnum)); + } + } +#endif + +#ifndef UNIXWARE + if (ioctl (pip -> ctl_fd, PIOCGENTRY, &pip -> entryset) < 0) + { + print_sys_errmsg (pip -> pathname, errno); + error ("PIOCGENTRY failed"); + } + + if (ioctl (pip -> ctl_fd, PIOCGEXIT, &pip -> exitset) < 0) + { + print_sys_errmsg (pip -> pathname, errno); + error ("PIOCGEXIT failed"); + } +#endif + + printf_filtered ("System call tracing information:\n\n"); + + printf_filtered ("\t%-12s %-8s %-8s\n", + "System call", + "Entry", + "Exit"); + for (syscallnum = 0; syscallnum < MAX_SYSCALLS; syscallnum++) + { + QUIT; + if (syscall_table[syscallnum] != NULL) + printf_filtered ("\t%-12s ", syscall_table[syscallnum]); + else + printf_filtered ("\t%-12d ", syscallnum); + +#ifdef UNIXWARE + printf_filtered ("%-8s ", + prismember (&pip->prstatus.pr_sysentry, syscallnum) + ? "on" : "off"); + printf_filtered ("%-8s ", + prismember (&pip->prstatus.pr_sysexit, syscallnum) + ? "on" : "off"); +#else + printf_filtered ("%-8s ", + prismember (&pip -> entryset, syscallnum) + ? "on" : "off"); + printf_filtered ("%-8s ", + prismember (&pip -> exitset, syscallnum) + ? "on" : "off"); +#endif + printf_filtered ("\n"); + } + printf_filtered ("\n"); + } } static char * -mappingflags (flags) - long flags; +signalname (signo) + int signo; { - static char asciiflags[8]; - - strcpy (asciiflags, "-------"); -#if defined (MA_PHYS) - if (flags & MA_PHYS) asciiflags[0] = 'd'; -#endif - if (flags & MA_STACK) asciiflags[1] = 's'; - if (flags & MA_BREAK) asciiflags[2] = 'b'; - if (flags & MA_SHARED) asciiflags[3] = 's'; - if (flags & MA_READ) asciiflags[4] = 'r'; - if (flags & MA_WRITE) asciiflags[5] = 'w'; - if (flags & MA_EXEC) asciiflags[6] = 'x'; - return (asciiflags); + const char *name; + static char locbuf[32]; + + name = strsigno (signo); + if (name == NULL) + { + sprintf (locbuf, "Signal %d", signo); + } + else + { + sprintf (locbuf, "%s (%d)", name, signo); + } + return (locbuf); +} + +static char * +errnoname (errnum) + int errnum; +{ + const char *name; + static char locbuf[32]; + + name = strerrno (errnum); + if (name == NULL) + { + sprintf (locbuf, "Errno %d", errnum); + } + else + { + sprintf (locbuf, "%s (%d)", name, errnum); + } + return (locbuf); } static void -info_proc_flags (pip, summary) +info_proc_signals (pip, summary) struct procinfo *pip; int summary; { - struct trans *transp; + int signo; - printf_filtered ("%-32s", "Process status flags:"); if (!summary) { - printf_filtered ("\n\n"); - } - for (transp = pr_flag_table; transp -> name != NULL; transp++) - { - if (pip -> prstatus.pr_flags & transp -> value) +#ifndef PROCFS_USE_READ_WRITE + if (ioctl (pip -> ctl_fd, PIOCGTRACE, &pip -> trace) < 0) { - if (summary) - { - printf_filtered ("%s ", transp -> name); - } + print_sys_errmsg (pip -> pathname, errno); + error ("PIOCGTRACE failed"); + } +#endif + + printf_filtered ("Disposition of signals:\n\n"); + printf_filtered ("\t%-15s %-8s %-8s %-8s %s\n\n", + "Signal", "Trace", "Hold", "Pending", "Description"); + for (signo = 0; signo < NSIG; signo++) + { + QUIT; + printf_filtered ("\t%-15s ", signalname (signo)); +#ifdef UNIXWARE + printf_filtered ("%-8s ", + prismember (&pip -> prstatus.pr_sigtrace, signo) + ? "on" : "off"); + printf_filtered ("%-8s ", + prismember (&pip -> prstatus.pr_lwp.pr_context.uc_sigmask, signo) + ? "on" : "off"); +#else + printf_filtered ("%-8s ", + prismember (&pip -> trace, signo) + ? "on" : "off"); + printf_filtered ("%-8s ", + prismember (&pip -> prstatus.pr_sighold, signo) + ? "on" : "off"); +#endif + +#ifdef UNIXWARE + if (prismember (&pip->prstatus.pr_sigpend, signo) || + prismember (&pip->prstatus.pr_lwp.pr_lwppend, signo)) + printf_filtered("%-8s ", "yes"); else - { - printf_filtered ("\t%-16s %s.\n", transp -> name, transp -> desc); - } + printf_filtered("%-8s ", "no"); +#else /* UNIXWARE */ +#ifdef PROCFS_SIGPEND_OFFSET + /* Alpha OSF/1 numbers the pending signals from 1. */ + printf_filtered ("%-8s ", + (signo ? prismember (&pip -> prstatus.pr_sigpend, + signo - 1) + : 0) + ? "yes" : "no"); +#else + printf_filtered ("%-8s ", + prismember (&pip -> prstatus.pr_sigpend, signo) + ? "yes" : "no"); +#endif +#endif /* UNIXWARE */ + printf_filtered (" %s\n", safe_strsignal (signo)); } + printf_filtered ("\n"); } - printf_filtered ("\n"); } static void -info_proc_stop (pip, summary) +info_proc_faults (pip, summary) struct procinfo *pip; int summary; { struct trans *transp; - int why; - int what; - - why = pip -> prstatus.pr_why; - what = pip -> prstatus.pr_what; - if (pip -> prstatus.pr_flags & PR_STOPPED) + if (!summary) { - printf_filtered ("%-32s", "Reason for stopping:"); - if (!summary) - { - printf_filtered ("\n\n"); - } - for (transp = pr_why_table; transp -> name != NULL; transp++) +#ifndef UNIXWARE + if (ioctl (pip -> ctl_fd, PIOCGFAULT, &pip->fltset.fltset) < 0) { - if (why == transp -> value) - { - if (summary) - { - printf_filtered ("%s ", transp -> name); - } - else - { - printf_filtered ("\t%-16s %s.\n", - transp -> name, transp -> desc); - } - break; - } + print_sys_errmsg (pip -> pathname, errno); + error ("PIOCGFAULT failed"); } +#endif - /* Use the pr_why field to determine what the pr_what field means, and - print more information. */ - - switch (why) + printf_filtered ("Current traced hardware fault set:\n\n"); + printf_filtered ("\t%-12s %-8s\n", "Fault", "Trace"); + + for (transp = faults_table; transp -> name != NULL; transp++) { - case PR_REQUESTED: - /* pr_what is unused for this case */ - break; - case PR_JOBCONTROL: - case PR_SIGNALLED: - if (summary) - { - printf_filtered ("%s ", signalname (what)); - } - else - { - printf_filtered ("\t%-16s %s.\n", signalname (what), - safe_strsignal (what)); - } - break; - case PR_SYSENTRY: - if (summary) - { - printf_filtered ("%s ", syscallname (what)); - } - else - { - printf_filtered ("\t%-16s %s.\n", syscallname (what), - "Entered this system call"); - } - break; - case PR_SYSEXIT: - if (summary) - { - printf_filtered ("%s ", syscallname (what)); - } - else - { - printf_filtered ("\t%-16s %s.\n", syscallname (what), - "Returned from this system call"); - } - break; - case PR_FAULTED: - if (summary) - { - printf_filtered ("%s ", - lookupname (faults_table, what, "fault")); - } - else - { - printf_filtered ("\t%-16s %s.\n", - lookupname (faults_table, what, "fault"), - lookupdesc (faults_table, what)); - } - break; - } + QUIT; + printf_filtered ("\t%-12s ", transp -> name); +#ifdef UNIXWARE + printf_filtered ("%-8s", prismember (&pip->prstatus.pr_flttrace, transp -> value) + ? "on" : "off"); +#else + printf_filtered ("%-8s", prismember (&pip->fltset.fltset, transp -> value) + ? "on" : "off"); +#endif + printf_filtered ("\n"); + } printf_filtered ("\n"); } } static void -info_proc_siginfo (pip, summary) +info_proc_mappings (pip, summary) struct procinfo *pip; int summary; { - struct siginfo *sip; + int nmap; + struct prmap *prmaps; + struct prmap *prmap; + struct stat sbuf; - if ((pip -> prstatus.pr_flags & PR_STOPPED) && - (pip -> prstatus.pr_why == PR_SIGNALLED || - pip -> prstatus.pr_why == PR_FAULTED)) + if (!summary) { - printf_filtered ("%-32s", "Additional signal/fault info:"); - sip = &pip -> prstatus.pr_info; - if (summary) - { - printf_filtered ("%s ", signalname (sip -> si_signo)); - if (sip -> si_errno > 0) - { - printf_filtered ("%s ", errnoname (sip -> si_errno)); - } - if (sip -> si_code <= 0) + printf_filtered ("Mapped address spaces:\n\n"); +#ifdef BFD_HOST_64_BIT + printf_filtered (" %18s %18s %10s %10s %7s\n", +#else + printf_filtered ("\t%10s %10s %10s %10s %7s\n", +#endif + "Start Addr", + " End Addr", + " Size", + " Offset", + "Flags"); +#ifdef PROCFS_USE_READ_WRITE + if (fstat (pip->map_fd, &sbuf) == 0) + { + nmap = sbuf.st_size / sizeof (prmap_t); + prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); + if ((lseek (pip->map_fd, 0, SEEK_SET) == 0) && + (read (pip->map_fd, (char *) prmaps, + nmap * sizeof (*prmaps)) == (nmap * sizeof (*prmaps)))) { - printf_filtered ("sent by %s, uid %d ", - target_pid_to_str (sip -> si_pid), - sip -> si_uid); - } - else + int i = 0; + for (prmap = prmaps; i < nmap; ++prmap, ++i) +#else + if (ioctl (pip -> ctl_fd, PIOCNMAP, &nmap) == 0) + { + prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); + if (ioctl (pip -> ctl_fd, PIOCMAP, prmaps) == 0) { - printf_filtered ("%s ", sigcodename (sip)); - if ((sip -> si_signo == SIGILL) || - (sip -> si_signo == SIGFPE) || - (sip -> si_signo == SIGSEGV) || - (sip -> si_signo == SIGBUS)) - { - printf_filtered ("addr=%#lx ", - (unsigned long) sip -> si_addr); - } - else if ((sip -> si_signo == SIGCHLD)) - { - printf_filtered ("child %s, status %u ", - target_pid_to_str (sip -> si_pid), - sip -> si_status); - } - else if ((sip -> si_signo == SIGPOLL)) + for (prmap = prmaps; prmap -> pr_size; ++prmap) +#endif /* PROCFS_USE_READ_WRITE */ { - printf_filtered ("band %u ", sip -> si_band); +#ifdef BFD_HOST_64_BIT + printf_filtered (" %#18lx %#18lx %#10x %#10x %7s\n", +#else + printf_filtered ("\t%#10lx %#10lx %#10x %#10x %7s\n", +#endif + (unsigned long)prmap -> pr_vaddr, + (unsigned long)prmap -> pr_vaddr + + prmap -> pr_size - 1, + prmap -> pr_size, + prmap -> pr_off, + mappingflags (prmap -> pr_mflags)); } } } - else + printf_filtered ("\n"); + } +} + +/* + +LOCAL FUNCTION + + info_proc -- implement the "info proc" command + +SYNOPSIS + + void info_proc (char *args, int from_tty) + +DESCRIPTION + + Implement gdb's "info proc" command by using the /proc interface + to print status information about any currently running process. + + Examples of the use of "info proc" are: + + info proc (prints summary info for current inferior) + info proc 123 (prints summary info for process with pid 123) + info proc mappings (prints address mappings) + info proc times (prints process/children times) + info proc id (prints pid, ppid, gid, sid, etc) + FIXME: i proc id not implemented. + info proc status (prints general process state info) + FIXME: i proc status not implemented. + info proc signals (prints info about signal handling) + info proc all (prints all info) + + */ + +static void +info_proc (args, from_tty) + char *args; + int from_tty; +{ + int pid; + struct procinfo *pip; + struct cleanup *old_chain; + char **argv; + int argsize; + int summary = 1; + int flags = 0; + int syscalls = 0; + int signals = 0; + int faults = 0; + int mappings = 0; + int times = 0; + int id = 0; + int status = 0; + int all = 0; + int nlwp; + int *lwps; + + old_chain = make_cleanup (null_cleanup, 0); + + /* Default to using the current inferior if no pid specified. Note + that inferior_pid may be 0, hence we set okerr. */ + + pid = inferior_pid & 0x7fffffff; /* strip off sol-thread bit */ + if (!(pip = find_procinfo (pid, 1))) /* inferior_pid no good? */ + pip = procinfo_list; /* take first available */ + pid = pid & 0xffff; /* extract "real" pid */ + + if (args != NULL) + { + if ((argv = buildargv (args)) == NULL) { - printf_filtered ("\n\n"); - printf_filtered ("\t%-16s %s.\n", signalname (sip -> si_signo), - safe_strsignal (sip -> si_signo)); - if (sip -> si_errno > 0) + nomem (0); + } + make_cleanup_freeargv (argv); + + while (*argv != NULL) + { + argsize = strlen (*argv); + if (argsize >= 1 && strncmp (*argv, "all", argsize) == 0) { - printf_filtered ("\t%-16s %s.\n", - errnoname (sip -> si_errno), - safe_strerror (sip -> si_errno)); + summary = 0; + all = 1; } - if (sip -> si_code <= 0) + else if (argsize >= 2 && strncmp (*argv, "faults", argsize) == 0) { - printf_filtered ("\t%-16u %s\n", sip -> si_pid, /* XXX need target_pid_to_str() */ - "PID of process sending signal"); - printf_filtered ("\t%-16u %s\n", sip -> si_uid, - "UID of process sending signal"); + summary = 0; + faults = 1; } - else + else if (argsize >= 2 && strncmp (*argv, "flags", argsize) == 0) { - printf_filtered ("\t%-16s %s.\n", sigcodename (sip), - sigcodedesc (sip)); - if ((sip -> si_signo == SIGILL) || - (sip -> si_signo == SIGFPE)) - { - printf_filtered ("\t%#-16lx %s.\n", - (unsigned long) sip -> si_addr, - "Address of faulting instruction"); - } - else if ((sip -> si_signo == SIGSEGV) || - (sip -> si_signo == SIGBUS)) - { - printf_filtered ("\t%#-16lx %s.\n", - (unsigned long) sip -> si_addr, - "Address of faulting memory reference"); - } - else if ((sip -> si_signo == SIGCHLD)) - { - printf_filtered ("\t%-16u %s.\n", sip -> si_pid, /* XXX need target_pid_to_str() */ - "Child process ID"); - printf_filtered ("\t%-16u %s.\n", sip -> si_status, - "Child process exit value or signal"); - } - else if ((sip -> si_signo == SIGPOLL)) + summary = 0; + flags = 1; + } + else if (argsize >= 1 && strncmp (*argv, "id", argsize) == 0) + { + summary = 0; + id = 1; + } + else if (argsize >= 1 && strncmp (*argv, "mappings", argsize) == 0) + { + summary = 0; + mappings = 1; + } + else if (argsize >= 2 && strncmp (*argv, "signals", argsize) == 0) + { + summary = 0; + signals = 1; + } + else if (argsize >= 2 && strncmp (*argv, "status", argsize) == 0) + { + summary = 0; + status = 1; + } + else if (argsize >= 2 && strncmp (*argv, "syscalls", argsize) == 0) + { + summary = 0; + syscalls = 1; + } + else if (argsize >= 1 && strncmp (*argv, "times", argsize) == 0) + { + summary = 0; + times = 1; + } + else if ((pid = atoi (*argv)) > 0) + { + pip = (struct procinfo *) xmalloc (sizeof (struct procinfo)); + memset (pip, 0, sizeof (*pip)); + + pip->pid = pid; + if (!open_proc_file (pid, pip, O_RDONLY, 0)) { - printf_filtered ("\t%-16u %s.\n", sip -> si_band, - "Band event for POLL_{IN,OUT,MSG}"); + perror_with_name (pip -> pathname); + /* NOTREACHED */ } + pid = pip->pid; + make_cleanup_close_proc_file (pip); } + else if (**argv != '\000') + { + error ("Unrecognized or ambiguous keyword `%s'.", *argv); + } + argv++; } - printf_filtered ("\n"); } -} - -static void -info_proc_syscalls (pip, summary) - struct procinfo *pip; - int summary; -{ - int syscallnum; - if (!summary) + /* If we don't have a valid open process at this point, then we have no + inferior or didn't specify a specific pid. */ + + if (!pip) + { + error ("\ +No process. Start debugging a program or specify an explicit process ID."); + } + + if (!procfs_read_status (pip)) + { + print_sys_errmsg (pip -> pathname, errno); + error ("procfs_read_status failed"); + } + +#ifndef PROCFS_USE_READ_WRITE +#ifdef PIOCLWPIDS + nlwp = pip->prstatus.pr_nlwp; + lwps = alloca ((2 * nlwp + 2) * sizeof (*lwps)); + + if (ioctl (pip->ctl_fd, PIOCLWPIDS, lwps)) + { + print_sys_errmsg (pip -> pathname, errno); + error ("PIOCLWPIDS failed"); + } +#else /* PIOCLWPIDS */ + nlwp = 1; + lwps = alloca ((2 * nlwp + 2) * sizeof *lwps); + lwps[0] = 0; +#endif /* PIOCLWPIDS */ + + for (; nlwp > 0; nlwp--, lwps++) { + pip = find_procinfo ((*lwps << 16) | pid, 1); -#if 0 /* FIXME: Needs to use gdb-wide configured info about system calls. */ - if (pip -> prstatus.pr_flags & PR_ASLEEP) + if (!pip) { - int syscallnum = pip -> prstatus.pr_reg[R_D0]; - if (summary) - { - printf_filtered ("%-32s", "Sleeping in system call:"); - printf_filtered ("%s", syscallname (syscallnum)); - } - else + pip = (struct procinfo *) xmalloc (sizeof (struct procinfo)); + memset (pip, 0, sizeof (*pip)); + if (!open_proc_file ((*lwps << 16) | pid, pip, O_RDONLY, 0)) + continue; + + make_cleanup_close_proc_file (pip); + + if (!procfs_read_status (pip)) { - printf_filtered ("Sleeping in system call '%s'.\n", - syscallname (syscallnum)); + print_sys_errmsg (pip -> pathname, errno); + error ("procfs_read_status failed"); } } -#endif - if (ioctl (pip -> fd, PIOCGENTRY, &pip -> entryset) < 0) +#endif /* PROCFS_USE_READ_WRITE */ + + /* Print verbose information of the requested type(s), or just a summary + of the information for all types. */ + + printf_filtered ("\nInformation for %s.%d:\n\n", pip -> pathname, *lwps); + if (summary || all || flags) { - print_sys_errmsg (pip -> pathname, errno); - error ("PIOCGENTRY failed"); + info_proc_flags (pip, summary); } - - if (ioctl (pip -> fd, PIOCGEXIT, &pip -> exitset) < 0) + if (summary || all) { - print_sys_errmsg (pip -> pathname, errno); - error ("PIOCGEXIT failed"); + info_proc_stop (pip, summary); +#ifdef UNIXWARE + supply_gregset (&pip->prstatus.pr_lwp.pr_context.uc_mcontext.gregs); +#else + supply_gregset (&pip->prstatus.pr_reg); +#endif + printf_filtered ("PC: "); + print_address (read_pc (), gdb_stdout); + printf_filtered ("\n"); } - - printf_filtered ("System call tracing information:\n\n"); - - printf_filtered ("\t%-12s %-8s %-8s\n", - "System call", - "Entry", - "Exit"); - for (syscallnum = 0; syscallnum < MAX_SYSCALLS; syscallnum++) + if (summary || all || signals || faults) { - QUIT; - if (syscall_table[syscallnum] != NULL) - { - printf_filtered ("\t%-12s ", syscall_table[syscallnum]); - printf_filtered ("%-8s ", - prismember (&pip -> entryset, syscallnum) - ? "on" : "off"); - printf_filtered ("%-8s ", - prismember (&pip -> exitset, syscallnum) - ? "on" : "off"); - printf_filtered ("\n"); - } - } + info_proc_siginfo (pip, summary); + } + if (summary || all || syscalls) + { + info_proc_syscalls (pip, summary); + } + if (summary || all || mappings) + { + info_proc_mappings (pip, summary); + } + if (summary || all || signals) + { + info_proc_signals (pip, summary); + } + if (summary || all || faults) + { + info_proc_faults (pip, summary); + } printf_filtered ("\n"); + + /* All done, deal with closing any temporary process info structure, + freeing temporary memory , etc. */ + + do_cleanups (old_chain); +#ifndef PROCFS_USE_READ_WRITE } +#endif } -static char * -signalname (signo) - int signo; +/* + +LOCAL FUNCTION + + modify_inherit_on_fork_flag - Change the inherit-on-fork flag + +SYNOPSIS + + void modify_inherit_on_fork_flag (fd, flag) + +DESCRIPTION + + Call this routine to modify the inherit-on-fork flag. This routine is + just a nice wrapper to hide the #ifdefs needed by various systems to + control this flag. + + */ + +static void +modify_inherit_on_fork_flag (fd, flag) + int fd; + int flag; { - const char *name; - static char locbuf[32]; +#if defined (PIOCSET) || defined (PCSET) + long pr_flags; +#endif + int retval = 0; + struct proc_ctl pctl; - name = strsigno (signo); - if (name == NULL) +#if defined (PIOCSET) || defined (PCSET) /* New method */ + pr_flags = PR_FORK; + if (flag) { - sprintf (locbuf, "Signal %d", signo); +#ifdef PROCFS_USE_READ_WRITE + pctl.cmd = PCSET; + pctl.data = PR_FORK; + if (write (fd, (char *) &pctl, sizeof (struct proc_ctl)) < 0) + retval = -1; +#else + retval = ioctl (fd, PIOCSET, &pr_flags); +#endif } else { - sprintf (locbuf, "%s (%d)", name, signo); +#ifdef PROCFS_USE_READ_WRITE + pctl.cmd = PCRESET; + pctl.data = PR_FORK; + if (write (fd, (char *) &pctl, sizeof (struct proc_ctl)) < 0) + retval = -1; +#else + retval = ioctl (fd, PIOCRESET, &pr_flags); +#endif } - return (locbuf); -} - -static char * -errnoname (errnum) - int errnum; -{ - const char *name; - static char locbuf[32]; - name = strerrno (errnum); - if (name == NULL) +#else +#ifdef PIOCSFORK /* Original method */ + if (flag) { - sprintf (locbuf, "Errno %d", errnum); + retval = ioctl (fd, PIOCSFORK, NULL); } else { - sprintf (locbuf, "%s (%d)", name, errnum); + retval = ioctl (fd, PIOCRFORK, NULL); } - return (locbuf); +#else + Neither PR_FORK nor PIOCSFORK exist!!! +#endif +#endif + + if (!retval) + return; + + print_sys_errmsg ("modify_inherit_on_fork_flag", errno); + error ("PIOCSFORK or PR_FORK modification failed"); } +/* + +LOCAL FUNCTION + + modify_run_on_last_close_flag - Change the run-on-last-close flag + +SYNOPSIS + + void modify_run_on_last_close_flag (fd, flag) + +DESCRIPTION + + Call this routine to modify the run-on-last-close flag. This routine + is just a nice wrapper to hide the #ifdefs needed by various systems to + control this flag. + + */ + static void -info_proc_signals (pip, summary) - struct procinfo *pip; - int summary; +modify_run_on_last_close_flag (fd, flag) + int fd; + int flag; { - int signo; +#if defined (PIOCSET) || defined (PCSET) + long pr_flags; +#endif + int retval = 0; + struct proc_ctl pctl; - if (!summary) +#if defined (PIOCSET) || defined (PCSET) /* New method */ + pr_flags = PR_RLC; + if (flag) { - if (ioctl (pip -> fd, PIOCGTRACE, &pip -> trace) < 0) - { - print_sys_errmsg (pip -> pathname, errno); - error ("PIOCGTRACE failed"); - } - - printf_filtered ("Disposition of signals:\n\n"); - printf_filtered ("\t%-15s %-8s %-8s %-8s %s\n\n", - "Signal", "Trace", "Hold", "Pending", "Description"); - for (signo = 0; signo < NSIG; signo++) - { - QUIT; - printf_filtered ("\t%-15s ", signalname (signo)); - printf_filtered ("%-8s ", - prismember (&pip -> trace, signo) - ? "on" : "off"); - printf_filtered ("%-8s ", - prismember (&pip -> prstatus.pr_sighold, signo) - ? "on" : "off"); - -#ifdef PROCFS_SIGPEND_OFFSET - /* Alpha OSF/1 numbers the pending signals from 1. */ - printf_filtered ("%-8s ", - (signo ? prismember (&pip -> prstatus.pr_sigpend, - signo - 1) - : 0) - ? "yes" : "no"); +#ifdef PROCFS_USE_READ_WRITE + pctl.cmd = PCSET; + pctl.data = PR_RLC; + if (write (fd, (char *) &pctl, sizeof (struct proc_ctl)) < 0) + retval = -1; #else - printf_filtered ("%-8s ", - prismember (&pip -> prstatus.pr_sigpend, signo) - ? "yes" : "no"); + retval = ioctl (fd, PIOCSET, &pr_flags); #endif - printf_filtered (" %s\n", safe_strsignal (signo)); - } - printf_filtered ("\n"); } + else + { +#ifdef PROCFS_USE_READ_WRITE + pctl.cmd = PCRESET; + pctl.data = PR_RLC; + if (write (fd, (char *) &pctl, sizeof (struct proc_ctl)) < 0) + retval = -1; +#else + retval = ioctl (fd, PIOCRESET, &pr_flags); +#endif + } + +#else +#ifdef PIOCSRLC /* Original method */ + if (flag) + retval = ioctl (fd, PIOCSRLC, NULL); + else + retval = ioctl (fd, PIOCRRLC, NULL); +#else + Neither PR_RLC nor PIOCSRLC exist!!! +#endif +#endif + + if (!retval) + return; + + print_sys_errmsg ("modify_run_on_last_close_flag", errno); + error ("PIOCSRLC or PR_RLC modification failed"); } +/* + +LOCAL FUNCTION + + procfs_clear_syscall_trap -- Deletes the trap for the specified system call. + +SYNOPSIS + + void procfs_clear_syscall_trap (struct procinfo *, int syscall_num, int errok) + +DESCRIPTION + + This function function disables traps for the specified system call. + errok is non-zero if errors should be ignored. + */ + static void -info_proc_faults (pip, summary) - struct procinfo *pip; - int summary; +procfs_clear_syscall_trap (pi, syscall_num, errok) + struct procinfo *pi; + int syscall_num; + int errok; { - struct trans *transp; + sysset_t sysset; + int goterr, i; - if (!summary) +#ifndef UNIXWARE + goterr = ioctl (pi->ctl_fd, PIOCGENTRY, &sysset) < 0; + + if (goterr && !errok) { - if (ioctl (pip -> fd, PIOCGFAULT, &pip -> fltset) < 0) - { - print_sys_errmsg (pip -> pathname, errno); - error ("PIOCGFAULT failed"); - } - - printf_filtered ("Current traced hardware fault set:\n\n"); - printf_filtered ("\t%-12s %-8s\n", "Fault", "Trace"); + print_sys_errmsg (pi->pathname, errno); + error ("PIOCGENTRY failed"); + } - for (transp = faults_table; transp -> name != NULL; transp++) + if (!goterr) + { + prdelset (&sysset, syscall_num); + + if ((ioctl (pi->ctl_fd, PIOCSENTRY, &sysset) < 0) && !errok) { - QUIT; - printf_filtered ("\t%-12s ", transp -> name); - printf_filtered ("%-8s", prismember (&pip -> fltset, transp -> value) - ? "on" : "off"); - printf_filtered ("\n"); + print_sys_errmsg (pi->pathname, errno); + error ("PIOCSENTRY failed"); } - printf_filtered ("\n"); } -} -static void -info_proc_mappings (pip, summary) - struct procinfo *pip; - int summary; -{ - int nmap; - struct prmap *prmaps; - struct prmap *prmap; + goterr = ioctl (pi->ctl_fd, PIOCGEXIT, &sysset) < 0; - if (!summary) + if (goterr && !errok) { - printf_filtered ("Mapped address spaces:\n\n"); -#ifdef BFD_HOST_64_BIT - printf_filtered (" %18s %18s %10s %10s %7s\n", -#else - printf_filtered ("\t%10s %10s %10s %10s %7s\n", -#endif - "Start Addr", - " End Addr", - " Size", - " Offset", - "Flags"); - if (ioctl (pip -> fd, PIOCNMAP, &nmap) == 0) - { - prmaps = (struct prmap *) alloca ((nmap + 1) * sizeof (*prmaps)); - if (ioctl (pip -> fd, PIOCMAP, prmaps) == 0) - { - for (prmap = prmaps; prmap -> pr_size; ++prmap) - { -#ifdef BFD_HOST_64_BIT - printf_filtered (" %#18lx %#18lx %#10x %#10x %7s\n", -#else - printf_filtered ("\t%#10lx %#10lx %#10x %#10x %7s\n", -#endif - (unsigned long)prmap -> pr_vaddr, - (unsigned long)prmap -> pr_vaddr - + prmap -> pr_size - 1, - prmap -> pr_size, - prmap -> pr_off, - mappingflags (prmap -> pr_mflags)); - } - } + procfs_clear_syscall_trap (pi, syscall_num, 1); + print_sys_errmsg (pi->pathname, errno); + error ("PIOCGEXIT failed"); + } + + if (!goterr) + { + praddset (&sysset, syscall_num); + + if ((ioctl (pi->ctl_fd, PIOCSEXIT, &sysset) < 0) && !errok) + { + procfs_clear_syscall_trap (pi, syscall_num, 1); + print_sys_errmsg (pi->pathname, errno); + error ("PIOCSEXIT failed"); } - printf_filtered ("\n"); } +#endif + + if (!pi->syscall_handlers) + { + if (!errok) + error ("procfs_clear_syscall_trap: syscall_handlers is empty"); + return; + } + + /* Remove handler func from the handler list */ + + for (i = 0; i < pi->num_syscall_handlers; i++) + if (pi->syscall_handlers[i].syscall_num == syscall_num) + { + if (i + 1 != pi->num_syscall_handlers) + { /* Not the last entry. + Move subsequent entries fwd. */ + memcpy (&pi->syscall_handlers[i], &pi->syscall_handlers[i + 1], + (pi->num_syscall_handlers - i - 1) + * sizeof (struct procfs_syscall_handler)); + } + + pi->syscall_handlers = xrealloc (pi->syscall_handlers, + (pi->num_syscall_handlers - 1) + * sizeof (struct procfs_syscall_handler)); + pi->num_syscall_handlers--; + return; + } + + if (!errok) + error ("procfs_clear_syscall_trap: Couldn't find handler for sys call %d", + syscall_num); } /* LOCAL FUNCTION - info_proc -- implement the "info proc" command + procfs_set_syscall_trap -- arrange for a function to be called when the + child executes the specified system call. SYNOPSIS - void info_proc (char *args, int from_tty) + void procfs_set_syscall_trap (struct procinfo *, int syscall_num, int flags, + syscall_func_t *function) DESCRIPTION - Implement gdb's "info proc" command by using the /proc interface - to print status information about any currently running process. - - Examples of the use of "info proc" are: - - info proc (prints summary info for current inferior) - info proc 123 (prints summary info for process with pid 123) - info proc mappings (prints address mappings) - info proc times (prints process/children times) - info proc id (prints pid, ppid, gid, sid, etc) - FIXME: i proc id not implemented. - info proc status (prints general process state info) - FIXME: i proc status not implemented. - info proc signals (prints info about signal handling) - info proc all (prints all info) - + This function sets up an entry and/or exit trap for the specified system + call. When the child executes the specified system call, your function + will be called with the call #, a flag that indicates entry or exit, and + pointers to rtnval and statval (which are used by procfs_wait). The + function should return non-zero if something interesting happened, zero + otherwise. */ static void -info_proc (args, from_tty) - char *args; - int from_tty; +procfs_set_syscall_trap (pi, syscall_num, flags, func) + struct procinfo *pi; + int syscall_num; + int flags; + syscall_func_t *func; { - int pid; - struct procinfo *pip; - struct cleanup *old_chain; - char **argv; - int argsize; - int summary = 1; - int flags = 0; - int syscalls = 0; - int signals = 0; - int faults = 0; - int mappings = 0; - int times = 0; - int id = 0; - int status = 0; - int all = 0; - - old_chain = make_cleanup (null_cleanup, 0); - - /* Default to using the current inferior if no pid specified. Note - that inferior_pid may be 0, hence we set okerr. */ + sysset_t sysset; - pip = find_procinfo (inferior_pid, 1); - - if (args != NULL) +#ifndef UNIXWARE + if (flags & PROCFS_SYSCALL_ENTRY) { - if ((argv = buildargv (args)) == NULL) + if (ioctl (pi->ctl_fd, PIOCGENTRY, &sysset) < 0) { - nomem (0); + print_sys_errmsg (pi->pathname, errno); + error ("PIOCGENTRY failed"); } - make_cleanup (freeargv, (char *) argv); - while (*argv != NULL) - { - argsize = strlen (*argv); - if (argsize >= 1 && strncmp (*argv, "all", argsize) == 0) - { - summary = 0; - all = 1; - } - else if (argsize >= 2 && strncmp (*argv, "faults", argsize) == 0) - { - summary = 0; - faults = 1; - } - else if (argsize >= 2 && strncmp (*argv, "flags", argsize) == 0) - { - summary = 0; - flags = 1; - } - else if (argsize >= 1 && strncmp (*argv, "id", argsize) == 0) - { - summary = 0; - id = 1; - } - else if (argsize >= 1 && strncmp (*argv, "mappings", argsize) == 0) - { - summary = 0; - mappings = 1; - } - else if (argsize >= 2 && strncmp (*argv, "signals", argsize) == 0) - { - summary = 0; - signals = 1; - } - else if (argsize >= 2 && strncmp (*argv, "status", argsize) == 0) - { - summary = 0; - status = 1; - } - else if (argsize >= 2 && strncmp (*argv, "syscalls", argsize) == 0) - { - summary = 0; - syscalls = 1; - } - else if (argsize >= 1 && strncmp (*argv, "times", argsize) == 0) - { - summary = 0; - times = 1; - } - else if ((pid = atoi (*argv)) > 0) - { - pip = (struct procinfo *) xmalloc (sizeof (struct procinfo)); - memset (pip, 0, sizeof (*pip)); + praddset (&sysset, syscall_num); - pip->pid = pid; - if (!open_proc_file (pid, pip, O_RDONLY)) - { - perror_with_name (pip -> pathname); - /* NOTREACHED */ - } - make_cleanup (close_proc_file, pip); - } - else if (**argv != '\000') - { - error ("Unrecognized or ambiguous keyword `%s'.", *argv); - } - argv++; + if (ioctl (pi->ctl_fd, PIOCSENTRY, &sysset) < 0) + { + print_sys_errmsg (pi->pathname, errno); + error ("PIOCSENTRY failed"); } } - /* If we don't have a valid open process at this point, then we have no - inferior or didn't specify a specific pid. */ - - if (!pip) - { - error ("\ -No process. Start debugging a program or specify an explicit process ID."); - } - if (ioctl (pip -> fd, PIOCSTATUS, &(pip -> prstatus)) < 0) + if (flags & PROCFS_SYSCALL_EXIT) { - print_sys_errmsg (pip -> pathname, errno); - error ("PIOCSTATUS failed"); - } + if (ioctl (pi->ctl_fd, PIOCGEXIT, &sysset) < 0) + { + procfs_clear_syscall_trap (pi, syscall_num, 1); + print_sys_errmsg (pi->pathname, errno); + error ("PIOCGEXIT failed"); + } - /* Print verbose information of the requested type(s), or just a summary - of the information for all types. */ + praddset (&sysset, syscall_num); - printf_filtered ("\nInformation for %s:\n\n", pip -> pathname); - if (summary || all || flags) - { - info_proc_flags (pip, summary); - } - if (summary || all) - { - info_proc_stop (pip, summary); - } - if (summary || all || signals || faults) - { - info_proc_siginfo (pip, summary); - } - if (summary || all || syscalls) - { - info_proc_syscalls (pip, summary); - } - if (summary || all || mappings) - { - info_proc_mappings (pip, summary); + if (ioctl (pi->ctl_fd, PIOCSEXIT, &sysset) < 0) + { + procfs_clear_syscall_trap (pi, syscall_num, 1); + print_sys_errmsg (pi->pathname, errno); + error ("PIOCSEXIT failed"); + } } - if (summary || all || signals) +#endif + + if (!pi->syscall_handlers) { - info_proc_signals (pip, summary); + pi->syscall_handlers = xmalloc (sizeof (struct procfs_syscall_handler)); + pi->syscall_handlers[0].syscall_num = syscall_num; + pi->syscall_handlers[0].func = func; + pi->num_syscall_handlers = 1; } - if (summary || all || faults) + else { - info_proc_faults (pip, summary); - } - printf_filtered ("\n"); + int i; - /* All done, deal with closing any temporary process info structure, - freeing temporary memory , etc. */ + for (i = 0; i < pi->num_syscall_handlers; i++) + if (pi->syscall_handlers[i].syscall_num == syscall_num) + { + pi->syscall_handlers[i].func = func; + return; + } - do_cleanups (old_chain); + pi->syscall_handlers = xrealloc (pi->syscall_handlers, (i + 1) + * sizeof (struct procfs_syscall_handler)); + pi->syscall_handlers[i].syscall_num = syscall_num; + pi->syscall_handlers[i].func = func; + pi->num_syscall_handlers++; + } } +#ifdef SYS_lwp_create + /* LOCAL FUNCTION - procfs_set_sproc_trap -- arrange for child to stop on sproc(). + procfs_lwp_creation_handler - handle exit from the _lwp_create syscall SYNOPSIS - void procfs_set_sproc_trap (struct procinfo *) + int procfs_lwp_creation_handler (pi, syscall_num, why, rtnvalp, statvalp) DESCRIPTION - This function sets up a trap on sproc system call exits so that we can - detect the arrival of a new thread. We are called with the new thread - stopped prior to it's first instruction. + This routine is called both when an inferior process and it's new lwp + are about to finish a _lwp_create() system call. This is the system + call that Solaris uses to create a lightweight process. When the + target process gets this event, we can look at sysarg[2] to find the + new childs lwp ID, and create a procinfo struct from that. After that, + we pretend that we got a SIGTRAP, and return non-zero to tell + procfs_wait to wake up. Subsequently, wait_for_inferior gets woken up, + sees the new process and continues it. - Also note that we turn on the inherit-on-fork flag in the child process - so that any grand-children start with all tracing flags set. - */ + When we see the child exiting from lwp_create, we just contine it, + since everything was handled when the parent trapped. -#ifdef SYS_sproc +NOTES + In effect, we are only paying attention to the parent's completion of + the lwp_create syscall. If we only paid attention to the child + instead, then we wouldn't detect the creation of a suspended thread. + */ -static void -procfs_set_sproc_trap (pi) +static int +procfs_lwp_creation_handler (pi, syscall_num, why, rtnvalp, statvalp) struct procinfo *pi; + int syscall_num; + int why; + int *rtnvalp; + int *statvalp; { - sysset_t exitset; - - if (ioctl (pi->fd, PIOCGEXIT, &exitset) < 0) - { - print_sys_errmsg (pi->pathname, errno); - error ("PIOCGEXIT failed"); - } + int lwp_id; + struct procinfo *childpi; + struct proc_ctl pctl; - praddset (&exitset, SYS_sproc); + /* We've just detected the completion of an lwp_create system call. Now we + need to setup a procinfo struct for this thread, and notify the thread + system of the new arrival. */ - /* We trap on fork() and vfork() in order to disable debugging in our grand- - children and descendant processes. At this time, GDB can only handle - threads (multiple processes, one address space). forks (and execs) result - in the creation of multiple address spaces, which GDB can't handle yet. */ + /* If lwp_create failed, then nothing interesting happened. Continue the + process and go back to sleep. */ - praddset (&exitset, SYS_fork); -#ifdef SYS_vfork - praddset (&exitset, SYS_vfork); +#ifdef UNIXWARE + /* Joel ... can you check this logic out please? JKJ */ + if (pi->prstatus.pr_lwp.pr_context.uc_mcontext.gregs[R_EFL] & 1) + { /* _lwp_create failed */ + pctl.cmd = PCRUN; + pctl.data = PRCFAULT; + + if (write (pi->ctl_fd, (char *) &pctl, sizeof (struct proc_ctl)) < 0) + perror_with_name (pi->pathname); + + return 0; + } +#else /* UNIXWARE */ + if (PROCFS_GET_CARRY (pi->prstatus.pr_reg)) + { /* _lwp_create failed */ + pi->prrun.pr_flags &= PRSTEP; + pi->prrun.pr_flags |= PRCFAULT; + + if (ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) + perror_with_name (pi->pathname); + + return 0; + } #endif - if (ioctl (pi->fd, PIOCSEXIT, &exitset) < 0) - { - print_sys_errmsg (pi->pathname, errno); - error ("PIOCSEXIT failed"); + /* At this point, the new thread is stopped at it's first instruction, and + the parent is stopped at the exit from lwp_create. */ + + if (pi->new_child) /* Child? */ + { /* Yes, just continue it */ +#ifdef UNIXWARE + pctl.cmd = PCRUN; + pctl.data = PRCFAULT; + + if (write(pi->ctl_fd, (char *)&pctl, sizeof (struct proc_ctl)) < 0) +#else /* !UNIXWARE */ + pi->prrun.pr_flags &= PRSTEP; + pi->prrun.pr_flags |= PRCFAULT; + + if ((pi->prstatus.pr_flags & PR_ISTOP) + && ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) +#endif /* !UNIXWARE */ + perror_with_name (pi->pathname); + + pi->new_child = 0; /* No longer new */ + + return 0; } - /* Turn on inherit-on-fork flag so that all grand-children of gdb start with - tracing flags set. */ + /* We're the proud parent of a new thread. Setup an exit trap for lwp_create + in the child and continue the parent. */ -#ifdef PIOCSET /* New method */ - { - long pr_flags; - pr_flags = PR_FORK; - ioctl (pi->fd, PIOCSET, &pr_flags); - } + /* Third arg is pointer to new thread id. */ + lwp_id = read_memory_integer ( + THE_PR_LWP(pi->prstatus).pr_sysarg[2], sizeof (int)); + + lwp_id = (lwp_id << 16) | PIDGET (pi->pid); + + childpi = create_procinfo (lwp_id); + + /* The new process has actually inherited the lwp_create syscall trap from + it's parent, but we still have to call this to register handlers for + that child. */ + + procfs_set_inferior_syscall_traps (childpi); + add_thread (lwp_id); + printf_filtered ("[New %s]\n", target_pid_to_str (lwp_id)); + + /* Continue the parent */ +#ifdef UNIXWARE + pctl.cmd = PCRUN; + pctl.data = PRCFAULT; + + if (write(pi->ctl_fd, (char *)&pctl, sizeof (struct proc_ctl)) < 0) #else -#ifdef PIOCSFORK /* Original method */ - ioctl (pi->fd, PIOCSFORK, NULL); + pi->prrun.pr_flags &= PRSTEP; + pi->prrun.pr_flags |= PRCFAULT; + if (ioctl (pi->ctl_fd, PIOCRUN, &pi->prrun) != 0) #endif + perror_with_name (pi->pathname); + + /* The new child may have been created in one of two states: + SUSPENDED or RUNNABLE. If runnable, we will simply signal it to run. + If suspended, we flag it to be continued later, when it has an event. */ + + if (THE_PR_LWP(childpi->prstatus).pr_why == PR_SUSPENDED) + childpi->new_child = 1; /* Flag this as an unseen child process */ + else + { + /* Continue the child */ +#ifdef UNIXWARE + pctl.cmd = PCRUN; + pctl.data = PRCFAULT; + + if (write(pi->ctl_fd, (char *)&pctl, sizeof (struct proc_ctl)) < 0) +#else + childpi->prrun.pr_flags &= PRSTEP; + childpi->prrun.pr_flags |= PRCFAULT; + + if (ioctl (childpi->ctl_fd, PIOCRUN, &childpi->prrun) != 0) #endif + perror_with_name (childpi->pathname); + } + return 0; } -#endif /* SYS_sproc */ +#endif /* SYS_lwp_create */ /* Fork an inferior process, and start debugging it with /proc. */ @@ -3620,17 +5532,11 @@ procfs_create_inferior (exec_file, allargs, env) } fork_inferior (exec_file, allargs, env, - proc_set_exec_trap, procfs_init_inferior, shell_file); + proc_set_exec_trap, procfs_init_inferior, NULL, shell_file); /* We are at the first instruction we care about. */ /* Pedal to the metal... */ - /* Setup traps on exit from sproc() */ - -#ifdef SYS_sproc - procfs_set_sproc_trap (current_procinfo); -#endif - proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0); } @@ -3657,9 +5563,14 @@ procfs_mourn_inferior () static int procfs_can_run () { - return(1); + /* This variable is controlled by modules that sit atop procfs that may layer + their own process structure atop that provided here. sol-thread.c does + this because of the Solaris two-level thread model. */ + + return !procfs_suppress_run; } #ifdef TARGET_HAS_HARDWARE_WATCHPOINTS +#ifndef UNIXWARE /* Insert a watchpoint */ int @@ -3676,7 +5587,7 @@ procfs_set_watchpoint(pid, addr, len, rw) wpt.pr_vaddr = (caddr_t)addr; wpt.pr_size = len; wpt.pr_wflags = ((rw & 1) ? MA_READ : 0) | ((rw & 2) ? MA_WRITE : 0); - if (ioctl (pi->fd, PIOCSWATCH, &wpt) < 0) + if (ioctl (pi->ctl_fd, PIOCSWATCH, &wpt) < 0) { if (errno == E2BIG) return -1; @@ -3705,7 +5616,7 @@ procfs_stopped_by_watchpoint(pid) what = pi->prstatus.pr_what; if (why == PR_FAULTED #if defined (FLTWATCH) && defined (FLTKWATCH) - && (what == FLTWATCH) || (what == FLTKWATCH) + && (what == FLTWATCH || what == FLTKWATCH) #else #ifdef FLTWATCH && (what == FLTWATCH) @@ -3719,7 +5630,57 @@ procfs_stopped_by_watchpoint(pid) } return 0; } -#endif +#endif /* !UNIXWARE */ +#endif /* TARGET_HAS_HARDWARE_WATCHPOINTS */ + +/* Why is this necessary? Shouldn't dead threads just be removed from the + thread database? */ + +static int +procfs_thread_alive (pid) + int pid; +{ + struct procinfo *pi, *next_pi; + + for (pi = procinfo_list; pi; pi = next_pi) + { + next_pi = pi->next; + if (pi -> pid == pid) + if (procfs_read_status (pi)) /* alive */ + return 1; + else /* defunct (exited) */ + { + close_proc_file (pi); + return 0; + } + } + return 0; +} + +int +procfs_first_available () +{ + struct procinfo *pi; + + for (pi = procinfo_list; pi; pi = pi->next) + { + if (procfs_read_status (pi)) + return pi->pid; + } + return -1; +} + +int +procfs_get_pid_fd (pid) + int pid; +{ + struct procinfo *pi = find_procinfo (pid, 1); + + if (pi == NULL) + return -1; + + return pi->ctl_fd; +} /* Send a SIGINT to the process group. This acts just like the user typed a ^C on the controlling terminal. @@ -3727,76 +5688,89 @@ procfs_stopped_by_watchpoint(pid) XXX - This may not be correct for all systems. Some may want to use killpg() instead of kill (-pgrp). */ -void +static void procfs_stop () { extern pid_t inferior_process_group; kill (-inferior_process_group, SIGINT); } + +/* Convert a pid to printable form. */ + +#ifdef TIDGET +char * +procfs_pid_to_str (pid) + int pid; +{ + static char buf[100]; + + sprintf (buf, "Kernel thread %d", TIDGET (pid)); + + return buf; +} +#endif /* TIDGET */ -struct target_ops procfs_ops = { - "procfs", /* to_shortname */ - "Unix /proc child process", /* to_longname */ - "Unix /proc child process (started by the \"run\" command).", /* to_doc */ - procfs_open, /* to_open */ - 0, /* to_close */ - procfs_attach, /* to_attach */ - procfs_detach, /* to_detach */ - procfs_resume, /* to_resume */ - procfs_wait, /* to_wait */ - procfs_fetch_registers, /* to_fetch_registers */ - procfs_store_registers, /* to_store_registers */ - procfs_prepare_to_store, /* to_prepare_to_store */ - procfs_xfer_memory, /* to_xfer_memory */ - procfs_files_info, /* to_files_info */ - memory_insert_breakpoint, /* to_insert_breakpoint */ - memory_remove_breakpoint, /* to_remove_breakpoint */ - terminal_init_inferior, /* to_terminal_init */ - terminal_inferior, /* to_terminal_inferior */ - terminal_ours_for_output, /* to_terminal_ours_for_output */ - terminal_ours, /* to_terminal_ours */ - child_terminal_info, /* to_terminal_info */ - procfs_kill_inferior, /* to_kill */ - 0, /* to_load */ - 0, /* to_lookup_symbol */ - procfs_create_inferior, /* to_create_inferior */ - procfs_mourn_inferior, /* to_mourn_inferior */ - procfs_can_run, /* to_can_run */ - procfs_notice_signals, /* to_notice_signals */ - procfs_stop, /* to_stop */ - process_stratum, /* to_stratum */ - 0, /* to_next */ - 1, /* to_has_all_memory */ - 1, /* to_has_memory */ - 1, /* to_has_stack */ - 1, /* to_has_registers */ - 1, /* to_has_execution */ - 0, /* sections */ - 0, /* sections_end */ - OPS_MAGIC /* to_magic */ -}; +static void +init_procfs_ops () +{ + procfs_ops.to_shortname = "procfs"; + procfs_ops.to_longname = "Unix /proc child process"; + procfs_ops.to_doc = "Unix /proc child process (started by the \"run\" command)."; + procfs_ops.to_open = procfs_open; + procfs_ops.to_attach = procfs_attach; + procfs_ops.to_detach = procfs_detach; + procfs_ops.to_resume = procfs_resume; + procfs_ops.to_wait = procfs_wait; + procfs_ops.to_fetch_registers = procfs_fetch_registers; + procfs_ops.to_store_registers = procfs_store_registers; + procfs_ops.to_prepare_to_store = procfs_prepare_to_store; + procfs_ops.to_xfer_memory = procfs_xfer_memory; + procfs_ops.to_files_info = procfs_files_info; + procfs_ops.to_insert_breakpoint = memory_insert_breakpoint; + procfs_ops.to_remove_breakpoint = memory_remove_breakpoint; + procfs_ops.to_terminal_init = terminal_init_inferior; + procfs_ops.to_terminal_inferior = terminal_inferior; + procfs_ops.to_terminal_ours_for_output = terminal_ours_for_output; + procfs_ops.to_terminal_ours = terminal_ours; + procfs_ops.to_terminal_info = child_terminal_info; + procfs_ops.to_kill = procfs_kill_inferior; + procfs_ops.to_create_inferior = procfs_create_inferior; + procfs_ops.to_mourn_inferior = procfs_mourn_inferior; + procfs_ops.to_can_run = procfs_can_run; + procfs_ops.to_notice_signals = procfs_notice_signals; + procfs_ops.to_thread_alive = procfs_thread_alive; + procfs_ops.to_stop = procfs_stop; + procfs_ops.to_stratum = process_stratum; + procfs_ops.to_has_all_memory = 1; + procfs_ops.to_has_memory = 1; + procfs_ops.to_has_stack = 1; + procfs_ops.to_has_registers = 1; + procfs_ops.to_has_execution = 1; + procfs_ops.to_magic = OPS_MAGIC; +} void _initialize_procfs () { #ifdef HAVE_OPTIONAL_PROC_FS - char procname[32]; + char procname[MAX_PROC_NAME_SIZE]; int fd; /* If we have an optional /proc filesystem (e.g. under OSF/1), don't add procfs support if we cannot access the running GDB via /proc. */ - sprintf (procname, PROC_NAME_FMT, getpid ()); + sprintf (procname, STATUS_PROC_NAME_FMT, getpid ()); if ((fd = open (procname, O_RDONLY)) < 0) return; close (fd); #endif + init_procfs_ops (); add_target (&procfs_ops); - add_info ("proc", info_proc, + add_info ("processes", info_proc, "Show process status information using /proc entry.\n\ Specify process id or use current inferior by default.\n\ Specify keywords for detailed information; default is summary.\n\