This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

gdbserver Solaris [5/9] - gdbserver procfs files [2/3]


I tried to send this mail a couple of times, but it is not added to the list.
I think the size is too big. So I have split mail [5/9] up into 3 smaller 
parts. This is part 2.

Pieter

gdb/gdbserver ChangeLog entry:

2010-04-23  Pieter Maljaars  <pieter.maljaars@altenpts.nl>

        procfs functionality copied from gdb/
        * proc-events.c: New file. [1/3]
	* proc-flags.c: New file. [3/3]
	* procfs.c: New file. [2/3]
	* proc-utils.h: New file. [3/3]
	* proc-why.c: New file. [3/3]


diff -upN src-orig/src/gdb/gdbserver/procfs.c src/gdb/gdbserver/procfs.c
--- src-orig/src/gdb/gdbserver/procfs.c	1970-01-01 01:00:00.000000000 +0100
+++ src/gdb/gdbserver/procfs.c	2010-04-23 14:28:20.000000000 +0200
@@ -0,0 +1,3280 @@
+/* Machine independent support for SVR4 /proc (process file system) for GDB.
+
+   Copyright (C) 2010 Free Software Foundation, Inc.
+
+   Written by Michael Snyder at Cygnus Solutions.
+   Based on work by Fred Fish, Stu Grossman, Geoff Noer, and others.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "server.h"
+#include "regcache.h"
+
+#define _STRUCTURED_PROC 1	/* Should be done by configure script. */
+
+#include <sys/procfs.h>
+#include <sys/fault.h>
+
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+#include <sys/errno.h>
+
+#include <signal.h>
+
+#include <ctype.h>
+
+#include "server.h"
+#include "solaris-low.h"
+#include <dirent.h>
+#include "target.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/* Determine which /proc API we are using:
+   The ioctl API defines PIOCSTATUS, while
+   the read/write (multiple fd) API never does.  */
+
+#include <sys/types.h>
+
+#include <fcntl.h>	/* for O_RDONLY */
+#include <unistd.h>	/* for "X_OK" */
+
+/* Note: procfs-utils.h must be included after the above system header
+   files, because it redefines various system calls using macros.
+   This may be incompatible with the prototype declarations.  */
+#include "proc-utils.h"
+
+
+void procfs_resume (ptid_t, int, enum target_signal);
+void procfs_fetch_registers (struct regcache*, int);
+void procfs_store_registers (struct regcache*, int);
+void procfs_kill_inferior (ptid_t ptid);
+static void procfs_mourn_inferior (void);
+ptid_t procfs_wait (ptid_t, struct target_waitstatus *);
+int procfs_xfer_memory (CORE_ADDR, char *, int, int);
+int procfs_thread_alive (ptid_t);
+void procfs_find_new_threads (void);
+char *procfs_pid_to_str (ptid_t);
+
+struct target_ops procfs_ops;		/* the target vector */
+
+static int attach_flag = 0;
+
+
+/* gdb_sigset_t */
+typedef sigset_t gdb_sigset_t;
+
+/* sigaction */
+typedef struct sigaction gdb_sigaction_t;
+
+/* siginfo */
+typedef struct siginfo gdb_siginfo_t;
+
+/* gdb_premptysysset */
+#define gdb_premptysysset premptyset
+
+/* praddsysset */
+#define gdb_praddsysset praddset
+
+/* prdelsysset */
+#define gdb_prdelsysset prdelset
+
+/* prissyssetmember */
+#define gdb_pr_issyssetmember prismember
+
+
+/* format strings for /proc paths */
+# ifndef CTL_PROC_NAME_FMT
+#  define MAIN_PROC_NAME_FMT   "/proc/%d"
+#  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"
+#  define MAX_PROC_NAME_SIZE sizeof("/proc/99999/lwp/8096/lstatus")
+# endif
+/* the name of the proc status struct depends on the implementation */
+
+
+typedef struct procinfo {
+  struct procinfo *next;
+  int pid;			/* Process ID    */
+  int tid;			/* Thread/LWP id */
+
+  /* process state */
+  int was_stopped;
+  int ignore_next_sigstop;
+
+  /* The following four fd fields may be identical, or may contain
+     several different fd's, depending on the version of /proc
+     (old ioctl or new read/write).  */
+
+  int ctl_fd;			/* File descriptor for /proc control file */
+  /*
+   * The next three file descriptors are actually only needed in the
+   * read/write, multiple-file-descriptor implemenation (NEW_PROC_API).
+   * However, to avoid a bunch of #ifdefs in the code, we will use
+   * them uniformly by (in the case of the ioctl single-file-descriptor
+   * implementation) filling them with copies of the control fd.
+   */
+  int status_fd;		/* File descriptor for /proc status file */
+  int as_fd;			/* File descriptor for /proc as file */
+
+  char pathname[MAX_PROC_NAME_SIZE];	/* Pathname to /proc entry */
+
+  fltset_t saved_fltset;	/* Saved traced hardware fault set */
+  gdb_sigset_t saved_sigset;	/* Saved traced signal set */
+  gdb_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 */
+
+  gdb_prstatus_t prstatus;	/* Current process status info */
+
+  gdb_fpregset_t fpregset;	/* Current floating point registers */
+
+
+  struct procinfo *thread_list;
+
+  int status_valid : 1;
+  int gregs_valid  : 1;
+  int fpregs_valid : 1;
+  int threads_valid: 1;
+} procinfo;
+
+
+static char errmsg[128];	/* shared error msg buffer */
+
+/* Function prototypes for procinfo module: */
+
+static procinfo *find_procinfo_or_die (int pid, int tid);
+static procinfo *find_procinfo (int pid, int tid);
+static procinfo *create_procinfo (int pid, int tid);
+static void destroy_procinfo (procinfo * p);
+static void dead_procinfo (procinfo * p, char *msg, int killp);
+static int open_procinfo_files (procinfo * p, int which);
+static void close_procinfo_files (procinfo * p);
+static int sysset_t_size (procinfo *p);
+static sysset_t *sysset_t_alloc (procinfo * pi);
+
+/* The head of the procinfo list: */
+static procinfo * procinfo_list;
+
+/*
+ * Function: find_procinfo
+ *
+ * Search the procinfo list.
+ *
+ * Returns: pointer to procinfo, or NULL if not found.
+ */
+
+static procinfo *
+find_procinfo (int pid, int tid)
+{
+  procinfo *pi;
+
+  for (pi = procinfo_list; pi; pi = pi->next)
+    if (pi->pid == pid)
+      break;
+
+  if (pi)
+    if (tid)
+      {
+	/* Don't check threads_valid.  If we're updating the
+	   thread_list, we want to find whatever threads are already
+	   here.  This means that in general it is the caller's
+	   responsibility to check threads_valid and update before
+	   calling find_procinfo, if the caller wants to find a new
+	   thread. */
+
+	for (pi = pi->thread_list; pi; pi = pi->next)
+	  if (pi->tid == tid)
+	    break;
+      }
+
+  return pi;
+}
+
+/*
+ * Function: find_procinfo_or_die
+ *
+ * Calls find_procinfo, but errors on failure.
+ */
+
+static procinfo *
+find_procinfo_or_die (int pid, int tid)
+{
+  procinfo *pi = find_procinfo (pid, tid);
+
+  if (pi == NULL)
+    {
+      if (tid)
+	error ("procfs: couldn't find pid %d (kernel thread %d) in procinfo list.",
+	       pid, tid);
+      else
+	error ("procfs: couldn't find pid %d in procinfo list.", pid);
+    }
+  return pi;
+}
+
+/* open_with_retry() is a wrapper for open().  The appropriate
+   open() call is attempted; if unsuccessful, it will be retried as
+   many times as needed for the EAGAIN and EINTR conditions.
+
+   For other conditions, open_with_retry() will retry the open() a
+   limited number of times.  In addition, a short sleep is imposed
+   prior to retrying the open().  The reason for this sleep is to give
+   the kernel a chance to catch up and create the file in question in
+   the event that GDB "wins" the race to open a file before the kernel
+   has created it.  */
+
+static int
+open_with_retry (const char *pathname, int flags)
+{
+  int retries_remaining, status;
+
+  retries_remaining = 2;
+
+  while (1)
+    {
+      status = open (pathname, flags);
+
+      if (status >= 0 || retries_remaining == 0)
+	break;
+      else if (errno != EINTR && errno != EAGAIN)
+	{
+	  retries_remaining--;
+	  sleep (1);
+	}
+    }
+
+  return status;
+}
+
+/*
+ * Function: open_procinfo_files
+ *
+ * Open the file descriptor for the process or LWP.
+ * ifdef NEW_PROC_API, we only open the control file descriptor;
+ * the others are opened lazily as needed.
+ * else (if not NEW_PROC_API), there is only one real
+ * file descriptor, but we keep multiple copies of it so that
+ * the code that uses them does not have to be #ifdef'd.
+ *
+ * Return: file descriptor, or zero for failure.
+ */
+
+enum { FD_CTL, FD_STATUS, FD_AS };
+
+static int
+open_procinfo_files (procinfo *pi, int which)
+{
+  char tmp[MAX_PROC_NAME_SIZE];
+  int  fd;
+
+  /*
+   * There are several different file descriptors that we might be 
+   * asked to open.  The control file descriptor will be opened 
+   * early, but the others will be opened lazily as they are needed.
+   */
+
+  strcpy (tmp, pi->pathname);
+  switch (which) {	/* which file descriptor to open? */
+  case FD_CTL:
+    if (pi->tid)
+      strcat (tmp, "/lwpctl");
+    else
+      strcat (tmp, "/ctl");
+    fd = open_with_retry (tmp, O_WRONLY);
+    if (fd <= 0)
+      return 0;		/* fail */
+    pi->ctl_fd = fd;
+    break;
+  case FD_AS:
+    if (pi->tid)
+      return 0;		/* there is no 'as' file descriptor for an lwp */
+    strcat (tmp, "/as");
+    fd = open_with_retry (tmp, O_RDWR);
+    if (fd <= 0)
+      return 0;		/* fail */
+    pi->as_fd = fd;
+    break;
+  case FD_STATUS:
+    if (pi->tid)
+      strcat (tmp, "/lwpstatus");
+    else
+      strcat (tmp, "/status");
+    fd = open_with_retry (tmp, O_RDONLY);
+    if (fd <= 0)
+      return 0;		/* fail */
+    pi->status_fd = fd;
+    break;
+  default:
+    return 0;		/* unknown file descriptor */
+  }
+
+  return 1;		/* success */
+}
+
+/*
+ * Function: create_procinfo
+ *
+ * Allocate a data structure and link it into the procinfo list.
+ * (First tries to find a pre-existing one (FIXME: why?)
+ *
+ * Return: pointer to new procinfo struct.
+ */
+
+static procinfo *
+create_procinfo (int pid, int tid)
+{
+  procinfo *pi, *parent = NULL;
+
+  if ((pi = find_procinfo (pid, tid)))
+    return pi;			/* Already exists, nothing to do. */
+
+  /* find parent before doing malloc, to save having to cleanup */
+  if (tid != 0)
+    parent = find_procinfo_or_die (pid, 0);	/* FIXME: should I
+						   create it if it
+						   doesn't exist yet? */
+
+  pi = (procinfo *) xmalloc (sizeof (procinfo));
+  memset (pi, 0, sizeof (procinfo));
+  pi->pid = pid;
+  pi->tid = tid;
+
+
+  pi->saved_entryset = sysset_t_alloc (pi);
+  pi->saved_exitset = sysset_t_alloc (pi);
+
+  /* Chain into list.  */
+  if (tid == 0)
+    {
+      sprintf (pi->pathname, MAIN_PROC_NAME_FMT, pid);
+      pi->next = procinfo_list;
+      procinfo_list = pi;
+    }
+  else
+    {
+      sprintf (pi->pathname, "/proc/%05d/lwp/%d", pid, tid);
+      pi->next = parent->thread_list;
+      parent->thread_list = pi;
+    }
+  return pi;
+}
+
+/*
+ * Function: close_procinfo_files
+ *
+ * Close all file descriptors associated with the procinfo
+ */
+
+static void
+close_procinfo_files (procinfo *pi)
+{
+  if (pi->ctl_fd > 0)
+    close (pi->ctl_fd);
+  if (pi->as_fd > 0)
+    close (pi->as_fd);
+  if (pi->status_fd > 0)
+    close (pi->status_fd);
+  pi->ctl_fd = pi->as_fd = pi->status_fd = 0;
+}
+
+/*
+ * Function: destroy_procinfo
+ *
+ * Destructor function.  Close, unlink and deallocate the object.
+ */
+
+static void
+destroy_one_procinfo (procinfo **list, procinfo *pi)
+{
+  procinfo *ptr;
+
+  /* Step one: unlink the procinfo from its list */
+  if (pi == *list)
+    *list = pi->next;
+  else
+    for (ptr = *list; ptr; ptr = ptr->next)
+      if (ptr->next == pi)
+	{
+	  ptr->next =  pi->next;
+	  break;
+	}
+
+  /* Step two: close any open file descriptors */
+  close_procinfo_files (pi);
+
+  /* Step three: free the memory. */
+  xfree (pi->saved_entryset);
+  xfree (pi->saved_exitset);
+  xfree (pi);
+}
+
+static void
+destroy_procinfo (procinfo *pi)
+{
+  procinfo *tmp;
+
+  if (pi->tid != 0)	/* destroy a thread procinfo */
+    {
+      tmp = find_procinfo (pi->pid, 0);	/* find the parent process */
+      destroy_one_procinfo (&tmp->thread_list, pi);
+    }
+  else			/* destroy a process procinfo and all its threads */
+    {
+      /* First destroy the children, if any; */
+      while (pi->thread_list != NULL)
+	destroy_one_procinfo (&pi->thread_list, pi->thread_list);
+      /* Then destroy the parent.  Genocide!!!  */
+      destroy_one_procinfo (&procinfo_list, pi);
+    }
+}
+
+
+enum { NOKILL, KILL };
+
+/*
+ * Function: dead_procinfo
+ *
+ * To be called on a non_recoverable error for a procinfo.
+ * Prints error messages, optionally sends a SIGKILL to the process,
+ * then destroys the data structure.
+ */
+static void
+dead_procinfo (procinfo *pi, char *msg, int kill_p)
+{
+  char procfile[80];
+
+  if (pi->pathname)
+    {
+      print_sys_errmsg (pi->pathname, errno);
+    }
+  else
+    {
+      sprintf (procfile, "process %d", pi->pid);
+      print_sys_errmsg (procfile, errno);
+    }
+  if (kill_p == KILL)
+    kill (pi->pid, SIGKILL);
+
+  destroy_procinfo (pi);
+  error ((msg));
+}
+
+/*
+ * Function: sysset_t_size
+ *
+ * Returns the (complete) size of a sysset_t struct.  Normally, this
+ * is just sizeof (syset_t), but in the case of Monterey/64, the actual
+ * size of sysset_t isn't known until runtime.
+ */
+
+static int
+sysset_t_size (procinfo * pi)
+{
+  return sizeof (sysset_t);
+}
+
+/* Function: sysset_t_alloc
+
+   Allocate and (partially) initialize a sysset_t struct.  */
+
+static sysset_t *
+sysset_t_alloc (procinfo * pi)
+{
+  sysset_t *ret;
+  int size = sysset_t_size (pi);
+  ret = xmalloc (size);
+  return ret;
+}
+
+
+int proc_get_status (procinfo * pi);
+long proc_flags (procinfo * pi);
+int proc_why (procinfo * pi);
+int proc_what (procinfo * pi);
+int proc_set_run_on_last_close (procinfo * pi);
+int proc_unset_run_on_last_close (procinfo * pi);
+int proc_set_inherit_on_fork (procinfo * pi);
+int proc_unset_inherit_on_fork (procinfo * pi);
+int proc_set_async (procinfo * pi);
+int proc_unset_async (procinfo * pi);
+int proc_stop_process (procinfo * pi);
+int proc_trace_signal (procinfo * pi, int signo);
+int proc_ignore_signal (procinfo * pi, int signo);
+int proc_clear_current_fault (procinfo * pi);
+int proc_set_current_signal (procinfo * pi, int signo);
+int proc_clear_current_signal (procinfo * pi);
+int proc_set_gregs (procinfo * pi);
+int proc_set_fpregs (procinfo * pi);
+int proc_wait_for_stop (procinfo * pi);
+int proc_run_process (procinfo * pi, int step, int signo);
+int proc_kill (procinfo * pi, int signo);
+int proc_parent_pid (procinfo * pi);
+int proc_get_nthreads (procinfo * pi);
+int proc_get_current_thread (procinfo * pi);
+int proc_set_held_signals (procinfo * pi, gdb_sigset_t * sighold);
+int proc_set_traced_sysexit (procinfo * pi, sysset_t * sysset);
+int proc_set_traced_sysentry (procinfo * pi, sysset_t * sysset);
+int proc_set_traced_faults (procinfo * pi, fltset_t * fltset);
+int proc_set_traced_signals (procinfo * pi, gdb_sigset_t * sigset);
+
+int proc_update_threads (procinfo * pi);
+int proc_iterate_over_threads (procinfo * pi,
+			       int (*func) (procinfo *, procinfo *, void *),
+			       void *ptr);
+
+gdb_gregset_t *proc_get_gregs (procinfo * pi);
+gdb_fpregset_t *proc_get_fpregs (procinfo * pi);
+sysset_t *proc_get_traced_sysexit (procinfo * pi, sysset_t * save);
+sysset_t *proc_get_traced_sysentry (procinfo * pi, sysset_t * save);
+fltset_t *proc_get_traced_faults (procinfo * pi, fltset_t * save);
+gdb_sigset_t *proc_get_traced_signals (procinfo * pi, gdb_sigset_t * save);
+gdb_sigset_t *proc_get_held_signals (procinfo * pi, gdb_sigset_t * save);
+gdb_sigset_t *proc_get_pending_signals (procinfo * pi, gdb_sigset_t * save);
+gdb_sigaction_t *proc_get_signal_actions (procinfo * pi, gdb_sigaction_t *save);
+
+void proc_warn (procinfo * pi, char *func, int line);
+void proc_error (procinfo * pi, char *func, int line);
+
+void
+proc_warn (procinfo *pi, char *func, int line)
+{
+  sprintf (errmsg, "procfs: %s line %d, %s", func, line, pi->pathname);
+  print_sys_errmsg (errmsg, errno);
+}
+
+void
+proc_error (procinfo *pi, char *func, int line)
+{
+  sprintf (errmsg, "procfs: %s line %d, %s", func, line, pi->pathname);
+  perror_with_name (errmsg);
+}
+
+/*
+ * Function: proc_get_status
+ *
+ * Updates the status struct in the procinfo.
+ * There is a 'valid' flag, to let other functions know when
+ * this function needs to be called (so the status is only
+ * read when it is needed).  The status file descriptor is
+ * also only opened when it is needed.
+ *
+ * Return: non-zero for success, zero for failure.
+ */
+
+int
+proc_get_status (procinfo *pi)
+{
+  /* Status file descriptor is opened "lazily" */
+  if (pi->status_fd == 0 &&
+      open_procinfo_files (pi, FD_STATUS) == 0)
+    {
+      pi->status_valid = 0;
+      return 0;
+    }
+
+  if (lseek (pi->status_fd, 0, SEEK_SET) < 0)
+    pi->status_valid = 0;			/* fail */
+  else
+    {
+      /* Sigh... I have to read a different data structure,
+	 depending on whether this is a main process or an LWP. */
+      if (pi->tid)
+	pi->status_valid = (read (pi->status_fd,
+				  (char *) &pi->prstatus.pr_lwp,
+				  sizeof (lwpstatus_t))
+			    == sizeof (lwpstatus_t));
+      else
+	{
+	  pi->status_valid = (read (pi->status_fd,
+				    (char *) &pi->prstatus,
+				    sizeof (gdb_prstatus_t))
+			      == sizeof (gdb_prstatus_t));
+	}
+    }
+
+  /* The status struct includes general regs, so mark them valid too */
+  pi->gregs_valid  = pi->status_valid;
+  /* In the read/write multiple-fd model,
+     the status struct includes the fp regs too, so mark them valid too */
+  pi->fpregs_valid = pi->status_valid;
+  return pi->status_valid;	/* True if success, false if failure. */
+}
+
+/*
+ * Function: proc_flags
+ *
+ * returns the process flags (pr_flags field).
+ */
+
+long
+proc_flags (procinfo *pi)
+{
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return 0;	/* FIXME: not a good failure value (but what is?) */
+
+  return pi->prstatus.pr_lwp.pr_flags;
+}
+
+/*
+ * Function: proc_why
+ *
+ * returns the pr_why field (why the process stopped).
+ */
+
+int
+proc_why (procinfo *pi)
+{
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return 0;	/* FIXME: not a good failure value (but what is?) */
+
+  return pi->prstatus.pr_lwp.pr_why;
+}
+
+/*
+ * Function: proc_what
+ *
+ * returns the pr_what field (details of why the process stopped).
+ */
+
+int
+proc_what (procinfo *pi)
+{
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return 0;	/* FIXME: not a good failure value (but what is?) */
+
+  return pi->prstatus.pr_lwp.pr_what;
+}
+
+/*
+ * Function: proc_nsysarg
+ *
+ * returns the pr_nsysarg field (number of args to the current syscall).
+ */
+
+int
+proc_nsysarg (procinfo *pi)
+{
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return 0;
+
+  return pi->prstatus.pr_lwp.pr_nsysarg;
+}
+
+/*
+ * Function: proc_sysargs
+ *
+ * returns the pr_sysarg field (pointer to the arguments of current syscall).
+ */
+
+long *
+proc_sysargs (procinfo *pi)
+{
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return NULL;
+
+  return (long *) &pi->prstatus.pr_lwp.pr_sysarg;
+}
+
+/*
+ * Function: proc_syscall
+ *
+ * returns the pr_syscall field (id of current syscall if we are in one).
+ */
+
+int
+proc_syscall (procinfo *pi)
+{
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return 0;
+
+  return pi->prstatus.pr_lwp.pr_syscall;
+}
+
+/*
+ * Function: proc_cursig:
+ *
+ * returns the pr_cursig field (current signal).
+ */
+
+long
+proc_cursig (struct procinfo *pi)
+{
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return 0;	/* FIXME: not a good failure value (but what is?) */
+
+  return pi->prstatus.pr_lwp.pr_cursig;
+}
+
+/*
+ * Function: proc_modify_flag
+ *
+ * Set or reset any of the following process flags:
+ *    PR_FORK	-- forked child will inherit trace flags
+ *    PR_RLC	-- traced process runs when last /proc file closed.
+ *    PR_KLC    -- traced process is killed when last /proc file closed.
+ *    PR_ASYNC	-- LWP's get to run/stop independently.
+ *
+ * Arguments:
+ *    pi   -- the procinfo
+ *    flag -- one of PR_FORK, PR_RLC, or PR_ASYNC
+ *    mode -- 1 for set, 0 for reset.
+ *
+ * Returns non-zero for success, zero for failure.
+ */
+
+enum { FLAG_RESET, FLAG_SET };
+
+static int
+proc_modify_flag (procinfo *pi, long flag, long mode)
+{
+  long win = 0;		/* default to fail */
+
+  /*
+   * These operations affect the process as a whole, and applying
+   * them to an individual LWP has the same meaning as applying them
+   * to the main process.  Therefore, if we're ever called with a
+   * pointer to an LWP's procinfo, let's substitute the process's
+   * procinfo and avoid opening the LWP's file descriptor
+   * unnecessarily.
+   */
+
+  if (pi->pid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  /* First normalize the PCUNSET/PCRESET command opcode
+     (which for no obvious reason has a different definition
+     from one operating system to the next...)  */
+#define GDBRESET PCUNSET
+  {
+    procfs_ctl_t arg[2];
+
+    if (mode == FLAG_SET)	/* Set the flag (RLC, FORK, or ASYNC) */
+      arg[0] = PCSET;
+    else			/* Reset the flag */
+      arg[0] = GDBRESET;
+
+    arg[1] = flag;
+    win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg));
+  }
+#undef GDBRESET
+  /* The above operation renders the procinfo's cached pstatus obsolete. */
+  pi->status_valid = 0;
+
+  if (!win)
+    warning ("procfs: modify_flag failed to turn %s %s",
+	     flag == PR_FORK  ? "PR_FORK"  :
+	     flag == PR_RLC   ? "PR_RLC"   :
+	     flag == PR_ASYNC ? "PR_ASYNC" :
+	     flag == PR_KLC   ? "PR_KLC"   :
+	     "<unknown flag>",
+	     mode == FLAG_RESET ? "off" : "on");
+
+  return win;
+}
+
+/*
+ * Function: proc_set_run_on_last_close
+ *
+ * Set the run_on_last_close flag.
+ * Process with all threads will become runnable
+ * when debugger closes all /proc fds.
+ *
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_set_run_on_last_close (procinfo *pi)
+{
+  return proc_modify_flag (pi, PR_RLC, FLAG_SET);
+}
+
+/*
+ * Function: proc_unset_run_on_last_close
+ *
+ * Reset the run_on_last_close flag.
+ * Process will NOT become runnable
+ * when debugger closes its file handles.
+ *
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_unset_run_on_last_close (procinfo *pi)
+{
+  return proc_modify_flag (pi, PR_RLC, FLAG_RESET);
+}
+
+/*
+ * Function: proc_set_kill_on_last_close
+ *
+ * Set the kill_on_last_close flag.
+ * Process with all threads will be killed when debugger
+ * closes all /proc fds (or debugger exits or dies).
+ *
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_set_kill_on_last_close (procinfo *pi)
+{
+  return proc_modify_flag (pi, PR_KLC, FLAG_SET);
+}
+
+/*
+ * Function: proc_unset_kill_on_last_close
+ *
+ * Reset the kill_on_last_close flag.
+ * Process will NOT be killed when debugger
+ * closes its file handles (or exits or dies).
+ *
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_unset_kill_on_last_close (procinfo *pi)
+{
+  return proc_modify_flag (pi, PR_KLC, FLAG_RESET);
+}
+
+/*
+ * Function: proc_set_inherit_on_fork
+ *
+ * Set inherit_on_fork flag.
+ * If the process forks a child while we are registered for events
+ * in the parent, then we will also recieve events from the child.
+ *
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_set_inherit_on_fork (procinfo *pi)
+{
+  return proc_modify_flag (pi, PR_FORK, FLAG_SET);
+}
+
+/*
+ * Function: proc_unset_inherit_on_fork
+ *
+ * Reset inherit_on_fork flag.
+ * If the process forks a child while we are registered for events
+ * in the parent, then we will NOT recieve events from the child.
+ *
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_unset_inherit_on_fork (procinfo *pi)
+{
+  return proc_modify_flag (pi, PR_FORK, FLAG_RESET);
+}
+
+/*
+ * Function: proc_set_async
+ *
+ * Set PR_ASYNC flag.
+ * If one LWP stops because of a debug event (signal etc.),
+ * the remaining LWPs will continue to run.
+ *
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_set_async (procinfo *pi)
+{
+  return proc_modify_flag (pi, PR_ASYNC, FLAG_SET);
+}
+
+/*
+ * Function: proc_unset_async
+ *
+ * Reset PR_ASYNC flag.
+ * If one LWP stops because of a debug event (signal etc.),
+ * then all other LWPs will stop as well.
+ *
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_unset_async (procinfo *pi)
+{
+  return proc_modify_flag (pi, PR_ASYNC, FLAG_RESET);
+}
+
+/*
+ * Function: proc_stop_process
+ *
+ * Request the process/LWP to stop.  Does not wait.
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_stop_process (procinfo *pi)
+{
+  int win;
+
+  /*
+   * We might conceivably apply this operation to an LWP, and
+   * the LWP's ctl file descriptor might not be open.
+   */
+
+  if (pi->ctl_fd == 0 &&
+      open_procinfo_files (pi, FD_CTL) == 0)
+    return 0;
+  else
+    {
+      procfs_ctl_t cmd = PCSTOP;
+      win = (write (pi->ctl_fd, (char *) &cmd, sizeof (cmd)) == sizeof (cmd));
+    }
+
+  return win;
+}
+
+/*
+ * Function: proc_wait_for_stop
+ *
+ * Wait for the process or LWP to stop (block until it does).
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_wait_for_stop (procinfo *pi)
+{
+  int win;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  {
+    procfs_ctl_t cmd = PCWSTOP;
+    win = (write (pi->ctl_fd, (char *) &cmd, sizeof (cmd)) == sizeof (cmd));
+    /* We been runnin' and we stopped -- need to update status.  */
+    pi->status_valid = 0;
+  }
+
+  return win;
+}
+
+/*
+ * Function: proc_run_process
+ *
+ * Make the process or LWP runnable.
+ * Options (not all are implemented):
+ *   - single-step
+ *   - clear current fault
+ *   - clear current signal
+ *   - abort the current system call
+ *   - stop as soon as finished with system call
+ *   - (ioctl): set traced signal set
+ *   - (ioctl): set held   signal set
+ *   - (ioctl): set traced fault  set
+ *   - (ioctl): set start pc (vaddr)
+ * Always clear the current fault.
+ * Clear the current signal if 'signo' is zero.
+ *
+ * Arguments:
+ *   pi		the process or LWP to operate on.
+ *   step	if true, set the process or LWP to trap after one instr.
+ *   signo	if zero, clear the current signal if any.
+ *		if non-zero, set the current signal to this one.
+ *
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_run_process (procinfo *pi, int step, int signo)
+{
+  int win;
+  int runflags;
+
+  /*
+   * We will probably have to apply this operation to individual threads,
+   * so make sure the control file descriptor is open.
+   */
+
+  if (pi->ctl_fd == 0 &&
+      open_procinfo_files (pi, FD_CTL) == 0)
+    {
+      return 0;
+    }
+
+  runflags    = PRCFAULT;	/* always clear current fault  */
+  if (step)
+    runflags |= PRSTEP;
+  if (signo == 0)
+    runflags |= PRCSIG;
+  else if (signo != -1)		/* -1 means do nothing W.R.T. signals */
+    proc_set_current_signal (pi, signo);
+
+  {
+    procfs_ctl_t cmd[2];
+
+    cmd[0]  = PCRUN;
+    cmd[1]  = runflags;
+    win = (write (pi->ctl_fd, (char *) &cmd, sizeof (cmd)) == sizeof (cmd));
+  }
+
+  return win;
+}
+
+/*
+ * Function: proc_set_traced_signals
+ *
+ * Register to trace signals in the process or LWP.
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_set_traced_signals (procinfo *pi, gdb_sigset_t *sigset)
+{
+  int win;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  {
+    struct {
+      procfs_ctl_t cmd;
+      /* Use char array to avoid alignment issues.  */
+      char sigset[sizeof (gdb_sigset_t)];
+    } arg;
+
+    arg.cmd = PCSTRACE;
+    memcpy (&arg.sigset, sigset, sizeof (gdb_sigset_t));
+
+    win = (write (pi->ctl_fd, (char *) &arg, sizeof (arg)) == sizeof (arg));
+  }
+  /* The above operation renders the procinfo's cached pstatus obsolete. */
+  pi->status_valid = 0;
+
+  if (!win)
+    warning ("procfs: set_traced_signals failed");
+  return win;
+}
+
+/*
+ * Function: proc_set_traced_faults
+ *
+ * Register to trace hardware faults in the process or LWP.
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_set_traced_faults (procinfo *pi, fltset_t *fltset)
+{
+  int win;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  {
+    struct {
+      procfs_ctl_t cmd;
+      /* Use char array to avoid alignment issues.  */
+      char fltset[sizeof (fltset_t)];
+    } arg;
+
+    arg.cmd = PCSFAULT;
+    memcpy (&arg.fltset, fltset, sizeof (fltset_t));
+
+    win = (write (pi->ctl_fd, (char *) &arg, sizeof (arg)) == sizeof (arg));
+  }
+  /* The above operation renders the procinfo's cached pstatus obsolete. */
+  pi->status_valid = 0;
+
+  return win;
+}
+
+/*
+ * Function: proc_set_traced_sysentry
+ *
+ * Register to trace entry to system calls in the process or LWP.
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_set_traced_sysentry (procinfo *pi, sysset_t *sysset)
+{
+  int win;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  {
+    struct gdb_proc_ctl_pcsentry {
+      procfs_ctl_t cmd;
+      /* Use char array to avoid alignment issues.  */
+      char sysset[sizeof (sysset_t)];
+    } *argp;
+    int argp_size = sizeof (struct gdb_proc_ctl_pcsentry)
+                  - sizeof (sysset_t)
+		  + sysset_t_size (pi);
+
+    argp = xmalloc (argp_size);
+
+    argp->cmd = PCSENTRY;
+    memcpy (&argp->sysset, sysset, sysset_t_size (pi));
+
+    win = (write (pi->ctl_fd, (char *) argp, argp_size) == argp_size);
+    xfree (argp);
+  }
+  /* The above operation renders the procinfo's cached pstatus obsolete. */
+  pi->status_valid = 0;
+
+  return win;
+}
+
+/*
+ * Function: proc_set_traced_sysexit
+ *
+ * Register to trace exit from system calls in the process or LWP.
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_set_traced_sysexit (procinfo *pi, sysset_t *sysset)
+{
+  int win;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  {
+    struct gdb_proc_ctl_pcsexit {
+      procfs_ctl_t cmd;
+      /* Use char array to avoid alignment issues.  */
+      char sysset[sizeof (sysset_t)];
+    } *argp;
+    int argp_size = sizeof (struct gdb_proc_ctl_pcsexit)
+                  - sizeof (sysset_t)
+		  + sysset_t_size (pi);
+
+    argp = xmalloc (argp_size);
+
+    argp->cmd = PCSEXIT;
+    memcpy (&argp->sysset, sysset, sysset_t_size (pi));
+
+    win = (write (pi->ctl_fd, (char *) argp, argp_size) == argp_size);
+    xfree (argp);
+  }
+  /* The above operation renders the procinfo's cached pstatus obsolete. */
+  pi->status_valid = 0;
+
+  return win;
+}
+
+/*
+ * Function: proc_set_held_signals
+ *
+ * Specify the set of blocked / held signals in the process or LWP.
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_set_held_signals (procinfo *pi, gdb_sigset_t *sighold)
+{
+  int win;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  {
+    struct {
+      procfs_ctl_t cmd;
+      /* Use char array to avoid alignment issues.  */
+      char hold[sizeof (gdb_sigset_t)];
+    } arg;
+
+    arg.cmd  = PCSHOLD;
+    memcpy (&arg.hold, sighold, sizeof (gdb_sigset_t));
+    win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg));
+  }
+  /* The above operation renders the procinfo's cached pstatus obsolete. */
+  pi->status_valid = 0;
+
+  return win;
+}
+
+/*
+ * Function: proc_get_pending_signals
+ *
+ * returns the set of signals that are pending in the process or LWP.
+ * Will also copy the sigset if 'save' is non-zero.
+ */
+
+gdb_sigset_t *
+proc_get_pending_signals (procinfo *pi, gdb_sigset_t *save)
+{
+  gdb_sigset_t *ret = NULL;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return NULL;
+
+  ret = &pi->prstatus.pr_lwp.pr_lwppend;
+  if (save && ret)
+    memcpy (save, ret, sizeof (gdb_sigset_t));
+
+  return ret;
+}
+
+/*
+ * Function: proc_get_signal_actions
+ *
+ * returns the set of signal actions.
+ * Will also copy the sigactionset if 'save' is non-zero.
+ */
+
+gdb_sigaction_t *
+proc_get_signal_actions (procinfo *pi, gdb_sigaction_t *save)
+{
+  gdb_sigaction_t *ret = NULL;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return NULL;
+
+  ret = &pi->prstatus.pr_lwp.pr_action;
+  if (save && ret)
+    memcpy (save, ret, sizeof (gdb_sigaction_t));
+
+  return ret;
+}
+
+/*
+ * Function: proc_get_held_signals
+ *
+ * returns the set of signals that are held / blocked.
+ * Will also copy the sigset if 'save' is non-zero.
+ */
+
+gdb_sigset_t *
+proc_get_held_signals (procinfo *pi, gdb_sigset_t *save)
+{
+  gdb_sigset_t *ret = NULL;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return NULL;
+
+  ret = &pi->prstatus.pr_lwp.pr_lwphold;
+  if (save && ret)
+    memcpy (save, ret, sizeof (gdb_sigset_t));
+
+  return ret;
+}
+
+/*
+ * Function: proc_get_traced_signals
+ *
+ * returns the set of signals that are traced / debugged.
+ * Will also copy the sigset if 'save' is non-zero.
+ */
+
+gdb_sigset_t *
+proc_get_traced_signals (procinfo *pi, gdb_sigset_t *save)
+{
+  gdb_sigset_t *ret = NULL;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return NULL;
+
+  ret = &pi->prstatus.pr_sigtrace;
+  if (save && ret)
+    memcpy (save, ret, sizeof (gdb_sigset_t));
+
+  return ret;
+}
+
+/*
+ * Function: proc_trace_signal
+ *
+ * Add 'signo' to the set of signals that are traced.
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_trace_signal (procinfo *pi, int signo)
+{
+  gdb_sigset_t temp;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  if (pi)
+    {
+      if (proc_get_traced_signals (pi, &temp))
+	{
+	  praddset (&temp, signo);
+	  return proc_set_traced_signals (pi, &temp);
+	}
+    }
+
+  return 0;	/* failure */
+}
+
+/*
+ * Function: proc_ignore_signal
+ *
+ * Remove 'signo' from the set of signals that are traced.
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+proc_ignore_signal (procinfo *pi, int signo)
+{
+  gdb_sigset_t temp;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  if (pi)
+    {
+      if (proc_get_traced_signals (pi, &temp))
+	{
+	  prdelset (&temp, signo);
+	  return proc_set_traced_signals (pi, &temp);
+	}
+    }
+
+  return 0;	/* failure */
+}
+
+/*
+ * Function: proc_get_traced_faults
+ *
+ * returns the set of hardware faults that are traced /debugged.
+ * Will also copy the faultset if 'save' is non-zero.
+ */
+
+fltset_t *
+proc_get_traced_faults (procinfo *pi, fltset_t *save)
+{
+  fltset_t *ret = NULL;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return NULL;
+
+  ret = &pi->prstatus.pr_flttrace;
+  if (save && ret)
+    memcpy (save, ret, sizeof (fltset_t));
+
+  return ret;
+}
+
+/*
+ * Function: proc_get_traced_sysentry
+ *
+ * returns the set of syscalls that are traced /debugged on entry.
+ * Will also copy the syscall set if 'save' is non-zero.
+ */
+
+sysset_t *
+proc_get_traced_sysentry (procinfo *pi, sysset_t *save)
+{
+  sysset_t *ret = NULL;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return NULL;
+
+  ret = &pi->prstatus.pr_sysentry;
+  if (save && ret)
+    memcpy (save, ret, sysset_t_size (pi));
+
+  return ret;
+}
+
+/*
+ * Function: proc_get_traced_sysexit
+ *
+ * returns the set of syscalls that are traced /debugged on exit.
+ * Will also copy the syscall set if 'save' is non-zero.
+ */
+
+sysset_t *
+proc_get_traced_sysexit (procinfo *pi, sysset_t *save)
+{
+  sysset_t * ret = NULL;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return NULL;
+
+  ret = &pi->prstatus.pr_sysexit;
+  if (save && ret)
+    memcpy (save, ret, sysset_t_size (pi));
+
+  return ret;
+}
+
+/*
+ * Function: proc_clear_current_fault
+ *
+ * The current fault (if any) is cleared; the associated signal
+ * will not be sent to the process or LWP when it resumes.
+ * Returns non-zero for success,  zero for failure.
+ */
+
+int
+proc_clear_current_fault (procinfo *pi)
+{
+  int win;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  {
+    procfs_ctl_t cmd = PCCFAULT;
+    win = (write (pi->ctl_fd, (void *) &cmd, sizeof (cmd)) == sizeof (cmd));
+  }
+
+  return win;
+}
+
+/*
+ * Function: proc_set_current_signal
+ *
+ * Set the "current signal" that will be delivered next to the process.
+ * NOTE: semantics are different from those of KILL.
+ * This signal will be delivered to the process or LWP
+ * immediately when it is resumed (even if the signal is held/blocked);
+ * it will NOT immediately cause another event of interest, and will NOT
+ * first trap back to the debugger.
+ *
+ * Returns non-zero for success,  zero for failure.
+ */
+
+int
+proc_set_current_signal (procinfo *pi, int signo)
+{
+  int win;
+  struct {
+    procfs_ctl_t cmd;
+    /* Use char array to avoid alignment issues.  */
+    char sinfo[sizeof (gdb_siginfo_t)];
+  } arg;
+  gdb_siginfo_t *mysinfo;
+  ptid_t wait_ptid;
+  struct target_waitstatus wait_status;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+
+  /* The pointer is just a type alias.  */
+  mysinfo = (gdb_siginfo_t *) &arg.sinfo;
+  get_last_target_status (&wait_ptid, &wait_status);
+  if (ptid_equal (wait_ptid, inferior_ptid)
+      && wait_status.kind == TARGET_WAITKIND_STOPPED
+      && wait_status.value.sig == target_signal_from_host (signo)
+      && proc_get_status (pi)
+      && pi->prstatus.pr_lwp.pr_info.si_signo == signo
+      )
+    /* Use the siginfo associated with the signal being
+       redelivered.  */
+    memcpy (mysinfo, &pi->prstatus.pr_lwp.pr_info, sizeof (gdb_siginfo_t));
+  else
+    {
+      mysinfo->si_signo = signo;
+      mysinfo->si_code  = 0;
+      mysinfo->si_pid   = getpid ();       /* ?why? */
+      mysinfo->si_uid   = getuid ();       /* ?why? */
+    }
+
+  arg.cmd = PCSSIG;
+  win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg))  == sizeof (arg));
+
+  return win;
+}
+
+/*
+ * Function: proc_clear_current_signal
+ *
+ * The current signal (if any) is cleared, and
+ * is not sent to the process or LWP when it resumes.
+ * Returns non-zero for success,  zero for failure.
+ */
+
+int
+proc_clear_current_signal (procinfo *pi)
+{
+  int win;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  {
+    struct {
+      procfs_ctl_t cmd;
+      /* Use char array to avoid alignment issues.  */
+      char sinfo[sizeof (gdb_siginfo_t)];
+    } arg;
+    gdb_siginfo_t *mysinfo;
+
+    arg.cmd = PCSSIG;
+    /* The pointer is just a type alias.  */
+    mysinfo = (gdb_siginfo_t *) &arg.sinfo;
+    mysinfo->si_signo = 0;
+    mysinfo->si_code  = 0;
+    mysinfo->si_errno = 0;
+    mysinfo->si_pid   = getpid ();       /* ?why? */
+    mysinfo->si_uid   = getuid ();       /* ?why? */
+
+    win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg));
+  }
+
+  return win;
+}
+
+/* Return the general-purpose registers for the process or LWP
+   corresponding to PI.  Upon failure, return NULL.  */
+
+gdb_gregset_t *
+proc_get_gregs (procinfo *pi)
+{
+  if (!pi->status_valid || !pi->gregs_valid)
+    if (!proc_get_status (pi))
+      return NULL;
+
+  return &pi->prstatus.pr_lwp.pr_reg;
+}
+
+/* Return the general-purpose registers for the process or LWP
+   corresponding to PI.  Upon failure, return NULL.  */
+
+gdb_fpregset_t *
+proc_get_fpregs (procinfo *pi)
+{
+  if (!pi->status_valid || !pi->fpregs_valid)
+    if (!proc_get_status (pi))
+      return NULL;
+
+  return &pi->prstatus.pr_lwp.pr_fpreg;
+
+}
+
+/* Write the general-purpose registers back to the process or LWP
+   corresponding to PI.  Return non-zero for success, zero for
+   failure.  */
+
+int
+proc_set_gregs (procinfo *pi)
+{
+  gdb_gregset_t *gregs;
+  int win;
+
+  gregs = proc_get_gregs (pi);
+  if (gregs == NULL)
+    return 0;			/* proc_get_regs has already warned.  */
+
+  if (pi->ctl_fd == 0 && open_procinfo_files (pi, FD_CTL) == 0)
+    {
+      return 0;
+    }
+  else
+    {
+      struct {
+	procfs_ctl_t cmd;
+	/* Use char array to avoid alignment issues.  */
+	char gregs[sizeof (gdb_gregset_t)];
+      } arg;
+
+      arg.cmd = PCSREG;
+      memcpy (&arg.gregs, gregs, sizeof (arg.gregs));
+      win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg));
+    }
+
+  /* Policy: writing the registers invalidates our cache.  */
+  pi->gregs_valid = 0;
+  return win;
+}
+
+/* Write the floating-pointer registers back to the process or LWP
+   corresponding to PI.  Return non-zero for success, zero for
+   failure.  */
+
+int
+proc_set_fpregs (procinfo *pi)
+{
+  gdb_fpregset_t *fpregs;
+  int win;
+
+  fpregs = proc_get_fpregs (pi);
+  if (fpregs == NULL)
+    return 0;			/* proc_get_fpregs has already warned.  */
+
+  if (pi->ctl_fd == 0 && open_procinfo_files (pi, FD_CTL) == 0)
+    {
+      return 0;
+    }
+  else
+    {
+      struct {
+	procfs_ctl_t cmd;
+	/* Use char array to avoid alignment issues.  */
+	char fpregs[sizeof (gdb_fpregset_t)];
+      } arg;
+
+      arg.cmd = PCSFPREG;
+      memcpy (&arg.fpregs, fpregs, sizeof (arg.fpregs));
+      win = (write (pi->ctl_fd, (void *) &arg, sizeof (arg)) == sizeof (arg));
+    }
+
+  /* Policy: writing the registers invalidates our cache.  */
+  pi->fpregs_valid = 0;
+  return win;
+}
+
+/*
+ * Function: proc_kill
+ *
+ * Send a signal to the proc or lwp with the semantics of "kill()".
+ * Returns non-zero for success,  zero for failure.
+ */
+
+int
+proc_kill (procinfo *pi, int signo)
+{
+  int win;
+
+  /*
+   * We might conceivably apply this operation to an LWP, and
+   * the LWP's ctl file descriptor might not be open.
+   */
+
+  if (pi->ctl_fd == 0 &&
+      open_procinfo_files (pi, FD_CTL) == 0)
+    {
+      return 0;
+    }
+  else
+    {
+      procfs_ctl_t cmd[2];
+
+      cmd[0] = PCKILL;
+      cmd[1] = signo;
+      win = (write (pi->ctl_fd, (char *) &cmd, sizeof (cmd)) == sizeof (cmd));
+  }
+
+  return win;
+}
+
+/*
+ * Function: proc_parent_pid
+ *
+ * Find the pid of the process that started this one.
+ * Returns the parent process pid, or zero.
+ */
+
+int
+proc_parent_pid (procinfo *pi)
+{
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return 0;
+
+  return pi->prstatus.pr_ppid;
+}
+
+/*
+ * Function: proc_get_nthreads
+ *
+ * Return the number of threads for the process
+ */
+
+int
+proc_get_nthreads (procinfo *pi)
+{
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return 0;
+
+  /*
+   * NEW_PROC_API: only works for the process procinfo,
+   * because the LWP procinfos do not get prstatus filled in.
+   */
+  if (pi->tid != 0)	/* find the parent process procinfo */
+    pi = find_procinfo_or_die (pi->pid, 0);
+  return pi->prstatus.pr_nlwp;
+}
+
+
+/*
+ * Function: proc_get_current_thread (LWP version)
+ *
+ * Return the ID of the thread that had an event of interest.
+ * (ie. the one that hit a breakpoint or other traced event).
+ * All other things being equal, this should be the ID of a
+ * thread that is currently executing.
+ */
+
+int
+proc_get_current_thread (procinfo *pi)
+{
+  /*
+   * Note: this should be applied to the root procinfo for the process,
+   * not to the procinfo for an LWP.  If applied to the procinfo for
+   * an LWP, it will simply return that LWP's ID.  In that case,
+   * find the parent process procinfo.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  if (!pi->status_valid)
+    if (!proc_get_status (pi))
+      return 0;
+
+  return pi->prstatus.pr_lwp.pr_lwpid;
+}
+
+
+/*
+ * Function: proc_update_threads
+ *
+ * Discover the IDs of all the threads within the process, and
+ * create a procinfo for each of them (chained to the parent).
+ *
+ * This unfortunately requires a different method on every OS.
+ *
+ * Return: non-zero for success, zero for failure.
+ */
+
+int
+proc_delete_dead_threads (procinfo *parent, procinfo *thread, void *ignore)
+{
+  if (thread && parent)	/* sanity */
+    {
+      thread->status_valid = 0;
+      if (!proc_get_status (thread))
+	destroy_one_procinfo (&parent->thread_list, thread);
+    }
+  return 0;	/* keep iterating */
+}
+
+
+static void
+do_closedir_cleanup (void *dir)
+{
+  closedir (dir);
+}
+
+int
+proc_update_threads (procinfo *pi)
+{
+  char pathname[MAX_PROC_NAME_SIZE + 16];
+  struct dirent *direntry;
+  DIR *dirp_saved;
+  procinfo *thread;
+  DIR *dirp;
+  int lwpid;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  proc_iterate_over_threads (pi, proc_delete_dead_threads, NULL);
+
+  strcpy (pathname, pi->pathname);
+  strcat (pathname, "/lwp");
+  if ((dirp = opendir (pathname)) == NULL)
+    proc_error (pi, "update_threads, opendir", __LINE__);
+
+  dirp_saved = dirp;
+  while ((direntry = readdir (dirp)) != NULL)
+    if (direntry->d_name[0] != '.')		/* skip '.' and '..' */
+      {
+	lwpid = atoi (&direntry->d_name[0]);
+	if ((thread = create_procinfo (pi->pid, lwpid)) == NULL)
+	  proc_error (pi, "update_threads, create_procinfo", __LINE__);
+      }
+  pi->threads_valid = 1;
+  do_closedir_cleanup(dirp_saved);
+  return 1;
+}
+
+/*
+ * Function: proc_iterate_over_threads
+ *
+ * Description:
+ *   Given a pointer to a function, call that function once
+ *   for each lwp in the procinfo list, until the function
+ *   returns non-zero, in which event return the value
+ *   returned by the function.
+ *
+ * Note: this function does NOT call update_threads.
+ * If you want to discover new threads first, you must
+ * call that function explicitly.  This function just makes
+ * a quick pass over the currently-known procinfos.
+ *
+ * Arguments:
+ *   pi		- parent process procinfo
+ *   func	- per-thread function
+ *   ptr	- opaque parameter for function.
+ *
+ * Return:
+ *   First non-zero return value from the callee, or zero.
+ */
+
+int
+proc_iterate_over_threads (procinfo *pi,
+			   int (*func) (procinfo *, procinfo *, void *),
+			   void *ptr)
+{
+  procinfo *thread, *next;
+  int retval = 0;
+
+  /*
+   * We should never have to apply this operation to any procinfo
+   * except the one for the main process.  If that ever changes
+   * for any reason, then take out the following clause and
+   * replace it with one that makes sure the ctl_fd is open.
+   */
+
+  if (pi->tid != 0)
+    pi = find_procinfo_or_die (pi->pid, 0);
+
+  for (thread = pi->thread_list; thread != NULL; thread = next)
+    {
+      next = thread->next;	/* in case thread is destroyed */
+      if ((retval = (*func) (pi, thread, ptr)) != 0)
+	break;
+    }
+
+  return retval;
+}
+
+ptid_t do_attach (ptid_t ptid);
+void do_detach (int signo);
+void request_interrupt(void);
+static int register_gdb_signals (procinfo *, gdb_sigset_t *);
+
+/*
+ * Function: procfs_debug_inferior
+ *
+ * Sets up the inferior to be debugged.
+ * Registers to trace signals, hardware faults, and syscalls.
+ * Note: does not set RLC flag: caller may want to customize that.
+ *
+ * Returns: zero for success (note! unlike most functions in this module)
+ *   On failure, returns the LINE NUMBER where it failed!
+ */
+
+static int
+procfs_debug_inferior (procinfo *pi)
+{
+  fltset_t traced_faults;
+  gdb_sigset_t traced_signals;
+  sysset_t *traced_syscall_entries;
+  sysset_t *traced_syscall_exits;
+  int status;
+
+  /* Register to trace hardware faults in the child. */
+  prfillset (&traced_faults);		/* trace all faults... */
+  prdelset  (&traced_faults, FLTPAGE);	/* except page fault.  */
+  if (!proc_set_traced_faults  (pi, &traced_faults))
+    return __LINE__;
+
+  /* Register to trace selected signals in the child. */
+  premptyset (&traced_signals);
+  if (!register_gdb_signals (pi, &traced_signals))
+    return __LINE__;
+
+
+  /* Register to trace the 'exit' system call (on entry).  */
+  traced_syscall_entries = sysset_t_alloc (pi);
+  gdb_premptysysset (traced_syscall_entries);
+  gdb_praddsysset (traced_syscall_entries, SYS_exit);
+  gdb_praddsysset (traced_syscall_entries, SYS_lwp_exit);
+
+  status = proc_set_traced_sysentry (pi, traced_syscall_entries);
+  xfree (traced_syscall_entries);
+  if (!status)
+    return __LINE__;
+
+  traced_syscall_exits = sysset_t_alloc (pi);
+  gdb_premptysysset (traced_syscall_exits);
+  gdb_praddsysset (traced_syscall_exits, SYS_exec);
+  gdb_praddsysset (traced_syscall_exits, SYS_execve);
+
+  gdb_praddsysset (traced_syscall_exits, SYS_lwp_create);
+  gdb_praddsysset (traced_syscall_exits, SYS_lwp_exit);
+
+
+  status = proc_set_traced_sysexit (pi, traced_syscall_exits);
+  xfree (traced_syscall_exits);
+  if (!status)
+    return __LINE__;
+
+  return 0;
+}
+
+ptid_t
+do_attach (ptid_t ptid)
+{
+  procinfo *pi;
+  int fail;
+
+  if ((pi = create_procinfo (GET_PID (ptid), 0)) == NULL)
+    perror ("procfs: out of memory in 'attach'");
+
+  if (!open_procinfo_files (pi, FD_CTL))
+    {
+      fprintf (stderr, "procfs:%d -- ", __LINE__);
+      sprintf (errmsg, "do_attach: couldn't open /proc file for process %d",
+	       GET_PID (ptid));
+      dead_procinfo (pi, errmsg, NOKILL);
+    }
+
+  /* Stop the process (if it isn't already stopped).  */
+  if (proc_flags (pi) & (PR_STOPPED | PR_ISTOP))
+    {
+      pi->was_stopped = 1;
+      proc_prettyprint_why (proc_why (pi), proc_what (pi), 1);
+    }
+  else
+    {
+      pi->was_stopped = 0;
+      /* Set the process to run again when we close it.  */
+      if (!proc_set_run_on_last_close (pi))
+	dead_procinfo (pi, "do_attach: couldn't set RLC.", NOKILL);
+
+      /* Now stop the process. */
+      if (!proc_stop_process (pi))
+	dead_procinfo (pi, "do_attach: couldn't stop the process.", NOKILL);
+      pi->ignore_next_sigstop = 1;
+    }
+  /* Save some of the /proc state to be restored if we detach.  */
+  if (!proc_get_traced_faults   (pi, &pi->saved_fltset))
+    dead_procinfo (pi, "do_attach: couldn't save traced faults.", NOKILL);
+  if (!proc_get_traced_signals  (pi, &pi->saved_sigset))
+    dead_procinfo (pi, "do_attach: couldn't save traced signals.", NOKILL);
+  if (!proc_get_traced_sysentry (pi, pi->saved_entryset))
+    dead_procinfo (pi, "do_attach: couldn't save traced syscall entries.",
+		   NOKILL);
+  if (!proc_get_traced_sysexit  (pi, pi->saved_exitset))
+    dead_procinfo (pi, "do_attach: couldn't save traced syscall exits.",
+		   NOKILL);
+  if (!proc_get_held_signals    (pi, &pi->saved_sighold))
+    dead_procinfo (pi, "do_attach: couldn't save held signals.", NOKILL);
+
+  if ((fail = procfs_debug_inferior (pi)) != 0)
+  {
+    printf("fail=%d\n", fail);
+    dead_procinfo (pi, "do_attach: failed in procfs_debug_inferior", NOKILL);
+  }
+
+  /* Let GDB know that the inferior was attached.  */
+  attach_flag = 1;
+  return ptid_build (pi->pid, proc_get_current_thread (pi), 0);
+}
+
+
+void
+do_detach (int signo)
+{
+  procinfo *pi;
+
+  /* Find procinfo for the main process */
+  pi = find_procinfo_or_die (GET_PID (inferior_ptid), 0); /* FIXME: threads */
+  if (signo)
+    if (!proc_set_current_signal (pi, signo))
+      proc_warn (pi, "do_detach, set_current_signal", __LINE__);
+
+  if (!proc_set_traced_signals (pi, &pi->saved_sigset))
+    proc_warn (pi, "do_detach, set_traced_signal", __LINE__);
+
+  if (!proc_set_traced_faults (pi, &pi->saved_fltset))
+    proc_warn (pi, "do_detach, set_traced_faults", __LINE__);
+
+  if (!proc_set_traced_sysentry (pi, pi->saved_entryset))
+    proc_warn (pi, "do_detach, set_traced_sysentry", __LINE__);
+
+  if (!proc_set_traced_sysexit (pi, pi->saved_exitset))
+    proc_warn (pi, "do_detach, set_traced_sysexit", __LINE__);
+
+  if (!proc_set_held_signals (pi, &pi->saved_sighold))
+    proc_warn (pi, "do_detach, set_held_signals", __LINE__);
+
+  if (signo || (proc_flags (pi) & (PR_STOPPED | PR_ISTOP)))
+    {
+      /* Clear any pending signal.  */
+      if (!proc_clear_current_fault (pi))
+        proc_warn (pi, "do_detach, clear_current_fault", __LINE__);
+
+      if (signo == 0 && !proc_clear_current_signal (pi))
+        proc_warn (pi, "do_detach, clear_current_signal", __LINE__);
+
+      if (!proc_set_run_on_last_close (pi))
+        proc_warn (pi, "do_detach, set_rlc", __LINE__);
+    }
+
+  attach_flag = 0;
+  destroy_procinfo (pi);
+}
+
+/*
+ * Function: procfs_stop_process
+ *
+ * Wait for the process to stop (block until it does).
+ * Returns non-zero for success, zero for failure.
+ */
+
+int
+procfs_stop_process (ptid_t ptid)
+{
+  if (!ptid_equal (ptid, null_ptid))
+    {
+      // Find procinfo for main process
+      procinfo *pi = find_procinfo (GET_PID (ptid), 0);
+
+      if (pi)
+        return proc_stop_process (pi);
+    }
+  return 0;
+}
+
+/* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
+   for all registers.
+
+   ??? Is the following note still relevant?  We can't get individual
+   registers with the PT_GETREGS ptrace(2) request either, yet we
+   don't bother with caching at all in that case.
+
+   NOTE: Since the /proc interface cannot give us individual
+   registers, we pay no attention to REGNUM, and just fetch them all.
+   This results in the possibility that we will do unnecessarily many
+   fetches, since we may be called repeatedly for individual
+   registers.  So we cache the results, and mark the cache invalid
+   when the process is resumed.  */
+
+void
+procfs_fetch_registers (struct regcache *regcache, int regnum)
+{
+  gdb_gregset_t *gregs;
+  procinfo *pi;
+  int pid = GET_PID (inferior_ptid);
+  int tid = GET_THREAD (inferior_ptid);
+
+  pi = find_procinfo_or_die (pid, tid);
+
+  if (pi == NULL)
+    error ("procfs: fetch_registers failed to find procinfo for %s",
+	   procfs_pid_to_str (inferior_ptid));
+
+  gregs = proc_get_gregs (pi);
+  if (gregs == NULL)
+    proc_error (pi, "fetch_registers, get_gregs", __LINE__);
+
+  supply_gregset (regcache, (const gdb_gregset_t *) gregs);
+
+  gdb_fpregset_t *fpregs;
+
+
+  fpregs = proc_get_fpregs (pi);
+  if (fpregs == NULL)
+    proc_error (pi, "fetch_registers, get_fpregs", __LINE__);
+
+  supply_fpregset (regcache, (const gdb_fpregset_t *) fpregs);
+}
+
+/* Store register REGNUM back into the inferior.  If REGNUM is -1, do
+   this for all registers.
+
+   NOTE: Since the /proc interface will not read individual registers,
+   we will cache these requests until the process is resumed, and only
+   then write them back to the inferior process.
+ 
+   FIXME: is that a really bad idea?  Have to think about cases where
+   writing one register might affect the value of others, etc.  */
+
+void
+procfs_store_registers (struct regcache *regcache, int regnum)
+{
+  gdb_gregset_t *gregs;
+  procinfo *pi;
+  int pid = GET_PID (inferior_ptid);
+  int tid = GET_THREAD (inferior_ptid);
+
+  pi = find_procinfo_or_die (pid, tid);
+
+  if (pi == NULL)
+    error ("procfs: store_registers: failed to find procinfo for %s",
+	   procfs_pid_to_str (inferior_ptid));
+
+  gregs = proc_get_gregs (pi);
+  if (gregs == NULL)
+    proc_error (pi, "store_registers, get_gregs", __LINE__);
+
+  fill_gregset (regcache, gregs, regnum);
+  if (!proc_set_gregs (pi))
+    proc_error (pi, "store_registers, set_gregs", __LINE__);
+
+      gdb_fpregset_t *fpregs;
+
+  fpregs = proc_get_fpregs (pi);
+  if (fpregs == NULL)
+    proc_error (pi, "store_registers, get_fpregs", __LINE__);
+
+  fill_fpregset (regcache, fpregs, regnum);
+  if (!proc_set_fpregs (pi))
+    proc_error (pi, "store_registers, set_fpregs", __LINE__);
+}
+
+static int
+syscall_is_lwp_exit (procinfo *pi, int scall)
+{
+
+  if (scall == SYS_lwp_exit)
+    return 1;
+  return 0;
+}
+
+static int
+syscall_is_exit (procinfo *pi, int scall)
+{
+  if (scall == SYS_exit)
+    return 1;
+  return 0;
+}
+
+static int
+syscall_is_exec (procinfo *pi, int scall)
+{
+  if (scall == SYS_exec)
+    return 1;
+  if (scall == SYS_execve)
+    return 1;
+  return 0;
+}
+
+static int
+syscall_is_lwp_create (procinfo *pi, int scall)
+{
+  if (scall == SYS_lwp_create)
+    return 1;
+  return 0;
+}
+
+void
+store_waitstatus (struct target_waitstatus *ourstatus, int hoststatus)
+{
+  if (WIFEXITED (hoststatus))
+    {
+      ourstatus->kind = TARGET_WAITKIND_EXITED;
+      ourstatus->value.sig = WEXITSTATUS (hoststatus);
+    }
+  else if (!WIFSTOPPED (hoststatus))
+    {
+      ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+      ourstatus->value.sig = target_signal_from_host (WTERMSIG (hoststatus));
+    }
+  else
+    {
+      ourstatus->kind = TARGET_WAITKIND_STOPPED;
+      ourstatus->value.sig = target_signal_from_host (WSTOPSIG (hoststatus));
+    }
+}
+
+/*
+ * Function: target_wait
+ *
+ * Retrieve the next stop event from the child process.
+ * If child has not stopped yet, wait for it to stop.
+ * Translate /proc eventcodes (or possibly wait eventcodes)
+ * into gdb internal event codes.
+ *
+ * Return: id of process (and possibly thread) that incurred the event.
+ *         event codes are returned thru a pointer parameter.
+ */
+
+ptid_t
+procfs_wait (ptid_t ptid, struct target_waitstatus *status)
+{
+  /* First cut: loosely based on original version 2.1 */
+  procinfo *pi;
+  int       wstat;
+  int       temp_tid;
+  ptid_t    retval, temp_ptid;
+  int       why, what, flags;
+  int       retry = 0;
+
+wait_again:
+
+  retry++;
+  wstat    = 0;
+  retval   = pid_to_ptid (-1);
+
+  /* Find procinfo for main process */
+  pi = find_procinfo_or_die (GET_PID (inferior_ptid), 0);
+  if (pi)
+    {
+      /* We must assume that the status is stale now... */
+      pi->status_valid = 0;
+      pi->gregs_valid  = 0;
+      pi->fpregs_valid = 0;
+
+      /* If child is not stopped, wait for it to stop.  */
+      if (!(proc_flags (pi) & (PR_STOPPED | PR_ISTOP)) &&
+	  !proc_wait_for_stop (pi))
+	{
+	  /* wait_for_stop failed: has the child terminated? */
+	  if (errno == ENOENT)
+	    {
+	      int wait_retval;
+
+	      /* /proc file not found; presumably child has terminated. */
+	      wait_retval = wait (&wstat); /* "wait" for the child's exit  */
+
+	      if (wait_retval != GET_PID (inferior_ptid)) /* wrong child? */
+		error ("procfs: couldn't stop process %d: wait returned %d.",
+		       GET_PID (inferior_ptid), wait_retval);
+	      /* FIXME: might I not just use waitpid?
+		 Or try find_procinfo to see if I know about this child? */
+	      retval = pid_to_ptid (wait_retval);
+	    }
+	  else if (errno == EINTR)
+	    goto wait_again;
+	  else
+	    {
+	      /* Unknown error from wait_for_stop. */
+	      proc_error (pi, "target_wait (wait_for_stop)", __LINE__);
+	    }
+	}
+      else
+	{
+	  /* This long block is reached if either:
+	     a) the child was already stopped, or
+	     b) we successfully waited for the child with wait_for_stop.
+	     This block will analyze the /proc status, and translate it
+	     into a waitstatus for GDB.
+
+	     If we actually had to call wait because the /proc file
+	     is gone (child terminated), then we skip this block,
+	     because we already have a waitstatus.  */
+
+	  flags = proc_flags (pi);
+	  why   = proc_why (pi);
+	  what  = proc_what (pi);
+
+	  if (flags & (PR_STOPPED | PR_ISTOP))
+	    {
+	      /* If it's running async (for single_thread control),
+		 set it back to normal again.  */
+	      if (flags & PR_ASYNC)
+		if (!proc_unset_async (pi))
+		  proc_error (pi, "target_wait, unset_async", __LINE__);
+
+	      if (debug_threads)
+		proc_prettyprint_why (why, what, 1);
+
+	      /* The 'pid' we will return to GDB is composed of
+		 the process ID plus the lwp ID.  */
+	      retval = BUILD_LWP (proc_get_current_thread (pi), pi->pid);
+
+	      switch (why) {
+	      case PR_SIGNALLED:
+		wstat = (what << 8) | 0177;
+		break;
+	      case PR_SYSENTRY:
+		if (syscall_is_lwp_exit (pi, what))
+		  {
+		    printf ("[%s exited]\n",
+				     procfs_pid_to_str (retval));
+		    remove_thread (find_thread_ptid(retval));
+		    status->kind = TARGET_WAITKIND_SPURIOUS;
+		    return retval;
+		  }
+		else if (syscall_is_exit (pi, what))
+		  {
+		    /* Handle SYS_exit call only */
+		    /* Stopped at entry to SYS_exit.
+		       Make it runnable, resume it, then use
+		       the wait system call to get its exit code.
+		       Proc_run_process always clears the current
+		       fault and signal.
+		       Then return its exit status.  */
+		    pi->status_valid = 0;
+		    wstat = 0;
+		    /* FIXME: what we should do is return
+		       TARGET_WAITKIND_SPURIOUS.  */
+		    if (!proc_run_process (pi, 0, 0))
+		      proc_error (pi, "target_wait, run_process", __LINE__);
+		    if (attach_flag)
+		      {
+			/* Don't call wait: simulate waiting for exit,
+			   return a "success" exit code.  Bogus: what if
+			   it returns something else?  */
+			wstat = 0;
+			retval = inferior_ptid;  /* ? ? ? */
+		      }
+		    else
+		      {
+			int temp = wait (&wstat);
+
+			/* FIXME: shouldn't I make sure I get the right
+			   event from the right process?  If (for
+			   instance) I have killed an earlier inferior
+			   process but failed to clean up after it
+			   somehow, I could get its termination event
+			   here.  */
+
+			/* If wait returns -1, that's what we return to GDB. */
+			if (temp < 0)
+			  retval = pid_to_ptid (temp);
+		      }
+		  }
+		else
+		  {
+		    printf ("procfs: trapped on entry to ");
+		    proc_prettyprint_syscall (proc_what (pi), 0);
+		    printf ("\n");
+		    {
+		      long i, nsysargs, *sysargs;
+
+		      if ((nsysargs = proc_nsysarg (pi)) > 0 &&
+			  (sysargs  = proc_sysargs (pi)) != NULL)
+			{
+			  printf ("%ld syscall arguments:\n", nsysargs);
+			  for (i = 0; i < nsysargs; i++)
+			    printf ("#%ld: 0x%08lx\n",
+					     i, sysargs[i]);
+			}
+
+		    }
+		    if (status)
+		      {
+			/* How to exit gracefully, returning "unknown event" */
+			status->kind = TARGET_WAITKIND_SPURIOUS;
+			return inferior_ptid;
+		      }
+		    else
+		      {
+			/* How to keep going without returning to wfi: */
+			regcache_invalidate_one (find_inferior_id (
+			      &all_threads, ptid));
+			procfs_resume (ptid, 0, TARGET_SIGNAL_0);
+			goto wait_again;
+		      }
+		  }
+		break;
+	      case PR_SYSEXIT:
+		if (syscall_is_exec (pi, what))
+		  {
+		    /* Hopefully this is our own "fork-child" execing
+		       the real child.  Hoax this event into a trap, and
+		       GDB will see the child about to execute its start
+		       address. */
+		    wstat = (SIGTRAP << 8) | 0177;
+		  }
+		else if (syscall_is_lwp_create (pi, what))
+		  {
+		    /*
+		     * This syscall is somewhat like fork/exec.
+		     * We will get the event twice: once for the parent LWP,
+		     * and once for the child.  We should already know about
+		     * the parent LWP, but the child will be new to us.  So,
+		     * whenever we get this event, if it represents a new
+		     * thread, simply add the thread to the list.
+		     */
+
+		    /* If not in procinfo list, add it.  */
+		    temp_tid = proc_get_current_thread (pi);
+		    if (!find_procinfo (pi->pid, temp_tid))
+		      create_procinfo  (pi->pid, temp_tid);
+
+		    temp_ptid = BUILD_LWP (temp_tid, pi->pid);
+		    /* If not in GDB's thread list, add it.  */
+		    if (find_thread_ptid(temp_ptid) == NULL)
+		      {
+			add_thread (temp_ptid, NULL);
+		      }
+
+		    /* Return to WFI, but tell it to immediately resume. */
+		    status->kind = TARGET_WAITKIND_SPURIOUS;
+		    return inferior_ptid;
+		  }
+		else if (syscall_is_lwp_exit (pi, what))
+		  {
+		    printf ("[%s exited]\n",
+				     procfs_pid_to_str (retval));
+		    remove_thread (find_thread_ptid(retval));
+		    status->kind = TARGET_WAITKIND_SPURIOUS;
+		    return retval;
+		  }
+		else if (0)
+		  {
+		    /* FIXME:  Do we need to handle SYS_sproc,
+		       SYS_fork, or SYS_vfork here?  The old procfs
+		       seemed to use this event to handle threads on
+		       older (non-LWP) systems, where I'm assuming
+		       that threads were actually separate processes.
+		       Irix, maybe?  Anyway, low priority for now.  */
+		  }
+		else
+		  {
+		    printf ("procfs: trapped on exit from ");
+		    proc_prettyprint_syscall (proc_what (pi), 0);
+		    printf ("\n");
+		    {
+		      long i, nsysargs, *sysargs;
+
+		      if ((nsysargs = proc_nsysarg (pi)) > 0 &&
+			  (sysargs  = proc_sysargs (pi)) != NULL)
+			{
+			  printf ("%ld syscall arguments:\n", nsysargs);
+			  for (i = 0; i < nsysargs; i++)
+			    printf ("#%ld: 0x%08lx\n",
+					     i, sysargs[i]);
+			}
+		    }
+		    status->kind = TARGET_WAITKIND_SPURIOUS;
+		    return inferior_ptid;
+		  }
+		break;
+	      case PR_REQUESTED:
+		if (retry < 5)
+		  {
+		    //printf ("Retry #%d:\n", retry);
+		    pi->status_valid = 0;
+		    goto wait_again;
+		  }
+		else
+		  {
+		    /* If not in procinfo list, add it.  */
+		    temp_tid = proc_get_current_thread (pi);
+		    if (!find_procinfo (pi->pid, temp_tid))
+		      create_procinfo  (pi->pid, temp_tid);
+
+		    /* If not in GDB's thread list, add it.  */
+		    temp_ptid = BUILD_LWP (temp_tid, pi->pid);
+		    if (find_thread_ptid (temp_ptid) == NULL)
+		      {
+		        add_thread (temp_ptid, NULL);
+		      }
+
+		    status->kind = TARGET_WAITKIND_STOPPED;
+		    status->value.sig = 0;
+		    return retval;
+		  }
+	      case PR_JOBCONTROL:
+		wstat = (what << 8) | 0177;
+		break;
+	      case PR_FAULTED:
+		switch (what) {
+		case FLTWATCH:
+		  wstat = (SIGTRAP << 8) | 0177;
+		  break;
+		  /* FIXME: use si_signo where possible. */
+		case FLTPRIV:
+		case FLTILL:
+		  wstat = (SIGILL << 8) | 0177;
+		  break;
+		case FLTBPT:
+		case FLTTRACE:
+		  wstat = (SIGTRAP << 8) | 0177;
+		  break;
+		case FLTSTACK:
+		case FLTACCESS:
+		case FLTBOUNDS:
+		  wstat = (SIGSEGV << 8) | 0177;
+		  break;
+		case FLTIOVF:
+		case FLTIZDIV:
+		case FLTFPE:
+		  wstat = (SIGFPE << 8) | 0177;
+		  break;
+		case FLTPAGE:		/* Recoverable page fault */
+		default:	 /* FIXME: use si_signo if possible for fault */
+		  retval = pid_to_ptid (-1);
+		  printf ("procfs:%d -- ", __LINE__);
+		  printf ("child stopped for unknown reason:\n");
+		  proc_prettyprint_why (why, what, 1);
+		  error ("... giving up...");
+		  break;
+		}
+		break;	/* case PR_FAULTED: */
+	      default:	/* switch (why) unmatched */
+		printf ("procfs:%d -- ", __LINE__);
+		printf ("child stopped for unknown reason:\n");
+		proc_prettyprint_why (why, what, 1);
+		error ("... giving up...");
+		break;
+	      }
+	      /*
+	       * Got this far without error:
+	       * If retval isn't in the threads database, add it.
+	       */
+	      if (GET_PID (retval) > 0 &&
+		  !ptid_equal (retval, inferior_ptid) &&
+		  (find_thread_ptid (retval) == NULL))
+		{
+		  /*
+		   * We have a new thread.
+		   * We need to add it both to GDB's list and to our own.
+		   * If we don't create a procinfo, resume may be unhappy
+		   * later.
+		   */
+		  add_thread (retval, NULL);
+		  if (find_procinfo (GET_PID (retval), GET_THREAD (retval)) == NULL)
+		    create_procinfo (GET_PID (retval), GET_THREAD (retval));
+
+		  /* In addition, it's possible that this is the first
+		   * new thread we've seen, in which case we may not
+		   * have created entries for inferior_ptid yet.
+		   */
+		  if (GET_THREAD (inferior_ptid) != 0)
+		    {
+		      if (find_thread_ptid (inferior_ptid) == NULL)
+		        {
+			  add_thread (inferior_ptid, NULL);
+		        }
+		      if (find_procinfo (GET_PID (inferior_ptid),
+					 GET_THREAD (inferior_ptid)) == NULL)
+			create_procinfo (GET_PID (inferior_ptid),
+					 GET_THREAD (inferior_ptid));
+		    }
+		}
+	    }
+	  else	/* flags do not indicate STOPPED */
+	    {
+	      /* surely this can't happen... */
+	      printf ("procfs:%d -- process not stopped.\n",
+			       __LINE__);
+	      proc_prettyprint_flags (flags, 1);
+	      error ("procfs: ...giving up...");
+	    }
+	}
+
+      if (status)
+	store_waitstatus (status, wstat);
+    }
+
+  return retval;
+}
+
+
+/* Transfer LEN bytes between GDB address MYADDR and target address
+   MEMADDR.  If DOWRITE is non-zero, transfer them to the target,
+   otherwise transfer them from the target.  TARGET is unused.
+
+   The return value is 0 if an error occurred or no bytes were
+   transferred.  Otherwise, it will be a positive value which
+   indicates the number of bytes transferred between gdb and the
+   target.  (Note that the interface also makes provisions for
+   negative values, but this capability isn't implemented here.) */
+
+int
+procfs_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int dowrite)//,
+{
+  procinfo *pi;
+  int nbytes = 0;
+
+  /* Find procinfo for main process */
+  pi = find_procinfo_or_die (GET_PID (inferior_ptid), 0);
+  if (pi->as_fd == 0 &&
+      open_procinfo_files (pi, FD_AS) == 0)
+    {
+      proc_warn (pi, "xfer_memory, open_proc_files", __LINE__);
+      return 0;
+    }
+
+  if (lseek (pi->as_fd, (off_t) memaddr, SEEK_SET) == (off_t) memaddr)
+    {
+      if (dowrite)
+	nbytes = write (pi->as_fd, myaddr, len);
+      else
+	nbytes = read (pi->as_fd, myaddr, len);
+      if (nbytes < 0)
+	nbytes = 0;
+    }
+  return nbytes;
+}
+
+/*
+ * Function: invalidate_cache
+ *
+ * Called by target_resume before making child runnable.
+ * Mark cached registers and status's invalid.
+ * If there are "dirty" caches that need to be written back
+ * to the child process, do that.
+ *
+ * File descriptors are also cached.
+ * As they are a limited resource, we cannot hold onto them indefinitely.
+ * However, as they are expensive to open, we don't want to throw them
+ * away indescriminately either.  As a compromise, we will keep the
+ * file descriptors for the parent process, but discard any file
+ * descriptors we may have accumulated for the threads.
+ *
+ * Return value:
+ * As this function is called by iterate_over_threads, it always
+ * returns zero (so that iterate_over_threads will keep iterating).
+ */
+
+
+static int
+invalidate_cache (procinfo *parent, procinfo *pi, void *ptr)
+{
+  /*
+   * About to run the child; invalidate caches and do any other cleanup.
+   */
+
+
+  if (parent != NULL)
+    {
+      /* The presence of a parent indicates that this is an LWP.
+	 Close any file descriptors that it might have open.
+	 We don't do this to the master (parent) procinfo.  */
+
+      close_procinfo_files (pi);
+    }
+  pi->gregs_valid   = 0;
+  pi->fpregs_valid  = 0;
+  pi->status_valid  = 0;
+  pi->threads_valid = 0;
+
+  return 0;
+}
+
+
+/*
+ * Function: target_resume
+ *
+ * Make the child process runnable.  Normally we will then call
+ * procfs_wait and wait for it to stop again (unles gdb is async).
+ *
+ * Arguments:
+ *  step:  if true, then arrange for the child to stop again
+ *         after executing a single instruction.
+ *  signo: if zero, then cancel any pending signal.
+ *         If non-zero, then arrange for the indicated signal
+ *         to be delivered to the child when it runs.
+ *  pid:   if -1, then allow any child thread to run.
+ *         if non-zero, then allow only the indicated thread to run.
+ *******   (not implemented yet)
+ */
+
+void
+procfs_resume (ptid_t ptid, int step, enum target_signal signo)
+{
+  procinfo *pi, *thread;
+  int native_signo;
+
+  /* 2.1:
+     prrun.prflags |= PRSVADDR;
+     prrun.pr_vaddr = $PC;	   set resume address
+     prrun.prflags |= PRSTRACE;    trace signals in pr_trace (all)
+     prrun.prflags |= PRSFAULT;    trace faults in pr_fault (all but PAGE)
+     prrun.prflags |= PRCFAULT;    clear current fault.
+
+     PRSTRACE and PRSFAULT can be done by other means
+     	(proc_trace_signals, proc_trace_faults)
+     PRSVADDR is unnecessary.
+     PRCFAULT may be replaced by a PIOCCFAULT call (proc_clear_current_fault)
+     This basically leaves PRSTEP and PRCSIG.
+     PRCSIG is like PIOCSSIG (proc_clear_current_signal).
+     So basically PR_STEP is the sole argument that must be passed
+     to proc_run_process (for use in the prrun struct by ioctl). */
+
+  /* Find procinfo for main process */
+  pi = find_procinfo_or_die (GET_PID (inferior_ptid), 0);
+
+  /* First cut: ignore pid argument */
+  errno = 0;
+
+  /* Convert signal to host numbering.  */
+  if (signo == 0 ||
+      (signo == TARGET_SIGNAL_STOP && pi->ignore_next_sigstop))
+    native_signo = 0;
+  else
+    native_signo = target_signal_to_host (signo);
+
+  pi->ignore_next_sigstop = 0;
+
+  /* Running the process voids all cached registers and status. */
+  /* Void the threads' caches first */
+  proc_iterate_over_threads (pi, invalidate_cache, NULL);
+  /* Void the process procinfo's caches.  */
+  invalidate_cache (NULL, pi, NULL);
+
+  if (GET_PID (ptid) != -1)
+    {
+      /* Resume a specific thread, presumably suppressing the others. */
+      thread = find_procinfo (GET_PID (ptid), GET_THREAD (ptid));
+      if (thread != NULL)
+	{
+	  if (thread->tid != 0)
+	    {
+	      /* We're to resume a specific thread, and not the others.
+	       * Set the child process's PR_ASYNC flag.
+	       */
+	      if (!proc_set_async (pi))
+		proc_error (pi, "target_resume, set_async", __LINE__);
+	      pi = thread;	/* substitute the thread's procinfo for run */
+	    }
+	}
+    }
+
+  if (!proc_run_process (pi, step, native_signo))
+    {
+      if (errno == EBUSY)
+	warning ("resume: target already running.  Pretend to resume, and hope for the best!");
+      else
+	proc_error (pi, "target_resume", __LINE__);
+    }
+}
+
+/*
+ * Function: register_gdb_signals
+ *
+ * Traverse the list of signals that GDB knows about
+ * (see "handle" command), and arrange for the target
+ * to be stopped or not, according to these settings.
+ *
+ * Returns non-zero for success, zero for failure.
+ */
+
+static int
+register_gdb_signals (procinfo *pi, gdb_sigset_t *signals)
+{
+  int signo;
+
+  for (signo = 0; signo < NSIG; signo ++)
+    if ( pass_signals[signo] == 1 )
+      prdelset (signals, signo);
+    else
+      praddset (signals, signo);
+
+  return proc_set_traced_signals (pi, signals);
+}
+
+/*
+ * Function: unconditionally_kill_inferior
+ *
+ * Make it die.  Wait for it to die.  Clean up after it.
+ * Note: this should only be applied to the real process,
+ * not to an LWP, because of the check for parent-process.
+ * If we need this to work for an LWP, it needs some more logic.
+ */
+
+static void
+unconditionally_kill_inferior (procinfo *pi)
+{
+  int parent_pid;
+
+  parent_pid = proc_parent_pid (pi);
+  if (!proc_kill (pi, SIGKILL))
+    proc_error (pi, "unconditionally_kill, proc_kill", __LINE__);
+  destroy_procinfo (pi);
+
+  /* If pi is GDB's child, wait for it to die.  */
+  if (parent_pid == getpid ())
+    {
+      wait (NULL);
+    }
+}
+
+/*
+ * Function: target_kill_inferior
+ *
+ * We're done debugging it, and we want it to go away.
+ * Then we want GDB to forget all about it.
+ */
+
+void
+procfs_kill_inferior (ptid_t ptid)
+{
+  if (!ptid_equal (ptid, null_ptid))
+    {
+      /* Find procinfo for main process */
+      procinfo *pi = find_procinfo (GET_PID (ptid), 0);
+
+      if (pi)
+	unconditionally_kill_inferior (pi);
+      procfs_mourn_inferior ();
+    }
+}
+
+/*
+ * Function: target_mourn_inferior
+ *
+ * Forget we ever debugged this thing!
+ */
+
+static void
+procfs_mourn_inferior (void)
+{
+  procinfo *pi;
+
+  if (!ptid_equal (inferior_ptid, null_ptid))
+    {
+      /* Find procinfo for main process */
+      pi = find_procinfo (GET_PID (inferior_ptid), 0);
+      if (pi)
+	destroy_procinfo (pi);
+    }
+
+  attach_flag = 0;
+}
+
+
+/*
+ * Function: init_inferior
+ *
+ * When GDB forks to create a runnable inferior process,
+ * this function is called on the parent side of the fork.
+ * It's job is to do whatever is necessary to make the child
+ * ready to be debugged, and then wait for the child to synchronize.
+ */
+
+void
+procfs_init_inferior (int pid)
+{
+  procinfo *pi;
+  gdb_sigset_t signals;
+  int fail;
+
+  /* This routine called on the parent side (GDB side)
+     after GDB forks the inferior.  */
+
+  if ((pi = create_procinfo (pid, 0)) == NULL)
+    perror ("procfs: out of memory in 'init_inferior'");
+
+  if (!open_procinfo_files (pi, FD_CTL))
+    proc_error (pi, "init_inferior, open_proc_files", __LINE__);
+
+  /*
+    xmalloc			// done
+    open_procinfo_files		// done
+    link list			// done
+    prfillset (trace)
+    procfs_notice_signals
+    prfillset (fault)
+    prdelset (FLTPAGE)
+    PIOCWSTOP
+    PIOCSFAULT
+    */
+
+  /* If not stopped yet, wait for it to stop. */
+  if (!(proc_flags (pi) & PR_STOPPED) &&
+      !(proc_wait_for_stop (pi)))
+    dead_procinfo (pi, "init_inferior: wait_for_stop failed", KILL);
+
+  /* Save some of the /proc state to be restored if we detach.  */
+  /* FIXME: Why?  In case another debugger was debugging it?
+     We're it's parent, for Ghu's sake! */
+  if (!proc_get_traced_signals  (pi, &pi->saved_sigset))
+    proc_error (pi, "init_inferior, get_traced_signals", __LINE__);
+  if (!proc_get_held_signals    (pi, &pi->saved_sighold))
+    proc_error (pi, "init_inferior, get_held_signals", __LINE__);
+  if (!proc_get_traced_faults   (pi, &pi->saved_fltset))
+    proc_error (pi, "init_inferior, get_traced_faults", __LINE__);
+  if (!proc_get_traced_sysentry (pi, pi->saved_entryset))
+    proc_error (pi, "init_inferior, get_traced_sysentry", __LINE__);
+  if (!proc_get_traced_sysexit  (pi, pi->saved_exitset))
+    proc_error (pi, "init_inferior, get_traced_sysexit", __LINE__);
+
+  /* Register to trace selected signals in the child. */
+  prfillset (&signals);
+  if (!register_gdb_signals (pi, &signals))
+    proc_error (pi, "init_inferior, register_signals", __LINE__);
+
+  if ((fail = procfs_debug_inferior (pi)) != 0)
+    proc_error (pi, "init_inferior (procfs_debug_inferior)", fail);
+
+  /* FIXME: logically, we should really be turning OFF run-on-last-close,
+     and possibly even turning ON kill-on-last-close at this point.  But
+     I can't make that change without careful testing which I don't have
+     time to do right now...  */
+  /* Turn on run-on-last-close flag so that the child
+     will die if GDB goes away for some reason.  */
+  if (!proc_set_run_on_last_close (pi))
+    proc_error (pi, "init_inferior, set_RLC", __LINE__);
+
+  /* The 'process ID' we return to GDB is composed of
+     the actual process ID plus the lwp ID. */
+  inferior_ptid = BUILD_LWP (proc_get_current_thread (pi), pi->pid);
+}
+
+/*
+ * Function: set_exec_trap
+ *
+ * When GDB forks to create a new process, this function is called
+ * on the child side of the fork before GDB exec's the user program.
+ * Its job is to make the child minimally debuggable, so that the
+ * parent GDB process can connect to the child and take over.
+ * This function should do only the minimum to make that possible,
+ * and to synchronize with the parent process.  The parent process
+ * should take care of the details.
+ */
+
+void
+procfs_set_exec_trap (void)
+{
+  /* This routine called on the child side (inferior side)
+     after GDB forks the inferior.  It must use only local variables,
+     because it may be sharing data space with its parent.  */
+
+  procinfo *pi;
+  sysset_t *exitset;
+
+  if ((pi = create_procinfo (getpid (), 0)) == NULL)
+    perror_with_name ("procfs: create_procinfo failed in child.");
+
+  if (open_procinfo_files (pi, FD_CTL) == 0)
+    {
+      proc_warn (pi, "set_exec_trap, open_proc_files", __LINE__);
+      fflush (stdout);
+      /* no need to call "dead_procinfo", because we're going to exit. */
+      _exit (127);
+    }
+
+  /* Everyone else's (except OSF) method for tracing exec syscalls */
+  /* 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. */
+
+  exitset = sysset_t_alloc (pi);
+  gdb_premptysysset (exitset);
+  gdb_praddsysset (exitset, SYS_exec);
+  gdb_praddsysset (exitset, SYS_execve);
+
+  if (!proc_set_traced_sysexit (pi, exitset))
+    {
+      proc_warn (pi, "set_exec_trap, set_traced_sysexit", __LINE__);
+      fflush (stdout);
+      _exit (127);
+    }
+
+  /* FIXME: should this be done in the parent instead? */
+  /* Turn off inherit on fork flag so that all grand-children
+     of gdb start with tracing flags cleared.  */
+  if (!proc_unset_inherit_on_fork (pi))
+    proc_warn (pi, "set_exec_trap, unset_inherit", __LINE__);
+
+  /* Turn off run on last close flag, so that the child process
+     cannot run away just because we close our handle on it.
+     We want it to wait for the parent to attach.  */
+  if (!proc_unset_run_on_last_close (pi))
+    proc_warn (pi, "set_exec_trap, unset_RLC", __LINE__);
+
+  /* FIXME: No need to destroy the procinfo --
+     we have our own address space, and we're about to do an exec! */
+  /*destroy_procinfo (pi);*/
+}
+
+/*
+ * Function: notice_thread
+ *
+ * Callback for find_new_threads.
+ * Calls "add_thread".
+ */
+
+static int
+procfs_notice_thread (procinfo *pi, procinfo *thread, void *ptr)
+{
+  ptid_t gdb_threadid = BUILD_LWP (thread->tid, pi->pid);
+
+  if (find_thread_ptid (gdb_threadid) == NULL)
+    add_thread (gdb_threadid, NULL);
+
+  return 0;
+}
+
+/*
+ * Function: target_find_new_threads
+ *
+ * Query all the threads that the target knows about,
+ * and give them back to GDB to add to its list.
+ */
+
+void
+procfs_find_new_threads (void)
+{
+  procinfo *pi;
+
+  /* Find procinfo for main process */
+  pi = find_procinfo_or_die (GET_PID (inferior_ptid), 0);
+  proc_update_threads (pi);
+  proc_iterate_over_threads (pi, procfs_notice_thread, NULL);
+}
+
+/*
+ * Function: target_thread_alive
+ *
+ * Return true if the thread is still 'alive'.
+ *
+ * This guy doesn't really seem to be doing his job.
+ * Got to investigate how to tell when a thread is really gone.
+ */
+
+int
+procfs_thread_alive (ptid_t ptid)
+{
+  int proc, thread;
+  procinfo *pi;
+
+  proc    = GET_PID (ptid);
+  thread  = GET_THREAD (ptid);
+  /* If I don't know it, it ain't alive! */
+  if ((pi = find_procinfo (proc, thread)) == NULL)
+    return 0;
+
+  /* If I can't get its status, it ain't alive!
+     What's more, I need to forget about it!  */
+  if (!proc_get_status (pi))
+    {
+      destroy_procinfo (pi);
+      return 0;
+    }
+  /* I couldn't have got its status if it weren't alive, so it's alive.  */
+  return 1;
+}
+
+/* Convert PTID to a string.  Returns the string in a static buffer.  */
+
+char *
+procfs_pid_to_str (ptid_t ptid)
+{
+  static char buf[20];
+
+  if (GET_THREAD (ptid) == 0)
+    sprintf (buf, "process %d", GET_PID (ptid));
+  else
+    sprintf (buf, "LWP %ld", GET_THREAD (ptid));
+
+  return buf;
+}
+
+/*
+ * Function: stopped_by_watchpoint
+ *
+ * Returns non-zero if process is stopped on a hardware watchpoint fault,
+ * else returns zero.
+ */
+
+int
+procfs_stopped_by_watchpoint (ptid_t ptid)
+{
+  procinfo *pi;
+
+  pi = find_procinfo_or_die (GET_PID (ptid) == -1 ?
+			     GET_PID (inferior_ptid) : GET_PID (ptid), 0);
+
+  if (!pi)	/* If no process, then not stopped by watchpoint!  */
+    return 0;
+
+  if (proc_flags (pi) & (PR_STOPPED | PR_ISTOP))
+    {
+      if (proc_why (pi) == PR_FAULTED)
+	{
+	  if (proc_what (pi) == FLTWATCH)
+	    return 1;
+	}
+    }
+  return 0;
+}
+
+
+/*
+ * Return a pid for which we guarantee
+ * we will be able to find a 'live' procinfo.
+ */
+
+ptid_t
+procfs_first_available (void)
+{
+  return pid_to_ptid (procinfo_list ? procinfo_list->pid : -1);
+}
+
+


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]